summaryrefslogtreecommitdiff
path: root/jni/ruby/test
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/test
Fresh start
Diffstat (limited to 'jni/ruby/test')
-rw-r--r--jni/ruby/test/-ext-/array/test_resize.rb29
-rw-r--r--jni/ruby/test/-ext-/bignum/test_big2str.rb29
-rw-r--r--jni/ruby/test/-ext-/bignum/test_bigzero.rb13
-rw-r--r--jni/ruby/test/-ext-/bignum/test_div.rb28
-rw-r--r--jni/ruby/test/-ext-/bignum/test_mul.rb137
-rw-r--r--jni/ruby/test/-ext-/bignum/test_pack.rb398
-rw-r--r--jni/ruby/test/-ext-/bignum/test_str2big.rb37
-rw-r--r--jni/ruby/test/-ext-/bug_reporter/test_bug_reporter.rb25
-rw-r--r--jni/ruby/test/-ext-/class/test_class2name.rb18
-rw-r--r--jni/ruby/test/-ext-/debug/test_debug.rb58
-rw-r--r--jni/ruby/test/-ext-/debug/test_profile_frames.rb104
-rw-r--r--jni/ruby/test/-ext-/exception/test_data_error.rb13
-rw-r--r--jni/ruby/test/-ext-/exception/test_enc_raise.rb15
-rw-r--r--jni/ruby/test/-ext-/exception/test_ensured.rb31
-rw-r--r--jni/ruby/test/-ext-/file/test_stat.rb14
-rw-r--r--jni/ruby/test/-ext-/float/test_nextafter.rb57
-rw-r--r--jni/ruby/test/-ext-/funcall/test_passing_block.rb22
-rw-r--r--jni/ruby/test/-ext-/hash/test_delete.rb19
-rw-r--r--jni/ruby/test/-ext-/iseq_load/test_iseq_load.rb95
-rw-r--r--jni/ruby/test/-ext-/iter/test_iter_break.rb15
-rw-r--r--jni/ruby/test/-ext-/iter/test_yield_block.rb21
-rw-r--r--jni/ruby/test/-ext-/load/test_dot_dot.rb10
-rw-r--r--jni/ruby/test/-ext-/marshal/test_usrmarshal.rb32
-rw-r--r--jni/ruby/test/-ext-/method/test_arity.rb37
-rw-r--r--jni/ruby/test/-ext-/num2int/test_num2int.rb267
-rw-r--r--jni/ruby/test/-ext-/path_to_class/test_path_to_class.rb12
-rw-r--r--jni/ruby/test/-ext-/postponed_job/test_postponed_job.rb28
-rw-r--r--jni/ruby/test/-ext-/proc/test_bmethod.rb37
-rw-r--r--jni/ruby/test/-ext-/rational/test_rat.rb31
-rw-r--r--jni/ruby/test/-ext-/st/test_foreach.rb15
-rw-r--r--jni/ruby/test/-ext-/st/test_numhash.rb49
-rw-r--r--jni/ruby/test/-ext-/st/test_update.rb50
-rw-r--r--jni/ruby/test/-ext-/string/test_coderange.rb59
-rw-r--r--jni/ruby/test/-ext-/string/test_cstr.rb119
-rw-r--r--jni/ruby/test/-ext-/string/test_ellipsize.rb46
-rw-r--r--jni/ruby/test/-ext-/string/test_enc_associate.rb12
-rw-r--r--jni/ruby/test/-ext-/string/test_enc_str_buf_cat.rb15
-rw-r--r--jni/ruby/test/-ext-/string/test_modify_expand.rb15
-rw-r--r--jni/ruby/test/-ext-/string/test_nofree.rb10
-rw-r--r--jni/ruby/test/-ext-/string/test_normalize.rb106
-rw-r--r--jni/ruby/test/-ext-/string/test_qsort.rb19
-rw-r--r--jni/ruby/test/-ext-/string/test_set_len.rb25
-rw-r--r--jni/ruby/test/-ext-/struct/test_member.rb15
-rw-r--r--jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb473
-rw-r--r--jni/ruby/test/-ext-/symbol/test_type.rb124
-rw-r--r--jni/ruby/test/-ext-/test_bug-3571.rb20
-rw-r--r--jni/ruby/test/-ext-/test_bug-3662.rb10
-rw-r--r--jni/ruby/test/-ext-/test_bug-5832.rb21
-rw-r--r--jni/ruby/test/-ext-/test_printf.rb186
-rw-r--r--jni/ruby/test/-ext-/test_recursion.rb35
-rw-r--r--jni/ruby/test/-ext-/tracepoint/test_tracepoint.rb79
-rw-r--r--jni/ruby/test/-ext-/typeddata/test_typeddata.rb16
-rw-r--r--jni/ruby/test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb45
-rw-r--r--jni/ruby/test/-ext-/win32/test_console_attr.rb43
-rw-r--r--jni/ruby/test/-ext-/win32/test_dln.rb34
-rw-r--r--jni/ruby/test/-ext-/win32/test_fd_setsize.rb24
-rw-r--r--jni/ruby/test/base64/test_base64.rb100
-rw-r--r--jni/ruby/test/benchmark/test_benchmark.rb161
-rw-r--r--jni/ruby/test/bigdecimal/test_bigdecimal.rb1555
-rw-r--r--jni/ruby/test/bigdecimal/test_bigdecimal_util.rb50
-rw-r--r--jni/ruby/test/bigdecimal/test_bigmath.rb80
-rw-r--r--jni/ruby/test/bigdecimal/testbase.rb27
-rw-r--r--jni/ruby/test/cgi/test_cgi_cookie.rb110
-rw-r--r--jni/ruby/test/cgi/test_cgi_core.rb312
-rw-r--r--jni/ruby/test/cgi/test_cgi_header.rb181
-rw-r--r--jni/ruby/test/cgi/test_cgi_modruby.rb146
-rw-r--r--jni/ruby/test/cgi/test_cgi_multipart.rb380
-rw-r--r--jni/ruby/test/cgi/test_cgi_session.rb172
-rw-r--r--jni/ruby/test/cgi/test_cgi_tag_helper.rb353
-rw-r--r--jni/ruby/test/cgi/test_cgi_util.rb104
-rw-r--r--jni/ruby/test/cgi/testdata/file1.html10
-rw-r--r--jni/ruby/test/cgi/testdata/large.pngbin0 -> 156414 bytes
-rw-r--r--jni/ruby/test/cgi/testdata/small.pngbin0 -> 82 bytes
-rw-r--r--jni/ruby/test/coverage/test_coverage.rb64
-rw-r--r--jni/ruby/test/csv/base.rb8
-rw-r--r--jni/ruby/test/csv/line_endings.gzbin0 -> 59 bytes
-rwxr-xr-xjni/ruby/test/csv/test_csv_parsing.rb221
-rwxr-xr-xjni/ruby/test/csv/test_csv_writing.rb97
-rwxr-xr-xjni/ruby/test/csv/test_data_converters.rb263
-rwxr-xr-xjni/ruby/test/csv/test_encodings.rb337
-rwxr-xr-xjni/ruby/test/csv/test_features.rb327
-rwxr-xr-xjni/ruby/test/csv/test_headers.rb297
-rwxr-xr-xjni/ruby/test/csv/test_interface.rb368
-rwxr-xr-xjni/ruby/test/csv/test_row.rb355
-rwxr-xr-xjni/ruby/test/csv/test_table.rb434
-rw-r--r--jni/ruby/test/csv/ts_all.rb20
-rw-r--r--jni/ruby/test/date/test_date.rb150
-rw-r--r--jni/ruby/test/date/test_date_arith.rb264
-rw-r--r--jni/ruby/test/date/test_date_attr.rb103
-rw-r--r--jni/ruby/test/date/test_date_base.rb442
-rw-r--r--jni/ruby/test/date/test_date_compat.rb21
-rw-r--r--jni/ruby/test/date/test_date_conv.rb137
-rw-r--r--jni/ruby/test/date/test_date_marshal.rb41
-rw-r--r--jni/ruby/test/date/test_date_new.rb271
-rw-r--r--jni/ruby/test/date/test_date_parse.rb1124
-rw-r--r--jni/ruby/test/date/test_date_strftime.rb422
-rw-r--r--jni/ruby/test/date/test_date_strptime.rb512
-rw-r--r--jni/ruby/test/date/test_switch_hitter.rb664
-rw-r--r--jni/ruby/test/dbm/test_dbm.rb631
-rw-r--r--jni/ruby/test/digest/digest/foo.rb10
-rw-r--r--jni/ruby/test/digest/test_digest.rb272
-rw-r--r--jni/ruby/test/digest/test_digest_extend.rb158
-rw-r--r--jni/ruby/test/drb/drbtest.rb368
-rw-r--r--jni/ruby/test/drb/ignore_test_drb.rb13
-rw-r--r--jni/ruby/test/drb/test_acl.rb197
-rw-r--r--jni/ruby/test/drb/test_drb.rb357
-rw-r--r--jni/ruby/test/drb/test_drbssl.rb76
-rw-r--r--jni/ruby/test/drb/test_drbunix.rb59
-rw-r--r--jni/ruby/test/drb/ut_array.rb16
-rw-r--r--jni/ruby/test/drb/ut_array_drbssl.rb40
-rw-r--r--jni/ruby/test/drb/ut_array_drbunix.rb16
-rw-r--r--jni/ruby/test/drb/ut_drb.rb167
-rw-r--r--jni/ruby/test/drb/ut_drb_drbssl.rb41
-rw-r--r--jni/ruby/test/drb/ut_drb_drbunix.rb17
-rw-r--r--jni/ruby/test/drb/ut_eq.rb36
-rw-r--r--jni/ruby/test/drb/ut_eval.rb36
-rw-r--r--jni/ruby/test/drb/ut_large.rb43
-rw-r--r--jni/ruby/test/drb/ut_port.rb15
-rw-r--r--jni/ruby/test/drb/ut_safe1.rb16
-rw-r--r--jni/ruby/test/drb/ut_timerholder.rb53
-rw-r--r--jni/ruby/test/dtrace/dummy.rb1
-rw-r--r--jni/ruby/test/dtrace/helper.rb50
-rw-r--r--jni/ruby/test/dtrace/test_array_create.rb35
-rw-r--r--jni/ruby/test/dtrace/test_cmethod.rb49
-rw-r--r--jni/ruby/test/dtrace/test_function_entry.rb87
-rw-r--r--jni/ruby/test/dtrace/test_gc.rb26
-rw-r--r--jni/ruby/test/dtrace/test_hash_create.rb52
-rw-r--r--jni/ruby/test/dtrace/test_load.rb52
-rw-r--r--jni/ruby/test/dtrace/test_method_cache.rb28
-rw-r--r--jni/ruby/test/dtrace/test_object_create_start.rb35
-rw-r--r--jni/ruby/test/dtrace/test_raise.rb29
-rw-r--r--jni/ruby/test/dtrace/test_require.rb34
-rw-r--r--jni/ruby/test/dtrace/test_singleton_function.rb55
-rw-r--r--jni/ruby/test/dtrace/test_string.rb27
-rw-r--r--jni/ruby/test/erb/hello.erb4
-rw-r--r--jni/ruby/test/erb/test_erb.rb504
-rw-r--r--jni/ruby/test/erb/test_erb_command.rb11
-rw-r--r--jni/ruby/test/erb/test_erb_m17n.rb123
-rw-r--r--jni/ruby/test/etc/test_etc.rb169
-rw-r--r--jni/ruby/test/fiddle/helper.rb124
-rw-r--r--jni/ruby/test/fiddle/test_c_struct_entry.rb76
-rw-r--r--jni/ruby/test/fiddle/test_c_union_entity.rb34
-rw-r--r--jni/ruby/test/fiddle/test_closure.rb84
-rw-r--r--jni/ruby/test/fiddle/test_cparser.rb35
-rw-r--r--jni/ruby/test/fiddle/test_fiddle.rb16
-rw-r--r--jni/ruby/test/fiddle/test_func.rb92
-rw-r--r--jni/ruby/test/fiddle/test_function.rb82
-rw-r--r--jni/ruby/test/fiddle/test_handle.rb195
-rw-r--r--jni/ruby/test/fiddle/test_import.rb150
-rw-r--r--jni/ruby/test/fiddle/test_pointer.rb237
-rw-r--r--jni/ruby/test/fileutils/clobber.rb91
-rw-r--r--jni/ruby/test/fileutils/fileasserts.rb111
-rw-r--r--jni/ruby/test/fileutils/test_dryrun.rb17
-rw-r--r--jni/ruby/test/fileutils/test_fileutils.rb1570
-rw-r--r--jni/ruby/test/fileutils/test_nowrite.rb17
-rw-r--r--jni/ruby/test/fileutils/test_verbose.rb17
-rw-r--r--jni/ruby/test/fileutils/visibility_tests.rb41
-rw-r--r--jni/ruby/test/gdbm/test_gdbm.rb719
-rw-r--r--jni/ruby/test/io/console/test_io_console.rb332
-rw-r--r--jni/ruby/test/io/nonblock/test_flush.rb52
-rw-r--r--jni/ruby/test/io/wait/test_io_wait.rb112
-rw-r--r--jni/ruby/test/irb/test_completion.rb21
-rw-r--r--jni/ruby/test/irb/test_option.rb11
-rw-r--r--jni/ruby/test/irb/test_raise_no_backtrace_exception.rb13
-rw-r--r--jni/ruby/test/json/fixtures/fail1.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail10.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail11.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail12.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail13.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail14.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail18.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail19.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail2.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail20.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail21.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail22.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail23.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail24.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail25.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail27.json2
-rw-r--r--jni/ruby/test/json/fixtures/fail28.json2
-rw-r--r--jni/ruby/test/json/fixtures/fail3.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail4.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail5.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail6.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail7.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail8.json1
-rw-r--r--jni/ruby/test/json/fixtures/fail9.json1
-rw-r--r--jni/ruby/test/json/fixtures/pass1.json56
-rw-r--r--jni/ruby/test/json/fixtures/pass15.json1
-rw-r--r--jni/ruby/test/json/fixtures/pass16.json1
-rw-r--r--jni/ruby/test/json/fixtures/pass17.json1
-rw-r--r--jni/ruby/test/json/fixtures/pass2.json1
-rw-r--r--jni/ruby/test/json/fixtures/pass26.json1
-rw-r--r--jni/ruby/test/json/fixtures/pass3.json6
-rw-r--r--jni/ruby/test/json/setup_variant.rb11
-rwxr-xr-xjni/ruby/test/json/test_json.rb557
-rwxr-xr-xjni/ruby/test/json/test_json_addition.rb196
-rw-r--r--jni/ruby/test/json/test_json_encoding.rb65
-rwxr-xr-xjni/ruby/test/json/test_json_fixtures.rb35
-rwxr-xr-xjni/ruby/test/json/test_json_generate.rb322
-rw-r--r--jni/ruby/test/json/test_json_generic_object.rb75
-rw-r--r--jni/ruby/test/json/test_json_string_matching.rb39
-rwxr-xr-xjni/ruby/test/json/test_json_unicode.rb72
-rw-r--r--jni/ruby/test/lib/envutil.rb604
-rw-r--r--jni/ruby/test/lib/find_executable.rb21
-rw-r--r--jni/ruby/test/lib/leakchecker.rb167
-rw-r--r--jni/ruby/test/lib/memory_status.rb111
-rw-r--r--jni/ruby/test/lib/minitest/README.txt457
-rw-r--r--jni/ruby/test/lib/minitest/autorun.rb13
-rw-r--r--jni/ruby/test/lib/minitest/benchmark.rb417
-rw-r--r--jni/ruby/test/lib/minitest/mock.rb195
-rw-r--r--jni/ruby/test/lib/minitest/unit.rb1419
-rw-r--r--jni/ruby/test/lib/profile_test_all.rb90
-rw-r--r--jni/ruby/test/lib/test/unit.rb896
-rw-r--r--jni/ruby/test/lib/test/unit/assertions.rb465
-rw-r--r--jni/ruby/test/lib/test/unit/parallel.rb190
-rw-r--r--jni/ruby/test/lib/test/unit/testcase.rb34
-rw-r--r--jni/ruby/test/lib/tracepointchecker.rb118
-rw-r--r--jni/ruby/test/lib/with_different_ofs.rb17
-rw-r--r--jni/ruby/test/logger/test_logdevice.rb419
-rw-r--r--jni/ruby/test/logger/test_logger.rb244
-rw-r--r--jni/ruby/test/logger/test_severity.rb15
-rw-r--r--jni/ruby/test/matrix/test_matrix.rb579
-rw-r--r--jni/ruby/test/matrix/test_vector.rb215
-rw-r--r--jni/ruby/test/minitest/metametameta.rb69
-rw-r--r--jni/ruby/test/minitest/test_minitest_benchmark.rb130
-rw-r--r--jni/ruby/test/minitest/test_minitest_mock.rb403
-rw-r--r--jni/ruby/test/minitest/test_minitest_unit.rb1803
-rw-r--r--jni/ruby/test/misc/test_ruby_mode.rb182
-rw-r--r--jni/ruby/test/mkmf/base.rb139
-rw-r--r--jni/ruby/test/mkmf/test_config.rb16
-rw-r--r--jni/ruby/test/mkmf/test_constant.rb37
-rw-r--r--jni/ruby/test/mkmf/test_convertible.rb34
-rw-r--r--jni/ruby/test/mkmf/test_find_executable.rb50
-rw-r--r--jni/ruby/test/mkmf/test_flags.rb56
-rw-r--r--jni/ruby/test/mkmf/test_framework.rb48
-rw-r--r--jni/ruby/test/mkmf/test_have_func.rb14
-rw-r--r--jni/ruby/test/mkmf/test_have_library.rb55
-rw-r--r--jni/ruby/test/mkmf/test_have_macro.rb35
-rw-r--r--jni/ruby/test/mkmf/test_libs.rb86
-rw-r--r--jni/ruby/test/mkmf/test_signedness.rb29
-rw-r--r--jni/ruby/test/mkmf/test_sizeof.rb47
-rw-r--r--jni/ruby/test/monitor/test_monitor.rb209
-rw-r--r--jni/ruby/test/net/ftp/test_buffered_socket.rb40
-rw-r--r--jni/ruby/test/net/ftp/test_ftp.rb833
-rw-r--r--jni/ruby/test/net/http/test_buffered_io.rb17
-rw-r--r--jni/ruby/test/net/http/test_http.rb933
-rw-r--r--jni/ruby/test/net/http/test_http_request.rb79
-rw-r--r--jni/ruby/test/net/http/test_httpheader.rb334
-rw-r--r--jni/ruby/test/net/http/test_httpresponse.rb394
-rw-r--r--jni/ruby/test/net/http/test_httpresponses.rb24
-rw-r--r--jni/ruby/test/net/http/test_https.rb194
-rw-r--r--jni/ruby/test/net/http/test_https_proxy.rb46
-rw-r--r--jni/ruby/test/net/http/utils.rb108
-rw-r--r--jni/ruby/test/net/imap/Makefile15
-rw-r--r--jni/ruby/test/net/imap/cacert.pem66
-rw-r--r--jni/ruby/test/net/imap/server.crt48
-rw-r--r--jni/ruby/test/net/imap/server.key15
-rw-r--r--jni/ruby/test/net/imap/test_imap.rb550
-rw-r--r--jni/ruby/test/net/imap/test_imap_response_parser.rb292
-rw-r--r--jni/ruby/test/net/pop/test_pop.rb136
-rw-r--r--jni/ruby/test/net/protocol/test_protocol.rb28
-rw-r--r--jni/ruby/test/net/smtp/test_response.rb99
-rw-r--r--jni/ruby/test/net/smtp/test_smtp.rb54
-rw-r--r--jni/ruby/test/net/smtp/test_ssl_socket.rb91
-rw-r--r--jni/ruby/test/nkf/test_kconv.rb81
-rw-r--r--jni/ruby/test/nkf/test_nkf.rb22
-rw-r--r--jni/ruby/test/objspace/test_objspace.rb288
-rw-r--r--jni/ruby/test/open-uri/test_open-uri.rb832
-rw-r--r--jni/ruby/test/open-uri/test_ssl.rb390
-rw-r--r--jni/ruby/test/openssl/test_asn1.rb609
-rw-r--r--jni/ruby/test/openssl/test_bn.rb52
-rw-r--r--jni/ruby/test/openssl/test_buffering.rb87
-rw-r--r--jni/ruby/test/openssl/test_cipher.rb260
-rw-r--r--jni/ruby/test/openssl/test_config.rb297
-rw-r--r--jni/ruby/test/openssl/test_digest.rb126
-rw-r--r--jni/ruby/test/openssl/test_engine.rb75
-rw-r--r--jni/ruby/test/openssl/test_fips.rb14
-rw-r--r--jni/ruby/test/openssl/test_hmac.rb41
-rw-r--r--jni/ruby/test/openssl/test_ns_spki.rb51
-rw-r--r--jni/ruby/test/openssl/test_ocsp.rb47
-rw-r--r--jni/ruby/test/openssl/test_pair.rb372
-rw-r--r--jni/ruby/test/openssl/test_partial_record_read.rb34
-rw-r--r--jni/ruby/test/openssl/test_pkcs12.rb209
-rw-r--r--jni/ruby/test/openssl/test_pkcs5.rb97
-rw-r--r--jni/ruby/test/openssl/test_pkcs7.rb297
-rw-r--r--jni/ruby/test/openssl/test_pkey_dh.rb82
-rw-r--r--jni/ruby/test/openssl/test_pkey_dsa.rb240
-rw-r--r--jni/ruby/test/openssl/test_pkey_ec.rb211
-rw-r--r--jni/ruby/test/openssl/test_pkey_rsa.rb313
-rw-r--r--jni/ruby/test/openssl/test_ssl.rb933
-rw-r--r--jni/ruby/test/openssl/test_ssl_session.rb380
-rw-r--r--jni/ruby/test/openssl/test_x509cert.rb226
-rw-r--r--jni/ruby/test/openssl/test_x509crl.rb220
-rw-r--r--jni/ruby/test/openssl/test_x509ext.rb69
-rw-r--r--jni/ruby/test/openssl/test_x509name.rb367
-rw-r--r--jni/ruby/test/openssl/test_x509req.rb158
-rw-r--r--jni/ruby/test/openssl/test_x509store.rb231
-rw-r--r--jni/ruby/test/openssl/utils.rb333
-rw-r--r--jni/ruby/test/optparse/test_acceptable.rb195
-rw-r--r--jni/ruby/test/optparse/test_autoconf.rb63
-rw-r--r--jni/ruby/test/optparse/test_bash_completion.rb42
-rw-r--r--jni/ruby/test/optparse/test_getopts.rb34
-rw-r--r--jni/ruby/test/optparse/test_noarg.rb57
-rw-r--r--jni/ruby/test/optparse/test_optarg.rb46
-rw-r--r--jni/ruby/test/optparse/test_optparse.rb66
-rw-r--r--jni/ruby/test/optparse/test_placearg.rb56
-rw-r--r--jni/ruby/test/optparse/test_reqarg.rb77
-rw-r--r--jni/ruby/test/optparse/test_summary.rb46
-rw-r--r--jni/ruby/test/optparse/test_zsh_completion.rb22
-rw-r--r--jni/ruby/test/ostruct/test_ostruct.rb138
-rw-r--r--jni/ruby/test/pathname/test_pathname.rb1389
-rw-r--r--jni/ruby/test/psych/handlers/test_recorder.rb25
-rw-r--r--jni/ruby/test/psych/helper.rb114
-rw-r--r--jni/ruby/test/psych/json/test_stream.rb109
-rw-r--r--jni/ruby/test/psych/nodes/test_enumerable.rb43
-rw-r--r--jni/ruby/test/psych/test_alias_and_anchor.rb96
-rw-r--r--jni/ruby/test/psych/test_array.rb57
-rw-r--r--jni/ruby/test/psych/test_boolean.rb36
-rw-r--r--jni/ruby/test/psych/test_class.rb36
-rw-r--r--jni/ruby/test/psych/test_coder.rb184
-rw-r--r--jni/ruby/test/psych/test_date_time.rb38
-rw-r--r--jni/ruby/test/psych/test_deprecated.rb214
-rw-r--r--jni/ruby/test/psych/test_document.rb46
-rw-r--r--jni/ruby/test/psych/test_emitter.rb93
-rw-r--r--jni/ruby/test/psych/test_encoding.rb259
-rw-r--r--jni/ruby/test/psych/test_exception.rb157
-rw-r--r--jni/ruby/test/psych/test_hash.rb49
-rw-r--r--jni/ruby/test/psych/test_json_tree.rb65
-rw-r--r--jni/ruby/test/psych/test_marshalable.rb54
-rw-r--r--jni/ruby/test/psych/test_merge_keys.rb180
-rw-r--r--jni/ruby/test/psych/test_nil.rb18
-rw-r--r--jni/ruby/test/psych/test_null.rb19
-rw-r--r--jni/ruby/test/psych/test_numeric.rb45
-rw-r--r--jni/ruby/test/psych/test_object.rb44
-rw-r--r--jni/ruby/test/psych/test_object_references.rb71
-rw-r--r--jni/ruby/test/psych/test_omap.rb75
-rw-r--r--jni/ruby/test/psych/test_parser.rb339
-rw-r--r--jni/ruby/test/psych/test_psych.rb168
-rw-r--r--jni/ruby/test/psych/test_safe_load.rb97
-rw-r--r--jni/ruby/test/psych/test_scalar.rb11
-rw-r--r--jni/ruby/test/psych/test_scalar_scanner.rb106
-rw-r--r--jni/ruby/test/psych/test_serialize_subclasses.rb38
-rw-r--r--jni/ruby/test/psych/test_set.rb49
-rw-r--r--jni/ruby/test/psych/test_stream.rb93
-rw-r--r--jni/ruby/test/psych/test_string.rb166
-rw-r--r--jni/ruby/test/psych/test_struct.rb49
-rw-r--r--jni/ruby/test/psych/test_symbol.rb25
-rw-r--r--jni/ruby/test/psych/test_tainted.rb130
-rw-r--r--jni/ruby/test/psych/test_to_yaml_properties.rb63
-rw-r--r--jni/ruby/test/psych/test_tree_builder.rb79
-rw-r--r--jni/ruby/test/psych/test_yaml.rb1288
-rw-r--r--jni/ruby/test/psych/test_yamldbm.rb193
-rw-r--r--jni/ruby/test/psych/test_yamlstore.rb85
-rw-r--r--jni/ruby/test/psych/visitors/test_depth_first.rb49
-rw-r--r--jni/ruby/test/psych/visitors/test_emitter.rb144
-rw-r--r--jni/ruby/test/psych/visitors/test_to_ruby.rb326
-rw-r--r--jni/ruby/test/psych/visitors/test_yaml_tree.rb173
-rw-r--r--jni/ruby/test/rake/file_creation.rb34
-rw-r--r--jni/ruby/test/rake/helper.rb129
-rw-r--r--jni/ruby/test/rake/support/rakefile_definitions.rb478
-rw-r--r--jni/ruby/test/rake/support/ruby_runner.rb34
-rw-r--r--jni/ruby/test/rake/test_private_reader.rb42
-rw-r--r--jni/ruby/test/rake/test_rake.rb40
-rw-r--r--jni/ruby/test/rake/test_rake_application.rb643
-rw-r--r--jni/ruby/test/rake/test_rake_application_options.rb466
-rw-r--r--jni/ruby/test/rake/test_rake_backtrace.rb119
-rw-r--r--jni/ruby/test/rake/test_rake_clean.rb61
-rw-r--r--jni/ruby/test/rake/test_rake_cpu_counter.rb68
-rw-r--r--jni/ruby/test/rake/test_rake_definitions.rb84
-rw-r--r--jni/ruby/test/rake/test_rake_directory_task.rb76
-rw-r--r--jni/ruby/test/rake/test_rake_dsl.rb40
-rw-r--r--jni/ruby/test/rake/test_rake_early_time.rb31
-rw-r--r--jni/ruby/test/rake/test_rake_extension.rb59
-rw-r--r--jni/ruby/test/rake/test_rake_file_creation_task.rb56
-rw-r--r--jni/ruby/test/rake/test_rake_file_list.rb655
-rw-r--r--jni/ruby/test/rake/test_rake_file_list_path_map.rb8
-rw-r--r--jni/ruby/test/rake/test_rake_file_task.rb197
-rw-r--r--jni/ruby/test/rake/test_rake_file_utils.rb309
-rw-r--r--jni/ruby/test/rake/test_rake_ftp_file.rb74
-rw-r--r--jni/ruby/test/rake/test_rake_functional.rb482
-rw-r--r--jni/ruby/test/rake/test_rake_invocation_chain.rb64
-rw-r--r--jni/ruby/test/rake/test_rake_late_time.rb18
-rw-r--r--jni/ruby/test/rake/test_rake_linked_list.rb84
-rw-r--r--jni/ruby/test/rake/test_rake_makefile_loader.rb46
-rw-r--r--jni/ruby/test/rake/test_rake_multi_task.rb64
-rw-r--r--jni/ruby/test/rake/test_rake_name_space.rb57
-rw-r--r--jni/ruby/test/rake/test_rake_package_task.rb79
-rw-r--r--jni/ruby/test/rake/test_rake_path_map.rb168
-rw-r--r--jni/ruby/test/rake/test_rake_path_map_explode.rb34
-rw-r--r--jni/ruby/test/rake/test_rake_path_map_partial.rb18
-rw-r--r--jni/ruby/test/rake/test_rake_pathname_extensions.rb15
-rw-r--r--jni/ruby/test/rake/test_rake_pseudo_status.rb21
-rw-r--r--jni/ruby/test/rake/test_rake_rake_test_loader.rb20
-rw-r--r--jni/ruby/test/rake/test_rake_reduce_compat.rb26
-rw-r--r--jni/ruby/test/rake/test_rake_require.rb40
-rw-r--r--jni/ruby/test/rake/test_rake_rules.rb388
-rw-r--r--jni/ruby/test/rake/test_rake_scope.rb44
-rw-r--r--jni/ruby/test/rake/test_rake_task.rb393
-rw-r--r--jni/ruby/test/rake/test_rake_task_argument_parsing.rb119
-rw-r--r--jni/ruby/test/rake/test_rake_task_arguments.rb127
-rw-r--r--jni/ruby/test/rake/test_rake_task_lib.rb9
-rw-r--r--jni/ruby/test/rake/test_rake_task_manager.rb178
-rw-r--r--jni/ruby/test/rake/test_rake_task_manager_argument_resolution.rb19
-rw-r--r--jni/ruby/test/rake/test_rake_task_with_arguments.rb172
-rw-r--r--jni/ruby/test/rake/test_rake_test_task.rb146
-rw-r--r--jni/ruby/test/rake/test_rake_thread_pool.rb145
-rw-r--r--jni/ruby/test/rake/test_rake_top_level_functions.rb71
-rw-r--r--jni/ruby/test/rake/test_rake_win32.rb72
-rw-r--r--jni/ruby/test/rake/test_thread_history_display.rb101
-rw-r--r--jni/ruby/test/rake/test_trace_output.rb52
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Amps and angle encoding.text21
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Auto links.text13
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Backslash escapes.text120
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Blockquotes with code blocks.text11
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Code Blocks.text14
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Code Spans.text6
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Hard-wrapped paragraphs with list-like lines.text8
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Horizontal rules.text67
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML (Advanced).text15
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML (Simple).text69
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML comments.text13
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, inline style.text12
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, reference style.text71
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, shortcut references.text20
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Literal quotes in titles.text7
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text306
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text888
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Nested blockquotes.text5
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Ordered and unordered lists.text131
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Strong and em together.text7
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Tabs.text21
-rw-r--r--jni/ruby/test/rdoc/MarkdownTest_1.0.3/Tidyness.text5
-rw-r--r--jni/ruby/test/rdoc/README1
-rw-r--r--jni/ruby/test/rdoc/binary.datbin0 -> 1024 bytes
-rw-r--r--jni/ruby/test/rdoc/hidden.zip.txt1
-rw-r--r--jni/ruby/test/rdoc/test.ja.largedoc3
-rw-r--r--jni/ruby/test/rdoc/test.ja.rdoc10
-rw-r--r--jni/ruby/test/rdoc/test.ja.txt8
-rw-r--r--jni/ruby/test/rdoc/test.txt1
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_alias.rb13
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_any_method.rb460
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_attr.rb190
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_class_module.rb1492
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_code_object.rb450
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_comment.rb504
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_constant.rb181
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_context.rb901
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_context_section.rb130
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_cross_reference.rb192
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_encoding.rb227
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_extend.rb94
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_generator_darkfish.rb229
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_generator_json_index.rb320
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_generator_markup.rb59
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_generator_pot.rb91
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_generator_pot_po.rb51
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_generator_pot_po_entry.rb139
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_generator_ri.rb78
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_i18n_locale.rb73
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_i18n_text.rb123
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_include.rb108
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markdown.rb980
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markdown_test.rb1884
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup.rb95
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_attribute_manager.rb364
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_attributes.rb39
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_document.rb207
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_formatter.rb175
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_hard_break.rb31
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_heading.rb29
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_include.rb19
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_indented_paragraph.rb53
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_paragraph.rb32
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_parser.rb1680
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_pre_process.rb473
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_raw.rb22
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_ansi.rb369
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_bs.rb366
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_html.rb663
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_html_crossref.rb225
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_html_snippet.rb711
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_joined_paragraph.rb32
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_label.rb112
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_markdown.rb389
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_rdoc.rb377
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_table_of_contents.rb126
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_to_tt_only.rb246
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_markup_verbatim.rb29
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_method_attr.rb193
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_normal_class.rb47
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_normal_module.rb42
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_options.rb766
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_parser.rb327
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_parser_c.rb1896
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_parser_changelog.rb315
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_parser_markdown.rb61
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_parser_rd.rb55
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_parser_ruby.rb3322
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_parser_simple.rb115
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_rd.rb30
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_rd_block_parser.rb535
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_rd_inline.rb63
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_rd_inline_parser.rb177
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_rdoc.rb455
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_require.rb25
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_ri_driver.rb1436
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_ri_paths.rb155
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_ruby_lex.rb421
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_ruby_token.rb19
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_rubygems_hook.rb251
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_servlet.rb535
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_single_class.rb20
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_stats.rb722
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_store.rb993
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_task.rb170
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_text.rb557
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_token_stream.rb42
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_tom_doc.rb520
-rw-r--r--jni/ruby/test/rdoc/test_rdoc_top_level.rb287
-rw-r--r--jni/ruby/test/rdoc/xref_data.rb76
-rw-r--r--jni/ruby/test/rdoc/xref_test_case.rb67
-rw-r--r--jni/ruby/test/readline/test_readline.rb530
-rw-r--r--jni/ruby/test/readline/test_readline_history.rb292
-rw-r--r--jni/ruby/test/resolv/test_addr.rb29
-rw-r--r--jni/ruby/test/resolv/test_dns.rb200
-rw-r--r--jni/ruby/test/resolv/test_resource.rb21
-rw-r--r--jni/ruby/test/rexml/data/LostineRiver.kml.gzbin0 -> 50154 bytes
-rw-r--r--jni/ruby/test/rexml/data/ProductionSupport.xml29
-rw-r--r--jni/ruby/test/rexml/data/axis.xml25
-rw-r--r--jni/ruby/test/rexml/data/bad.xml5
-rw-r--r--jni/ruby/test/rexml/data/basic.xml11
-rw-r--r--jni/ruby/test/rexml/data/basicupdate.xml47
-rw-r--r--jni/ruby/test/rexml/data/broken.rss20
-rw-r--r--jni/ruby/test/rexml/data/contents.xml70
-rw-r--r--jni/ruby/test/rexml/data/dash.xml12
-rw-r--r--jni/ruby/test/rexml/data/defaultNamespace.xml6
-rw-r--r--jni/ruby/test/rexml/data/doctype_test.xml34
-rw-r--r--jni/ruby/test/rexml/data/documentation.xml542
-rw-r--r--jni/ruby/test/rexml/data/euc.xml296
-rw-r--r--jni/ruby/test/rexml/data/evaluate.xml28
-rw-r--r--jni/ruby/test/rexml/data/fibo.xml29
-rw-r--r--jni/ruby/test/rexml/data/foo.xml10
-rw-r--r--jni/ruby/test/rexml/data/google.2.xml156
-rw-r--r--jni/ruby/test/rexml/data/id.xml21
-rw-r--r--jni/ruby/test/rexml/data/iso8859-1.xml4
-rw-r--r--jni/ruby/test/rexml/data/jaxen24.xml2
-rw-r--r--jni/ruby/test/rexml/data/jaxen3.xml15
-rw-r--r--jni/ruby/test/rexml/data/lang.xml11
-rw-r--r--jni/ruby/test/rexml/data/lang0.xml18
-rw-r--r--jni/ruby/test/rexml/data/message.xml27
-rw-r--r--jni/ruby/test/rexml/data/moreover.xml244
-rw-r--r--jni/ruby/test/rexml/data/much_ado.xml6850
-rw-r--r--jni/ruby/test/rexml/data/namespaces.xml18
-rw-r--r--jni/ruby/test/rexml/data/nitf.xml67
-rw-r--r--jni/ruby/test/rexml/data/numbers.xml18
-rw-r--r--jni/ruby/test/rexml/data/ofbiz-issues-full-177.xml13971
-rw-r--r--jni/ruby/test/rexml/data/pi.xml13
-rw-r--r--jni/ruby/test/rexml/data/pi2.xml6
-rw-r--r--jni/ruby/test/rexml/data/project.xml1
-rw-r--r--jni/ruby/test/rexml/data/simple.xml2
-rw-r--r--jni/ruby/test/rexml/data/stream_accents.xml4
-rw-r--r--jni/ruby/test/rexml/data/t63-1.xmlbin0 -> 161690 bytes
-rw-r--r--jni/ruby/test/rexml/data/t63-2.svg2828
-rw-r--r--jni/ruby/test/rexml/data/t75.xml31
-rw-r--r--jni/ruby/test/rexml/data/test/tests.xml683
-rw-r--r--jni/ruby/test/rexml/data/test/tests.xsl369
-rw-r--r--jni/ruby/test/rexml/data/testNamespaces.xml22
-rw-r--r--jni/ruby/test/rexml/data/testsrc.xml64
-rw-r--r--jni/ruby/test/rexml/data/text.xml10
-rw-r--r--jni/ruby/test/rexml/data/ticket_61.xml4
-rw-r--r--jni/ruby/test/rexml/data/ticket_68.xml590
-rw-r--r--jni/ruby/test/rexml/data/tutorial.xml678
-rw-r--r--jni/ruby/test/rexml/data/underscore.xml6
-rw-r--r--jni/ruby/test/rexml/data/utf16.xmlbin0 -> 207464 bytes
-rw-r--r--jni/ruby/test/rexml/data/web.xml42
-rw-r--r--jni/ruby/test/rexml/data/web2.xml7
-rw-r--r--jni/ruby/test/rexml/data/working.rss202
-rw-r--r--jni/ruby/test/rexml/data/xmlfile-bug.xml15
-rw-r--r--jni/ruby/test/rexml/data/xp.tst27
-rw-r--r--jni/ruby/test/rexml/data/yahoo.xml80
-rw-r--r--jni/ruby/test/rexml/listener.rb50
-rw-r--r--jni/ruby/test/rexml/parse/test_document_type_declaration.rb49
-rw-r--r--jni/ruby/test/rexml/parse/test_notation_declaration.rb99
-rw-r--r--jni/ruby/test/rexml/parser/test_sax2.rb202
-rw-r--r--jni/ruby/test/rexml/parser/test_tree.rb42
-rw-r--r--jni/ruby/test/rexml/parser/test_ultra_light.rb69
-rw-r--r--jni/ruby/test/rexml/rexml_test_utils.rb6
-rw-r--r--jni/ruby/test/rexml/test_attributes.rb222
-rw-r--r--jni/ruby/test/rexml/test_attributes_mixin.rb31
-rw-r--r--jni/ruby/test/rexml/test_changing_encoding.rb44
-rw-r--r--jni/ruby/test/rexml/test_comment.rb25
-rw-r--r--jni/ruby/test/rexml/test_contrib.rb584
-rw-r--r--jni/ruby/test/rexml/test_core.rb1467
-rw-r--r--jni/ruby/test/rexml/test_doctype.rb106
-rw-r--r--jni/ruby/test/rexml/test_document.rb415
-rw-r--r--jni/ruby/test/rexml/test_elements.rb118
-rw-r--r--jni/ruby/test/rexml/test_encoding.rb107
-rw-r--r--jni/ruby/test/rexml/test_entity.rb205
-rw-r--r--jni/ruby/test/rexml/test_functions.rb224
-rw-r--r--jni/ruby/test/rexml/test_functions_number.rb34
-rw-r--r--jni/ruby/test/rexml/test_jaxen.rb129
-rw-r--r--jni/ruby/test/rexml/test_light.rb106
-rw-r--r--jni/ruby/test/rexml/test_lightparser.rb15
-rw-r--r--jni/ruby/test/rexml/test_listener.rb130
-rw-r--r--jni/ruby/test/rexml/test_martin_fowler.rb39
-rw-r--r--jni/ruby/test/rexml/test_namespace.rb40
-rw-r--r--jni/ruby/test/rexml/test_order.rb109
-rw-r--r--jni/ruby/test/rexml/test_preceding_sibling.rb40
-rw-r--r--jni/ruby/test/rexml/test_pullparser.rb102
-rw-r--r--jni/ruby/test/rexml/test_rexml_issuezilla.rb18
-rw-r--r--jni/ruby/test/rexml/test_sax.rb286
-rw-r--r--jni/ruby/test/rexml/test_stream.rb129
-rw-r--r--jni/ruby/test/rexml/test_text.rb21
-rw-r--r--jni/ruby/test/rexml/test_ticket_80.rb58
-rw-r--r--jni/ruby/test/rexml/test_validation_rng.rb792
-rw-r--r--jni/ruby/test/rexml/test_xml_declaration.rb35
-rw-r--r--jni/ruby/test/rexml/xpath/test_attribute.rb29
-rw-r--r--jni/ruby/test/rexml/xpath/test_axis_preceding_sibling.rb39
-rw-r--r--jni/ruby/test/rexml/xpath/test_base.rb1089
-rw-r--r--jni/ruby/test/rexml/xpath/test_node.rb42
-rw-r--r--jni/ruby/test/rexml/xpath/test_predicate.rb82
-rw-r--r--jni/ruby/test/rexml/xpath/test_text.rb74
-rw-r--r--jni/ruby/test/rinda/test_rinda.rb810
-rw-r--r--jni/ruby/test/rinda/test_tuplebag.rb172
-rw-r--r--jni/ruby/test/ripper/dummyparser.rb220
-rw-r--r--jni/ruby/test/ripper/test_files.rb23
-rw-r--r--jni/ruby/test/ripper/test_filter.rb83
-rw-r--r--jni/ruby/test/ripper/test_parser_events.rb1232
-rw-r--r--jni/ruby/test/ripper/test_ripper.rb63
-rw-r--r--jni/ruby/test/ripper/test_scanner_events.rb909
-rw-r--r--jni/ruby/test/ripper/test_sexp.rb44
-rw-r--r--jni/ruby/test/rss/dot.pngbin0 -> 111 bytes
-rw-r--r--jni/ruby/test/rss/rss-assertions.rb2090
-rw-r--r--jni/ruby/test/rss/rss-testcase.rb478
-rw-r--r--jni/ruby/test/rss/test_1.0.rb307
-rw-r--r--jni/ruby/test/rss/test_2.0.rb411
-rw-r--r--jni/ruby/test/rss/test_accessor.rb103
-rw-r--r--jni/ruby/test/rss/test_atom.rb683
-rw-r--r--jni/ruby/test/rss/test_content.rb104
-rw-r--r--jni/ruby/test/rss/test_dublincore.rb269
-rw-r--r--jni/ruby/test/rss/test_image.rb214
-rw-r--r--jni/ruby/test/rss/test_inherit.rb40
-rw-r--r--jni/ruby/test/rss/test_itunes.rb347
-rw-r--r--jni/ruby/test/rss/test_maker_0.9.rb474
-rw-r--r--jni/ruby/test/rss/test_maker_1.0.rb516
-rw-r--r--jni/ruby/test/rss/test_maker_2.0.rb757
-rw-r--r--jni/ruby/test/rss/test_maker_atom_entry.rb393
-rw-r--r--jni/ruby/test/rss/test_maker_atom_feed.rb454
-rw-r--r--jni/ruby/test/rss/test_maker_content.rb47
-rw-r--r--jni/ruby/test/rss/test_maker_dc.rb149
-rw-r--r--jni/ruby/test/rss/test_maker_image.rb62
-rw-r--r--jni/ruby/test/rss/test_maker_itunes.rb469
-rw-r--r--jni/ruby/test/rss/test_maker_slash.rb37
-rw-r--r--jni/ruby/test/rss/test_maker_sy.rb44
-rw-r--r--jni/ruby/test/rss/test_maker_taxo.rb81
-rw-r--r--jni/ruby/test/rss/test_maker_trackback.rb41
-rw-r--r--jni/ruby/test/rss/test_maker_xml-stylesheet.rb83
-rw-r--r--jni/ruby/test/rss/test_parser.rb64
-rw-r--r--jni/ruby/test/rss/test_parser_1.0.rb528
-rw-r--r--jni/ruby/test/rss/test_parser_2.0.rb122
-rw-r--r--jni/ruby/test/rss/test_parser_atom_entry.rb163
-rw-r--r--jni/ruby/test/rss/test_parser_atom_feed.rb276
-rw-r--r--jni/ruby/test/rss/test_setup_maker_0.9.rb246
-rw-r--r--jni/ruby/test/rss/test_setup_maker_1.0.rb550
-rw-r--r--jni/ruby/test/rss/test_setup_maker_2.0.rb308
-rw-r--r--jni/ruby/test/rss/test_setup_maker_atom_entry.rb409
-rw-r--r--jni/ruby/test/rss/test_setup_maker_atom_feed.rb445
-rw-r--r--jni/ruby/test/rss/test_setup_maker_itunes.rb143
-rw-r--r--jni/ruby/test/rss/test_setup_maker_slash.rb38
-rw-r--r--jni/ruby/test/rss/test_slash.rb64
-rw-r--r--jni/ruby/test/rss/test_syndication.rb125
-rw-r--r--jni/ruby/test/rss/test_taxonomy.rb172
-rw-r--r--jni/ruby/test/rss/test_to_s.rb670
-rw-r--r--jni/ruby/test/rss/test_trackback.rb135
-rw-r--r--jni/ruby/test/rss/test_version.rb9
-rw-r--r--jni/ruby/test/rss/test_xml-stylesheet.rb108
-rw-r--r--jni/ruby/test/ruby/allpairs.rb102
-rw-r--r--jni/ruby/test/ruby/beginmainend.rb80
-rw-r--r--jni/ruby/test/ruby/enc/test_big5.rb28
-rw-r--r--jni/ruby/test/ruby/enc/test_cp949.rb28
-rw-r--r--jni/ruby/test/ruby/enc/test_emoji.rb442
-rw-r--r--jni/ruby/test/ruby/enc/test_euc_jp.rb24
-rw-r--r--jni/ruby/test/ruby/enc/test_euc_kr.rb36
-rw-r--r--jni/ruby/test/ruby/enc/test_euc_tw.rb28
-rw-r--r--jni/ruby/test/ruby/enc/test_gb18030.rb126
-rw-r--r--jni/ruby/test/ruby/enc/test_gbk.rb28
-rw-r--r--jni/ruby/test/ruby/enc/test_iso_8859.rb163
-rw-r--r--jni/ruby/test/ruby/enc/test_koi8.rb22
-rw-r--r--jni/ruby/test/ruby/enc/test_shift_jis.rb27
-rw-r--r--jni/ruby/test/ruby/enc/test_utf16.rb384
-rw-r--r--jni/ruby/test/ruby/enc/test_utf32.rb93
-rw-r--r--jni/ruby/test/ruby/enc/test_windows_1251.rb16
-rw-r--r--jni/ruby/test/ruby/endblockwarn_rb12
-rw-r--r--jni/ruby/test/ruby/lbtest.rb49
-rw-r--r--jni/ruby/test/ruby/marshaltestlib.rb436
-rw-r--r--jni/ruby/test/ruby/sentence.rb668
-rw-r--r--jni/ruby/test/ruby/test_alias.rb196
-rw-r--r--jni/ruby/test/ruby/test_argf.rb859
-rw-r--r--jni/ruby/test/ruby/test_arity.rb69
-rw-r--r--jni/ruby/test/ruby/test_array.rb2533
-rw-r--r--jni/ruby/test/ruby/test_assignment.rb760
-rw-r--r--jni/ruby/test/ruby/test_autoload.rb207
-rw-r--r--jni/ruby/test/ruby/test_backtrace.rb246
-rw-r--r--jni/ruby/test/ruby/test_basicinstructions.rb700
-rw-r--r--jni/ruby/test/ruby/test_beginendblock.rb190
-rw-r--r--jni/ruby/test/ruby/test_bignum.rb711
-rw-r--r--jni/ruby/test/ruby/test_call.rb34
-rw-r--r--jni/ruby/test/ruby/test_case.rb124
-rw-r--r--jni/ruby/test/ruby/test_class.rb402
-rw-r--r--jni/ruby/test/ruby/test_clone.rb28
-rw-r--r--jni/ruby/test/ruby/test_comparable.rb94
-rw-r--r--jni/ruby/test/ruby/test_complex.rb1014
-rw-r--r--jni/ruby/test/ruby/test_complex2.rb735
-rw-r--r--jni/ruby/test/ruby/test_complexrational.rb407
-rw-r--r--jni/ruby/test/ruby/test_condition.rb16
-rw-r--r--jni/ruby/test/ruby/test_const.rb72
-rw-r--r--jni/ruby/test/ruby/test_continuation.rb134
-rw-r--r--jni/ruby/test/ruby/test_defined.rb211
-rw-r--r--jni/ruby/test/ruby/test_dir.rb328
-rw-r--r--jni/ruby/test/ruby/test_dir_m17n.rb389
-rw-r--r--jni/ruby/test/ruby/test_econv.rb926
-rw-r--r--jni/ruby/test/ruby/test_encoding.rb123
-rw-r--r--jni/ruby/test/ruby/test_enum.rb733
-rw-r--r--jni/ruby/test/ruby/test_enumerator.rb628
-rw-r--r--jni/ruby/test/ruby/test_env.rb552
-rw-r--r--jni/ruby/test/ruby/test_eval.rb510
-rw-r--r--jni/ruby/test/ruby/test_exception.rb701
-rw-r--r--jni/ruby/test/ruby/test_fiber.rb347
-rw-r--r--jni/ruby/test/ruby/test_file.rb423
-rw-r--r--jni/ruby/test/ruby/test_file_exhaustive.rb1238
-rw-r--r--jni/ruby/test/ruby/test_fixnum.rb315
-rw-r--r--jni/ruby/test/ruby/test_flip.rb41
-rw-r--r--jni/ruby/test/ruby/test_float.rb683
-rw-r--r--jni/ruby/test/ruby/test_fnmatch.rb131
-rw-r--r--jni/ruby/test/ruby/test_gc.rb379
-rw-r--r--jni/ruby/test/ruby/test_hash.rb1300
-rw-r--r--jni/ruby/test/ruby/test_ifunless.rb14
-rw-r--r--jni/ruby/test/ruby/test_integer.rb280
-rw-r--r--jni/ruby/test/ruby/test_integer_comb.rb631
-rw-r--r--jni/ruby/test/ruby/test_io.rb3171
-rw-r--r--jni/ruby/test/ruby/test_io_m17n.rb2566
-rw-r--r--jni/ruby/test/ruby/test_iseq.rb144
-rw-r--r--jni/ruby/test/ruby/test_iterator.rb497
-rw-r--r--jni/ruby/test/ruby/test_keyword.rb579
-rw-r--r--jni/ruby/test/ruby/test_lambda.rb173
-rw-r--r--jni/ruby/test/ruby/test_lazy_enumerator.rb494
-rw-r--r--jni/ruby/test/ruby/test_literal.rb461
-rw-r--r--jni/ruby/test/ruby/test_m17n.rb1599
-rw-r--r--jni/ruby/test/ruby/test_m17n_comb.rb1638
-rw-r--r--jni/ruby/test/ruby/test_marshal.rb650
-rw-r--r--jni/ruby/test/ruby/test_math.rb292
-rw-r--r--jni/ruby/test/ruby/test_metaclass.rb167
-rw-r--r--jni/ruby/test/ruby/test_method.rb894
-rw-r--r--jni/ruby/test/ruby/test_mixed_unicode_escapes.rb25
-rw-r--r--jni/ruby/test/ruby/test_module.rb2032
-rw-r--r--jni/ruby/test/ruby/test_not.rb12
-rw-r--r--jni/ruby/test/ruby/test_notimp.rb84
-rw-r--r--jni/ruby/test/ruby/test_numeric.rb366
-rw-r--r--jni/ruby/test/ruby/test_object.rb861
-rw-r--r--jni/ruby/test/ruby/test_objectspace.rb112
-rw-r--r--jni/ruby/test/ruby/test_optimization.rb292
-rw-r--r--jni/ruby/test/ruby/test_pack.rb730
-rw-r--r--jni/ruby/test/ruby/test_parse.rb882
-rw-r--r--jni/ruby/test/ruby/test_path.rb259
-rw-r--r--jni/ruby/test/ruby/test_pipe.rb29
-rw-r--r--jni/ruby/test/ruby/test_primitive.rb423
-rw-r--r--jni/ruby/test/ruby/test_proc.rb1323
-rw-r--r--jni/ruby/test/ruby/test_process.rb2023
-rw-r--r--jni/ruby/test/ruby/test_rand.rb527
-rw-r--r--jni/ruby/test/ruby/test_range.rb583
-rw-r--r--jni/ruby/test/ruby/test_rational.rb927
-rw-r--r--jni/ruby/test/ruby/test_rational2.rb1386
-rw-r--r--jni/ruby/test/ruby/test_readpartial.rb72
-rw-r--r--jni/ruby/test/ruby/test_refinement.rb1450
-rw-r--r--jni/ruby/test/ruby/test_regexp.rb1102
-rw-r--r--jni/ruby/test/ruby/test_require.rb704
-rw-r--r--jni/ruby/test/ruby/test_rubyoptions.rb796
-rw-r--r--jni/ruby/test/ruby/test_rubyvm.rb17
-rw-r--r--jni/ruby/test/ruby/test_settracefunc.rb1355
-rw-r--r--jni/ruby/test/ruby/test_signal.rb294
-rw-r--r--jni/ruby/test/ruby/test_sleep.rb16
-rw-r--r--jni/ruby/test/ruby/test_sprintf.rb412
-rw-r--r--jni/ruby/test/ruby/test_sprintf_comb.rb553
-rw-r--r--jni/ruby/test/ruby/test_string.rb2314
-rw-r--r--jni/ruby/test/ruby/test_stringchar.rb181
-rw-r--r--jni/ruby/test/ruby/test_struct.rb374
-rw-r--r--jni/ruby/test/ruby/test_super.rb528
-rw-r--r--jni/ruby/test/ruby/test_symbol.rb289
-rw-r--r--jni/ruby/test/ruby/test_syntax.rb622
-rw-r--r--jni/ruby/test/ruby/test_system.rb162
-rw-r--r--jni/ruby/test/ruby/test_thread.rb1036
-rw-r--r--jni/ruby/test/ruby/test_threadgroup.rb57
-rw-r--r--jni/ruby/test/ruby/test_time.rb1049
-rw-r--r--jni/ruby/test/ruby/test_time_tz.rb415
-rw-r--r--jni/ruby/test/ruby/test_trace.rb61
-rw-r--r--jni/ruby/test/ruby/test_transcode.rb2121
-rw-r--r--jni/ruby/test/ruby/test_undef.rb37
-rw-r--r--jni/ruby/test/ruby/test_unicode_escape.rb270
-rw-r--r--jni/ruby/test/ruby/test_variable.rb121
-rw-r--r--jni/ruby/test/ruby/test_weakmap.rb133
-rw-r--r--jni/ruby/test/ruby/test_whileuntil.rb82
-rw-r--r--jni/ruby/test/ruby/test_yield.rb393
-rw-r--r--jni/ruby/test/ruby/ut_eof.rb128
-rw-r--r--jni/ruby/test/rubygems/alternate_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/alternate_cert_32.pem18
-rw-r--r--jni/ruby/test/rubygems/alternate_key.pem27
-rw-r--r--jni/ruby/test/rubygems/bad_rake.rb1
-rw-r--r--jni/ruby/test/rubygems/bogussources.rb8
-rw-r--r--jni/ruby/test/rubygems/ca_cert.pem68
-rw-r--r--jni/ruby/test/rubygems/child_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/child_cert_32.pem18
-rw-r--r--jni/ruby/test/rubygems/child_key.pem27
-rw-r--r--jni/ruby/test/rubygems/client.pem49
-rw-r--r--jni/ruby/test/rubygems/data/gem-private_key.pem27
-rw-r--r--jni/ruby/test/rubygems/data/gem-public_cert.pem20
-rw-r--r--jni/ruby/test/rubygems/data/null-type.gemspec.rzbin0 -> 554 bytes
-rw-r--r--jni/ruby/test/rubygems/encrypted_private_key.pem30
-rw-r--r--jni/ruby/test/rubygems/expired_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/fake_certlib/openssl.rb7
-rw-r--r--jni/ruby/test/rubygems/fix_openssl_warnings.rb12
-rw-r--r--jni/ruby/test/rubygems/foo/discover.rb0
-rw-r--r--jni/ruby/test/rubygems/future_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/future_cert_32.pem18
-rw-r--r--jni/ruby/test/rubygems/good_rake.rb1
-rw-r--r--jni/ruby/test/rubygems/grandchild_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/grandchild_cert_32.pem18
-rw-r--r--jni/ruby/test/rubygems/grandchild_key.pem27
-rw-r--r--jni/ruby/test/rubygems/invalid_client.pem49
-rw-r--r--jni/ruby/test/rubygems/invalid_issuer_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/invalid_issuer_cert_32.pem18
-rw-r--r--jni/ruby/test/rubygems/invalid_key.pem27
-rw-r--r--jni/ruby/test/rubygems/invalid_signer_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/invalid_signer_cert_32.pem18
-rw-r--r--jni/ruby/test/rubygems/invalidchild_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/invalidchild_cert_32.pem18
-rw-r--r--jni/ruby/test/rubygems/invalidchild_key.pem27
-rw-r--r--jni/ruby/test/rubygems/plugin/exception/rubygems_plugin.rb2
-rw-r--r--jni/ruby/test/rubygems/plugin/load/rubygems_plugin.rb3
-rw-r--r--jni/ruby/test/rubygems/plugin/standarderror/rubygems_plugin.rb2
-rw-r--r--jni/ruby/test/rubygems/private_key.pem27
-rw-r--r--jni/ruby/test/rubygems/public_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/public_cert_32.pem18
-rw-r--r--jni/ruby/test/rubygems/public_key.pem9
-rw-r--r--jni/ruby/test/rubygems/rubygems/commands/crash_command.rb5
-rw-r--r--jni/ruby/test/rubygems/rubygems_plugin.rb21
-rw-r--r--jni/ruby/test/rubygems/sff/discover.rb0
-rw-r--r--jni/ruby/test/rubygems/simple_gem.rb66
-rw-r--r--jni/ruby/test/rubygems/specifications/bar-0.0.2.gemspec9
-rw-r--r--jni/ruby/test/rubygems/specifications/foo-0.0.1.gemspecbin0 -> 269 bytes
-rw-r--r--jni/ruby/test/rubygems/ssl_cert.pem19
-rw-r--r--jni/ruby/test/rubygems/ssl_key.pem15
-rw-r--r--jni/ruby/test/rubygems/test_bundled_ca.rb60
-rw-r--r--jni/ruby/test/rubygems/test_config.rb14
-rw-r--r--jni/ruby/test/rubygems/test_deprecate.rb76
-rw-r--r--jni/ruby/test/rubygems/test_gem.rb1576
-rw-r--r--jni/ruby/test/rubygems/test_gem_available_set.rb128
-rw-r--r--jni/ruby/test/rubygems/test_gem_command.rb243
-rw-r--r--jni/ruby/test/rubygems/test_gem_command_manager.rb263
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_build_command.rb110
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_cert_command.rb670
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_check_command.rb68
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_cleanup_command.rb167
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_contents_command.rb239
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_dependency_command.rb221
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_environment_command.rb153
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_fetch_command.rb126
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_generate_index_command.rb50
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_help_command.rb74
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_install_command.rb997
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_list_command.rb33
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_lock_command.rb68
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_mirror.rb32
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_open_command.rb46
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_outdated_command.rb33
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_owner_command.rb204
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_pristine_command.rb462
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_push_command.rb262
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_query_command.rb668
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_search_command.rb17
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_server_command.rb59
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_setup_command.rb136
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_sources_command.rb248
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_specification_command.rb250
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_stale_command.rb40
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_uninstall_command.rb281
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_unpack_command.rb210
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_update_command.rb543
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_which_command.rb85
-rw-r--r--jni/ruby/test/rubygems/test_gem_commands_yank_command.rb97
-rw-r--r--jni/ruby/test/rubygems/test_gem_config_file.rb467
-rw-r--r--jni/ruby/test/rubygems/test_gem_dependency.rb357
-rw-r--r--jni/ruby/test/rubygems/test_gem_dependency_installer.rb1208
-rw-r--r--jni/ruby/test/rubygems/test_gem_dependency_list.rb259
-rw-r--r--jni/ruby/test/rubygems/test_gem_dependency_resolution_error.rb28
-rw-r--r--jni/ruby/test/rubygems/test_gem_doctor.rb168
-rw-r--r--jni/ruby/test/rubygems/test_gem_ext_builder.rb334
-rw-r--r--jni/ruby/test/rubygems/test_gem_ext_cmake_builder.rb84
-rw-r--r--jni/ruby/test/rubygems/test_gem_ext_configure_builder.rb82
-rw-r--r--jni/ruby/test/rubygems/test_gem_ext_ext_conf_builder.rb206
-rw-r--r--jni/ruby/test/rubygems/test_gem_ext_rake_builder.rb64
-rw-r--r--jni/ruby/test/rubygems/test_gem_gem_runner.rb68
-rw-r--r--jni/ruby/test/rubygems/test_gem_gemcutter_utilities.rb234
-rw-r--r--jni/ruby/test/rubygems/test_gem_impossible_dependencies_error.rb61
-rw-r--r--jni/ruby/test/rubygems/test_gem_indexer.rb366
-rw-r--r--jni/ruby/test/rubygems/test_gem_install_update_options.rb184
-rw-r--r--jni/ruby/test/rubygems/test_gem_installer.rb1533
-rw-r--r--jni/ruby/test/rubygems/test_gem_local_remote_options.rb133
-rw-r--r--jni/ruby/test/rubygems/test_gem_name_tuple.rb44
-rw-r--r--jni/ruby/test/rubygems/test_gem_package.rb824
-rw-r--r--jni/ruby/test/rubygems/test_gem_package_old.rb89
-rw-r--r--jni/ruby/test/rubygems/test_gem_package_tar_header.rb146
-rw-r--r--jni/ruby/test/rubygems/test_gem_package_tar_reader.rb89
-rw-r--r--jni/ruby/test/rubygems/test_gem_package_tar_reader_entry.rb134
-rw-r--r--jni/ruby/test/rubygems/test_gem_package_tar_writer.rb254
-rw-r--r--jni/ruby/test/rubygems/test_gem_package_task.rb80
-rw-r--r--jni/ruby/test/rubygems/test_gem_path_support.rb84
-rw-r--r--jni/ruby/test/rubygems/test_gem_platform.rb296
-rw-r--r--jni/ruby/test/rubygems/test_gem_rdoc.rb269
-rw-r--r--jni/ruby/test/rubygems/test_gem_remote_fetcher.rb987
-rw-r--r--jni/ruby/test/rubygems/test_gem_request.rb362
-rw-r--r--jni/ruby/test/rubygems/test_gem_request_connection_pools.rb120
-rw-r--r--jni/ruby/test/rubygems/test_gem_request_set.rb597
-rw-r--r--jni/ruby/test/rubygems/test_gem_request_set_gem_dependency_api.rb823
-rw-r--r--jni/ruby/test/rubygems/test_gem_request_set_lockfile.rb1276
-rw-r--r--jni/ruby/test/rubygems/test_gem_requirement.rb376
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver.rb733
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_activation_request.rb73
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_api_set.rb208
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_api_specification.rb144
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_best_set.rb137
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_composed_set.rb45
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_conflict.rb87
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_dependency_request.rb84
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_git_set.rb189
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_git_specification.rb112
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_index_set.rb89
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_index_specification.rb89
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_installed_specification.rb49
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_installer_set.rb261
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_local_specification.rb45
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_lock_set.rb63
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_lock_specification.rb98
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_requirement_list.rb20
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_specification.rb64
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_vendor_set.rb83
-rw-r--r--jni/ruby/test/rubygems/test_gem_resolver_vendor_specification.rb83
-rw-r--r--jni/ruby/test/rubygems/test_gem_security.rb306
-rw-r--r--jni/ruby/test/rubygems/test_gem_security_policy.rb540
-rw-r--r--jni/ruby/test/rubygems/test_gem_security_signer.rb208
-rw-r--r--jni/ruby/test/rubygems/test_gem_security_trust_dir.rb98
-rw-r--r--jni/ruby/test/rubygems/test_gem_server.rb404
-rw-r--r--jni/ruby/test/rubygems/test_gem_silent_ui.rb116
-rw-r--r--jni/ruby/test/rubygems/test_gem_source.rb241
-rw-r--r--jni/ruby/test/rubygems/test_gem_source_fetch_problem.rb19
-rw-r--r--jni/ruby/test/rubygems/test_gem_source_git.rb308
-rw-r--r--jni/ruby/test/rubygems/test_gem_source_installed.rb36
-rw-r--r--jni/ruby/test/rubygems/test_gem_source_list.rb111
-rw-r--r--jni/ruby/test/rubygems/test_gem_source_local.rb106
-rw-r--r--jni/ruby/test/rubygems/test_gem_source_lock.rb114
-rw-r--r--jni/ruby/test/rubygems/test_gem_source_specific_file.rb75
-rw-r--r--jni/ruby/test/rubygems/test_gem_source_vendor.rb31
-rw-r--r--jni/ruby/test/rubygems/test_gem_spec_fetcher.rb310
-rw-r--r--jni/ruby/test/rubygems/test_gem_specification.rb3138
-rw-r--r--jni/ruby/test/rubygems/test_gem_stream_ui.rb238
-rw-r--r--jni/ruby/test/rubygems/test_gem_stub_specification.rb199
-rw-r--r--jni/ruby/test/rubygems/test_gem_text.rb74
-rw-r--r--jni/ruby/test/rubygems/test_gem_uninstaller.rb483
-rw-r--r--jni/ruby/test/rubygems/test_gem_unsatisfiable_dependency_error.rb32
-rw-r--r--jni/ruby/test/rubygems/test_gem_uri_formatter.rb28
-rw-r--r--jni/ruby/test/rubygems/test_gem_util.rb31
-rw-r--r--jni/ruby/test/rubygems/test_gem_validator.rb45
-rw-r--r--jni/ruby/test/rubygems/test_gem_version.rb213
-rw-r--r--jni/ruby/test/rubygems/test_gem_version_option.rb151
-rw-r--r--jni/ruby/test/rubygems/test_kernel.rb85
-rw-r--r--jni/ruby/test/rubygems/test_require.rb275
-rw-r--r--jni/ruby/test/rubygems/wrong_key_cert.pem18
-rw-r--r--jni/ruby/test/rubygems/wrong_key_cert_32.pem18
-rw-r--r--jni/ruby/test/runner.rb45
-rw-r--r--jni/ruby/test/scanf/data.txt6
-rw-r--r--jni/ruby/test/scanf/test_scanf.rb303
-rw-r--r--jni/ruby/test/scanf/test_scanfblocks.rb81
-rw-r--r--jni/ruby/test/scanf/test_scanfio.rb20
-rw-r--r--jni/ruby/test/sdbm/test_sdbm.rb541
-rw-r--r--jni/ruby/test/shell/test_command_processor.rb68
-rw-r--r--jni/ruby/test/socket/test_addrinfo.rb651
-rw-r--r--jni/ruby/test/socket/test_ancdata.rb66
-rw-r--r--jni/ruby/test/socket/test_basicsocket.rb88
-rw-r--r--jni/ruby/test/socket/test_nonblock.rb297
-rw-r--r--jni/ruby/test/socket/test_socket.rb654
-rw-r--r--jni/ruby/test/socket/test_sockopt.rb65
-rw-r--r--jni/ruby/test/socket/test_tcp.rb79
-rw-r--r--jni/ruby/test/socket/test_udp.rb72
-rw-r--r--jni/ruby/test/socket/test_unix.rb666
-rw-r--r--jni/ruby/test/stringio/test_stringio.rb589
-rw-r--r--jni/ruby/test/strscan/test_stringscanner.rb720
-rw-r--r--jni/ruby/test/syslog/test_syslog_logger.rb572
-rw-r--r--jni/ruby/test/test_abbrev.rb54
-rw-r--r--jni/ruby/test/test_cmath.rb16
-rw-r--r--jni/ruby/test/test_delegate.rb240
-rw-r--r--jni/ruby/test/test_find.rb301
-rw-r--r--jni/ruby/test/test_ipaddr.rb271
-rw-r--r--jni/ruby/test/test_mathn.rb119
-rw-r--r--jni/ruby/test/test_mutex_m.rb26
-rw-r--r--jni/ruby/test/test_open3.rb268
-rw-r--r--jni/ruby/test/test_pp.rb195
-rw-r--r--jni/ruby/test/test_prettyprint.rb519
-rw-r--r--jni/ruby/test/test_prime.rb174
-rw-r--r--jni/ruby/test/test_pstore.rb144
-rw-r--r--jni/ruby/test/test_pty.rb241
-rw-r--r--jni/ruby/test/test_rbconfig.rb53
-rw-r--r--jni/ruby/test/test_securerandom.rb185
-rw-r--r--jni/ruby/test/test_set.rb697
-rw-r--r--jni/ruby/test/test_shellwords.rb61
-rw-r--r--jni/ruby/test/test_singleton.rb103
-rw-r--r--jni/ruby/test/test_syslog.rb189
-rw-r--r--jni/ruby/test/test_tempfile.rb343
-rw-r--r--jni/ruby/test/test_time.rb500
-rw-r--r--jni/ruby/test/test_timeout.rb100
-rw-r--r--jni/ruby/test/test_tmpdir.rb39
-rw-r--r--jni/ruby/test/test_tracer.rb60
-rw-r--r--jni/ruby/test/test_tsort.rb113
-rw-r--r--jni/ruby/test/test_unicode_normalize.rb184
-rw-r--r--jni/ruby/test/test_weakref.rb71
-rw-r--r--jni/ruby/test/test_win32api.rb23
-rw-r--r--jni/ruby/test/testunit/test4test_hideskip.rb9
-rw-r--r--jni/ruby/test/testunit/test4test_redefinition.rb13
-rw-r--r--jni/ruby/test/testunit/test4test_sorting.rb17
-rw-r--r--jni/ruby/test/testunit/test_assertion.rb8
-rw-r--r--jni/ruby/test/testunit/test_hideskip.rb16
-rw-r--r--jni/ruby/test/testunit/test_parallel.rb191
-rw-r--r--jni/ruby/test/testunit/test_rake_integration.rb35
-rw-r--r--jni/ruby/test/testunit/test_redefinition.rb15
-rw-r--r--jni/ruby/test/testunit/test_sorting.rb17
-rw-r--r--jni/ruby/test/testunit/tests_for_parallel/ptest_first.rb7
-rw-r--r--jni/ruby/test/testunit/tests_for_parallel/ptest_forth.rb29
-rw-r--r--jni/ruby/test/testunit/tests_for_parallel/ptest_second.rb11
-rw-r--r--jni/ruby/test/testunit/tests_for_parallel/ptest_third.rb10
-rw-r--r--jni/ruby/test/testunit/tests_for_parallel/runner.rb13
-rw-r--r--jni/ruby/test/thread/test_cv.rb221
-rw-r--r--jni/ruby/test/thread/test_queue.rb280
-rw-r--r--jni/ruby/test/thread/test_sync.rb63
-rw-r--r--jni/ruby/test/uri/test_common.rb174
-rw-r--r--jni/ruby/test/uri/test_ftp.rb66
-rw-r--r--jni/ruby/test/uri/test_generic.rb847
-rw-r--r--jni/ruby/test/uri/test_http.rb64
-rw-r--r--jni/ruby/test/uri/test_ldap.rb100
-rw-r--r--jni/ruby/test/uri/test_mailto.rb136
-rw-r--r--jni/ruby/test/uri/test_parser.rb47
-rw-r--r--jni/ruby/test/webrick/.htaccess1
-rw-r--r--jni/ruby/test/webrick/test_cgi.rb151
-rw-r--r--jni/ruby/test/webrick/test_cookie.rb131
-rw-r--r--jni/ruby/test/webrick/test_filehandler.rb309
-rw-r--r--jni/ruby/test/webrick/test_htmlutils.rb20
-rw-r--r--jni/ruby/test/webrick/test_httpauth.rb198
-rw-r--r--jni/ruby/test/webrick/test_httpproxy.rb284
-rw-r--r--jni/ruby/test/webrick/test_httprequest.rb411
-rw-r--r--jni/ruby/test/webrick/test_httpresponse.rb149
-rw-r--r--jni/ruby/test/webrick/test_httpserver.rb412
-rw-r--r--jni/ruby/test/webrick/test_httputils.rb100
-rw-r--r--jni/ruby/test/webrick/test_httpversion.rb40
-rw-r--r--jni/ruby/test/webrick/test_server.rb131
-rw-r--r--jni/ruby/test/webrick/test_utils.rb64
-rw-r--r--jni/ruby/test/webrick/utils.rb70
-rw-r--r--jni/ruby/test/webrick/webrick.cgi36
-rw-r--r--jni/ruby/test/webrick/webrick_long_filename.cgi36
-rw-r--r--jni/ruby/test/win32ole/err_in_callback.rb9
-rw-r--r--jni/ruby/test/win32ole/orig_data.csv5
-rw-r--r--jni/ruby/test/win32ole/test_err_in_callback.rb55
-rw-r--r--jni/ruby/test/win32ole/test_folderitem2_invokeverb.rb65
-rw-r--r--jni/ruby/test/win32ole/test_nil2vtempty.rb36
-rw-r--r--jni/ruby/test/win32ole/test_ole_methods.rb36
-rw-r--r--jni/ruby/test/win32ole/test_propertyputref.rb30
-rw-r--r--jni/ruby/test/win32ole/test_thread.rb33
-rw-r--r--jni/ruby/test/win32ole/test_win32ole.rb564
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_event.rb391
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_method.rb146
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_param.rb106
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_record.rb313
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_type.rb249
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_typelib.rb116
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_variable.rb61
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_variant.rb725
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_variant_m.rb35
-rw-r--r--jni/ruby/test/win32ole/test_win32ole_variant_outarg.rb68
-rw-r--r--jni/ruby/test/win32ole/test_word.rb71
-rw-r--r--jni/ruby/test/xmlrpc/data/blog.xml18
-rw-r--r--jni/ruby/test/xmlrpc/data/bug_bool.expected3
-rw-r--r--jni/ruby/test/xmlrpc/data/bug_bool.xml8
-rw-r--r--jni/ruby/test/xmlrpc/data/bug_cdata.expected3
-rw-r--r--jni/ruby/test/xmlrpc/data/bug_cdata.xml8
-rw-r--r--jni/ruby/test/xmlrpc/data/bug_covert.expected10
-rw-r--r--jni/ruby/test/xmlrpc/data/bug_covert.xml6
-rw-r--r--jni/ruby/test/xmlrpc/data/datetime_iso8601.xml8
-rw-r--r--jni/ruby/test/xmlrpc/data/fault.xml16
-rw-r--r--jni/ruby/test/xmlrpc/data/value.expected7
-rw-r--r--jni/ruby/test/xmlrpc/data/value.xml22
-rw-r--r--jni/ruby/test/xmlrpc/data/xml1.expected243
-rw-r--r--jni/ruby/test/xmlrpc/data/xml1.xml1
-rw-r--r--jni/ruby/test/xmlrpc/htpasswd2
-rw-r--r--jni/ruby/test/xmlrpc/test_client.rb317
-rw-r--r--jni/ruby/test/xmlrpc/test_cookie.rb96
-rw-r--r--jni/ruby/test/xmlrpc/test_datetime.rb161
-rw-r--r--jni/ruby/test/xmlrpc/test_features.rb50
-rw-r--r--jni/ruby/test/xmlrpc/test_marshal.rb110
-rw-r--r--jni/ruby/test/xmlrpc/test_parser.rb93
-rw-r--r--jni/ruby/test/xmlrpc/test_webrick_server.rb135
-rw-r--r--jni/ruby/test/xmlrpc/webrick_testing.rb55
-rw-r--r--jni/ruby/test/zlib/test_zlib.rb1094
1110 files changed, 268613 insertions, 0 deletions
diff --git a/jni/ruby/test/-ext-/array/test_resize.rb b/jni/ruby/test/-ext-/array/test_resize.rb
new file mode 100644
index 0000000..8e526b5
--- /dev/null
+++ b/jni/ruby/test/-ext-/array/test_resize.rb
@@ -0,0 +1,29 @@
+require 'test/unit'
+require '-test-/array/resize'
+
+class TestArray < Test::Unit::TestCase
+ class TestResize < Test::Unit::TestCase
+ def test_expand
+ feature = '[ruby-dev:42912]'
+ ary = [*1..10]
+ ary.__resize__(10)
+ assert_equal(10, ary.size, feature)
+ assert_equal([*1..10], ary, feature)
+ ary.__resize__(100)
+ assert_equal(100, ary.size, feature)
+ assert_equal([*1..10]+[nil]*90, ary, feature)
+ ary.__resize__(20)
+ assert_equal(20, ary.size, feature)
+ assert_equal([*1..10]+[nil]*10, ary, feature)
+ ary.__resize__(2)
+ assert_equal(2, ary.size, feature)
+ assert_equal([1,2], ary, feature)
+ ary.__resize__(3)
+ assert_equal(3, ary.size, feature)
+ assert_equal([1,2,nil], ary, feature)
+ ary.__resize__(10)
+ assert_equal(10, ary.size, feature)
+ assert_equal([1,2]+[nil]*8, ary, feature)
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/bignum/test_big2str.rb b/jni/ruby/test/-ext-/bignum/test_big2str.rb
new file mode 100644
index 0000000..0af552e
--- /dev/null
+++ b/jni/ruby/test/-ext-/bignum/test_big2str.rb
@@ -0,0 +1,29 @@
+require 'test/unit'
+require "-test-/bignum"
+
+class TestBignum < Test::Unit::TestCase
+ class TestBig2str < Test::Unit::TestCase
+
+ SIZEOF_BDIGIT = Bignum::SIZEOF_BDIGIT
+ BITSPERDIG = Bignum::BITSPERDIG
+ BDIGMAX = (1 << BITSPERDIG) - 1
+
+ def test_big2str_generic
+ x = 10**1000
+ assert_equal("1" + "0" * 1000, x.big2str_generic(10))
+ end
+
+ def test_big2str_poweroftwo
+ e = BITSPERDIG*2
+ x = 0b10**e
+ assert_equal("1" + "0" * e, x.big2str_poweroftwo(2))
+ end
+
+ def test_big2str_gmp
+ x = 10**1000
+ assert_equal("1" + "0" * 1000, x.big2str_gmp(10))
+ rescue NotImplementedError
+ end
+
+ end
+end
diff --git a/jni/ruby/test/-ext-/bignum/test_bigzero.rb b/jni/ruby/test/-ext-/bignum/test_bigzero.rb
new file mode 100644
index 0000000..f75c459
--- /dev/null
+++ b/jni/ruby/test/-ext-/bignum/test_bigzero.rb
@@ -0,0 +1,13 @@
+require 'test/unit'
+require "-test-/bignum"
+
+class TestBignum < Test::Unit::TestCase
+ class TestBigZero < Test::Unit::TestCase
+ def test_equal_0
+ bug8204 = '[ruby-core:53893] [Bug #8204]'
+ (0..10).each do |i|
+ assert_equal(0, Bug::Bignum.zero(i), "#{bug8204} Bignum.zero(#{i})")
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/bignum/test_div.rb b/jni/ruby/test/-ext-/bignum/test_div.rb
new file mode 100644
index 0000000..9c1a3c3
--- /dev/null
+++ b/jni/ruby/test/-ext-/bignum/test_div.rb
@@ -0,0 +1,28 @@
+require 'test/unit'
+require "-test-/bignum"
+
+class TestBignum < Test::Unit::TestCase
+ class TestDiv < Test::Unit::TestCase
+
+ SIZEOF_BDIGIT = Bignum::SIZEOF_BDIGIT
+ BITSPERDIG = Bignum::BITSPERDIG
+ BDIGMAX = (1 << BITSPERDIG) - 1
+
+ def test_divrem_normal
+ x = (1 << (BITSPERDIG*2)) | (2 << BITSPERDIG) | 3
+ y = (1 << BITSPERDIG) | 1
+ q = (1 << BITSPERDIG) | 1
+ r = 2
+ assert_equal([q, r], x.big_divrem_normal(y))
+ end
+
+ def test_divrem_gmp
+ x = (1 << (BITSPERDIG*2)) | (2 << BITSPERDIG) | 3
+ y = (1 << BITSPERDIG) | 1
+ q = (1 << BITSPERDIG) | 1
+ r = 2
+ assert_equal([q, r], x.big_divrem_gmp(y))
+ rescue NotImplementedError
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/bignum/test_mul.rb b/jni/ruby/test/-ext-/bignum/test_mul.rb
new file mode 100644
index 0000000..3e78247
--- /dev/null
+++ b/jni/ruby/test/-ext-/bignum/test_mul.rb
@@ -0,0 +1,137 @@
+require 'test/unit'
+require "-test-/bignum"
+
+class TestBignum < Test::Unit::TestCase
+ class TestMul < Test::Unit::TestCase
+
+ SIZEOF_BDIGIT = Bignum::SIZEOF_BDIGIT
+ BITSPERDIG = Bignum::BITSPERDIG
+ BDIGMAX = (1 << BITSPERDIG) - 1
+
+ def test_mul_normal
+ x = (1 << BITSPERDIG) | 1
+ y = (1 << BITSPERDIG) | 1
+ z = (1 << (BITSPERDIG*2)) | (2 << BITSPERDIG) | 1
+ assert_equal(z, x.big_mul_normal(y))
+ end
+
+ def test_mul_normal_zero_in_x
+ x = (1 << (2*BITSPERDIG)) | 1
+ y = (1 << BITSPERDIG) | 1
+ z = (1 << (BITSPERDIG*3)) | (1 << (BITSPERDIG*2)) | (1 << BITSPERDIG) | 1
+ assert_equal(z, x.big_mul_normal(y))
+ end
+
+ def test_mul_normal_zero_in_y
+ x = (1 << BITSPERDIG) | 1
+ y = (1 << (2*BITSPERDIG)) | 1
+ z = (1 << (BITSPERDIG*3)) | (1 << (BITSPERDIG*2)) | (1 << BITSPERDIG) | 1
+ assert_equal(z, x.big_mul_normal(y))
+ end
+
+ def test_mul_normal_max_max
+ x = (1 << (2*BITSPERDIG)) - 1
+ y = (1 << (2*BITSPERDIG)) - 1
+ z = (1 << (4*BITSPERDIG)) - (1 << (2*BITSPERDIG+1)) + 1
+ assert_equal(z, x.big_mul_normal(y))
+ end
+
+ def test_sq_fast
+ x = (1 << BITSPERDIG) | 1
+ z = (1 << 2*BITSPERDIG) | (2 << BITSPERDIG) | 1
+ assert_equal(z, x.big_sq_fast)
+ end
+
+ def test_sq_fast_max2
+ x = (BDIGMAX << BITSPERDIG) | BDIGMAX
+ assert_equal(x.big_mul_normal(x), x.big_sq_fast)
+ end
+
+ def test_sq_fast_zero_in_middle
+ x = (BDIGMAX << 2*BITSPERDIG) | BDIGMAX
+ assert_equal(x.big_mul_normal(x), x.big_sq_fast)
+ end
+
+ def test_mul_balance
+ x = (1 << BITSPERDIG) | 1
+ y = (1 << BITSPERDIG) | 1
+ z = (1 << (BITSPERDIG*2)) | (2 << BITSPERDIG) | 1
+ assert_equal(z, x.big_mul_balance(y))
+ end
+
+ def test_mul_balance_2x16
+ x = (1 << Bignum::BITSPERDIG) | 1
+ y = (1 << Bignum::BITSPERDIG*16) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_balance(y))
+ end
+
+ def test_mul_balance_2x17
+ x = (1 << Bignum::BITSPERDIG) | 1
+ y = (1 << Bignum::BITSPERDIG*17) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_balance(y))
+ end
+
+ def test_mul_karatsuba
+ x = (1 << BITSPERDIG) | 1
+ y = (1 << BITSPERDIG) | 1
+ z = (1 << (BITSPERDIG*2)) | (2 << BITSPERDIG) | 1
+ assert_equal(z, x.big_mul_karatsuba(y))
+ end
+
+ def test_mul_karatsuba_odd_y
+ x = (1 << BITSPERDIG) | 1
+ y = (1 << (2*BITSPERDIG)) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_karatsuba(y))
+ end
+
+ def test_mul_karatsuba_odd_xy
+ x = (1 << (2*BITSPERDIG)) | 1
+ y = (1 << (2*BITSPERDIG)) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_karatsuba(y))
+ end
+
+ def test_mul_karatsuba_x1_gt_x0
+ x = (2 << BITSPERDIG) | 1
+ y = (1 << BITSPERDIG) | 2
+ assert_equal(x.big_mul_normal(y), x.big_mul_karatsuba(y))
+ end
+
+ def test_mul_karatsuba_y1_gt_y0
+ x = (1 << BITSPERDIG) | 2
+ y = (2 << BITSPERDIG) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_karatsuba(y))
+ end
+
+ def test_mul_karatsuba_x1_gt_x0_and_y1_gt_y0
+ x = (2 << BITSPERDIG) | 1
+ y = (2 << BITSPERDIG) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_karatsuba(y))
+ end
+
+ def test_mul_karatsuba_carry2
+ x = (1 << BITSPERDIG) | BDIGMAX
+ y = (1 << BITSPERDIG) | BDIGMAX
+ assert_equal(x.big_mul_normal(y), x.big_mul_karatsuba(y))
+ end
+
+ def test_mul_karatsuba_borrow
+ x = (BDIGMAX << BITSPERDIG) | 1
+ y = (BDIGMAX << BITSPERDIG) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_karatsuba(y))
+ end
+
+ def test_mul_toom3
+ x = (1 << 2*BITSPERDIG) | (1 << BITSPERDIG) | 1
+ y = (1 << 2*BITSPERDIG) | (1 << BITSPERDIG) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_toom3(y))
+ end
+
+ def test_mul_gmp
+ x = (1 << 2*BITSPERDIG) | (1 << BITSPERDIG) | 1
+ y = (1 << 2*BITSPERDIG) | (1 << BITSPERDIG) | 1
+ assert_equal(x.big_mul_normal(y), x.big_mul_gmp(y))
+ rescue NotImplementedError
+ end
+
+ end
+end
diff --git a/jni/ruby/test/-ext-/bignum/test_pack.rb b/jni/ruby/test/-ext-/bignum/test_pack.rb
new file mode 100644
index 0000000..f2a3df8
--- /dev/null
+++ b/jni/ruby/test/-ext-/bignum/test_pack.rb
@@ -0,0 +1,398 @@
+# coding: ASCII-8BIT
+
+require 'test/unit'
+require "-test-/bignum"
+
+class TestBignum < Test::Unit::TestCase
+ class TestPack < Test::Unit::TestCase
+
+ MSWORD_FIRST = Integer::INTEGER_PACK_MSWORD_FIRST
+ LSWORD_FIRST = Integer::INTEGER_PACK_LSWORD_FIRST
+ MSBYTE_FIRST = Integer::INTEGER_PACK_MSBYTE_FIRST
+ LSBYTE_FIRST = Integer::INTEGER_PACK_LSBYTE_FIRST
+ NATIVE_BYTE_ORDER = Integer::INTEGER_PACK_NATIVE_BYTE_ORDER
+ TWOCOMP = Integer::INTEGER_PACK_2COMP
+ LITTLE_ENDIAN = Integer::INTEGER_PACK_LITTLE_ENDIAN
+ BIG_ENDIAN = Integer::INTEGER_PACK_BIG_ENDIAN
+ NEGATIVE = Integer::INTEGER_PACK_NEGATIVE
+ GENERIC = Integer::INTEGER_PACK_FORCE_GENERIC_IMPLEMENTATION
+
+ def test_pack_zero
+ assert_equal([0, ""], 0.test_pack(0, 1, 0, BIG_ENDIAN))
+ end
+
+ def test_pack_argument_check
+ assert_raise(ArgumentError) { 0.test_pack_raw("", 2, 1, 0, MSBYTE_FIRST) }
+ assert_raise(ArgumentError) { 0.test_pack_raw("", 0, 1, 0, MSWORD_FIRST) }
+ assert_raise(ArgumentError) { 0.test_pack_raw("", 0, 0, 0, BIG_ENDIAN) }
+ assert_raise(ArgumentError) { 0.test_pack_raw("", 0, 1, 8, BIG_ENDIAN) }
+
+ # assume sizeof(ssize_t) == sizeof(intptr_t)
+ assert_raise(ArgumentError) { 0.test_pack_raw("", 1 << ([""].pack("p").length * 8 - 1), 0, BIG_ENDIAN) }
+ end
+
+ def test_pack_wordsize
+ assert_equal([1, "\x01"], 1.test_pack(1, 1, 0, BIG_ENDIAN))
+ assert_equal([1, "\x00\x01"], 1.test_pack(1, 2, 0, BIG_ENDIAN))
+ assert_equal([1, "\x00\x00\x01"], 1.test_pack(1, 3, 0, BIG_ENDIAN))
+ assert_equal([1, "\x01"], 1.test_pack(1, 1, 0, LITTLE_ENDIAN))
+ assert_equal([1, "\x01\x00"], 1.test_pack(1, 2, 0, LITTLE_ENDIAN))
+ assert_equal([1, "\x01\x00\x00"], 1.test_pack(1, 3, 0, LITTLE_ENDIAN))
+ end
+
+ def test_pack_fixed_buffer
+ assert_equal([0, "\x00\x00"], 0.test_pack(2, 1, 0, BIG_ENDIAN))
+ assert_equal([1, "\x00\x01"], 0x01.test_pack(2, 1, 0, BIG_ENDIAN))
+ assert_equal([1, "\x02\x01"], 0x0201.test_pack(2, 1, 0, BIG_ENDIAN))
+ assert_equal([2, "\x02\x01"], 0x030201.test_pack(2, 1, 0, BIG_ENDIAN))
+ assert_equal([2, "\x02\x01"], 0x04030201.test_pack(2, 1, 0, BIG_ENDIAN))
+ assert_equal([0, "\x00\x00"], 0.test_pack(2, 1, 0, LITTLE_ENDIAN))
+ assert_equal([1, "\x01\x00"], 0x01.test_pack(2, 1, 0, LITTLE_ENDIAN))
+ assert_equal([1, "\x01\x02"], 0x0201.test_pack(2, 1, 0, LITTLE_ENDIAN))
+ assert_equal([2, "\x01\x02"], 0x030201.test_pack(2, 1, 0, LITTLE_ENDIAN))
+ assert_equal([2, "\x01\x02"], 0x04030201.test_pack(2, 1, 0, LITTLE_ENDIAN))
+ end
+
+ def test_pack_wordorder_and_endian
+ assert_equal([1, "\x12\x34\x56\x78"], 0x12345678.test_pack(2, 2, 0, MSWORD_FIRST|MSBYTE_FIRST))
+ assert_equal([1, "\x34\x12\x78\x56"], 0x12345678.test_pack(2, 2, 0, MSWORD_FIRST|LSBYTE_FIRST))
+ assert_equal([1, "\x56\x78\x12\x34"], 0x12345678.test_pack(2, 2, 0, LSWORD_FIRST|MSBYTE_FIRST))
+ assert_equal([1, "\x78\x56\x34\x12"], 0x12345678.test_pack(2, 2, 0, LSWORD_FIRST|LSBYTE_FIRST))
+ end
+
+ def test_pack_native_endian
+ assert_equal([1, [0x1234].pack("S!")], 0x1234.test_pack(1, 2, 0, MSWORD_FIRST|NATIVE_BYTE_ORDER))
+ end
+
+ def test_pack_nail
+ assert_equal([1, "\x01\x00\x00\x00\x01\x01"], 0b100011.test_pack(6, 1, 7, BIG_ENDIAN))
+ assert_equal([1, "\x01\x02\x03\x04\x05\x06\x07\x08"], 0x12345678.test_pack(8, 1, 4, BIG_ENDIAN))
+ assert_equal([1, "\x00\x12\x00\x34\x00\x56\x00\x78"], 0x12345678.test_pack(4, 2, 8, BIG_ENDIAN))
+ end
+
+ def test_pack_overflow
+ assert_equal([-2, "\x1"], (-0x11).test_pack(1, 1, 4, BIG_ENDIAN))
+ assert_equal([-2, "\x0"], (-0x10).test_pack(1, 1, 4, BIG_ENDIAN))
+ assert_equal([-1, "\xF"], (-0x0F).test_pack(1, 1, 4, BIG_ENDIAN))
+ assert_equal([+1, "\xF"], (+0x0F).test_pack(1, 1, 4, BIG_ENDIAN))
+ assert_equal([+2, "\x0"], (+0x10).test_pack(1, 1, 4, BIG_ENDIAN))
+ assert_equal([+2, "\x1"], (+0x11).test_pack(1, 1, 4, BIG_ENDIAN))
+
+ assert_equal([-2, "\x01"], (-0x101).test_pack(1, 1, 0, BIG_ENDIAN))
+ assert_equal([-2, "\x00"], (-0x100).test_pack(1, 1, 0, BIG_ENDIAN))
+ assert_equal([-1, "\xFF"], (-0x0FF).test_pack(1, 1, 0, BIG_ENDIAN))
+ assert_equal([+1, "\xFF"], (+0x0FF).test_pack(1, 1, 0, BIG_ENDIAN))
+ assert_equal([+2, "\x00"], (+0x100).test_pack(1, 1, 0, BIG_ENDIAN))
+ assert_equal([+2, "\x01"], (+0x101).test_pack(1, 1, 0, BIG_ENDIAN))
+
+ assert_equal([-2, "\x00\x00\x00\x00\x00\x00\x00\x01"], (-0x10000000000000001).test_pack(2, 4, 0, BIG_ENDIAN))
+ assert_equal([-2, "\x00\x00\x00\x00\x00\x00\x00\x00"], (-0x10000000000000000).test_pack(2, 4, 0, BIG_ENDIAN))
+ assert_equal([-1, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"], (-0x0FFFFFFFFFFFFFFFF).test_pack(2, 4, 0, BIG_ENDIAN))
+ assert_equal([+1, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"], (+0x0FFFFFFFFFFFFFFFF).test_pack(2, 4, 0, BIG_ENDIAN))
+ assert_equal([+2, "\x00\x00\x00\x00\x00\x00\x00\x00"], (+0x10000000000000000).test_pack(2, 4, 0, BIG_ENDIAN))
+ assert_equal([+2, "\x00\x00\x00\x00\x00\x00\x00\x01"], (+0x10000000000000001).test_pack(2, 4, 0, BIG_ENDIAN))
+
+ 1.upto(16) {|wordsize|
+ 1.upto(20) {|numwords|
+ w = numwords*wordsize
+ n = 256**w
+ assert_equal([-2, "\x00"*(w-1)+"\x01"], (-n-1).test_pack(numwords, wordsize, 0, BIG_ENDIAN))
+ assert_equal([-2, "\x00"*w], (-n ).test_pack(numwords, wordsize, 0, BIG_ENDIAN))
+ assert_equal([-1, "\xFF"*w], (-n+1).test_pack(numwords, wordsize, 0, BIG_ENDIAN))
+ assert_equal([+1, "\xFF"*w], (+n-1).test_pack(numwords, wordsize, 0, BIG_ENDIAN))
+ assert_equal([+2, "\x00"*w], (+n ).test_pack(numwords, wordsize, 0, BIG_ENDIAN))
+ assert_equal([+2, "\x00"*(w-1)+"\x01"], (+n+1).test_pack(numwords, wordsize, 0, BIG_ENDIAN))
+ }
+ }
+
+ 1.upto(16) {|wordsize|
+ 1.upto(20) {|numwords|
+ w = numwords*wordsize
+ n = 256**w
+ assert_equal([-2, "\x01"+"\x00"*(w-1)], (-n-1).test_pack(numwords, wordsize, 0, LITTLE_ENDIAN))
+ assert_equal([-2, "\x00"*w], (-n ).test_pack(numwords, wordsize, 0, LITTLE_ENDIAN))
+ assert_equal([-1, "\xFF"*w], (-n+1).test_pack(numwords, wordsize, 0, LITTLE_ENDIAN))
+ assert_equal([+1, "\xFF"*w], (+n-1).test_pack(numwords, wordsize, 0, LITTLE_ENDIAN))
+ assert_equal([+2, "\x00"*w], (+n ).test_pack(numwords, wordsize, 0, LITTLE_ENDIAN))
+ assert_equal([+2, "\x01"+"\x00"*(w-1)], (+n+1).test_pack(numwords, wordsize, 0, LITTLE_ENDIAN))
+ }
+ }
+ end
+
+ def test_pack_sign
+ assert_equal([-1, "\x01"], (-1).test_pack(1, 1, 0, BIG_ENDIAN))
+ assert_equal([-1, "\x80\x70\x60\x50\x40\x30\x20\x10"], (-0x8070605040302010).test_pack(8, 1, 0, BIG_ENDIAN))
+ end
+
+ def test_pack_orders
+ [MSWORD_FIRST, LSWORD_FIRST].each {|word_order|
+ [MSBYTE_FIRST, LSBYTE_FIRST, NATIVE_BYTE_ORDER].each {|byte_order|
+ 1.upto(16) {|wordsize|
+ 1.upto(20) {|numwords|
+ w = numwords*wordsize
+ n = 0;
+ 0.upto(w) {|i|
+ n |= ((i+1) % 256) << (i*8)
+ }
+ assert_equal(n.test_pack(numwords, wordsize, 0, word_order|byte_order|GENERIC),
+ n.test_pack(numwords, wordsize, 0, word_order|byte_order),
+ "#{'%#x' % n}.test_pack(#{numwords}, #{wordsize}, 0, #{'%#x' % (word_order|byte_order)})")
+ }
+ }
+ }
+ }
+ end
+
+ def test_pack2comp_zero
+ assert_equal([0, ""], 0.test_pack(0, 1, 0, TWOCOMP|BIG_ENDIAN))
+ end
+
+ def test_pack2comp_emptybuf
+ assert_equal([-2, ""], (-3).test_pack(0, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-2, ""], (-2).test_pack(0, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, ""], (-1).test_pack(0, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([ 0, ""], 0.test_pack(0, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, ""], 1.test_pack(0, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, ""], 2.test_pack(0, 1, 0, TWOCOMP|BIG_ENDIAN))
+ end
+
+ def test_pack2comp_nearly_zero
+ assert_equal([-1, "\xFE"], (-2).test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\xFF"], (-1).test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([ 0, "\x00"], 0.test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+1, "\x01"], 1.test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+1, "\x02"], 2.test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ end
+
+ def test_pack2comp_overflow
+ assert_equal([-2, "\xF"], (-0x11).test_pack(1, 1, 4, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\x0"], (-0x10).test_pack(1, 1, 4, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\x1"], (-0x0F).test_pack(1, 1, 4, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+1, "\xF"], (+0x0F).test_pack(1, 1, 4, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, "\x0"], (+0x10).test_pack(1, 1, 4, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, "\x1"], (+0x11).test_pack(1, 1, 4, TWOCOMP|BIG_ENDIAN))
+
+ assert_equal([-2, "\xFF"], (-0x101).test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\x00"], (-0x100).test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\x01"], (-0x0FF).test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+1, "\xFF"], (+0x0FF).test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, "\x00"], (+0x100).test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, "\x01"], (+0x101).test_pack(1, 1, 0, TWOCOMP|BIG_ENDIAN))
+
+ assert_equal([-2, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"], (-0x10000000000000001).test_pack(2, 4, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\x00\x00\x00\x00\x00\x00\x00\x00"], (-0x10000000000000000).test_pack(2, 4, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\x00\x00\x00\x00\x00\x00\x00\x01"], (-0x0FFFFFFFFFFFFFFFF).test_pack(2, 4, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+1, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"], (+0x0FFFFFFFFFFFFFFFF).test_pack(2, 4, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, "\x00\x00\x00\x00\x00\x00\x00\x00"], (+0x10000000000000000).test_pack(2, 4, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, "\x00\x00\x00\x00\x00\x00\x00\x01"], (+0x10000000000000001).test_pack(2, 4, 0, TWOCOMP|BIG_ENDIAN))
+
+ 1.upto(16) {|wordsize|
+ 1.upto(20) {|numwords|
+ w = numwords*wordsize
+ n = 256**w
+ assert_equal([-2, "\xFF"*w ], (-n-1).test_pack(numwords, wordsize, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\x00"*w], (-n ).test_pack(numwords, wordsize, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([-1, "\x00"*(w-1)+"\x01"], (-n+1).test_pack(numwords, wordsize, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+1, "\xFF"*w], (+n-1).test_pack(numwords, wordsize, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, "\x00"*w], (+n ).test_pack(numwords, wordsize, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal([+2, "\x00"*(w-1)+"\x01"], (+n+1).test_pack(numwords, wordsize, 0, TWOCOMP|BIG_ENDIAN))
+ }
+ }
+
+ 1.upto(16) {|wordsize|
+ 1.upto(20) {|numwords|
+ w = numwords*wordsize
+ n = 256**w
+ assert_equal([-2, "\xFF"*w ], (-n-1).test_pack(numwords, wordsize, 0, TWOCOMP|LITTLE_ENDIAN))
+ assert_equal([-1, "\x00"*w], (-n ).test_pack(numwords, wordsize, 0, TWOCOMP|LITTLE_ENDIAN))
+ assert_equal([-1, "\x01"+"\x00"*(w-1)], (-n+1).test_pack(numwords, wordsize, 0, TWOCOMP|LITTLE_ENDIAN))
+ assert_equal([+1, "\xFF"*w], (+n-1).test_pack(numwords, wordsize, 0, TWOCOMP|LITTLE_ENDIAN))
+ assert_equal([+2, "\x00"*w], (+n ).test_pack(numwords, wordsize, 0, TWOCOMP|LITTLE_ENDIAN))
+ assert_equal([+2, "\x01"+"\x00"*(w-1)], (+n+1).test_pack(numwords, wordsize, 0, TWOCOMP|LITTLE_ENDIAN))
+ }
+ }
+
+ 2.upto(16) {|wordsize|
+ w = wordsize
+ b = 8*wordsize-1
+ n = 2**b
+ assert_equal([-2, "\x7F"+"\xFF"*(w-2)+"\xFF"], (-n-1).test_pack(1, wordsize, 1, TWOCOMP|MSBYTE_FIRST))
+ assert_equal([-1, "\x00"+"\x00"*(w-2)+"\x00"], (-n ).test_pack(1, wordsize, 1, TWOCOMP|MSBYTE_FIRST))
+ assert_equal([-1, "\x00"+"\x00"*(w-2)+"\x01"], (-n+1).test_pack(1, wordsize, 1, TWOCOMP|MSBYTE_FIRST))
+ assert_equal([+1, "\x7F"+"\xFF"*(w-2)+"\xFF"], (+n-1).test_pack(1, wordsize, 1, TWOCOMP|MSBYTE_FIRST))
+ assert_equal([+2, "\x00"+"\x00"*(w-2)+"\x00"], (+n ).test_pack(1, wordsize, 1, TWOCOMP|MSBYTE_FIRST))
+ assert_equal([+2, "\x00"+"\x00"*(w-2)+"\x01"], (+n+1).test_pack(1, wordsize, 1, TWOCOMP|MSBYTE_FIRST))
+ }
+
+ 2.upto(16) {|wordsize|
+ w = wordsize
+ b = 8*wordsize-1
+ n = 2**b
+ assert_equal([-2, "\xFF"+"\xFF"*(w-2)+"\x7F"], (-n-1).test_pack(1, wordsize, 1, TWOCOMP|LSBYTE_FIRST))
+ assert_equal([-1, "\x00"+"\x00"*(w-2)+"\x00"], (-n ).test_pack(1, wordsize, 1, TWOCOMP|LSBYTE_FIRST))
+ assert_equal([-1, "\x01"+"\x00"*(w-2)+"\x00"], (-n+1).test_pack(1, wordsize, 1, TWOCOMP|LSBYTE_FIRST))
+ assert_equal([+1, "\xFF"+"\xFF"*(w-2)+"\x7F"], (+n-1).test_pack(1, wordsize, 1, TWOCOMP|LSBYTE_FIRST))
+ assert_equal([+2, "\x00"+"\x00"*(w-2)+"\x00"], (+n ).test_pack(1, wordsize, 1, TWOCOMP|LSBYTE_FIRST))
+ assert_equal([+2, "\x01"+"\x00"*(w-2)+"\x00"], (+n+1).test_pack(1, wordsize, 1, TWOCOMP|LSBYTE_FIRST))
+ }
+
+ end
+
+ def test_unpack_zero
+ assert_equal(0, Integer.test_unpack("", 0, 1, 0, BIG_ENDIAN))
+ end
+
+ def test_unpack_argument_check
+ assert_raise(ArgumentError) { Integer.test_unpack("x", 2, 1, 0, MSBYTE_FIRST) }
+ assert_raise(ArgumentError) { Integer.test_unpack("x", 1, 1, 0, MSWORD_FIRST) }
+ assert_raise(ArgumentError) { Integer.test_unpack("x", 1, 0, 0, BIG_ENDIAN) }
+ assert_raise(ArgumentError) { Integer.test_unpack("x", 1, 1, 8, BIG_ENDIAN) }
+
+ # assume sizeof(ssize_t) == sizeof(intptr_t)
+ assert_raise(ArgumentError) { Integer.test_unpack("x", 1, 1 << ([""].pack("p").length * 8 - 1), 0, BIG_ENDIAN) }
+ end
+
+ def test_unpack_wordsize
+ assert_equal(1, Integer.test_unpack("\x01", 1, 1, 0, BIG_ENDIAN))
+ assert_equal(1, Integer.test_unpack("\x00\x01", 1, 2, 0, BIG_ENDIAN))
+ assert_equal(1, Integer.test_unpack("\x00\x00\x01", 1, 3, 0, BIG_ENDIAN))
+ assert_equal(1, Integer.test_unpack("\x01", 1, 1, 0, LITTLE_ENDIAN))
+ assert_equal(1, Integer.test_unpack("\x01\x00", 1, 2, 0, LITTLE_ENDIAN))
+ assert_equal(1, Integer.test_unpack("\x01\x00\x00", 1, 3, 0, LITTLE_ENDIAN))
+ end
+
+ def test_unpack_wordorder_and_endian
+ assert_equal(0x01020304, Integer.test_unpack("\x01\x02\x03\x04", 2, 2, 0, MSWORD_FIRST|MSBYTE_FIRST))
+ assert_equal(0x02010403, Integer.test_unpack("\x01\x02\x03\x04", 2, 2, 0, MSWORD_FIRST|LSBYTE_FIRST))
+ assert_equal(0x03040102, Integer.test_unpack("\x01\x02\x03\x04", 2, 2, 0, LSWORD_FIRST|MSBYTE_FIRST))
+ assert_equal(0x04030201, Integer.test_unpack("\x01\x02\x03\x04", 2, 2, 0, LSWORD_FIRST|LSBYTE_FIRST))
+ end
+
+ def test_unpack_native_endian
+ assert_equal("\x12\x34".unpack("S!")[0], Integer.test_unpack("\x12\x34", 1, 2, 0, MSWORD_FIRST|NATIVE_BYTE_ORDER))
+ end
+
+ def test_unpack_nail
+ assert_equal(0b100011, Integer.test_unpack("\x01\x00\x00\x00\x01\x01", 6, 1, 7, BIG_ENDIAN))
+ assert_equal(0x12345678, Integer.test_unpack("\x01\x02\x03\x04\x05\x06\x07\x08", 8, 1, 4, BIG_ENDIAN))
+ assert_equal(0x12345678, Integer.test_unpack("\x00\x12\x00\x34\x00\x56\x00\x78", 4, 2, 8, BIG_ENDIAN))
+ end
+
+ def test_unpack_sign
+ assert_equal(-1, Integer.test_unpack("\x01", 1, 1, 0, BIG_ENDIAN|NEGATIVE))
+ assert_equal(-0x8070605040302010, Integer.test_unpack("\x80\x70\x60\x50\x40\x30\x20\x10", 8, 1, 0, BIG_ENDIAN|NEGATIVE))
+ end
+
+ def test_unpack_orders
+ [MSWORD_FIRST, LSWORD_FIRST].each {|word_order|
+ [MSBYTE_FIRST, LSBYTE_FIRST, NATIVE_BYTE_ORDER].each {|byte_order|
+ 1.upto(16) {|wordsize|
+ 1.upto(20) {|numwords|
+ w = numwords*wordsize
+ ary = []
+ 0.upto(w) {|i|
+ ary << ((i+1) % 256);
+ }
+ str = ary.pack("C*")
+ flags = word_order|byte_order
+ assert_equal(Integer.test_unpack(str, numwords, wordsize, 0, flags|GENERIC),
+ Integer.test_unpack(str, numwords, wordsize, 0, flags),
+ "Integer.test_unpack(#{str.dump}, #{numwords}, #{wordsize}, 0, #{'%#x' % flags})")
+ }
+ }
+ }
+ }
+ end
+
+ def test_unpack2comp_single_byte
+ assert_equal(-128, Integer.test_unpack("\x80", 1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal( -2, Integer.test_unpack("\xFE", 1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal( -1, Integer.test_unpack("\xFF", 1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal( 0, Integer.test_unpack("\x00", 1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal( 1, Integer.test_unpack("\x01", 1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal( 2, Integer.test_unpack("\x02", 1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal( 127, Integer.test_unpack("\x7F", 1, 1, 0, TWOCOMP|BIG_ENDIAN))
+ end
+
+ def test_unpack2comp_sequence_of_ff
+ assert_equal(-1, Integer.test_unpack("\xFF"*2, 2, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal(-1, Integer.test_unpack("\xFF"*3, 3, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal(-1, Integer.test_unpack("\xFF"*4, 4, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal(-1, Integer.test_unpack("\xFF"*5, 5, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal(-1, Integer.test_unpack("\xFF"*6, 6, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal(-1, Integer.test_unpack("\xFF"*7, 7, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal(-1, Integer.test_unpack("\xFF"*8, 8, 1, 0, TWOCOMP|BIG_ENDIAN))
+ assert_equal(-1, Integer.test_unpack("\xFF"*9, 9, 1, 0, TWOCOMP|BIG_ENDIAN))
+ end
+
+ def test_unpack2comp_negative_single_byte
+ assert_equal(-256, Integer.test_unpack("\x00", 1, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
+ assert_equal(-255, Integer.test_unpack("\x01", 1, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
+ assert_equal(-254, Integer.test_unpack("\x02", 1, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
+ assert_equal(-129, Integer.test_unpack("\x7F", 1, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
+ assert_equal(-128, Integer.test_unpack("\x80", 1, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
+ assert_equal( -2, Integer.test_unpack("\xFE", 1, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
+ assert_equal( -1, Integer.test_unpack("\xFF", 1, 1, 0, TWOCOMP|BIG_ENDIAN|NEGATIVE))
+ end
+
+ def test_unpack2comp_negative_zero
+ 0.upto(100) {|n|
+ str = "\x00"*n
+ flags = TWOCOMP|BIG_ENDIAN|NEGATIVE
+ assert_equal(-(256**n), Integer.test_unpack(str, n, 1, 0, flags))
+ flags = TWOCOMP|LITTLE_ENDIAN|NEGATIVE
+ assert_equal(-(256**n), Integer.test_unpack(str, n, 1, 0, flags),
+ "Integer.test_unpack(#{str.dump}, #{n}, 1, 0, #{'%#x' % flags})")
+ }
+ end
+ end
+
+ def test_numbits_2comp
+ assert_equal(4, -9.test_numbits_2comp_without_sign)
+ assert_equal(3, -8.test_numbits_2comp_without_sign)
+ assert_equal(3, -7.test_numbits_2comp_without_sign)
+ assert_equal(3, -6.test_numbits_2comp_without_sign)
+ assert_equal(3, -5.test_numbits_2comp_without_sign)
+ assert_equal(2, -4.test_numbits_2comp_without_sign)
+ assert_equal(2, -3.test_numbits_2comp_without_sign)
+ assert_equal(1, -2.test_numbits_2comp_without_sign)
+ assert_equal(0, -1.test_numbits_2comp_without_sign)
+ assert_equal(0, 0.test_numbits_2comp_without_sign)
+ assert_equal(1, 1.test_numbits_2comp_without_sign)
+ assert_equal(2, 2.test_numbits_2comp_without_sign)
+ assert_equal(2, 3.test_numbits_2comp_without_sign)
+ assert_equal(3, 4.test_numbits_2comp_without_sign)
+ assert_equal(3, 5.test_numbits_2comp_without_sign)
+ assert_equal(3, 6.test_numbits_2comp_without_sign)
+ assert_equal(3, 7.test_numbits_2comp_without_sign)
+ assert_equal(4, 8.test_numbits_2comp_without_sign)
+ assert_equal(4, 9.test_numbits_2comp_without_sign)
+ end
+
+ def test_numbytes_2comp
+ assert_equal(6, -0x8000000001.test_numbytes_2comp_with_sign)
+ assert_equal(5, -0x8000000000.test_numbytes_2comp_with_sign)
+ assert_equal(5, -0x80000001.test_numbytes_2comp_with_sign)
+ assert_equal(4, -0x80000000.test_numbytes_2comp_with_sign)
+ assert_equal(4, -0x800001.test_numbytes_2comp_with_sign)
+ assert_equal(3, -0x800000.test_numbytes_2comp_with_sign)
+ assert_equal(3, -0x8001.test_numbytes_2comp_with_sign)
+ assert_equal(2, -0x8000.test_numbytes_2comp_with_sign)
+ assert_equal(2, -0x81.test_numbytes_2comp_with_sign)
+ assert_equal(1, -0x80.test_numbytes_2comp_with_sign)
+ assert_equal(1, -1.test_numbytes_2comp_with_sign)
+ assert_equal(1, 0.test_numbytes_2comp_with_sign)
+ assert_equal(1, 1.test_numbytes_2comp_with_sign)
+ assert_equal(1, 0x7f.test_numbytes_2comp_with_sign)
+ assert_equal(2, 0x80.test_numbytes_2comp_with_sign)
+ assert_equal(2, 0x7fff.test_numbytes_2comp_with_sign)
+ assert_equal(3, 0x8000.test_numbytes_2comp_with_sign)
+ assert_equal(3, 0x7fffff.test_numbytes_2comp_with_sign)
+ assert_equal(4, 0x800000.test_numbytes_2comp_with_sign)
+ assert_equal(4, 0x7fffffff.test_numbytes_2comp_with_sign)
+ assert_equal(5, 0x80000000.test_numbytes_2comp_with_sign)
+ assert_equal(5, 0x7fffffffff.test_numbytes_2comp_with_sign)
+ assert_equal(6, 0x8000000000.test_numbytes_2comp_with_sign)
+ end
+
+end
diff --git a/jni/ruby/test/-ext-/bignum/test_str2big.rb b/jni/ruby/test/-ext-/bignum/test_str2big.rb
new file mode 100644
index 0000000..4304be8
--- /dev/null
+++ b/jni/ruby/test/-ext-/bignum/test_str2big.rb
@@ -0,0 +1,37 @@
+require 'test/unit'
+require "-test-/bignum"
+
+class TestBignum < Test::Unit::TestCase
+ class TestStr2big < Test::Unit::TestCase
+
+ SIZEOF_BDIGIT = Bignum::SIZEOF_BDIGIT
+ BITSPERDIG = Bignum::BITSPERDIG
+ BDIGMAX = (1 << BITSPERDIG) - 1
+
+ def test_str2big_poweroftwo
+ s = "1" + "0" * 1000
+ n = 16 ** 1000
+ assert_equal(n, s.str2big_poweroftwo(16, true))
+ end
+
+ def test_str2big_normal
+ s = "1" + "0" * 1000
+ n = 10 ** 1000
+ assert_equal(n, s.str2big_normal(10, true))
+ end
+
+ def test_str2big_karatsuba
+ s = "1" + "0" * 1000
+ n = 10 ** 1000
+ assert_equal(n, s.str2big_karatsuba(10, true))
+ end
+
+ def test_str2big_gmp
+ s = "1" + "0" * 1000
+ n = 10 ** 1000
+ assert_equal(n, s.str2big_gmp(10, true))
+ rescue NotImplementedError
+ end
+
+ end
+end
diff --git a/jni/ruby/test/-ext-/bug_reporter/test_bug_reporter.rb b/jni/ruby/test/-ext-/bug_reporter/test_bug_reporter.rb
new file mode 100644
index 0000000..6ac5b8f
--- /dev/null
+++ b/jni/ruby/test/-ext-/bug_reporter/test_bug_reporter.rb
@@ -0,0 +1,25 @@
+require 'test/unit'
+require 'tmpdir'
+
+class TestBugReporter < Test::Unit::TestCase
+ def test_bug_reporter_add
+ expected_stderr = [
+ :*,
+ /\[BUG\]\sSegmentation\sfault.*\n/,
+ /#{ Regexp.quote(RUBY_DESCRIPTION) }\n\n/,
+ :*,
+ /Sample bug reporter: 12345/,
+ :*
+ ]
+ tmpdir = Dir.mktmpdir
+
+ args = ["--disable-gems", "-r-test-/bug_reporter/bug_reporter",
+ "-C", tmpdir]
+ stdin = "register_sample_bug_reporter(12345); Process.kill :SEGV, $$"
+ _, stderr, status = EnvUtil.invoke_ruby(args, stdin, false, true)
+ stderr.force_encoding("ASCII-8BIT")
+ assert_pattern_list(expected_stderr, stderr)
+ ensure
+ FileUtils.rm_rf(tmpdir) if tmpdir
+ end
+end
diff --git a/jni/ruby/test/-ext-/class/test_class2name.rb b/jni/ruby/test/-ext-/class/test_class2name.rb
new file mode 100644
index 0000000..070be5a
--- /dev/null
+++ b/jni/ruby/test/-ext-/class/test_class2name.rb
@@ -0,0 +1,18 @@
+require 'test/unit'
+require "-test-/class"
+
+class Test_Class < Test::Unit::TestCase
+ class Test_Class2Name < superclass
+ def test_toplevel_class
+ assert_equal("Object", Bug::Class.class2name(::Object))
+ end
+
+ def test_toplevel_module
+ assert_equal("Kernel", Bug::Class.class2name(::Kernel))
+ end
+
+ def test_singleton_class
+ assert_equal("Object", Bug::Class.class2name(::Object.new.singleton_class))
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/debug/test_debug.rb b/jni/ruby/test/-ext-/debug/test_debug.rb
new file mode 100644
index 0000000..ec506e0
--- /dev/null
+++ b/jni/ruby/test/-ext-/debug/test_debug.rb
@@ -0,0 +1,58 @@
+require 'test/unit'
+require '-test-/debug'
+
+class TestDebug < Test::Unit::TestCase
+
+ def binds_check(binds, msg = nil)
+ count = Hash.new(0)
+ assert_instance_of(Array, binds, msg)
+ binds.each{|(_self, bind, klass, iseq, loc)|
+ if _self == self
+ count[:self] += 1
+ end
+
+ if bind
+ assert_instance_of(Binding, bind, msg)
+ count[:bind] += 1
+ end
+
+ if klass
+ assert(klass.instance_of?(Module) || klass.instance_of?(Class), msg)
+ count[:class] += 1
+ end
+
+ if iseq
+ count[:iseq] += 1
+ assert_instance_of(RubyVM::InstructionSequence, iseq, msg)
+
+ # check same location
+ assert_equal(loc.path, iseq.path, msg)
+ assert_equal(loc.absolute_path, iseq.absolute_path, msg)
+ assert_equal(loc.label, iseq.label, msg)
+ assert_operator(loc.lineno, :>=, iseq.first_lineno, msg)
+ end
+
+ assert_instance_of(Thread::Backtrace::Location, loc, msg)
+
+ }
+ assert_operator(0, :<, count[:self], msg)
+ assert_operator(0, :<, count[:bind], msg)
+ assert_operator(0, :<, count[:iseq], msg)
+ assert_operator(0, :<, count[:class], msg)
+ end
+
+ def test_inspector_open
+ binds = Bug::Debug.inspector
+ binds_check binds
+ end
+
+ def inspector_in_eval
+ eval("Bug::Debug.inspector")
+ end
+
+ def test_inspector_open_in_eval
+ bug7635 = '[ruby-core:51640]'
+ binds = inspector_in_eval
+ binds_check binds, bug7635
+ end
+end
diff --git a/jni/ruby/test/-ext-/debug/test_profile_frames.rb b/jni/ruby/test/-ext-/debug/test_profile_frames.rb
new file mode 100644
index 0000000..1879c22
--- /dev/null
+++ b/jni/ruby/test/-ext-/debug/test_profile_frames.rb
@@ -0,0 +1,104 @@
+require 'test/unit'
+require '-test-/debug'
+
+class SampleClassForTestProfileFrames
+ class Sample2
+ def baz(block)
+ instance_eval "def zab(block) block.call end"
+ [self, zab(block)]
+ end
+ end
+
+ def self.bar(block)
+ Sample2.new.baz(block)
+ end
+
+ def foo(block)
+ self.class.bar(block)
+ end
+end
+
+class TestProfileFrames < Test::Unit::TestCase
+ def test_profile_frames
+ obj, frames = Fiber.new{
+ Fiber.yield SampleClassForTestProfileFrames.new.foo(lambda{ Bug::Debug.profile_frames(0, 10) })
+ }.resume
+
+ labels = [
+ "block (2 levels) in test_profile_frames",
+ "zab",
+ "baz",
+ "bar",
+ "foo",
+ "block in test_profile_frames",
+ ]
+ base_labels = [
+ "test_profile_frames",
+ "zab",
+ "baz",
+ "bar",
+ "foo",
+ "test_profile_frames",
+ ]
+ full_labels = [
+ "block (2 levels) in TestProfileFrames#test_profile_frames",
+ "#{obj.inspect}.zab",
+ "SampleClassForTestProfileFrames::Sample2#baz",
+ "SampleClassForTestProfileFrames.bar",
+ "SampleClassForTestProfileFrames#foo",
+ "block in TestProfileFrames#test_profile_frames",
+ ]
+ classes = [
+ TestProfileFrames,
+ obj,
+ SampleClassForTestProfileFrames::Sample2,
+ SampleClassForTestProfileFrames, # singleton method
+ SampleClassForTestProfileFrames,
+ TestProfileFrames,
+ ]
+ singleton_method_p = [
+ false, true, false, true, false, false, false,
+ ]
+ method_names = [
+ "test_profile_frames",
+ "zab",
+ "baz",
+ "bar",
+ "foo",
+ "test_profile_frames",
+ ]
+ qualified_method_names = [
+ "TestProfileFrames#test_profile_frames",
+ "#{obj.inspect}.zab",
+ "SampleClassForTestProfileFrames::Sample2#baz",
+ "SampleClassForTestProfileFrames.bar",
+ "SampleClassForTestProfileFrames#foo",
+ "TestProfileFrames#test_profile_frames",
+ ]
+ paths = [ file=__FILE__, "(eval)", file, file, file, file ]
+ absolute_paths = [ file, nil, file, file, file, file ]
+
+ # pp frames
+
+ assert_equal(labels.size, frames.size)
+
+ frames.each.with_index{|(path, absolute_path, label, base_label, full_label, first_lineno,
+ classpath, singleton_p, method_name, qualified_method_name), i|
+ err_msg = "#{i}th frame"
+ assert_equal(paths[i], path, err_msg)
+ assert_equal(absolute_paths[i], absolute_path, err_msg)
+ assert_equal(labels[i], label, err_msg)
+ assert_equal(base_labels[i], base_label, err_msg)
+ assert_equal(singleton_method_p[i], singleton_p, err_msg)
+ assert_equal(method_names[i], method_name, err_msg)
+ assert_match(qualified_method_names[i], qualified_method_name, err_msg)
+ assert_match(full_labels[i], full_label, err_msg)
+ assert_match(classes[i].inspect, classpath, err_msg)
+ if label == method_name
+ c = classes[i]
+ m = singleton_p ? c.method(method_name) : c.instance_method(method_name)
+ assert_equal(m.source_location[1], first_lineno, err_msg)
+ end
+ }
+ end
+end
diff --git a/jni/ruby/test/-ext-/exception/test_data_error.rb b/jni/ruby/test/-ext-/exception/test_data_error.rb
new file mode 100644
index 0000000..53cbb28
--- /dev/null
+++ b/jni/ruby/test/-ext-/exception/test_data_error.rb
@@ -0,0 +1,13 @@
+require 'test/unit'
+
+module Bug
+ class TestException < Test::Unit::TestCase
+ def test_cleanup_data_error
+ bug9167 = '[ruby-core:58643] [Bug #9167]'
+ assert_normal_exit(<<-'end;', bug9167) # do
+ require '-test-/exception'
+ raise Bug::Exception::DataError, "Error"
+ end;
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/exception/test_enc_raise.rb b/jni/ruby/test/-ext-/exception/test_enc_raise.rb
new file mode 100644
index 0000000..a578b16
--- /dev/null
+++ b/jni/ruby/test/-ext-/exception/test_enc_raise.rb
@@ -0,0 +1,15 @@
+require 'test/unit'
+require '-test-/exception'
+
+module Bug
+ class TestException < Test::Unit::TestCase
+ def test_enc_raise
+ feature5650 = '[ruby-core:41160]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ e = assert_raise(Bug::Exception) {Bug::Exception.enc_raise(enc, "[Feature #5650]")}
+ assert_equal(enc, e.message.encoding, feature5650)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/exception/test_ensured.rb b/jni/ruby/test/-ext-/exception/test_ensured.rb
new file mode 100644
index 0000000..97d9794
--- /dev/null
+++ b/jni/ruby/test/-ext-/exception/test_ensured.rb
@@ -0,0 +1,31 @@
+require 'test/unit'
+
+module Bug
+ class Bug7802 < RuntimeError
+ end
+
+ class TestException < Test::Unit::TestCase
+ def test_ensured
+ assert_separately([], <<-'end;') # do
+
+ require '-test-/exception'
+
+ module Bug
+ class Bug7802 < RuntimeError
+ def try_method
+ raise self
+ end
+
+ def ensured_method
+ [1].detect {|i| true}
+ end
+ end
+ end
+
+ assert_raise(Bug::Bug7802, '[ruby-core:52022] [Bug #7802]') {
+ Bug::Exception.ensured(Bug::Bug7802.new)
+ }
+ end;
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/file/test_stat.rb b/jni/ruby/test/-ext-/file/test_stat.rb
new file mode 100644
index 0000000..b9aa132
--- /dev/null
+++ b/jni/ruby/test/-ext-/file/test_stat.rb
@@ -0,0 +1,14 @@
+require 'test/unit'
+require "-test-/file"
+
+class Test_FileStat < Test::Unit::TestCase
+ def test_stat_for_fd
+ st = open(__FILE__) {|f| Bug::File::Stat.for_fd(f.fileno)}
+ assert_equal(File.stat(__FILE__), st)
+ end
+
+ def test_stat_for_path
+ st = Bug::File::Stat.for_path(__FILE__)
+ assert_equal(File.stat(__FILE__), st)
+ end
+end
diff --git a/jni/ruby/test/-ext-/float/test_nextafter.rb b/jni/ruby/test/-ext-/float/test_nextafter.rb
new file mode 100644
index 0000000..e3a3e72
--- /dev/null
+++ b/jni/ruby/test/-ext-/float/test_nextafter.rb
@@ -0,0 +1,57 @@
+require 'test/unit'
+require "-test-/float"
+
+class TestFloatExt < Test::Unit::TestCase
+ NEXTAFTER_VALUES = [
+ -Float::INFINITY,
+ -Float::MAX,
+ -100.0,
+ -1.0-Float::EPSILON,
+ -1.0,
+ -Float::EPSILON,
+ -Float::MIN/2,
+ -Math.ldexp(0.5, Float::MIN_EXP - Float::MANT_DIG + 1),
+ -0.0,
+ 0.0,
+ Math.ldexp(0.5, Float::MIN_EXP - Float::MANT_DIG + 1),
+ Float::MIN/2,
+ Float::MIN,
+ Float::EPSILON,
+ 1.0,
+ 1.0+Float::EPSILON,
+ 100.0,
+ Float::MAX,
+ Float::INFINITY,
+ Float::NAN
+ ]
+
+ test_number = 0
+ NEXTAFTER_VALUES.each {|n1|
+ NEXTAFTER_VALUES.each {|n2|
+ tag = n2.infinite? ? "ruby" : "other"
+ test_name = "test_nextafter_#{test_number}_#{tag}_#{n1}_#{n2}"
+ test_number += 1
+ define_method(test_name) {
+ v1 = Bug::Float.missing_nextafter(n1, n2)
+ v2 = Bug::Float.system_nextafter(n1, n2)
+ assert_kind_of(Float, v1)
+ assert_kind_of(Float, v2)
+ if v1.nan?
+ assert(v2.nan?, "Bug::Float.system_nextafter(#{n1}, #{n2}).nan?")
+ else
+ assert_equal(v1, v2,
+ "Bug::Float.missing_nextafter(#{'%a' % n1}, #{'%a' % n2}) = #{'%a' % v1} != " +
+ "#{'%a' % v2} = Bug::Float.system_nextafter(#{'%a' % n1}, #{'%a' % n2})")
+ if v1 == 0
+ s1 = 1.0/v1 < 0 ? "negative-zero" : "positive-zero"
+ s2 = 1.0/v2 < 0 ? "negative-zero" : "positive-zero"
+ assert_equal(s1, s2,
+ "Bug::Float.missing_nextafter(#{'%a' % n1}, #{'%a' % n2}) = #{'%a' % v1} != " +
+ "#{'%a' % v2} = Bug::Float.system_nextafter(#{'%a' % n1}, #{'%a' % n2})")
+ end
+ end
+ }
+ }
+ }
+
+end
diff --git a/jni/ruby/test/-ext-/funcall/test_passing_block.rb b/jni/ruby/test/-ext-/funcall/test_passing_block.rb
new file mode 100644
index 0000000..87aed22
--- /dev/null
+++ b/jni/ruby/test/-ext-/funcall/test_passing_block.rb
@@ -0,0 +1,22 @@
+require 'test/unit'
+
+class TestFuncall < Test::Unit::TestCase
+ module Relay
+ def self.target(*args, &block)
+ yield(*args) if block
+ end
+ end
+ require '-test-/funcall/funcall'
+
+ def test_with_funcall2
+ ok = nil
+ Relay.with_funcall2("feature#4504") {|arg| ok = arg || true}
+ assert_nil(ok)
+ end
+
+ def test_with_funcall_passing_block
+ ok = nil
+ Relay.with_funcall_passing_block("feature#4504") {|arg| ok = arg || true}
+ assert_equal("feature#4504", ok)
+ end
+end
diff --git a/jni/ruby/test/-ext-/hash/test_delete.rb b/jni/ruby/test/-ext-/hash/test_delete.rb
new file mode 100644
index 0000000..13f3595
--- /dev/null
+++ b/jni/ruby/test/-ext-/hash/test_delete.rb
@@ -0,0 +1,19 @@
+require 'test/unit'
+require '-test-/hash'
+
+class TestHash < Test::Unit::TestCase
+ class TestDelete < Test::Unit::TestCase
+ def test_delete
+ hash = Bug::Hash.new
+ hash[1] = 2
+ called = false
+ assert_equal 1, hash.size
+ assert_equal [2], hash.delete!(1) {called = true}
+ assert_equal false, called, "block called"
+ assert_equal 0, hash.size
+ assert_equal nil, hash.delete!(1) {called = true}
+ assert_equal false, called, "block called"
+ assert_equal 0, hash.size
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/iseq_load/test_iseq_load.rb b/jni/ruby/test/-ext-/iseq_load/test_iseq_load.rb
new file mode 100644
index 0000000..5bbd49e
--- /dev/null
+++ b/jni/ruby/test/-ext-/iseq_load/test_iseq_load.rb
@@ -0,0 +1,95 @@
+require 'test/unit'
+
+class TestIseqLoad < Test::Unit::TestCase
+ require '-test-/iseq_load/iseq_load'
+ ISeq = RubyVM::InstructionSequence
+
+ def test_bug8543
+ assert_iseq_roundtrip <<-'end;'
+ puts "tralivali"
+ def funct(a, b)
+ a**b
+ end
+ 3.times { |i| puts "Hello, world#{funct(2,i)}!" }
+ end;
+ end
+
+ def test_case_when
+ assert_iseq_roundtrip <<-'end;'
+ def user_mask(target)
+ target.each_char.inject(0) do |mask, chr|
+ case chr
+ when "u"
+ mask | 04700
+ when "g"
+ mask | 02070
+ when "o"
+ mask | 01007
+ when "a"
+ mask | 07777
+ else
+ raise ArgumentError, "invalid `who' symbol in file mode: #{chr}"
+ end
+ end
+ end
+ end;
+ end
+
+ def test_splatsplat
+ assert_iseq_roundtrip('def splatsplat(**); end')
+ end
+
+ def test_hidden
+ assert_iseq_roundtrip('def x(a, (b, *c), d: false); end')
+ end
+
+ def assert_iseq_roundtrip(src)
+ a = ISeq.compile(src).to_a
+ b = ISeq.iseq_load(a).to_a
+ warn diff(a, b) if a != b
+ assert_equal a, b
+ assert_equal a, ISeq.iseq_load(b).to_a
+ end
+
+ def test_next_in_block_in_block
+ skip "failing due to stack_max mismatch"
+ assert_iseq_roundtrip <<-'end;'
+ 3.times { 3.times { next } }
+ end;
+ end
+
+ def test_break_ensure
+ skip "failing due to exception entry sp mismatch"
+ assert_iseq_roundtrip <<-'end;'
+ def m
+ bad = true
+ while true
+ begin
+ break
+ ensure
+ bad = false
+ end
+ end
+ end
+ end;
+ end
+
+ # FIXME: still failing
+ def test_require_integration
+ skip "iseq loader require integration tests still failing"
+ f = File.expand_path(__FILE__)
+ # $(top_srcdir)/test/ruby/test_....rb
+ 3.times { f = File.dirname(f) }
+ Dir[File.join(f, 'ruby', '*.rb')].each do |f|
+ iseq = ISeq.compile_file(f)
+ orig = iseq.to_a.freeze
+
+ loaded = ISeq.iseq_load(orig).to_a
+ if loaded != orig
+ warn f
+ warn diff(orig, loaded)
+ end
+ #assert_equal orig, loaded
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/iter/test_iter_break.rb b/jni/ruby/test/-ext-/iter/test_iter_break.rb
new file mode 100644
index 0000000..5bac633
--- /dev/null
+++ b/jni/ruby/test/-ext-/iter/test_iter_break.rb
@@ -0,0 +1,15 @@
+require 'test/unit'
+require '-test-/iter'
+
+module TestIter
+end
+
+class TestIter::IterBreak < Test::Unit::TestCase
+ def test_iter_break
+ backport7896 = '[ruby-core:52607]'
+ assert_equal(nil, 1.times{Bug::Iter::Breakable.iter_break}, backport7896)
+
+ feature5895 = '[ruby-dev:45132]'
+ assert_equal(42, 1.times{Bug::Iter::Breakable.iter_break_value(42)}, feature5895)
+ end
+end
diff --git a/jni/ruby/test/-ext-/iter/test_yield_block.rb b/jni/ruby/test/-ext-/iter/test_yield_block.rb
new file mode 100644
index 0000000..bec993c
--- /dev/null
+++ b/jni/ruby/test/-ext-/iter/test_yield_block.rb
@@ -0,0 +1,21 @@
+require 'test/unit'
+require '-test-/iter'
+
+module TestIter
+end
+
+class TestIter::YieldBlock < Test::Unit::TestCase
+ class YieldTest
+ include Bug::Iter::Yield
+ attr_reader :blockarg
+ def test(arg, &block)
+ block.call(arg) {|blockarg| @blockarg = blockarg}
+ end
+ end
+
+ def test_yield_block
+ a = YieldTest.new
+ a.yield_block(:test, "foo") {|x, &b| assert_kind_of(Proc, b); b.call(x)}
+ assert_equal("foo", a.blockarg)
+ end
+end
diff --git a/jni/ruby/test/-ext-/load/test_dot_dot.rb b/jni/ruby/test/-ext-/load/test_dot_dot.rb
new file mode 100644
index 0000000..82aa10a
--- /dev/null
+++ b/jni/ruby/test/-ext-/load/test_dot_dot.rb
@@ -0,0 +1,10 @@
+require 'test/unit'
+
+class Test_DotDot < Test::Unit::TestCase
+ def test_load_dot_dot
+ feature = '[ruby-dev:41774]'
+ assert_nothing_raised(LoadError, feature) {
+ require '-test-/load/dot.dot/dot.dot'
+ }
+ end
+end
diff --git a/jni/ruby/test/-ext-/marshal/test_usrmarshal.rb b/jni/ruby/test/-ext-/marshal/test_usrmarshal.rb
new file mode 100644
index 0000000..8d8db01
--- /dev/null
+++ b/jni/ruby/test/-ext-/marshal/test_usrmarshal.rb
@@ -0,0 +1,32 @@
+require 'test/unit'
+require '-test-/marshal/usr'
+
+module Bug end
+
+module Bug::Marshal
+ class TestUsrMarshal < Test::Unit::TestCase
+ def old_dump
+ @old_dump ||=
+ begin
+ src = "module Bug; module Marshal; class UsrMarshal; def initialize(val) @value = val; end; end; ::Marshal.dump(UsrMarshal.new(42), STDOUT); end; end"
+ EnvUtil.invoke_ruby([], src, true)[0]
+ end
+ end
+
+ def test_marshal
+ v = ::Marshal.load(::Marshal.dump(UsrMarshal.new(42)))
+ assert_instance_of(UsrMarshal, v)
+ assert_equal(42, v.value)
+ end
+
+ def test_incompat
+ assert_raise_with_message(ArgumentError, "dump format error") {::Marshal.load(old_dump)}
+ end
+
+ def test_compat
+ out, err = EnvUtil.invoke_ruby(["-r-test-/marshal/usr", "-r-test-/marshal/compat", "-e", "::Marshal.dump(::Marshal.load(STDIN), STDOUT)"], old_dump, true, true)
+ assert_equal(::Marshal.dump(UsrMarshal.new(42)), out)
+ assert_equal("", err)
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/method/test_arity.rb b/jni/ruby/test/-ext-/method/test_arity.rb
new file mode 100644
index 0000000..79ef23b
--- /dev/null
+++ b/jni/ruby/test/-ext-/method/test_arity.rb
@@ -0,0 +1,37 @@
+require '-test-/method'
+require 'test/unit'
+
+class TestMethod < Test::Unit::TestCase
+ class TestArity < Test::Unit::TestCase
+ class A
+ def foo0()
+ end
+ def foom1(*a)
+ end
+ def foom2(a,*b)
+ end
+ def foo1(a)
+ end
+ def foo2(a,b)
+ end
+ end
+
+ class B<A
+ private :foo1, :foo2
+ end
+
+ METHODS = {foo0: 0, foo1: 1, foo2: 2, foom1: -1, foom2: -2}
+
+ def test_base
+ METHODS.each do |name, arity|
+ assert_equal(arity, Bug::Method.mod_method_arity(A, name), "A##{name}")
+ end
+ end
+
+ def test_zsuper
+ METHODS.each do |name, arity|
+ assert_equal(arity, Bug::Method.mod_method_arity(B, name), "B##{name}")
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/num2int/test_num2int.rb b/jni/ruby/test/-ext-/num2int/test_num2int.rb
new file mode 100644
index 0000000..f579659
--- /dev/null
+++ b/jni/ruby/test/-ext-/num2int/test_num2int.rb
@@ -0,0 +1,267 @@
+require 'test/unit'
+require '-test-/num2int/num2int'
+
+class TestNum2int < Test::Unit::TestCase
+ SHRT_MIN = -32768
+ SHRT_MAX = 32767
+ USHRT_MAX = 65535
+
+ INT_MIN = -2147483648
+ INT_MAX = 2147483647
+ UINT_MAX = 4294967295
+
+ case [0].pack('L!').size
+ when 4
+ LONG_MAX = 2147483647
+ LONG_MIN = -2147483648
+ ULONG_MAX = 4294967295
+ when 8
+ LONG_MAX = 9223372036854775807
+ LONG_MIN = -9223372036854775808
+ ULONG_MAX = 18446744073709551615
+ end
+
+ LLONG_MAX = 9223372036854775807
+ LLONG_MIN = -9223372036854775808
+ ULLONG_MAX = 18446744073709551615
+
+ FIXNUM_MAX = LONG_MAX/2
+ FIXNUM_MIN = LONG_MIN/2
+
+ def fix2big(n)
+ 10000000000000000000000000000.coerce(n)[0]
+ end
+
+ def assert_num2i_success_internal(exp, func, arg)
+ mesg = "#{func}(#{arg.inspect})"
+ out = nil
+ assert_nothing_raised(mesg) {
+ out = Num2int.send(func, arg)
+ }
+ assert_equal(exp, out, mesg)
+ end
+
+ def assert_num2i_success(type, num, result=num)
+ func = "NUM2#{type}".upcase
+ assert_num2i_success_internal(result.to_s, func, num)
+ assert_num2i_success_internal(result.to_s, func, fix2big(num))
+ assert_num2i_success_internal(result.to_s, func, Rational(num, 1))
+ if num.to_f.to_i == num
+ assert_num2i_success_internal(result.to_s, func, num.to_f)
+ end
+ # The conversion functions such as NUM2INT uses (conceptually) to_int.
+ if (arg = num.to_f + 0.5) != num.to_f && arg.to_int == num
+ assert_num2i_success_internal(result.to_s, func, arg)
+ end
+ if (arg = num.to_f - 0.5) != num.to_f && arg.to_int == num
+ assert_num2i_success_internal(result.to_s, func, arg)
+ end
+ if (arg = num + Rational(1,2)) && arg.to_int == num
+ assert_num2i_success_internal(result.to_s, func, arg)
+ end
+ if (arg = num - Rational(1,2)) && arg.to_int == num
+ assert_num2i_success_internal(result.to_s, func, arg)
+ end
+ end
+
+ def assert_num2i_error_internal(func, arg)
+ assert_raise(RangeError, "#{func}(#{arg.inspect})") {
+ Num2int.send(func, arg)
+ }
+ end
+
+ def assert_num2i_error(type, num)
+ func = "NUM2#{type}".upcase
+ assert_num2i_error_internal(func, num)
+ assert_num2i_error_internal(func, fix2big(num))
+ assert_num2i_error_internal(func, Rational(num, 1))
+ if num.to_f.to_i == num
+ assert_num2i_error_internal(func, num.to_f)
+ end
+ # The conversion functions such as NUM2INT uses (conceptually) to_int.
+ if (arg = num.to_f + 0.5) != num.to_f && arg.to_int == num
+ assert_num2i_error_internal(func, arg)
+ end
+ if (arg = num.to_f - 0.5) != num.to_f && arg.to_int == num
+ assert_num2i_error_internal(func, arg)
+ end
+ if (arg = num + Rational(1,2)) && arg.to_int == num
+ assert_num2i_error_internal(func, arg)
+ end
+ if (arg = num - Rational(1,2)) && arg.to_int == num
+ assert_num2i_error_internal(func, arg)
+ end
+ end
+
+ def assert_fix2i_success_internal(exp, func, arg)
+ mesg = "#{func}(#{arg.inspect})"
+ out = nil
+ assert_nothing_raised(mesg) {
+ out = Num2int.send(func, arg)
+ }
+ assert_equal(exp, out, mesg)
+ end
+
+ def assert_fix2i_success(type, num, result=num)
+ return if !num.kind_of?(Fixnum)
+ func = "FIX2#{type}".upcase
+ assert_fix2i_success_internal(result.to_s, func, num)
+ end
+
+ def assert_fix2i_error_internal(func, arg)
+ assert_raise(RangeError, "#{func}(#{arg.inspect})") {
+ Num2int.send(func, arg)
+ }
+ end
+
+ def assert_fix2i_error(type, num)
+ return if !num.kind_of?(Fixnum)
+ func = "FIX2#{type}".upcase
+ assert_num2i_error_internal(func, num)
+ end
+
+ def test_num2short
+ assert_num2i_success(:short, SHRT_MIN)
+ assert_num2i_success(:short, SHRT_MIN+1)
+ assert_num2i_success(:short, SHRT_MAX)
+ assert_num2i_error(:short, SHRT_MIN-1)
+ assert_num2i_error(:short, SHRT_MAX+1)
+ end
+
+ def test_num2ushort
+ assert_num2i_success(:ushort, 0)
+ assert_num2i_success(:ushort, USHRT_MAX)
+ assert_num2i_success(:ushort, -1, USHRT_MAX)
+ assert_num2i_success(:ushort, SHRT_MIN, SHRT_MAX+1)
+ assert_num2i_success(:ushort, SHRT_MIN+1, SHRT_MAX+2)
+ assert_num2i_error(:ushort, SHRT_MIN-1)
+ assert_num2i_error(:ushort, USHRT_MAX+1)
+ end
+
+ def test_num2int
+ assert_num2i_success(:int, INT_MIN)
+ assert_num2i_success(:int, INT_MIN+1)
+ assert_num2i_success(:int, INT_MAX)
+ assert_num2i_error(:int, INT_MIN-1)
+ assert_num2i_error(:int, INT_MAX+1)
+ end
+
+ def test_num2uint
+ assert_num2i_success(:uint, 0)
+ assert_num2i_success(:uint, UINT_MAX)
+ assert_num2i_success(:uint, -1, UINT_MAX)
+ assert_num2i_success(:uint, INT_MIN, INT_MAX+1)
+ assert_num2i_success(:uint, INT_MIN+1, INT_MAX+2)
+ assert_num2i_error(:uint, INT_MIN-1)
+ assert_num2i_error(:uint, UINT_MAX+1)
+ end
+
+ def test_num2long
+ assert_num2i_success(:long, LONG_MIN)
+ assert_num2i_success(:long, LONG_MIN+1)
+ assert_num2i_success(:long, LONG_MAX)
+ assert_num2i_error(:long, LONG_MIN-1)
+ assert_num2i_error(:long, LONG_MAX+1)
+ assert_num2i_success(:long, FIXNUM_MIN)
+ assert_num2i_success(:long, FIXNUM_MIN+1)
+ assert_num2i_success(:long, FIXNUM_MIN-1)
+ assert_num2i_success(:long, FIXNUM_MAX)
+ assert_num2i_success(:long, FIXNUM_MAX+1)
+ end
+
+ def test_num2ulong
+ assert_num2i_success(:ulong, 0)
+ assert_num2i_success(:ulong, ULONG_MAX)
+ assert_num2i_success(:ulong, -1, ULONG_MAX)
+ assert_num2i_success(:ulong, LONG_MIN, LONG_MAX+1)
+ assert_num2i_success(:ulong, LONG_MIN+1, LONG_MAX+2)
+ assert_num2i_error(:ulong, LONG_MIN-1)
+ assert_num2i_error(:ulong, ULONG_MAX+1)
+ assert_num2i_success(:ulong, FIXNUM_MIN, ULONG_MAX-FIXNUM_MAX)
+ assert_num2i_success(:ulong, FIXNUM_MIN+1, ULONG_MAX-FIXNUM_MAX+1)
+ assert_num2i_success(:ulong, FIXNUM_MIN-1, ULONG_MAX-FIXNUM_MAX-1)
+ assert_num2i_success(:ulong, FIXNUM_MAX, FIXNUM_MAX)
+ assert_num2i_success(:ulong, FIXNUM_MAX+1, FIXNUM_MAX+1)
+ end
+
+ def test_num2ll
+ assert_num2i_success(:ll, LLONG_MIN)
+ assert_num2i_success(:ll, LLONG_MIN+1)
+ assert_num2i_success(:ll, LLONG_MAX)
+ assert_num2i_error(:ll, LLONG_MIN-1)
+ assert_num2i_error(:ll, LLONG_MAX+1)
+ assert_num2i_success(:ll, FIXNUM_MIN)
+ assert_num2i_success(:ll, FIXNUM_MIN+1)
+ assert_num2i_success(:ll, FIXNUM_MIN-1)
+ assert_num2i_success(:ll, FIXNUM_MAX)
+ assert_num2i_success(:ll, FIXNUM_MAX+1)
+ end if defined?(Num2int.NUM2LL)
+
+ def test_num2ull
+ assert_num2i_success(:ull, 0)
+ assert_num2i_success(:ull, ULLONG_MAX)
+ assert_num2i_success(:ull, -1, ULLONG_MAX)
+ assert_num2i_success(:ull, LLONG_MIN, LLONG_MAX+1)
+ assert_num2i_success(:ull, LLONG_MIN+1, LLONG_MAX+2)
+ assert_num2i_error(:ull, LLONG_MIN-1)
+ assert_num2i_error(:ull, ULLONG_MAX+1)
+ assert_num2i_success(:ull, FIXNUM_MIN, ULLONG_MAX-FIXNUM_MAX)
+ assert_num2i_success(:ull, FIXNUM_MIN+1, ULLONG_MAX-FIXNUM_MAX+1)
+ assert_num2i_success(:ull, FIXNUM_MIN-1, ULLONG_MAX-FIXNUM_MAX-1)
+ assert_num2i_success(:ull, FIXNUM_MAX)
+ assert_num2i_success(:ull, FIXNUM_MAX+1)
+ end if defined?(Num2int.NUM2ULL)
+
+ def test_fix2short
+ assert_fix2i_success(:short, 0)
+ assert_fix2i_success(:short, SHRT_MAX)
+ assert_fix2i_success(:short, SHRT_MIN)
+ assert_fix2i_success(:short, SHRT_MIN+1)
+ assert_fix2i_error(:short, SHRT_MAX+1)
+ assert_fix2i_error(:short, SHRT_MIN-1)
+ assert_fix2i_error(:short, FIXNUM_MAX)
+ assert_fix2i_error(:short, FIXNUM_MIN)
+ assert_fix2i_error(:short, FIXNUM_MIN+1)
+ end
+
+ def test_fix2int
+ assert_fix2i_success(:int, 0)
+ assert_fix2i_success(:int, INT_MAX)
+ assert_fix2i_success(:int, INT_MIN)
+ assert_fix2i_success(:int, INT_MIN+1)
+ assert_fix2i_error(:int, INT_MAX+1)
+ assert_fix2i_error(:int, INT_MIN-1)
+ assert_fix2i_error(:int, FIXNUM_MAX) if INT_MAX < FIXNUM_MAX
+ assert_fix2i_error(:int, FIXNUM_MIN) if FIXNUM_MIN < INT_MIN
+ assert_fix2i_error(:int, FIXNUM_MIN+1) if FIXNUM_MIN+1 < INT_MIN
+ end
+
+ def test_fix2uint
+ assert_fix2i_success(:uint, 0)
+ assert_fix2i_success(:uint, UINT_MAX)
+ assert_fix2i_success(:uint, INT_MAX)
+ assert_fix2i_success(:uint, INT_MIN, INT_MAX+1)
+ assert_fix2i_success(:uint, INT_MIN+1, INT_MAX+2)
+ assert_fix2i_error(:uint, UINT_MAX+1)
+ assert_fix2i_error(:uint, INT_MIN-1)
+ assert_fix2i_error(:uint, FIXNUM_MAX) if UINT_MAX < FIXNUM_MAX
+ assert_fix2i_error(:uint, FIXNUM_MIN) if FIXNUM_MIN < INT_MIN
+ assert_fix2i_error(:uint, FIXNUM_MIN+1) if FIXNUM_MIN+1 < INT_MIN
+ end
+
+ def test_fix2long
+ assert_fix2i_success(:long, 0)
+ assert_fix2i_success(:long, FIXNUM_MAX)
+ assert_fix2i_success(:long, FIXNUM_MIN)
+ assert_fix2i_success(:long, FIXNUM_MIN+1)
+ end
+
+ def test_fix2ulong
+ assert_fix2i_success(:ulong, 0)
+ assert_fix2i_success(:ulong, FIXNUM_MAX)
+ assert_fix2i_success(:ulong, -1, ULONG_MAX)
+ end
+
+end
+
+
diff --git a/jni/ruby/test/-ext-/path_to_class/test_path_to_class.rb b/jni/ruby/test/-ext-/path_to_class/test_path_to_class.rb
new file mode 100644
index 0000000..fdf4097
--- /dev/null
+++ b/jni/ruby/test/-ext-/path_to_class/test_path_to_class.rb
@@ -0,0 +1,12 @@
+require 'test/unit'
+
+class Test_PathToClass < Test::Unit::TestCase
+ require '-test-/path_to_class/path_to_class'
+
+ def test_path_to_class
+ bug5691 = '[ruby-core:41410]'
+ assert_raise(ArgumentError, bug5691) {
+ Test_PathToClass.path_to_class("Test_PathToClass::Object")
+ }
+ end
+end
diff --git a/jni/ruby/test/-ext-/postponed_job/test_postponed_job.rb b/jni/ruby/test/-ext-/postponed_job/test_postponed_job.rb
new file mode 100644
index 0000000..032e35c
--- /dev/null
+++ b/jni/ruby/test/-ext-/postponed_job/test_postponed_job.rb
@@ -0,0 +1,28 @@
+require 'test/unit'
+require 'thread'
+require '-test-/postponed_job'
+
+module Bug
+ def self.postponed_job_call_direct_wrapper(*args)
+ postponed_job_call_direct(*args)
+ end
+
+ def self.postponed_job_register_wrapper(*args)
+ postponed_job_register(*args)
+ end
+end
+
+class TestPostponed_job < Test::Unit::TestCase
+ def test_register
+ direct, registered = [], []
+
+ Bug.postponed_job_call_direct_wrapper(direct)
+ Bug.postponed_job_register_wrapper(registered)
+
+ assert_match( /postponed_job_call_direct_wrapper/, direct.join)
+ assert_not_match( /postponed_job_register_wrapper/, registered.join)
+
+ Bug.postponed_job_register_one(ary = [])
+ assert_equal [1], ary
+ end
+end
diff --git a/jni/ruby/test/-ext-/proc/test_bmethod.rb b/jni/ruby/test/-ext-/proc/test_bmethod.rb
new file mode 100644
index 0000000..16927dc
--- /dev/null
+++ b/jni/ruby/test/-ext-/proc/test_bmethod.rb
@@ -0,0 +1,37 @@
+require 'test/unit'
+require '-test-/proc'
+
+class TestProc < Test::Unit::TestCase
+ class TestBMethod < Test::Unit::TestCase
+ end
+end
+
+class TestProc::TestBMethod
+ class Base
+ def foo(*a)
+ a
+ end
+ end
+
+ class Bound < Base
+ define_method(:foo, Bug::Proc.make_call_super(42))
+ define_method(:receiver, Bug::Proc.make_call_receiver(nil))
+ end
+
+ def test_super_in_bmethod
+ obj = Bound.new
+ assert_equal([1, 42], obj.foo(1))
+ end
+
+ def test_block_super
+ obj = Bound.new
+ result = nil
+ obj.foo(2) {|*a| result = a}
+ assert_equal([2, 42], result)
+ end
+
+ def test_receiver_in_bmethod
+ obj = Bound.new
+ assert_same(obj, obj.receiver)
+ end
+end
diff --git a/jni/ruby/test/-ext-/rational/test_rat.rb b/jni/ruby/test/-ext-/rational/test_rat.rb
new file mode 100644
index 0000000..ef7e7fe
--- /dev/null
+++ b/jni/ruby/test/-ext-/rational/test_rat.rb
@@ -0,0 +1,31 @@
+require 'test/unit'
+require "-test-/rational"
+
+class TestRational < Test::Unit::TestCase
+ class TestGCD < Test::Unit::TestCase
+
+ def test_gcd_normal
+ x = 2*2*3*3*3
+ y = 2*2*2*3*3
+ gcd = 2*2*3*3
+ assert_equal(gcd, x.gcd_normal(y))
+ end
+
+ def test_gcd_gmp
+ x = 2*2*3*3*3
+ y = 2*2*2*3*3
+ gcd = 2*2*3*3
+ assert_equal(gcd, x.gcd_gmp(y))
+ rescue NotImplementedError
+ end
+
+ def test_gcd_gmp_brute_force
+ -13.upto(13) {|x|
+ -13.upto(13) {|y|
+ assert_equal(x.gcd_normal(y), x.gcd_gmp(y))
+ }
+ }
+ rescue NotImplementedError
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/st/test_foreach.rb b/jni/ruby/test/-ext-/st/test_foreach.rb
new file mode 100644
index 0000000..259b0a9
--- /dev/null
+++ b/jni/ruby/test/-ext-/st/test_foreach.rb
@@ -0,0 +1,15 @@
+require 'test/unit'
+require '-test-/st/foreach'
+
+class Test_StForeachUnpack < Test::Unit::TestCase
+ def test_st_foreach_check_unpack
+ assert_nil Bug.unp_st_foreach_check(:check), "goto unpacked_continue"
+ assert_nil Bug.unp_st_foreach_check(:delete1), "goto unpacked"
+ assert_nil Bug.unp_st_foreach_check(:delete2), "goto deleted"
+ end
+
+ def test_st_foreach_unpack
+ assert_nil Bug.unp_st_foreach(:unpacked), "goto unpacked"
+ assert_nil Bug.unp_st_foreach(:unpack_delete), "if (!ptr) return 0"
+ end
+end
diff --git a/jni/ruby/test/-ext-/st/test_numhash.rb b/jni/ruby/test/-ext-/st/test_numhash.rb
new file mode 100644
index 0000000..24dc87c
--- /dev/null
+++ b/jni/ruby/test/-ext-/st/test_numhash.rb
@@ -0,0 +1,49 @@
+require 'test/unit'
+require "-test-/st/numhash"
+
+class Bug::StNumHash
+ class Test_NumHash < Test::Unit::TestCase
+ def setup
+ @tbl = Bug::StNumHash.new
+ 5.times {|i| @tbl[i] = i}
+ end
+
+ def test_check
+ keys = []
+ @tbl.each do |k, v, t|
+ keys << k
+ t[5] = 5 if k == 3
+ true
+ end
+ assert_equal([*0..5], keys)
+ end
+
+ def test_update
+ assert_equal(true, @tbl.update(0) {@tbl[5] = :x})
+ assert_equal(:x, @tbl[0])
+ assert_equal(:x, @tbl[5])
+ end
+
+ def test_size_after_delete_safe
+ 10.downto(1) do |up|
+ tbl = Bug::StNumHash.new
+ 1.upto(up){|i| tbl[i] = i}
+ assert_equal(1, tbl.delete_safe(1))
+ assert_equal(up - 1, tbl.size, "delete_safe doesn't change size from #{up} to #{up-1}")
+ end
+ end
+
+ def test_delete_safe_on_iteration
+ 10.downto(1) do |up|
+ tbl = Bug::StNumHash.new
+ 1.upto(up){|i| tbl[i] = i}
+ assert_nothing_raised("delete_safe forces iteration to fail with size #{up}") do
+ tbl.each do |k, v, t|
+ assert_equal k, t.delete_safe(k)
+ true
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/st/test_update.rb b/jni/ruby/test/-ext-/st/test_update.rb
new file mode 100644
index 0000000..1b41d2b
--- /dev/null
+++ b/jni/ruby/test/-ext-/st/test_update.rb
@@ -0,0 +1,50 @@
+require 'test/unit'
+require "-test-/st/update"
+
+class Bug::StTable
+ class Test_Update < Test::Unit::TestCase
+ def setup
+ @tbl = Bug::StTable.new
+ @tbl[:a] = 1
+ @tbl[:b] = 2
+ end
+
+ def test_notfound
+ assert_equal(false, @tbl.st_update(:c) {42})
+ assert_equal({a: 1, b: 2, c: 42}, @tbl)
+ end
+
+ def test_continue
+ args = nil
+ assert_equal(true, @tbl.st_update(:a) {|*x| args = x; false})
+ assert_equal({a: 1, b: 2}, @tbl, :a)
+ assert_equal([:a, 1], args)
+ end
+
+ def test_delete
+ args = nil
+ assert_equal(true, @tbl.st_update(:a) {|*x| args = x; nil})
+ assert_equal({b: 2}, @tbl, :a)
+ assert_equal([:a, 1], args)
+ end
+
+ def test_update
+ args = nil
+ assert_equal(true, @tbl.st_update(:a) {|*x| args = x; 3})
+ assert_equal({a: 3, b: 2}, @tbl, :a)
+ assert_equal([:a, 1], args)
+ end
+
+ def test_pass_objects_in_st_table
+ bug7330 = '[ruby-core:49220]'
+ key = "abc".freeze
+ value = "def"
+ @tbl[key] = value
+ @tbl.st_update("abc") {|*args|
+ assert_same(key, args[0], bug7330)
+ assert_same(value, args[1], bug7330)
+ nil
+ }
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_coderange.rb b/jni/ruby/test/-ext-/string/test_coderange.rb
new file mode 100644
index 0000000..83cebf1
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_coderange.rb
@@ -0,0 +1,59 @@
+# coding: ascii-8bit
+require 'test/unit'
+require "-test-/string/string"
+require "rbconfig/sizeof"
+
+class Test_StringCoderange < Test::Unit::TestCase
+ def setup
+ @sizeof_voidp = RbConfig::SIZEOF["void*"]
+ @a8 = Encoding::ASCII_8BIT
+ @a7 = Encoding::US_ASCII
+ @u8 = Encoding::UTF_8
+ end
+
+ def test_ascii8bit
+ enc = @a8
+ str = "a"
+ str.force_encoding(enc)
+ assert_equal :"7bit", Bug::String.new(str).coderange_scan
+
+ str = "a\xBE".force_encoding(enc)
+ assert_equal :valid, Bug::String.new(str).coderange_scan
+ end
+
+ def test_usascii
+ enc = @a7
+ str = "a"
+ str.force_encoding(enc)
+ assert_equal :"7bit", Bug::String.new(str).coderange_scan
+
+ str = "a" * (@sizeof_voidp * 2)
+ str << "\xBE"
+ str.force_encoding(enc)
+ assert_equal :broken, Bug::String.new(str).coderange_scan
+ end
+
+ def test_utf8
+ enc = @u8
+ str = "a"
+ str.force_encoding(enc)
+ assert_equal :"7bit", Bug::String.new(str).coderange_scan
+
+ str = "a" * (@sizeof_voidp * 3)
+ str << "aa\xC2\x80"
+ str.force_encoding(enc)
+ assert_equal :valid, Bug::String.new(str).coderange_scan
+
+ str = "a" * (@sizeof_voidp * 2)
+ str << "\xC2\x80"
+ str << "a" * (@sizeof_voidp * 2)
+ str.force_encoding(enc)
+ assert_equal :valid, Bug::String.new(str).coderange_scan
+
+ str = "a" * (@sizeof_voidp * 2)
+ str << "\xC1\x80"
+ str << "a" * (@sizeof_voidp * 2)
+ str.force_encoding(enc)
+ assert_equal :broken, Bug::String.new(str).coderange_scan
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_cstr.rb b/jni/ruby/test/-ext-/string/test_cstr.rb
new file mode 100644
index 0000000..272e090
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_cstr.rb
@@ -0,0 +1,119 @@
+require 'test/unit'
+require "-test-/string/string"
+
+class Test_StringCStr < Test::Unit::TestCase
+ Bug4319 = '[ruby-dev:43094]'
+
+ def test_embed
+ s = Bug::String.new("abcdef")
+ s.set_len(3)
+ assert_equal(0, s.cstr_term, Bug4319)
+ end
+
+ def test_long
+ s = Bug::String.new("abcdef")*100000
+ assert_equal(0, s.cstr_term, Bug4319)
+ end
+
+ WCHARS = [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE]
+
+ def test_wchar_embed
+ WCHARS.each do |enc|
+ s = Bug::String.new("\u{4022}a".encode(enc))
+ assert_nothing_raised(ArgumentError) {s.cstr_term}
+ s.set_len(s.bytesize / 2)
+ assert_equal(1, s.size)
+ assert_equal(0, s.cstr_term)
+ end
+ end
+
+ def test_wchar_long
+ str = "\u{4022}abcdef"
+ n = 100
+ len = str.size * n
+ WCHARS.each do |enc|
+ s = Bug::String.new(str.encode(enc))*n
+ assert_nothing_raised(ArgumentError, enc.name) {s.cstr_term}
+ s.set_len(s.bytesize / 2)
+ assert_equal(len / 2, s.size, enc.name)
+ assert_equal(0, s.cstr_term, enc.name)
+ end
+ end
+
+ def test_wchar_lstrip!
+ assert_wchars_term_char(" a") {|s| s.lstrip!}
+ end
+
+ def test_wchar_rstrip!
+ assert_wchars_term_char("a ") {|s| s.rstrip!}
+ end
+
+ def test_wchar_chop!
+ assert_wchars_term_char("a\n") {|s| s.chop!}
+ end
+
+ def test_wchar_chomp!
+ assert_wchars_term_char("a\n") {|s| s.chomp!}
+ end
+
+ def test_wchar_aset
+ assert_wchars_term_char("a"*30) {|s| s[29,1] = ""}
+ end
+
+ def test_wchar_sub!
+ assert_wchars_term_char("foobar") {|s| s.sub!(/#{"foo".encode(s.encoding)}/, "")}
+ end
+
+ def test_wchar_delete!
+ assert_wchars_term_char("foobar") {|s| s.delete!("ao".encode(s.encoding))}
+ end
+
+ def test_wchar_squeeze!
+ assert_wchars_term_char("foo!") {|s| s.squeeze!}
+ end
+
+ def test_wchar_tr!
+ assert_wchars_term_char("\u{3042}foobar") {|s|
+ enc = s.encoding
+ s.tr!("\u{3042}".encode(enc), "c".encode(enc))
+ }
+ end
+
+ def test_wchar_tr_s!
+ assert_wchars_term_char("\u{3042}foobar") {|s|
+ enc = s.encoding
+ s.tr_s!("\u{3042}".encode(enc), "c".encode(enc))
+ }
+ end
+
+ def test_embedded_from_heap
+ gh821 = "[GH-821]"
+ embedded_string = "abcdefghi"
+ string = embedded_string.gsub("efg", "123")
+ {}[string] = 1
+ non_terminated = "#{string}#{nil}"
+ assert_nil(Bug::String.cstr_term_char(non_terminated), gh821)
+
+ result = {}
+ WCHARS.map do |enc|
+ embedded_string = "ab".encode(enc)
+ string = embedded_string.gsub("b".encode(enc), "1".encode(enc))
+ {}[string] = 1
+ non_terminated = "#{string}#{nil}"
+ c = Bug::String.cstr_term_char(non_terminated)
+ result[enc] = c if c
+ end
+ assert_empty(result, gh821)
+ end
+
+ def assert_wchars_term_char(str)
+ result = {}
+ WCHARS.map do |enc|
+ s = Bug::String.new(str.encode(enc))
+ yield s
+ c = s.cstr_term_char
+ result[enc] = c if c
+ end
+ assert_empty(result)
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_ellipsize.rb b/jni/ruby/test/-ext-/string/test_ellipsize.rb
new file mode 100644
index 0000000..2c14c0c
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_ellipsize.rb
@@ -0,0 +1,46 @@
+require 'test/unit'
+require "-test-/string/string"
+
+class Test_StringEllipsize < Test::Unit::TestCase
+ def setup
+ @foobar = Bug::String.new("foobar")
+ end
+
+ def assert_equal_with_class(expected, result, *rest)
+ assert_equal(expected.encoding, result.encoding, *rest)
+ assert_equal(expected, result, result.encoding.name)
+ assert_instance_of(Bug::String, result, *rest)
+ end
+
+ def test_longer
+ assert_equal_with_class("", @foobar.ellipsize(0))
+ assert_equal_with_class(".", @foobar.ellipsize(1))
+ assert_equal_with_class("..", @foobar.ellipsize(2))
+ assert_equal_with_class("...", @foobar.ellipsize(3))
+ assert_equal_with_class("f...", @foobar.ellipsize(4))
+ assert_equal_with_class("fo...", @foobar.ellipsize(5))
+ end
+
+ def test_shorter
+ assert_same(@foobar, @foobar.ellipsize(6))
+ assert_same(@foobar, @foobar.ellipsize(7))
+ end
+
+ def test_negative_length
+ assert_raise(IndexError) {@foobar.ellipsize(-1)}
+ end
+
+ def test_nonascii
+ a = "\u3042"
+ Encoding.list.each do |enc|
+ next if enc.dummy?
+ begin
+ s = a.encode(enc)
+ e = "...".encode(enc)
+ rescue
+ else
+ assert_equal_with_class(s*12+e, Bug::String.new(s*20).ellipsize(15))
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_enc_associate.rb b/jni/ruby/test/-ext-/string/test_enc_associate.rb
new file mode 100644
index 0000000..edddfb4
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_enc_associate.rb
@@ -0,0 +1,12 @@
+require 'test/unit'
+require "-test-/string/string"
+
+class Test_StrEncAssociate < Test::Unit::TestCase
+ def test_frozen
+ s = Bug::String.new("abc")
+ s.force_encoding(Encoding::US_ASCII)
+ s.freeze
+ assert_raise(RuntimeError) {s.associate_encoding!(Encoding::US_ASCII)}
+ assert_raise(RuntimeError) {s.associate_encoding!(Encoding::UTF_8)}
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_enc_str_buf_cat.rb b/jni/ruby/test/-ext-/string/test_enc_str_buf_cat.rb
new file mode 100644
index 0000000..9582fe2
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_enc_str_buf_cat.rb
@@ -0,0 +1,15 @@
+require 'test/unit'
+require "-test-/string/string"
+
+class Test_StringEncStrBufCat < Test::Unit::TestCase
+ Bug6509 = '[ruby-dev:45688]'
+
+ def test_unknown
+ a8_str = "a\xBE".force_encoding(Encoding::ASCII_8BIT)
+ cr_unknown_str = [0x62].pack('C*')
+ assert_equal(true, a8_str.valid_encoding?, "an assertion for following tests")
+ assert_equal(:valid, Bug::String.new(a8_str).coderange, "an assertion for following tests")
+ assert_equal(:unknown, Bug::String.new(cr_unknown_str).coderange, "an assertion for following tests")
+ assert_equal(:valid, Bug::String.new(a8_str).enc_str_buf_cat(cr_unknown_str).coderange, Bug6509)
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_modify_expand.rb b/jni/ruby/test/-ext-/string/test_modify_expand.rb
new file mode 100644
index 0000000..34b7be7
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_modify_expand.rb
@@ -0,0 +1,15 @@
+require 'test/unit'
+require "-test-/string/string"
+
+class Test_StringModifyExpand < Test::Unit::TestCase
+ def test_modify_expand_memory_leak
+ assert_no_memory_leak(["-r-test-/string/string"],
+ <<-PRE, <<-CMD, "rb_str_modify_expand()", limit: 2.5)
+ s=Bug::String.new
+ PRE
+ size = $initial_size
+ 10.times{s.modify_expand!(size)}
+ s.replace("")
+ CMD
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_nofree.rb b/jni/ruby/test/-ext-/string/test_nofree.rb
new file mode 100644
index 0000000..234c84d
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_nofree.rb
@@ -0,0 +1,10 @@
+require 'test/unit'
+
+class Test_StringNoFree < Test::Unit::TestCase
+ def test_no_memory_leak
+ bug10942 = '[ruby-core:68436] [Bug #10942] no leak on nofree string'
+ assert_no_memory_leak(%w(-r-test-/string/string), '',
+ '1000000.times {Bug::String.nofree << "a" * 100}',
+ bug10942, rss: true, limit: 2.0)
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_normalize.rb b/jni/ruby/test/-ext-/string/test_normalize.rb
new file mode 100644
index 0000000..283ca93
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_normalize.rb
@@ -0,0 +1,106 @@
+require 'test/unit'
+require "-test-/string/string"
+require "tempfile"
+
+class Test_StringNormalize < Test::Unit::TestCase
+=begin
+ def test_normalize_all
+ exclude = [
+ #0x340, 0x341, 0x343, 0x344
+ ]
+ (0x0080..0xFFFD).each do |n|
+ next if 0xD800 <= n && n <= 0xDFFF
+ next if exclude.include? n
+ code = n.to_s(16)
+ Tempfile.create("#{code}-#{n.chr(Encoding::UTF_8)}-") do |tempfile|
+ ary = Dir.glob(File.expand_path("../#{code}-*", tempfile.path))
+ assert_equal 1, ary.size
+ result = ary[0]
+ rn = result[/\/\h+-(.+?)-/, 1]
+ #assert_equal tempfile.path, result, "#{rn.dump} is not U+#{n.to_s(16)}"
+ r2 = Bug::String.new(result ).normalize_ospath
+ rn2 = r2[/\/\h+-(.+?)-/, 1]
+ if tempfile.path == result
+ if tempfile.path == r2
+ else
+ puts "U+#{n.to_s(16)} shouldn't be r2#{rn2.dump}"
+ end
+ else
+ if tempfile.path == r2
+ # puts "U+#{n.to_s(16)} shouldn't be r#{rn.dump}"
+ elsif result == r2
+ puts "U+#{n.to_s(16)} shouldn't be #{rn.dump}"
+ else
+ puts "U+#{n.to_s(16)} shouldn't be r#{rn.dump} r2#{rn2.dump}"
+ end
+ end
+ end
+ end
+ end
+=end
+
+ def test_normalize
+ %[
+ \u304C \u304B\u3099
+ \u3077 \u3075\u309A
+ \u308F\u3099 \u308F\u3099
+ \u30F4 \u30A6\u3099
+ \u30DD \u30DB\u309A
+ \u30AB\u303A \u30AB\u303A
+ \u00C1 A\u0301
+ B\u030A B\u030A
+ \u0386 \u0391\u0301
+ \u03D3 \u03D2\u0301
+ \u0401 \u0415\u0308
+ \u2260 =\u0338
+ \u{c548} \u{110b}\u{1161}\u{11ab}
+ ].scan(/(\S+)\s+(\S+)/) do |expected, src|
+ result = Bug::String.new(src).normalize_ospath
+ assert_equal expected, result,
+ "#{expected.dump} is expected but #{src.dump}"
+ end
+ rescue NotImplementedError
+ end
+
+ def test_not_normalize_kc
+ %[
+ \u2460
+ \u2162
+ \u3349
+ \u33A1
+ \u337B
+ \u2116
+ \u33CD
+ \u2121
+ \u32A4
+ \u3231
+ ].split.each do |src|
+ result = Bug::String.new(src).normalize_ospath
+ assert_equal src, result,
+ "#{src.dump} is expected not to be normalized, but #{result.dump}"
+ end
+ rescue NotImplementedError
+ end
+
+ def test_dont_normalize_hfsplus
+ %[
+ \u2190\u0338
+ \u219A
+ \u212B
+ \uF90A
+ \uF9F4
+ \uF961 \uF9DB
+ \uF96F \uF3AA
+ \uF915 \uF95C \uF9BF
+ \uFA0C
+ \uFA10
+ \uFA19
+ \uFA26
+ ].split.each do |src|
+ result = Bug::String.new(src).normalize_ospath
+ assert_equal src, result,
+ "#{src.dump} is expected not to be normalized, but #{result.dump}"
+ end
+ rescue NotImplementedError
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_qsort.rb b/jni/ruby/test/-ext-/string/test_qsort.rb
new file mode 100644
index 0000000..3a58523
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_qsort.rb
@@ -0,0 +1,19 @@
+require 'test/unit'
+require "-test-/string/string"
+
+class Test_StringQSort < Test::Unit::TestCase
+ def test_qsort
+ s = Bug::String.new("xxozfxx")
+ s.qsort!
+ assert_equal("foxxxxz", s)
+ end
+
+ def test_qsort_slice
+ s = Bug::String.new("xxofzx1")
+ s.qsort!(nil, nil, 3)
+ assert_equal("fzxxxo1", s)
+ s = Bug::String.new("xxofzx231")
+ s.qsort!(nil, nil, 3)
+ assert_equal("231fzxxxo", s)
+ end
+end
diff --git a/jni/ruby/test/-ext-/string/test_set_len.rb b/jni/ruby/test/-ext-/string/test_set_len.rb
new file mode 100644
index 0000000..e257cac
--- /dev/null
+++ b/jni/ruby/test/-ext-/string/test_set_len.rb
@@ -0,0 +1,25 @@
+require 'test/unit'
+require "-test-/string/string"
+
+class Test_StrSetLen < Test::Unit::TestCase
+ def setup
+ @s0 = [*"a".."z"].join("").freeze
+ @s1 = Bug::String.new(@s0)
+ end
+
+ def teardown
+ orig = [*"a".."z"].join("")
+ assert_equal(orig, @s0)
+ end
+
+ def test_non_shared
+ @s1.modify!
+ assert_equal("abc", @s1.set_len(3))
+ end
+
+ def test_shared
+ assert_raise(RuntimeError) {
+ assert_equal("abc", @s1.set_len(3))
+ }
+ end
+end
diff --git a/jni/ruby/test/-ext-/struct/test_member.rb b/jni/ruby/test/-ext-/struct/test_member.rb
new file mode 100644
index 0000000..18e9808
--- /dev/null
+++ b/jni/ruby/test/-ext-/struct/test_member.rb
@@ -0,0 +1,15 @@
+require 'test/unit'
+require "-test-/struct"
+
+class Bug::Struct::Test_Member < Test::Unit::TestCase
+ S = Bug::Struct.new(:a)
+
+ def test_member_get
+ s = S.new(1)
+ assert_equal(1, s.get(:a))
+ assert_raise_with_message(NameError, /is not a struct member/) {s.get(:b)}
+ EnvUtil.with_default_external(Encoding::UTF_8) do
+ assert_raise_with_message(NameError, /\u{3042}/) {s.get(:"\u{3042}")}
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb b/jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb
new file mode 100644
index 0000000..f772086
--- /dev/null
+++ b/jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb
@@ -0,0 +1,473 @@
+require 'test/unit'
+require "-test-/symbol"
+
+module Test_Symbol
+ class TestInadvertent < Test::Unit::TestCase
+ def noninterned_name(prefix = "")
+ prefix += "_#{Thread.current.object_id.to_s(36).tr('-', '_')}"
+ begin
+ name = "#{prefix}_#{rand(0x1000).to_s(16)}_#{Time.now.usec}"
+ end while Bug::Symbol.find(name)
+ name
+ end
+
+ def setup
+ @obj = Object.new
+ end
+
+ def assert_not_pinneddown(name, msg = nil)
+ assert_not_send([Bug::Symbol, :pinneddown?, name], msg)
+ end
+
+ def assert_not_interned(name, msg = nil)
+ assert_not_send([Bug::Symbol, :find, name], msg)
+ end
+
+ def assert_not_interned_error(obj, meth, name, msg = nil, &block)
+ e = assert_raise(NameError, msg) {obj.__send__(meth, name, &block)}
+ if Symbol === name
+ assert_not_pinneddown(name, msg)
+ else
+ assert_not_interned(name, msg)
+ end
+ e
+ end
+
+ def assert_not_interned_false(obj, meth, name, msg = nil)
+ assert_not_send([obj, meth, name], msg)
+ if Symbol === name
+ assert_not_pinneddown(name, msg)
+ else
+ assert_not_interned(name, msg)
+ end
+ end
+
+ Feature5072 = '[ruby-core:38367]'
+
+ def test_module_const_get
+ cl = Class.new
+ name = noninterned_name("A")
+
+ assert_not_interned_error(cl, :const_get, name, Feature5072)
+
+ assert_not_interned_error(cl, :const_get, name.to_sym)
+ end
+
+ def test_module_const_defined?
+ cl = Class.new
+ name = noninterned_name("A")
+
+ assert_not_interned_false(cl, :const_defined?, name, Feature5072)
+
+ name = noninterned_name
+ assert_not_interned_error(cl, :const_defined?, name.to_sym)
+ end
+
+ def test_respond_to_missing
+ feature5072 = Feature5072
+ c = Class.new do
+ def self.respond_to_missing?(*)
+ super
+ end
+ end
+ s = noninterned_name
+
+ # assert_not_interned_false(c, :respond_to?, s, feature5072)
+ assert_not_interned_false(c, :method_defined?, s, feature5072)
+ assert_not_interned_false(c, :public_method_defined?, s, feature5072)
+ assert_not_interned_false(c, :private_method_defined?, s, feature5072)
+ assert_not_interned_false(c, :protected_method_defined?, s, feature5072)
+ assert_not_interned_false(c, :const_defined?, noninterned_name("A"), feature5072)
+ assert_not_interned_false(c, :instance_variable_defined?, noninterned_name("@"), feature5072)
+ assert_not_interned_false(c, :class_variable_defined?, noninterned_name("@@"), feature5072)
+ end
+
+ def test_missing_method
+ bug10985 = '[ruby-core:68564] [Bug #10985]'
+ m = nil
+ c = Class.new do
+ def self.respond_to_missing?(*)
+ true
+ end
+ end
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.method(s)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.public_method(s.to_sym)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.singleton_method(s.to_sym)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+ end
+
+ Feature5079 = '[ruby-core:38404]'
+
+ def test_undefined_instance_variable
+ feature5079 = feature5079
+ c = Class.new
+ iv = noninterned_name("@")
+
+ assert_not_interned_false(c, :instance_variable_get, iv, feature5079)
+ assert_not_interned_error(c, :remove_instance_variable, iv, feature5079)
+ end
+
+ def test_undefined_class_variable
+ feature5079 = feature5079
+ c = Class.new
+ cv = noninterned_name("@@")
+
+ assert_not_interned_error(c, :class_variable_get, cv, feature5079)
+ assert_not_interned_error(c, :remove_class_variable, cv, feature5079)
+ end
+
+
+ def test_undefined_const
+ feature5079 = feature5079
+ c = Class.new
+ s = noninterned_name("A")
+
+ assert_not_interned_error(c, :remove_const, s, feature5079)
+ end
+
+ def test_undefined_method
+ feature5079 = feature5079
+ c = Class.new
+ s = noninterned_name
+
+ assert_not_interned_error(c, :method, s, feature5079)
+ assert_not_interned_error(c, :public_method, s, feature5079)
+ assert_not_interned_error(c, :instance_method, s, feature5079)
+ assert_not_interned_error(c, :public_instance_method, s, feature5079)
+ end
+
+ Feature5089 = '[ruby-core:38447]'
+ def test_const_missing
+ feature5089 = Feature5089
+ c = Class.new do
+ def self.const_missing(const_name)
+ raise NameError, const_name.to_s
+ end
+ end
+ s = noninterned_name("A")
+
+ assert_not_interned_error(c, :const_get, s.to_sym, feature5089)
+ assert_not_interned_false(c, :autoload?, s.to_sym, feature5089)
+ end
+
+ def test_aliased_method
+ feature5089 = Feature5089
+ c = Class.new do
+ def self.alias_method(str)
+ super(:puts, str)
+ end
+ end
+ s = noninterned_name
+
+ assert_not_interned_error(c, :alias_method, s, feature5089)
+ assert_not_interned_error(c, :private_class_method, s, feature5089)
+ assert_not_interned_error(c, :private_constant, s, feature5089)
+ assert_not_interned_error(c, :private, s, feature5089)
+ assert_not_interned_error(c, :protected, s, feature5089)
+ assert_not_interned_error(c, :public, s, feature5089)
+ assert_not_interned_error(c, :public_class_method, s, feature5089)
+ assert_not_interned_error(c, :public_constant, s, feature5089)
+ assert_not_interned_error(c, :remove_method, s, feature5089)
+ assert_not_interned_error(c, :undef_method, s, feature5089)
+ assert_not_interned_error(c, :untrace_var, s, feature5089)
+ end
+
+ Feature5112 = '[ruby-core:38576]'
+
+ def test_public_send
+ name = noninterned_name
+ e = assert_raise(NoMethodError) {@obj.public_send(name, Feature5112)}
+ assert_not_interned(name)
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+
+ def test_send
+ name = noninterned_name
+ e = assert_raise(NoMethodError) {@obj.send(name, Feature5112)}
+ assert_not_interned(name)
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+
+ def test___send__
+ name = noninterned_name
+ e = assert_raise(NoMethodError) {@obj.__send__(name, Feature5112)}
+ assert_not_interned(name)
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+
+ def test_thread_aref
+ Thread.current[:test] = nil
+ name = noninterned_name
+ assert_nil(Thread.current[name])
+ assert_not_interned(name)
+ end
+
+ def test_thread_key?
+ Thread.current[:test] = nil
+ name = noninterned_name
+ assert_not_send([Thread.current, :key?, name])
+ assert_not_interned(name)
+ end
+
+ def test_thread_variable_get
+ Thread.current.thread_variable_set(:test, nil)
+ name = noninterned_name
+ assert_nil(Thread.current.thread_variable_get(name))
+ assert_not_pinneddown(name)
+ end
+
+ def test_thread_variable_set
+ name = noninterned_name
+ Thread.current.thread_variable_set(name, 42)
+ assert_not_pinneddown(name)
+ end
+
+ def test_thread_variable?
+ Thread.current.thread_variable_set(:test, nil)
+ name = noninterned_name
+ assert_not_send([Thread.current, :thread_variable?, name])
+ assert_not_interned(name)
+ end
+
+ def test_enumerable_inject_op
+ name = noninterned_name
+ assert_raise(NoMethodError) {[1, 2].inject(name)}
+ assert_not_interned(name)
+ end
+
+ def test_module_const_set
+ name = noninterned_name
+ mod = Module.new
+ assert_raise(NameError) {mod.const_set(name, true)}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.const_set(name.to_sym, true)}
+ assert_not_pinneddown(name)
+ end
+
+ def test_module_cvar_set
+ name = noninterned_name
+ mod = Module.new
+ assert_raise(NameError) {mod.class_variable_set(name, true)}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.class_variable_set(name.to_sym, true)}
+ assert_not_pinneddown(name)
+ end
+
+ def test_object_ivar_set
+ name = noninterned_name
+ obj = Object.new
+ assert_raise(NameError) {obj.instance_variable_set(name, true)}
+ assert_not_interned(name)
+ assert_raise(NameError) {obj.instance_variable_set(name.to_sym, true)}
+ assert_not_pinneddown(name)
+ end
+
+ def test_struct_new
+ name = noninterned_name
+ assert_raise(NameError) {Struct.new(name)}
+ assert_not_interned(name)
+ end
+
+ def test_struct_aref
+ s = Struct.new(:foo).new
+ name = noninterned_name
+ assert_raise(NameError) {s[name]}
+ assert_not_interned(name)
+ end
+
+ def test_struct_aset
+ s = Struct.new(:foo).new
+ name = noninterned_name
+ assert_raise(NameError) {s[name] = true}
+ assert_not_interned(name)
+ end
+
+ def test_invalid_attr
+ name = noninterned_name("*")
+ mod = Module.new
+ assert_raise(NameError) {mod.module_eval {attr(name)}}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.module_eval {attr(name.to_sym)}}
+ assert_not_pinneddown(name)
+ end
+
+ def test_invalid_attr_reader
+ name = noninterned_name("*")
+ mod = Module.new
+ assert_raise(NameError) {mod.module_eval {attr_reader(name)}}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.module_eval {attr_reader(name.to_sym)}}
+ assert_not_pinneddown(name)
+ end
+
+ def test_invalid_attr_writer
+ name = noninterned_name("*")
+ mod = Module.new
+ assert_raise(NameError) {mod.module_eval {attr_writer(name)}}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.module_eval {attr_writer(name.to_sym)}}
+ assert_not_pinneddown(name)
+ end
+
+ def test_invalid_attr_accessor
+ name = noninterned_name("*")
+ mod = Module.new
+ assert_raise(NameError) {mod.module_eval {attr_accessor(name)}}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.module_eval {attr_accessor(name.to_sym)}}
+ assert_not_pinneddown(name)
+ end
+
+ def test_gc_attrset
+ assert_separately(['-r-test-/symbol', '-', '[ruby-core:62226] [Bug #9787]'], <<-'end;') # begin
+ bug = ARGV.shift
+ def noninterned_name(prefix = "")
+ prefix += "_#{Thread.current.object_id.to_s(36).tr('-', '_')}"
+ begin
+ name = "#{prefix}_#{rand(0x1000).to_s(16)}_#{Time.now.usec}"
+ end while Bug::Symbol.find(name) or Bug::Symbol.find(name + "=")
+ name
+ end
+ names = Array.new(1000) {noninterned_name("gc")}
+ names.each {|n| n.to_sym}
+ GC.start(immediate_sweep: false)
+ names.each do |n|
+ eval(":#{n}=")
+ assert_nothing_raised(TypeError, bug) {eval("proc{self.#{n} = nil}")}
+ end
+ end;
+ end
+
+ def test_execopt_key
+ name = noninterned_name.intern
+ assert_raise(ArgumentError) {
+ system(".", name => nil)
+ }
+ assert_not_pinneddown(name)
+ end
+
+ def test_execopt_redirect_value
+ name = noninterned_name.intern
+ assert_raise(ArgumentError) {
+ system(".", [] => name)
+ }
+ assert_not_pinneddown(name)
+ end
+
+ def test_execopt_redirect_path
+ name = noninterned_name.intern
+ assert_raise(TypeError) {
+ system(".", [] => [name, 0])
+ }
+ assert_not_pinneddown(name)
+ end
+
+ def test_execopt_redirect_symbol
+ name = noninterned_name.intern
+ assert_raise(ArgumentError) {
+ system(".", in: name)
+ }
+ assert_not_pinneddown(name)
+ end
+
+ def assert_no_immortal_symbol_created(name)
+ name = noninterned_name(name)
+ yield(name)
+ assert_not_pinneddown(name)
+ end
+
+ def assert_no_immortal_symbol_in_method_missing(name)
+ assert_no_immortal_symbol_created("send should not leak - #{name}") do |name|
+ assert_raise(NoMethodError) {yield(name)}
+ end
+ end
+
+ def test_send_leak_string
+ assert_no_immortal_symbol_in_method_missing("str") do |name|
+ 42.send(name)
+ end
+ end
+
+ def test_send_leak_symbol
+ assert_no_immortal_symbol_in_method_missing("sym") do |name|
+ 42.send(name.to_sym)
+ end
+ end
+
+ def test_send_leak_string_custom_method_missing
+ x = Object.new
+ def x.method_missing(*); super; end
+ assert_no_immortal_symbol_in_method_missing("str mm") do |name|
+ x.send(name)
+ end
+ end
+
+ def test_send_leak_symbol_custom_method_missing
+ x = Object.new
+ def x.method_missing(*); super; end
+ assert_no_immortal_symbol_in_method_missing("sym mm") do |name|
+ x.send(name.to_sym)
+ end
+ end
+
+ def test_send_leak_string_no_optimization
+ assert_no_immortal_symbol_in_method_missing("str slow") do |name|
+ 42.method(:send).call(name)
+ end
+ end
+
+ def test_send_leak_symbol_no_optimization
+ assert_no_immortal_symbol_in_method_missing("sym slow") do |name|
+ 42.method(:send).call(name.to_sym)
+ end
+ end
+
+ def test_send_leak_string_custom_method_missing_no_optimization
+ x = Object.new
+ def x.method_missing(*); super; end
+ assert_no_immortal_symbol_in_method_missing("str mm slow") do |name|
+ x.method(:send).call(name)
+ end
+ end
+
+ def test_send_leak_symbol_custom_method_missing_no_optimization
+ x = Object.new
+ def x.method_missing(*); super; end
+ assert_no_immortal_symbol_in_method_missing("sym mm slow") do |name|
+ x.method(:send).call(name.to_sym)
+ end
+ end
+
+ def test_kwarg_symbol_leak_no_rest
+ foo = -> (arg: 42) {}
+ assert_no_immortal_symbol_created("kwarg no rest") do |name|
+ assert_raise(ArgumentError) { foo.call(name.to_sym => 42) }
+ end
+ end
+
+ def test_kwarg_symbol_leak_with_rest
+ foo = -> (arg: 2, **options) {}
+ assert_no_immortal_symbol_created("kwarg with rest") do |name|
+ foo.call(name.to_sym => 42)
+ end
+ end
+
+ def test_kwarg_symbol_leak_just_rest
+ foo = -> (**options) {}
+ assert_no_immortal_symbol_created("kwarg just rest") do |name|
+ foo.call(name.to_sym => 42)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/symbol/test_type.rb b/jni/ruby/test/-ext-/symbol/test_type.rb
new file mode 100644
index 0000000..f1749f5
--- /dev/null
+++ b/jni/ruby/test/-ext-/symbol/test_type.rb
@@ -0,0 +1,124 @@
+require 'test/unit'
+require "-test-/symbol"
+
+module Test_Symbol
+ class TestType < Test::Unit::TestCase
+ def test_id2str_fstring_bug9171
+ fstr = eval("# encoding: us-ascii
+ 'foobar'.freeze")
+ assert_same fstr, Bug::Symbol.id2str(:foobar)
+
+ fstr = eval("# encoding: us-ascii
+ '>'.freeze")
+ assert_same fstr, Bug::Symbol.id2str(:>)
+ end
+
+ def assert_symtype(sym, pred, msg = nil)
+ assert_send([Bug::Symbol, pred, sym], msg)
+ end
+
+ def assert_not_symtype(sym, pred, msg = nil)
+ assert_not_send([Bug::Symbol, pred, sym], msg)
+ end
+
+ def test_const
+ assert_symtype("Foo", :const?)
+ assert_not_symtype("F!", :const?)
+ assert_not_symtype("foo", :const?)
+ assert_not_symtype("@foo", :const?)
+ assert_not_symtype("@@foo", :const?)
+ assert_not_symtype("$foo", :const?)
+ assert_not_symtype("foo=", :const?)
+ assert_not_symtype("[foo]", :const?)
+ assert_not_symtype("xFoo", :const?)
+ end
+
+ def test_local
+ assert_symtype("foo", :local?)
+ assert_symtype("fooBar", :local?)
+ assert_symtype("foo_bar", :local?)
+ assert_not_symtype("foo!", :local?)
+ assert_not_symtype("foo?", :local?)
+ assert_not_symtype("Foo", :local?)
+ assert_not_symtype("@foo", :local?)
+ assert_not_symtype("@@foo", :local?)
+ assert_not_symtype("$foo", :local?)
+ assert_not_symtype("foo=", :local?)
+ assert_not_symtype("[foo]", :local?)
+ end
+
+ def test_global
+ assert_symtype("$foo", :global?)
+ assert_symtype("$$", :global?)
+ assert_not_symtype("$()", :global?)
+ assert_not_symtype("$", :global?)
+ assert_not_symtype("foo", :global?)
+ assert_not_symtype("Foo", :global?)
+ assert_not_symtype("@foo", :global?)
+ assert_not_symtype("@@foo", :global?)
+ assert_not_symtype("foo=", :global?)
+ assert_not_symtype("[foo]", :global?)
+ end
+
+ def test_instance
+ assert_symtype("@foo", :instance?)
+ assert_not_symtype("@", :instance?)
+ assert_not_symtype("@1", :instance?)
+ assert_not_symtype("@@", :instance?)
+ assert_not_symtype("foo", :instance?)
+ assert_not_symtype("Foo", :instance?)
+ assert_not_symtype("@@foo", :instance?)
+ assert_not_symtype("$foo", :instance?)
+ assert_not_symtype("foo=", :instance?)
+ assert_not_symtype("[foo]", :instance?)
+ end
+
+ def test_class
+ assert_symtype("@@foo", :class?)
+ assert_not_symtype("@@", :class?)
+ assert_not_symtype("@", :class?)
+ assert_not_symtype("@@1", :class?)
+ assert_not_symtype("foo", :class?)
+ assert_not_symtype("Foo", :class?)
+ assert_not_symtype("@foo", :class?)
+ assert_not_symtype("$foo", :class?)
+ assert_not_symtype("foo=", :class?)
+ assert_not_symtype("[foo]", :class?)
+ end
+
+ def test_attrset
+ assert_symtype("foo=", :attrset?)
+ assert_symtype("Foo=", :attrset?)
+ assert_symtype("@foo=", :attrset?)
+ assert_symtype("@@foo=", :attrset?)
+ assert_symtype("$foo=", :attrset?)
+ assert_symtype("0=", :attrset?)
+ assert_symtype("@=", :attrset?)
+ assert_symtype("@@=", :attrset?)
+ assert_not_symtype("foo", :attrset?)
+ assert_not_symtype("Foo", :attrset?)
+ assert_not_symtype("@foo", :attrset?)
+ assert_not_symtype("@@foo", :attrset?)
+ assert_not_symtype("$foo", :attrset?)
+ assert_not_symtype("[foo]", :attrset?)
+ assert_symtype("[foo]=", :attrset?)
+ assert_equal(:"foo=", Bug::Symbol.attrset("foo"))
+ assert_symtype(Bug::Symbol.attrset("foo"), :attrset?)
+ assert_equal(:"Foo=", Bug::Symbol.attrset("Foo"))
+ assert_symtype(Bug::Symbol.attrset("Foo"), :attrset?)
+ assert_equal(:"@foo=", Bug::Symbol.attrset("@foo"))
+ assert_symtype(Bug::Symbol.attrset("@foo"), :attrset?)
+ assert_equal(:"@@foo=", Bug::Symbol.attrset("@@foo"))
+ assert_symtype(Bug::Symbol.attrset("@@foo"), :attrset?)
+ assert_equal(:"$foo=", Bug::Symbol.attrset("$foo"))
+ assert_symtype(Bug::Symbol.attrset("$foo"), :attrset?)
+ assert_equal(:"[foo]=", Bug::Symbol.attrset("[foo]"))
+ assert_symtype(Bug::Symbol.attrset("[foo]"), :attrset?)
+ assert_equal(:[]=, Bug::Symbol.attrset(:[]))
+ assert_symtype(Bug::Symbol.attrset("foo?="), :attrset?)
+ assert_equal(:"foo?=", Bug::Symbol.attrset(:foo?))
+ assert_symtype(Bug::Symbol.attrset("foo!="), :attrset?)
+ assert_equal(:"foo!=", Bug::Symbol.attrset(:foo!))
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/test_bug-3571.rb b/jni/ruby/test/-ext-/test_bug-3571.rb
new file mode 100644
index 0000000..d7c26d1
--- /dev/null
+++ b/jni/ruby/test/-ext-/test_bug-3571.rb
@@ -0,0 +1,20 @@
+require 'test/unit'
+
+class Test_BUG_3571 < Test::Unit::TestCase
+ def test_block_call_id
+ bug3571 = '[ruby-dev:41852]'
+ src = <<SRC
+begin
+ Bug.start
+rescue NotImplementedError => e
+ STDERR.puts e.message, e.backtrace[$0.size..-1]
+end
+SRC
+ out = [
+ "start() function is unimplemented on this machine",
+ "-:2:in `start'",
+ "-:2:in `<main>'",
+ ]
+ assert_in_out_err(%w"-r-test-/bug-3571/bug", src, [], out, bug3571)
+ end
+end
diff --git a/jni/ruby/test/-ext-/test_bug-3662.rb b/jni/ruby/test/-ext-/test_bug-3662.rb
new file mode 100644
index 0000000..73c0697
--- /dev/null
+++ b/jni/ruby/test/-ext-/test_bug-3662.rb
@@ -0,0 +1,10 @@
+require '-test-/bug-3662/bug'
+
+class Test_BUG_3662 < Test::Unit::TestCase
+ def test_funcall_notimplement
+ bug3662 = '[ruby-dev:41953]'
+ assert_raise(NotImplementedError, bug3662) {
+ Bug.funcall(:notimplement)
+ }
+ end
+end
diff --git a/jni/ruby/test/-ext-/test_bug-5832.rb b/jni/ruby/test/-ext-/test_bug-5832.rb
new file mode 100644
index 0000000..11f8a52
--- /dev/null
+++ b/jni/ruby/test/-ext-/test_bug-5832.rb
@@ -0,0 +1,21 @@
+require '-test-/bug-5832/bug'
+
+class Test_BUG_5832 < Test::Unit::TestCase
+ def test_block_passing
+ bug5832 = '[ruby-dev:45071]'
+
+ c = Class.new do
+ define_method(:call_invoke_block_from_c) do
+ Bug.funcall_callback(self)
+ end
+
+ def callback
+ yield if block_given?
+ end
+ end
+
+ assert_nothing_raised(RuntimeError, bug5832) do
+ c.new.call_invoke_block_from_c { raise 'unreachable' }
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/test_printf.rb b/jni/ruby/test/-ext-/test_printf.rb
new file mode 100644
index 0000000..1f06ad6
--- /dev/null
+++ b/jni/ruby/test/-ext-/test_printf.rb
@@ -0,0 +1,186 @@
+require 'test/unit'
+require "-test-/printf"
+require_relative '../ruby/allpairs'
+
+class Test_SPrintf < Test::Unit::TestCase
+ def to_s
+ "#{self.class}:#{object_id}"
+ end
+
+ def inspect
+ "<#{self.class}:#{object_id}>"
+ end
+
+ def test_to_str
+ assert_equal("<#{self.class}:#{object_id}>", Bug::Printf.s(self))
+ end
+
+ def test_inspect
+ assert_equal("{<#{self.class}:#{object_id}>}", Bug::Printf.v(self))
+ end
+
+ def test_quote
+ assert_equal('["\n"]', Bug::Printf.q("\n"))
+ assert_equal('[aaa]', Bug::Printf.q('aaa'))
+ assert_equal('[a a]', Bug::Printf.q('a a'))
+ end
+
+ def test_encoding
+ def self.to_s
+ "\u{3042 3044 3046 3048 304a}"
+ end
+ assert_equal("<\u{3042 3044 3046 3048 304a}>", Bug::Printf.s(self))
+ end
+
+ def test_taint
+ obj = Object.new.taint
+ assert_equal({to_s: true, inspect: true},
+ {
+ to_s: Bug::Printf.s(obj).tainted?,
+ inspect: Bug::Printf.v(obj).tainted?,
+ })
+ end
+
+ VS = [
+ #-0x1000000000000000000000000000000000000000000000002,
+ #-0x1000000000000000000000000000000000000000000000001,
+ #-0x1000000000000000000000000000000000000000000000000,
+ #-0xffffffffffffffffffffffffffffffffffffffffffffffff,
+ #-0x1000000000000000000000002,
+ #-0x1000000000000000000000001,
+ #-0x1000000000000000000000000,
+ #-0xffffffffffffffffffffffff,
+ -0x10000000000000002,
+ -0x10000000000000001,
+ -0x10000000000000000,
+ -0xffffffffffffffff,
+ -0x4000000000000002,
+ -0x4000000000000001,
+ -0x4000000000000000,
+ -0x3fffffffffffffff,
+ -0x100000002,
+ -0x100000001,
+ -0x100000000,
+ -0xffffffff,
+ #-0xc717a08d, # 0xc717a08d * 0x524b2245 = 0x4000000000000001
+ -0x80000002,
+ -0x80000001,
+ -0x80000000,
+ -0x7fffffff,
+ #-0x524b2245,
+ -0x40000002,
+ -0x40000001,
+ -0x40000000,
+ -0x3fffffff,
+ #-0x10002,
+ #-0x10001,
+ #-0x10000,
+ #-0xffff,
+ #-0x8101, # 0x8101 * 0x7f01 = 0x40000001
+ #-0x8002,
+ #-0x8001,
+ #-0x8000,
+ #-0x7fff,
+ #-0x7f01,
+ #-65,
+ #-64,
+ #-63,
+ #-62,
+ #-33,
+ #-32,
+ #-31,
+ #-30,
+ -3,
+ -2,
+ -1,
+ 0,
+ 1,
+ 2,
+ 3,
+ #30,
+ #31,
+ #32,
+ #33,
+ #62,
+ #63,
+ #64,
+ #65,
+ #0x7f01,
+ #0x7ffe,
+ #0x7fff,
+ #0x8000,
+ #0x8001,
+ #0x8101,
+ #0xfffe,
+ #0xffff,
+ #0x10000,
+ #0x10001,
+ 0x3ffffffe,
+ 0x3fffffff,
+ 0x40000000,
+ 0x40000001,
+ #0x524b2245,
+ 0x7ffffffe,
+ 0x7fffffff,
+ 0x80000000,
+ 0x80000001,
+ #0xc717a08d,
+ 0xfffffffe,
+ 0xffffffff,
+ 0x100000000,
+ 0x100000001,
+ 0x3ffffffffffffffe,
+ 0x3fffffffffffffff,
+ 0x4000000000000000,
+ 0x4000000000000001,
+ 0xfffffffffffffffe,
+ 0xffffffffffffffff,
+ 0x10000000000000000,
+ 0x10000000000000001,
+ #0xffffffffffffffffffffffff,
+ #0x1000000000000000000000000,
+ #0x1000000000000000000000001,
+ #0xffffffffffffffffffffffffffffffffffffffffffffffff,
+ #0x1000000000000000000000000000000000000000000000000,
+ #0x1000000000000000000000000000000000000000000000001
+ ]
+ VS.reverse!
+
+ FLAGS = [[nil, ' '], [nil, '#'], [nil, '+'], [nil, '-'], [nil, '0']]
+
+ def self.assertions_format_integer(format, type, **opts)
+ proc {
+ VS.each {|v|
+ begin
+ r = Bug::Printf.(type, v, **opts)
+ rescue RangeError
+ else
+ e = sprintf format, v
+ assert_equal([e, format], r, "rb_sprintf(#{format.dump}, #{v})")
+ end
+ }
+ }
+ end
+
+ AllPairs.each(%w[d],
+ # octal and hexadecimal deal with negative values differently
+ [nil, 0, 5, 20],
+ [nil, true, 0], # 8, 20
+ *FLAGS) {
+ |type, width, prec, sp, hs, pl, mi, zr|
+ precision = ".#{prec unless prec == true}" if prec
+ format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
+ define_method("test_format_integer(#{format})",
+ assertions_format_integer(format, type,
+ space: sp, hash: hs,
+ plus: pl, minus: mi,
+ zero: zr, width: width,
+ prec: prec))
+ }
+
+ def test_string_prec
+ assert_equal("a", Bug::Printf.("s", "a", prec: 3)[0])
+ assert_equal(" a", Bug::Printf.("s", "a", width: 3, prec: 3)[0])
+ assert_equal("a ", Bug::Printf.("s", "a", minus: true, width: 3, prec: 3)[0])
+ end
+end
diff --git a/jni/ruby/test/-ext-/test_recursion.rb b/jni/ruby/test/-ext-/test_recursion.rb
new file mode 100644
index 0000000..06faf6d
--- /dev/null
+++ b/jni/ruby/test/-ext-/test_recursion.rb
@@ -0,0 +1,35 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+
+class TestRecursion < Test::Unit::TestCase
+ require '-test-/recursion'
+
+ def setup
+ @obj = Struct.new(:visited).new(false)
+ @obj.extend(Bug::Recursive)
+ end
+
+ def test_recursive
+ def @obj.doit
+ self.visited = true
+ exec_recursive(:doit)
+ raise "recursive"
+ end
+ assert_raise_with_message(RuntimeError, "recursive") {
+ @obj.exec_recursive(:doit)
+ }
+ assert(@obj.visited, "obj.hash was not called")
+ end
+
+ def test_recursive_outer
+ def @obj.doit
+ self.visited = true
+ exec_recursive_outer(:doit)
+ raise "recursive_outer should short circuit intermediate calls"
+ end
+ assert_nothing_raised {
+ @obj.exec_recursive_outer(:doit)
+ }
+ assert(@obj.visited, "obj.hash was not called")
+ end
+end
diff --git a/jni/ruby/test/-ext-/tracepoint/test_tracepoint.rb b/jni/ruby/test/-ext-/tracepoint/test_tracepoint.rb
new file mode 100644
index 0000000..b5a6e76
--- /dev/null
+++ b/jni/ruby/test/-ext-/tracepoint/test_tracepoint.rb
@@ -0,0 +1,79 @@
+require 'test/unit'
+require '-test-/tracepoint'
+
+class TestTracepointObj < Test::Unit::TestCase
+ def test_not_available_from_ruby
+ assert_raise ArgumentError do
+ TracePoint.trace(:obj_new){}
+ end
+ end
+
+ def test_tracks_objspace_events
+ result = Bug.tracepoint_track_objspace_events{
+ 99
+ 'abc'
+ _="foobar"
+ Object.new
+ nil
+ }
+
+ newobj_count, free_count, gc_start_count, gc_end_mark_count, gc_end_sweep_count, *newobjs = *result
+ assert_equal 2, newobj_count
+ assert_equal 2, newobjs.size
+ assert_equal 'foobar', newobjs[0]
+ assert_equal Object, newobjs[1].class
+ assert_operator free_count, :>=, 0
+ assert_operator gc_start_count, :==, gc_end_mark_count
+ assert_operator gc_start_count, :>=, gc_end_sweep_count
+ end
+
+ def test_tracks_objspace_count
+ stat1 = {}
+ stat2 = {}
+ GC.disable
+ GC.stat(stat1)
+ result = Bug.tracepoint_track_objspace_events{
+ GC.enable
+ 1_000_000.times{''}
+ GC.disable
+ }
+ GC.stat(stat2)
+ GC.enable
+
+ newobj_count, free_count, gc_start_count, gc_end_mark_count, gc_end_sweep_count, *newobjs = *result
+
+ assert_operator stat2[:total_allocated_objects] - stat1[:total_allocated_objects], :>=, newobj_count
+ assert_operator 1_000_000, :<=, newobj_count
+
+ assert_operator stat2[:total_freed_objects] + stat2[:heap_final_slots] - stat1[:total_freed_objects], :>=, free_count
+ assert_operator stat2[:count] - stat1[:count], :==, gc_start_count
+
+ assert_operator gc_start_count, :==, gc_end_mark_count
+ assert_operator gc_start_count, :>=, gc_end_sweep_count
+ assert_operator stat2[:count] - stat1[:count] - 1, :<=, gc_end_sweep_count
+ end
+
+ def test_tracepoint_specify_normal_and_internal_events
+ assert_raise(TypeError){ Bug.tracepoint_specify_normal_and_internal_events }
+ end
+
+ def test_after_gc_start_hook_with_GC_stress
+ bug8492 = '[ruby-dev:47400] [Bug #8492]: infinite after_gc_start_hook reentrance'
+ assert_nothing_raised(Timeout::Error, bug8492) do
+ assert_in_out_err(%w[-r-test-/tracepoint], <<-'end;', /\A[1-9]/, timeout: 2)
+ stress, GC.stress = GC.stress, false
+ count = 0
+ Bug.after_gc_start_hook = proc {count += 1}
+ begin
+ GC.stress = true
+ 3.times {Object.new}
+ ensure
+ GC.stress = stress
+ Bug.after_gc_start_hook = nil
+ end
+ puts count
+ end;
+ end
+ end
+
+end
diff --git a/jni/ruby/test/-ext-/typeddata/test_typeddata.rb b/jni/ruby/test/-ext-/typeddata/test_typeddata.rb
new file mode 100644
index 0000000..c24ad08
--- /dev/null
+++ b/jni/ruby/test/-ext-/typeddata/test_typeddata.rb
@@ -0,0 +1,16 @@
+require 'test/unit'
+require "-test-/typeddata/typeddata"
+
+class Test_TypedData < Test::Unit::TestCase
+ def test_wrong_argtype
+ assert_raise_with_message(TypeError, "wrong argument type false (expected typed_data)") {Bug::TypedData.check(false)}
+
+ assert_raise_with_message(TypeError, "wrong argument type true (expected typed_data)") {Bug::TypedData.check(true)}
+
+ assert_raise_with_message(TypeError, "wrong argument type Symbol (expected typed_data)") {Bug::TypedData.check(:e)}
+
+ assert_raise_with_message(TypeError, "wrong argument type Fixnum (expected typed_data)") {Bug::TypedData.check(0)}
+
+ assert_raise_with_message(TypeError, "wrong argument type String (expected typed_data)") {Bug::TypedData.check("a")}
+ end
+end
diff --git a/jni/ruby/test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb b/jni/ruby/test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb
new file mode 100644
index 0000000..e88dbef
--- /dev/null
+++ b/jni/ruby/test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb
@@ -0,0 +1,45 @@
+require 'test/unit'
+
+class TestWaitForSingleFD < Test::Unit::TestCase
+ require '-test-/wait_for_single_fd/wait_for_single_fd'
+
+ def with_pipe
+ r, w = IO.pipe
+ begin
+ yield r, w
+ ensure
+ r.close unless r.closed?
+ w.close unless w.closed?
+ end
+ end
+
+ def test_wait_for_valid_fd
+ with_pipe do |r,w|
+ rc = IO.wait_for_single_fd(w.fileno, RB_WAITFD_OUT, nil)
+ assert_equal RB_WAITFD_OUT, rc
+ end
+ end
+
+ def test_wait_for_invalid_fd
+ # FreeBSD 8.2 or prior sticks this
+ # http://bugs.ruby-lang.org/issues/5524
+ skip if /freebsd[1-8]/ =~ RUBY_PLATFORM
+ with_pipe do |r,w|
+ wfd = w.fileno
+ w.close
+ assert_raise(Errno::EBADF) do
+ IO.wait_for_single_fd(wfd, RB_WAITFD_OUT, nil)
+ end
+ end
+ end
+
+ def test_wait_for_closed_pipe
+ with_pipe do |r,w|
+ w.close
+ rc = IO.wait_for_single_fd(r.fileno, RB_WAITFD_IN, nil)
+ assert_equal RB_WAITFD_IN, rc
+ end
+ end
+
+
+end
diff --git a/jni/ruby/test/-ext-/win32/test_console_attr.rb b/jni/ruby/test/-ext-/win32/test_console_attr.rb
new file mode 100644
index 0000000..3afb2d9
--- /dev/null
+++ b/jni/ruby/test/-ext-/win32/test_console_attr.rb
@@ -0,0 +1,43 @@
+if /mswin|mingw/ =~ RUBY_PLATFORM and STDOUT.tty?
+ require '-test-/win32/console'
+ require 'io/console'
+ require 'test/unit'
+
+ class Test_Win32Console < Test::Unit::TestCase
+ def reset
+ STDOUT.console_attribute(7)
+ end
+
+ alias setup reset
+ alias teardown reset
+
+ def test_default
+ info = STDOUT.console_info
+ assert_equal(7, info.attr);
+ end
+
+ def test_reverse
+ print "\e[7m"
+ info = STDOUT.console_info
+ assert_equal(0x70, info.attr);
+ end
+
+ def test_bold
+ print "\e[1m"
+ info = STDOUT.console_info
+ assert_equal(0x8, info.attr&0x8);
+ end
+
+ def test_bold_reverse
+ print "\e[1;7m"
+ info = STDOUT.console_info
+ assert_equal(0xf0, info.attr);
+ end
+
+ def test_reverse_bold
+ print "\e[7;1m"
+ info = STDOUT.console_info
+ assert_equal(0xf0, info.attr);
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/win32/test_dln.rb b/jni/ruby/test/-ext-/win32/test_dln.rb
new file mode 100644
index 0000000..2801ebe
--- /dev/null
+++ b/jni/ruby/test/-ext-/win32/test_dln.rb
@@ -0,0 +1,34 @@
+require 'test/unit'
+require 'tmpdir'
+require 'rbconfig'
+
+module Bug
+ module Win32
+ class TestDln < Test::Unit::TestCase
+ def test_check_imported
+ bug = '[Bug #6303]'
+ assert_in_out_err(['-r-test-/win32/dln', '-eexit'], '', [], [], bug, timeout: 10)
+ end
+
+ def test_nonascii_load
+ bug9699 = '[ruby-core:61845] [Bug #9699]'
+ so = "-test-/win32/dln/empty." + RbConfig::CONFIG["DLEXT"]
+ so = $:.find {|d| d = ::File.join(d, so); break d if ::File.exist?(d)}
+ assert_not_nil(so)
+ Dir.mkdir(dir = ::File.join(testdir = Dir.mktmpdir("test"), "\u{30c6 30b9 30c8}"))
+ ::File.copy_stream(so, ::File.join(dir, ::File.basename(so)))
+ assert_separately(['-', bug9699, testdir, ::File.basename(so)], <<-'end;')
+ bug, dir, so = *ARGV
+ assert_nothing_raised(LoadError, bug) do
+ require ::File.join(dir, "\u{30c6 30b9 30c8}", so)
+ end
+ end;
+ ensure
+ ::File.unlink(::File.join(dir, ::File.basename(so))) rescue nil
+ Dir.rmdir(dir) rescue nil
+ Dir.rmdir(testdir) rescue nil
+ end
+
+ end
+ end
+end if /mswin|mingw/ =~ RUBY_PLATFORM
diff --git a/jni/ruby/test/-ext-/win32/test_fd_setsize.rb b/jni/ruby/test/-ext-/win32/test_fd_setsize.rb
new file mode 100644
index 0000000..6fe889c
--- /dev/null
+++ b/jni/ruby/test/-ext-/win32/test_fd_setsize.rb
@@ -0,0 +1,24 @@
+require 'test/unit'
+
+module Bug
+ module Win32
+ class TestFdSetSize < Test::Unit::TestCase
+ def test_select_with_unmatched_fd_setsize
+ bug6532 = '[ruby-core:44588]'
+ assert_in_out_err([], <<-INPUT, %w(:ok), [], bug6532)
+ require '-test-/win32/fd_setsize'
+ Bug::Win32.test_select
+ p :ok
+ INPUT
+ end
+
+ def test_fdset_with_unmatched_fd_setsize
+ bug6532 = '[ruby-core:44588]'
+ assert_in_out_err([], <<-INPUT, %w(:ok), [], bug6532)
+ require '-test-/win32/fd_setsize'
+ p :ok if Bug::Win32.test_fdset
+ INPUT
+ end
+ end
+ end
+end if /mswin|mingw/ =~ RUBY_PLATFORM
diff --git a/jni/ruby/test/base64/test_base64.rb b/jni/ruby/test/base64/test_base64.rb
new file mode 100644
index 0000000..c5e61b3
--- /dev/null
+++ b/jni/ruby/test/base64/test_base64.rb
@@ -0,0 +1,100 @@
+# coding: US-ASCII
+require "test/unit"
+require "base64"
+
+class TestBase64 < Test::Unit::TestCase
+ def test_sample
+ assert_equal("U2VuZCByZWluZm9yY2VtZW50cw==\n", Base64.encode64('Send reinforcements'))
+ assert_equal('Send reinforcements', Base64.decode64("U2VuZCByZWluZm9yY2VtZW50cw==\n"))
+ assert_equal(
+ "Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\nUnVieQ==\n",
+ Base64.encode64("Now is the time for all good coders\nto learn Ruby"))
+ assert_equal(
+ "Now is the time for all good coders\nto learn Ruby",
+ Base64.decode64("Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\nUnVieQ==\n"))
+ assert_equal(
+ "VGhpcyBpcyBsaW5lIG9uZQpUaGlzIGlzIGxpbmUgdHdvClRoaXMgaXMgbGlu\nZSB0aHJlZQpBbmQgc28gb24uLi4K\n",
+ Base64.encode64("This is line one\nThis is line two\nThis is line three\nAnd so on...\n"))
+ assert_equal(
+ "This is line one\nThis is line two\nThis is line three\nAnd so on...\n",
+ Base64.decode64("VGhpcyBpcyBsaW5lIG9uZQpUaGlzIGlzIGxpbmUgdHdvClRoaXMgaXMgbGluZSB0aHJlZQpBbmQgc28gb24uLi4K"))
+ end
+
+ def test_encode64
+ assert_equal("", Base64.encode64(""))
+ assert_equal("AA==\n", Base64.encode64("\0"))
+ assert_equal("AAA=\n", Base64.encode64("\0\0"))
+ assert_equal("AAAA\n", Base64.encode64("\0\0\0"))
+ assert_equal("/w==\n", Base64.encode64("\377"))
+ assert_equal("//8=\n", Base64.encode64("\377\377"))
+ assert_equal("////\n", Base64.encode64("\377\377\377"))
+ assert_equal("/+8=\n", Base64.encode64("\xff\xef"))
+ end
+
+ def test_decode64
+ assert_equal("", Base64.decode64(""))
+ assert_equal("\0", Base64.decode64("AA==\n"))
+ assert_equal("\0\0", Base64.decode64("AAA=\n"))
+ assert_equal("\0\0\0", Base64.decode64("AAAA\n"))
+ assert_equal("\377", Base64.decode64("/w==\n"))
+ assert_equal("\377\377", Base64.decode64("//8=\n"))
+ assert_equal("\377\377\377", Base64.decode64("////\n"))
+ assert_equal("\xff\xef", Base64.decode64("/+8=\n"))
+ end
+
+ def test_strict_encode64
+ assert_equal("", Base64.strict_encode64(""))
+ assert_equal("AA==", Base64.strict_encode64("\0"))
+ assert_equal("AAA=", Base64.strict_encode64("\0\0"))
+ assert_equal("AAAA", Base64.strict_encode64("\0\0\0"))
+ assert_equal("/w==", Base64.strict_encode64("\377"))
+ assert_equal("//8=", Base64.strict_encode64("\377\377"))
+ assert_equal("////", Base64.strict_encode64("\377\377\377"))
+ assert_equal("/+8=", Base64.strict_encode64("\xff\xef"))
+ end
+
+ def test_strict_decode64
+ assert_equal("", Base64.strict_decode64(""))
+ assert_equal("\0", Base64.strict_decode64("AA=="))
+ assert_equal("\0\0", Base64.strict_decode64("AAA="))
+ assert_equal("\0\0\0", Base64.strict_decode64("AAAA"))
+ assert_equal("\377", Base64.strict_decode64("/w=="))
+ assert_equal("\377\377", Base64.strict_decode64("//8="))
+ assert_equal("\377\377\377", Base64.strict_decode64("////"))
+ assert_equal("\xff\xef", Base64.strict_decode64("/+8="))
+
+ assert_raise(ArgumentError) { Base64.strict_decode64("^") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("A") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("A^") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("AA") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("AA=") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("AA===") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("AA=x") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("AAA") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("AAA^") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("AB==") }
+ assert_raise(ArgumentError) { Base64.strict_decode64("AAB=") }
+ end
+
+ def test_urlsafe_encode64
+ assert_equal("", Base64.urlsafe_encode64(""))
+ assert_equal("AA==", Base64.urlsafe_encode64("\0"))
+ assert_equal("AAA=", Base64.urlsafe_encode64("\0\0"))
+ assert_equal("AAAA", Base64.urlsafe_encode64("\0\0\0"))
+ assert_equal("_w==", Base64.urlsafe_encode64("\377"))
+ assert_equal("__8=", Base64.urlsafe_encode64("\377\377"))
+ assert_equal("____", Base64.urlsafe_encode64("\377\377\377"))
+ assert_equal("_-8=", Base64.urlsafe_encode64("\xff\xef"))
+ end
+
+ def test_urlsafe_decode64
+ assert_equal("", Base64.urlsafe_decode64(""))
+ assert_equal("\0", Base64.urlsafe_decode64("AA=="))
+ assert_equal("\0\0", Base64.urlsafe_decode64("AAA="))
+ assert_equal("\0\0\0", Base64.urlsafe_decode64("AAAA"))
+ assert_equal("\377", Base64.urlsafe_decode64("_w=="))
+ assert_equal("\377\377", Base64.urlsafe_decode64("__8="))
+ assert_equal("\377\377\377", Base64.urlsafe_decode64("____"))
+ assert_equal("\xff\xef", Base64.urlsafe_decode64("_+8="))
+ end
+end
diff --git a/jni/ruby/test/benchmark/test_benchmark.rb b/jni/ruby/test/benchmark/test_benchmark.rb
new file mode 100644
index 0000000..1b9964e
--- /dev/null
+++ b/jni/ruby/test/benchmark/test_benchmark.rb
@@ -0,0 +1,161 @@
+require 'test/unit'
+require 'benchmark'
+
+class TestBenchmark < Test::Unit::TestCase
+ BENCH_FOR_TIMES_UPTO = lambda do |x|
+ n = 1000
+ tf = x.report("for:") { for _ in 1..n; '1'; end }
+ tt = x.report("times:") { n.times do ; '1'; end }
+ tu = x.report("upto:") { 1.upto(n) do ; '1'; end }
+ [tf+tt+tu, (tf+tt+tu)/3]
+ end
+
+ BENCH_FOR_TIMES_UPTO_NO_LABEL = lambda do |x|
+ n = 1000
+ x.report { for _ in 1..n; '1'; end }
+ x.report { n.times do ; '1'; end }
+ x.report { 1.upto(n) do ; '1'; end }
+ end
+
+ def labels
+ %w[first second third]
+ end
+
+ def bench(type = :bm, *args, &block)
+ if block
+ Benchmark.send(type, *args, &block)
+ else
+ Benchmark.send(type, *args) do |x|
+ labels.each { |label|
+ x.report(label) {}
+ }
+ end
+ end
+ end
+
+ def capture_output
+ capture_io { yield }.first.gsub(/[ \-]\d\.\d{6}/, ' --time--')
+ end
+
+ def capture_bench_output(type, *args, &block)
+ capture_output { bench(type, *args, &block) }
+ end
+
+ def test_tms_outputs_nicely
+ assert_equal(" 0.000000 0.000000 0.000000 ( 0.000000)\n", Benchmark::Tms.new.to_s)
+ assert_equal(" 1.000000 2.000000 10.000000 ( 5.000000)\n", Benchmark::Tms.new(1,2,3,4,5).to_s)
+ assert_equal("1.000000 2.000000 3.000000 4.000000 10.000000 (5.000000) label",
+ Benchmark::Tms.new(1,2,3,4,5,'label').format('%u %y %U %Y %t %r %n'))
+ assert_equal("1.000000 2.000", Benchmark::Tms.new(1).format('%u %.3f', 2))
+ assert_equal("100.000000 150.000000 250.000000 (200.000000)\n",
+ Benchmark::Tms.new(100, 150, 0, 0, 200).to_s)
+ end
+
+ def test_tms_wont_modify_the_format_String_given
+ format = "format %u"
+ Benchmark::Tms.new.format(format)
+ assert_equal("format %u", format)
+ end
+
+ BENCHMARK_OUTPUT_WITH_TOTAL_AVG = <<BENCH
+ user system total real
+for: --time-- --time-- --time-- ( --time--)
+times: --time-- --time-- --time-- ( --time--)
+upto: --time-- --time-- --time-- ( --time--)
+>total: --time-- --time-- --time-- ( --time--)
+>avg: --time-- --time-- --time-- ( --time--)
+BENCH
+
+ def test_benchmark_does_not_print_any_space_if_the_given_caption_is_empty
+ assert_equal(<<-BENCH, capture_bench_output(:benchmark))
+first --time-- --time-- --time-- ( --time--)
+second --time-- --time-- --time-- ( --time--)
+third --time-- --time-- --time-- ( --time--)
+BENCH
+ end
+
+ def test_benchmark_makes_extra_calcultations_with_an_Array_at_the_end_of_the_benchmark_and_show_the_result
+ assert_equal(BENCHMARK_OUTPUT_WITH_TOTAL_AVG,
+ capture_bench_output(:benchmark,
+ Benchmark::CAPTION, 7,
+ Benchmark::FORMAT, ">total:", ">avg:",
+ &BENCH_FOR_TIMES_UPTO))
+ end
+
+ def test_bm_returns_an_Array_of_the_times_with_the_labels
+ [:bm, :bmbm].each do |meth|
+ capture_io do
+ results = bench(meth)
+ assert_instance_of(Array, results)
+ assert_equal(labels.size, results.size)
+ results.zip(labels).each { |tms, label|
+ assert_instance_of(Benchmark::Tms, tms)
+ assert_equal(label, tms.label)
+ }
+ end
+ end
+ end
+
+ def test_bm_correctly_output_when_the_label_width_is_given
+ assert_equal(<<-BENCH, capture_bench_output(:bm, 6))
+ user system total real
+first --time-- --time-- --time-- ( --time--)
+second --time-- --time-- --time-- ( --time--)
+third --time-- --time-- --time-- ( --time--)
+BENCH
+ end
+
+ def test_bm_correctly_output_when_no_label_is_given
+ assert_equal(<<-BENCH, capture_bench_output(:bm, &BENCH_FOR_TIMES_UPTO_NO_LABEL))
+ user system total real
+ --time-- --time-- --time-- ( --time--)
+ --time-- --time-- --time-- ( --time--)
+ --time-- --time-- --time-- ( --time--)
+BENCH
+ end
+
+ def test_bm_can_make_extra_calcultations_with_an_array_at_the_end_of_the_benchmark
+ assert_equal(BENCHMARK_OUTPUT_WITH_TOTAL_AVG,
+ capture_bench_output(:bm, 7, ">total:", ">avg:",
+ &BENCH_FOR_TIMES_UPTO))
+ end
+
+ BMBM_OUTPUT = <<BENCH
+Rehearsal ------------------------------------------
+first --time-- --time-- --time-- ( --time--)
+second --time-- --time-- --time-- ( --time--)
+third --time-- --time-- --time-- ( --time--)
+--------------------------------- total: --time--sec
+
+ user system total real
+first --time-- --time-- --time-- ( --time--)
+second --time-- --time-- --time-- ( --time--)
+third --time-- --time-- --time-- ( --time--)
+BENCH
+
+ def test_bmbm_correctly_guess_the_label_width_even_when_not_given
+ assert_equal(BMBM_OUTPUT, capture_bench_output(:bmbm))
+ end
+
+ def test_bmbm_correctly_output_when_the_label_width_is_given__bmbm_ignore_it__but_it_is_a_frequent_mistake
+ assert_equal(BMBM_OUTPUT, capture_bench_output(:bmbm, 6))
+ end
+
+ def test_report_item_shows_the_title__even_if_not_a_string
+ assert_operator(capture_bench_output(:bm) { |x| x.report(:title) {} }, :include?, 'title')
+ assert_operator(capture_bench_output(:bmbm) { |x| x.report(:title) {} }, :include?, 'title')
+ end
+
+ def test_bugs_ruby_dev_40906_can_add_in_place_the_time_of_execution_of_the_block_given
+ t = Benchmark::Tms.new
+ assert_equal(0, t.real)
+ t.add! { sleep 0.1 }
+ assert_not_equal(0, t.real)
+ end
+
+ def test_realtime_output
+ sleeptime = 1.0
+ realtime = Benchmark.realtime { sleep sleeptime }
+ assert_operator sleeptime, :<, realtime
+ end
+end
diff --git a/jni/ruby/test/bigdecimal/test_bigdecimal.rb b/jni/ruby/test/bigdecimal/test_bigdecimal.rb
new file mode 100644
index 0000000..042d11d
--- /dev/null
+++ b/jni/ruby/test/bigdecimal/test_bigdecimal.rb
@@ -0,0 +1,1555 @@
+require_relative "testbase"
+require 'bigdecimal/math'
+
+require 'thread'
+
+class TestBigDecimal < Test::Unit::TestCase
+ include TestBigDecimalBase
+
+ ROUNDING_MODE_MAP = [
+ [ BigDecimal::ROUND_UP, :up],
+ [ BigDecimal::ROUND_DOWN, :down],
+ [ BigDecimal::ROUND_DOWN, :truncate],
+ [ BigDecimal::ROUND_HALF_UP, :half_up],
+ [ BigDecimal::ROUND_HALF_UP, :default],
+ [ BigDecimal::ROUND_HALF_DOWN, :half_down],
+ [ BigDecimal::ROUND_HALF_EVEN, :half_even],
+ [ BigDecimal::ROUND_HALF_EVEN, :banker],
+ [ BigDecimal::ROUND_CEILING, :ceiling],
+ [ BigDecimal::ROUND_CEILING, :ceil],
+ [ BigDecimal::ROUND_FLOOR, :floor],
+ ]
+
+ def assert_nan(x)
+ assert(x.nan?, "Expected #{x.inspect} to be NaN")
+ end
+
+ def assert_positive_infinite(x)
+ assert(x.infinite?, "Expected #{x.inspect} to be positive infinite")
+ assert_operator(x, :>, 0)
+ end
+
+ def assert_negative_infinite(x)
+ assert(x.infinite?, "Expected #{x.inspect} to be negative infinite")
+ assert_operator(x, :<, 0)
+ end
+
+ def assert_positive_zero(x)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, x.sign,
+ "Expected #{x.inspect} to be positive zero")
+ end
+
+ def assert_negative_zero(x)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, x.sign,
+ "Expected #{x.inspect} to be negative zero")
+ end
+
+ def test_not_equal
+ assert_not_equal BigDecimal("1"), BigDecimal.allocate
+ end
+
+ def test_global_new
+ assert_equal(1, BigDecimal("1"))
+ assert_equal(1, BigDecimal("1", 1))
+ assert_raise(ArgumentError) { BigDecimal("1", -1) }
+ assert_raise(ArgumentError) { BigDecimal(4.2) }
+ begin
+ BigDecimal(4.2)
+ rescue ArgumentError => error
+ assert_match(/Float/, error.message)
+ end
+ assert_raise(ArgumentError) { BigDecimal(42.quo(7)) }
+ begin
+ BigDecimal(42.quo(7))
+ rescue ArgumentError => error
+ assert_match(/Rational/, error.message)
+ end
+ end
+
+ def test_global_new_with_integer
+ assert_equal(BigDecimal("1"), BigDecimal(1))
+ assert_equal(BigDecimal("-1"), BigDecimal(-1))
+ assert_equal(BigDecimal((2**100).to_s), BigDecimal(2**100))
+ assert_equal(BigDecimal((-2**100).to_s), BigDecimal(-2**100))
+ end
+
+ def test_global_new_with_rational
+ assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal(1.quo(3), 21))
+ assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal(-1.quo(3), 21))
+ assert_raise(ArgumentError) { BigDecimal(1.quo(3)) }
+ end
+
+ def test_global_new_with_float
+ assert_equal(BigDecimal("0.1235"), BigDecimal(0.1234567, 4))
+ assert_equal(BigDecimal("-0.1235"), BigDecimal(-0.1234567, 4))
+ assert_raise(ArgumentError) { BigDecimal(0.1) }
+ assert_raise(ArgumentError) { BigDecimal(0.1, Float::DIG + 2) }
+ assert_nothing_raised { BigDecimal(0.1, Float::DIG + 1) }
+
+ bug9214 = '[ruby-core:58858]'
+ assert_equal(BigDecimal(-0.0, Float::DIG).sign, -1, bug9214)
+ end
+
+ def test_global_new_with_big_decimal
+ assert_equal(BigDecimal(1), BigDecimal(BigDecimal(1)))
+ assert_equal(BigDecimal('+0'), BigDecimal(BigDecimal('+0')))
+ assert_equal(BigDecimal('-0'), BigDecimal(BigDecimal('-0')))
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_positive_infinite(BigDecimal(BigDecimal('Infinity')))
+ assert_negative_infinite(BigDecimal(BigDecimal('-Infinity')))
+ assert_nan(BigDecimal(BigDecimal('NaN')))
+ end
+ end
+
+ def test_global_new_with_tainted_string
+ Thread.new {
+ $SAFE = 1
+ BigDecimal('1'.taint)
+ }.join
+ end
+
+ def test_new
+ assert_equal(1, BigDecimal.new("1"))
+ assert_equal(1, BigDecimal.new("1", 1))
+ assert_equal(1, BigDecimal.new(" 1 "))
+ assert_equal(111, BigDecimal.new("1_1_1_"))
+ assert_equal(0, BigDecimal.new("_1_1_1"))
+ assert_equal(10**(-1), BigDecimal.new("1E-1"), '#4825')
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_equal( 1, BigDecimal.new("Infinity").infinite?)
+ assert_equal(-1, BigDecimal.new("-Infinity").infinite?)
+ assert_equal(true, BigDecimal.new("NaN").nan?)
+ assert_equal( 1, BigDecimal.new("1E1111111111111111111").infinite?)
+ end
+
+ def test_new_with_integer
+ assert_equal(BigDecimal("1"), BigDecimal.new(1))
+ assert_equal(BigDecimal("-1"), BigDecimal.new(-1))
+ assert_equal(BigDecimal((2**100).to_s), BigDecimal.new(2**100))
+ assert_equal(BigDecimal((-2**100).to_s), BigDecimal.new(-2**100))
+ end
+
+ def test_new_with_rational
+ assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal.new(1.quo(3), 21))
+ assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal.new(-1.quo(3), 21))
+ assert_raise(ArgumentError) { BigDecimal.new(1.quo(3)) }
+ end
+
+ def test_new_with_float
+ assert_equal(BigDecimal("0.1235"), BigDecimal(0.1234567, 4))
+ assert_equal(BigDecimal("-0.1235"), BigDecimal(-0.1234567, 4))
+ assert_raise(ArgumentError) { BigDecimal.new(0.1) }
+ assert_raise(ArgumentError) { BigDecimal.new(0.1, Float::DIG + 2) }
+ assert_nothing_raised { BigDecimal.new(0.1, Float::DIG + 1) }
+ end
+
+ def test_new_with_big_decimal
+ assert_equal(BigDecimal(1), BigDecimal.new(BigDecimal(1)))
+ assert_equal(BigDecimal('+0'), BigDecimal.new(BigDecimal('+0')))
+ assert_equal(BigDecimal('-0'), BigDecimal.new(BigDecimal('-0')))
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_positive_infinite(BigDecimal.new(BigDecimal('Infinity')))
+ assert_negative_infinite(BigDecimal.new(BigDecimal('-Infinity')))
+ assert_nan(BigDecimal(BigDecimal.new('NaN')))
+ end
+ end
+
+ def test_new_with_tainted_string
+ Thread.new {
+ $SAFE = 1
+ BigDecimal.new('1'.taint)
+ }.join
+ end
+
+ def _test_mode(type)
+ BigDecimal.mode(type, true)
+ assert_raise(FloatDomainError) { yield }
+
+ BigDecimal.mode(type, false)
+ assert_nothing_raised { yield }
+ end
+
+ def test_mode
+ assert_raise(ArgumentError) { BigDecimal.mode(BigDecimal::EXCEPTION_ALL, 1) }
+ assert_raise(ArgumentError) { BigDecimal.mode(BigDecimal::ROUND_MODE, 256) }
+ assert_raise(ArgumentError) { BigDecimal.mode(BigDecimal::ROUND_MODE, :xyzzy) }
+ assert_raise(TypeError) { BigDecimal.mode(0xf000, true) }
+
+ begin
+ saved_mode = BigDecimal.mode(BigDecimal::ROUND_MODE)
+
+ [ BigDecimal::ROUND_UP,
+ BigDecimal::ROUND_DOWN,
+ BigDecimal::ROUND_HALF_UP,
+ BigDecimal::ROUND_HALF_DOWN,
+ BigDecimal::ROUND_CEILING,
+ BigDecimal::ROUND_FLOOR,
+ BigDecimal::ROUND_HALF_EVEN,
+ ].each do |mode|
+ BigDecimal.mode(BigDecimal::ROUND_MODE, mode)
+ assert_equal(mode, BigDecimal.mode(BigDecimal::ROUND_MODE))
+ end
+ ensure
+ BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode)
+ end
+
+ BigDecimal.save_rounding_mode do
+ ROUNDING_MODE_MAP.each do |const, sym|
+ BigDecimal.mode(BigDecimal::ROUND_MODE, sym)
+ assert_equal(const, BigDecimal.mode(BigDecimal::ROUND_MODE))
+ end
+ end
+ end
+
+ def test_thread_local_mode
+ begin
+ saved_mode = BigDecimal.mode(BigDecimal::ROUND_MODE)
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_UP)
+ Thread.start {
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
+ assert_equal(BigDecimal::ROUND_HALF_EVEN, BigDecimal.mode(BigDecimal::ROUND_MODE))
+ }.join
+ assert_equal(BigDecimal::ROUND_UP, BigDecimal.mode(BigDecimal::ROUND_MODE))
+ ensure
+ BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode)
+ end
+ end
+
+ def test_save_exception_mode
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ mode = BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW)
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true)
+ end
+ assert_equal(mode, BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW))
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_FLOOR)
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
+ end
+ assert_equal(BigDecimal::ROUND_HALF_EVEN, BigDecimal.mode(BigDecimal::ROUND_MODE))
+
+ assert_equal(42, BigDecimal.save_exception_mode { 42 })
+ end
+
+ def test_save_rounding_mode
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_FLOOR)
+ BigDecimal.save_rounding_mode do
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
+ end
+ assert_equal(BigDecimal::ROUND_FLOOR, BigDecimal.mode(BigDecimal::ROUND_MODE))
+
+ assert_equal(42, BigDecimal.save_rounding_mode { 42 })
+ end
+
+ def test_save_limit
+ begin
+ old = BigDecimal.limit
+ BigDecimal.limit(100)
+ BigDecimal.save_limit do
+ BigDecimal.limit(200)
+ end
+ assert_equal(100, BigDecimal.limit);
+ ensure
+ BigDecimal.limit(old)
+ end
+
+ assert_equal(42, BigDecimal.save_limit { 42 })
+ end
+
+ def test_exception_nan
+ _test_mode(BigDecimal::EXCEPTION_NaN) { BigDecimal.new("NaN") }
+ end
+
+ def test_exception_infinity
+ _test_mode(BigDecimal::EXCEPTION_INFINITY) { BigDecimal.new("Infinity") }
+ end
+
+ def test_exception_underflow
+ _test_mode(BigDecimal::EXCEPTION_UNDERFLOW) do
+ x = BigDecimal.new("0.1")
+ 100.times do
+ x *= x
+ end
+ end
+ end
+
+ def test_exception_overflow
+ _test_mode(BigDecimal::EXCEPTION_OVERFLOW) do
+ x = BigDecimal.new("10")
+ 100.times do
+ x *= x
+ end
+ end
+ end
+
+ def test_exception_zerodivide
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ _test_mode(BigDecimal::EXCEPTION_ZERODIVIDE) { 1 / BigDecimal.new("0") }
+ _test_mode(BigDecimal::EXCEPTION_ZERODIVIDE) { -1 / BigDecimal.new("0") }
+ end
+
+ def test_round_up
+ n4 = BigDecimal.new("4") # n4 / 9 = 0.44444...
+ n5 = BigDecimal.new("5") # n5 / 9 = 0.55555...
+ n6 = BigDecimal.new("6") # n6 / 9 = 0.66666...
+ m4, m5, m6 = -n4, -n5, -n6
+ n2h = BigDecimal.new("2.5")
+ n3h = BigDecimal.new("3.5")
+ m2h, m3h = -n2h, -n3h
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_UP)
+ assert_operator(n4, :<, n4 / 9 * 9)
+ assert_operator(n5, :<, n5 / 9 * 9)
+ assert_operator(n6, :<, n6 / 9 * 9)
+ assert_operator(m4, :>, m4 / 9 * 9)
+ assert_operator(m5, :>, m5 / 9 * 9)
+ assert_operator(m6, :>, m6 / 9 * 9)
+ assert_equal(3, n2h.round)
+ assert_equal(4, n3h.round)
+ assert_equal(-3, m2h.round)
+ assert_equal(-4, m3h.round)
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_DOWN)
+ assert_operator(n4, :>, n4 / 9 * 9)
+ assert_operator(n5, :>, n5 / 9 * 9)
+ assert_operator(n6, :>, n6 / 9 * 9)
+ assert_operator(m4, :<, m4 / 9 * 9)
+ assert_operator(m5, :<, m5 / 9 * 9)
+ assert_operator(m6, :<, m6 / 9 * 9)
+ assert_equal(2, n2h.round)
+ assert_equal(3, n3h.round)
+ assert_equal(-2, m2h.round)
+ assert_equal(-3, m3h.round)
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP)
+ assert_operator(n4, :>, n4 / 9 * 9)
+ assert_operator(n5, :<, n5 / 9 * 9)
+ assert_operator(n6, :<, n6 / 9 * 9)
+ assert_operator(m4, :<, m4 / 9 * 9)
+ assert_operator(m5, :>, m5 / 9 * 9)
+ assert_operator(m6, :>, m6 / 9 * 9)
+ assert_equal(3, n2h.round)
+ assert_equal(4, n3h.round)
+ assert_equal(-3, m2h.round)
+ assert_equal(-4, m3h.round)
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_DOWN)
+ assert_operator(n4, :>, n4 / 9 * 9)
+ assert_operator(n5, :>, n5 / 9 * 9)
+ assert_operator(n6, :<, n6 / 9 * 9)
+ assert_operator(m4, :<, m4 / 9 * 9)
+ assert_operator(m5, :<, m5 / 9 * 9)
+ assert_operator(m6, :>, m6 / 9 * 9)
+ assert_equal(2, n2h.round)
+ assert_equal(3, n3h.round)
+ assert_equal(-2, m2h.round)
+ assert_equal(-3, m3h.round)
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN)
+ assert_operator(n4, :>, n4 / 9 * 9)
+ assert_operator(n5, :<, n5 / 9 * 9)
+ assert_operator(n6, :<, n6 / 9 * 9)
+ assert_operator(m4, :<, m4 / 9 * 9)
+ assert_operator(m5, :>, m5 / 9 * 9)
+ assert_operator(m6, :>, m6 / 9 * 9)
+ assert_equal(2, n2h.round)
+ assert_equal(4, n3h.round)
+ assert_equal(-2, m2h.round)
+ assert_equal(-4, m3h.round)
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_CEILING)
+ assert_operator(n4, :<, n4 / 9 * 9)
+ assert_operator(n5, :<, n5 / 9 * 9)
+ assert_operator(n6, :<, n6 / 9 * 9)
+ assert_operator(m4, :<, m4 / 9 * 9)
+ assert_operator(m5, :<, m5 / 9 * 9)
+ assert_operator(m6, :<, m6 / 9 * 9)
+ assert_equal(3, n2h.round)
+ assert_equal(4, n3h.round)
+ assert_equal(-2, m2h.round)
+ assert_equal(-3, m3h.round)
+
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_FLOOR)
+ assert_operator(n4, :>, n4 / 9 * 9)
+ assert_operator(n5, :>, n5 / 9 * 9)
+ assert_operator(n6, :>, n6 / 9 * 9)
+ assert_operator(m4, :>, m4 / 9 * 9)
+ assert_operator(m5, :>, m5 / 9 * 9)
+ assert_operator(m6, :>, m6 / 9 * 9)
+ assert_equal(2, n2h.round)
+ assert_equal(3, n3h.round)
+ assert_equal(-3, m2h.round)
+ assert_equal(-4, m3h.round)
+ end
+
+ def test_zero_p
+ assert_equal(true, BigDecimal.new("0").zero?)
+ assert_equal(false, BigDecimal.new("1").zero?)
+ assert_equal(true, BigDecimal.new("0E200000000000000").zero?)
+ end
+
+ def test_nonzero_p
+ assert_equal(nil, BigDecimal.new("0").nonzero?)
+ assert_equal(BigDecimal.new("1"), BigDecimal.new("1").nonzero?)
+ end
+
+ def test_double_fig
+ assert_kind_of(Integer, BigDecimal.double_fig)
+ end
+
+ def test_cmp
+ n1 = BigDecimal.new("1")
+ n2 = BigDecimal.new("2")
+ assert_equal( 0, n1 <=> n1)
+ assert_equal( 1, n2 <=> n1)
+ assert_equal(-1, n1 <=> n2)
+ assert_operator(n1, :==, n1)
+ assert_operator(n1, :!=, n2)
+ assert_operator(n1, :<, n2)
+ assert_operator(n1, :<=, n1)
+ assert_operator(n1, :<=, n2)
+ assert_operator(n2, :>, n1)
+ assert_operator(n2, :>=, n1)
+ assert_operator(n1, :>=, n1)
+
+ assert_operator(BigDecimal.new("-0"), :==, BigDecimal.new("0"))
+ assert_operator(BigDecimal.new("0"), :<, BigDecimal.new("1"))
+ assert_operator(BigDecimal.new("1"), :>, BigDecimal.new("0"))
+ assert_operator(BigDecimal.new("1"), :>, BigDecimal.new("-1"))
+ assert_operator(BigDecimal.new("-1"), :<, BigDecimal.new("1"))
+ assert_operator(BigDecimal.new((2**100).to_s), :>, BigDecimal.new("1"))
+ assert_operator(BigDecimal.new("1"), :<, BigDecimal.new((2**100).to_s))
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ inf = BigDecimal.new("Infinity")
+ assert_operator(inf, :>, 1)
+ assert_operator(1, :<, inf)
+
+ assert_operator(BigDecimal("1E-1"), :==, 10**(-1), '#4825')
+ assert_equal(0, BigDecimal("1E-1") <=> 10**(-1), '#4825')
+ end
+
+ def test_cmp_issue9192
+ bug9192 = '[ruby-core:58756] [#9192]'
+ operators = { :== => :==, :< => :>, :> => :<, :<= => :>=, :>= => :<= }
+ 5.upto(8) do |i|
+ s = "706.0#{i}"
+ d = BigDecimal(s)
+ f = s.to_f
+ operators.each do |op, inv|
+ assert_equal(d.send(op, f), f.send(inv, d),
+ "(BigDecimal(#{s.inspect}) #{op} #{s}) and (#{s} #{inv} BigDecimal(#{s.inspect})) is different #{bug9192}")
+ end
+ end
+ end
+
+ def test_cmp_nan
+ n1 = BigDecimal.new("1")
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_equal(nil, BigDecimal.new("NaN") <=> n1)
+ assert_equal(false, BigDecimal.new("NaN") > n1)
+ end
+
+ def test_cmp_failing_coercion
+ n1 = BigDecimal.new("1")
+ assert_equal(nil, n1 <=> nil)
+ assert_raise(ArgumentError){n1 > nil}
+ end
+
+ def test_cmp_coerce
+ n1 = BigDecimal.new("1")
+ n2 = BigDecimal.new("2")
+ o1 = Object.new; def o1.coerce(x); [x, BigDecimal.new("1")]; end
+ o2 = Object.new; def o2.coerce(x); [x, BigDecimal.new("2")]; end
+ assert_equal( 0, n1 <=> o1)
+ assert_equal( 1, n2 <=> o1)
+ assert_equal(-1, n1 <=> o2)
+ assert_operator(n1, :==, o1)
+ assert_operator(n1, :!=, o2)
+ assert_operator(n1, :<, o2)
+ assert_operator(n1, :<=, o1)
+ assert_operator(n1, :<=, o2)
+ assert_operator(n2, :>, o1)
+ assert_operator(n2, :>=, o1)
+ assert_operator(n1, :>=, 1)
+ end
+
+ def test_cmp_bignum
+ assert_operator(BigDecimal.new((2**100).to_s), :==, 2**100)
+ end
+
+ def test_cmp_data
+ d = Time.now; def d.coerce(x); [x, x]; end
+ assert_operator(BigDecimal.new((2**100).to_s), :==, d)
+ end
+
+ def test_precs
+ a = BigDecimal.new("1").precs
+ assert_instance_of(Array, a)
+ assert_equal(2, a.size)
+ assert_kind_of(Integer, a[0])
+ assert_kind_of(Integer, a[1])
+ end
+
+ def test_hash
+ a = []
+ b = BigDecimal.new("1")
+ 10.times { a << b *= 10 }
+ h = {}
+ a.each_with_index {|x, i| h[x] = i }
+ a.each_with_index do |x, i|
+ assert_equal(i, h[x])
+ end
+ end
+
+ def test_marshal
+ s = Marshal.dump(BigDecimal("1", 1))
+ assert_equal(BigDecimal("1", 1), Marshal.load(s))
+
+ # corrupt data
+ s = s.gsub(/BigDecimal.*\z/m) {|x| x.gsub(/\d/m, "-") }
+ assert_raise(TypeError) { Marshal.load(s) }
+ end
+
+ def test_finite_infinite_nan
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
+
+ x = BigDecimal.new("0")
+ assert_equal(true, x.finite?)
+ assert_equal(nil, x.infinite?)
+ assert_equal(false, x.nan?)
+ y = 1 / x
+ assert_equal(false, y.finite?)
+ assert_equal(1, y.infinite?)
+ assert_equal(false, y.nan?)
+ y = -1 / x
+ assert_equal(false, y.finite?)
+ assert_equal(-1, y.infinite?)
+ assert_equal(false, y.nan?)
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ y = 0 / x
+ assert_equal(false, y.finite?)
+ assert_equal(nil, y.infinite?)
+ assert_equal(true, y.nan?)
+ end
+
+ def test_to_i
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+
+ x = BigDecimal.new("0")
+ assert_kind_of(Integer, x.to_i)
+ assert_equal(0, x.to_i)
+ assert_raise(FloatDomainError){( 1 / x).to_i}
+ assert_raise(FloatDomainError){(-1 / x).to_i}
+ assert_raise(FloatDomainError) {( 0 / x).to_i}
+ x = BigDecimal.new("1")
+ assert_equal(1, x.to_i)
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(2**100, x.to_i)
+ end
+
+ def test_to_f
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
+
+ x = BigDecimal.new("0")
+ assert_instance_of(Float, x.to_f)
+ assert_equal(0.0, x.to_f)
+ assert_equal( 1.0 / 0.0, ( 1 / x).to_f)
+ assert_equal(-1.0 / 0.0, (-1 / x).to_f)
+ assert_equal(true, ( 0 / x).to_f.nan?)
+ x = BigDecimal.new("1")
+ assert_equal(1.0, x.to_f)
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal((2**100).to_f, x.to_f)
+ x = BigDecimal.new("1" + "0" * 10000)
+ assert_equal(0, BigDecimal.new("-0").to_f)
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true)
+ assert_raise(FloatDomainError) { x.to_f }
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ assert_kind_of(Float, x .to_f)
+ assert_kind_of(Float, (-x).to_f)
+
+ bug6944 = '[ruby-core:47342]'
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, true)
+ x = "1e#{Float::MIN_10_EXP - 2*Float::DIG}"
+ assert_raise(FloatDomainError, x) {BigDecimal(x).to_f}
+ x = "-#{x}"
+ assert_raise(FloatDomainError, x) {BigDecimal(x).to_f}
+ x = "1e#{Float::MIN_10_EXP - Float::DIG}"
+ assert_nothing_raised(FloatDomainError, x) {
+ assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944)
+ }
+ x = "-#{x}"
+ assert_nothing_raised(FloatDomainError, x) {
+ assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944)
+ }
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, false)
+ x = "1e#{Float::MIN_10_EXP - 2*Float::DIG}"
+ assert_equal( 0.0, BigDecimal(x).to_f, x)
+ x = "-#{x}"
+ assert_equal(-0.0, BigDecimal(x).to_f, x)
+ x = "1e#{Float::MIN_10_EXP - Float::DIG}"
+ assert_nothing_raised(FloatDomainError, x) {
+ assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944)
+ }
+ x = "-#{x}"
+ assert_nothing_raised(FloatDomainError, x) {
+ assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944)
+ }
+
+ assert_equal( 0.0, BigDecimal( '9e-325').to_f)
+ assert_equal( 0.0, BigDecimal( '10e-325').to_f)
+ assert_equal(-0.0, BigDecimal( '-9e-325').to_f)
+ assert_equal(-0.0, BigDecimal('-10e-325').to_f)
+ end
+
+ def test_coerce
+ a, b = BigDecimal.new("1").coerce(1.0)
+ assert_instance_of(BigDecimal, a)
+ assert_instance_of(BigDecimal, b)
+ assert_equal(2, 1 + BigDecimal.new("1"), '[ruby-core:25697]')
+
+ a, b = BigDecimal("1").coerce(1.quo(10))
+ assert_equal(BigDecimal("0.1"), a, '[ruby-core:34318]')
+
+ a, b = BigDecimal("0.11111").coerce(1.quo(3))
+ assert_equal(BigDecimal("0." + "3"*a.precs[0]), a)
+
+ assert_nothing_raised(TypeError, '#7176') do
+ BigDecimal.new('1') + Rational(1)
+ end
+ end
+
+ def test_uplus
+ x = BigDecimal.new("1")
+ assert_equal(x, x.send(:+@))
+ end
+
+ def test_add
+ x = BigDecimal.new("1")
+ assert_equal(BigDecimal.new("2"), x + x)
+ assert_equal(1, BigDecimal.new("0") + 1)
+ assert_equal(1, x + 0)
+
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("0") + 0).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("-0") + 0).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal.new("-0") + BigDecimal.new("-0")).sign)
+
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(BigDecimal.new((2**100+1).to_s), x + 1)
+ end
+
+ def test_sub
+ x = BigDecimal.new("1")
+ assert_equal(BigDecimal.new("0"), x - x)
+ assert_equal(-1, BigDecimal.new("0") - 1)
+ assert_equal(1, x - 0)
+
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("0") - 0).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal.new("-0") - 0).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("-0") - BigDecimal.new("-0")).sign)
+
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(BigDecimal.new((2**100-1).to_s), x - 1)
+ end
+
+ def test_sub_with_float
+ assert_kind_of(BigDecimal, BigDecimal.new("3") - 1.0)
+ end
+
+ def test_sub_with_rational
+ assert_kind_of(BigDecimal, BigDecimal.new("3") - 1.quo(3))
+ end
+
+ def test_mult
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(BigDecimal.new((2**100 * 3).to_s), (x * 3).to_i)
+ assert_equal(x, (x * 1).to_i)
+ assert_equal(x, (BigDecimal("1") * x).to_i)
+ assert_equal(BigDecimal.new((2**200).to_s), (x * x).to_i)
+ end
+
+ def test_mult_with_float
+ assert_kind_of(BigDecimal, BigDecimal.new("3") * 1.5)
+ end
+
+ def test_mult_with_rational
+ assert_kind_of(BigDecimal, BigDecimal.new("3") * 1.quo(3))
+ end
+
+ def test_mult_with_nil
+ assert_raise(TypeError) {
+ BigDecimal('1.1') * nil
+ }
+ end
+
+ def test_div
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(BigDecimal.new((2**100 / 3).to_s), (x / 3).to_i)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (BigDecimal.new("0") / 1).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (BigDecimal.new("-0") / 1).sign)
+ assert_equal(2, BigDecimal.new("2") / 1)
+ assert_equal(-2, BigDecimal.new("2") / -1)
+
+ assert_equal(BigDecimal('1486.868686869'), BigDecimal('1472.0') / BigDecimal('0.99'), '[ruby-core:59365] [#9316]')
+
+ assert_equal(4.124045235, BigDecimal('0.9932') / (700 * BigDecimal('0.344045') / BigDecimal('1000.0')), '[#9305]')
+ end
+
+ def test_div_with_float
+ assert_kind_of(BigDecimal, BigDecimal.new("3") / 1.5)
+ end
+
+ def test_div_with_rational
+ assert_kind_of(BigDecimal, BigDecimal.new("3") / 1.quo(3))
+ end
+
+ def test_mod
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(1, x % 3)
+ assert_equal(2, (-x) % 3)
+ assert_equal(-2, x % -3)
+ assert_equal(-1, (-x) % -3)
+ end
+
+ def test_mod_with_float
+ assert_kind_of(BigDecimal, BigDecimal.new("3") % 1.5)
+ end
+
+ def test_mod_with_rational
+ assert_kind_of(BigDecimal, BigDecimal.new("3") % 1.quo(3))
+ end
+
+ def test_remainder
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(1, x.remainder(3))
+ assert_equal(-1, (-x).remainder(3))
+ assert_equal(1, x.remainder(-3))
+ assert_equal(-1, (-x).remainder(-3))
+ end
+
+ def test_remainder_with_float
+ assert_kind_of(BigDecimal, BigDecimal.new("3").remainder(1.5))
+ end
+
+ def test_remainder_with_rational
+ assert_kind_of(BigDecimal, BigDecimal.new("3").remainder(1.quo(3)))
+ end
+
+ def test_divmod
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal([(x / 3).floor, 1], x.divmod(3))
+ assert_equal([(-x / 3).floor, 2], (-x).divmod(3))
+
+ assert_equal([0, 0], BigDecimal.new("0").divmod(2))
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_raise(ZeroDivisionError){BigDecimal.new("0").divmod(0)}
+ end
+
+ def test_add_bigdecimal
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(3000000000000000000000000000000, x.add(x, 1))
+ assert_equal(2500000000000000000000000000000, x.add(x, 2))
+ assert_equal(2540000000000000000000000000000, x.add(x, 3))
+ end
+
+ def test_sub_bigdecimal
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(1000000000000000000000000000000, x.sub(1, 1))
+ assert_equal(1300000000000000000000000000000, x.sub(1, 2))
+ assert_equal(1270000000000000000000000000000, x.sub(1, 3))
+ end
+
+ def test_mult_bigdecimal
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(4000000000000000000000000000000, x.mult(3, 1))
+ assert_equal(3800000000000000000000000000000, x.mult(3, 2))
+ assert_equal(3800000000000000000000000000000, x.mult(3, 3))
+ end
+
+ def test_div_bigdecimal
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(422550200076076467165567735125, x.div(3))
+ assert_equal(400000000000000000000000000000, x.div(3, 1))
+ assert_equal(420000000000000000000000000000, x.div(3, 2))
+ assert_equal(423000000000000000000000000000, x.div(3, 3))
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert_equal(0, BigDecimal("0").div(BigDecimal("Infinity")))
+ end
+ end
+
+ def test_abs_bigdecimal
+ x = BigDecimal.new((2**100).to_s)
+ assert_equal(1267650600228229401496703205376, x.abs)
+ x = BigDecimal.new("-" + (2**100).to_s)
+ assert_equal(1267650600228229401496703205376, x.abs)
+ x = BigDecimal.new("0")
+ assert_equal(0, x.abs)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ x = BigDecimal.new("NaN")
+ assert_equal(true, x.abs.nan?)
+ end
+
+ def test_sqrt_bigdecimal
+ x = BigDecimal.new("0.09")
+ assert_in_delta(0.3, x.sqrt(1), 0.001)
+ x = BigDecimal.new((2**100).to_s)
+ y = BigDecimal("1125899906842624")
+ e = y.exponent
+ assert_equal(true, (x.sqrt(100) - y).abs < BigDecimal("1E#{e-100}"))
+ assert_equal(true, (x.sqrt(200) - y).abs < BigDecimal("1E#{e-200}"))
+ assert_equal(true, (x.sqrt(300) - y).abs < BigDecimal("1E#{e-300}"))
+ x = BigDecimal.new("-" + (2**100).to_s)
+ assert_raise(FloatDomainError) { x.sqrt(1) }
+ x = BigDecimal.new((2**200).to_s)
+ assert_equal(2**100, x.sqrt(1))
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_raise(FloatDomainError) { BigDecimal.new("NaN").sqrt(1) }
+
+ assert_equal(0, BigDecimal.new("0").sqrt(1))
+ assert_equal(1, BigDecimal.new("1").sqrt(1))
+ end
+
+ def test_sqrt_5266
+ x = BigDecimal('2' + '0'*100)
+ assert_equal('0.14142135623730950488016887242096980785696718753769480731',
+ x.sqrt(56).to_s(56).split(' ')[0])
+ assert_equal('0.1414213562373095048801688724209698078569671875376948073',
+ x.sqrt(55).to_s(55).split(' ')[0])
+
+ x = BigDecimal('2' + '0'*200)
+ assert_equal('0.14142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727350138462',
+ x.sqrt(110).to_s(110).split(' ')[0])
+ assert_equal('0.1414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641572735013846',
+ x.sqrt(109).to_s(109).split(' ')[0])
+ end
+
+ def test_fix
+ x = BigDecimal.new("1.1")
+ assert_equal(1, x.fix)
+ end
+
+ def test_frac
+ x = BigDecimal.new("1.1")
+ assert_equal(0.1, x.frac)
+ assert_equal(0.1, BigDecimal.new("0.1").frac)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_equal(true, BigDecimal.new("NaN").frac.nan?)
+ end
+
+ def test_round
+ assert_equal(3, BigDecimal.new("3.14159").round)
+ assert_equal(9, BigDecimal.new("8.7").round)
+ assert_equal(3.142, BigDecimal.new("3.14159").round(3))
+ assert_equal(13300.0, BigDecimal.new("13345.234").round(-2))
+
+ x = BigDecimal.new("111.111")
+ assert_equal(111 , x.round)
+ assert_equal(111.1 , x.round(1))
+ assert_equal(111.11 , x.round(2))
+ assert_equal(111.111, x.round(3))
+ assert_equal(111.111, x.round(4))
+ assert_equal(110 , x.round(-1))
+ assert_equal(100 , x.round(-2))
+ assert_equal( 0 , x.round(-3))
+ assert_equal( 0 , x.round(-4))
+
+ x = BigDecimal.new("2.5")
+ assert_equal(3, x.round(0, BigDecimal::ROUND_UP))
+ assert_equal(2, x.round(0, BigDecimal::ROUND_DOWN))
+ assert_equal(3, x.round(0, BigDecimal::ROUND_HALF_UP))
+ assert_equal(2, x.round(0, BigDecimal::ROUND_HALF_DOWN))
+ assert_equal(2, x.round(0, BigDecimal::ROUND_HALF_EVEN))
+ assert_equal(3, x.round(0, BigDecimal::ROUND_CEILING))
+ assert_equal(2, x.round(0, BigDecimal::ROUND_FLOOR))
+ assert_raise(ArgumentError) { x.round(0, 256) }
+
+ ROUNDING_MODE_MAP.each do |const, sym|
+ assert_equal(x.round(0, const), x.round(0, sym))
+ end
+
+ bug3803 = '[ruby-core:32136]'
+ 15.times do |n|
+ x = BigDecimal.new("5#{'0'*n}1")
+ assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_DOWN), bug3803)
+ assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_EVEN), bug3803)
+ x = BigDecimal.new("0.5#{'0'*n}1")
+ assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_DOWN), bug3803)
+ assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_EVEN), bug3803)
+ x = BigDecimal.new("-0.5#{'0'*n}1")
+ assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_DOWN), bug3803)
+ assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_EVEN), bug3803)
+ end
+ end
+
+ def test_truncate
+ assert_equal(3, BigDecimal.new("3.14159").truncate)
+ assert_equal(8, BigDecimal.new("8.7").truncate)
+ assert_equal(3.141, BigDecimal.new("3.14159").truncate(3))
+ assert_equal(13300.0, BigDecimal.new("13345.234").truncate(-2))
+ end
+
+ def test_floor
+ assert_equal(3, BigDecimal.new("3.14159").floor)
+ assert_equal(-10, BigDecimal.new("-9.1").floor)
+ assert_equal(3.141, BigDecimal.new("3.14159").floor(3))
+ assert_equal(13300.0, BigDecimal.new("13345.234").floor(-2))
+ end
+
+ def test_ceil
+ assert_equal(4, BigDecimal.new("3.14159").ceil)
+ assert_equal(-9, BigDecimal.new("-9.1").ceil)
+ assert_equal(3.142, BigDecimal.new("3.14159").ceil(3))
+ assert_equal(13400.0, BigDecimal.new("13345.234").ceil(-2))
+ end
+
+ def test_to_s
+ assert_equal('-123.45678 90123 45678 9', BigDecimal.new('-123.45678901234567890').to_s('5F'))
+ assert_equal('+123.45678901 23456789', BigDecimal.new('123.45678901234567890').to_s('+8F'))
+ assert_equal(' 123.4567890123456789', BigDecimal.new('123.45678901234567890').to_s(' F'))
+ assert_equal('0.1234567890123456789E3', BigDecimal.new('123.45678901234567890').to_s)
+ assert_equal('0.12345 67890 12345 6789E3', BigDecimal.new('123.45678901234567890').to_s(5))
+ end
+
+ def test_split
+ x = BigDecimal.new('-123.45678901234567890')
+ assert_equal([-1, "1234567890123456789", 10, 3], x.split)
+ assert_equal([1, "0", 10, 0], BigDecimal.new("0").split)
+ assert_equal([-1, "0", 10, 0], BigDecimal.new("-0").split)
+
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_equal([0, "NaN", 10, 0], BigDecimal.new("NaN").split)
+ assert_equal([1, "Infinity", 10, 0], BigDecimal.new("Infinity").split)
+ assert_equal([-1, "Infinity", 10, 0], BigDecimal.new("-Infinity").split)
+ end
+
+ def test_exponent
+ x = BigDecimal.new('-123.45678901234567890')
+ assert_equal(3, x.exponent)
+ end
+
+ def test_inspect
+ x = BigDecimal.new("1234.5678")
+ prec, maxprec = x.precs
+ assert_match(/^#<BigDecimal:[0-9a-f]+,'0.12345678E4',#{prec}\(#{maxprec}\)>$/, x.inspect)
+ end
+
+ def test_power
+ assert_nothing_raised(TypeError, '[ruby-core:47632]') do
+ 1000.times { BigDecimal.new('1001.10')**0.75 }
+ end
+ end
+
+ def test_power_with_nil
+ assert_raise(TypeError) do
+ BigDecimal(3) ** nil
+ end
+ end
+
+ def test_power_of_nan
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert_nan(BigDecimal::NAN ** 0)
+ assert_nan(BigDecimal::NAN ** 1)
+ assert_nan(BigDecimal::NAN ** 42)
+ assert_nan(BigDecimal::NAN ** -42)
+ assert_nan(BigDecimal::NAN ** 42.0)
+ assert_nan(BigDecimal::NAN ** -42.0)
+ assert_nan(BigDecimal::NAN ** BigDecimal(42))
+ assert_nan(BigDecimal::NAN ** BigDecimal(-42))
+ assert_nan(BigDecimal::NAN ** BigDecimal::INFINITY)
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert_nan(BigDecimal::NAN ** (-BigDecimal::INFINITY))
+ end
+ end
+ end
+
+ def test_power_with_Bignum
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert_equal(0, BigDecimal(0) ** (2**100))
+
+ assert_positive_infinite(BigDecimal(0) ** -(2**100))
+ assert_positive_infinite((-BigDecimal(0)) ** -(2**100))
+ assert_negative_infinite((-BigDecimal(0)) ** -(2**100 + 1))
+
+ assert_equal(1, BigDecimal(1) ** (2**100))
+
+ assert_positive_infinite(BigDecimal(3) ** (2**100))
+ assert_positive_zero(BigDecimal(3) ** (-2**100))
+
+ assert_negative_infinite(BigDecimal(-3) ** (2**100))
+ assert_positive_infinite(BigDecimal(-3) ** (2**100 + 1))
+ assert_negative_zero(BigDecimal(-3) ** (-2**100))
+ assert_positive_zero(BigDecimal(-3) ** (-2**100 - 1))
+
+ assert_positive_zero(BigDecimal(0.5, Float::DIG) ** (2**100))
+ assert_positive_infinite(BigDecimal(0.5, Float::DIG) ** (-2**100))
+
+ assert_negative_zero(BigDecimal(-0.5, Float::DIG) ** (2**100))
+ assert_positive_zero(BigDecimal(-0.5, Float::DIG) ** (2**100 - 1))
+ assert_negative_infinite(BigDecimal(-0.5, Float::DIG) ** (-2**100))
+ assert_positive_infinite(BigDecimal(-0.5, Float::DIG) ** (-2**100 - 1))
+ end
+ end
+
+ def test_power_with_BigDecimal
+ assert_nothing_raised do
+ assert_in_delta(3 ** 3, BigDecimal(3) ** BigDecimal(3))
+ end
+ end
+
+ def test_power_of_finite_with_zero
+ x = BigDecimal(1)
+ assert_equal(1, x ** 0)
+ assert_equal(1, x ** 0.quo(1))
+ assert_equal(1, x ** 0.0)
+ assert_equal(1, x ** BigDecimal(0))
+
+ x = BigDecimal(42)
+ assert_equal(1, x ** 0)
+ assert_equal(1, x ** 0.quo(1))
+ assert_equal(1, x ** 0.0)
+ assert_equal(1, x ** BigDecimal(0))
+
+ x = BigDecimal(-42)
+ assert_equal(1, x ** 0)
+ assert_equal(1, x ** 0.quo(1))
+ assert_equal(1, x ** 0.0)
+ assert_equal(1, x ** BigDecimal(0))
+ end
+
+ def test_power_of_three
+ x = BigDecimal(3)
+ assert_equal(81, x ** 4)
+ assert_equal(1.quo(81), x ** -4)
+ assert_in_delta(1.0/81, x ** -4)
+ end
+
+ def test_power_of_zero
+ zero = BigDecimal(0)
+ assert_equal(0, zero ** 4)
+ assert_equal(0, zero ** 4.quo(1))
+ assert_equal(0, zero ** 4.0)
+ assert_equal(0, zero ** BigDecimal(4))
+ assert_equal(1, zero ** 0)
+ assert_equal(1, zero ** 0.quo(1))
+ assert_equal(1, zero ** 0.0)
+ assert_equal(1, zero ** BigDecimal(0))
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ assert_positive_infinite(zero ** -1)
+ assert_positive_infinite(zero ** -1.quo(1))
+ assert_positive_infinite(zero ** -1.0)
+ assert_positive_infinite(zero ** BigDecimal(-1))
+
+ m_zero = BigDecimal("-0")
+ assert_negative_infinite(m_zero ** -1)
+ assert_negative_infinite(m_zero ** -1.quo(1))
+ assert_negative_infinite(m_zero ** -1.0)
+ assert_negative_infinite(m_zero ** BigDecimal(-1))
+ assert_positive_infinite(m_zero ** -2)
+ assert_positive_infinite(m_zero ** -2.quo(1))
+ assert_positive_infinite(m_zero ** -2.0)
+ assert_positive_infinite(m_zero ** BigDecimal(-2))
+ end
+ end
+
+ def test_power_of_positive_infinity
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ assert_positive_infinite(BigDecimal::INFINITY ** 3)
+ assert_positive_infinite(BigDecimal::INFINITY ** 3.quo(1))
+ assert_positive_infinite(BigDecimal::INFINITY ** 3.0)
+ assert_positive_infinite(BigDecimal::INFINITY ** BigDecimal(3))
+ assert_positive_infinite(BigDecimal::INFINITY ** 2)
+ assert_positive_infinite(BigDecimal::INFINITY ** 2.quo(1))
+ assert_positive_infinite(BigDecimal::INFINITY ** 2.0)
+ assert_positive_infinite(BigDecimal::INFINITY ** BigDecimal(2))
+ assert_positive_infinite(BigDecimal::INFINITY ** 1)
+ assert_positive_infinite(BigDecimal::INFINITY ** 1.quo(1))
+ assert_positive_infinite(BigDecimal::INFINITY ** 1.0)
+ assert_positive_infinite(BigDecimal::INFINITY ** BigDecimal(1))
+ assert_equal(1, BigDecimal::INFINITY ** 0)
+ assert_equal(1, BigDecimal::INFINITY ** 0.quo(1))
+ assert_equal(1, BigDecimal::INFINITY ** 0.0)
+ assert_equal(1, BigDecimal::INFINITY ** BigDecimal(0))
+ assert_positive_zero(BigDecimal::INFINITY ** -1)
+ assert_positive_zero(BigDecimal::INFINITY ** -1.quo(1))
+ assert_positive_zero(BigDecimal::INFINITY ** -1.0)
+ assert_positive_zero(BigDecimal::INFINITY ** BigDecimal(-1))
+ assert_positive_zero(BigDecimal::INFINITY ** -2)
+ assert_positive_zero(BigDecimal::INFINITY ** -2.0)
+ assert_positive_zero(BigDecimal::INFINITY ** BigDecimal(-2))
+ end
+ end
+
+ def test_power_of_negative_infinity
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ assert_negative_infinite((-BigDecimal::INFINITY) ** 3)
+ assert_negative_infinite((-BigDecimal::INFINITY) ** 3.quo(1))
+ assert_negative_infinite((-BigDecimal::INFINITY) ** 3.0)
+ assert_negative_infinite((-BigDecimal::INFINITY) ** BigDecimal(3))
+ assert_positive_infinite((-BigDecimal::INFINITY) ** 2)
+ assert_positive_infinite((-BigDecimal::INFINITY) ** 2.quo(1))
+ assert_positive_infinite((-BigDecimal::INFINITY) ** 2.0)
+ assert_positive_infinite((-BigDecimal::INFINITY) ** BigDecimal(2))
+ assert_negative_infinite((-BigDecimal::INFINITY) ** 1)
+ assert_negative_infinite((-BigDecimal::INFINITY) ** 1.quo(1))
+ assert_negative_infinite((-BigDecimal::INFINITY) ** 1.0)
+ assert_negative_infinite((-BigDecimal::INFINITY) ** BigDecimal(1))
+ assert_equal(1, (-BigDecimal::INFINITY) ** 0)
+ assert_equal(1, (-BigDecimal::INFINITY) ** 0.quo(1))
+ assert_equal(1, (-BigDecimal::INFINITY) ** 0.0)
+ assert_equal(1, (-BigDecimal::INFINITY) ** BigDecimal(0))
+ assert_negative_zero((-BigDecimal::INFINITY) ** -1)
+ assert_negative_zero((-BigDecimal::INFINITY) ** -1.quo(1))
+ assert_negative_zero((-BigDecimal::INFINITY) ** -1.0)
+ assert_negative_zero((-BigDecimal::INFINITY) ** BigDecimal(-1))
+ assert_positive_zero((-BigDecimal::INFINITY) ** -2)
+ assert_positive_zero((-BigDecimal::INFINITY) ** -2.quo(1))
+ assert_positive_zero((-BigDecimal::INFINITY) ** -2.0)
+ assert_positive_zero((-BigDecimal::INFINITY) ** BigDecimal(-2))
+ end
+ end
+
+ def test_power_without_prec
+ pi = BigDecimal("3.14159265358979323846264338327950288419716939937511")
+ e = BigDecimal("2.71828182845904523536028747135266249775724709369996")
+ pow = BigDecimal("22.4591577183610454734271522045437350275893151339967843873233068")
+ assert_equal(pow, pi.power(e))
+ end
+
+ def test_power_with_prec
+ pi = BigDecimal("3.14159265358979323846264338327950288419716939937511")
+ e = BigDecimal("2.71828182845904523536028747135266249775724709369996")
+ pow = BigDecimal("22.459157718361045473")
+ assert_equal(pow, pi.power(e, 20))
+
+ b = BigDecimal('1.034482758620689655172413793103448275862068965517241379310344827586206896551724')
+ assert_equal(BigDecimal('0.114523E1'), b.power(4, 5), '[Bug #8818] [ruby-core:56802]')
+ end
+
+ def test_limit
+ BigDecimal.limit(1)
+ x = BigDecimal.new("3")
+ assert_equal(90, x ** 4) # OK? must it be 80?
+ # 3 * 3 * 3 * 3 = 10 * 3 * 3 = 30 * 3 = 90 ???
+ assert_raise(ArgumentError) { BigDecimal.limit(-1) }
+
+ bug7458 = '[ruby-core:50269] [#7458]'
+ one = BigDecimal('1')
+ epsilon = BigDecimal('0.7E-18')
+ BigDecimal.save_limit do
+ BigDecimal.limit(0)
+ assert_equal(BigDecimal("1.0000000000000000007"), one + epsilon, "limit(0) #{bug7458}")
+
+ 1.upto(18) do |lim|
+ BigDecimal.limit(lim)
+ assert_equal(BigDecimal("1.0"), one + epsilon, "limit(#{lim}) #{bug7458}")
+ end
+
+ BigDecimal.limit(19)
+ assert_equal(BigDecimal("1.000000000000000001"), one + epsilon, "limit(19) #{bug7458}")
+
+ BigDecimal.limit(20)
+ assert_equal(BigDecimal("1.0000000000000000007"), one + epsilon, "limit(20) #{bug7458}")
+ end
+ end
+
+ def test_sign
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false)
+
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, BigDecimal.new("0").sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, BigDecimal.new("-0").sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_FINITE, BigDecimal.new("1").sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_FINITE, BigDecimal.new("-1").sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_INFINITE, (BigDecimal.new("1") / 0).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_INFINITE, (BigDecimal.new("-1") / 0).sign)
+ assert_equal(BigDecimal::SIGN_NaN, (BigDecimal.new("0") / 0).sign)
+ end
+
+ def test_inf
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ inf = BigDecimal.new("Infinity")
+
+ assert_equal(inf, inf + inf)
+ assert_equal(true, (inf + (-inf)).nan?)
+ assert_equal(true, (inf - inf).nan?)
+ assert_equal(inf, inf - (-inf))
+ assert_equal(inf, inf * inf)
+ assert_equal(true, (inf / inf).nan?)
+
+ assert_equal(inf, inf + 1)
+ assert_equal(inf, inf - 1)
+ assert_equal(inf, inf * 1)
+ assert_equal(true, (inf * 0).nan?)
+ assert_equal(inf, inf / 1)
+
+ assert_equal(inf, 1 + inf)
+ assert_equal(-inf, 1 - inf)
+ assert_equal(inf, 1 * inf)
+ assert_equal(-inf, -1 * inf)
+ assert_equal(true, (0 * inf).nan?)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (1 / inf).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (-1 / inf).sign)
+ end
+
+ def test_to_special_string
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ nan = BigDecimal.new("NaN")
+ assert_equal("NaN", nan.to_s)
+ inf = BigDecimal.new("Infinity")
+ assert_equal("Infinity", inf.to_s)
+ assert_equal(" Infinity", inf.to_s(" "))
+ assert_equal("+Infinity", inf.to_s("+"))
+ assert_equal("-Infinity", (-inf).to_s)
+ pzero = BigDecimal.new("0")
+ assert_equal("0.0", pzero.to_s)
+ assert_equal(" 0.0", pzero.to_s(" "))
+ assert_equal("+0.0", pzero.to_s("+"))
+ assert_equal("-0.0", (-pzero).to_s)
+ end
+
+ def test_to_string
+ assert_equal("0.01", BigDecimal("0.01").to_s("F"))
+ s = "0." + "0" * 100 + "1"
+ assert_equal(s, BigDecimal(s).to_s("F"))
+ s = "1" + "0" * 100 + ".0"
+ assert_equal(s, BigDecimal(s).to_s("F"))
+ end
+
+ def test_ctov
+ assert_equal(0.1, BigDecimal.new("1E-1"))
+ assert_equal(10, BigDecimal.new("1E+1"))
+ assert_equal(1, BigDecimal.new("+1"))
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+
+ assert_equal(BigDecimal::SIGN_POSITIVE_INFINITE, BigDecimal.new("1E1" + "0" * 10000).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_INFINITE, BigDecimal.new("-1E1" + "0" * 10000).sign)
+ assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, BigDecimal.new("1E-1" + "0" * 10000).sign)
+ assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, BigDecimal.new("-1E-1" + "0" * 10000).sign)
+ end
+
+ def test_split_under_gc_stress
+ bug3258 = '[ruby-dev:41213]'
+ expect = 10.upto(20).map{|i|[1, "1", 10, i+1].inspect}
+ assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, expect, [], bug3258)
+ GC.stress = true
+ 10.upto(20) do |i|
+ p BigDecimal.new("1"+"0"*i).split
+ end
+ EOS
+ end
+
+ def test_coerce_under_gc_stress
+ assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
+ expect = ":too_long_to_embed_as_string can't be coerced into BigDecimal"
+ b = BigDecimal.new("1")
+ GC.stress = true
+ 10.times do
+ begin
+ b.coerce(:too_long_to_embed_as_string)
+ rescue => e
+ raise unless e.is_a?(TypeError)
+ raise "'\#{expect}' is expected, but '\#{e.message}'" unless e.message == expect
+ end
+ end
+ EOS
+ end
+
+ def test_INFINITY
+ assert(BigDecimal::INFINITY.infinite?, "BigDecimal::INFINITY is not a infinity")
+ end
+
+ def test_NAN
+ assert(BigDecimal::NAN.nan?, "BigDecimal::NAN is not NaN")
+ end
+
+ def test_exp_with_zerp_precision
+ assert_raise(ArgumentError) do
+ BigMath.exp(1, 0)
+ end
+ end
+
+ def test_exp_with_negative_precision
+ assert_raise(ArgumentError) do
+ BigMath.exp(1, -42)
+ end
+ end
+
+ def test_exp_with_complex
+ assert_raise(ArgumentError) do
+ BigMath.exp(Complex(1, 2), 20)
+ end
+ end
+
+ def test_exp_with_negative_infinite
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert_equal(0, BigMath.exp(-BigDecimal::INFINITY, 20))
+ end
+ end
+
+ def test_exp_with_positive_infinite
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert(BigMath.exp(BigDecimal::INFINITY, 20) > 0)
+ assert(BigMath.exp(BigDecimal::INFINITY, 20).infinite?)
+ end
+ end
+
+ def test_exp_with_nan
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert(BigMath.exp(BigDecimal::NAN, 20).nan?)
+ end
+ end
+
+ def test_exp_with_1
+ assert_in_epsilon(Math::E, BigMath.exp(1, 20))
+ end
+
+ def test_BigMath_exp
+ prec = 20
+ assert_in_epsilon(Math.exp(20), BigMath.exp(BigDecimal("20"), prec))
+ assert_in_epsilon(Math.exp(40), BigMath.exp(BigDecimal("40"), prec))
+ assert_in_epsilon(Math.exp(-20), BigMath.exp(BigDecimal("-20"), prec))
+ assert_in_epsilon(Math.exp(-40), BigMath.exp(BigDecimal("-40"), prec))
+ end
+
+ def test_BigMath_exp_with_float
+ prec = 20
+ assert_in_epsilon(Math.exp(20), BigMath.exp(20.0, prec))
+ assert_in_epsilon(Math.exp(40), BigMath.exp(40.0, prec))
+ assert_in_epsilon(Math.exp(-20), BigMath.exp(-20.0, prec))
+ assert_in_epsilon(Math.exp(-40), BigMath.exp(-40.0, prec))
+ end
+
+ def test_BigMath_exp_with_fixnum
+ prec = 20
+ assert_in_epsilon(Math.exp(20), BigMath.exp(20, prec))
+ assert_in_epsilon(Math.exp(40), BigMath.exp(40, prec))
+ assert_in_epsilon(Math.exp(-20), BigMath.exp(-20, prec))
+ assert_in_epsilon(Math.exp(-40), BigMath.exp(-40, prec))
+ end
+
+ def test_BigMath_exp_with_rational
+ prec = 20
+ assert_in_epsilon(Math.exp(20), BigMath.exp(Rational(40,2), prec))
+ assert_in_epsilon(Math.exp(40), BigMath.exp(Rational(80,2), prec))
+ assert_in_epsilon(Math.exp(-20), BigMath.exp(Rational(-40,2), prec))
+ assert_in_epsilon(Math.exp(-40), BigMath.exp(Rational(-80,2), prec))
+ end
+
+ def test_BigMath_exp_under_gc_stress
+ assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
+ expect = ":too_long_to_embed_as_string can't be coerced into BigDecimal"
+ 10.times do
+ begin
+ BigMath.exp(:too_long_to_embed_as_string, 6)
+ rescue => e
+ raise unless e.is_a?(ArgumentError)
+ raise "'\#{expect}' is expected, but '\#{e.message}'" unless e.message == expect
+ end
+ end
+ EOS
+ end
+
+ def test_BigMath_log_with_string
+ assert_raise(ArgumentError) do
+ BigMath.log("foo", 20)
+ end
+ end
+
+ def test_BigMath_log_with_nil
+ assert_raise(ArgumentError) do
+ BigMath.log(nil, 20)
+ end
+ end
+
+ def test_BigMath_log_with_non_integer_precision
+ assert_raise(ArgumentError) do
+ BigMath.log(1, 0.5)
+ end
+ end
+
+ def test_BigMath_log_with_nil_precision
+ assert_raise(ArgumentError) do
+ BigMath.log(1, nil)
+ end
+ end
+
+ def test_BigMath_log_with_complex
+ assert_raise(Math::DomainError) do
+ BigMath.log(Complex(1, 2), 20)
+ end
+ end
+
+ def test_BigMath_log_with_zero_arg
+ assert_raise(Math::DomainError) do
+ BigMath.log(0, 20)
+ end
+ end
+
+ def test_BigMath_log_with_negative_arg
+ assert_raise(Math::DomainError) do
+ BigMath.log(-1, 20)
+ end
+ end
+
+ def test_BigMath_log_with_zero_precision
+ assert_raise(ArgumentError) do
+ BigMath.log(1, 0)
+ end
+ end
+
+ def test_BigMath_log_with_negative_precision
+ assert_raise(ArgumentError) do
+ BigMath.log(1, -42)
+ end
+ end
+
+ def test_BigMath_log_with_negative_infinite
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert_raise(Math::DomainError) do
+ BigMath.log(-BigDecimal::INFINITY, 20)
+ end
+ end
+ end
+
+ def test_BigMath_log_with_positive_infinite
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+ assert(BigMath.log(BigDecimal::INFINITY, 20) > 0)
+ assert(BigMath.log(BigDecimal::INFINITY, 20).infinite?)
+ end
+ end
+
+ def test_BigMath_log_with_nan
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert(BigMath.log(BigDecimal::NAN, 20).nan?)
+ end
+ end
+
+ def test_BigMath_log_with_float_nan
+ BigDecimal.save_exception_mode do
+ BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+ assert(BigMath.log(Float::NAN, 20).nan?)
+ end
+ end
+
+ def test_BigMath_log_with_1
+ assert_in_delta(0.0, BigMath.log(1, 20))
+ assert_in_delta(0.0, BigMath.log(1.0, 20))
+ assert_in_delta(0.0, BigMath.log(BigDecimal(1), 20))
+ end
+
+ def test_BigMath_log_with_exp_1
+ assert_in_delta(1.0, BigMath.log(BigMath.E(10), 10))
+ end
+
+ def test_BigMath_log_with_2
+ assert_in_delta(Math.log(2), BigMath.log(2, 20))
+ assert_in_delta(Math.log(2), BigMath.log(2.0, 20))
+ assert_in_delta(Math.log(2), BigMath.log(BigDecimal(2), 20))
+ end
+
+ def test_BigMath_log_with_square_of_E
+ assert_in_delta(2, BigMath.log(BigMath.E(20)**2, 20))
+ end
+
+ def test_BigMath_log_with_high_precision_case
+ e = BigDecimal('2.71828182845904523536028747135266249775724709369996')
+ e_3 = e.mult(e, 50).mult(e, 50)
+ log_3 = BigMath.log(e_3, 50)
+ assert_in_delta(3, log_3, 0.0000000000_0000000000_0000000000_0000000000_0000000001)
+ end
+
+ def test_BigMath_log_with_42
+ assert_in_delta(Math.log(42), BigMath.log(42, 20))
+ assert_in_delta(Math.log(42), BigMath.log(42.0, 20))
+ assert_in_delta(Math.log(42), BigMath.log(BigDecimal(42), 20))
+ end
+
+ def test_BigMath_log_with_101
+ # this is mainly a performance test (should be very fast, not the 0.3 s)
+ assert_in_delta(Math.log(101), BigMath.log(101, 20), 1E-15)
+ end
+
+ def test_BigMath_log_with_reciprocal_of_42
+ assert_in_delta(Math.log(1e-42), BigMath.log(1e-42, 20))
+ assert_in_delta(Math.log(1e-42), BigMath.log(BigDecimal("1e-42"), 20))
+ end
+
+ def test_BigMath_log_under_gc_stress
+ assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
+ expect = ":too_long_to_embed_as_string can't be coerced into BigDecimal"
+ 10.times do
+ begin
+ BigMath.log(:too_long_to_embed_as_string, 6)
+ rescue => e
+ raise unless e.is_a?(ArgumentError)
+ raise "'\#{expect}' is expected, but '\#{e.message}'" unless e.message == expect
+ end
+ end
+ EOS
+ end
+
+ def test_dup
+ [1, -1, 2**100, -2**100].each do |i|
+ x = BigDecimal(i)
+ assert_equal(x, x.dup)
+ end
+ end
+
+ def test_dup_subclass
+ c = Class.new(BigDecimal)
+ x = c.new(1)
+ y = x.dup
+ assert_equal(1, y)
+ assert_kind_of(c, y)
+ end
+
+ def test_to_d
+ bug6093 = '[ruby-core:42969]'
+ code = "exit(BigDecimal.new('10.0') == 10.0.to_d)"
+ assert_ruby_status(%w[-rbigdecimal -rbigdecimal/util -rmathn -], code, bug6093)
+ end
+
+ def test_bug6406
+ assert_in_out_err(%w[-rbigdecimal --disable-gems], <<-EOS, [], [])
+ Thread.current.keys.to_s
+ EOS
+ end
+end
diff --git a/jni/ruby/test/bigdecimal/test_bigdecimal_util.rb b/jni/ruby/test/bigdecimal/test_bigdecimal_util.rb
new file mode 100644
index 0000000..e4d6400
--- /dev/null
+++ b/jni/ruby/test/bigdecimal/test_bigdecimal_util.rb
@@ -0,0 +1,50 @@
+require_relative "testbase"
+
+require 'bigdecimal/util'
+
+class TestBigDecimalUtil < Test::Unit::TestCase
+ def test_BigDecimal_to_d
+ x = BigDecimal(1)
+ assert_same(x, x.to_d)
+ end
+
+ def test_Integer_to_d
+ assert_equal(BigDecimal(1), 1.to_d)
+ assert_equal(BigDecimal(2<<100), (2<<100).to_d)
+ end
+
+ def test_Float_to_d_without_precision
+ delta = 1.0/10**(Float::DIG)
+ assert_in_delta(BigDecimal(0.5, Float::DIG), 0.5.to_d, delta)
+ assert_in_delta(BigDecimal(355.0/113.0, Float::DIG), (355.0/113.0).to_d, delta)
+ assert_equal(9.05.to_d.to_s('F'), "9.05")
+
+ bug9214 = '[ruby-core:58858]'
+ assert_equal((-0.0).to_d.sign, -1, bug9214)
+ end
+
+ def test_Float_to_d_with_precision
+ digits = 5
+ delta = 1.0/10**(digits)
+ assert_in_delta(BigDecimal(0.5, 5), 0.5.to_d(digits), delta)
+ assert_in_delta(BigDecimal(355.0/113.0, 5), (355.0/113.0).to_d(digits), delta)
+
+ bug9214 = '[ruby-core:58858]'
+ assert_equal((-0.0).to_d(digits).sign, -1, bug9214)
+ end
+
+ def test_Rational_to_d
+ digits = 100
+ delta = 1.0/10**(digits)
+ assert_in_delta(BigDecimal(1.quo(2), digits), 1.quo(2).to_d(digits), delta)
+ assert_in_delta(BigDecimal(355.quo(113), digits), 355.quo(113).to_d(digits), delta)
+ end
+
+ def test_Rational_to_d_with_zero_precision
+ assert_raise(ArgumentError) { 355.quo(113).to_d(0) }
+ end
+
+ def test_Rational_to_d_with_negative_precision
+ assert_raise(ArgumentError) { 355.quo(113).to_d(-42) }
+ end
+end
diff --git a/jni/ruby/test/bigdecimal/test_bigmath.rb b/jni/ruby/test/bigdecimal/test_bigmath.rb
new file mode 100644
index 0000000..740c755
--- /dev/null
+++ b/jni/ruby/test/bigdecimal/test_bigmath.rb
@@ -0,0 +1,80 @@
+require_relative "testbase"
+require "bigdecimal/math"
+
+class TestBigMath < Test::Unit::TestCase
+ include TestBigDecimalBase
+ include BigMath
+ N = 20
+ PINF = BigDecimal("+Infinity")
+ MINF = BigDecimal("-Infinity")
+ NAN = BigDecimal("NaN")
+
+ def test_const
+ assert_in_delta(Math::PI, PI(N))
+ assert_in_delta(Math::E, E(N))
+ end
+
+ def test_sqrt
+ assert_in_delta(2**0.5, sqrt(BigDecimal("2"), N))
+ assert_equal(10, sqrt(BigDecimal("100"), N))
+ assert_equal(0.0, sqrt(BigDecimal("0"), N))
+ assert_equal(0.0, sqrt(BigDecimal("-0"), N))
+ assert_raise(FloatDomainError) {sqrt(BigDecimal("-1.0"), N)}
+ assert_raise(FloatDomainError) {sqrt(NAN, N)}
+ assert_raise(FloatDomainError) {sqrt(PINF, N)}
+ end
+
+ def test_sin
+ assert_in_delta(0.0, sin(BigDecimal("0.0"), N))
+ assert_in_delta(Math.sqrt(2.0) / 2, sin(PI(N) / 4, N))
+ assert_in_delta(1.0, sin(PI(N) / 2, N))
+ assert_in_delta(0.0, sin(PI(N) * 2, N))
+ assert_in_delta(0.0, sin(PI(N), N))
+ assert_in_delta(-1.0, sin(PI(N) / -2, N))
+ assert_in_delta(0.0, sin(PI(N) * -2, N))
+ assert_in_delta(0.0, sin(-PI(N), N))
+ assert_in_delta(0.0, sin(PI(N) * 21, N))
+ assert_in_delta(0.0, sin(PI(N) * 30, N))
+ assert_in_delta(-1.0, sin(PI(N) * BigDecimal("301.5"), N))
+ end
+
+ def test_cos
+ assert_in_delta(1.0, cos(BigDecimal("0.0"), N))
+ assert_in_delta(Math.sqrt(2.0) / 2, cos(PI(N) / 4, N))
+ assert_in_delta(0.0, cos(PI(N) / 2, N))
+ assert_in_delta(1.0, cos(PI(N) * 2, N))
+ assert_in_delta(-1.0, cos(PI(N), N))
+ assert_in_delta(0.0, cos(PI(N) / -2, N))
+ assert_in_delta(1.0, cos(PI(N) * -2, N))
+ assert_in_delta(-1.0, cos(-PI(N), N))
+ assert_in_delta(-1.0, cos(PI(N) * 21, N))
+ assert_in_delta(1.0, cos(PI(N) * 30, N))
+ assert_in_delta(0.0, cos(PI(N) * BigDecimal("301.5"), N))
+ end
+
+ def test_atan
+ assert_equal(0.0, atan(BigDecimal("0.0"), N))
+ assert_in_delta(Math::PI/4, atan(BigDecimal("1.0"), N))
+ assert_in_delta(Math::PI/6, atan(sqrt(BigDecimal("3.0"), N) / 3, N))
+ assert_in_delta(Math::PI/2, atan(PINF, N))
+ assert_equal(BigDecimal("0.823840753418636291769355073102514088959345624027952954058347023122539489"),
+ atan(BigDecimal("1.08"), 72).round(72), '[ruby-dev:41257]')
+ end
+
+ def test_log
+ assert_equal(0, BigMath.log(BigDecimal("1.0"), 10))
+ assert_in_epsilon(Math.log(10)*1000, BigMath.log(BigDecimal("1e1000"), 10))
+ assert_raise(Math::DomainError) {BigMath.log(BigDecimal("0"), 10)}
+ assert_raise(Math::DomainError) {BigMath.log(BigDecimal("-1"), 10)}
+ assert_separately(%w[-rbigdecimal], <<-SRC)
+ begin
+ x = BigMath.log(BigDecimal("1E19999999999999"), 10)
+ rescue FloatDomainError
+ else
+ unless x.infinite?
+ assert_in_epsilon(Math.log(10)*19999999999999, x)
+ end
+ end
+ SRC
+ end
+end
diff --git a/jni/ruby/test/bigdecimal/testbase.rb b/jni/ruby/test/bigdecimal/testbase.rb
new file mode 100644
index 0000000..c014e61
--- /dev/null
+++ b/jni/ruby/test/bigdecimal/testbase.rb
@@ -0,0 +1,27 @@
+require "test/unit"
+require "bigdecimal"
+
+module TestBigDecimalBase
+ def setup
+ @mode = BigDecimal.mode(BigDecimal::EXCEPTION_ALL)
+ BigDecimal.mode(BigDecimal::EXCEPTION_ALL, true)
+ BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, true)
+ BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true)
+ BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP)
+ BigDecimal.limit(0)
+ end
+
+ def teardown
+ [BigDecimal::EXCEPTION_INFINITY, BigDecimal::EXCEPTION_NaN,
+ BigDecimal::EXCEPTION_UNDERFLOW, BigDecimal::EXCEPTION_OVERFLOW].each do |mode|
+ BigDecimal.mode(mode, !(@mode & mode).zero?)
+ end
+ end
+
+ def under_gc_stress
+ stress, GC.stress = GC.stress, true
+ yield
+ ensure
+ GC.stress = stress
+ end
+end
diff --git a/jni/ruby/test/cgi/test_cgi_cookie.rb b/jni/ruby/test/cgi/test_cgi_cookie.rb
new file mode 100644
index 0000000..c1c6a30
--- /dev/null
+++ b/jni/ruby/test/cgi/test_cgi_cookie.rb
@@ -0,0 +1,110 @@
+require 'test/unit'
+require 'cgi'
+require 'stringio'
+
+
+class CGICookieTest < Test::Unit::TestCase
+
+
+ def setup
+ ENV['REQUEST_METHOD'] = 'GET'
+ @str1="\xE3\x82\x86\xE3\x82\x93\xE3\x82\x86\xE3\x82\x93"
+ @str1.force_encoding("UTF-8") if defined?(::Encoding)
+ end
+
+ def teardown
+ %W[REQUEST_METHOD SCRIPT_NAME].each do |name|
+ ENV.delete(name)
+ end
+ end
+
+
+ def test_cgi_cookie_new_simple
+ cookie = CGI::Cookie.new('name1', 'val1', '&<>"', @str1)
+ assert_equal('name1', cookie.name)
+ assert_equal(['val1', '&<>"', @str1], cookie.value)
+ assert_nil(cookie.domain)
+ assert_nil(cookie.expires)
+ assert_equal('', cookie.path)
+ assert_equal(false, cookie.secure)
+ assert_equal("name1=val1&%26%3C%3E%22&%E3%82%86%E3%82%93%E3%82%86%E3%82%93; path=", cookie.to_s)
+ end
+
+
+ def test_cgi_cookie_new_complex
+ t = Time.gm(2030, 12, 31, 23, 59, 59)
+ value = ['val1', '&<>"', "\xA5\xE0\xA5\xB9\xA5\xAB"]
+ value[2].force_encoding("EUC-JP") if defined?(::Encoding)
+ cookie = CGI::Cookie.new('name'=>'name1',
+ 'value'=>value,
+ 'path'=>'/cgi-bin/myapp/',
+ 'domain'=>'www.example.com',
+ 'expires'=>t,
+ 'secure'=>true
+ )
+ assert_equal('name1', cookie.name)
+ assert_equal(value, cookie.value)
+ assert_equal('www.example.com', cookie.domain)
+ assert_equal(t, cookie.expires)
+ assert_equal('/cgi-bin/myapp/', cookie.path)
+ assert_equal(true, cookie.secure)
+ assert_equal('name1=val1&%26%3C%3E%22&%A5%E0%A5%B9%A5%AB; domain=www.example.com; path=/cgi-bin/myapp/; expires=Tue, 31 Dec 2030 23:59:59 GMT; secure', cookie.to_s)
+ end
+
+
+ def test_cgi_cookie_scriptname
+ cookie = CGI::Cookie.new('name1', 'value1')
+ assert_equal('', cookie.path)
+ cookie = CGI::Cookie.new('name'=>'name1', 'value'=>'value1')
+ assert_equal('', cookie.path)
+ ## when ENV['SCRIPT_NAME'] is set, cookie.path is set automatically
+ ENV['SCRIPT_NAME'] = '/cgi-bin/app/example.cgi'
+ cookie = CGI::Cookie.new('name1', 'value1')
+ assert_equal('/cgi-bin/app/', cookie.path)
+ cookie = CGI::Cookie.new('name'=>'name1', 'value'=>'value1')
+ assert_equal('/cgi-bin/app/', cookie.path)
+ end
+
+
+ def test_cgi_cookie_parse
+ ## ';' separator
+ cookie_str = 'name1=val1&val2; name2=val2&%26%3C%3E%22&%E3%82%86%E3%82%93%E3%82%86%E3%82%93;_session_id=12345'
+ cookies = CGI::Cookie.parse(cookie_str)
+ list = [
+ ['name1', ['val1', 'val2']],
+ ['name2', ['val2', '&<>"',@str1]],
+ ['_session_id', ['12345']],
+ ]
+ list.each do |name, value|
+ cookie = cookies[name]
+ assert_equal(name, cookie.name)
+ assert_equal(value, cookie.value)
+ end
+ ## ',' separator
+ cookie_str = 'name1=val1&val2, name2=val2&%26%3C%3E%22&%E3%82%86%E3%82%93%E3%82%86%E3%82%93,_session_id=12345'
+ cookies = CGI::Cookie.parse(cookie_str)
+ list.each do |name, value|
+ cookie = cookies[name]
+ assert_equal(name, cookie.name)
+ assert_equal(value, cookie.value)
+ end
+ end
+
+
+ def test_cgi_cookie_arrayinterface
+ cookie = CGI::Cookie.new('name1', 'a', 'b', 'c')
+ assert_equal('a', cookie[0])
+ assert_equal('c', cookie[2])
+ assert_nil(cookie[3])
+ assert_equal('a', cookie.first)
+ assert_equal('c', cookie.last)
+ assert_equal(['A', 'B', 'C'], cookie.collect{|e| e.upcase})
+ end
+
+
+
+ instance_methods.each do |method|
+ private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
+ end if ENV['TEST']
+
+end
diff --git a/jni/ruby/test/cgi/test_cgi_core.rb b/jni/ruby/test/cgi/test_cgi_core.rb
new file mode 100644
index 0000000..274d088
--- /dev/null
+++ b/jni/ruby/test/cgi/test_cgi_core.rb
@@ -0,0 +1,312 @@
+require 'test/unit'
+require 'cgi'
+require 'stringio'
+
+
+class CGICoreTest < Test::Unit::TestCase
+
+
+ def setup
+ #@environ = {
+ # 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ # 'REQUEST_METHOD' => 'GET',
+ # 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ #}
+ #ENV.update(@environ)
+ end
+
+
+ def teardown
+ @environ.each do |key, val| ENV.delete(key) end
+ $stdout = STDOUT
+ end
+
+ def test_cgi_parse_illegal_query
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => 'a=111&&b=222&c&d=',
+ 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ assert_equal(["a","b","c","d"],cgi.keys.sort)
+ assert_equal("",cgi["d"])
+ end
+
+ def test_cgi_core_params_GET
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => 'id=123&id=456&id=&id&str=%40h+%3D%7E+%2F%5E%24%2F',
+ 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ ## cgi[]
+ assert_equal('123', cgi['id'])
+ assert_equal('@h =~ /^$/', cgi['str'])
+ ## cgi.params
+ assert_equal(['123', '456', ''], cgi.params['id'])
+ assert_equal(['@h =~ /^$/'], cgi.params['str'])
+ ## cgi.keys
+ assert_equal(['id', 'str'], cgi.keys.sort)
+ ## cgi.key?, cgi.has_key?, cgi.include?
+ assert_equal(true, cgi.key?('id'))
+ assert_equal(true, cgi.has_key?('id'))
+ assert_equal(true, cgi.include?('id'))
+ assert_equal(false, cgi.key?('foo'))
+ assert_equal(false, cgi.has_key?('foo'))
+ assert_equal(false, cgi.include?('foo'))
+ ## invalid parameter name
+ assert_equal('', cgi['*notfound*']) # [ruby-dev:30740]
+ assert_equal([], cgi.params['*notfound*'])
+ end
+
+
+ def test_cgi_core_params_POST
+ query_str = 'id=123&id=456&id=&str=%40h+%3D%7E+%2F%5E%24%2F'
+ @environ = {
+ 'REQUEST_METHOD' => 'POST',
+ 'CONTENT_LENGTH' => query_str.length.to_s,
+ 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ $stdin = StringIO.new
+ $stdin << query_str
+ $stdin.rewind
+ cgi = CGI.new
+ ## cgi[]
+ assert_equal('123', cgi['id'])
+ assert_equal('@h =~ /^$/', cgi['str'])
+ ## cgi.params
+ assert_equal(['123', '456', ''], cgi.params['id'])
+ assert_equal(['@h =~ /^$/'], cgi.params['str'])
+ ## invalid parameter name
+ assert_equal('', cgi['*notfound*'])
+ assert_equal([], cgi.params['*notfound*'])
+ end
+
+ def test_cgi_core_params_encoding_check
+ query_str = 'str=%BE%BE%B9%BE'
+ @environ = {
+ 'REQUEST_METHOD' => 'POST',
+ 'CONTENT_LENGTH' => query_str.length.to_s,
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ $stdin = StringIO.new
+ $stdin << query_str
+ $stdin.rewind
+ if defined?(::Encoding)
+ hash={}
+ cgi = CGI.new(:accept_charset=>"UTF-8"){|key,val|hash[key]=val}
+ ## cgi[]
+ assert_equal("\xBE\xBE\xB9\xBE".force_encoding("UTF-8"), cgi['str'])
+ ## cgi.params
+ assert_equal(["\xBE\xBE\xB9\xBE".force_encoding("UTF-8")], cgi.params['str'])
+ ## accept-charset error
+ assert_equal({"str"=>"\xBE\xBE\xB9\xBE".force_encoding("UTF-8")},hash)
+
+ $stdin.rewind
+ assert_raise(CGI::InvalidEncoding) do
+ cgi = CGI.new(:accept_charset=>"UTF-8")
+ end
+
+ $stdin.rewind
+ cgi = CGI.new(:accept_charset=>"EUC-JP")
+ ## cgi[]
+ assert_equal("\xBE\xBE\xB9\xBE".force_encoding("EUC-JP"), cgi['str'])
+ ## cgi.params
+ assert_equal(["\xBE\xBE\xB9\xBE".force_encoding("EUC-JP")], cgi.params['str'])
+ else
+ assert(true)
+ end
+ end
+
+
+ def test_cgi_core_cookie
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => 'id=123&id=456&id=&str=%40h+%3D%7E+%2F%5E%24%2F',
+ 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ assert_not_equal(nil,cgi.cookies)
+ [ ['_session_id', ['12345'], ],
+ ['name1', ['val1', 'val2'], ],
+ ].each do |key, expected|
+ cookie = cgi.cookies[key]
+ assert_kind_of(CGI::Cookie, cookie)
+ assert_equal(expected, cookie.value)
+ assert_equal(false, cookie.secure)
+ assert_nil(cookie.expires)
+ assert_nil(cookie.domain)
+ assert_equal('', cookie.path)
+ end
+ end
+
+
+ def test_cgi_core_maxcontentlength
+ @environ = {
+ 'REQUEST_METHOD' => 'POST',
+ 'CONTENT_LENGTH' => (64 * 1024 * 1024).to_s
+ }
+ ENV.update(@environ)
+ ex = assert_raise(StandardError) do
+ CGI.new
+ end
+ assert_equal("too large post data.", ex.message)
+ end if CGI.const_defined?(:MAX_CONTENT_LENGTH)
+
+
+ def test_cgi_core_out
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => 'id=123&id=456&id=&str=%40h+%3D%7E+%2F%5E%24%2F',
+ 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ ## euc string
+ euc_str = "\270\253\244\355\241\242\277\315\244\254\245\264\245\337\244\316\244\350\244\246\244\300"
+ ## utf8 (not converted)
+ options = { 'charset'=>'utf8' }
+ $stdout = StringIO.new
+ cgi.out(options) { euc_str }
+ assert_nil(options['language'])
+ actual = $stdout.string
+ expected = "Content-Type: text/html; charset=utf8\r\n" +
+ "Content-Length: 22\r\n" +
+ "\r\n" +
+ euc_str
+ if defined?(::Encoding)
+ actual.force_encoding("ASCII-8BIT")
+ expected.force_encoding("ASCII-8BIT")
+ end
+ assert_equal(expected, actual)
+ ## language is keeped
+ options = { 'charset'=>'Shift_JIS', 'language'=>'en' }
+ $stdout = StringIO.new
+ cgi.out(options) { euc_str }
+ assert_equal('en', options['language'])
+ ## HEAD method
+ ENV['REQUEST_METHOD'] = 'HEAD'
+ options = { 'charset'=>'utf8' }
+ $stdout = StringIO.new
+ cgi.out(options) { euc_str }
+ actual = $stdout.string
+ expected = "Content-Type: text/html; charset=utf8\r\n" +
+ "Content-Length: 22\r\n" +
+ "\r\n"
+ assert_equal(expected, actual)
+ end
+
+
+ def test_cgi_core_print
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ $stdout = StringIO.new
+ str = "foobar"
+ cgi.print(str)
+ expected = str
+ actual = $stdout.string
+ assert_equal(expected, actual)
+ end
+
+
+ def test_cgi_core_environs
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ ##
+ list1 = %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
+ PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
+ REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
+ SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
+ HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
+ HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT
+ ]
+ # list2 = %w[ CONTENT_LENGTH SERVER_PORT ]
+ ## string expected
+ list1.each do |name|
+ @environ[name] = "**#{name}**"
+ end
+ ENV.update(@environ)
+ list1.each do |name|
+ method = name.sub(/\AHTTP_/, '').downcase
+ actual = cgi.__send__ method
+ expected = "**#{name}**"
+ assert_equal(expected, actual)
+ end
+ ## integer expected
+ ENV['CONTENT_LENGTH'] = '123'
+ ENV['SERVER_PORT'] = '8080'
+ assert_equal(123, cgi.content_length)
+ assert_equal(8080, cgi.server_port)
+ ## raw cookie
+ ENV['HTTP_COOKIE'] = 'name1=val1'
+ ENV['HTTP_COOKIE2'] = 'name2=val2'
+ assert_equal('name1=val1', cgi.raw_cookie)
+ assert_equal('name2=val2', cgi.raw_cookie2)
+ end
+
+
+ def test_cgi_core_htmltype_header
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ }
+ ENV.update(@environ)
+ ## no htmltype
+ cgi = CGI.new
+ assert_raise(NoMethodError) do cgi.doctype end
+ assert_equal("Content-Type: text/html\r\n\r\n",cgi.header)
+ ## html3
+ cgi = CGI.new('html3')
+ expected = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">'
+ assert_equal(expected, cgi.doctype)
+ assert_equal("Content-Type: text/html\r\n\r\n",cgi.header)
+ ## html4
+ cgi = CGI.new('html4')
+ expected = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
+ assert_equal(expected, cgi.doctype)
+ assert_equal("Content-Type: text/html\r\n\r\n",cgi.header)
+ ## html4 transitional
+ cgi = CGI.new('html4Tr')
+ expected = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
+ assert_equal(expected, cgi.doctype)
+ assert_equal("Content-Type: text/html\r\n\r\n",cgi.header)
+ ## html4 frameset
+ cgi = CGI.new('html4Fr')
+ expected = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
+ assert_equal(expected, cgi.doctype)
+ assert_equal("Content-Type: text/html\r\n\r\n",cgi.header)
+ ## html5
+ cgi = CGI.new('html5')
+ expected = '<!DOCTYPE HTML>'
+ assert_equal(expected, cgi.doctype)
+ assert_match(/^<HEADER><\/HEADER>$/i,cgi.header)
+ end
+
+
+ instance_methods.each do |method|
+ private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
+ end if ENV['TEST']
+
+end
diff --git a/jni/ruby/test/cgi/test_cgi_header.rb b/jni/ruby/test/cgi/test_cgi_header.rb
new file mode 100644
index 0000000..9022301
--- /dev/null
+++ b/jni/ruby/test/cgi/test_cgi_header.rb
@@ -0,0 +1,181 @@
+require 'test/unit'
+require 'cgi'
+require 'time'
+
+
+class CGIHeaderTest < Test::Unit::TestCase
+
+
+ def setup
+ @environ = {
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ }
+ ENV.update(@environ)
+ end
+
+
+ def teardown
+ @environ.each do |key, val| ENV.delete(key) end
+ end
+
+
+ def test_cgi_http_header_simple
+ cgi = CGI.new
+ ## default content type
+ expected = "Content-Type: text/html\r\n\r\n"
+ actual = cgi.http_header
+ assert_equal(expected, actual)
+ ## content type specified as string
+ expected = "Content-Type: text/xhtml; charset=utf8\r\n\r\n"
+ actual = cgi.http_header('text/xhtml; charset=utf8')
+ assert_equal(expected, actual)
+ ## content type specified as hash
+ expected = "Content-Type: image/png\r\n\r\n"
+ actual = cgi.http_header('type'=>'image/png')
+ assert_equal(expected, actual)
+ ## charset specified
+ expected = "Content-Type: text/html; charset=utf8\r\n\r\n"
+ actual = cgi.http_header('charset'=>'utf8')
+ assert_equal(expected, actual)
+ end
+
+
+ def test_cgi_http_header_complex
+ cgi = CGI.new
+ options = {
+ 'type' => 'text/xhtml',
+ 'charset' => 'utf8',
+ 'status' => 'REDIRECT',
+ 'server' => 'webrick',
+ 'connection' => 'close',
+ 'length' => 123,
+ 'language' => 'ja',
+ 'expires' => Time.gm(2000, 1, 23, 12, 34, 56),
+ 'location' => 'http://www.ruby-lang.org/',
+ }
+ expected = "Status: 302 Found\r\n"
+ expected << "Server: webrick\r\n"
+ expected << "Connection: close\r\n"
+ expected << "Content-Type: text/xhtml; charset=utf8\r\n"
+ expected << "Content-Length: 123\r\n"
+ expected << "Content-Language: ja\r\n"
+ expected << "Expires: Sun, 23 Jan 2000 12:34:56 GMT\r\n"
+ expected << "location: http://www.ruby-lang.org/\r\n"
+ expected << "\r\n"
+ actual = cgi.http_header(options)
+ assert_equal(expected, actual)
+ end
+
+
+ def test_cgi_http_header_argerr
+ cgi = CGI.new
+ expected = ArgumentError
+
+ assert_raise(expected) do
+ cgi.http_header(nil)
+ end
+ end
+
+
+ def test_cgi_http_header_cookie
+ cgi = CGI.new
+ cookie1 = CGI::Cookie.new('name1', 'abc', '123')
+ cookie2 = CGI::Cookie.new('name'=>'name2', 'value'=>'value2', 'secure'=>true)
+ ctype = "Content-Type: text/html\r\n"
+ sep = "\r\n"
+ c1 = "Set-Cookie: name1=abc&123; path=\r\n"
+ c2 = "Set-Cookie: name2=value2; path=; secure\r\n"
+ ## CGI::Cookie object
+ actual = cgi.http_header('cookie'=>cookie1)
+ expected = ctype + c1 + sep
+ assert_equal(expected, actual)
+ ## String
+ actual = cgi.http_header('cookie'=>cookie2.to_s)
+ expected = ctype + c2 + sep
+ assert_equal(expected, actual)
+ ## Array
+ actual = cgi.http_header('cookie'=>[cookie1, cookie2])
+ expected = ctype + c1 + c2 + sep
+ assert_equal(expected, actual)
+ ## Hash
+ actual = cgi.http_header('cookie'=>{'name1'=>cookie1, 'name2'=>cookie2})
+ expected = ctype + c1 + c2 + sep
+ assert_equal(expected, actual)
+ end
+
+
+ def test_cgi_http_header_output_cookies
+ cgi = CGI.new
+ ## output cookies
+ cookies = [ CGI::Cookie.new('name1', 'abc', '123'),
+ CGI::Cookie.new('name'=>'name2', 'value'=>'value2', 'secure'=>true),
+ ]
+ cgi.instance_variable_set('@output_cookies', cookies)
+ expected = "Content-Type: text/html; charset=utf8\r\n"
+ expected << "Set-Cookie: name1=abc&123; path=\r\n"
+ expected << "Set-Cookie: name2=value2; path=; secure\r\n"
+ expected << "\r\n"
+ ## header when string
+ actual = cgi.http_header('text/html; charset=utf8')
+ assert_equal(expected, actual)
+ ## _header_for_string
+ actual = cgi.http_header('type'=>'text/html', 'charset'=>'utf8')
+ assert_equal(expected, actual)
+ end
+
+
+ def test_cgi_http_header_nph
+ time_start = Time.now.to_i
+ cgi = CGI.new
+ ## 'nph' is true
+ ENV['SERVER_SOFTWARE'] = 'Apache 2.2.0'
+ actual1 = cgi.http_header('nph'=>true)
+ ## when old IIS, NPH-mode is forced
+ ENV['SERVER_SOFTWARE'] = 'IIS/4.0'
+ actual2 = cgi.http_header
+ actual3 = cgi.http_header('status'=>'REDIRECT', 'location'=>'http://www.example.com/')
+ ## newer IIS doesn't require NPH-mode ## [ruby-dev:30537]
+ ENV['SERVER_SOFTWARE'] = 'IIS/5.0'
+ actual4 = cgi.http_header
+ actual5 = cgi.http_header('status'=>'REDIRECT', 'location'=>'http://www.example.com/')
+ time_end = Time.now.to_i
+ date = /^Date: ([A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d GMT)\r\n/
+ [actual1, actual2, actual3].each do |actual|
+ assert_match(date, actual)
+ assert_includes(time_start..time_end, date =~ actual && Time.parse($1).to_i)
+ actual.sub!(date, "Date: DATE_IS_REMOVED\r\n")
+ end
+ ## assertion
+ expected = "HTTP/1.1 200 OK\r\n"
+ expected << "Date: DATE_IS_REMOVED\r\n"
+ expected << "Server: Apache 2.2.0\r\n"
+ expected << "Connection: close\r\n"
+ expected << "Content-Type: text/html\r\n"
+ expected << "\r\n"
+ assert_equal(expected, actual1)
+ expected.sub!(/^Server: .*?\r\n/, "Server: IIS/4.0\r\n")
+ assert_equal(expected, actual2)
+ expected.sub!(/^HTTP\/1.1 200 OK\r\n/, "HTTP/1.1 302 Found\r\n")
+ expected.sub!(/\r\n\r\n/, "\r\nlocation: http://www.example.com/\r\n\r\n")
+ assert_equal(expected, actual3)
+ expected = "Content-Type: text/html\r\n"
+ expected << "\r\n"
+ assert_equal(expected, actual4)
+ expected = "Status: 302 Found\r\n"
+ expected << "Content-Type: text/html\r\n"
+ expected << "location: http://www.example.com/\r\n"
+ expected << "\r\n"
+ assert_equal(expected, actual5)
+ ensure
+ ENV.delete('SERVER_SOFTWARE')
+ end
+
+
+
+ instance_methods.each do |method|
+ private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
+ end if ENV['TEST']
+
+end
diff --git a/jni/ruby/test/cgi/test_cgi_modruby.rb b/jni/ruby/test/cgi/test_cgi_modruby.rb
new file mode 100644
index 0000000..b0fc442
--- /dev/null
+++ b/jni/ruby/test/cgi/test_cgi_modruby.rb
@@ -0,0 +1,146 @@
+require 'test/unit'
+require 'cgi'
+
+
+class CGIModrubyTest < Test::Unit::TestCase
+
+
+ def setup
+ @environ = {
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ #'QUERY_STRING' => 'a=foo&b=bar',
+ }
+ ENV.update(@environ)
+ CGI.class_eval { const_set(:MOD_RUBY, true) }
+ Apache._reset()
+ #@cgi = CGI.new
+ #@req = Apache.request
+ end
+
+
+ def teardown
+ @environ.each do |key, val| ENV.delete(key) end
+ CGI.class_eval { remove_const(:MOD_RUBY) }
+ end
+
+
+ def test_cgi_modruby_simple
+ req = Apache.request
+ cgi = CGI.new
+ assert(req._setup_cgi_env_invoked?)
+ assert(! req._send_http_header_invoked?)
+ actual = cgi.http_header
+ assert_equal('', actual)
+ assert_equal('text/html', req.content_type)
+ assert(req._send_http_header_invoked?)
+ end
+
+
+ def test_cgi_modruby_complex
+ req = Apache.request
+ cgi = CGI.new
+ options = {
+ 'status' => 'FORBIDDEN',
+ 'location' => 'http://www.example.com/',
+ 'type' => 'image/gif',
+ 'content-encoding' => 'deflate',
+ 'cookie' => [ CGI::Cookie.new('name1', 'abc', '123'),
+ CGI::Cookie.new('name'=>'name2', 'value'=>'value2', 'secure'=>true),
+ ],
+ }
+ assert(req._setup_cgi_env_invoked?)
+ assert(! req._send_http_header_invoked?)
+ actual = cgi.http_header(options)
+ assert_equal('', actual)
+ assert_equal('image/gif', req.content_type)
+ assert_equal('403 Forbidden', req.status_line)
+ assert_equal(403, req.status)
+ assert_equal('deflate', req.content_encoding)
+ assert_equal('http://www.example.com/', req.headers_out['location'])
+ assert_equal(["name1=abc&123; path=", "name2=value2; path=; secure"],
+ req.headers_out['Set-Cookie'])
+ assert(req._send_http_header_invoked?)
+ end
+
+
+ def test_cgi_modruby_location
+ req = Apache.request
+ cgi = CGI.new
+ options = {
+ 'status' => '200 OK',
+ 'location' => 'http://www.example.com/',
+ }
+ cgi.http_header(options)
+ assert_equal('200 OK', req.status_line) # should be '302 Found' ?
+ assert_equal(302, req.status)
+ assert_equal('http://www.example.com/', req.headers_out['location'])
+ end
+
+
+ def test_cgi_modruby_requestparams
+ req = Apache.request
+ req.args = 'a=foo&b=bar'
+ cgi = CGI.new
+ assert_equal('foo', cgi['a'])
+ assert_equal('bar', cgi['b'])
+ end
+
+
+ instance_methods.each do |method|
+ private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
+ end if ENV['TEST']
+
+end
+
+
+
+## dummy class for mod_ruby
+class Apache #:nodoc:
+
+ def self._reset
+ @request = Request.new
+ end
+
+ def self.request
+ return @request
+ end
+
+ class Request
+
+ def initialize
+ hash = {}
+ def hash.add(name, value)
+ (self[name] ||= []) << value
+ end
+ @http_header = nil
+ @headers_out = hash
+ @status_line = nil
+ @status = nil
+ @content_type = nil
+ @content_encoding = nil
+ end
+ attr_accessor :headers_out, :status_line, :status, :content_type, :content_encoding
+
+ attr_accessor :args
+ #def args
+ # return ENV['QUERY_STRING']
+ #end
+
+ def send_http_header
+ @http_header = '*invoked*'
+ end
+ def _send_http_header_invoked?
+ @http_header ? true : false
+ end
+
+ def setup_cgi_env
+ @cgi_env = '*invoked*'
+ end
+ def _setup_cgi_env_invoked?
+ @cgi_env ? true : false
+ end
+
+ end
+
+end
diff --git a/jni/ruby/test/cgi/test_cgi_multipart.rb b/jni/ruby/test/cgi/test_cgi_multipart.rb
new file mode 100644
index 0000000..1325798
--- /dev/null
+++ b/jni/ruby/test/cgi/test_cgi_multipart.rb
@@ -0,0 +1,380 @@
+require 'test/unit'
+require 'cgi'
+require 'tempfile'
+require 'stringio'
+
+
+##
+## usage:
+## boundary = 'foobar1234' # or nil
+## multipart = MultiPart.new(boundary)
+## multipart.append('name1', 'value1')
+## multipart.append('file1', File.read('file1.html'), 'file1.html')
+## str = multipart.close()
+## str.each_line {|line| p line }
+## ## output:
+## # "--foobar1234\r\n"
+## # "Content-Disposition: form-data: name=\"name1\"\r\n"
+## # "\r\n"
+## # "value1\r\n"
+## # "--foobar1234\r\n"
+## # "Content-Disposition: form-data: name=\"file1\"; filename=\"file1.html\"\r\n"
+## # "Content-Type: text/html\r\n"
+## # "\r\n"
+## # "<html>\n"
+## # "<body><p>Hello</p></body>\n"
+## # "</html>\n"
+## # "\r\n"
+## # "--foobar1234--\r\n"
+##
+class MultiPart
+
+ def initialize(boundary=nil)
+ @boundary = boundary || create_boundary()
+ @buf = ''
+ @buf.force_encoding(::Encoding::ASCII_8BIT) if defined?(::Encoding)
+ end
+ attr_reader :boundary
+
+ def append(name, value, filename=nil, content_type=nil)
+ content_type = detect_content_type(filename) if filename && content_type.nil?
+ s = filename ? "; filename=\"#{filename}\"" : ''
+ buf = @buf
+ buf << "--#{boundary}\r\n"
+ buf << "Content-Disposition: form-data: name=\"#{name}\"#{s}\r\n"
+ buf << "Content-Type: #{content_type}\r\n" if content_type
+ buf << "\r\n"
+ value = value.dup.force_encoding(::Encoding::ASCII_8BIT) if defined?(::Encoding)
+ buf << value
+ buf << "\r\n"
+ return self
+ end
+
+ def close
+ buf = @buf
+ @buf = ''
+ return buf << "--#{boundary}--\r\n"
+ end
+
+ def create_boundary() #:nodoc:
+ return "--boundary#{rand().to_s[2..-1]}"
+ end
+
+ def detect_content_type(filename) #:nodoc:
+ filename =~ /\.(\w+)\z/
+ return MIME_TYPES[$1] || 'application/octet-stream'
+ end
+
+ MIME_TYPES = {
+ 'gif' => 'image/gif',
+ 'jpg' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'bmp' => 'image/bmp',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'xml' => 'text/xml',
+ 'txt' => 'text/plain',
+ 'text' => 'text/plain',
+ 'css' => 'text/css',
+ 'mpg' => 'video/mpeg',
+ 'mpeg' => 'video/mpeg',
+ 'mov' => 'video/quicktime',
+ 'avi' => 'video/x-msvideo',
+ 'mp3' => 'audio/mpeg',
+ 'mid' => 'audio/midi',
+ 'wav' => 'audio/x-wav',
+ 'zip' => 'application/zip',
+ #'tar.gz' => 'application/gtar',
+ 'gz' => 'application/gzip',
+ 'bz2' => 'application/bzip2',
+ 'rtf' => 'application/rtf',
+ 'pdf' => 'application/pdf',
+ 'ps' => 'application/postscript',
+ 'js' => 'application/x-javascript',
+ 'xls' => 'application/vnd.ms-excel',
+ 'doc' => 'application/msword',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ }
+
+end
+
+
+
+class CGIMultipartTest < Test::Unit::TestCase
+
+ def setup
+ ENV['REQUEST_METHOD'] = 'POST'
+ @tempfiles = []
+ end
+
+ def teardown
+ %w[ REQUEST_METHOD CONTENT_TYPE CONTENT_LENGTH REQUEST_METHOD ].each do |name|
+ ENV.delete(name)
+ end
+ $stdin.close() if $stdin.is_a?(Tempfile)
+ $stdin = STDIN
+ @tempfiles.each {|t|
+ t.close!
+ }
+ end
+
+ def _prepare(data)
+ ## create multipart input
+ multipart = MultiPart.new(defined?(@boundary) ? @boundary : nil)
+ data.each do |hash|
+ multipart.append(hash[:name], hash[:value], hash[:filename])
+ end
+ input = multipart.close()
+ input = yield(input) if block_given?
+ #$stderr.puts "*** debug: input=\n#{input.collect{|line| line.inspect}.join("\n")}"
+ @boundary ||= multipart.boundary
+ ## set environment
+ ENV['CONTENT_TYPE'] = "multipart/form-data; boundary=#{@boundary}"
+ ENV['CONTENT_LENGTH'] = input.length.to_s
+ ENV['REQUEST_METHOD'] = 'POST'
+ ## set $stdin
+ tmpfile = Tempfile.new('test_cgi_multipart')
+ @tempfiles << tmpfile
+ tmpfile.binmode
+ tmpfile << input
+ tmpfile.rewind()
+ $stdin = tmpfile
+ end
+
+ def _test_multipart(cgi_options={})
+ caller(0).find {|s| s =~ /in `test_(.*?)'/ }
+ #testname = $1
+ #$stderr.puts "*** debug: testname=#{testname.inspect}"
+ _prepare(@data)
+ options = {:accept_charset=>"UTF-8"}
+ options.merge! cgi_options
+ cgi = CGI.new(options)
+ expected_names = @data.collect{|hash| hash[:name] }.sort
+ assert_equal(expected_names, cgi.params.keys.sort)
+ threshold = 1024*10
+ @data.each do |hash|
+ name = hash[:name]
+ expected = hash[:value]
+ if hash[:filename] #if file
+ expected_class = @expected_class || (hash[:value].length < threshold ? StringIO : Tempfile)
+ assert(cgi.files.keys.member?(hash[:name]))
+ else
+ expected_class = String
+ assert_equal(expected, cgi[name])
+ assert_equal(false,cgi.files.keys.member?(hash[:name]))
+ end
+ assert_kind_of(expected_class, cgi[name])
+ assert_equal(expected, cgi[name].read())
+ assert_equal(hash[:filename] || '', cgi[name].original_filename) #if hash[:filename]
+ assert_equal(hash[:content_type] || '', cgi[name].content_type) #if hash[:content_type]
+ end
+ ensure
+ if cgi
+ cgi.params.each {|name, vals|
+ vals.each {|val|
+ if val.kind_of?(Tempfile) && val.path
+ val.close!
+ end
+ }
+ }
+ end
+ end
+
+
+ def _read(basename)
+ filename = File.join(File.dirname(__FILE__), 'testdata', basename)
+ s = File.open(filename, 'rb') {|f| f.read() }
+
+ return s
+ end
+
+
+ def test_cgi_multipart_stringio
+ @boundary = '----WebKitFormBoundaryAAfvAII+YL9102cX'
+ @data = [
+ {:name=>'hidden1', :value=>'foobar'},
+ {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"},
+ {:name=>'file1', :value=>_read('file1.html'),
+ :filename=>'file1.html', :content_type=>'text/html'},
+ {:name=>'image1', :value=>_read('small.png'),
+ :filename=>'small.png', :content_type=>'image/png'}, # small image
+ ]
+ @data[1][:value].force_encoding(::Encoding::UTF_8) if defined?(::Encoding)
+ @expected_class = StringIO
+ _test_multipart()
+ end
+
+
+ def test_cgi_multipart_tempfile
+ @boundary = '----WebKitFormBoundaryAAfvAII+YL9102cX'
+ @data = [
+ {:name=>'hidden1', :value=>'foobar'},
+ {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"},
+ {:name=>'file1', :value=>_read('file1.html'),
+ :filename=>'file1.html', :content_type=>'text/html'},
+ {:name=>'image1', :value=>_read('large.png'),
+ :filename=>'large.png', :content_type=>'image/png'}, # large image
+ ]
+ @data[1][:value].force_encoding(::Encoding::UTF_8) if defined?(::Encoding)
+ @expected_class = Tempfile
+ _test_multipart()
+ end
+
+
+ def _set_const(klass, name, value)
+ old = nil
+ klass.class_eval do
+ old = const_get(name)
+ remove_const(name)
+ const_set(name, value)
+ end
+ return old
+ end
+
+
+ def test_cgi_multipart_maxmultipartlength
+ @data = [
+ {:name=>'image1', :value=>_read('large.png'),
+ :filename=>'large.png', :content_type=>'image/png'}, # large image
+ ]
+ begin
+ ex = assert_raise(StandardError) do
+ _test_multipart(:max_multipart_length=>2 * 1024) # set via simple scalar
+ end
+ assert_equal("too large multipart data.", ex.message)
+ ensure
+ end
+ end
+
+
+ def test_cgi_multipart_maxmultipartlength_lambda
+ @data = [
+ {:name=>'image1', :value=>_read('large.png'),
+ :filename=>'large.png', :content_type=>'image/png'}, # large image
+ ]
+ begin
+ ex = assert_raise(StandardError) do
+ _test_multipart(:max_multipart_length=>lambda{2*1024}) # set via lambda
+ end
+ assert_equal("too large multipart data.", ex.message)
+ ensure
+ end
+ end
+
+
+ def test_cgi_multipart_maxmultipartcount
+ @data = [
+ {:name=>'file1', :value=>_read('file1.html'),
+ :filename=>'file1.html', :content_type=>'text/html'},
+ ]
+ item = @data.first
+ 500.times { @data << item }
+ #original = _set_const(CGI, :MAX_MULTIPART_COUNT, 128)
+ begin
+ ex = assert_raise(StandardError) do
+ _test_multipart()
+ end
+ assert_equal("too many parameters.", ex.message)
+ ensure
+ #_set_const(CGI, :MAX_MULTIPART_COUNT, original)
+ end
+ end if CGI.const_defined?(:MAX_MULTIPART_COUNT)
+
+
+ def test_cgi_multipart_badbody ## [ruby-dev:28470]
+ @data = [
+ {:name=>'file1', :value=>_read('file1.html'),
+ :filename=>'file1.html', :content_type=>'text/html'},
+ ]
+ _prepare(@data) do |input|
+ input2 = input.sub(/--(\r\n)?\z/, "\r\n")
+ assert input2 != input
+ #p input2
+ input2
+ end
+ ex = assert_raise(EOFError) do
+ CGI.new(:accept_charset=>"UTF-8")
+ end
+ assert_equal("bad content body", ex.message)
+ #
+ _prepare(@data) do |input|
+ input2 = input.sub(/--(\r\n)?\z/, "")
+ assert input2 != input
+ #p input2
+ input2
+ end
+ ex = assert_raise(EOFError) do
+ CGI.new(:accept_charset=>"UTF-8")
+ end
+ assert_equal("bad content body", ex.message)
+ end
+
+
+ def test_cgi_multipart_quoteboundary ## [JVN#84798830]
+ @boundary = '(.|\n)*'
+ @data = [
+ {:name=>'hidden1', :value=>'foobar'},
+ {:name=>'text1', :value=>"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"},
+ {:name=>'file1', :value=>_read('file1.html'),
+ :filename=>'file1.html', :content_type=>'text/html'},
+ {:name=>'image1', :value=>_read('small.png'),
+ :filename=>'small.png', :content_type=>'image/png'}, # small image
+ ]
+ @data[1][:value].force_encoding("UTF-8")
+ _prepare(@data)
+ cgi = CGI.new(:accept_charset=>"UTF-8")
+ assert_equal('file1.html', cgi['file1'].original_filename)
+ end
+
+ def test_cgi_multipart_boundary_10240 # [Bug #3866]
+ @boundary = 'AaB03x'
+ @data = [
+ {:name=>'file', :value=>"b"*10134,
+ :filename=>'file.txt', :content_type=>'text/plain'},
+ {:name=>'foo', :value=>"bar"},
+ ]
+ _prepare(@data)
+ cgi = CGI.new(:accept_charset=>"UTF-8")
+ assert_equal(cgi['foo'], 'bar')
+ assert_equal(cgi['file'].read, 'b'*10134)
+ cgi['file'].close! if cgi['file'].kind_of? Tempfile
+ end
+
+ def test_cgi_multipart_without_tempfile
+ assert_in_out_err([], <<-'EOM')
+ require 'cgi'
+ require 'stringio'
+ ENV['REQUEST_METHOD'] = 'POST'
+ ENV['CONTENT_TYPE'] = 'multipart/form-data; boundary=foobar1234'
+ body = <<-BODY
+--foobar1234
+Content-Disposition: form-data: name=\"name1\"
+
+value1
+--foobar1234
+Content-Disposition: form-data: name=\"file1\"; filename=\"file1.html\"
+Content-Type: text/html
+
+<html>
+<body><p>Hello</p></body>
+</html>
+
+--foobar1234--
+BODY
+ body.gsub!(/\n/, "\r\n")
+ ENV['CONTENT_LENGTH'] = body.size.to_s
+ $stdin = StringIO.new(body)
+ CGI.new
+ EOM
+ end
+
+ ###
+
+ self.instance_methods.each do |method|
+ private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
+ end if ENV['TEST']
+
+end
diff --git a/jni/ruby/test/cgi/test_cgi_session.rb b/jni/ruby/test/cgi/test_cgi_session.rb
new file mode 100644
index 0000000..8bd5177
--- /dev/null
+++ b/jni/ruby/test/cgi/test_cgi_session.rb
@@ -0,0 +1,172 @@
+require 'test/unit'
+require 'cgi'
+require 'cgi/session'
+require 'cgi/session/pstore'
+require 'stringio'
+require 'tmpdir'
+
+class CGISessionTest < Test::Unit::TestCase
+ def setup
+ @session_dir = Dir.mktmpdir(%w'session dir')
+ end
+
+ def teardown
+ @environ.each do |key, val| ENV.delete(key) end
+ $stdout = STDOUT
+ FileUtils.rm_rf(@session_dir)
+ end
+
+ def test_cgi_session_filestore
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ # 'QUERY_STRING' => 'id=123&id=456&id=&str=%40h+%3D%7E+%2F%5E%24%2F',
+ # 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ value1="value1"
+ value2="\x8F\xBC\x8D]"
+ value2.force_encoding("SJIS") if defined?(::Encoding)
+ ENV.update(@environ)
+ cgi = CGI.new
+ session = CGI::Session.new(cgi,"tmpdir"=>@session_dir)
+ session["key1"]=value1
+ session["key2"]=value2
+ assert_equal(value1,session["key1"])
+ assert_equal(value2,session["key2"])
+ session.close
+ $stdout = StringIO.new
+ cgi.out{""}
+
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ # 'HTTP_COOKIE' => "_session_id=#{session_id}",
+ 'QUERY_STRING' => "_session_id=#{session.session_id}",
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ session = CGI::Session.new(cgi,"tmpdir"=>@session_dir)
+ $stdout = StringIO.new
+ assert_equal(value1,session["key1"])
+ assert_equal(value2,session["key2"])
+ session.close
+
+ end
+ def test_cgi_session_pstore
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ # 'QUERY_STRING' => 'id=123&id=456&id=&str=%40h+%3D%7E+%2F%5E%24%2F',
+ # 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ value1="value1"
+ value2="\x8F\xBC\x8D]"
+ value2.force_encoding("SJIS") if defined?(::Encoding)
+ ENV.update(@environ)
+ cgi = CGI.new
+ session = CGI::Session.new(cgi,"tmpdir"=>@session_dir,"database_manager"=>CGI::Session::PStore)
+ session["key1"]=value1
+ session["key2"]=value2
+ assert_equal(value1,session["key1"])
+ assert_equal(value2,session["key2"])
+ session.close
+ $stdout = StringIO.new
+ cgi.out{""}
+
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ # 'HTTP_COOKIE' => "_session_id=#{session_id}",
+ 'QUERY_STRING' => "_session_id=#{session.session_id}",
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ session = CGI::Session.new(cgi,"tmpdir"=>@session_dir,"database_manager"=>CGI::Session::PStore)
+ $stdout = StringIO.new
+ assert_equal(value1,session["key1"])
+ assert_equal(value2,session["key2"])
+ session.close
+ end
+ def test_cgi_session_specify_session_id
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ # 'QUERY_STRING' => 'id=123&id=456&id=&str=%40h+%3D%7E+%2F%5E%24%2F',
+ # 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ value1="value1"
+ value2="\x8F\xBC\x8D]"
+ value2.force_encoding("SJIS") if defined?(::Encoding)
+ ENV.update(@environ)
+ cgi = CGI.new
+ session = CGI::Session.new(cgi,"tmpdir"=>@session_dir,"session_id"=>"foo")
+ session["key1"]=value1
+ session["key2"]=value2
+ assert_equal(value1,session["key1"])
+ assert_equal(value2,session["key2"])
+ assert_equal("foo",session.session_id)
+ #session_id=session.session_id
+ session.close
+ $stdout = StringIO.new
+ cgi.out{""}
+
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ # 'HTTP_COOKIE' => "_session_id=#{session_id}",
+ 'QUERY_STRING' => "_session_id=#{session.session_id}",
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ session = CGI::Session.new(cgi,"tmpdir"=>@session_dir)
+ $stdout = StringIO.new
+ assert_equal(value1,session["key1"])
+ assert_equal(value2,session["key2"])
+ assert_equal("foo",session.session_id)
+ session.close
+ end
+ def test_cgi_session_specify_session_key
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ # 'QUERY_STRING' => 'id=123&id=456&id=&str=%40h+%3D%7E+%2F%5E%24%2F',
+ # 'HTTP_COOKIE' => '_session_id=12345; name1=val1&val2;',
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ value1="value1"
+ value2="\x8F\xBC\x8D]"
+ value2.force_encoding("SJIS") if defined?(::Encoding)
+ ENV.update(@environ)
+ cgi = CGI.new
+ session = CGI::Session.new(cgi,"tmpdir"=>@session_dir,"session_key"=>"bar")
+ session["key1"]=value1
+ session["key2"]=value2
+ assert_equal(value1,session["key1"])
+ assert_equal(value2,session["key2"])
+ session_id=session.session_id
+ session.close
+ $stdout = StringIO.new
+ cgi.out{""}
+
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ 'HTTP_COOKIE' => "bar=#{session_id}",
+ # 'QUERY_STRING' => "bar=#{session.session_id}",
+ 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ }
+ ENV.update(@environ)
+ cgi = CGI.new
+ session = CGI::Session.new(cgi,"tmpdir"=>@session_dir,"session_key"=>"bar")
+ $stdout = StringIO.new
+ assert_equal(value1,session["key1"])
+ assert_equal(value2,session["key2"])
+ session.close
+ end
+end
diff --git a/jni/ruby/test/cgi/test_cgi_tag_helper.rb b/jni/ruby/test/cgi/test_cgi_tag_helper.rb
new file mode 100644
index 0000000..eb3c20a
--- /dev/null
+++ b/jni/ruby/test/cgi/test_cgi_tag_helper.rb
@@ -0,0 +1,353 @@
+require 'test/unit'
+require 'cgi'
+require 'stringio'
+
+
+class CGITagHelperTest < Test::Unit::TestCase
+
+
+ def setup
+ #@environ = {
+ # 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ # 'REQUEST_METHOD' => 'GET',
+ # 'SERVER_SOFTWARE' => 'Apache 2.2.0',
+ #}
+ #ENV.update(@environ)
+ end
+
+
+ def teardown
+ @environ.each do |key, val| ENV.delete(key) end
+ $stdout = STDOUT
+ end
+
+
+ def test_cgi_tag_helper_html3
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ }
+ ENV.update(@environ)
+ ## html3
+ cgi = CGI.new('html3')
+ assert_equal('<A HREF=""></A>',cgi.a)
+ assert_equal('<A HREF="bar"></A>',cgi.a('bar'))
+ assert_equal('<A HREF="">foo</A>',cgi.a{'foo'})
+ assert_equal('<A HREF="bar">foo</A>',cgi.a('bar'){'foo'})
+ assert_equal('<TT></TT>',cgi.tt)
+ assert_equal('<TT></TT>',cgi.tt('bar'))
+ assert_equal('<TT>foo</TT>',cgi.tt{'foo'})
+ assert_equal('<TT>foo</TT>',cgi.tt('bar'){'foo'})
+ assert_equal('<I></I>',cgi.i)
+ assert_equal('<I></I>',cgi.i('bar'))
+ assert_equal('<I>foo</I>',cgi.i{'foo'})
+ assert_equal('<I>foo</I>',cgi.i('bar'){'foo'})
+ assert_equal('<B></B>',cgi.b)
+ assert_equal('<B></B>',cgi.b('bar'))
+ assert_equal('<B>foo</B>',cgi.b{'foo'})
+ assert_equal('<B>foo</B>',cgi.b('bar'){'foo'})
+ assert_equal('<U></U>',cgi.u)
+ assert_equal('<U></U>',cgi.u('bar'))
+ assert_equal('<U>foo</U>',cgi.u{'foo'})
+ assert_equal('<U>foo</U>',cgi.u('bar'){'foo'})
+ assert_equal('<STRIKE></STRIKE>',cgi.strike)
+ assert_equal('<STRIKE></STRIKE>',cgi.strike('bar'))
+ assert_equal('<STRIKE>foo</STRIKE>',cgi.strike{'foo'})
+ assert_equal('<STRIKE>foo</STRIKE>',cgi.strike('bar'){'foo'})
+ assert_equal('<BIG></BIG>',cgi.big)
+ assert_equal('<BIG></BIG>',cgi.big('bar'))
+ assert_equal('<BIG>foo</BIG>',cgi.big{'foo'})
+ assert_equal('<BIG>foo</BIG>',cgi.big('bar'){'foo'})
+ assert_equal('<SMALL></SMALL>',cgi.small)
+ assert_equal('<SMALL></SMALL>',cgi.small('bar'))
+ assert_equal('<SMALL>foo</SMALL>',cgi.small{'foo'})
+ assert_equal('<SMALL>foo</SMALL>',cgi.small('bar'){'foo'})
+ assert_equal('<SUB></SUB>',cgi.sub)
+ assert_equal('<SUB></SUB>',cgi.sub('bar'))
+ assert_equal('<SUB>foo</SUB>',cgi.sub{'foo'})
+ assert_equal('<SUB>foo</SUB>',cgi.sub('bar'){'foo'})
+ assert_equal('<SUP></SUP>',cgi.sup)
+ assert_equal('<SUP></SUP>',cgi.sup('bar'))
+ assert_equal('<SUP>foo</SUP>',cgi.sup{'foo'})
+ assert_equal('<SUP>foo</SUP>',cgi.sup('bar'){'foo'})
+ assert_equal('<EM></EM>',cgi.em)
+ assert_equal('<EM></EM>',cgi.em('bar'))
+ assert_equal('<EM>foo</EM>',cgi.em{'foo'})
+ assert_equal('<EM>foo</EM>',cgi.em('bar'){'foo'})
+ assert_equal('<STRONG></STRONG>',cgi.strong)
+ assert_equal('<STRONG></STRONG>',cgi.strong('bar'))
+ assert_equal('<STRONG>foo</STRONG>',cgi.strong{'foo'})
+ assert_equal('<STRONG>foo</STRONG>',cgi.strong('bar'){'foo'})
+ assert_equal('<DFN></DFN>',cgi.dfn)
+ assert_equal('<DFN></DFN>',cgi.dfn('bar'))
+ assert_equal('<DFN>foo</DFN>',cgi.dfn{'foo'})
+ assert_equal('<DFN>foo</DFN>',cgi.dfn('bar'){'foo'})
+ assert_equal('<CODE></CODE>',cgi.code)
+ assert_equal('<CODE></CODE>',cgi.code('bar'))
+ assert_equal('<CODE>foo</CODE>',cgi.code{'foo'})
+ assert_equal('<CODE>foo</CODE>',cgi.code('bar'){'foo'})
+ assert_equal('<SAMP></SAMP>',cgi.samp)
+ assert_equal('<SAMP></SAMP>',cgi.samp('bar'))
+ assert_equal('<SAMP>foo</SAMP>',cgi.samp{'foo'})
+ assert_equal('<SAMP>foo</SAMP>',cgi.samp('bar'){'foo'})
+ assert_equal('<KBD></KBD>',cgi.kbd)
+ assert_equal('<KBD></KBD>',cgi.kbd('bar'))
+ assert_equal('<KBD>foo</KBD>',cgi.kbd{'foo'})
+ assert_equal('<KBD>foo</KBD>',cgi.kbd('bar'){'foo'})
+ assert_equal('<VAR></VAR>',cgi.var)
+ assert_equal('<VAR></VAR>',cgi.var('bar'))
+ assert_equal('<VAR>foo</VAR>',cgi.var{'foo'})
+ assert_equal('<VAR>foo</VAR>',cgi.var('bar'){'foo'})
+ assert_equal('<CITE></CITE>',cgi.cite)
+ assert_equal('<CITE></CITE>',cgi.cite('bar'))
+ assert_equal('<CITE>foo</CITE>',cgi.cite{'foo'})
+ assert_equal('<CITE>foo</CITE>',cgi.cite('bar'){'foo'})
+ assert_equal('<FONT></FONT>',cgi.font)
+ assert_equal('<FONT></FONT>',cgi.font('bar'))
+ assert_equal('<FONT>foo</FONT>',cgi.font{'foo'})
+ assert_equal('<FONT>foo</FONT>',cgi.font('bar'){'foo'})
+ assert_equal('<ADDRESS></ADDRESS>',cgi.address)
+ assert_equal('<ADDRESS></ADDRESS>',cgi.address('bar'))
+ assert_equal('<ADDRESS>foo</ADDRESS>',cgi.address{'foo'})
+ assert_equal('<ADDRESS>foo</ADDRESS>',cgi.address('bar'){'foo'})
+ assert_equal('<DIV></DIV>',cgi.div)
+ assert_equal('<DIV></DIV>',cgi.div('bar'))
+ assert_equal('<DIV>foo</DIV>',cgi.div{'foo'})
+ assert_equal('<DIV>foo</DIV>',cgi.div('bar'){'foo'})
+ assert_equal('<CENTER></CENTER>',cgi.center)
+ assert_equal('<CENTER></CENTER>',cgi.center('bar'))
+ assert_equal('<CENTER>foo</CENTER>',cgi.center{'foo'})
+ assert_equal('<CENTER>foo</CENTER>',cgi.center('bar'){'foo'})
+ assert_equal('<MAP></MAP>',cgi.map)
+ assert_equal('<MAP></MAP>',cgi.map('bar'))
+ assert_equal('<MAP>foo</MAP>',cgi.map{'foo'})
+ assert_equal('<MAP>foo</MAP>',cgi.map('bar'){'foo'})
+ assert_equal('<APPLET></APPLET>',cgi.applet)
+ assert_equal('<APPLET></APPLET>',cgi.applet('bar'))
+ assert_equal('<APPLET>foo</APPLET>',cgi.applet{'foo'})
+ assert_equal('<APPLET>foo</APPLET>',cgi.applet('bar'){'foo'})
+ assert_equal('<PRE></PRE>',cgi.pre)
+ assert_equal('<PRE></PRE>',cgi.pre('bar'))
+ assert_equal('<PRE>foo</PRE>',cgi.pre{'foo'})
+ assert_equal('<PRE>foo</PRE>',cgi.pre('bar'){'foo'})
+ assert_equal('<XMP></XMP>',cgi.xmp)
+ assert_equal('<XMP></XMP>',cgi.xmp('bar'))
+ assert_equal('<XMP>foo</XMP>',cgi.xmp{'foo'})
+ assert_equal('<XMP>foo</XMP>',cgi.xmp('bar'){'foo'})
+ assert_equal('<LISTING></LISTING>',cgi.listing)
+ assert_equal('<LISTING></LISTING>',cgi.listing('bar'))
+ assert_equal('<LISTING>foo</LISTING>',cgi.listing{'foo'})
+ assert_equal('<LISTING>foo</LISTING>',cgi.listing('bar'){'foo'})
+ assert_equal('<DL></DL>',cgi.dl)
+ assert_equal('<DL></DL>',cgi.dl('bar'))
+ assert_equal('<DL>foo</DL>',cgi.dl{'foo'})
+ assert_equal('<DL>foo</DL>',cgi.dl('bar'){'foo'})
+ assert_equal('<OL></OL>',cgi.ol)
+ assert_equal('<OL></OL>',cgi.ol('bar'))
+ assert_equal('<OL>foo</OL>',cgi.ol{'foo'})
+ assert_equal('<OL>foo</OL>',cgi.ol('bar'){'foo'})
+ assert_equal('<UL></UL>',cgi.ul)
+ assert_equal('<UL></UL>',cgi.ul('bar'))
+ assert_equal('<UL>foo</UL>',cgi.ul{'foo'})
+ assert_equal('<UL>foo</UL>',cgi.ul('bar'){'foo'})
+ assert_equal('<DIR></DIR>',cgi.dir)
+ assert_equal('<DIR></DIR>',cgi.dir('bar'))
+ assert_equal('<DIR>foo</DIR>',cgi.dir{'foo'})
+ assert_equal('<DIR>foo</DIR>',cgi.dir('bar'){'foo'})
+ assert_equal('<MENU></MENU>',cgi.menu)
+ assert_equal('<MENU></MENU>',cgi.menu('bar'))
+ assert_equal('<MENU>foo</MENU>',cgi.menu{'foo'})
+ assert_equal('<MENU>foo</MENU>',cgi.menu('bar'){'foo'})
+ assert_equal('<SELECT></SELECT>',cgi.select)
+ assert_equal('<SELECT></SELECT>',cgi.select('bar'))
+ assert_equal('<SELECT>foo</SELECT>',cgi.select{'foo'})
+ assert_equal('<SELECT>foo</SELECT>',cgi.select('bar'){'foo'})
+ assert_equal('<TABLE></TABLE>',cgi.table)
+ assert_equal('<TABLE></TABLE>',cgi.table('bar'))
+ assert_equal('<TABLE>foo</TABLE>',cgi.table{'foo'})
+ assert_equal('<TABLE>foo</TABLE>',cgi.table('bar'){'foo'})
+ assert_equal('<TITLE></TITLE>',cgi.title)
+ assert_equal('<TITLE></TITLE>',cgi.title('bar'))
+ assert_equal('<TITLE>foo</TITLE>',cgi.title{'foo'})
+ assert_equal('<TITLE>foo</TITLE>',cgi.title('bar'){'foo'})
+ assert_equal('<STYLE></STYLE>',cgi.style)
+ assert_equal('<STYLE></STYLE>',cgi.style('bar'))
+ assert_equal('<STYLE>foo</STYLE>',cgi.style{'foo'})
+ assert_equal('<STYLE>foo</STYLE>',cgi.style('bar'){'foo'})
+ assert_equal('<SCRIPT></SCRIPT>',cgi.script)
+ assert_equal('<SCRIPT></SCRIPT>',cgi.script('bar'))
+ assert_equal('<SCRIPT>foo</SCRIPT>',cgi.script{'foo'})
+ assert_equal('<SCRIPT>foo</SCRIPT>',cgi.script('bar'){'foo'})
+ assert_equal('<H1></H1>',cgi.h1)
+ assert_equal('<H1></H1>',cgi.h1('bar'))
+ assert_equal('<H1>foo</H1>',cgi.h1{'foo'})
+ assert_equal('<H1>foo</H1>',cgi.h1('bar'){'foo'})
+ assert_equal('<H2></H2>',cgi.h2)
+ assert_equal('<H2></H2>',cgi.h2('bar'))
+ assert_equal('<H2>foo</H2>',cgi.h2{'foo'})
+ assert_equal('<H2>foo</H2>',cgi.h2('bar'){'foo'})
+ assert_equal('<H3></H3>',cgi.h3)
+ assert_equal('<H3></H3>',cgi.h3('bar'))
+ assert_equal('<H3>foo</H3>',cgi.h3{'foo'})
+ assert_equal('<H3>foo</H3>',cgi.h3('bar'){'foo'})
+ assert_equal('<H4></H4>',cgi.h4)
+ assert_equal('<H4></H4>',cgi.h4('bar'))
+ assert_equal('<H4>foo</H4>',cgi.h4{'foo'})
+ assert_equal('<H4>foo</H4>',cgi.h4('bar'){'foo'})
+ assert_equal('<H5></H5>',cgi.h5)
+ assert_equal('<H5></H5>',cgi.h5('bar'))
+ assert_equal('<H5>foo</H5>',cgi.h5{'foo'})
+ assert_equal('<H5>foo</H5>',cgi.h5('bar'){'foo'})
+ assert_equal('<H6></H6>',cgi.h6)
+ assert_equal('<H6></H6>',cgi.h6('bar'))
+ assert_equal('<H6>foo</H6>',cgi.h6{'foo'})
+ assert_equal('<H6>foo</H6>',cgi.h6('bar'){'foo'})
+ assert_match(/^<TEXTAREA .*><\/TEXTAREA>$/,cgi.textarea)
+ assert_match(/COLS="70"/,cgi.textarea)
+ assert_match(/ROWS="10"/,cgi.textarea)
+ assert_match(/NAME=""/,cgi.textarea)
+ assert_match(/^<TEXTAREA .*><\/TEXTAREA>$/,cgi.textarea("bar"))
+ assert_match(/COLS="70"/,cgi.textarea("bar"))
+ assert_match(/ROWS="10"/,cgi.textarea("bar"))
+ assert_match(/NAME="bar"/,cgi.textarea("bar"))
+ assert_match(/^<TEXTAREA .*>foo<\/TEXTAREA>$/,cgi.textarea{"foo"})
+ assert_match(/COLS="70"/,cgi.textarea{"foo"})
+ assert_match(/ROWS="10"/,cgi.textarea{"foo"})
+ assert_match(/NAME=""/,cgi.textarea{"foo"})
+ assert_match(/^<TEXTAREA .*>foo<\/TEXTAREA>$/,cgi.textarea("bar"){"foo"})
+ assert_match(/COLS="70"/,cgi.textarea("bar"){"foo"})
+ assert_match(/ROWS="10"/,cgi.textarea("bar"){"foo"})
+ assert_match(/NAME="bar"/,cgi.textarea("bar"){"foo"})
+ assert_match(/^<FORM .*><\/FORM>$/,cgi.form)
+ assert_match(/METHOD="post"/,cgi.form)
+ assert_match(/ENCTYPE="application\/x-www-form-urlencoded"/,cgi.form)
+ assert_match(/^<FORM .*><\/FORM>$/,cgi.form("bar"))
+ assert_match(/METHOD="bar"/,cgi.form("bar"))
+ assert_match(/ENCTYPE="application\/x-www-form-urlencoded"/,cgi.form("bar"))
+ assert_match(/^<FORM .*>foo<\/FORM>$/,cgi.form{"foo"})
+ assert_match(/METHOD="post"/,cgi.form{"foo"})
+ assert_match(/ENCTYPE="application\/x-www-form-urlencoded"/,cgi.form{"foo"})
+ assert_match(/^<FORM .*>foo<\/FORM>$/,cgi.form("bar"){"foo"})
+ assert_match(/METHOD="bar"/,cgi.form("bar"){"foo"})
+ assert_match(/ENCTYPE="application\/x-www-form-urlencoded"/,cgi.form("bar"){"foo"})
+ assert_equal('<BLOCKQUOTE></BLOCKQUOTE>',cgi.blockquote)
+ assert_equal('<BLOCKQUOTE CITE="bar"></BLOCKQUOTE>',cgi.blockquote('bar'))
+ assert_equal('<BLOCKQUOTE>foo</BLOCKQUOTE>',cgi.blockquote{'foo'})
+ assert_equal('<BLOCKQUOTE CITE="bar">foo</BLOCKQUOTE>',cgi.blockquote('bar'){'foo'})
+ assert_equal('<CAPTION></CAPTION>',cgi.caption)
+ assert_equal('<CAPTION ALIGN="bar"></CAPTION>',cgi.caption('bar'))
+ assert_equal('<CAPTION>foo</CAPTION>',cgi.caption{'foo'})
+ assert_equal('<CAPTION ALIGN="bar">foo</CAPTION>',cgi.caption('bar'){'foo'})
+ assert_equal('<IMG SRC="" ALT="">',cgi.img)
+ assert_equal('<IMG SRC="bar" ALT="">',cgi.img('bar'))
+ assert_equal('<IMG SRC="" ALT="">',cgi.img{'foo'})
+ assert_equal('<IMG SRC="bar" ALT="">',cgi.img('bar'){'foo'})
+ assert_equal('<BASE HREF="">',cgi.base)
+ assert_equal('<BASE HREF="bar">',cgi.base('bar'))
+ assert_equal('<BASE HREF="">',cgi.base{'foo'})
+ assert_equal('<BASE HREF="bar">',cgi.base('bar'){'foo'})
+ assert_equal('<BASEFONT>',cgi.basefont)
+ assert_equal('<BASEFONT>',cgi.basefont('bar'))
+ assert_equal('<BASEFONT>',cgi.basefont{'foo'})
+ assert_equal('<BASEFONT>',cgi.basefont('bar'){'foo'})
+ assert_equal('<BR>',cgi.br)
+ assert_equal('<BR>',cgi.br('bar'))
+ assert_equal('<BR>',cgi.br{'foo'})
+ assert_equal('<BR>',cgi.br('bar'){'foo'})
+ assert_equal('<AREA>',cgi.area)
+ assert_equal('<AREA>',cgi.area('bar'))
+ assert_equal('<AREA>',cgi.area{'foo'})
+ assert_equal('<AREA>',cgi.area('bar'){'foo'})
+ assert_equal('<LINK>',cgi.link)
+ assert_equal('<LINK>',cgi.link('bar'))
+ assert_equal('<LINK>',cgi.link{'foo'})
+ assert_equal('<LINK>',cgi.link('bar'){'foo'})
+ assert_equal('<PARAM>',cgi.param)
+ assert_equal('<PARAM>',cgi.param('bar'))
+ assert_equal('<PARAM>',cgi.param{'foo'})
+ assert_equal('<PARAM>',cgi.param('bar'){'foo'})
+ assert_equal('<HR>',cgi.hr)
+ assert_equal('<HR>',cgi.hr('bar'))
+ assert_equal('<HR>',cgi.hr{'foo'})
+ assert_equal('<HR>',cgi.hr('bar'){'foo'})
+ assert_equal('<INPUT>',cgi.input)
+ assert_equal('<INPUT>',cgi.input('bar'))
+ assert_equal('<INPUT>',cgi.input{'foo'})
+ assert_equal('<INPUT>',cgi.input('bar'){'foo'})
+ assert_equal('<ISINDEX>',cgi.isindex)
+ assert_equal('<ISINDEX>',cgi.isindex('bar'))
+ assert_equal('<ISINDEX>',cgi.isindex{'foo'})
+ assert_equal('<ISINDEX>',cgi.isindex('bar'){'foo'})
+ assert_equal('<META>',cgi.meta)
+ assert_equal('<META>',cgi.meta('bar'))
+ assert_equal('<META>',cgi.meta{'foo'})
+ assert_equal('<META>',cgi.meta('bar'){'foo'})
+ assert_equal('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>',cgi.html)
+ assert_equal('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>foo</HTML>',cgi.html{'foo'})
+ assert_equal('<HEAD>',cgi.head)
+ assert_equal('<HEAD>foo</HEAD>',cgi.head{'foo'})
+ assert_equal('<BODY>',cgi.body)
+ assert_equal('<BODY>foo</BODY>',cgi.body{'foo'})
+ assert_equal('<P>',cgi.p)
+ assert_equal('<P>foo</P>',cgi.p{'foo'})
+ assert_equal('<PLAINTEXT>',cgi.plaintext)
+ assert_equal('<PLAINTEXT>foo</PLAINTEXT>',cgi.plaintext{'foo'})
+ assert_equal('<DT>',cgi.dt)
+ assert_equal('<DT>foo</DT>',cgi.dt{'foo'})
+ assert_equal('<DD>',cgi.dd)
+ assert_equal('<DD>foo</DD>',cgi.dd{'foo'})
+ assert_equal('<LI>',cgi.li)
+ assert_equal('<LI>foo</LI>',cgi.li{'foo'})
+ assert_equal('<OPTION>',cgi.option)
+ assert_equal('<OPTION>foo</OPTION>',cgi.option{'foo'})
+ assert_equal('<TR>',cgi.tr)
+ assert_equal('<TR>foo</TR>',cgi.tr{'foo'})
+ assert_equal('<TH>',cgi.th)
+ assert_equal('<TH>foo</TH>',cgi.th{'foo'})
+ assert_equal('<TD>',cgi.td)
+ assert_equal('<TD>foo</TD>',cgi.td{'foo'})
+ str=cgi.checkbox_group("foo",["aa","bb"],["cc","dd"])
+ assert_match(/^<INPUT .*VALUE="aa".*>bb<INPUT .*VALUE="cc".*>dd$/,str)
+ assert_match(/^<INPUT .*TYPE="checkbox".*>bb<INPUT .*TYPE="checkbox".*>dd$/,str)
+ assert_match(/^<INPUT .*NAME="foo".*>bb<INPUT .*NAME="foo".*>dd$/,str)
+ str=cgi.radio_group("foo",["aa","bb"],["cc","dd"])
+ assert_match(/^<INPUT .*VALUE="aa".*>bb<INPUT .*VALUE="cc".*>dd$/,str)
+ assert_match(/^<INPUT .*TYPE="radio".*>bb<INPUT .*TYPE="radio".*>dd$/,str)
+ assert_match(/^<INPUT .*NAME="foo".*>bb<INPUT .*NAME="foo".*>dd$/,str)
+ str=cgi.checkbox_group("foo",["aa","bb"],["cc","dd",true])
+ assert_match(/^<INPUT .*VALUE="aa".*>bb<INPUT .*VALUE="cc".*>dd$/,str)
+ assert_match(/^<INPUT .*TYPE="checkbox".*>bb<INPUT .*TYPE="checkbox".*>dd$/,str)
+ assert_match(/^<INPUT .*NAME="foo".*>bb<INPUT .*NAME="foo".*>dd$/,str)
+ assert_match(/^<INPUT .*>bb<INPUT .*CHECKED.*>dd$/,str)
+ assert_match(/<INPUT .*TYPE="text".*>/,cgi.text_field(:name=>"name",:value=>"value"))
+ str=cgi.radio_group("foo",["aa","bb"],["cc","dd",false])
+ assert_match(/^<INPUT .*VALUE="aa".*>bb<INPUT .*VALUE="cc".*>dd$/,str)
+ assert_match(/^<INPUT .*TYPE="radio".*>bb<INPUT .*TYPE="radio".*>dd$/,str)
+ assert_match(/^<INPUT .*NAME="foo".*>bb<INPUT .*NAME="foo".*>dd$/,str)
+ end
+
+=begin
+ def test_cgi_tag_helper_html4
+ ## html4
+ cgi = CGI.new('html4')
+ ## html4 transitional
+ cgi = CGI.new('html4Tr')
+ ## html4 frameset
+ cgi = CGI.new('html4Fr')
+ end
+=end
+
+ def test_cgi_tag_helper_html5
+ @environ = {
+ 'REQUEST_METHOD' => 'GET',
+ }
+ ENV.update(@environ)
+ ## html5
+ cgi = CGI.new('html5')
+ assert_equal('<HEADER></HEADER>',cgi.header)
+ assert_equal('<FOOTER></FOOTER>',cgi.footer)
+ assert_equal('<ARTICLE></ARTICLE>',cgi.article)
+ assert_equal('<SECTION></SECTION>',cgi.section)
+ assert_equal('<!DOCTYPE HTML><HTML BLA="TEST"></HTML>',cgi.html("BLA"=>"TEST"){})
+ end
+
+end
diff --git a/jni/ruby/test/cgi/test_cgi_util.rb b/jni/ruby/test/cgi/test_cgi_util.rb
new file mode 100644
index 0000000..802379d
--- /dev/null
+++ b/jni/ruby/test/cgi/test_cgi_util.rb
@@ -0,0 +1,104 @@
+require 'test/unit'
+require 'cgi'
+require 'stringio'
+
+
+class CGIUtilTest < Test::Unit::TestCase
+ include CGI::Util
+
+ def setup
+ ENV['REQUEST_METHOD'] = 'GET'
+ @str1="&<>\" \xE3\x82\x86\xE3\x82\x93\xE3\x82\x86\xE3\x82\x93"
+ @str1.force_encoding("UTF-8") if defined?(::Encoding)
+ end
+
+ def teardown
+ %W[REQUEST_METHOD SCRIPT_NAME].each do |name|
+ ENV.delete(name)
+ end
+ end
+
+
+ def test_cgi_escape
+ assert_equal('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93', CGI::escape(@str1))
+ assert_equal('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93'.ascii_only?, CGI::escape(@str1).ascii_only?) if defined?(::Encoding)
+ end
+
+ def test_cgi_escape_with_invalid_byte_sequence
+ assert_nothing_raised(ArgumentError) do
+ assert_equal('%C0%3C%3C', CGI::escape("\xC0\<\<".force_encoding("UTF-8")))
+ end
+ end
+
+ def test_cgi_escape_preserve_encoding
+ assert_equal(Encoding::US_ASCII, CGI::escape("\xC0\<\<".force_encoding("US-ASCII")).encoding)
+ assert_equal(Encoding::ASCII_8BIT, CGI::escape("\xC0\<\<".force_encoding("ASCII-8BIT")).encoding)
+ assert_equal(Encoding::UTF_8, CGI::escape("\xC0\<\<".force_encoding("UTF-8")).encoding)
+ end
+
+ def test_cgi_unescape
+ assert_equal(@str1, CGI::unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93'))
+ assert_equal(@str1.encoding, CGI::unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93').encoding) if defined?(::Encoding)
+ assert_equal("\u{30E1 30E2 30EA 691C 7D22}", CGI.unescape("\u{30E1 30E2 30EA}%E6%A4%9C%E7%B4%A2"))
+ end
+
+ def test_cgi_unescape_preserve_encoding
+ assert_equal(Encoding::US_ASCII, CGI::unescape("%C0%3C%3C".force_encoding("US-ASCII")).encoding)
+ assert_equal(Encoding::ASCII_8BIT, CGI::unescape("%C0%3C%3C".force_encoding("ASCII-8BIT")).encoding)
+ assert_equal(Encoding::UTF_8, CGI::unescape("%C0%3C%3C".force_encoding("UTF-8")).encoding)
+ end
+
+ def test_cgi_pretty
+ assert_equal("<HTML>\n <BODY>\n </BODY>\n</HTML>\n",CGI::pretty("<HTML><BODY></BODY></HTML>"))
+ assert_equal("<HTML>\n\t<BODY>\n\t</BODY>\n</HTML>\n",CGI::pretty("<HTML><BODY></BODY></HTML>","\t"))
+ end
+
+ def test_cgi_escapeHTML
+ assert_equal(CGI::escapeHTML("'&\"><"),"&#39;&amp;&quot;&gt;&lt;")
+ end
+
+ def test_cgi_unescapeHTML
+ assert_equal(CGI::unescapeHTML("&#39;&amp;&quot;&gt;&lt;"),"'&\"><")
+ end
+
+ def test_cgi_unescapeHTML_uppercasecharacter
+ assert_equal(CGI::unescapeHTML("&#x3042;&#x3044;&#X3046;"),"\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86")
+ end
+
+ def test_cgi_include_escape
+ assert_equal('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93', escape(@str1))
+ end
+
+ def test_cgi_include_escapeHTML
+ assert_equal(escapeHTML("'&\"><"),"&#39;&amp;&quot;&gt;&lt;")
+ end
+
+ def test_cgi_include_h
+ assert_equal(h("'&\"><"),"&#39;&amp;&quot;&gt;&lt;")
+ end
+
+ def test_cgi_include_unescape
+ assert_equal(@str1, unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93'))
+ assert_equal(@str1.encoding, unescape('%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93').encoding) if defined?(::Encoding)
+ assert_equal("\u{30E1 30E2 30EA 691C 7D22}", unescape("\u{30E1 30E2 30EA}%E6%A4%9C%E7%B4%A2"))
+ end
+
+ def test_cgi_include_unescapeHTML
+ assert_equal(unescapeHTML("&#39;&amp;&quot;&gt;&lt;"),"'&\"><")
+ end
+
+ def test_cgi_escapeElement
+ assert_equal("<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;", escapeElement('<BR><A HREF="url"></A>', "A", "IMG"))
+ assert_equal("<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;", escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"]))
+ assert_equal("<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;", escape_element('<BR><A HREF="url"></A>', "A", "IMG"))
+ assert_equal("<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt;", escape_element('<BR><A HREF="url"></A>', ["A", "IMG"]))
+ end
+
+
+ def test_cgi_unescapeElement
+ assert_equal('&lt;BR&gt;<A HREF="url"></A>', unescapeElement(escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG"))
+ assert_equal('&lt;BR&gt;<A HREF="url"></A>', unescapeElement(escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"]))
+ assert_equal('&lt;BR&gt;<A HREF="url"></A>', unescape_element(escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG"))
+ assert_equal('&lt;BR&gt;<A HREF="url"></A>', unescape_element(escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"]))
+ end
+end
diff --git a/jni/ruby/test/cgi/testdata/file1.html b/jni/ruby/test/cgi/testdata/file1.html
new file mode 100644
index 0000000..2ceaf6b
--- /dev/null
+++ b/jni/ruby/test/cgi/testdata/file1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>ムスカ大佐のひとりごと</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF8">
+ </head>
+ <body>
+ <p>バカどもにはちょうどいい目くらましだ。</p>
+ </body>
+</html>
diff --git a/jni/ruby/test/cgi/testdata/large.png b/jni/ruby/test/cgi/testdata/large.png
new file mode 100644
index 0000000..d716396
--- /dev/null
+++ b/jni/ruby/test/cgi/testdata/large.png
Binary files differ
diff --git a/jni/ruby/test/cgi/testdata/small.png b/jni/ruby/test/cgi/testdata/small.png
new file mode 100644
index 0000000..753d58e
--- /dev/null
+++ b/jni/ruby/test/cgi/testdata/small.png
Binary files differ
diff --git a/jni/ruby/test/coverage/test_coverage.rb b/jni/ruby/test/coverage/test_coverage.rb
new file mode 100644
index 0000000..f4f192a
--- /dev/null
+++ b/jni/ruby/test/coverage/test_coverage.rb
@@ -0,0 +1,64 @@
+require "test/unit"
+require "coverage"
+require "tmpdir"
+
+class TestCoverage < Test::Unit::TestCase
+ def test_result_without_start
+ assert_raise(RuntimeError) {Coverage.result}
+ end
+ def test_result_with_nothing
+ Coverage.start
+ result = Coverage.result
+ assert_kind_of(Hash, result)
+ result.each do |key, val|
+ assert_kind_of(String, key)
+ assert_kind_of(Array, val)
+ end
+ end
+
+ def test_restarting_coverage
+ loaded_features = $".dup
+
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.open("test.rb", "w") do |f|
+ f.puts <<-EOS
+ def coverage_test_method
+ :ok
+ end
+ EOS
+ end
+
+ Coverage.start
+ require tmp + '/test.rb'
+ assert_equal 3, Coverage.result[tmp + '/test.rb'].size
+ Coverage.start
+ coverage_test_method
+ assert_equal 0, Coverage.result[tmp + '/test.rb'].size
+ }
+ }
+ ensure
+ $".replace loaded_features
+ end
+
+ def test_big_code
+ loaded_features = $".dup
+
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ File.open("test.rb", "w") do |f|
+ f.puts "__id__\n" * 10000
+ f.puts "def ignore(x); end"
+ f.puts "ignore([1"
+ f.puts "])"
+ end
+
+ Coverage.start
+ require tmp + '/test.rb'
+ assert_equal 10003, Coverage.result[tmp + '/test.rb'].size
+ }
+ }
+ ensure
+ $".replace loaded_features
+ end
+end
diff --git a/jni/ruby/test/csv/base.rb b/jni/ruby/test/csv/base.rb
new file mode 100644
index 0000000..621569e
--- /dev/null
+++ b/jni/ruby/test/csv/base.rb
@@ -0,0 +1,8 @@
+require "test/unit"
+
+require "csv"
+
+require_relative "../lib/with_different_ofs.rb"
+
+class TestCSV < Test::Unit::TestCase
+end
diff --git a/jni/ruby/test/csv/line_endings.gz b/jni/ruby/test/csv/line_endings.gz
new file mode 100644
index 0000000..39e1729
--- /dev/null
+++ b/jni/ruby/test/csv/line_endings.gz
Binary files differ
diff --git a/jni/ruby/test/csv/test_csv_parsing.rb b/jni/ruby/test/csv/test_csv_parsing.rb
new file mode 100755
index 0000000..319f3f3
--- /dev/null
+++ b/jni/ruby/test/csv/test_csv_parsing.rb
@@ -0,0 +1,221 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_csv_parsing.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require "timeout"
+
+require_relative "base"
+
+#
+# Following tests are my interpretation of the
+# {CSV RCF}[http://www.ietf.org/rfc/rfc4180.txt]. I only deviate from that
+# document in one place (intentionally) and that is to make the default row
+# separator <tt>$/</tt>.
+#
+class TestCSV::Parsing < TestCSV
+ extend DifferentOFS
+
+ BIG_DATA = "123456789\n" * 1024
+
+ def test_mastering_regex_example
+ ex = %Q{Ten Thousand,10000, 2710 ,,"10,000","It's ""10 Grand"", baby",10K}
+ assert_equal( [ "Ten Thousand", "10000", " 2710 ", nil, "10,000",
+ "It's \"10 Grand\", baby", "10K" ],
+ CSV.parse_line(ex) )
+ end
+
+ # Old Ruby 1.8 CSV library tests.
+ def test_std_lib_csv
+ [ ["\t", ["\t"]],
+ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
+ ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
+ ["\"\"\"\n\",\"\"\"\n\"", ["\"\n", "\"\n"]],
+ ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
+ ["\"\"", [""]],
+ ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
+ ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
+ ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
+ ["foo,\"\",baz", ["foo", "", "baz"]],
+ ["\",\"", [","]],
+ ["foo", ["foo"]],
+ [",,", [nil, nil, nil]],
+ [",", [nil, nil]],
+ ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
+ ["foo,,baz", ["foo", nil, "baz"]],
+ ["\"\"\"\r\",\"\"\"\r\"", ["\"\r", "\"\r"]],
+ ["\",\",\",\"", [",", ","]],
+ ["foo,bar,", ["foo", "bar", nil]],
+ [",foo,bar", [nil, "foo", "bar"]],
+ ["foo,bar", ["foo", "bar"]],
+ [";", [";"]],
+ ["\t,\t", ["\t", "\t"]],
+ ["foo,\"\r\n\r\",baz", ["foo", "\r\n\r", "baz"]],
+ ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
+ ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]],
+ [";,;", [";", ";"]] ].each do |csv_test|
+ assert_equal(csv_test.last, CSV.parse_line(csv_test.first))
+ end
+
+ [ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
+ ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
+ ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
+ ["\"\"", [""]],
+ ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
+ ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
+ ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
+ ["foo,\"\",baz", ["foo", "", "baz"]],
+ ["foo", ["foo"]],
+ [",,", [nil, nil, nil]],
+ [",", [nil, nil]],
+ ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
+ ["foo,,baz", ["foo", nil, "baz"]],
+ ["foo,bar", ["foo", "bar"]],
+ ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
+ ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]] ].each do |csv_test|
+ assert_equal(csv_test.last, CSV.parse_line(csv_test.first))
+ end
+ end
+
+ # From: http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-core/6496
+ def test_aras_edge_cases
+ [ [%Q{a,b}, ["a", "b"]],
+ [%Q{a,"""b"""}, ["a", "\"b\""]],
+ [%Q{a,"""b"}, ["a", "\"b"]],
+ [%Q{a,"b"""}, ["a", "b\""]],
+ [%Q{a,"\nb"""}, ["a", "\nb\""]],
+ [%Q{a,"""\nb"}, ["a", "\"\nb"]],
+ [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]],
+ [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]],
+ [%Q{a,,,}, ["a", nil, nil, nil]],
+ [%Q{,}, [nil, nil]],
+ [%Q{"",""}, ["", ""]],
+ [%Q{""""}, ["\""]],
+ [%Q{"""",""}, ["\"",""]],
+ [%Q{,""}, [nil,""]],
+ [%Q{,"\r"}, [nil,"\r"]],
+ [%Q{"\r\n,"}, ["\r\n,"]],
+ [%Q{"\r\n,",}, ["\r\n,", nil]] ].each do |edge_case|
+ assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
+ end
+ end
+
+ def test_james_edge_cases
+ # A read at eof? should return nil.
+ assert_equal(nil, CSV.parse_line(""))
+ #
+ # With Ruby 1.8 CSV it's impossible to tell an empty line from a line
+ # containing a single +nil+ field. The old CSV library returns
+ # <tt>[nil]</tt> in these cases, but <tt>Array.new</tt> makes more sense to
+ # me.
+ #
+ assert_equal(Array.new, CSV.parse_line("\n1,2,3\n"))
+ end
+
+ def test_rob_edge_cases
+ [ [%Q{"a\nb"}, ["a\nb"]],
+ [%Q{"\n\n\n"}, ["\n\n\n"]],
+ [%Q{a,"b\n\nc"}, ['a', "b\n\nc"]],
+ [%Q{,"\r\n"}, [nil,"\r\n"]],
+ [%Q{,"\r\n."}, [nil,"\r\n."]],
+ [%Q{"a\na","one newline"}, ["a\na", 'one newline']],
+ [%Q{"a\n\na","two newlines"}, ["a\n\na", 'two newlines']],
+ [%Q{"a\r\na","one CRLF"}, ["a\r\na", 'one CRLF']],
+ [%Q{"a\r\n\r\na","two CRLFs"}, ["a\r\n\r\na", 'two CRLFs']],
+ [%Q{with blank,"start\n\nfinish"\n}, ['with blank', "start\n\nfinish"]],
+ ].each do |edge_case|
+ assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
+ end
+ end
+
+ def test_non_regex_edge_cases
+ # An early version of the non-regex parser fails this test
+ [ [ "foo,\"foo,bar,baz,foo\",\"foo\"",
+ ["foo", "foo,bar,baz,foo", "foo"] ] ].each do |edge_case|
+ assert_equal(edge_case.last, CSV.parse_line(edge_case.first))
+ end
+
+ assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line("1,\"23\"4\"5\", 6")
+ end
+ end
+
+ def test_malformed_csv
+ assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line("1,2\r,3", row_sep: "\n")
+ end
+
+ bad_data = <<-END_DATA.gsub(/^ +/, "")
+ line,1,abc
+ line,2,"def\nghi"
+
+ line,4,some\rjunk
+ line,5,jkl
+ END_DATA
+ lines = bad_data.lines.to_a
+ assert_equal(6, lines.size)
+ assert_match(/\Aline,4/, lines.find { |l| l =~ /some\rjunk/ })
+
+ csv = CSV.new(bad_data)
+ begin
+ loop do
+ assert_not_nil(csv.shift)
+ assert_send([csv.lineno, :<, 4])
+ end
+ rescue CSV::MalformedCSVError
+ assert_equal( "Unquoted fields do not allow \\r or \\n (line 4).",
+ $!.message )
+ end
+
+ assert_raise(CSV::MalformedCSVError) { CSV.parse_line('1,2,"3...') }
+
+ bad_data = <<-END_DATA.gsub(/^ +/, "")
+ line,1,abc
+ line,2,"def\nghi"
+
+ line,4,8'10"
+ line,5,jkl
+ END_DATA
+ lines = bad_data.lines.to_a
+ assert_equal(6, lines.size)
+ assert_match(/\Aline,4/, lines.find { |l| l =~ /8'10"/ })
+
+ csv = CSV.new(bad_data)
+ begin
+ loop do
+ assert_not_nil(csv.shift)
+ assert_send([csv.lineno, :<, 4])
+ end
+ rescue CSV::MalformedCSVError
+ assert_equal("Illegal quoting in line 4.", $!.message)
+ end
+ end
+
+ def test_the_parse_fails_fast_when_it_can_for_unquoted_fields
+ assert_parse_errors_out('valid,fields,bad start"' + BIG_DATA)
+ end
+
+ def test_the_parse_fails_fast_when_it_can_for_unescaped_quotes
+ assert_parse_errors_out('valid,fields,"bad start"unescaped' + BIG_DATA)
+ end
+
+ def test_field_size_limit_controls_lookahead
+ assert_parse_errors_out( 'valid,fields,"' + BIG_DATA + '"',
+ field_size_limit: 2048 )
+ end
+
+ private
+
+ def assert_parse_errors_out(*args)
+ assert_raise(CSV::MalformedCSVError) do
+ Timeout.timeout(0.2) do
+ CSV.parse(*args)
+ fail("Parse didn't error out")
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/csv/test_csv_writing.rb b/jni/ruby/test/csv/test_csv_writing.rb
new file mode 100755
index 0000000..704c1d7
--- /dev/null
+++ b/jni/ruby/test/csv/test_csv_writing.rb
@@ -0,0 +1,97 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_csv_writing.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require_relative "base"
+
+class TestCSV::Writing < TestCSV
+ extend DifferentOFS
+
+ def test_writing
+ [ ["\t", ["\t"]],
+ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
+ ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
+ ["\"\"\"\n\",\"\"\"\n\"", ["\"\n", "\"\n"]],
+ ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
+ ["\"\"", [""]],
+ ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
+ ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
+ ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
+ ["foo,\"\",baz", ["foo", "", "baz"]],
+ ["\",\"", [","]],
+ ["foo", ["foo"]],
+ [",,", [nil, nil, nil]],
+ [",", [nil, nil]],
+ ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
+ ["foo,,baz", ["foo", nil, "baz"]],
+ ["\"\"\"\r\",\"\"\"\r\"", ["\"\r", "\"\r"]],
+ ["\",\",\",\"", [",", ","]],
+ ["foo,bar,", ["foo", "bar", nil]],
+ [",foo,bar", [nil, "foo", "bar"]],
+ ["foo,bar", ["foo", "bar"]],
+ [";", [";"]],
+ ["\t,\t", ["\t", "\t"]],
+ ["foo,\"\r\n\r\",baz", ["foo", "\r\n\r", "baz"]],
+ ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
+ ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]],
+ [";,;", [";", ";"]],
+ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]],
+ ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]],
+ ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]],
+ ["\"\"", [""]],
+ ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]],
+ ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]],
+ ["foo,\"\r\",baz", ["foo", "\r", "baz"]],
+ ["foo,\"\",baz", ["foo", "", "baz"]],
+ ["foo", ["foo"]],
+ [",,", [nil, nil, nil]],
+ [",", [nil, nil]],
+ ["foo,\"\n\",baz", ["foo", "\n", "baz"]],
+ ["foo,,baz", ["foo", nil, "baz"]],
+ ["foo,bar", ["foo", "bar"]],
+ ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]],
+ ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]],
+ [%Q{a,b}, ["a", "b"]],
+ [%Q{a,"""b"""}, ["a", "\"b\""]],
+ [%Q{a,"""b"}, ["a", "\"b"]],
+ [%Q{a,"b"""}, ["a", "b\""]],
+ [%Q{a,"\nb"""}, ["a", "\nb\""]],
+ [%Q{a,"""\nb"}, ["a", "\"\nb"]],
+ [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]],
+ [%Q{a,"""\nb\n""",}, ["a", "\"\nb\n\"", nil]],
+ [%Q{a,,,}, ["a", nil, nil, nil]],
+ [%Q{,}, [nil, nil]],
+ [%Q{"",""}, ["", ""]],
+ [%Q{""""}, ["\""]],
+ [%Q{"""",""}, ["\"",""]],
+ [%Q{,""}, [nil,""]],
+ [%Q{,"\r"}, [nil,"\r"]],
+ [%Q{"\r\n,"}, ["\r\n,"]],
+ [%Q{"\r\n,",}, ["\r\n,", nil]] ].each do |test_case|
+ assert_equal(test_case.first + $/, CSV.generate_line(test_case.last))
+ end
+ end
+
+ def test_col_sep
+ assert_equal( "a;b;;c\n", CSV.generate_line( ["a", "b", nil, "c"],
+ col_sep: ";" ) )
+ assert_equal( "a\tb\t\tc\n", CSV.generate_line( ["a", "b", nil, "c"],
+ col_sep: "\t" ) )
+ end
+
+ def test_row_sep
+ assert_equal( "a,b,,c\r\n", CSV.generate_line( ["a", "b", nil, "c"],
+ row_sep: "\r\n" ) )
+ end
+
+ def test_force_quotes
+ assert_equal( %Q{"1","b","","already ""quoted"""\n},
+ CSV.generate_line( [1, "b", nil, %Q{already "quoted"}],
+ force_quotes: true ) )
+ end
+end
diff --git a/jni/ruby/test/csv/test_data_converters.rb b/jni/ruby/test/csv/test_data_converters.rb
new file mode 100755
index 0000000..89b6dd1
--- /dev/null
+++ b/jni/ruby/test/csv/test_data_converters.rb
@@ -0,0 +1,263 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_data_converters.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require_relative "base"
+
+class TestCSV::DataConverters < TestCSV
+ extend DifferentOFS
+
+ def setup
+ super
+ @data = "Numbers,:integer,1,:float,3.015"
+ @parser = CSV.new(@data)
+
+ @custom = lambda { |field| field =~ /\A:(\S.*?)\s*\Z/ ? $1.to_sym : field }
+
+ @win_safe_time_str = Time.now.strftime("%a %b %d %H:%M:%S %Y")
+ end
+
+ def test_builtin_integer_converter
+ # does convert
+ [-5, 1, 10000000000].each do |n|
+ assert_equal(n, CSV::Converters[:integer][n.to_s])
+ end
+
+ # does not convert
+ (%w{junk 1.0} + [""]).each do |str|
+ assert_equal(str, CSV::Converters[:integer][str])
+ end
+ end
+
+ def test_builtin_float_converter
+ # does convert
+ [-5.1234, 0, 2.3e-11].each do |n|
+ assert_equal(n, CSV::Converters[:float][n.to_s])
+ end
+
+ # does not convert
+ (%w{junk 1..0 .015F} + [""]).each do |str|
+ assert_equal(str, CSV::Converters[:float][str])
+ end
+ end
+
+ def test_builtin_date_converter
+ # does convert
+ assert_instance_of(
+ Date,
+ CSV::Converters[:date][@win_safe_time_str.sub(/\d+:\d+:\d+ /, "")]
+ )
+
+ # does not convert
+ assert_instance_of(String, CSV::Converters[:date]["junk"])
+ end
+
+ def test_builtin_date_time_converter
+ # does convert
+ assert_instance_of( DateTime,
+ CSV::Converters[:date_time][@win_safe_time_str] )
+
+ # does not convert
+ assert_instance_of(String, CSV::Converters[:date_time]["junk"])
+ end
+
+ def test_convert_with_builtin_integer
+ # setup parser...
+ assert_respond_to(@parser, :convert)
+ assert_nothing_raised(Exception) { @parser.convert(:integer) }
+
+ # and use
+ assert_equal(["Numbers", ":integer", 1, ":float", "3.015"], @parser.shift)
+ end
+
+ def test_convert_with_builtin_float
+ # setup parser...
+ assert_respond_to(@parser, :convert)
+ assert_nothing_raised(Exception) { @parser.convert(:float) }
+
+ # and use
+ assert_equal(["Numbers", ":integer", 1.0, ":float", 3.015], @parser.shift)
+ end
+
+ def test_convert_order_float_integer
+ # floats first, then integers...
+ assert_nothing_raised(Exception) do
+ @parser.convert(:float)
+ @parser.convert(:integer)
+ end
+
+ # gets us nothing but floats
+ assert_equal( [String, String, Float, String, Float],
+ @parser.shift.map { |field| field.class } )
+ end
+
+ def test_convert_order_integer_float
+ # integers have precendance...
+ assert_nothing_raised(Exception) do
+ @parser.convert(:integer)
+ @parser.convert(:float)
+ end
+
+ # gives us proper number conversion
+ assert_equal( [String, String, Fixnum, String, Float],
+ @parser.shift.map { |field| field.class } )
+ end
+
+ def test_builtin_numeric_combo_converter
+ # setup parser...
+ assert_nothing_raised(Exception) { @parser.convert(:numeric) }
+
+ # and use
+ assert_equal( [String, String, Fixnum, String, Float],
+ @parser.shift.map { |field| field.class } )
+ end
+
+ def test_builtin_all_nested_combo_converter
+ # setup parser...
+ @data << ",#{@win_safe_time_str}" # add a DateTime field
+ @parser = CSV.new(@data) # reset parser
+ assert_nothing_raised(Exception) { @parser.convert(:all) }
+
+ # and use
+ assert_equal( [String, String, Fixnum, String, Float, DateTime],
+ @parser.shift.map { |field| field.class } )
+ end
+
+ def test_convert_with_custom_code
+ # define custom converter...
+ assert_nothing_raised(Exception) do
+ @parser.convert { |field| field =~ /\A:(\S.*?)\s*\Z/ ? $1.to_sym : field }
+ end
+
+ # and use
+ assert_equal(["Numbers", :integer, "1", :float, "3.015"], @parser.shift)
+ end
+
+ def test_convert_with_custom_code_mix
+ # mix built-in and custom...
+ assert_nothing_raised(Exception) { @parser.convert(:numeric) }
+ assert_nothing_raised(Exception) { @parser.convert(&@custom) }
+
+ # and use
+ assert_equal(["Numbers", :integer, 1, :float, 3.015], @parser.shift)
+ end
+
+ def test_convert_with_custom_code_using_field_info
+ # define custom converter that uses field information...
+ assert_nothing_raised(Exception) do
+ @parser.convert do |field, info|
+ assert_equal(1, info.line)
+ info.index == 4 ? Float(field).floor : field
+ end
+ end
+
+ # and use
+ assert_equal(["Numbers", ":integer", "1", ":float", 3], @parser.shift)
+ end
+
+ def test_convert_with_custom_code_using_field_info_header
+ @parser = CSV.new(@data, headers: %w{one two three four five})
+
+ # define custom converter that uses field header information...
+ assert_nothing_raised(Exception) do
+ @parser.convert do |field, info|
+ info.header == "three" ? Integer(field) * 100 : field
+ end
+ end
+
+ # and use
+ assert_equal( ["Numbers", ":integer", 100, ":float", "3.015"],
+ @parser.shift.fields )
+ end
+
+ def test_shortcut_interface
+ assert_equal( ["Numbers", ":integer", 1, ":float", 3.015],
+ CSV.parse_line(@data, converters: :numeric) )
+
+ assert_equal( ["Numbers", ":integer", 1, ":float", 3.015],
+ CSV.parse_line(@data, converters: [:integer, :float]) )
+
+ assert_equal( ["Numbers", :integer, 1, :float, 3.015],
+ CSV.parse_line(@data, converters: [:numeric, @custom]) )
+ end
+
+ def test_unconverted_fields
+ [ [ @data,
+ ["Numbers", :integer, 1, :float, 3.015],
+ %w{Numbers :integer 1 :float 3.015} ],
+ ["\n", Array.new, Array.new] ].each do |test, fields, unconverted|
+ row = nil
+ assert_nothing_raised(Exception) do
+ row = CSV.parse_line( test,
+ converters: [:numeric, @custom],
+ unconverted_fields: true )
+ end
+ assert_not_nil(row)
+ assert_equal(fields, row)
+ assert_respond_to(row, :unconverted_fields)
+ assert_equal(unconverted, row.unconverted_fields)
+ end
+
+ data = <<-END_CSV.gsub(/^\s+/, "")
+ first,second,third
+ 1,2,3
+ END_CSV
+ row = nil
+ assert_nothing_raised(Exception) do
+ row = CSV.parse_line( data,
+ converters: :numeric,
+ unconverted_fields: true,
+ headers: :first_row )
+ end
+ assert_not_nil(row)
+ assert_equal([["first", 1], ["second", 2], ["third", 3]], row.to_a)
+ assert_respond_to(row, :unconverted_fields)
+ assert_equal(%w{1 2 3}, row.unconverted_fields)
+
+ assert_nothing_raised(Exception) do
+ row = CSV.parse_line( data,
+ converters: :numeric,
+ unconverted_fields: true,
+ headers: :first_row,
+ return_headers: true )
+ end
+ assert_not_nil(row)
+ assert_equal( [%w{first first}, %w{second second}, %w{third third}],
+ row.to_a )
+ assert_respond_to(row, :unconverted_fields)
+ assert_equal(%w{first second third}, row.unconverted_fields)
+
+ assert_nothing_raised(Exception) do
+ row = CSV.parse_line( data,
+ converters: :numeric,
+ unconverted_fields: true,
+ headers: :first_row,
+ return_headers: true,
+ header_converters: :symbol )
+ end
+ assert_not_nil(row)
+ assert_equal( [[:first, "first"], [:second, "second"], [:third, "third"]],
+ row.to_a )
+ assert_respond_to(row, :unconverted_fields)
+ assert_equal(%w{first second third}, row.unconverted_fields)
+
+ assert_nothing_raised(Exception) do
+ row = CSV.parse_line( data,
+ converters: :numeric,
+ unconverted_fields: true,
+ headers: %w{my new headers},
+ return_headers: true,
+ header_converters: :symbol )
+ end
+ assert_not_nil(row)
+ assert_equal( [[:my, "my"], [:new, "new"], [:headers, "headers"]],
+ row.to_a )
+ assert_respond_to(row, :unconverted_fields)
+ assert_equal(Array.new, row.unconverted_fields)
+ end
+end
diff --git a/jni/ruby/test/csv/test_encodings.rb b/jni/ruby/test/csv/test_encodings.rb
new file mode 100755
index 0000000..dc45692
--- /dev/null
+++ b/jni/ruby/test/csv/test_encodings.rb
@@ -0,0 +1,337 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_encodings.rb
+#
+# Created by James Edward Gray II on 2008-09-13.
+# Copyright 2008 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require_relative "base"
+
+class TestCSV::Encodings < TestCSV
+ extend DifferentOFS
+
+ def setup
+ super
+ require 'tempfile'
+ @temp_csv_file = Tempfile.new(%w"test_csv. .csv")
+ @temp_csv_path = @temp_csv_file.path
+ @temp_csv_file.close
+ end
+
+ def teardown
+ @temp_csv_file.close!
+ super
+ end
+
+ ########################################
+ ### Hand Test Some Popular Encodings ###
+ ########################################
+
+ def test_parses_utf8_encoding
+ assert_parses( [ %w[ one two … ],
+ %w[ 1 … 3 ],
+ %w[ … 5 6 ] ], "UTF-8" )
+ end
+
+ def test_parses_latin1_encoding
+ assert_parses( [ %w[ one two Résumé ],
+ %w[ 1 Résumé 3 ],
+ %w[ Résumé 5 6 ] ], "ISO-8859-1" )
+ end
+
+ def test_parses_utf16be_encoding
+ assert_parses( [ %w[ one two … ],
+ %w[ 1 … 3 ],
+ %w[ … 5 6 ] ], "UTF-16BE" )
+ end
+
+ def test_parses_shift_jis_encoding
+ assert_parses( [ %w[ 一 二 三 ],
+ %w[ 四 五 六 ],
+ %w[ 七 八 九 ] ], "Shift_JIS" )
+ end
+
+ ###########################################################
+ ### Try Simple Reading for All Non-dummy Ruby Encodings ###
+ ###########################################################
+
+ def test_reading_with_most_encodings
+ each_encoding do |encoding|
+ begin
+ assert_parses( [ %w[ abc def ],
+ %w[ ghi jkl ] ], encoding )
+ rescue Encoding::ConverterNotFoundError
+ fail("Failed to support #{encoding.name}.")
+ end
+ end
+ end
+
+ def test_regular_expression_escaping
+ each_encoding do |encoding|
+ begin
+ assert_parses( [ %w[ abc def ],
+ %w[ ghi jkl ] ], encoding, col_sep: "|" )
+ rescue Encoding::ConverterNotFoundError
+ fail("Failed to properly escape #{encoding.name}.")
+ end
+ end
+ end
+
+ def test_read_with_default_encoding
+ data = "abc"
+ default_external = Encoding.default_external
+ each_encoding do |encoding|
+ File.open(@temp_csv_path, "wb", encoding: encoding) {|f| f << data}
+ begin
+ no_warnings do
+ Encoding.default_external = encoding
+ end
+ result = CSV.read(@temp_csv_path)[0][0]
+ ensure
+ no_warnings do
+ Encoding.default_external = default_external
+ end
+ end
+ assert_equal(encoding, result.encoding)
+ end
+ end
+
+ #######################################################################
+ ### Stress Test ASCII Compatible and Non-ASCII Compatible Encodings ###
+ #######################################################################
+
+ def test_auto_line_ending_detection
+ # arrange data to place a \r at the end of CSV's read ahead point
+ encode_for_tests([["a" * 509]], row_sep: "\r\n") do |data|
+ assert_equal("\r\n".encode(data.encoding), CSV.new(data).row_sep)
+ end
+ end
+
+ def test_csv_chars_are_transcoded
+ encode_for_tests([%w[abc def]]) do |data|
+ %w[col_sep row_sep quote_char].each do |csv_char|
+ assert_equal( "|".encode(data.encoding),
+ CSV.new(data, csv_char.to_sym => "|").send(csv_char) )
+ end
+ end
+ end
+
+ def test_parser_works_with_encoded_headers
+ encode_for_tests([%w[one two three], %w[1 2 3]]) do |data|
+ parsed = CSV.parse(data, headers: true)
+ assert_all?(parsed.headers, "Wrong data encoding.") {|h| h.encoding == data.encoding}
+ parsed.each do |row|
+ assert_all?(row.fields, "Wrong data encoding.") {|f| f.encoding == data.encoding}
+ end
+ end
+ end
+
+ def test_built_in_converters_transcode_to_utf_8_then_convert
+ encode_for_tests([%w[one two three], %w[1 2 3]]) do |data|
+ parsed = CSV.parse(data, converters: :integer)
+ assert_all?(parsed[0], "Wrong data encoding.") {|f| f.encoding == data.encoding}
+ assert_equal([1, 2, 3], parsed[1])
+ end
+ end
+
+ def test_built_in_header_converters_transcode_to_utf_8_then_convert
+ encode_for_tests([%w[one two three], %w[1 2 3]]) do |data|
+ parsed = CSV.parse( data, headers: true,
+ header_converters: :downcase )
+ assert_all?(parsed.headers, "Wrong data encoding.") {|h| h.encoding.name == "UTF-8"}
+ assert_all?(parsed[0].fields, "Wrong data encoding.") {|f| f.encoding == data.encoding}
+ end
+ end
+
+ def test_open_allows_you_to_set_encodings
+ encode_for_tests([%w[abc def]]) do |data|
+ # read and write in encoding
+ File.open(@temp_csv_path, "wb:#{data.encoding.name}") { |f| f << data }
+ CSV.open(@temp_csv_path, "rb:#{data.encoding.name}") do |csv|
+ csv.each do |row|
+ assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding}
+ end
+ end
+
+ # read and write with transcoding
+ File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f|
+ f << data
+ end
+ CSV.open(@temp_csv_path, "rb:UTF-32BE:#{data.encoding.name}") do |csv|
+ csv.each do |row|
+ assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding}
+ end
+ end
+ end
+ end
+
+ def test_foreach_allows_you_to_set_encodings
+ encode_for_tests([%w[abc def]]) do |data|
+ # read and write in encoding
+ File.open(@temp_csv_path, "wb", encoding: data.encoding) { |f| f << data }
+ CSV.foreach(@temp_csv_path, encoding: data.encoding) do |row|
+ row.each {|f| assert_equal(f.encoding, data.encoding)}
+ end
+
+ # read and write with transcoding
+ File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f|
+ f << data
+ end
+ CSV.foreach( @temp_csv_path,
+ encoding: "UTF-32BE:#{data.encoding.name}" ) do |row|
+ assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding}
+ end
+ end
+ end
+
+ def test_read_allows_you_to_set_encodings
+ encode_for_tests([%w[abc def]]) do |data|
+ # read and write in encoding
+ File.open(@temp_csv_path, "wb:#{data.encoding.name}") { |f| f << data }
+ rows = CSV.read(@temp_csv_path, encoding: data.encoding.name)
+ assert_all?(rows.flatten, "Wrong data encoding.") {|f| f.encoding == data.encoding}
+
+ # read and write with transcoding
+ File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f|
+ f << data
+ end
+ rows = CSV.read( @temp_csv_path,
+ encoding: "UTF-32BE:#{data.encoding.name}" )
+ assert_all?(rows.flatten, "Wrong data encoding.") {|f| f.encoding == data.encoding}
+ end
+ end
+
+ #################################
+ ### Write CSV in any Encoding ###
+ #################################
+
+ def test_can_write_csv_in_any_encoding
+ each_encoding do |encoding|
+ # test generate_line with encoding hint
+ begin
+ csv = %w[abc d|ef].map { |f| f.encode(encoding) }.
+ to_csv(col_sep: "|", encoding: encoding.name)
+ rescue Encoding::ConverterNotFoundError
+ next
+ end
+ assert_equal(encoding, csv.encoding)
+
+ # test generate_line with encoding guessing from fields
+ csv = %w[abc d|ef].map { |f| f.encode(encoding) }.to_csv(col_sep: "|")
+ assert_equal(encoding, csv.encoding)
+
+ # writing to files
+ data = encode_ary([%w[abc d,ef], %w[123 456 ]], encoding)
+ CSV.open(@temp_csv_path, "wb:#{encoding.name}") do |f|
+ data.each { |row| f << row }
+ end
+ assert_equal(data, CSV.read(@temp_csv_path, encoding: encoding.name))
+ end
+ end
+
+ def test_encoding_is_upgraded_during_writing_as_needed
+ data = ["foo".force_encoding("US-ASCII"), "\u3042"]
+ assert_equal("US-ASCII", data.first.encoding.name)
+ assert_equal("UTF-8", data.last.encoding.name)
+ assert_equal("UTF-8", data.join('').encoding.name)
+ assert_equal("UTF-8", data.to_csv.encoding.name)
+ end
+
+ def test_encoding_is_upgraded_for_ascii_content_during_writing_as_needed
+ data = ["foo".force_encoding("ISO-8859-1"), "\u3042"]
+ assert_equal("ISO-8859-1", data.first.encoding.name)
+ assert_equal("UTF-8", data.last.encoding.name)
+ assert_equal("UTF-8", data.join('').encoding.name)
+ assert_equal("UTF-8", data.to_csv.encoding.name)
+ end
+
+ def test_explicit_encoding
+ bug9766 = '[ruby-core:62113] [Bug #9766]'
+ s = CSV.generate(encoding: "Windows-31J") do |csv|
+ csv << ["foo".force_encoding("ISO-8859-1"), "\u3042"]
+ end
+ assert_equal(["foo,\u3042\n".encode(Encoding::Windows_31J), Encoding::Windows_31J], [s, s.encoding], bug9766)
+ end
+
+ private
+
+ def assert_parses(fields, encoding, options = { })
+ encoding = Encoding.find(encoding) unless encoding.is_a? Encoding
+ orig_fields = fields
+ fields = encode_ary(fields, encoding)
+ data = ary_to_data(fields, options)
+ parsed = CSV.parse(data, options)
+ assert_equal(fields, parsed)
+ parsed.flatten.each_with_index do |field, i|
+ assert_equal(encoding, field.encoding, "Field[#{i + 1}] was transcoded.")
+ end
+ File.open(@temp_csv_path, "wb") {|f| f.print(data)}
+ CSV.open(@temp_csv_path, "rb:#{encoding}", options) do |csv|
+ csv.each_with_index do |row, i|
+ assert_equal(fields[i], row)
+ end
+ end
+ begin
+ CSV.open(@temp_csv_path, "rb:#{encoding}:#{__ENCODING__}", options) do |csv|
+ csv.each_with_index do |row, i|
+ assert_equal(orig_fields[i], row)
+ end
+ end unless encoding == __ENCODING__
+ rescue Encoding::ConverterNotFoundError
+ end
+ options[:encoding] = encoding.name
+ CSV.open(@temp_csv_path, options) do |csv|
+ csv.each_with_index do |row, i|
+ assert_equal(fields[i], row)
+ end
+ end
+ options.delete(:encoding)
+ options[:external_encoding] = encoding.name
+ options[:internal_encoding] = __ENCODING__.name
+ begin
+ CSV.open(@temp_csv_path, options) do |csv|
+ csv.each_with_index do |row, i|
+ assert_equal(orig_fields[i], row)
+ end
+ end unless encoding == __ENCODING__
+ rescue Encoding::ConverterNotFoundError
+ end
+ end
+
+ def encode_ary(ary, encoding)
+ ary.map { |row| row.map { |field| field.encode(encoding) } }
+ end
+
+ def ary_to_data(ary, options = { })
+ encoding = ary.flatten.first.encoding
+ quote_char = (options[:quote_char] || '"').encode(encoding)
+ col_sep = (options[:col_sep] || ",").encode(encoding)
+ row_sep = (options[:row_sep] || "\n").encode(encoding)
+ ary.map { |row|
+ row.map { |field|
+ [quote_char, field.encode(encoding), quote_char].join('')
+ }.join(col_sep) + row_sep
+ }.join('').encode(encoding)
+ end
+
+ def encode_for_tests(data, options = { })
+ yield ary_to_data(encode_ary(data, "UTF-8"), options)
+ yield ary_to_data(encode_ary(data, "UTF-16BE"), options)
+ end
+
+ def each_encoding
+ Encoding.list.each do |encoding|
+ next if encoding.dummy? # skip "dummy" encodings
+ yield encoding
+ end
+ end
+
+ def no_warnings
+ old_verbose, $VERBOSE = $VERBOSE, nil
+ yield
+ ensure
+ $VERBOSE = old_verbose
+ end
+end
diff --git a/jni/ruby/test/csv/test_features.rb b/jni/ruby/test/csv/test_features.rb
new file mode 100755
index 0000000..e314657
--- /dev/null
+++ b/jni/ruby/test/csv/test_features.rb
@@ -0,0 +1,327 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_features.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+begin
+ require "zlib"
+rescue LoadError
+end
+
+require_relative "base"
+require "tempfile"
+
+class TestCSV::Features < TestCSV
+ extend DifferentOFS
+
+ TEST_CASES = [ [%Q{a,b}, ["a", "b"]],
+ [%Q{a,"""b"""}, ["a", "\"b\""]],
+ [%Q{a,"""b"}, ["a", "\"b"]],
+ [%Q{a,"b"""}, ["a", "b\""]],
+ [%Q{a,"\nb"""}, ["a", "\nb\""]],
+ [%Q{a,"""\nb"}, ["a", "\"\nb"]],
+ [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]],
+ [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]],
+ [%Q{a,,,}, ["a", nil, nil, nil]],
+ [%Q{,}, [nil, nil]],
+ [%Q{"",""}, ["", ""]],
+ [%Q{""""}, ["\""]],
+ [%Q{"""",""}, ["\"",""]],
+ [%Q{,""}, [nil,""]],
+ [%Q{,"\r"}, [nil,"\r"]],
+ [%Q{"\r\n,"}, ["\r\n,"]],
+ [%Q{"\r\n,",}, ["\r\n,", nil]] ]
+
+ def setup
+ super
+ @sample_data = <<-END_DATA.gsub(/^ +/, "")
+ line,1,abc
+ line,2,"def\nghi"
+
+ line,4,jkl
+ END_DATA
+ @csv = CSV.new(@sample_data)
+ end
+
+ def test_col_sep
+ [";", "\t"].each do |sep|
+ TEST_CASES.each do |test_case|
+ assert_equal( test_case.last.map { |t| t.tr(",", sep) unless t.nil? },
+ CSV.parse_line( test_case.first.tr(",", sep),
+ col_sep: sep ) )
+ end
+ end
+ assert_equal([",,,", nil], CSV.parse_line(",,,;", col_sep: ";"))
+ end
+
+ def test_row_sep
+ assert_raise(CSV::MalformedCSVError) do
+ CSV.parse_line("1,2,3\n,4,5\r\n", row_sep: "\r\n")
+ end
+ assert_equal( ["1", "2", "3\n", "4", "5"],
+ CSV.parse_line(%Q{1,2,"3\n",4,5\r\n}, row_sep: "\r\n"))
+ end
+
+ def test_quote_char
+ TEST_CASES.each do |test_case|
+ assert_equal( test_case.last.map { |t| t.tr('"', "'") unless t.nil? },
+ CSV.parse_line( test_case.first.tr('"', "'"),
+ quote_char: "'" ) )
+ end
+ end
+
+ def test_bug_8405
+ TEST_CASES.each do |test_case|
+ assert_equal( test_case.last.map { |t| t.tr('"', "|") unless t.nil? },
+ CSV.parse_line( test_case.first.tr('"', "|"),
+ quote_char: "|" ) )
+ end
+ end
+
+ def test_csv_char_readers
+ %w[col_sep row_sep quote_char].each do |reader|
+ csv = CSV.new("abc,def", reader.to_sym => "|")
+ assert_equal("|", csv.send(reader))
+ end
+ end
+
+ def test_row_sep_auto_discovery
+ ["\r\n", "\n", "\r"].each do |line_end|
+ data = "1,2,3#{line_end}4,5#{line_end}"
+ discovered = CSV.new(data).row_sep
+ assert_equal(line_end, discovered)
+ end
+
+ assert_equal("\n", CSV.new("\n\r\n\r").row_sep)
+
+ assert_equal($/, CSV.new("").row_sep)
+
+ assert_equal($/, CSV.new(STDERR).row_sep)
+ end
+
+ def test_lineno
+ assert_equal(5, @sample_data.lines.to_a.size)
+
+ 4.times do |line_count|
+ assert_equal(line_count, @csv.lineno)
+ assert_not_nil(@csv.shift)
+ assert_equal(line_count + 1, @csv.lineno)
+ end
+ assert_nil(@csv.shift)
+ end
+
+ def test_readline
+ test_lineno
+
+ @csv.rewind
+
+ test_lineno
+ end
+
+ def test_unknown_options
+ assert_raise_with_message(ArgumentError, /unknown/) {
+ CSV.new(@sample_data, unknown: :error)
+ }
+ end
+
+ def test_skip_blanks
+ assert_equal(4, @csv.to_a.size)
+
+ @csv = CSV.new(@sample_data, skip_blanks: true)
+
+ count = 0
+ @csv.each do |row|
+ count += 1
+ assert_equal("line", row.first)
+ end
+ assert_equal(3, count)
+ end
+
+ def test_csv_behavior_readers
+ %w[ unconverted_fields return_headers write_headers
+ skip_blanks force_quotes ].each do |behavior|
+ assert_not_predicate(CSV.new("abc,def"), "#{behavior}?", "Behavior defaulted to on.")
+ csv = CSV.new("abc,def", behavior.to_sym => true)
+ assert_predicate(csv, "#{behavior}?", "Behavior change now registered.")
+ end
+ end
+
+ def test_converters_reader
+ # no change
+ assert_equal( [:integer],
+ CSV.new("abc,def", converters: [:integer]).converters )
+
+ # just one
+ assert_equal( [:integer],
+ CSV.new("abc,def", converters: :integer).converters )
+
+ # expanded
+ assert_equal( [:integer, :float],
+ CSV.new("abc,def", converters: :numeric).converters )
+
+ # custom
+ csv = CSV.new("abc,def", converters: [:integer, lambda { }])
+ assert_equal(2, csv.converters.size)
+ assert_equal(:integer, csv.converters.first)
+ assert_instance_of(Proc, csv.converters.last)
+ end
+
+ def test_header_converters_reader
+ # no change
+ hc = :header_converters
+ assert_equal([:downcase], CSV.new("abc,def", hc => [:downcase]).send(hc))
+
+ # just one
+ assert_equal([:downcase], CSV.new("abc,def", hc => :downcase).send(hc))
+
+ # custom
+ csv = CSV.new("abc,def", hc => [:symbol, lambda { }])
+ assert_equal(2, csv.send(hc).size)
+ assert_equal(:symbol, csv.send(hc).first)
+ assert_instance_of(Proc, csv.send(hc).last)
+ end
+
+ # reported by Kev Jackson
+ def test_failing_to_escape_col_sep_bug_fix
+ assert_nothing_raised(Exception) { CSV.new(String.new, col_sep: "|") }
+ end
+
+ # reported by Chris Roos
+ def test_failing_to_reset_headers_in_rewind_bug_fix
+ csv = CSV.new("forename,surname", headers: true, return_headers: true)
+ csv.each {|row| assert_predicate row, :header_row?}
+ csv.rewind
+ csv.each {|row| assert_predicate row, :header_row?}
+ end
+
+ # reported by Dave Burt
+ def test_leading_empty_fields_with_multibyte_col_sep_bug_fix
+ data = <<-END_DATA.gsub(/^\s+/, "")
+ <=><=>A<=>B<=>C
+ 1<=>2<=>3
+ END_DATA
+ parsed = CSV.parse(data, col_sep: "<=>")
+ assert_equal([[nil, nil, "A", "B", "C"], ["1", "2", "3"]], parsed)
+ end
+
+ def test_gzip_reader_bug_fix
+ zipped = nil
+ assert_nothing_raised(NoMethodError) do
+ zipped = CSV.new(
+ Zlib::GzipReader.open(
+ File.join(File.dirname(__FILE__), "line_endings.gz")
+ )
+ )
+ end
+ assert_equal("\r\n", zipped.row_sep)
+ ensure
+ zipped.close
+ end if defined?(Zlib::GzipReader)
+
+ def test_gzip_writer_bug_fix
+ Tempfile.create(%w"temp .gz") {|tempfile|
+ tempfile.close
+ file = tempfile.path
+ zipped = nil
+ assert_nothing_raised(NoMethodError) do
+ zipped = CSV.new(Zlib::GzipWriter.open(file))
+ end
+ zipped << %w[one two three]
+ zipped << [1, 2, 3]
+ zipped.close
+
+ assert_include(Zlib::GzipReader.open(file) {|f| f.read},
+ $INPUT_RECORD_SEPARATOR, "@row_sep did not default")
+ }
+ end if defined?(Zlib::GzipWriter)
+
+ def test_inspect_is_smart_about_io_types
+ str = CSV.new("string,data").inspect
+ assert_include(str, "io_type:StringIO", "IO type not detected.")
+
+ str = CSV.new($stderr).inspect
+ assert_include(str, "io_type:$stderr", "IO type not detected.")
+
+ Tempfile.create(%w"temp .csv") {|tempfile|
+ tempfile.close
+ path = tempfile.path
+ File.open(path, "w") { |csv| csv << "one,two,three\n1,2,3\n" }
+ str = CSV.open(path) { |csv| csv.inspect }
+ assert_include(str, "io_type:File", "IO type not detected.")
+ }
+ end
+
+ def test_inspect_shows_key_attributes
+ str = @csv.inspect
+ %w[lineno col_sep row_sep quote_char].each do |attr_name|
+ assert_match(/\b#{attr_name}:[^\s>]+/, str)
+ end
+ end
+
+ def test_inspect_shows_headers_when_available
+ CSV.new("one,two,three\n1,2,3\n", headers: true) do |csv|
+ assert_include(csv.inspect, "headers:true", "Header hint not shown.")
+ csv.shift # load headers
+ assert_match(/headers:\[[^\]]+\]/, csv.inspect)
+ end
+ end
+
+ def test_inspect_encoding_is_ascii_compatible
+ CSV.new("one,two,three\n1,2,3\n".encode("UTF-16BE")) do |csv|
+ assert_send([Encoding, :compatible?,
+ Encoding.find("US-ASCII"), csv.inspect.encoding],
+ "inspect() was not ASCII compatible.")
+ end
+ end
+
+ def test_version
+ assert_not_nil(CSV::VERSION)
+ assert_instance_of(String, CSV::VERSION)
+ assert_predicate(CSV::VERSION, :frozen?)
+ assert_match(/\A\d\.\d\.\d\Z/, CSV::VERSION)
+ end
+
+ def test_accepts_comment_skip_lines_option
+ assert_nothing_raised(ArgumentError) do
+ CSV.new(@sample_data, :skip_lines => /\A\s*#/)
+ end
+ end
+
+ def test_accepts_comment_defaults_to_nil
+ c = CSV.new(@sample_data)
+ assert_nil(c.skip_lines)
+ end
+
+ class RegexStub
+ end
+
+ def test_requires_skip_lines_to_call_match
+ regex_stub = RegexStub.new
+ assert_raise_with_message(ArgumentError, /skip_lines/) do
+ CSV.new(@sample_data, :skip_lines => regex_stub)
+ end
+ end
+
+ def test_comment_rows_are_ignored
+ sample_data = "line,1,a\n#not,a,line\nline,2,b\n #also,no,line"
+ c = CSV.new sample_data, :skip_lines => /\A\s*#/
+ assert_equal [["line", "1", "a"], ["line", "2", "b"]], c.each.to_a
+ end
+
+ def test_quoted_skip_line_markers_are_ignored
+ sample_data = "line,1,a\n\"#not\",a,line\nline,2,b"
+ c = CSV.new sample_data, :skip_lines => /\A\s*#/
+ assert_equal [["line", "1", "a"], ["#not", "a", "line"], ["line", "2", "b"]], c.each.to_a
+ end
+
+ def test_string_works_like_a_regexp
+ sample_data = "line,1,a\n#(not,a,line\nline,2,b\n also,#no,line"
+ c = CSV.new sample_data, :skip_lines => "#"
+ assert_equal [["line", "1", "a"], ["line", "2", "b"]], c.each.to_a
+ end
+
+end
diff --git a/jni/ruby/test/csv/test_headers.rb b/jni/ruby/test/csv/test_headers.rb
new file mode 100755
index 0000000..79ccd20
--- /dev/null
+++ b/jni/ruby/test/csv/test_headers.rb
@@ -0,0 +1,297 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_headers.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require_relative "base"
+
+class TestCSV::Headers < TestCSV
+ extend DifferentOFS
+
+ def setup
+ super
+ @data = <<-END_CSV.gsub(/^\s+/, "")
+ first,second,third
+ A,B,C
+ 1,2,3
+ END_CSV
+ end
+
+ def test_first_row
+ [:first_row, true].each do |setting| # two names for the same setting
+ # activate headers
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: setting)
+ end
+
+ # first data row - skipping headers
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a)
+
+ # second data row
+ row = csv[1]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a)
+
+ # empty
+ assert_nil(csv[2])
+ end
+ end
+
+ def test_array_of_headers
+ # activate headers
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: [:my, :new, :headers])
+ end
+
+ # first data row - skipping headers
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal( [[:my, "first"], [:new, "second"], [:headers, "third"]],
+ row.to_a )
+
+ # second data row
+ row = csv[1]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([[:my, "A"], [:new, "B"], [:headers, "C"]], row.to_a)
+
+ # third data row
+ row = csv[2]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([[:my, "1"], [:new, "2"], [:headers, "3"]], row.to_a)
+
+ # empty
+ assert_nil(csv[3])
+
+ # with return and convert
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse( @data, headers: [:my, :new, :headers],
+ return_headers: true,
+ header_converters: lambda { |h| h.to_s } )
+ end
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([["my", :my], ["new", :new], ["headers", :headers]], row.to_a)
+ assert_predicate(row, :header_row?)
+ assert_not_predicate(row, :field_row?)
+ end
+
+ def test_csv_header_string
+ # activate headers
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: "my,new,headers")
+ end
+
+ # first data row - skipping headers
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
+
+ # second data row
+ row = csv[1]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{my A}, %w{new B}, %w{headers C}], row.to_a)
+
+ # third data row
+ row = csv[2]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{my 1}, %w{new 2}, %w{headers 3}], row.to_a)
+
+ # empty
+ assert_nil(csv[3])
+
+ # with return and convert
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse( @data, headers: "my,new,headers",
+ return_headers: true,
+ header_converters: :symbol )
+ end
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([[:my, "my"], [:new, "new"], [:headers, "headers"]], row.to_a)
+ assert_predicate(row, :header_row?)
+ assert_not_predicate(row, :field_row?)
+ end
+
+ def test_csv_header_string_inherits_separators
+ # parse with custom col_sep
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse( @data.tr(",", "|"), col_sep: "|",
+ headers: "my|new|headers" )
+ end
+
+ # verify headers were recognized
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
+ end
+
+ def test_return_headers
+ # activate headers and request they are returned
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: true, return_headers: true)
+ end
+
+ # header row
+ row = csv[0]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal( [%w{first first}, %w{second second}, %w{third third}],
+ row.to_a )
+ assert_predicate(row, :header_row?)
+ assert_not_predicate(row, :field_row?)
+
+ # first data row - skipping headers
+ row = csv[1]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a)
+ assert_not_predicate(row, :header_row?)
+ assert_predicate(row, :field_row?)
+
+ # second data row
+ row = csv[2]
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a)
+ assert_not_predicate(row, :header_row?)
+ assert_predicate(row, :field_row?)
+
+ # empty
+ assert_nil(csv[3])
+ end
+
+ def test_converters
+ # create test data where headers and fields look alike
+ data = <<-END_MATCHING_CSV.gsub(/^\s+/, "")
+ 1,2,3
+ 1,2,3
+ END_MATCHING_CSV
+
+ # normal converters do not affect headers
+ csv = CSV.parse( data, headers: true,
+ return_headers: true,
+ converters: :numeric )
+ assert_equal([%w{1 1}, %w{2 2}, %w{3 3}], csv[0].to_a)
+ assert_equal([["1", 1], ["2", 2], ["3", 3]], csv[1].to_a)
+ assert_nil(csv[2])
+
+ # header converters do affect headers (only)
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse( data, headers: true,
+ return_headers: true,
+ converters: :numeric,
+ header_converters: :symbol )
+ end
+ assert_equal([[:"1", "1"], [:"2", "2"], [:"3", "3"]], csv[0].to_a)
+ assert_equal([[:"1", 1], [:"2", 2], [:"3", 3]], csv[1].to_a)
+ assert_nil(csv[2])
+ end
+
+ def test_builtin_downcase_converter
+ csv = CSV.parse( "One,TWO Three", headers: true,
+ return_headers: true,
+ header_converters: :downcase )
+ assert_equal(%w{one two\ three}, csv.headers)
+ end
+
+ def test_builtin_symbol_converter
+ # Note that the trailing space is intentional
+ csv = CSV.parse( "One,TWO Three ", headers: true,
+ return_headers: true,
+ header_converters: :symbol )
+ assert_equal([:one, :two_three], csv.headers)
+ end
+
+ def test_builtin_converters_with_blank_header
+ csv = CSV.parse( "one,,three", headers: true,
+ return_headers: true,
+ header_converters: [:downcase, :symbol] )
+ assert_equal([:one, nil, :three], csv.headers)
+ end
+
+ def test_custom_converter
+ converter = lambda { |header| header.tr(" ", "_") }
+ csv = CSV.parse( "One,TWO Three",
+ headers: true,
+ return_headers: true,
+ header_converters: converter )
+ assert_equal(%w{One TWO_Three}, csv.headers)
+ end
+
+ def test_table_support
+ csv = nil
+ assert_nothing_raised(Exception) do
+ csv = CSV.parse(@data, headers: true)
+ end
+
+ assert_instance_of(CSV::Table, csv)
+ end
+
+ def test_skip_blanks
+ @data = <<-END_CSV.gsub(/^ +/, "")
+
+
+ A,B,C
+
+ 1,2,3
+
+
+
+ END_CSV
+
+ expected = [%w[1 2 3]]
+ CSV.parse(@data, headers: true, skip_blanks: true) do |row|
+ assert_equal(expected.shift, row.fields)
+ end
+
+ expected = [%w[A B C], %w[1 2 3]]
+ CSV.parse( @data,
+ headers: true,
+ return_headers: true,
+ skip_blanks: true ) do |row|
+ assert_equal(expected.shift, row.fields)
+ end
+ end
+
+ def test_headers_reader
+ # no headers
+ assert_nil(CSV.new(@data).headers)
+
+ # headers
+ csv = CSV.new(@data, headers: true)
+ assert_equal(true, csv.headers) # before headers are read
+ csv.shift # set headers
+ assert_equal(%w[first second third], csv.headers) # after headers are read
+ end
+
+ def test_blank_row_bug_fix
+ @data += "\n#{@data}" # add a blank row
+
+ # ensure that everything returned is a Row object
+ CSV.parse(@data, headers: true) do |row|
+ assert_instance_of(CSV::Row, row)
+ end
+ end
+end
diff --git a/jni/ruby/test/csv/test_interface.rb b/jni/ruby/test/csv/test_interface.rb
new file mode 100755
index 0000000..d6bf470
--- /dev/null
+++ b/jni/ruby/test/csv/test_interface.rb
@@ -0,0 +1,368 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_interface.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require_relative "base"
+require "tempfile"
+
+class TestCSV::Interface < TestCSV
+ extend DifferentOFS
+
+ def setup
+ super
+ @tempfile = Tempfile.new(%w"temp .csv")
+ @tempfile.close
+ @path = @tempfile.path
+
+ File.open(@path, "wb") do |file|
+ file << "1\t2\t3\r\n"
+ file << "4\t5\r\n"
+ end
+
+ @expected = [%w{1 2 3}, %w{4 5}]
+ end
+
+ def teardown
+ @tempfile.close(true)
+ super
+ end
+
+ ### Test Read Interface ###
+
+ def test_foreach
+ CSV.foreach(@path, col_sep: "\t", row_sep: "\r\n") do |row|
+ assert_equal(@expected.shift, row)
+ end
+ end
+
+ def test_foreach_enum
+ CSV.foreach(@path, col_sep: "\t", row_sep: "\r\n").zip(@expected) do |row, exp|
+ assert_equal(exp, row)
+ end
+ end
+
+ def test_open_and_close
+ csv = CSV.open(@path, "r+", col_sep: "\t", row_sep: "\r\n")
+ assert_not_nil(csv)
+ assert_instance_of(CSV, csv)
+ assert_not_predicate(csv, :closed?)
+ csv.close
+ assert_predicate(csv, :closed?)
+
+ ret = CSV.open(@path) do |new_csv|
+ csv = new_csv
+ assert_instance_of(CSV, new_csv)
+ "Return value."
+ end
+ assert_predicate(csv, :closed?)
+ assert_equal("Return value.", ret)
+ end
+
+ def test_parse
+ data = File.binread(@path)
+ assert_equal( @expected,
+ CSV.parse(data, col_sep: "\t", row_sep: "\r\n") )
+
+ CSV.parse(data, col_sep: "\t", row_sep: "\r\n") do |row|
+ assert_equal(@expected.shift, row)
+ end
+ end
+
+ def test_parse_line
+ row = CSV.parse_line("1;2;3", col_sep: ";")
+ assert_not_nil(row)
+ assert_instance_of(Array, row)
+ assert_equal(%w{1 2 3}, row)
+
+ # shortcut interface
+ row = "1;2;3".parse_csv(col_sep: ";")
+ assert_not_nil(row)
+ assert_instance_of(Array, row)
+ assert_equal(%w{1 2 3}, row)
+ end
+
+ def test_parse_line_with_empty_lines
+ assert_equal(nil, CSV.parse_line("")) # to signal eof
+ assert_equal(Array.new, CSV.parse_line("\n1,2,3"))
+ end
+
+ def test_read_and_readlines
+ assert_equal( @expected,
+ CSV.read(@path, col_sep: "\t", row_sep: "\r\n") )
+ assert_equal( @expected,
+ CSV.readlines(@path, col_sep: "\t", row_sep: "\r\n") )
+
+
+ data = CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ csv.read
+ end
+ assert_equal(@expected, data)
+ data = CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ csv.readlines
+ end
+ assert_equal(@expected, data)
+ end
+
+ def test_table
+ table = CSV.table(@path, col_sep: "\t", row_sep: "\r\n")
+ assert_instance_of(CSV::Table, table)
+ assert_equal([[:"1", :"2", :"3"], [4, 5, nil]], table.to_a)
+ end
+
+ def test_shift # aliased as gets() and readline()
+ CSV.open(@path, "rb+", col_sep: "\t", row_sep: "\r\n") do |csv|
+ assert_equal(@expected.shift, csv.shift)
+ assert_equal(@expected.shift, csv.shift)
+ assert_equal(nil, csv.shift)
+ end
+ end
+
+ def test_enumerators_are_supported
+ CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
+ enum = csv.each
+ assert_instance_of(Enumerator, enum)
+ assert_equal(@expected.shift, enum.next)
+ end
+ end
+
+ def test_nil_is_not_acceptable
+ assert_raise_with_message ArgumentError, "Cannot parse nil as CSV" do
+ CSV.new(nil)
+ end
+ end
+
+ ### Test Write Interface ###
+
+ def test_generate
+ str = CSV.generate do |csv| # default empty String
+ assert_instance_of(CSV, csv)
+ assert_equal(csv, csv << [1, 2, 3])
+ assert_equal(csv, csv << [4, nil, 5])
+ end
+ assert_not_nil(str)
+ assert_instance_of(String, str)
+ assert_equal("1,2,3\n4,,5\n", str)
+
+ CSV.generate(str) do |csv| # appending to a String
+ assert_equal(csv, csv << ["last", %Q{"row"}])
+ end
+ assert_equal(%Q{1,2,3\n4,,5\nlast,"""row"""\n}, str)
+ end
+
+ def test_generate_line
+ line = CSV.generate_line(%w{1 2 3}, col_sep: ";")
+ assert_not_nil(line)
+ assert_instance_of(String, line)
+ assert_equal("1;2;3\n", line)
+
+ # shortcut interface
+ line = %w{1 2 3}.to_csv(col_sep: ";")
+ assert_not_nil(line)
+ assert_instance_of(String, line)
+ assert_equal("1;2;3\n", line)
+ end
+
+ def test_write_header_detection
+ File.unlink(@path)
+
+ headers = %w{a b c}
+ CSV.open(@path, "w", headers: true) do |csv|
+ csv << headers
+ csv << %w{1 2 3}
+ assert_equal(headers, csv.instance_variable_get(:@headers))
+ end
+ end
+
+ def test_write_lineno
+ File.unlink(@path)
+
+ CSV.open(@path, "w") do |csv|
+ lines = 20
+ lines.times { csv << %w{a b c} }
+ assert_equal(lines, csv.lineno)
+ end
+ end
+
+ def test_write_hash
+ File.unlink(@path)
+
+ lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}]
+ CSV.open( @path, "wb", headers: true,
+ header_converters: :symbol ) do |csv|
+ csv << lines.first.keys
+ lines.each { |line| csv << line }
+ end
+ CSV.open( @path, "rb", headers: true,
+ converters: :all,
+ header_converters: :symbol ) do |csv|
+ csv.each { |line| assert_equal(lines.shift, line.to_hash) }
+ end
+ end
+
+ def test_write_hash_with_string_keys
+ File.unlink(@path)
+
+ lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}]
+ CSV.open( @path, "wb", headers: true ) do |csv|
+ csv << lines.first.keys
+ lines.each { |line| csv << line }
+ end
+ CSV.open( @path, "rb", headers: true ) do |csv|
+ csv.each do |line|
+ csv.headers.each_with_index do |header, h|
+ keys = line.to_hash.keys
+ assert_instance_of(String, keys[h])
+ assert_same(header, keys[h])
+ end
+ end
+ end
+ end
+
+ def test_write_hash_with_headers_array
+ File.unlink(@path)
+
+ lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}]
+ CSV.open(@path, "wb", headers: [:b, :a, :c]) do |csv|
+ lines.each { |line| csv << line }
+ end
+
+ # test writing fields in the correct order
+ File.open(@path, "rb") do |f|
+ assert_equal("2,1,3", f.gets.strip)
+ assert_equal("5,4,6", f.gets.strip)
+ end
+
+ # test reading CSV with headers
+ CSV.open( @path, "rb", headers: [:b, :a, :c],
+ converters: :all ) do |csv|
+ csv.each { |line| assert_equal(lines.shift, line.to_hash) }
+ end
+ end
+
+ def test_write_hash_with_headers_string
+ File.unlink(@path)
+
+ lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}]
+ CSV.open(@path, "wb", headers: "b|a|c", col_sep: "|") do |csv|
+ lines.each { |line| csv << line }
+ end
+
+ # test writing fields in the correct order
+ File.open(@path, "rb") do |f|
+ assert_equal("2|1|3", f.gets.strip)
+ assert_equal("5|4|6", f.gets.strip)
+ end
+
+ # test reading CSV with headers
+ CSV.open( @path, "rb", headers: "b|a|c",
+ col_sep: "|",
+ converters: :all ) do |csv|
+ csv.each { |line| assert_equal(lines.shift, line.to_hash) }
+ end
+ end
+
+ def test_write_headers
+ File.unlink(@path)
+
+ lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}]
+ CSV.open( @path, "wb", headers: "b|a|c",
+ write_headers: true,
+ col_sep: "|" ) do |csv|
+ lines.each { |line| csv << line }
+ end
+
+ # test writing fields in the correct order
+ File.open(@path, "rb") do |f|
+ assert_equal("b|a|c", f.gets.strip)
+ assert_equal("2|1|3", f.gets.strip)
+ assert_equal("5|4|6", f.gets.strip)
+ end
+
+ # test reading CSV with headers
+ CSV.open( @path, "rb", headers: true,
+ col_sep: "|",
+ converters: :all ) do |csv|
+ csv.each { |line| assert_equal(lines.shift, line.to_hash) }
+ end
+ end
+
+ def test_append # aliased add_row() and puts()
+ File.unlink(@path)
+
+ CSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv|
+ @expected.each { |row| csv << row }
+ end
+
+ test_shift
+
+ # same thing using CSV::Row objects
+ File.unlink(@path)
+
+ CSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv|
+ @expected.each { |row| csv << CSV::Row.new(Array.new, row) }
+ end
+
+ test_shift
+ end
+
+ ### Test Read and Write Interface ###
+
+ def test_filter
+ assert_respond_to(CSV, :filter)
+
+ expected = [[1, 2, 3], [4, 5]]
+ CSV.filter( "1;2;3\n4;5\n", (result = String.new),
+ in_col_sep: ";", out_col_sep: ",",
+ converters: :all ) do |row|
+ assert_equal(row, expected.shift)
+ row.map! { |n| n * 2 }
+ row << "Added\r"
+ end
+ assert_equal("2,4,6,\"Added\r\"\n8,10,\"Added\r\"\n", result)
+ end
+
+ def test_instance
+ csv = String.new
+
+ first = nil
+ assert_nothing_raised(Exception) do
+ first = CSV.instance(csv, col_sep: ";")
+ first << %w{a b c}
+ end
+
+ assert_equal("a;b;c\n", csv)
+
+ second = nil
+ assert_nothing_raised(Exception) do
+ second = CSV.instance(csv, col_sep: ";")
+ second << [1, 2, 3]
+ end
+
+ assert_equal(first.object_id, second.object_id)
+ assert_equal("a;b;c\n1;2;3\n", csv)
+
+ # shortcuts
+ assert_equal(STDOUT, CSV.instance.instance_eval { @io })
+ assert_equal(STDOUT, CSV { |new_csv| new_csv.instance_eval { @io } })
+ end
+
+ def test_options_are_not_modified
+ opt = {}.freeze
+ assert_nothing_raised { CSV.foreach(@path, opt) }
+ assert_nothing_raised { CSV.open(@path, opt){} }
+ assert_nothing_raised { CSV.parse("", opt) }
+ assert_nothing_raised { CSV.parse_line("", opt) }
+ assert_nothing_raised { CSV.read(@path, opt) }
+ assert_nothing_raised { CSV.readlines(@path, opt) }
+ assert_nothing_raised { CSV.table(@path, opt) }
+ assert_nothing_raised { CSV.generate(opt){} }
+ assert_nothing_raised { CSV.generate_line([], opt) }
+ assert_nothing_raised { CSV.filter("", "", opt){} }
+ assert_nothing_raised { CSV.instance("", opt) }
+ end
+end
diff --git a/jni/ruby/test/csv/test_row.rb b/jni/ruby/test/csv/test_row.rb
new file mode 100755
index 0000000..3acceea
--- /dev/null
+++ b/jni/ruby/test/csv/test_row.rb
@@ -0,0 +1,355 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_row.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require_relative "base"
+
+class TestCSV::Row < TestCSV
+ extend DifferentOFS
+
+ def setup
+ super
+ @row = CSV::Row.new(%w{A B C A A}, [1, 2, 3, 4])
+ end
+
+ def test_initialize
+ # basic
+ row = CSV::Row.new(%w{A B C}, [1, 2, 3])
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([["A", 1], ["B", 2], ["C", 3]], row.to_a)
+
+ # missing headers
+ row = CSV::Row.new(%w{A}, [1, 2, 3])
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([["A", 1], [nil, 2], [nil, 3]], row.to_a)
+
+ # missing fields
+ row = CSV::Row.new(%w{A B C}, [1, 2])
+ assert_not_nil(row)
+ assert_instance_of(CSV::Row, row)
+ assert_equal([["A", 1], ["B", 2], ["C", nil]], row.to_a)
+ end
+
+ def test_row_type
+ # field rows
+ row = CSV::Row.new(%w{A B C}, [1, 2, 3]) # implicit
+ assert_not_predicate(row, :header_row?)
+ assert_predicate(row, :field_row?)
+ row = CSV::Row.new(%w{A B C}, [1, 2, 3], false) # explicit
+ assert_not_predicate(row, :header_row?)
+ assert_predicate(row, :field_row?)
+
+ # header row
+ row = CSV::Row.new(%w{A B C}, [1, 2, 3], true)
+ assert_predicate(row, :header_row?)
+ assert_not_predicate(row, :field_row?)
+ end
+
+ def test_headers
+ assert_equal(%w{A B C A A}, @row.headers)
+ end
+
+ def test_field
+ # by name
+ assert_equal(2, @row.field("B"))
+ assert_equal(2, @row["B"]) # alias
+
+ # by index
+ assert_equal(3, @row.field(2))
+
+ # missing
+ assert_nil(@row.field("Missing"))
+ assert_nil(@row.field(10))
+
+ # minimum index
+ assert_equal(1, @row.field("A"))
+ assert_equal(1, @row.field("A", 0))
+ assert_equal(4, @row.field("A", 1))
+ assert_equal(4, @row.field("A", 2))
+ assert_equal(4, @row.field("A", 3))
+ assert_equal(nil, @row.field("A", 4))
+ assert_equal(nil, @row.field("A", 5))
+ end
+
+ def test_fetch
+ # only by name
+ assert_equal(2, @row.fetch('B'))
+
+ # missing header raises KeyError
+ assert_raise KeyError do
+ @row.fetch('foo')
+ end
+
+ # missing header yields itself to block
+ assert_equal 'bar', @row.fetch('foo') { |header|
+ header == 'foo' ? 'bar' : false }
+
+ # missing header returns the given default value
+ assert_equal 'bar', @row.fetch('foo', 'bar')
+
+ # more than one vararg raises ArgumentError
+ assert_raise ArgumentError do
+ @row.fetch('foo', 'bar', 'baz')
+ end
+ end
+
+ def test_has_key?
+ assert_equal(true, @row.has_key?('B'))
+ assert_equal(false, @row.has_key?('foo'))
+ end
+
+ def test_set_field
+ # set field by name
+ assert_equal(100, @row["A"] = 100)
+
+ # set field by index
+ assert_equal(300, @row[3] = 300)
+
+ # set field by name and minimum index
+ assert_equal([:a, :b, :c], @row["A", 4] = [:a, :b, :c])
+
+ # verify the changes
+ assert_equal( [ ["A", 100],
+ ["B", 2],
+ ["C", 3],
+ ["A", 300],
+ ["A", [:a, :b, :c]] ], @row.to_a )
+
+ # assigning an index past the end
+ assert_equal("End", @row[10] = "End")
+ assert_equal( [ ["A", 100],
+ ["B", 2],
+ ["C", 3],
+ ["A", 300],
+ ["A", [:a, :b, :c]],
+ [nil, nil],
+ [nil, nil],
+ [nil, nil],
+ [nil, nil],
+ [nil, nil],
+ [nil, "End"] ], @row.to_a )
+
+ # assigning a new field by header
+ assert_equal("New", @row[:new] = "New")
+ assert_equal( [ ["A", 100],
+ ["B", 2],
+ ["C", 3],
+ ["A", 300],
+ ["A", [:a, :b, :c]],
+ [nil, nil],
+ [nil, nil],
+ [nil, nil],
+ [nil, nil],
+ [nil, nil],
+ [nil, "End"],
+ [:new, "New"] ], @row.to_a )
+ end
+
+ def test_append
+ # add a value
+ assert_equal(@row, @row << "Value")
+ assert_equal( [ ["A", 1],
+ ["B", 2],
+ ["C", 3],
+ ["A", 4],
+ ["A", nil],
+ [nil, "Value"] ], @row.to_a )
+
+ # add a pair
+ assert_equal(@row, @row << %w{Header Field})
+ assert_equal( [ ["A", 1],
+ ["B", 2],
+ ["C", 3],
+ ["A", 4],
+ ["A", nil],
+ [nil, "Value"],
+ %w{Header Field} ], @row.to_a )
+
+ # a pair with Hash syntax
+ assert_equal(@row, @row << {key: :value})
+ assert_equal( [ ["A", 1],
+ ["B", 2],
+ ["C", 3],
+ ["A", 4],
+ ["A", nil],
+ [nil, "Value"],
+ %w{Header Field},
+ [:key, :value] ], @row.to_a )
+
+ # multiple fields at once
+ assert_equal(@row, @row.push(100, 200, [:last, 300]))
+ assert_equal( [ ["A", 1],
+ ["B", 2],
+ ["C", 3],
+ ["A", 4],
+ ["A", nil],
+ [nil, "Value"],
+ %w{Header Field},
+ [:key, :value],
+ [nil, 100],
+ [nil, 200],
+ [:last, 300] ], @row.to_a )
+ end
+
+ def test_delete
+ # by index
+ assert_equal(["B", 2], @row.delete(1))
+
+ # by header
+ assert_equal(["C", 3], @row.delete("C"))
+
+ # using a block
+ assert_equal(@row, @row.delete_if { |h, f| h == "A" and not f.nil? })
+ assert_equal([["A", nil]], @row.to_a)
+ end
+
+ def test_fields
+ # all fields
+ assert_equal([1, 2, 3, 4, nil], @row.fields)
+
+ # by header
+ assert_equal([1, 3], @row.fields("A", "C"))
+
+ # by index
+ assert_equal([2, 3, nil], @row.fields(1, 2, 10))
+
+ # by both
+ assert_equal([2, 3, 4], @row.fields("B", "C", 3))
+
+ # with minimum indices
+ assert_equal([2, 3, 4], @row.fields("B", "C", ["A", 3]))
+
+ # by header range
+ assert_equal([2, 3], @row.values_at("B".."C"))
+ end
+
+ def test_index
+ # basic usage
+ assert_equal(0, @row.index("A"))
+ assert_equal(1, @row.index("B"))
+ assert_equal(2, @row.index("C"))
+ assert_equal(nil, @row.index("Z"))
+
+ # with minimum index
+ assert_equal(0, @row.index("A"))
+ assert_equal(0, @row.index("A", 0))
+ assert_equal(3, @row.index("A", 1))
+ assert_equal(3, @row.index("A", 2))
+ assert_equal(3, @row.index("A", 3))
+ assert_equal(4, @row.index("A", 4))
+ assert_equal(nil, @row.index("A", 5))
+ end
+
+ def test_queries
+ # headers
+ assert_send([@row, :header?, "A"])
+ assert_send([@row, :header?, "C"])
+ assert_not_send([@row, :header?, "Z"])
+ assert_send([@row, :include?, "A"]) # alias
+
+ # fields
+ assert(@row.field?(4))
+ assert(@row.field?(nil))
+ assert(!@row.field?(10))
+ end
+
+ def test_each
+ # array style
+ ary = @row.to_a
+ @row.each do |pair|
+ assert_equal(ary.first.first, pair.first)
+ assert_equal(ary.shift.last, pair.last)
+ end
+
+ # hash style
+ ary = @row.to_a
+ @row.each do |header, field|
+ assert_equal(ary.first.first, header)
+ assert_equal(ary.shift.last, field)
+ end
+
+ # verify that we can chain the call
+ assert_equal(@row, @row.each { })
+ end
+
+ def test_enumerable
+ assert_equal( [["A", 1], ["A", 4], ["A", nil]],
+ @row.select { |pair| pair.first == "A" } )
+
+ assert_equal(10, @row.inject(0) { |sum, (_, n)| sum + (n || 0) })
+ end
+
+ def test_to_a
+ row = CSV::Row.new(%w{A B C}, [1, 2, 3]).to_a
+ assert_instance_of(Array, row)
+ row.each do |pair|
+ assert_instance_of(Array, pair)
+ assert_equal(2, pair.size)
+ end
+ assert_equal([["A", 1], ["B", 2], ["C", 3]], row)
+ end
+
+ def test_to_hash
+ hash = @row.to_hash
+ assert_equal({"A" => nil, "B" => 2, "C" => 3}, hash)
+ hash.keys.each_with_index do |string_key, h|
+ assert_predicate(string_key, :frozen?)
+ assert_same(string_key, @row.headers[h])
+ end
+ end
+
+ def test_to_csv
+ # normal conversion
+ assert_equal("1,2,3,4,\n", @row.to_csv)
+ assert_equal("1,2,3,4,\n", @row.to_s) # alias
+
+ # with options
+ assert_equal( "1|2|3|4|\r\n",
+ @row.to_csv(col_sep: "|", row_sep: "\r\n") )
+ end
+
+ def test_array_delegation
+ assert_not_empty(@row, "Row was empty.")
+
+ assert_equal([@row.headers.size, @row.fields.size].max, @row.size)
+ end
+
+ def test_inspect_shows_header_field_pairs
+ str = @row.inspect
+ @row.each do |header, field|
+ assert_include(str, "#{header.inspect}:#{field.inspect}",
+ "Header field pair not found.")
+ end
+ end
+
+ def test_inspect_encoding_is_ascii_compatible
+ assert_send([Encoding, :compatible?,
+ Encoding.find("US-ASCII"),
+ @row.inspect.encoding],
+ "inspect() was not ASCII compatible.")
+ end
+
+ def test_inspect_shows_symbol_headers_as_bare_attributes
+ str = CSV::Row.new(@row.headers.map { |h| h.to_sym }, @row.fields).inspect
+ @row.each do |header, field|
+ assert_include(str, "#{header}:#{field.inspect}",
+ "Header field pair not found.")
+ end
+ end
+
+ def test_can_be_compared_with_other_classes
+ assert_not_nil(CSV::Row.new([ ], [ ]), "The row was nil")
+ end
+
+ def test_can_be_compared_when_not_a_row
+ r = @row == []
+ assert_equal false, r
+ end
+end
diff --git a/jni/ruby/test/csv/test_table.rb b/jni/ruby/test/csv/test_table.rb
new file mode 100755
index 0000000..44e9d4a
--- /dev/null
+++ b/jni/ruby/test/csv/test_table.rb
@@ -0,0 +1,434 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# tc_table.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require_relative "base"
+
+class TestCSV::Table < TestCSV
+ extend DifferentOFS
+
+ def setup
+ super
+ @rows = [ CSV::Row.new(%w{A B C}, [1, 2, 3]),
+ CSV::Row.new(%w{A B C}, [4, 5, 6]),
+ CSV::Row.new(%w{A B C}, [7, 8, 9]) ]
+ @table = CSV::Table.new(@rows)
+
+ @header_table = CSV::Table.new(
+ [CSV::Row.new(%w{A B C}, %w{A B C}, true)] + @rows
+ )
+ end
+
+ def test_initialze
+ assert_not_nil(@table)
+ assert_instance_of(CSV::Table, @table)
+ end
+
+ def test_modes
+ assert_equal(:col_or_row, @table.mode)
+
+ # non-destructive changes, intended for one shot calls
+ cols = @table.by_col
+ assert_equal(:col_or_row, @table.mode)
+ assert_equal(:col, cols.mode)
+ assert_equal(@table, cols)
+
+ rows = @table.by_row
+ assert_equal(:col_or_row, @table.mode)
+ assert_equal(:row, rows.mode)
+ assert_equal(@table, rows)
+
+ # destructive mode changing calls
+ assert_equal(@table, @table.by_row!)
+ assert_equal(:row, @table.mode)
+ assert_equal(@table, @table.by_col_or_row!)
+ assert_equal(:col_or_row, @table.mode)
+ end
+
+ def test_headers
+ assert_equal(@rows.first.headers, @table.headers)
+ end
+
+ def test_headers_empty
+ t = CSV::Table.new([])
+ assert_equal Array.new, t.headers
+ end
+
+ def test_index
+ ##################
+ ### Mixed Mode ###
+ ##################
+ # by row
+ @rows.each_index { |i| assert_equal(@rows[i], @table[i]) }
+ assert_equal(nil, @table[100]) # empty row
+
+ # by col
+ @rows.first.headers.each do |header|
+ assert_equal(@rows.map { |row| row[header] }, @table[header])
+ end
+ assert_equal([nil] * @rows.size, @table["Z"]) # empty col
+
+ # by cell, row then col
+ assert_equal(2, @table[0][1])
+ assert_equal(6, @table[1]["C"])
+
+ # by cell, col then row
+ assert_equal(5, @table["B"][1])
+ assert_equal(9, @table["C"][2])
+
+ # with headers (by col)
+ assert_equal(["B", 2, 5, 8], @header_table["B"])
+
+ ###################
+ ### Column Mode ###
+ ###################
+ @table.by_col!
+
+ assert_equal([2, 5, 8], @table[1])
+ assert_equal([2, 5, 8], @table["B"])
+
+ ################
+ ### Row Mode ###
+ ################
+ @table.by_row!
+
+ assert_equal(@rows[1], @table[1])
+ assert_raise(TypeError) { @table["B"] }
+
+ ############################
+ ### One Shot Mode Change ###
+ ############################
+ assert_equal(@rows[1], @table[1])
+ assert_equal([2, 5, 8], @table.by_col[1])
+ assert_equal(@rows[1], @table[1])
+ end
+
+ def test_set_row_or_column
+ ##################
+ ### Mixed Mode ###
+ ##################
+ # set row
+ @table[2] = [10, 11, 12]
+ assert_equal([%w[A B C], [1, 2, 3], [4, 5, 6], [10, 11, 12]], @table.to_a)
+
+ @table[3] = CSV::Row.new(%w[A B C], [13, 14, 15])
+ assert_equal( [%w[A B C], [1, 2, 3], [4, 5, 6], [10, 11, 12], [13, 14, 15]],
+ @table.to_a )
+
+ # set col
+ @table["Type"] = "data"
+ assert_equal( [ %w[A B C Type],
+ [1, 2, 3, "data"],
+ [4, 5, 6, "data"],
+ [10, 11, 12, "data"],
+ [13, 14, 15, "data"] ],
+ @table.to_a )
+
+ @table["Index"] = [1, 2, 3]
+ assert_equal( [ %w[A B C Type Index],
+ [1, 2, 3, "data", 1],
+ [4, 5, 6, "data", 2],
+ [10, 11, 12, "data", 3],
+ [13, 14, 15, "data", nil] ],
+ @table.to_a )
+
+ @table["B"] = [100, 200]
+ assert_equal( [ %w[A B C Type Index],
+ [1, 100, 3, "data", 1],
+ [4, 200, 6, "data", 2],
+ [10, nil, 12, "data", 3],
+ [13, nil, 15, "data", nil] ],
+ @table.to_a )
+
+ # verify resulting table
+ assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
+ A,B,C,Type,Index
+ 1,100,3,data,1
+ 4,200,6,data,2
+ 10,,12,data,3
+ 13,,15,data,
+ END_RESULT
+
+ # with headers
+ @header_table["Type"] = "data"
+ assert_equal(%w[Type data data data], @header_table["Type"])
+
+ ###################
+ ### Column Mode ###
+ ###################
+ @table.by_col!
+
+ @table[1] = [2, 5, 11, 14]
+ assert_equal( [ %w[A B C Type Index],
+ [1, 2, 3, "data", 1],
+ [4, 5, 6, "data", 2],
+ [10, 11, 12, "data", 3],
+ [13, 14, 15, "data", nil] ],
+ @table.to_a )
+
+ @table["Extra"] = "new stuff"
+ assert_equal( [ %w[A B C Type Index Extra],
+ [1, 2, 3, "data", 1, "new stuff"],
+ [4, 5, 6, "data", 2, "new stuff"],
+ [10, 11, 12, "data", 3, "new stuff"],
+ [13, 14, 15, "data", nil, "new stuff"] ],
+ @table.to_a )
+
+ ################
+ ### Row Mode ###
+ ################
+ @table.by_row!
+
+ @table[1] = (1..6).to_a
+ assert_equal( [ %w[A B C Type Index Extra],
+ [1, 2, 3, "data", 1, "new stuff"],
+ [1, 2, 3, 4, 5, 6],
+ [10, 11, 12, "data", 3, "new stuff"],
+ [13, 14, 15, "data", nil, "new stuff"] ],
+ @table.to_a )
+
+ assert_raise(TypeError) { @table["Extra"] = nil }
+ end
+
+ def test_set_by_col_with_header_row
+ r = [ CSV::Row.new(%w{X Y Z}, [97, 98, 99], true) ]
+ t = CSV::Table.new(r)
+ t.by_col!
+ t['A'] = [42]
+ assert_equal(['A'], t['A'])
+ end
+
+ def test_each
+ ######################
+ ### Mixed/Row Mode ###
+ ######################
+ i = 0
+ @table.each do |row|
+ assert_equal(@rows[i], row)
+ i += 1
+ end
+
+ # verify that we can chain the call
+ assert_equal(@table, @table.each { })
+
+ ###################
+ ### Column Mode ###
+ ###################
+ @table.by_col!
+
+ headers = @table.headers
+ @table.each do |header, column|
+ assert_equal(headers.shift, header)
+ assert_equal(@table[header], column)
+ end
+
+ ############################
+ ### One Shot Mode Change ###
+ ############################
+ @table.by_col_or_row!
+
+ @table.each { |row| assert_instance_of(CSV::Row, row) }
+ @table.by_col.each { |tuple| assert_instance_of(Array, tuple) }
+ @table.each { |row| assert_instance_of(CSV::Row, row) }
+ end
+
+ def test_enumerable
+ assert_equal( @rows.values_at(0, 2),
+ @table.select { |row| (row["B"] % 2).zero? } )
+
+ assert_equal(@rows[1], @table.find { |row| row["C"] > 5 })
+ end
+
+ def test_to_a
+ assert_equal([%w[A B C], [1, 2, 3], [4, 5, 6], [7, 8, 9]], @table.to_a)
+
+ # with headers
+ assert_equal( [%w[A B C], [1, 2, 3], [4, 5, 6], [7, 8, 9]],
+ @header_table.to_a )
+ end
+
+ def test_to_csv
+ csv = <<-END_CSV.gsub(/^\s+/, "")
+ A,B,C
+ 1,2,3
+ 4,5,6
+ 7,8,9
+ END_CSV
+
+ # normal conversion
+ assert_equal(csv, @table.to_csv)
+ assert_equal(csv, @table.to_s) # alias
+
+ # with options
+ assert_equal( csv.gsub(",", "|").gsub("\n", "\r\n"),
+ @table.to_csv(col_sep: "|", row_sep: "\r\n") )
+ assert_equal( csv.lines.to_a[1..-1].join(''),
+ @table.to_csv(:write_headers => false) )
+
+ # with headers
+ assert_equal(csv, @header_table.to_csv)
+ end
+
+ def test_append
+ # verify that we can chain the call
+ assert_equal(@table, @table << [10, 11, 12])
+
+ # Array append
+ assert_equal(CSV::Row.new(%w[A B C], [10, 11, 12]), @table[-1])
+
+ # Row append
+ assert_equal(@table, @table << CSV::Row.new(%w[A B C], [13, 14, 15]))
+ assert_equal(CSV::Row.new(%w[A B C], [13, 14, 15]), @table[-1])
+ end
+
+ def test_delete_mixed
+ ##################
+ ### Mixed Mode ###
+ ##################
+ # delete a row
+ assert_equal(@rows[1], @table.delete(1))
+
+ # delete a col
+ assert_equal(@rows.map { |row| row["A"] }, @table.delete("A"))
+
+ # verify resulting table
+ assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
+ B,C
+ 2,3
+ 8,9
+ END_RESULT
+ end
+
+ def test_delete_column
+ ###################
+ ### Column Mode ###
+ ###################
+ @table.by_col!
+
+ assert_equal(@rows.map { |row| row[0] }, @table.delete(0))
+ assert_equal(@rows.map { |row| row["C"] }, @table.delete("C"))
+
+ # verify resulting table
+ assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
+ B
+ 2
+ 5
+ 8
+ END_RESULT
+ end
+
+ def test_delete_row
+ ################
+ ### Row Mode ###
+ ################
+ @table.by_row!
+
+ assert_equal(@rows[1], @table.delete(1))
+ assert_raise(TypeError) { @table.delete("C") }
+
+ # verify resulting table
+ assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
+ A,B,C
+ 1,2,3
+ 7,8,9
+ END_RESULT
+ end
+
+ def test_delete_with_blank_rows
+ data = "col1,col2\nra1,ra2\n\nrb1,rb2"
+ table = CSV.parse(data, :headers => true)
+ assert_equal(["ra2", nil, "rb2"], table.delete("col2"))
+ end
+
+ def test_delete_if_row
+ ######################
+ ### Mixed/Row Mode ###
+ ######################
+ # verify that we can chain the call
+ assert_equal(@table, @table.delete_if { |row| (row["B"] % 2).zero? })
+
+ # verify resulting table
+ assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
+ A,B,C
+ 4,5,6
+ END_RESULT
+ end
+
+ def test_delete_if_column
+ ###################
+ ### Column Mode ###
+ ###################
+ @table.by_col!
+
+ assert_equal(@table, @table.delete_if { |h, v| h > "A" })
+ assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
+ A
+ 1
+ 4
+ 7
+ END_RESULT
+ end
+
+ def test_values_at
+ ##################
+ ### Mixed Mode ###
+ ##################
+ # rows
+ assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
+ assert_equal(@rows.values_at(1..2), @table.values_at(1..2))
+
+ # cols
+ assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at("A", "C"))
+ assert_equal([[2, 3], [5, 6], [8, 9]], @table.values_at("B".."C"))
+
+ ###################
+ ### Column Mode ###
+ ###################
+ @table.by_col!
+
+ assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at(0, 2))
+ assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at("A", "C"))
+
+ ################
+ ### Row Mode ###
+ ################
+ @table.by_row!
+
+ assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
+ assert_raise(TypeError) { @table.values_at("A", "C") }
+
+ ############################
+ ### One Shot Mode Change ###
+ ############################
+ assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
+ assert_equal([[1, 3], [4, 6], [7, 9]], @table.by_col.values_at(0, 2))
+ assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
+ end
+
+ def test_array_delegation
+ assert_not_empty(@table, "Table was empty.")
+
+ assert_equal(@rows.size, @table.size)
+ end
+
+ def test_inspect_shows_current_mode
+ str = @table.inspect
+ assert_include(str, "mode:#{@table.mode}", "Mode not shown.")
+
+ @table.by_col!
+ str = @table.inspect
+ assert_include(str, "mode:#{@table.mode}", "Mode not shown.")
+ end
+
+ def test_inspect_encoding_is_ascii_compatible
+ assert_send([Encoding, :compatible?,
+ Encoding.find("US-ASCII"),
+ @table.inspect.encoding],
+ "inspect() was not ASCII compatible." )
+ end
+end
diff --git a/jni/ruby/test/csv/ts_all.rb b/jni/ruby/test/csv/ts_all.rb
new file mode 100644
index 0000000..3893841
--- /dev/null
+++ b/jni/ruby/test/csv/ts_all.rb
@@ -0,0 +1,20 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+
+# ts_all.rb
+#
+# Created by James Edward Gray II on 2005-10-31.
+# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
+# under the terms of Ruby's license.
+
+require "test/unit"
+
+require "test_csv_parsing"
+require "test_features"
+require "test_interface"
+require "test_csv_writing"
+require "test_data_converters"
+require "test_row"
+require "test_table"
+require "test_headers"
+require "test_encodings"
diff --git a/jni/ruby/test/date/test_date.rb b/jni/ruby/test/date/test_date.rb
new file mode 100644
index 0000000..3d8bf86
--- /dev/null
+++ b/jni/ruby/test/date/test_date.rb
@@ -0,0 +1,150 @@
+require 'test/unit'
+require 'date'
+
+class DateSub < Date; end
+class DateTimeSub < DateTime; end
+
+class TestDate < Test::Unit::TestCase
+
+ def test__const
+ assert_nil(Date::MONTHNAMES[0])
+ assert_equal('January', Date::MONTHNAMES[1])
+ assert_equal(13, Date::MONTHNAMES.size)
+ assert_equal('Sunday', Date::DAYNAMES[0])
+ assert_equal(7, Date::DAYNAMES.size)
+
+ assert_nil(Date::ABBR_MONTHNAMES[0])
+ assert_equal('Jan', Date::ABBR_MONTHNAMES[1])
+ assert_equal(13, Date::ABBR_MONTHNAMES.size)
+ assert_equal('Sun', Date::ABBR_DAYNAMES[0])
+ assert_equal(7, Date::ABBR_DAYNAMES.size)
+
+ assert(Date::MONTHNAMES.frozen?)
+ assert(Date::MONTHNAMES[1].frozen?)
+ assert(Date::DAYNAMES.frozen?)
+ assert(Date::DAYNAMES[0].frozen?)
+
+ assert(Date::ABBR_MONTHNAMES.frozen?)
+ assert(Date::ABBR_MONTHNAMES[1].frozen?)
+ assert(Date::ABBR_DAYNAMES.frozen?)
+ assert(Date::ABBR_DAYNAMES[0].frozen?)
+ end
+
+ def test_sub
+ d = DateSub.new
+ dt = DateTimeSub.new
+
+ assert_instance_of(DateSub, d)
+ assert_instance_of(DateTimeSub, dt)
+
+ assert_instance_of(DateSub, DateSub.today)
+ assert_instance_of(DateTimeSub, DateTimeSub.now)
+
+ assert_equal('-4712-01-01', d.to_s)
+ assert_equal('-4712-01-01T00:00:00+00:00', dt.to_s)
+
+ d2 = d + 1
+ assert_instance_of(DateSub, d2)
+ d2 = d - 1
+ assert_instance_of(DateSub, d2)
+ d2 = d >> 1
+ assert_instance_of(DateSub, d2)
+ d2 = d << 1
+ assert_instance_of(DateSub, d2)
+ d2 = d.succ
+ assert_instance_of(DateSub, d2)
+ d2 = d.next
+ assert_instance_of(DateSub, d2)
+ d2 = d.italy
+ assert_instance_of(DateSub, d2)
+ d2 = d.england
+ assert_instance_of(DateSub, d2)
+ d2 = d.julian
+ assert_instance_of(DateSub, d2)
+ d2 = d.gregorian
+ assert_instance_of(DateSub, d2)
+ s = Marshal.dump(d)
+ d2 = Marshal.load(s)
+ assert_equal(d2, d)
+ assert_instance_of(DateSub, d2)
+
+ dt2 = dt + 1
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt - 1
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt >> 1
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt << 1
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt.succ
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt.next
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt.italy
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt.england
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt.julian
+ assert_instance_of(DateTimeSub, dt2)
+ dt2 = dt.gregorian
+ assert_instance_of(DateTimeSub, dt2)
+ s = Marshal.dump(dt)
+ dt2 = Marshal.load(s)
+ assert_equal(dt2, dt)
+ assert_instance_of(DateTimeSub, dt2)
+ end
+
+ def test_eql_p
+ d = Date.jd(0)
+ d2 = Date.jd(0)
+ dt = DateTime.jd(0)
+ dt2 = DateTime.jd(0)
+
+ assert_equal(d, d2)
+ assert_not_equal(d, 0)
+
+ assert_equal(dt, dt2)
+ assert_not_equal(dt, 0)
+
+ assert_equal(d, dt)
+ assert_equal(d2, dt2)
+ end
+
+ def test_hash
+ h = {}
+ h[Date.new(1999,5,23)] = 0
+ h[Date.new(1999,5,24)] = 1
+ h[Date.new(1999,5,25)] = 2
+ h[Date.new(1999,5,25)] = 9
+ assert_equal(3, h.size)
+ assert_equal(9, h[Date.new(1999,5,25)])
+ assert_equal(9, h[DateTime.new(1999,5,25)])
+
+ h = {}
+ h[DateTime.new(1999,5,23)] = 0
+ h[DateTime.new(1999,5,24)] = 1
+ h[DateTime.new(1999,5,25)] = 2
+ h[DateTime.new(1999,5,25)] = 9
+ assert_equal(3, h.size)
+ assert_equal(9, h[Date.new(1999,5,25)])
+ assert_equal(9, h[DateTime.new(1999,5,25)])
+ end
+
+ def test_freeze
+ d = Date.new
+ d.freeze
+ assert_equal(true, d.frozen?)
+ assert_instance_of(Fixnum, d.yday)
+ assert_instance_of(String, d.to_s)
+ end
+
+ def test_submillisecond_comparison
+ d1 = DateTime.new(2013, 12, 6, 0, 0, Rational(1, 10000))
+ d2 = DateTime.new(2013, 12, 6, 0, 0, Rational(2, 10000))
+ # d1 is 0.0001s earlier than d2
+ assert_equal(-1, d1 <=> d2)
+ assert_equal(0, d1 <=> d1)
+ assert_equal(1, d2 <=> d1)
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_arith.rb b/jni/ruby/test/date/test_date_arith.rb
new file mode 100644
index 0000000..7b79c18
--- /dev/null
+++ b/jni/ruby/test/date/test_date_arith.rb
@@ -0,0 +1,264 @@
+require 'test/unit'
+require 'date'
+
+class TestDateArith < Test::Unit::TestCase
+
+ def new_offset
+ d = DateTime.new(2002, 3, 14)
+ assert_equal(Rational(9, 24), d.new_offset(Rational(9, 24)).offset)
+ assert_equal(Rational(9, 24), d.new_offset('+0900').offset)
+ end
+
+ def test__plus
+ d = Date.new(2000,2,29) + -1
+ assert_equal([2000, 2, 28], [d.year, d.mon, d.mday])
+ d = Date.new(2000,2,29) + 0
+ assert_equal([2000, 2, 29], [d.year, d.mon, d.mday])
+ d = Date.new(2000,2,29) + 1
+ assert_equal([2000, 3, 1], [d.year, d.mon, d.mday])
+
+ d = DateTime.new(2000,2,29) + 1.to_r/2
+ assert_equal([2000, 2, 29, 12, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ end
+
+ def test__plus__ex
+ e = TypeError
+ assert_raise(e) do
+ Date.new(2000,2,29) + 'foo'
+ end
+ assert_raise(e) do
+ DateTime.new(2000,2,29) + 'foo'
+ end
+ assert_raise(e) do
+ Date.new(2000,2,29) + Time.mktime(2000,2,29)
+ end
+ assert_raise(e) do
+ DateTime.new(2000,2,29) + Time.mktime(2000,2,29)
+ end
+ end
+
+ def test__minus
+ d = Date.new(2000,3,1) - -1
+ assert_equal([2000, 3, 2], [d.year, d.mon, d.mday])
+ d = Date.new(2000,3,1) - 0
+ assert_equal([2000, 3, 1], [d.year, d.mon, d.mday])
+ d = Date.new(2000,3,1) - 1
+ assert_equal([2000, 2, 29], [d.year, d.mon, d.mday])
+
+ d = Date.new(2000,3,1) - Date.new(2000,2,29)
+ assert_equal(1, d)
+ d = Date.new(2000,2,29) - Date.new(2000,3,1)
+ assert_equal(-1, d)
+
+ d = DateTime.new(2000,3,1) - 1.to_r/2
+ assert_equal([2000, 2, 29, 12, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ end
+
+ def test__minus__ex
+ e = TypeError
+ assert_raise(e) do
+ Date.new(2000,2,29) - 'foo'
+ end
+ assert_raise(e) do
+ DateTime.new(2000,2,29) - 'foo'
+ end
+ assert_raise(e) do
+ Date.new(2000,2,29) - Time.mktime(2000,2,29)
+ end
+ assert_raise(e) do
+ DateTime.new(2000,2,29) - Time.mktime(2000,2,29)
+ end
+ end
+
+ def test__compare
+ assert_equal(0, (Date.new(2000,1,1) <=> Date.new(2000,1,1)))
+ assert_equal(-1, (Date.new(2000,1,1) <=> Date.new(2000,1,2)))
+ assert_equal(1, (Date.new(2000,1,2) <=> Date.new(2000,1,1)))
+ assert_equal(0, (Date.new(2001,1,4,Date::JULIAN) <=>
+ Date.new(2001,1,17, Date::GREGORIAN)))
+ assert_equal(0, (DateTime.new(2001,1,4,0,0,0,0,Date::JULIAN) <=>
+ DateTime.new(2001,1,17,0,0,0,0,Date::GREGORIAN)))
+ end
+
+ def test_prev
+ d = Date.new(2000,1,1)
+ assert_raise(NoMethodError) do
+ d.prev
+ end
+ end
+
+ def test_prev_day
+ d = Date.new(2001,1,1).prev_day
+ assert_equal([2000, 12, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2001,1,1).prev_day(2)
+ assert_equal([2000, 12, 30], [d.year, d.mon, d.mday])
+ d = Date.new(2000,12,31).prev_day(-2)
+ assert_equal([2001, 1, 2], [d.year, d.mon, d.mday])
+
+ d = DateTime.new(2000,3,1).prev_day(1.to_r/2)
+ assert_equal([2000, 2, 29, 12, 0, 0], [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ end
+
+ def test_prev_month
+ d = Date.new(2000,1,31) << -1
+ assert_equal([2000, 2, 29], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31) << 1
+ assert_equal([1999, 12, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31) << 12
+ assert_equal([1999, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31) << 14
+ assert_equal([1998, 11, 30], [d.year, d.mon, d.mday])
+
+ end
+
+ def test_prev_month__2
+ d = Date.new(2000,1,31).prev_month(-1)
+ assert_equal([2000, 2, 29], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).prev_month
+ assert_equal([1999, 12, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).prev_month(12)
+ assert_equal([1999, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).prev_month(14)
+ assert_equal([1998, 11, 30], [d.year, d.mon, d.mday])
+ end
+
+ def test_prev_year
+ d = Date.new(2000,1,31).prev_year(-1)
+ assert_equal([2001, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).prev_year
+ assert_equal([1999, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).prev_year(10)
+ assert_equal([1990, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).prev_year(100)
+ assert_equal([1900, 1, 31], [d.year, d.mon, d.mday])
+ end
+
+ def test_next
+ d = Date.new(2000,12,31).next
+ assert_equal([2001, 1, 1], [d.year, d.mon, d.mday])
+ d = Date.new(2000,12,31).succ
+ assert_equal([2001, 1, 1], [d.year, d.mon, d.mday])
+
+ d = Date.today
+ d2 = d.next
+ assert_equal(d, (d2 - 1))
+ d = Date.today
+ d2 = d.succ
+ assert_equal(d, (d2 - 1))
+
+ d = DateTime.now
+ d2 = d.next
+ assert_equal(d, (d2 - 1))
+ d = DateTime.now
+ d2 = d.succ
+ assert_equal(d, (d2 - 1))
+ end
+
+ def test_next_day
+ d = Date.new(2000,12,31).next_day
+ assert_equal([2001, 1, 1], [d.year, d.mon, d.mday])
+ d = Date.new(2000,12,31).next_day(2)
+ assert_equal([2001, 1, 2], [d.year, d.mon, d.mday])
+ d = Date.new(2001,1,1).next_day(-2)
+ assert_equal([2000, 12, 30], [d.year, d.mon, d.mday])
+
+ d = DateTime.new(2000,2,29).next_day(1.to_r/2)
+ assert_equal([2000, 2, 29, 12, 0, 0], [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ end
+
+ def test_next_month
+ d = Date.new(2000,1,31) >> -1
+ assert_equal([1999, 12, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31) >> 1
+ assert_equal([2000, 2, 29], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31) >> 12
+ assert_equal([2001, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31) >> 13
+ assert_equal([2001, 2, 28], [d.year, d.mon, d.mday])
+ end
+
+ def test_next_month__2
+ d = Date.new(2000,1,31).next_month(-1)
+ assert_equal([1999, 12, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).next_month
+ assert_equal([2000, 2, 29], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).next_month(12)
+ assert_equal([2001, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).next_month(13)
+ assert_equal([2001, 2, 28], [d.year, d.mon, d.mday])
+ end
+
+ def test_next_year
+ d = Date.new(2000,1,31).next_year(-1)
+ assert_equal([1999, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).next_year
+ assert_equal([2001, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).next_year(10)
+ assert_equal([2010, 1, 31], [d.year, d.mon, d.mday])
+ d = Date.new(2000,1,31).next_year(100)
+ assert_equal([2100, 1, 31], [d.year, d.mon, d.mday])
+ end
+
+ def test_downto
+ p = Date.new(2001,1,14)
+ q = Date.new(2001,1,7)
+ i = 0
+ p.downto(q) do
+ i += 1
+ end
+ assert_equal(8, i)
+ end
+
+ def test_downto__noblock
+ p = Date.new(2001,1,14)
+ q = Date.new(2001,1,7)
+ e = p.downto(q)
+ assert_equal(8, e.to_a.size)
+ end
+
+ def test_upto
+ p = Date.new(2001,1,14)
+ q = Date.new(2001,1,21)
+ i = 0
+ p.upto(q) do
+ i += 1
+ end
+ assert_equal(8, i)
+ end
+
+ def test_upto__noblock
+ p = Date.new(2001,1,14)
+ q = Date.new(2001,1,21)
+ e = p.upto(q)
+ assert_equal(8, e.to_a.size)
+ end
+
+ def test_step
+ p = Date.new(2001,1,14)
+ q = Date.new(2001,1,21)
+ i = 0
+ p.step(q, 2) do
+ i += 1
+ end
+ assert_equal(4, i)
+
+ i = 0
+ p.step(q) do
+ i += 1
+ end
+ assert_equal(8, i)
+ end
+
+ def test_step__noblock
+ p = Date.new(2001,1,14)
+ q = Date.new(2001,1,21)
+ e = p.step(q, 2)
+ assert_equal(4, e.to_a.size)
+
+ e = p.step(q)
+ assert_equal(8, e.to_a.size)
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_attr.rb b/jni/ruby/test/date/test_date_attr.rb
new file mode 100644
index 0000000..bc6bec2
--- /dev/null
+++ b/jni/ruby/test/date/test_date_attr.rb
@@ -0,0 +1,103 @@
+require 'test/unit'
+require 'date'
+
+class TestDateAttr < Test::Unit::TestCase
+
+ def test__attr
+ date = Date.new(1965, 5, 23)
+ datetime = DateTime.new(1965, 5, 23, 22, 31, 59)
+
+ [date, datetime].each_with_index do |d, i|
+
+ if i == 0
+ assert_equal('1965-05-23', d.to_s)
+ else
+ assert_equal('1965-05-23T22:31:59+00:00', d.to_s)
+ end
+
+ assert_equal('', d.inspect.gsub!(/./,''))
+ assert_equal('', d.to_s.gsub!(/./,''))
+
+ assert_equal(2438904, d.jd)
+
+ if i == 0
+ assert_equal(0, d.day_fraction)
+ else
+ assert_equal(22.to_r/24 + 31.to_r/1440 + 59.to_r/86400, d.day_fraction)
+ end
+
+ assert_equal(38903, d.mjd)
+ assert_equal(139744, d.ld)
+
+ assert_equal(1965, d.year)
+ assert_equal(143, d.yday)
+ assert_equal(5, d.mon)
+ assert_equal(d.mon, d.month)
+ assert_equal(23, d.mday)
+ assert_equal(d.mday, d.day)
+
+ if i == 0
+ assert_equal(false, d.respond_to?(:hour))
+ assert_equal(false, d.respond_to?(:min))
+ assert_equal(false, d.respond_to?(:sec))
+ assert_equal(false, d.respond_to?(:sec_fraction))
+ assert_equal(false, d.respond_to?(:zone))
+ assert_equal(false, d.respond_to?(:offset))
+ else
+ assert_equal(22, d.hour)
+ assert_equal(31, d.min)
+ assert_equal(59, d.sec)
+ assert_equal(0, d.sec_fraction)
+ assert_equal('+00:00', d.zone)
+ assert_equal(0, d.offset)
+ end
+
+ assert_equal(1965, d.cwyear)
+ assert_equal(20, d.cweek)
+ assert_equal(7, d.cwday)
+
+ assert_equal(0, d.wday)
+ assert_equal(false, d.leap?)
+ assert_equal(false, d.julian?)
+ assert_equal(true, d.gregorian?)
+
+ assert_equal(Date::ITALY, d.start)
+ assert_equal(d.start, d.start)
+ end
+
+ d = DateTime.new(1965, 5, 23, 22, 31, 59) + 1.to_r/(86400*2)
+ assert_equal(1.to_r/2, d.sec_fraction)
+ end
+
+ def test__wday_predicate
+ d = Date.new(2005, 10, 23)
+ assert_equal(true, d.sunday?)
+ assert_equal(false, d.monday?)
+ assert_equal(false, d.tuesday?)
+ assert_equal(false, d.wednesday?)
+ assert_equal(false, d.thursday?)
+ assert_equal(false, d.friday?)
+ assert_equal(false, d.saturday?)
+
+ d = Date.new(2005, 10, 30)
+ 14.times do |i|
+ assert((d + i).__send__(%w(sunday? monday? tuesday? wednesday?
+ thursday? friday? saturday?)[i % 7]))
+ end
+ end
+
+ def test_nth_kday
+ skip unless Date.new.respond_to?(:nth_kday?, true)
+ assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, 1,0))
+ assert_equal(true, Date.new(2001,1,14).__send__(:nth_kday?, 2,0))
+ assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, 3,0))
+ assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, 4,0))
+ assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, 5,0))
+ assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, -1,0))
+ assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, -2,0))
+ assert_equal(true, Date.new(2001,1,14).__send__(:nth_kday?, -3,0))
+ assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, -4,0))
+ assert_equal(false, Date.new(2001,1,14).__send__(:nth_kday?, -5,0))
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_base.rb b/jni/ruby/test/date/test_date_base.rb
new file mode 100644
index 0000000..1f3d8c0
--- /dev/null
+++ b/jni/ruby/test/date/test_date_base.rb
@@ -0,0 +1,442 @@
+require 'test/unit'
+require 'date'
+
+begin
+ require 'calendar'
+ include Calendar
+rescue LoadError
+end
+
+class TestDateBase < Test::Unit::TestCase
+
+ def setup
+ if defined?(Calendar)
+ @from ||= julian_day_number_from_absolute(absolute_from_julian(1, 1, 1601))
+ @to ||= julian_day_number_from_absolute(absolute_from_julian(12, 31, 2400))
+ @from4t ||= julian_day_number_from_absolute(absolute_from_julian(1, 1, 1970))
+ @to4t ||= julian_day_number_from_absolute(absolute_from_julian(12, 31, 2037))
+ end
+ end
+
+ def test__inf
+ assert_equal(0, Date::Infinity.new(-1) <=> Date::Infinity.new(-1))
+ assert_equal(-1, Date::Infinity.new(-1) <=> Date::Infinity.new(+1))
+ assert_equal(-1, Date::Infinity.new(-1) <=> 0)
+
+ assert_equal(1, Date::Infinity.new(+1) <=> Date::Infinity.new(-1))
+ assert_equal(0, Date::Infinity.new(+1) <=> Date::Infinity.new(+1))
+ assert_equal(1, Date::Infinity.new(+1) <=> 0)
+
+ assert_equal(1, 0 <=> Date::Infinity.new(-1))
+ assert_equal(-1, 0 <=> Date::Infinity.new(+1))
+ assert_equal(0, 0 <=> 0)
+
+ assert_equal(0, Date::ITALY <=> Date::ITALY)
+ assert_equal(-1, Date::ITALY <=> Date::ENGLAND)
+ assert_equal(-1, Date::ITALY <=> Date::JULIAN)
+ assert_equal(1, Date::ITALY <=> Date::GREGORIAN)
+
+ assert_equal(1, Date::ENGLAND <=> Date::ITALY)
+ assert_equal(0, Date::ENGLAND <=> Date::ENGLAND)
+ assert_equal(-1, Date::ENGLAND <=> Date::JULIAN)
+ assert_equal(1, Date::ENGLAND <=> Date::GREGORIAN)
+
+ assert_equal(1, Date::JULIAN <=> Date::ITALY)
+ assert_equal(1, Date::JULIAN <=> Date::ENGLAND)
+ assert_equal(0, Date::JULIAN <=> Date::JULIAN)
+ assert_equal(1, Date::JULIAN <=> Date::GREGORIAN)
+
+ assert_equal(-1, Date::GREGORIAN <=> Date::ITALY)
+ assert_equal(-1, Date::GREGORIAN <=> Date::ENGLAND)
+ assert_equal(-1, Date::GREGORIAN <=> Date::JULIAN)
+ assert_equal(0, Date::GREGORIAN <=> Date::GREGORIAN)
+ end
+
+ def test_ordinal__julian
+ skip unless defined?(Calendar)
+ for j in @from..@to
+ m, d, y = julian_from_absolute(absolute_from_julian_day_number(j))
+ j0 = julian_day_number_from_absolute(absolute_from_julian(12, 31, y - 1))
+ j2 = julian_day_number_from_absolute(absolute_from_julian(m, d, y))
+ assert_equal(j, j2)
+ oy, od = Date.__send__(:jd_to_ordinal, j, Date::JULIAN)
+ assert_equal(y, oy)
+ assert_equal(j2 - j0, od)
+ oj = Date.__send__(:ordinal_to_jd, oy, od, Date::JULIAN)
+ assert_equal(j, oj)
+ end
+ end
+
+ def test_ordinal__gregorian
+ skip unless defined?(Calendar)
+ for j in @from..@to
+ m, d, y = gregorian_from_absolute(absolute_from_julian_day_number(j))
+ j0 =
+ julian_day_number_from_absolute(absolute_from_gregorian(12, 31, y - 1))
+ j2 = julian_day_number_from_absolute(absolute_from_gregorian(m, d, y))
+ assert_equal(j, j2)
+ oy, od = Date.__send__(:jd_to_ordinal, j, Date::GREGORIAN)
+ assert_equal(y, oy)
+ assert_equal(j2 - j0, od)
+ oj = Date.__send__(:ordinal_to_jd, oy, od, Date::GREGORIAN)
+ assert_equal(j, oj)
+ end
+ end
+
+ def test_civil__julian
+ skip unless defined?(Calendar)
+ for j in @from..@to
+ m, d, y = julian_from_absolute(absolute_from_julian_day_number(j))
+ j2 = julian_day_number_from_absolute(absolute_from_julian(m, d, y))
+ assert_equal(j2, j)
+ cy, cm, cd = Date.__send__(:jd_to_civil, j, Date::JULIAN)
+ assert_equal(y, cy)
+ assert_equal(m, cm)
+ assert_equal(d, cd)
+ cj = Date.__send__(:civil_to_jd, cy, cm, cd, Date::JULIAN)
+ assert_equal(j, cj)
+ end
+ end
+
+ def test_civil__gregorian
+ skip unless defined?(Calendar)
+ for j in @from..@to
+ m, d, y = gregorian_from_absolute(absolute_from_julian_day_number(j))
+ j2 = julian_day_number_from_absolute(absolute_from_gregorian(m, d, y))
+ assert_equal(j2, j)
+ cy, cm, cd = Date.__send__(:jd_to_civil, j, Date::GREGORIAN)
+ assert_equal(y, cy)
+ assert_equal(m, cm)
+ assert_equal(d, cd)
+ cj = Date.__send__(:civil_to_jd, cy, cm, cd, Date::GREGORIAN)
+ assert_equal(j, cj)
+ end
+ end
+
+ def test_commercial__gregorian
+ skip unless defined?(Calendar)
+ for j in @from..@to
+ w, d, y = iso_from_absolute(absolute_from_julian_day_number(j))
+ j2 = julian_day_number_from_absolute(absolute_from_iso(w, d, y))
+ assert_equal(j2, j)
+ cy, cw, cd = Date.__send__(:jd_to_commercial, j, Date::GREGORIAN)
+ assert_equal(y, cy)
+ assert_equal(w, cw)
+ assert_equal(d, cd)
+ cj = Date.__send__(:commercial_to_jd, cy, cw, cd, Date::GREGORIAN)
+ assert_equal(j, cj)
+ end
+ end
+
+ def test_weeknum
+ skip unless defined?(Calendar)
+ for j in @from..@to
+ for k in 0..1
+ wy, ww, wd = Date.__send__(:jd_to_weeknum, j, k, Date::GREGORIAN)
+ wj = Date.__send__(:weeknum_to_jd, wy, ww, wd, k, Date::GREGORIAN)
+ assert_equal(j, wj)
+ end
+ end
+ end
+
+ def test_weeknum__2
+ skip unless defined?(Calendar)
+ for j in @from4t..@to4t
+ d = Date.jd(j)
+ t = Time.mktime(d.year, d.mon, d.mday)
+ [
+ '%Y %U %w',
+ '%Y %U %u',
+ '%Y %W %w',
+ '%Y %W %u'
+ ].each do |fmt|
+ s = t.strftime(fmt)
+ d2 = Date.strptime(s, fmt)
+ assert_equal(j, d2.jd)
+ end
+ end
+ end
+
+ def test_nth_kday
+ skip unless defined?(Calendar)
+ skip unless (Date.respond_to?(:nth_kday_to_jd, true) &&
+ Date.respond_to?(:jd_to_nth_kday, true))
+ for y in 1601..2401
+ for m in 1..12
+ for n in -5..5
+ next if n == 0
+ for k in 0..6
+ j = julian_day_number_from_absolute(Nth_Kday(n, k, m, y))
+ j2 = Date.__send__(:nth_kday_to_jd, y, m, n, k, Date::GREGORIAN)
+ assert_equal(j, j2)
+
+ d1 = Date.__send__(:jd_to_nth_kday, j2, Date::GREGORIAN)
+ j3 = Date.__send__(:nth_kday_to_jd, *d1)
+ assert_equal(j, j3)
+ end
+ end
+ end
+ end
+ end
+
+ def test_jd
+ assert_equal(1 << 33, Date.jd(1 << 33).jd)
+ end
+
+ def test_mjd
+ skip unless Date.respond_to?(:mjd_to_jd, true)
+ jd = Date.__send__(:mjd_to_jd, 51321)
+ mjd = Date.__send__(:jd_to_mjd, jd)
+ assert_equal(51321, mjd)
+ end
+
+ def test_ld
+ skip unless Date.respond_to?(:ld_to_jd, true)
+ jd = Date.__send__(:ld_to_jd, 152162)
+ ld = Date.__send__(:jd_to_ld, jd)
+ assert_equal(152162, ld)
+ end
+
+ def test_wday
+ skip unless Date.respond_to?(:jd_to_wday, true)
+ assert_equal(4, Date.__send__(:jd_to_wday, 3))
+ assert_equal(3, Date.__send__(:jd_to_wday, 2))
+ assert_equal(2, Date.__send__(:jd_to_wday, 1))
+ assert_equal(1, Date.__send__(:jd_to_wday, 0))
+ assert_equal(0, Date.__send__(:jd_to_wday, -1))
+ assert_equal(6, Date.__send__(:jd_to_wday, -2))
+ assert_equal(5, Date.__send__(:jd_to_wday, -3))
+ end
+
+ def test_leap?
+ assert_equal(true, Date.julian_leap?(1900))
+ assert_equal(false, Date.julian_leap?(1999))
+ assert_equal(true, Date.julian_leap?(2000))
+
+ assert_equal(false, Date.gregorian_leap?(1900))
+ assert_equal(false, Date.gregorian_leap?(1999))
+ assert_equal(true, Date.gregorian_leap?(2000))
+
+ assert_equal(Date.leap?(1990), Date.gregorian_leap?(1900))
+ assert_equal(Date.leap?(1999), Date.gregorian_leap?(1999))
+ assert_equal(Date.leap?(2000), Date.gregorian_leap?(2000))
+ end
+
+ def test_valid_jd
+ valid_jd_p = :_valid_jd?
+ skip unless Date.respond_to?(valid_jd_p, true)
+ assert_equal(-1, Date.__send__(valid_jd_p, -1))
+ assert_equal(0, Date.__send__(valid_jd_p, 0))
+ assert_equal(1, Date.__send__(valid_jd_p, 1))
+ assert_equal(2452348, Date.__send__(valid_jd_p, 2452348))
+ end
+
+ def test_valid_ordinal
+ valid_ordinal_p = :_valid_ordinal?
+ skip unless Date.respond_to?(valid_ordinal_p, true)
+ assert_nil(Date.__send__(valid_ordinal_p, 1999,366))
+ assert_equal(2451910, Date.__send__(valid_ordinal_p, 2000,366))
+ assert_nil(Date.__send__(valid_ordinal_p, 1999,-366))
+ assert_equal(2451545, Date.__send__(valid_ordinal_p, 2000,-366))
+ assert_equal(2452275, Date.__send__(valid_ordinal_p, 2001,365))
+ assert_nil(Date.__send__(valid_ordinal_p, 2001,366))
+ assert_equal(Date.__send__(valid_ordinal_p, 2001,1),
+ Date.__send__(valid_ordinal_p, 2001,-365))
+ assert_nil(Date.__send__(valid_ordinal_p, 2001,-366))
+ assert_equal(2452348, Date.__send__(valid_ordinal_p, 2002,73))
+ end
+
+ def test_valid_ordinal__edge
+ valid_ordinal_p = :_valid_ordinal?
+ skip unless Date.respond_to?(valid_ordinal_p, true)
+ (1601..2400).each do |y|
+ d = if Date.leap?(y) then 366 else 365 end
+ assert_not_nil(Date.__send__(valid_ordinal_p, y,d))
+ assert_nil(Date.__send__(valid_ordinal_p, y,d + 1))
+ assert_not_nil(Date.__send__(valid_ordinal_p, y,-d))
+ assert_nil(Date.__send__(valid_ordinal_p, y,-(d + 1)))
+ end
+ end
+
+ # October 1582
+ # S M Tu W Th F S
+ # 274 275 276 277 288 289
+ # 290 291 292 293 294 295 296
+ # 297 298 299 300 301 302 303
+ # 304
+
+ # October 1582
+ # S M Tu W Th F S
+ # -92 -91 -90 -89 -78 -77
+ # -76 -75 -74 -73 -72 -71 -70
+ # -69 -68 -67 -66 -65 -64 -63
+ # -62
+
+ def test_valid_ordinal__italy
+ valid_ordinal_p = :_valid_ordinal?
+ skip unless Date.respond_to?(valid_ordinal_p, true)
+ (1..355).each do |d|
+ assert_not_nil(Date.__send__(valid_ordinal_p, 1582,d,Date::ITALY))
+ end
+ (356..365).each do |d|
+ assert_nil(Date.__send__(valid_ordinal_p, 1582,d,Date::ITALY))
+ end
+ end
+
+ # September 1752
+ # S M Tu W Th F S
+ # 245 246 258 259 260
+ # 261 262 263 264 265 266 267
+ # 268 269 270 271 272 273 274
+
+ def test_valid_ordinal__england
+ valid_ordinal_p = :_valid_ordinal?
+ skip unless Date.respond_to?(valid_ordinal_p, true)
+ (1..355).each do |d|
+ assert_not_nil(Date.__send__(valid_ordinal_p, 1752,d,Date::ENGLAND))
+ end
+ (356..366).each do |d|
+ assert_nil(Date.__send__(valid_ordinal_p, 1752,d,Date::ENGLAND))
+ end
+ end
+
+ def test_valid_civil
+ valid_civil_p = :_valid_civil?
+ skip unless Date.respond_to?(valid_civil_p, true)
+ assert_nil(Date.__send__(valid_civil_p, 1999,2,29))
+ assert_equal(2451604, Date.__send__(valid_civil_p, 2000,2,29))
+ assert_nil(Date.__send__(valid_civil_p, 1999,2,-29))
+ assert_equal(2451576, Date.__send__(valid_civil_p, 2000,2,-29))
+ assert_equal(2451941, Date.__send__(valid_civil_p, 2001,1,31))
+ assert_nil(Date.__send__(valid_civil_p, 2001,1,32))
+ assert_equal(Date.__send__(valid_civil_p, 2001,1,1),
+ Date.__send__(valid_civil_p, 2001,1,-31))
+ assert_nil(Date.__send__(valid_civil_p, 2001,1,-32))
+ assert_equal(2452348, Date.__send__(valid_civil_p, 2002,3,14))
+ assert_nil(Date.__send__(valid_civil_p, 2010,-13,-1))
+ end
+
+ def test_valid_civil__edge
+ valid_civil_p = :_valid_civil?
+ skip unless Date.respond_to?(valid_civil_p, true)
+ (1601..2400).each do |y|
+ d = if Date.leap?(y) then 29 else 28 end
+ assert_not_nil(Date.__send__(valid_civil_p, y,2,d))
+ assert_nil(Date.__send__(valid_civil_p, y,2,d + 1))
+ assert_not_nil(Date.__send__(valid_civil_p, y,2,-d))
+ assert_nil(Date.__send__(valid_civil_p, y,2,-(d + 1)))
+ end
+ end
+
+ # October 1582
+ # S M Tu W Th F S
+ # 1 2 3 4 15 16
+ # 17 18 19 20 21 22 23
+ # 24 25 26 27 28 29 30
+ # 31
+
+ def test_valid_civil__italy
+ valid_civil_p = :_valid_civil?
+ skip unless Date.respond_to?(valid_civil_p, true)
+ (1..4).each do |d|
+ assert_not_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
+ end
+ (5..14).each do |d|
+ assert_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
+ end
+ (15..31).each do |d|
+ assert_not_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
+ end
+ (32..100).each do |d|
+ assert_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
+ end
+ (-31..-22).each do |d|
+ assert_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
+ end
+ (-21..-1).each do |d|
+ assert_not_nil(Date.__send__(valid_civil_p, 1582,10,d,Date::ITALY))
+ end
+ end
+
+ # September 1752
+ # S M Tu W Th F S
+ # 1 2 14 15 16
+ # 17 18 19 20 21 22 23
+ # 24 25 26 27 28 29 30
+
+ def test_valid_civil__england
+ valid_civil_p = :_valid_civil?
+ skip unless Date.respond_to?(valid_civil_p, true)
+ (1..2).each do |d|
+ assert_not_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
+ end
+ (3..13).each do |d|
+ assert_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
+ end
+ (14..30).each do |d|
+ assert_not_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
+ end
+ (31..100).each do |d|
+ assert_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
+ end
+ (-31..-20).each do |d|
+ assert_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
+ end
+ (-19..-1).each do |d|
+ assert_not_nil(Date.__send__(valid_civil_p, 1752,9,d,Date::ENGLAND))
+ end
+ end
+
+ def test_valid_commercial
+ valid_commercial_p = :_valid_commercial?
+ skip unless Date.respond_to?(valid_commercial_p, true)
+ assert_nil(Date.__send__(valid_commercial_p, 1999,53,1))
+ assert_equal(2453367, Date.__send__(valid_commercial_p, 2004,53,1))
+ assert_nil(Date.__send__(valid_commercial_p, 1999,-53,-1))
+ assert_equal(2453009, Date.__send__(valid_commercial_p, 2004,-53,-1))
+ assert_equal(2452348, Date.__send__(valid_commercial_p, 2002,11,4))
+ end
+
+ def test_valid_weeknum
+ valid_weeknum_p = :_valid_weeknum?
+ skip unless Date.respond_to?(valid_weeknum_p, true)
+ assert_nil(Date.__send__(valid_weeknum_p, 1999,53,0, 0))
+ assert_equal(2454101, Date.__send__(valid_weeknum_p, 2006,53,0, 0))
+ assert_nil(Date.__send__(valid_weeknum_p, 1999,-53,-1, 0))
+ assert_equal(2453743, Date.__send__(valid_weeknum_p, 2006,-53,-1, 0))
+ assert_equal(2452355, Date.__send__(valid_weeknum_p, 2002,11,4, 0))
+ assert_nil(Date.__send__(valid_weeknum_p, 1999,53,0, 1))
+ assert_equal(2454101, Date.__send__(valid_weeknum_p, 2006,52,6, 1))
+ assert_nil(Date.__send__(valid_weeknum_p, 1999,-53,-1, 1))
+ assert_equal(2453743, Date.__send__(valid_weeknum_p, 2006,-52,-2, 1))
+ assert_equal(2452355, Date.__send__(valid_weeknum_p, 2002,11,3, 1))
+ end
+
+ def test_valid_nth_kday
+ valid_nth_kday_p = :_valid_nth_kday?
+ skip unless Date.respond_to?(valid_nth_kday_p, true)
+ assert_nil(Date.__send__(valid_nth_kday_p, 1992,2, 5,0))
+ assert_equal(2448682, Date.__send__(valid_nth_kday_p, 1992,2, 5,6))
+ assert_equal(2448682, Date.__send__(valid_nth_kday_p, 1992,2, 5,-1))
+ assert_equal(2448682, Date.__send__(valid_nth_kday_p, 1992,2, -1,6))
+ assert_equal(2448682, Date.__send__(valid_nth_kday_p, 1992,2, -1,-1))
+ end
+
+ def test_valid_time
+ valid_time_p = :_valid_time?
+ skip unless Date.respond_to?(valid_time_p, true)
+ assert_equal(Rational(0), DateTime.__send__(valid_time_p, 0,0,0))
+ assert_nil(DateTime.__send__(valid_time_p, 25,59,59))
+ assert_nil(DateTime.__send__(valid_time_p, 23,60,59))
+ assert_nil(DateTime.__send__(valid_time_p, 23,59,60))
+ assert_equal(Rational(86399, 86400),
+ DateTime.__send__(valid_time_p, 23,59,59))
+ assert_equal(Rational(86399, 86400),
+ DateTime.__send__(valid_time_p, -1,-1,-1))
+ assert_equal(Rational(1), DateTime.__send__(valid_time_p, 24,0,0))
+ assert_nil(DateTime.__send__(valid_time_p, 24,0,1))
+ assert_nil(DateTime.__send__(valid_time_p, 24,1,0))
+ assert_nil(DateTime.__send__(valid_time_p, 24,1,1))
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_compat.rb b/jni/ruby/test/date/test_date_compat.rb
new file mode 100644
index 0000000..8284007
--- /dev/null
+++ b/jni/ruby/test/date/test_date_compat.rb
@@ -0,0 +1,21 @@
+require 'test/unit'
+require 'date'
+
+class TestDateCompat < Test::Unit::TestCase
+
+ def test_compat
+ assert_equal(DateTime.new, Date.new)
+ assert_equal(DateTime.new(2002,3,19), Date.new(2002,3,19))
+ assert_equal(DateTime.new(2002,3,19, 0,0,0), Date.new(2002,3,19))
+ assert_equal(DateTime.new(2002,3,19, 0,0,0, 0), Date.new(2002,3,19))
+ assert_equal(DateTime.new(2002,3,19, 0,0,0, 0.to_r), Date.new(2002,3,19))
+ assert_equal(DateTime.new(2002,3,19, 0,0,0, 0, Date::GREGORIAN), Date.new(2002,3,19, Date::GREGORIAN))
+ assert_equal(DateTime.new(2002,3,19, 0,0,0, 0, Date::JULIAN), Date.new(2002,3,19, Date::JULIAN))
+
+ assert(Date.new(2002,3,19) != DateTime.new(2002,3,19, 12,0,0))
+ assert(Date.new(2002,3,19) != DateTime.new(2002,3,19, 0,0,1))
+ assert(Date.new(2002,3,19) === DateTime.new(2002,3,19, 12,0,0))
+ assert(Date.new(2002,3,19) === DateTime.new(2002,3,19, 0,0,1))
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_conv.rb b/jni/ruby/test/date/test_date_conv.rb
new file mode 100644
index 0000000..daf0374
--- /dev/null
+++ b/jni/ruby/test/date/test_date_conv.rb
@@ -0,0 +1,137 @@
+require 'test/unit'
+require 'date'
+
+class TestDateConv < Test::Unit::TestCase
+
+ def test_to_class
+ [Time.now, Date.today, DateTime.now].each do |o|
+ assert_instance_of(Time, o.to_time)
+ assert_instance_of(Date, o.to_date)
+ assert_instance_of(DateTime, o.to_datetime)
+ end
+ end
+
+ def test_to_time__from_time
+ t = Time.mktime(2004, 9, 19, 1, 2, 3, 456789)
+ t2 = t.to_time
+ assert_equal([2004, 9, 19, 1, 2, 3, 456789],
+ [t2.year, t2.mon, t2.mday, t2.hour, t2.min, t2.sec, t2.usec])
+
+ t = Time.utc(2004, 9, 19, 1, 2, 3, 456789)
+ t2 = t.to_time.utc
+ assert_equal([2004, 9, 19, 1, 2, 3, 456789],
+ [t2.year, t2.mon, t2.mday, t2.hour, t2.min, t2.sec, t2.usec])
+ end
+
+ def test_to_time__from_date
+ d = Date.new(2004, 9, 19)
+ t = d.to_time
+ assert_equal([2004, 9, 19, 0, 0, 0, 0],
+ [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.usec])
+ end
+
+ def test_to_time__from_datetime
+ d = DateTime.new(2004, 9, 19, 1, 2, 3, 9.to_r/24) + 456789.to_r/86400000000
+ t = d.to_time
+ if t.utc_offset == 9*60*60
+ assert_equal([2004, 9, 19, 1, 2, 3, 456789],
+ [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.usec])
+ end
+
+ d = DateTime.new(2004, 9, 19, 1, 2, 3, 0) + 456789.to_r/86400000000
+ t = d.to_time.utc
+ assert_equal([2004, 9, 19, 1, 2, 3, 456789],
+ [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.usec])
+
+ if Time.allocate.respond_to?(:nsec)
+ d = DateTime.new(2004, 9, 19, 1, 2, 3, 0) + 456789123.to_r/86400000000000
+ t = d.to_time.utc
+ assert_equal([2004, 9, 19, 1, 2, 3, 456789123],
+ [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.nsec])
+ end
+
+ if Time.allocate.respond_to?(:subsec)
+ d = DateTime.new(2004, 9, 19, 1, 2, 3, 0) + 456789123456789123.to_r/86400000000000000000000
+ t = d.to_time.utc
+ assert_equal([2004, 9, 19, 1, 2, 3, Rational(456789123456789123,1000000000000000000)],
+ [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.subsec])
+ end
+ end
+
+ def test_to_date__from_time
+ t = Time.mktime(2004, 9, 19, 1, 2, 3, 456789)
+ d = t.to_date
+ assert_equal([2004, 9, 19, 0], [d.year, d.mon, d.mday, d.day_fraction])
+
+ t = Time.utc(2004, 9, 19, 1, 2, 3, 456789)
+ d = t.to_date
+ assert_equal([2004, 9, 19, 0], [d.year, d.mon, d.mday, d.day_fraction])
+ end
+
+ def test_to_date__from_date
+ d = Date.new(2004, 9, 19) + 1.to_r/2
+ d2 = d.to_date
+ assert_equal([2004, 9, 19, 1.to_r/2],
+ [d2.year, d2.mon, d2.mday, d2.day_fraction])
+ end
+
+ def test_to_date__from_datetime
+ d = DateTime.new(2004, 9, 19, 1, 2, 3, 9.to_r/24) + 456789.to_r/86400000000
+ d2 = d.to_date
+ assert_equal([2004, 9, 19, 0], [d2.year, d2.mon, d2.mday, d2.day_fraction])
+
+ d = DateTime.new(2004, 9, 19, 1, 2, 3, 0) + 456789.to_r/86400000000
+ d2 = d.to_date
+ assert_equal([2004, 9, 19, 0], [d2.year, d2.mon, d2.mday, d2.day_fraction])
+ end
+
+ def test_to_datetime__from_time
+ t = Time.mktime(2004, 9, 19, 1, 2, 3, 456789)
+ d = t.to_datetime
+ assert_equal([2004, 9, 19, 1, 2, 3,
+ 456789.to_r/1000000,
+ t.utc_offset.to_r/86400],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec,
+ d.sec_fraction, d.offset])
+
+ t = Time.utc(2004, 9, 19, 1, 2, 3, 456789)
+ d = t.to_datetime
+ assert_equal([2004, 9, 19, 1, 2, 3,
+ 456789.to_r/1000000,
+ 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec,
+ d.sec_fraction, d.offset])
+
+ t = Time.now
+ d = t.to_datetime
+ require 'time'
+ assert_equal(t.iso8601(10), d.iso8601(10))
+ end
+
+ def test_to_datetime__from_date
+ d = Date.new(2004, 9, 19) + 1.to_r/2
+ d2 = d.to_datetime
+ assert_equal([2004, 9, 19, 0, 0, 0, 0, 0],
+ [d2.year, d2.mon, d2.mday, d2.hour, d2.min, d2.sec,
+ d2.sec_fraction, d2.offset])
+ end
+
+ def test_to_datetime__from_datetime
+ d = DateTime.new(2004, 9, 19, 1, 2, 3, 9.to_r/24) + 456789.to_r/86400000000
+ d2 = d.to_datetime
+ assert_equal([2004, 9, 19, 1, 2, 3,
+ 456789.to_r/1000000,
+ 9.to_r/24],
+ [d2.year, d2.mon, d2.mday, d2.hour, d2.min, d2.sec,
+ d2.sec_fraction, d2.offset])
+
+ d = DateTime.new(2004, 9, 19, 1, 2, 3, 0) + 456789.to_r/86400000000
+ d2 = d.to_datetime
+ assert_equal([2004, 9, 19, 1, 2, 3,
+ 456789.to_r/1000000,
+ 0],
+ [d2.year, d2.mon, d2.mday, d2.hour, d2.min, d2.sec,
+ d2.sec_fraction, d2.offset])
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_marshal.rb b/jni/ruby/test/date/test_date_marshal.rb
new file mode 100644
index 0000000..4ea5565
--- /dev/null
+++ b/jni/ruby/test/date/test_date_marshal.rb
@@ -0,0 +1,41 @@
+require 'test/unit'
+require 'date'
+
+class TestDateMarshal < Test::Unit::TestCase
+
+ def test_marshal
+ d = Date.new
+ m = Marshal.dump(d)
+ d2 = Marshal.load(m)
+ assert_equal(d, d2)
+ assert_equal(d.start, d2.start)
+ assert_instance_of(String, d2.to_s)
+
+ d = Date.today
+ m = Marshal.dump(d)
+ d2 = Marshal.load(m)
+ assert_equal(d, d2)
+ assert_equal(d.start, d2.start)
+ assert_instance_of(String, d2.to_s)
+
+ d = DateTime.now
+ m = Marshal.dump(d)
+ d2 = Marshal.load(m)
+ assert_equal(d, d2)
+ assert_equal(d.start, d2.start)
+ assert_instance_of(String, d2.to_s)
+
+ d = Date.today
+ a = d.marshal_dump
+ d.freeze
+ assert(d.frozen?)
+ assert_raise(RuntimeError){d.marshal_load(a)}
+
+ d = DateTime.now
+ a = d.marshal_dump
+ d.freeze
+ assert(d.frozen?)
+ assert_raise(RuntimeError){d.marshal_load(a)}
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_new.rb b/jni/ruby/test/date/test_date_new.rb
new file mode 100644
index 0000000..0bbbfee
--- /dev/null
+++ b/jni/ruby/test/date/test_date_new.rb
@@ -0,0 +1,271 @@
+require 'test/unit'
+require 'date'
+
+class TestDateNew < Test::Unit::TestCase
+
+ def test_jd
+ d = Date.jd
+ dt = DateTime.jd
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ assert_equal([-4712, 1, 1], [dt.year, dt.mon, dt.mday])
+ assert_equal([0, 0, 0], [dt.hour, dt.min, dt.sec])
+
+ d2 = Date.jd
+ dt2 = DateTime.jd
+ assert_equal(d, d2)
+ assert_equal(dt, dt2)
+
+ d = Date.jd(0)
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ d = DateTime.jd(0, 0,0,0, 0)
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.jd(0, 0,0,0, '+0900')
+ assert_equal([-4712, 1, 1, 0, 0, 0, 9.to_r/24],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_jd__ex
+ assert_raise(ArgumentError) do
+ DateTime.jd(0, 23,59,60,0)
+ end
+ end
+
+ def test_ordinal
+ d = Date.ordinal
+ dt = DateTime.ordinal
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ assert_equal([-4712, 1, 1], [dt.year, dt.mon, dt.mday])
+ assert_equal([0, 0, 0], [dt.hour, dt.min, dt.sec])
+
+ d2 = Date.ordinal
+ dt2 = DateTime.ordinal
+ assert_equal(d, d2)
+ assert_equal(dt, dt2)
+
+ d = Date.ordinal(-4712,1)
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+
+ d = Date.ordinal(-4712,1.0)
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+
+ d = DateTime.ordinal(-4712,1, 0,0,0, 0)
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.ordinal(-4712,1, 0,0,0, '+0900')
+ assert_equal([-4712, 1, 1, 0, 0, 0, 9.to_r/24],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_ordinal__neg
+ d = Date.ordinal(-1,-1)
+ assert_equal([-1, 365], [d.year, d.yday])
+
+ d = DateTime.ordinal(-1,-1, -1,-1,-1, 0)
+ assert_equal([-1, 365, 23, 59, 59, 0],
+ [d.year, d.yday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_ordinal__ex
+ assert_raise(ArgumentError) do
+ Date.ordinal(2001,366)
+ end
+ assert_raise(ArgumentError) do
+ DateTime.ordinal(2001,365, 23,59,60, 0)
+ end
+ end
+
+ def test_civil
+ d = Date.civil
+ dt = DateTime.civil
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ assert_equal([-4712, 1, 1], [dt.year, dt.mon, dt.mday])
+ assert_equal([0, 0, 0], [dt.hour, dt.min, dt.sec])
+
+ d2 = Date.civil
+ dt2 = DateTime.civil
+ assert_equal(d, d2)
+ assert_equal(dt, dt2)
+
+ d = Date.civil(-4712,1,1)
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+
+ d = Date.civil(-4712,1,1.0)
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+
+ d = DateTime.civil(-4712,1,1, 0,0,0, 0)
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.civil(-4712,1,1, 0,0,0, '+0900')
+ assert_equal([-4712, 1, 1, 0, 0, 0, 9.to_r/24],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+
+
+ d = DateTime.civil(2001,2,3 + 1.to_r/2)
+ assert_equal([2001, 2, 3, 12, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.civil(2001,2,3, 4 + 1.to_r/2)
+ assert_equal([2001, 2, 3, 4, 30, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.civil(2001,2,3, 4,5 + 1.to_r/2)
+ assert_equal([2001, 2, 3, 4, 5, 30, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.civil(2001,2,3, 4,5,6 + 1.to_r/2)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ assert_equal(1.to_r/2, d.sec_fraction)
+ end
+
+ def test_civil__neg
+ d = Date.civil(-1,-1,-1)
+ assert_equal([-1, 12, 31], [d.year, d.mon, d.mday])
+
+ d = DateTime.civil(-1,-1,-1, -1,-1,-1, 0)
+ assert_equal([-1, 12, 31, 23, 59, 59, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_civil__ex
+ assert_raise(ArgumentError) do
+ Date.civil(2001,2,29)
+ end
+ assert_raise(ArgumentError) do
+ DateTime.civil(2001,2,28, 23,59,60, 0)
+ end
+ assert_raise(ArgumentError) do
+ DateTime.civil(2001,2,28, 24,59,59, 0)
+ end
+ end
+
+ def test_civil__reform
+ d = Date.jd(Date::ENGLAND, Date::ENGLAND)
+ dt = DateTime.jd(Date::ENGLAND, 0,0,0,0, Date::ENGLAND)
+ assert_equal([1752, 9, 14], [d.year, d.mon, d.mday])
+ assert_equal([1752, 9, 14], [dt.year, dt.mon, dt.mday])
+ d -= 1
+ dt -= 1
+ assert_equal([1752, 9, 2], [d.year, d.mon, d.mday])
+ assert_equal([1752, 9, 2], [dt.year, dt.mon, dt.mday])
+
+ d = Date.jd(Date::ITALY, Date::ITALY)
+ dt = DateTime.jd(Date::ITALY, 0,0,0,0, Date::ITALY)
+ assert_equal([1582, 10, 15], [d.year, d.mon, d.mday])
+ assert_equal([1582, 10, 15], [dt.year, dt.mon, dt.mday])
+ d -= 1
+ dt -= 1
+ assert_equal([1582, 10, 4], [d.year, d.mon, d.mday])
+ assert_equal([1582, 10, 4], [dt.year, dt.mon, dt.mday])
+ end
+
+ def test_commercial
+ d = Date.commercial
+ dt = DateTime.commercial
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ assert_equal([-4712, 1, 1], [dt.year, dt.mon, dt.mday])
+ assert_equal([0, 0, 0], [dt.hour, dt.min, dt.sec])
+
+ d2 = Date.commercial
+ dt2 = DateTime.commercial
+ assert_equal(d, d2)
+ assert_equal(dt, dt2)
+
+ d = Date.commercial(1582,40,5)
+ assert_equal([1582, 10, 15], [d.year, d.mon, d.mday])
+
+ d = Date.commercial(1582,40,5.0)
+ assert_equal([1582, 10, 15], [d.year, d.mon, d.mday])
+
+ d = DateTime.commercial(1582,40,5, 0,0,0, 0)
+ assert_equal([1582, 10, 15, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.commercial(1582,40,5, 0,0,0, '+0900')
+ assert_equal([1582, 10, 15, 0, 0, 0, 9.to_r/24],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_commercial__neg
+ d = Date.commercial(1998,-1,-1)
+ assert_equal([1999, 1, 3], [d.year, d.mon, d.mday])
+
+ d = DateTime.commercial(1998,-1,-1, -1,-1,-1, 0)
+ assert_equal([1999, 1, 3, 23, 59, 59, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_commercial__ex
+ assert_raise(ArgumentError) do
+ Date.commercial(1997,53,1)
+ end
+ assert_raise(ArgumentError) do
+ DateTime.commercial(1997,52,1, 23,59,60, 0)
+ end
+ end
+
+ def test_weeknum
+ skip unless Date.respond_to?(:weeknum, true)
+ d = Date.__send__(:weeknum)
+ dt = DateTime.__send__(:weeknum)
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ assert_equal([-4712, 1, 1], [dt.year, dt.mon, dt.mday])
+ assert_equal([0, 0, 0], [dt.hour, dt.min, dt.sec])
+
+ d = Date.__send__(:weeknum, 2002,11,4, 0)
+ assert_equal(2452355, d.jd)
+
+ d = DateTime.__send__(:weeknum, 2002,11,4, 0, 11,22,33)
+ assert_equal(2452355, d.jd)
+ assert_equal([11,22,33], [d.hour, d.min, d.sec])
+
+ assert_raise(ArgumentError) do
+ Date.__send__(:weeknum, 1999,53,0, 0)
+ end
+ assert_raise(ArgumentError) do
+ Date.__send__(:weeknum, 1999,-53,-1, 0)
+ end
+ end
+
+ def test_nth_kday
+ skip unless Date.respond_to?(:nth_kday, true)
+ d = Date.__send__(:nth_kday)
+ dt = DateTime.__send__(:nth_kday)
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ assert_equal([-4712, 1, 1], [dt.year, dt.mon, dt.mday])
+ assert_equal([0, 0, 0], [dt.hour, dt.min, dt.sec])
+
+ d = Date.__send__(:nth_kday, 1992,2, 5,6)
+ assert_equal(2448682, d.jd)
+
+ d = DateTime.__send__(:nth_kday, 1992,2, 5,6, 11,22,33)
+ assert_equal(2448682, d.jd)
+ assert_equal([11,22,33], [d.hour, d.min, d.sec])
+
+ assert_raise(ArgumentError) do
+ Date.__send__(:nth_kday, 2006,5, 5,0)
+ end
+ assert_raise(ArgumentError) do
+ Date.__send__(:nth_kday, 2006,5, -5,0)
+ end
+ end
+
+ def test_today
+ z = Time.now
+ d = Date.today
+ t = Time.now
+ t2 = Time.utc(t.year, t.mon, t.mday)
+ t3 = Time.utc(d.year, d.mon, d.mday)
+ assert_in_delta(t2, t3, t - z + 2)
+
+ assert_equal(false, DateTime.respond_to?(:today))
+ end
+
+ def test_now
+ assert_equal(false, Date.respond_to?(:now))
+
+ z = Time.now
+ d = DateTime.now
+ t = Time.now
+ t2 = Time.local(d.year, d.mon, d.mday, d.hour, d.min, d.sec)
+ assert_in_delta(t, t2, t - z + 2)
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_parse.rb b/jni/ruby/test/date/test_date_parse.rb
new file mode 100644
index 0000000..e0cd602
--- /dev/null
+++ b/jni/ruby/test/date/test_date_parse.rb
@@ -0,0 +1,1124 @@
+require 'test/unit'
+require 'date'
+
+class TestDateParse < Test::Unit::TestCase
+
+ def test__parse
+ [
+ # ctime(3), asctime(3)
+ [['Sat Aug 28 02:55:50 1999',false],[1999,8,28,2,55,50,nil,nil,6], __LINE__],
+ [['Sat Aug 28 02:55:50 02',false],[2,8,28,2,55,50,nil,nil,6], __LINE__],
+ [['Sat Aug 28 02:55:50 02',true],[2002,8,28,2,55,50,nil,nil,6], __LINE__],
+ [['Sat Aug 28 02:55:50 0002',false],[2,8,28,2,55,50,nil,nil,6], __LINE__],
+ [['Sat Aug 28 02:55:50 0002',true],[2,8,28,2,55,50,nil,nil,6], __LINE__],
+
+ # date(1)
+ [['Sat Aug 28 02:29:34 JST 1999',false],[1999,8,28,2,29,34,'JST',9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 MET DST 1999',false],[1999,8,28,2,29,34,'MET DST',2*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 AMT 1999',false],[1999,8,28,2,29,34,'AMT',nil,6], __LINE__],
+ [['Sat Aug 28 02:29:34 PMT 1999',false],[1999,8,28,2,29,34,'PMT',nil,6], __LINE__],
+ [['Sat Aug 28 02:29:34 PMT -1999',false],[-1999,8,28,2,29,34,'PMT',nil,6], __LINE__],
+
+ [['Sat Aug 28 02:29:34 JST 02',false],[2,8,28,2,29,34,'JST',9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 JST 02',true],[2002,8,28,2,29,34,'JST',9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 JST 0002',false],[2,8,28,2,29,34,'JST',9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 JST 0002',true],[2,8,28,2,29,34,'JST',9*3600,6], __LINE__],
+
+ [['Sat Aug 28 02:29:34 GMT+09 0002',false],[2,8,28,2,29,34,'GMT+09',9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT+0900 0002',false],[2,8,28,2,29,34,'GMT+0900',9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT+09:00 0002',false],[2,8,28,2,29,34,'GMT+09:00',9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT-09 0002',false],[2,8,28,2,29,34,'GMT-09',-9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT-0900 0002',false],[2,8,28,2,29,34,'GMT-0900',-9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT-09:00 0002',false],[2,8,28,2,29,34,'GMT-09:00',-9*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT-090102 0002',false],[2,8,28,2,29,34,'GMT-090102',-9*3600-60-2,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT-09:01:02 0002',false],[2,8,28,2,29,34,'GMT-09:01:02',-9*3600-60-2,6], __LINE__],
+
+ [['Sat Aug 28 02:29:34 GMT Standard Time 2000',false],[2000,8,28,2,29,34,'GMT Standard Time',0*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 Mountain Standard Time 2000',false],[2000,8,28,2,29,34,'Mountain Standard Time',-7*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 Mountain Daylight Time 2000',false],[2000,8,28,2,29,34,'Mountain Daylight Time',-6*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 Mexico Standard Time 2000',false],[2000,8,28,2,29,34,'Mexico Standard Time',-6*3600,6], __LINE__],
+ [['Sat Aug 28 02:29:34 E. Australia Standard Time 2000',false],[2000,8,28,2,29,34,'E. Australia Standard Time',10*3600,6], __LINE__],
+
+ # part of iso 8601
+ [['1999-05-23 23:55:21',false],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['1999-05-23 23:55:21+0900',false],[1999,5,23,23,55,21,'+0900',9*3600,nil], __LINE__],
+ [['1999-05-23 23:55:21-0900',false],[1999,5,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['1999-05-23 23:55:21+09:00',false],[1999,5,23,23,55,21,'+09:00',9*3600,nil], __LINE__],
+ [['1999-05-23T23:55:21-09:00',false],[1999,5,23,23,55,21,'-09:00',-9*3600,nil], __LINE__],
+ [['1999-05-23 23:55:21Z',false],[1999,5,23,23,55,21,'Z',0,nil], __LINE__],
+ [['1999-05-23T23:55:21Z',false],[1999,5,23,23,55,21,'Z',0,nil], __LINE__],
+ [['-1999-05-23T23:55:21Z',false],[-1999,5,23,23,55,21,'Z',0,nil], __LINE__],
+ [['-1999-05-23T23:55:21Z',true],[-1999,5,23,23,55,21,'Z',0,nil], __LINE__],
+ [['19990523T23:55:21Z',false],[1999,5,23,23,55,21,'Z',0,nil], __LINE__],
+
+ [['+011985-04-12',false],[11985,4,12,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['+011985-04-12T10:15:30',false],[11985,4,12,10,15,30,nil,nil,nil], __LINE__],
+ [['-011985-04-12',false],[-11985,4,12,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['-011985-04-12T10:15:30',false],[-11985,4,12,10,15,30,nil,nil,nil], __LINE__],
+
+ [['02-04-12',false],[2,4,12,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['02-04-12',true],[2002,4,12,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['0002-04-12',false],[2,4,12,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['0002-04-12',true],[2,4,12,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['19990523',true],[1999,5,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['-19990523',true],[-1999,5,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['990523',true],[1999,5,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['0523',false],[nil,5,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['23',false],[nil,nil,23,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['19990523 235521',true],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['990523 235521',true],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['0523 2355',false],[nil,5,23,23,55,nil,nil,nil,nil], __LINE__],
+ [['23 2355',false],[nil,nil,23,23,55,nil,nil,nil,nil], __LINE__],
+
+ [['19990523T235521',true],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['990523T235521',true],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['19990523T235521.99',true],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['990523T235521.99',true],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['0523T2355',false],[nil,5,23,23,55,nil,nil,nil,nil], __LINE__],
+
+ [['19990523T235521+0900',true],[1999,5,23,23,55,21,'+0900',9*3600,nil], __LINE__],
+ [['990523T235521-0900',true],[1999,5,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['19990523T235521.99+0900',true],[1999,5,23,23,55,21,'+0900',9*3600,nil], __LINE__],
+ [['990523T235521.99-0900',true],[1999,5,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['0523T2355Z',false],[nil,5,23,23,55,nil,'Z',0,nil], __LINE__],
+
+ [['19990523235521.123456+0900',true],[1999,5,23,23,55,21,'+0900',9*3600,nil], __LINE__],
+ [['19990523235521.123456-0900',true],[1999,5,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['19990523235521,123456+0900',true],[1999,5,23,23,55,21,'+0900',9*3600,nil], __LINE__],
+ [['19990523235521,123456-0900',true],[1999,5,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+
+ [['990523235521,123456-0900',false],[99,5,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['0523235521,123456-0900',false],[nil,5,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['23235521,123456-0900',false],[nil,nil,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['235521,123456-0900',false],[nil,nil,nil,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['5521,123456-0900',false],[nil,nil,nil,nil,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['21,123456-0900',false],[nil,nil,nil,nil,nil,21,'-0900',-9*3600,nil], __LINE__],
+
+ [['3235521,123456-0900',false],[nil,nil,3,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['35521,123456-0900',false],[nil,nil,nil,3,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['521,123456-0900',false],[nil,nil,nil,nil,5,21,'-0900',-9*3600,nil], __LINE__],
+
+ # reversed iso 8601 (?)
+ [['23-05-1999',false],[1999,5,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['23-05-1999 23:55:21',false],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['23-05--1999 23:55:21',false],[-1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [["23-05-'99",false],[99,5,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["23-05-'99",true],[1999,5,23,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # broken iso 8601 (?)
+ [['19990523T23:55:21Z',false],[1999,5,23,23,55,21,'Z',0,nil], __LINE__],
+ [['19990523235521.1234-100',true],[1999,5,23,23,55,21,'-100',-1*3600,nil], __LINE__],
+ [['19990523235521.1234-10',true],[1999,5,23,23,55,21,'-10',-10*3600,nil], __LINE__],
+
+ # part of jis x0301
+ [['M11.05.23',false],[1878,5,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['T11.05.23 23:55:21+0900',false],[1922,5,23,23,55,21,'+0900',9*3600,nil], __LINE__],
+ [['S11.05.23 23:55:21-0900',false],[1936,5,23,23,55,21,'-0900',-9*3600,nil], __LINE__],
+ [['S40.05.23 23:55:21+09:00',false],[1965,5,23,23,55,21,'+09:00',9*3600,nil], __LINE__],
+ [['S40.05.23T23:55:21-09:00',false],[1965,5,23,23,55,21,'-09:00',-9*3600,nil], __LINE__],
+ [['H11.05.23 23:55:21Z',false],[1999,5,23,23,55,21,'Z',0,nil], __LINE__],
+ [['H11.05.23T23:55:21Z',false],[1999,5,23,23,55,21,'Z',0,nil], __LINE__],
+
+ # ofx date
+ [['19990523235521',false],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['19990523235521.123',false],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
+ [['19990523235521.123[-9]',false],[1999,5,23,23,55,21,'-9',-(9*3600),nil], __LINE__],
+ [['19990523235521.123[+9]',false],[1999,5,23,23,55,21,'+9',+(9*3600),nil], __LINE__],
+ [['19990523235521.123[9]',false],[1999,5,23,23,55,21,'9',+(9*3600),nil], __LINE__],
+ [['19990523235521.123[-9.50]',false],[1999,5,23,23,55,21,'-9.50',-(9*3600+30*60),nil], __LINE__],
+ [['19990523235521.123[+9.50]',false],[1999,5,23,23,55,21,'+9.50',+(9*3600+30*60),nil], __LINE__],
+ [['19990523235521.123[-5:EST]',false],[1999,5,23,23,55,21,'EST',-5*3600,nil], __LINE__],
+ [['19990523235521.123[+9:JST]',false],[1999,5,23,23,55,21,'JST',9*3600,nil], __LINE__],
+ [['19990523235521.123[+12:XXX YYY ZZZ]',false],[1999,5,23,23,55,21,'XXX YYY ZZZ',12*3600,nil], __LINE__],
+ [['235521.123',false],[nil,nil,nil,23,55,21,nil,nil,nil], __LINE__],
+ [['235521.123[-9]',false],[nil,nil,nil,23,55,21,'-9',-9*3600,nil], __LINE__],
+ [['235521.123[+9]',false],[nil,nil,nil,23,55,21,'+9',+9*3600,nil], __LINE__],
+ [['235521.123[-5:EST]',false],[nil,nil,nil,23,55,21,'EST',-5*3600,nil], __LINE__],
+ [['235521.123[+9:JST]',false],[nil,nil,nil,23,55,21,'JST',+9*3600,nil], __LINE__],
+
+ # rfc 2822
+ [['Sun, 22 Aug 1999 00:45:29 -0400',false],[1999,8,22,0,45,29,'-0400',-4*3600,0], __LINE__],
+ [['Sun, 22 Aug 1999 00:45:29 -9959',false],[1999,8,22,0,45,29,'-9959',-(99*3600+59*60),0], __LINE__],
+ [['Sun, 22 Aug 1999 00:45:29 +9959',false],[1999,8,22,0,45,29,'+9959',+(99*3600+59*60),0], __LINE__],
+ [['Sun, 22 Aug 05 00:45:29 -0400',true],[2005,8,22,0,45,29,'-0400',-4*3600,0], __LINE__],
+ [['Sun, 22 Aug 49 00:45:29 -0400',true],[2049,8,22,0,45,29,'-0400',-4*3600,0], __LINE__],
+ [['Sun, 22 Aug 1999 00:45:29 GMT',false],[1999,8,22,0,45,29,'GMT',0,0], __LINE__],
+ [["Sun,\00022\r\nAug\r\n1999\r\n00:45:29\r\nGMT",false],[1999,8,22,0,45,29,'GMT',0,0], __LINE__],
+ [['Sun, 22 Aug 1999 00:45 GMT',false],[1999,8,22,0,45,nil,'GMT',0,0], __LINE__],
+ [['Sun, 22 Aug -1999 00:45 GMT',false],[-1999,8,22,0,45,nil,'GMT',0,0], __LINE__],
+ [['Sun, 22 Aug 99 00:45:29 UT',true],[1999,8,22,0,45,29,'UT',0,0], __LINE__],
+ [['Sun, 22 Aug 0099 00:45:29 UT',true],[99,8,22,0,45,29,'UT',0,0], __LINE__],
+
+ # rfc 850, obsoleted by rfc 1036
+ [['Tuesday, 02-Mar-99 11:20:32 GMT',true],[1999,3,2,11,20,32,'GMT',0,2], __LINE__],
+
+ # W3C Working Draft - XForms - 4.8 Time
+ [['2000-01-31 13:20:00-5',false],[2000,1,31,13,20,0,'-5',-5*3600,nil], __LINE__],
+
+ # [-+]\d+.\d+
+ [['2000-01-31 13:20:00-5.5',false],[2000,1,31,13,20,0,'-5.5',-5*3600-30*60,nil], __LINE__],
+ [['2000-01-31 13:20:00-5,5',false],[2000,1,31,13,20,0,'-5,5',-5*3600-30*60,nil], __LINE__],
+ [['2000-01-31 13:20:00+3.5',false],[2000,1,31,13,20,0,'+3.5',3*3600+30*60,nil], __LINE__],
+ [['2000-01-31 13:20:00+3,5',false],[2000,1,31,13,20,0,'+3,5',3*3600+30*60,nil], __LINE__],
+
+ # mil
+ [['2000-01-31 13:20:00 Z',false],[2000,1,31,13,20,0,'Z',0*3600,nil], __LINE__],
+ [['2000-01-31 13:20:00 H',false],[2000,1,31,13,20,0,'H',8*3600,nil], __LINE__],
+ [['2000-01-31 13:20:00 M',false],[2000,1,31,13,20,0,'M',12*3600,nil], __LINE__],
+ [['2000-01-31 13:20 M',false],[2000,1,31,13,20,nil,'M',12*3600,nil], __LINE__],
+ [['2000-01-31 13:20:00 S',false],[2000,1,31,13,20,0,'S',-6*3600,nil], __LINE__],
+ [['2000-01-31 13:20:00 A',false],[2000,1,31,13,20,0,'A',1*3600,nil], __LINE__],
+ [['2000-01-31 13:20:00 P',false],[2000,1,31,13,20,0,'P',-3*3600,nil], __LINE__],
+
+ # dot
+ [['1999.5.2',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['1999.05.02',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['-1999.05.02',false],[-1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['0099.5.2',false],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['0099.5.2',true],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [["'99.5.2",false],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["'99.5.2",true],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # reversed dot
+ [['2.5.1999',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['02.05.1999',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['02.05.-1999',false],[-1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['2.5.0099',false],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2.5.0099',true],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [["2.5.'99",false],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["2.5.'99",true],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # vms
+ [['08-DEC-1988',false],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['31-JAN-1999',false],[1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['31-JAN--1999',false],[-1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['08-DEC-88',false],[88,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['08-DEC-88',true],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['08-DEC-0088',false],[88,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['08-DEC-0088',true],[88,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # swaped vms
+ [['DEC-08-1988',false],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['JAN-31-1999',false],[1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['JAN-31--1999',false],[-1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['JAN-1999',false],[1999,1,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['JAN--1999',false],[-1999,1,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # reversed vms
+ [['1988-DEC-08',false],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['1999-JAN-31',false],[1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['-1999-JAN-31',false],[-1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['0088-DEC-08',false],[88,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['0088-DEC-08',true],[88,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [["'88/12/8",false],[88,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["'88/12/8",true],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # non-spaced eu
+ [['08/dec/1988',false],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['31/jan/1999',false],[1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['31/jan/-1999',false],[-1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['08.dec.1988',false],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['31.jan.1999',false],[1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['31.jan.-1999',false],[-1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # non-spaced us
+ [['dec/08/1988',false],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['jan/31/1999',false],[1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['jan/31/-1999',false],[-1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['jan/31',false],[nil,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['jan/1988',false],[1988,1,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['dec.08.1988',false],[1988,12,8,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['jan.31.1999',false],[1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['jan.31.-1999',false],[-1999,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['jan.31',false],[nil,1,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['jan.1988',false],[1988,1,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # month and day of month
+ [['Jan 1',false],[nil,1,1,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['Jul 11',false],[nil,7,11,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['July 11',false],[nil,7,11,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['Sept 23',false],[nil,9,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['Sep. 23',false],[nil,9,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['Sept. 23',false],[nil,9,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['September 23',false],[nil,9,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['October 1st',false],[nil,10,1,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['October 23rd',false],[nil,10,23,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['October 25th 1999',false],[1999,10,25,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['October 25th -1999',false],[-1999,10,25,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['october 25th 1999',false],[1999,10,25,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['OCTOBER 25th 1999',false],[1999,10,25,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['oCtoBer 25th 1999',false],[1999,10,25,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['aSep 23',false],[nil,nil,23,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # month and year
+ [['Sept 1990',false],[1990,9,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["Sept '90",false],[90,9,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["Sept '90",true],[1990,9,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['1990/09',false],[1990,9,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['09/1990',false],[1990,9,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["aSep '90",false],[90,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # year
+ [["'90",false],[90,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["'90",true],[1990,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # month
+ [['Jun',false],[nil,6,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['June',false],[nil,6,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['Sep',false],[nil,9,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['Sept',false],[nil,9,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['September',false],[nil,9,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['aSep',false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # day of month
+ [['1st',false],[nil,nil,1,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2nd',false],[nil,nil,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['3rd',false],[nil,nil,3,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['4th',false],[nil,nil,4,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['29th',false],[nil,nil,29,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['31st',false],[nil,nil,31,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['1sta',false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # era
+ [['Sat Aug 28 02:29:34 GMT CE 2000',false],[2000,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT C.E. 2000',false],[2000,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT BCE 2000',false],[-1999,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT B.C.E. 2000',false],[-1999,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT AD 2000',false],[2000,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT A.D. 2000',false],[2000,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT BC 2000',false],[-1999,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT B.C. 2000',false],[-1999,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT 2000 BC',false],[-1999,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT 2000 BCE',false],[-1999,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT 2000 B.C.',false],[-1999,8,28,2,29,34,'GMT',0,6], __LINE__],
+ [['Sat Aug 28 02:29:34 GMT 2000 B.C.E.',false],[-1999,8,28,2,29,34,'GMT',0,6], __LINE__],
+
+ # collection
+ [['Tuesday, May 18, 1999 Published at 13:36 GMT 14:36 UK',false],[1999,5,18,13,36,nil,'GMT',0,2], __LINE__], # bbc.co.uk
+ [['July 20, 2000 Web posted at: 3:37 p.m. EDT (1937 GMT)',false],[2000,7,20,15,37,nil,'EDT',-4*3600,nil], __LINE__], # cnn.com
+ [['12:54 p.m. EDT, September 11, 2006',false],[2006,9,11,12,54,nil,'EDT',-4*3600,nil], __LINE__], # cnn.com
+ [['February 04, 2001 at 10:59 AM PST',false],[2001,2,4,10,59,nil,'PST',-8*3600,nil], __LINE__], # old amazon.com
+ [['Monday May 08, @01:55PM',false],[nil,5,8,13,55,nil,nil,nil,1], __LINE__], # slashdot.org
+ [['06.June 2005',false],[2005,6,6,nil,nil,nil,nil,nil,nil], __LINE__], # dhl.com
+
+ # etc.
+ [['8:00 pm lt',false],[nil,nil,nil,20,0,nil,'lt',nil,nil], __LINE__],
+ [['4:00 AM, Jan. 12, 1990',false],[1990,1,12,4,0,nil,nil,nil,nil], __LINE__],
+ [['Jan. 12 4:00 AM 1990',false],[1990,1,12,4,0,nil,nil,nil,nil], __LINE__],
+ [['1990-01-12 04:00:00+00',false],[1990,1,12,4,0,0,'+00',0,nil], __LINE__],
+ [['1990-01-11 20:00:00-08',false],[1990,1,11,20,0,0,'-08',-8*3600,nil], __LINE__],
+ [['1990/01/12 04:00:00',false],[1990,1,12,4,0,0,nil,nil,nil], __LINE__],
+ [['Thu Jan 11 20:00:00 PST 1990',false],[1990,1,11,20,0,0,'PST',-8*3600,4], __LINE__],
+ [['Fri Jan 12 04:00:00 GMT 1990',false],[1990,1,12,4,0,0,'GMT',0,5], __LINE__],
+ [['Thu, 11 Jan 1990 20:00:00 -0800',false],[1990,1,11,20,0,0,'-0800',-8*3600,4], __LINE__],
+ [['12-January-1990, 04:00 WET',false],[1990,1,12,4,0,nil,'WET',0*3600,nil], __LINE__],
+ [['jan 2 3 am +4 5',false],[5,1,2,3,nil,nil,'+4',4*3600,nil], __LINE__],
+ [['jan 2 3 am +4 5',true],[2005,1,2,3,nil,nil,'+4',4*3600,nil], __LINE__],
+ [['fri1feb3bc4pm+5',false],[-2,2,1,16,nil,nil,'+5',5*3600,5], __LINE__],
+ [['fri1feb3bc4pm+5',true],[-2,2,1,16,nil,nil,'+5',5*3600,5], __LINE__],
+ [['03 feb 1st',false],[03,2,1,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # apostrophe
+ [["July 4, '79",true],[1979,7,4,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["4th July '79",true],[1979,7,4,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # day of week
+ [['Sunday',false],[nil,nil,nil,nil,nil,nil,nil,nil,0], __LINE__],
+ [['Mon',false],[nil,nil,nil,nil,nil,nil,nil,nil,1], __LINE__],
+ [['Tue',false],[nil,nil,nil,nil,nil,nil,nil,nil,2], __LINE__],
+ [['Wed',false],[nil,nil,nil,nil,nil,nil,nil,nil,3], __LINE__],
+ [['Thurs',false],[nil,nil,nil,nil,nil,nil,nil,nil,4], __LINE__],
+ [['Friday',false],[nil,nil,nil,nil,nil,nil,nil,nil,5], __LINE__],
+ [['Sat.',false],[nil,nil,nil,nil,nil,nil,nil,nil,6], __LINE__],
+ [['sat.',false],[nil,nil,nil,nil,nil,nil,nil,nil,6], __LINE__],
+ [['SAT.',false],[nil,nil,nil,nil,nil,nil,nil,nil,6], __LINE__],
+ [['sAt.',false],[nil,nil,nil,nil,nil,nil,nil,nil,6], __LINE__],
+
+ # time
+ [['09:55',false],[nil,nil,nil,9,55,nil,nil,nil,nil], __LINE__],
+ [['09:55:30',false],[nil,nil,nil,9,55,30,nil,nil,nil], __LINE__],
+ [['09:55:30am',false],[nil,nil,nil,9,55,30,nil,nil,nil], __LINE__],
+ [['09:55:30pm',false],[nil,nil,nil,21,55,30,nil,nil,nil], __LINE__],
+ [['09:55:30a.m.',false],[nil,nil,nil,9,55,30,nil,nil,nil], __LINE__],
+ [['09:55:30p.m.',false],[nil,nil,nil,21,55,30,nil,nil,nil], __LINE__],
+ [['09:55:30pm GMT',false],[nil,nil,nil,21,55,30,'GMT',0,nil], __LINE__],
+ [['09:55:30p.m. GMT',false],[nil,nil,nil,21,55,30,'GMT',0,nil], __LINE__],
+ [['09:55+0900',false],[nil,nil,nil,9,55,nil,'+0900',9*3600,nil], __LINE__],
+ [['09 AM',false],[nil,nil,nil,9,nil,nil,nil,nil,nil], __LINE__],
+ [['09am',false],[nil,nil,nil,9,nil,nil,nil,nil,nil], __LINE__],
+ [['09 A.M.',false],[nil,nil,nil,9,nil,nil,nil,nil,nil], __LINE__],
+ [['09 PM',false],[nil,nil,nil,21,nil,nil,nil,nil,nil], __LINE__],
+ [['09pm',false],[nil,nil,nil,21,nil,nil,nil,nil,nil], __LINE__],
+ [['09 P.M.',false],[nil,nil,nil,21,nil,nil,nil,nil,nil], __LINE__],
+
+ [['9h22m23s',false],[nil,nil,nil,9,22,23,nil,nil,nil], __LINE__],
+ [['9h 22m 23s',false],[nil,nil,nil,9,22,23,nil,nil,nil], __LINE__],
+ [['9h22m',false],[nil,nil,nil,9,22,nil,nil,nil,nil], __LINE__],
+ [['9h 22m',false],[nil,nil,nil,9,22,nil,nil,nil,nil], __LINE__],
+ [['9h',false],[nil,nil,nil,9,nil,nil,nil,nil,nil], __LINE__],
+ [['9h 22m 23s am',false],[nil,nil,nil,9,22,23,nil,nil,nil], __LINE__],
+ [['9h 22m 23s pm',false],[nil,nil,nil,21,22,23,nil,nil,nil], __LINE__],
+ [['9h 22m am',false],[nil,nil,nil,9,22,nil,nil,nil,nil], __LINE__],
+ [['9h 22m pm',false],[nil,nil,nil,21,22,nil,nil,nil,nil], __LINE__],
+ [['9h am',false],[nil,nil,nil,9,nil,nil,nil,nil,nil], __LINE__],
+ [['9h pm',false],[nil,nil,nil,21,nil,nil,nil,nil,nil], __LINE__],
+
+ [['00:00',false],[nil,nil,nil,0,0,nil,nil,nil,nil], __LINE__],
+ [['01:00',false],[nil,nil,nil,1,0,nil,nil,nil,nil], __LINE__],
+ [['11:00',false],[nil,nil,nil,11,0,nil,nil,nil,nil], __LINE__],
+ [['12:00',false],[nil,nil,nil,12,0,nil,nil,nil,nil], __LINE__],
+ [['13:00',false],[nil,nil,nil,13,0,nil,nil,nil,nil], __LINE__],
+ [['23:00',false],[nil,nil,nil,23,0,nil,nil,nil,nil], __LINE__],
+ [['24:00',false],[nil,nil,nil,24,0,nil,nil,nil,nil], __LINE__],
+
+ [['00:00 AM',false],[nil,nil,nil,0,0,nil,nil,nil,nil], __LINE__],
+ [['12:00 AM',false],[nil,nil,nil,0,0,nil,nil,nil,nil], __LINE__],
+ [['01:00 AM',false],[nil,nil,nil,1,0,nil,nil,nil,nil], __LINE__],
+ [['11:00 AM',false],[nil,nil,nil,11,0,nil,nil,nil,nil], __LINE__],
+ [['00:00 PM',false],[nil,nil,nil,12,0,nil,nil,nil,nil], __LINE__],
+ [['12:00 PM',false],[nil,nil,nil,12,0,nil,nil,nil,nil], __LINE__],
+ [['01:00 PM',false],[nil,nil,nil,13,0,nil,nil,nil,nil], __LINE__],
+ [['11:00 PM',false],[nil,nil,nil,23,0,nil,nil,nil,nil], __LINE__],
+
+ # pick up the rest
+ [['2000-01-02 1',false],[2000,1,2,1,nil,nil,nil,nil,nil], __LINE__],
+ [['2000-01-02 23',false],[2000,1,2,23,nil,nil,nil,nil,nil], __LINE__],
+ [['2000-01-02 24',false],[2000,1,2,24,nil,nil,nil,nil,nil], __LINE__],
+ [['1 03:04:05',false],[nil,nil,1,3,4,5,nil,nil,nil], __LINE__],
+ [['02 03:04:05',false],[nil,nil,2,3,4,5,nil,nil,nil], __LINE__],
+ [['31 03:04:05',false],[nil,nil,31,3,4,5,nil,nil,nil], __LINE__],
+
+ # null, space
+ [['',false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["\s",false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["\s" * 10, true],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["\t",false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["\n",false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["\v",false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["\f",false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["\r",false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["\t\n\v\f\r\s",false],[nil,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["1999-05-23\t\n\v\f\r\s21:34:56",false],[1999,5,23,21,34,56,nil,nil,nil], __LINE__],
+ ].each do |x,y,l|
+ h = Date._parse(*x)
+ a = h.values_at(:year,:mon,:mday,:hour,:min,:sec,:zone,:offset,:wday)
+ if y[1] == -1
+ a[1] = -1
+ a[2] = h[:yday]
+ end
+ assert_equal(y, a, format('<failed at line %d>', l))
+ end
+ end
+
+ def test__parse_slash_exp
+ [
+ # little
+ [['2/5/1999',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['02/05/1999',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['02/05/-1999',false],[-1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['05/02',false],[nil,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [[' 5/ 2',false],[nil,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [["2/5/'99",true],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2/5/0099',false],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2/5/0099',true],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['2/5 1999',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2/5-1999',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2/5--1999',false],[-1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ # big
+ [['99/5/2',false],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['99/5/2',true],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['1999/5/2',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['1999/05/02',false],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['-1999/05/02',false],[-1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['0099/5/2',false],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['0099/5/2',true],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [["'99/5/2",false],[99,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["'99/5/2",true],[1999,5,2,nil,nil,nil,nil,nil,nil], __LINE__],
+ ].each do |x,y,l|
+ h = Date._parse(*x)
+ a = h.values_at(:year,:mon,:mday,:hour,:min,:sec,:zone,:offset,:wday)
+ if y[1] == -1
+ a[1] = -1
+ a[2] = h[:yday]
+ end
+ assert_equal(y, a, format('<failed at line %d>', l))
+ end
+ end
+
+ def test__parse__2
+ h = Date._parse('22:45:59.5')
+ assert_equal([22, 45, 59, 5.to_r/10**1], h.values_at(:hour, :min, :sec, :sec_fraction))
+ h = Date._parse('22:45:59.05')
+ assert_equal([22, 45, 59, 5.to_r/10**2], h.values_at(:hour, :min, :sec, :sec_fraction))
+ h = Date._parse('22:45:59.005')
+ assert_equal([22, 45, 59, 5.to_r/10**3], h.values_at(:hour, :min, :sec, :sec_fraction))
+ h = Date._parse('22:45:59.0123')
+ assert_equal([22, 45, 59, 123.to_r/10**4], h.values_at(:hour, :min, :sec, :sec_fraction))
+
+ h = Date._parse('224559.5')
+ assert_equal([22, 45, 59, 5.to_r/10**1], h.values_at(:hour, :min, :sec, :sec_fraction))
+ h = Date._parse('224559.05')
+ assert_equal([22, 45, 59, 5.to_r/10**2], h.values_at(:hour, :min, :sec, :sec_fraction))
+ h = Date._parse('224559.005')
+ assert_equal([22, 45, 59, 5.to_r/10**3], h.values_at(:hour, :min, :sec, :sec_fraction))
+ h = Date._parse('224559.0123')
+ assert_equal([22, 45, 59, 123.to_r/10**4], h.values_at(:hour, :min, :sec, :sec_fraction))
+
+ h = Date._parse('2006-w15-5')
+ assert_equal([2006, 15, 5], h.values_at(:cwyear, :cweek, :cwday))
+ h = Date._parse('2006w155')
+ assert_equal([2006, 15, 5], h.values_at(:cwyear, :cweek, :cwday))
+ h = Date._parse('06w155', false)
+ assert_equal([6, 15, 5], h.values_at(:cwyear, :cweek, :cwday))
+ h = Date._parse('06w155', true)
+ assert_equal([2006, 15, 5], h.values_at(:cwyear, :cweek, :cwday))
+
+ h = Date._parse('2006-w15')
+ assert_equal([2006, 15, nil], h.values_at(:cwyear, :cweek, :cwday))
+ h = Date._parse('2006w15')
+ assert_equal([2006, 15, nil], h.values_at(:cwyear, :cweek, :cwday))
+
+ h = Date._parse('-w15-5')
+ assert_equal([nil, 15, 5], h.values_at(:cwyear, :cweek, :cwday))
+ h = Date._parse('-w155')
+ assert_equal([nil, 15, 5], h.values_at(:cwyear, :cweek, :cwday))
+
+ h = Date._parse('-w15')
+ assert_equal([nil, 15, nil], h.values_at(:cwyear, :cweek, :cwday))
+ h = Date._parse('-w15')
+ assert_equal([nil, 15, nil], h.values_at(:cwyear, :cweek, :cwday))
+
+ h = Date._parse('-w-5')
+ assert_equal([nil, nil, 5], h.values_at(:cwyear, :cweek, :cwday))
+
+ h = Date._parse('--11-29')
+ assert_equal([nil, 11, 29], h.values_at(:year, :mon, :mday))
+ h = Date._parse('--1129')
+ assert_equal([nil, 11, 29], h.values_at(:year, :mon, :mday))
+ h = Date._parse('--11')
+ assert_equal([nil, 11, nil], h.values_at(:year, :mon, :mday))
+ h = Date._parse('---29')
+ assert_equal([nil, nil, 29], h.values_at(:year, :mon, :mday))
+ h = Date._parse('-333')
+ assert_equal([nil, 333], h.values_at(:year, :yday))
+
+ h = Date._parse('2006-333')
+ assert_equal([2006, 333], h.values_at(:year, :yday))
+ h = Date._parse('2006333')
+ assert_equal([2006, 333], h.values_at(:year, :yday))
+ h = Date._parse('06333', false)
+ assert_equal([6, 333], h.values_at(:year, :yday))
+ h = Date._parse('06333', true)
+ assert_equal([2006, 333], h.values_at(:year, :yday))
+ h = Date._parse('333')
+ assert_equal([nil, 333], h.values_at(:year, :yday))
+
+ h = Date._parse('')
+ assert_equal({}, h)
+ end
+
+ def test_parse
+ assert_equal(Date.new, Date.parse)
+ assert_equal(Date.new(2002,3,14), Date.parse('2002-03-14'))
+
+ assert_equal(DateTime.new(2002,3,14,11,22,33, 0),
+ DateTime.parse('2002-03-14T11:22:33Z'))
+ assert_equal(DateTime.new(2002,3,14,11,22,33, 9.to_r/24),
+ DateTime.parse('2002-03-14T11:22:33+09:00'))
+ assert_equal(DateTime.new(2002,3,14,11,22,33, -9.to_r/24),
+ DateTime.parse('2002-03-14T11:22:33-09:00'))
+ assert_equal(DateTime.new(2002,3,14,11,22,33, -9.to_r/24) + 123456789.to_r/1000000000/86400,
+ DateTime.parse('2002-03-14T11:22:33.123456789-09:00'))
+ end
+
+ def test_parse__2
+ d1 = DateTime.parse('2004-03-13T22:45:59.5')
+ d2 = DateTime.parse('2004-03-13T22:45:59')
+ assert_equal(d2 + 5.to_r/10**1/86400, d1)
+ d1 = DateTime.parse('2004-03-13T22:45:59.05')
+ d2 = DateTime.parse('2004-03-13T22:45:59')
+ assert_equal(d2 + 5.to_r/10**2/86400, d1)
+ d1 = DateTime.parse('2004-03-13T22:45:59.005')
+ d2 = DateTime.parse('2004-03-13T22:45:59')
+ assert_equal(d2 + 5.to_r/10**3/86400, d1)
+ d1 = DateTime.parse('2004-03-13T22:45:59.0123')
+ d2 = DateTime.parse('2004-03-13T22:45:59')
+ assert_equal(d2 + 123.to_r/10**4/86400, d1)
+ d1 = DateTime.parse('2004-03-13T22:45:59.5')
+ d1 += 1.to_r/2/86400
+ d2 = DateTime.parse('2004-03-13T22:46:00')
+ assert_equal(d2, d1)
+ end
+
+ def test__parse_odd_offset
+ h = DateTime._parse('2001-02-03T04:05:06+1')
+ assert_equal(3600, h[:offset])
+ h = DateTime._parse('2001-02-03T04:05:06+123')
+ assert_equal(4980, h[:offset])
+ h = DateTime._parse('2001-02-03T04:05:06+12345')
+ assert_equal(5025, h[:offset])
+ end
+
+ require 'time'
+
+ def test_parse__time
+ methods = [:to_s, :asctime, :iso8601, :rfc2822, :httpdate, :xmlschema]
+
+ t = Time.utc(2001,2,3,4,5,6)
+ methods.each do |m|
+ d = DateTime.parse(t.__send__(m))
+ assert_equal([2001, 2, 3, 4, 5, 6],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec],
+ [m, t.__send__(m)].inspect)
+ end
+
+ t = Time.mktime(2001,2,3,4,5,6)
+ methods.each do |m|
+ next if m == :httpdate
+ d = DateTime.parse(t.__send__(m))
+ assert_equal([2001, 2, 3, 4, 5, 6],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec],
+ [m, t.__send__(m)].inspect)
+ end
+ end
+
+ def test_parse__comp
+ n = DateTime.now
+
+ d = DateTime.parse('073')
+ assert_equal([n.year, 73, 0, 0, 0],
+ [d.year, d.yday, d.hour, d.min, d.sec])
+ d = DateTime.parse('13')
+ assert_equal([n.year, n.mon, 13, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+
+ d = DateTime.parse('Mar 13')
+ assert_equal([n.year, 3, 13, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.parse('Mar 2004')
+ assert_equal([2004, 3, 1, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.parse('23:55')
+ assert_equal([n.year, n.mon, n.mday, 23, 55, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.parse('23:55:30')
+ assert_equal([n.year, n.mon, n.mday, 23, 55, 30],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+
+ d = DateTime.parse('Sun 23:55')
+ d2 = d - d.wday
+ assert_equal([d2.year, d2.mon, d2.mday, 23, 55, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.parse('Aug 23:55')
+ assert_equal([n.year, 8, 1, 23, 55, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ end
+
+ def test_parse__d_to_s
+ d = Date.new(2002,3,14)
+ assert_equal(d, Date.parse(d.to_s))
+
+ d = DateTime.new(2002,3,14,11,22,33, 9.to_r/24)
+ assert_equal(d, DateTime.parse(d.to_s))
+ end
+
+ def test_parse_utf8
+ h = DateTime._parse("Sun\u{3000}Aug 16 01:02:03 \u{65e5}\u{672c} 2009")
+ assert_equal(2009, h[:year])
+ assert_equal(8, h[:mon])
+ assert_equal(16, h[:mday])
+ assert_equal(0, h[:wday])
+ assert_equal(1, h[:hour])
+ assert_equal(2, h[:min])
+ assert_equal(3, h[:sec])
+ assert_equal("\u{65e5}\u{672c}", h[:zone])
+ end
+
+ def test_parse__ex
+ assert_raise(ArgumentError) do
+ Date.parse('')
+ end
+ assert_raise(ArgumentError) do
+ DateTime.parse('')
+ end
+ assert_raise(ArgumentError) do
+ Date.parse('2001-02-29')
+ end
+ assert_raise(ArgumentError) do
+ DateTime.parse('2001-02-29T23:59:60')
+ end
+ assert_nothing_raised(ArgumentError) do
+ DateTime.parse('2001-03-01T23:59:60')
+ end
+ assert_raise(ArgumentError) do
+ DateTime.parse('2001-03-01T23:59:61')
+ end
+ assert_raise(ArgumentError) do
+ Date.parse('23:55')
+ end
+ end
+
+ def test__iso8601
+ h = Date._iso8601('01-02-03T04:05:06Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001-02-03T04:05:06Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('--02-03T04:05:06Z')
+ assert_equal([nil, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('---03T04:05:06Z')
+ assert_equal([nil, nil, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('2001-02-03T04:05')
+ assert_equal([2001, 2, 3, 4, 5, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001-02-03T04:05:06')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001-02-03T04:05:06,07')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001-02-03T04:05:06Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001-02-03T04:05:06.07+01:00')
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('010203T040506Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203T040506Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('--0203T040506Z')
+ assert_equal([nil, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('---03T040506Z')
+ assert_equal([nil, nil, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('010203T0405')
+ assert_equal([2001, 2, 3, 4, 5, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203T0405')
+ assert_equal([2001, 2, 3, 4, 5, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203T040506')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203T040506,07')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203T040506Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203T040506.07+0100')
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('200102030405')
+ assert_equal([2001, 2, 3, 4, 5, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203040506')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203040506,07')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203040506Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('20010203040506.07+0100')
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('01-023T04:05:06Z')
+ assert_equal([2001, 23, 4, 5, 6, 0],
+ h.values_at(:year, :yday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001-023T04:05:06Z')
+ assert_equal([2001, 23, 4, 5, 6, 0],
+ h.values_at(:year, :yday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('-023T04:05:06Z')
+ assert_equal([nil, 23, 4, 5, 6, 0],
+ h.values_at(:year, :yday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('01023T040506Z')
+ assert_equal([2001, 23, 4, 5, 6, 0],
+ h.values_at(:year, :yday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001023T040506Z')
+ assert_equal([2001, 23, 4, 5, 6, 0],
+ h.values_at(:year, :yday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('-023T040506Z')
+ assert_equal([nil, 23, 4, 5, 6, 0],
+ h.values_at(:year, :yday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('01-w02-3T04:05:06Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:cwyear, :cweek, :cwday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001-w02-3T04:05:06Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:cwyear, :cweek, :cwday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('-w02-3T04:05:06Z')
+ assert_equal([nil, 2, 3, 4, 5, 6, 0],
+ h.values_at(:cwyear, :cweek, :cwday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('-w-3T04:05:06Z')
+ assert_equal([nil, nil, 3, 4, 5, 6, 0],
+ h.values_at(:cwyear, :cweek, :cwday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('01w023T040506Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:cwyear, :cweek, :cwday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('2001w023T040506Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:cwyear, :cweek, :cwday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('-w023T040506Z')
+ assert_equal([nil, 2, 3, 4, 5, 6, 0],
+ h.values_at(:cwyear, :cweek, :cwday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('-w-3T040506Z')
+ assert_equal([nil, nil, 3, 4, 5, 6, 0],
+ h.values_at(:cwyear, :cweek, :cwday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('04:05')
+ assert_equal([nil, nil, nil, 4, 5, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('04:05:06')
+ assert_equal([nil, nil, nil, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('04:05:06,07')
+ assert_equal([nil, nil, nil, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('04:05:06Z')
+ assert_equal([nil, nil, nil, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('04:05:06.07+01:00')
+ assert_equal([nil, nil, nil, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('040506,07')
+ assert_equal([nil, nil, nil, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._iso8601('040506.07+0100')
+ assert_equal([nil, nil, nil, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._iso8601('')
+ assert_equal({}, h)
+ end
+
+ def test__rfc3339
+ h = Date._rfc3339('2001-02-03T04:05:06Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._rfc3339('2001-02-03 04:05:06Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._rfc3339('2001-02-03T04:05:06.07+01:00')
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._rfc3339('')
+ assert_equal({}, h)
+ end
+
+ def test__xmlschema
+ h = Date._xmlschema('2001-02-03')
+ assert_equal([2001, 2, 3, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-02-03Z')
+ assert_equal([2001, 2, 3, nil, nil, nil, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-02-03+01:00')
+ assert_equal([2001, 2, 3, nil, nil, nil, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._xmlschema('2001-02-03T04:05:06')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-02-03T04:05:06.07')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-02-03T04:05:06.07Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-02-03T04:05:06.07+01:00')
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._xmlschema('04:05:06')
+ assert_equal([nil, nil, nil, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('04:05:06Z')
+ assert_equal([nil, nil, nil, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('04:05:06+01:00')
+ assert_equal([nil, nil, nil, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._xmlschema('2001-02')
+ assert_equal([2001, 2, nil, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-02Z')
+ assert_equal([2001, 2, nil, nil, nil, nil, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-02+01:00')
+ assert_equal([2001, 2, nil, nil, nil, nil, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-02-01:00')
+ assert_equal([2001, 2, nil, nil, nil, nil, -3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._xmlschema('2001')
+ assert_equal([2001, nil, nil, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001Z')
+ assert_equal([2001, nil, nil, nil, nil, nil, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001+01:00')
+ assert_equal([2001, nil, nil, nil, nil, nil, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('2001-01:00')
+ assert_equal([2001, nil, nil, nil, nil, nil, -3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._xmlschema('--02')
+ assert_equal([nil, 2, nil, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('--02Z')
+ assert_equal([nil, 2, nil, nil, nil, nil, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._xmlschema('--02+01:00')
+ assert_equal([nil, 2, nil, nil, nil, nil, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._xmlschema('92001-02-03T04:05:06.07+01:00')
+ assert_equal([92001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._xmlschema('-92001-02-03T04:05:06.07+01:00')
+ assert_equal([-92001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._xmlschema('')
+ assert_equal({}, h)
+ end
+
+ def test__rfc2822
+ h = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 UT')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 EST')
+ assert_equal([2001, 2, 3, 4, 5, 6, -5*3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 +0000')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 +0100')
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._rfc2822('Sat, 03 Feb 50 04:05:06 +0100')
+ assert_equal([1950, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._rfc2822('Sat, 03 Feb 49 04:05:06 +0100')
+ assert_equal([2049, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._rfc2822('Sat, 03 Feb 100 04:05:06 +0100')
+ assert_equal([2000, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h1 = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 UT')
+ h2 = Date._rfc822('Sat, 3 Feb 2001 04:05:06 UT')
+ assert_equal(h1, h2)
+
+ h = Date._rfc2822('')
+ assert_equal({}, h)
+ end
+
+ def test__httpdate
+ h = Date._httpdate('Sat, 03 Feb 2001 04:05:06 GMT')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._httpdate('Saturday, 03-Feb-01 04:05:06 GMT')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._httpdate('Sat Feb 3 04:05:06 2001')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._httpdate('Sat Feb 03 04:05:06 2001')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._httpdate('')
+ assert_equal({}, h)
+ end
+
+ def test__jisx0301
+ h = Date._jisx0301('13.02.03')
+ assert_equal([2001, 2, 3, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H13.02.03')
+ assert_equal([2001, 2, 3, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('S63.02.03')
+ assert_equal([1988, 2, 3, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._jisx0301('H13.02.03T04:05:06')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H13.02.03T04:05:06,07')
+ assert_equal([2001, 2, 3, 4, 5, 6, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H13.02.03T04:05:06Z')
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+ h = Date._jisx0301('H13.02.03T04:05:06.07+0100')
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
+
+ h = Date._jisx0301('')
+ assert_equal({}, h)
+ end
+
+ def test_iso8601
+ assert_instance_of(Date, Date.iso8601)
+ assert_instance_of(DateTime, DateTime.iso8601)
+
+ d = Date.iso8601('2001-02-03', Date::ITALY + 10)
+ assert_equal(Date.new(2001,2,3), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.iso8601('2001-02-03T04:05:06+07:00', Date::ITALY + 10)
+ assert_equal(DateTime.new(2001,2,3,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+ end
+
+ def test_rfc3339
+ assert_instance_of(Date, Date.rfc3339)
+ assert_instance_of(DateTime, DateTime.rfc3339)
+
+ d = Date.rfc3339('2001-02-03T04:05:06+07:00', Date::ITALY + 10)
+ assert_equal(Date.new(2001,2,3), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.rfc3339('2001-02-03T04:05:06+07:00', Date::ITALY + 10)
+ assert_equal(DateTime.new(2001,2,3,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+ end
+
+ def test_xmlschema
+ assert_instance_of(Date, Date.xmlschema)
+ assert_instance_of(DateTime, DateTime.xmlschema)
+
+ d = Date.xmlschema('2001-02-03', Date::ITALY + 10)
+ assert_equal(Date.new(2001,2,3), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.xmlschema('2001-02-03T04:05:06+07:00', Date::ITALY + 10)
+ assert_equal(DateTime.new(2001,2,3,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+ end
+
+ def test_rfc2822
+ assert_instance_of(Date, Date.rfc2822)
+ assert_instance_of(DateTime, DateTime.rfc2822)
+ assert_instance_of(Date, Date.rfc822)
+ assert_instance_of(DateTime, DateTime.rfc822)
+
+ d = Date.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700', Date::ITALY + 10)
+ assert_equal(Date.new(2001,2,3), d)
+ assert_equal(Date::ITALY + 10, d.start)
+ d = Date.rfc2822('3 Feb 2001 04:05:06 +0700', Date::ITALY + 10)
+ assert_equal(Date.new(2001,2,3), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700', Date::ITALY + 10)
+ assert_equal(DateTime.new(2001,2,3,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+ d = DateTime.rfc2822('3 Feb 2001 04:05:06 +0700', Date::ITALY + 10)
+ assert_equal(DateTime.new(2001,2,3,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+ end
+
+ def test_httpdate
+ assert_instance_of(Date, Date.httpdate)
+ assert_instance_of(DateTime, DateTime.httpdate)
+
+ d = Date.httpdate('Sat, 03 Feb 2001 04:05:06 GMT', Date::ITALY + 10)
+ assert_equal(Date.new(2001,2,3), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.httpdate('Sat, 03 Feb 2001 04:05:06 GMT', Date::ITALY + 10)
+ assert_equal(DateTime.new(2001,2,3,4,5,6,'+00:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+ end
+
+ def test_jisx0301
+ assert_instance_of(Date, Date.jisx0301)
+ assert_instance_of(DateTime, DateTime.jisx0301)
+
+ d = Date.jisx0301('H13.02.03', Date::ITALY + 10)
+ assert_equal(Date.new(2001,2,3), d)
+ assert_equal(Date::ITALY + 10, d.start)
+
+ d = DateTime.jisx0301('H13.02.03T04:05:06+07:00', Date::ITALY + 10)
+ assert_equal(DateTime.new(2001,2,3,4,5,6,'+07:00'), d)
+ assert_equal(Date::ITALY + 10, d.start)
+ end
+
+ def test_given_string
+ s = '2001-02-03T04:05:06Z'
+ s0 = s.dup
+
+ assert_not_equal({}, Date._parse(s))
+ assert_equal(s0, s)
+
+ assert_not_equal({}, Date._iso8601(s))
+ assert_equal(s0, s)
+
+ assert_not_equal({}, Date._rfc3339(s))
+ assert_equal(s0, s)
+
+ assert_not_equal({}, Date._xmlschema(s))
+ assert_equal(s0, s)
+
+ s = 'Sat, 3 Feb 2001 04:05:06 UT'
+ s0 = s.dup
+ assert_not_equal({}, Date._rfc2822(s))
+ assert_equal(s0, s)
+ assert_not_equal({}, Date._rfc822(s))
+ assert_equal(s0, s)
+
+ s = 'Sat, 03 Feb 2001 04:05:06 GMT'
+ s0 = s.dup
+ assert_not_equal({}, Date._httpdate(s))
+ assert_equal(s0, s)
+
+ s = 'H13.02.03T04:05:06,07Z'
+ s0 = s.dup
+ assert_not_equal({}, Date._jisx0301(s))
+ assert_equal(s0, s)
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_strftime.rb b/jni/ruby/test/date/test_date_strftime.rb
new file mode 100644
index 0000000..0ed9215
--- /dev/null
+++ b/jni/ruby/test/date/test_date_strftime.rb
@@ -0,0 +1,422 @@
+require 'test/unit'
+require 'date'
+
+class TestDateStrftime < Test::Unit::TestCase
+
+ STRFTIME_2001_02_03 = {
+ '%A'=>['Saturday',{:wday=>6}],
+ '%a'=>['Sat',{:wday=>6}],
+ '%B'=>['February',{:mon=>2}],
+ '%b'=>['Feb',{:mon=>2}],
+ '%c'=>['Sat Feb 3 00:00:00 2001',
+ {:wday=>6,:mon=>2,:mday=>3,:hour=>0,:min=>0,:sec=>0,:year=>2001}],
+ '%d'=>['03',{:mday=>3}],
+ '%e'=>[' 3',{:mday=>3}],
+ '%H'=>['00',{:hour=>0}],
+ '%I'=>['12',{:hour=>0}],
+ '%j'=>['034',{:yday=>34}],
+ '%M'=>['00',{:min=>0}],
+ '%m'=>['02',{:mon=>2}],
+ '%p'=>['AM',{}],
+ '%S'=>['00',{:sec=>0}],
+ '%U'=>['04',{:wnum0=>4}],
+ '%W'=>['05',{:wnum1=>5}],
+ '%X'=>['00:00:00',{:hour=>0,:min=>0,:sec=>0}],
+ '%x'=>['02/03/01',{:mon=>2,:mday=>3,:year=>2001}],
+ '%Y'=>['2001',{:year=>2001}],
+ '%y'=>['01',{:year=>2001}],
+ '%Z'=>['+00:00',{:zone=>'+00:00',:offset=>0}],
+ '%%'=>['%',{}],
+ '%C'=>['20',{}],
+ '%D'=>['02/03/01',{:mon=>2,:mday=>3,:year=>2001}],
+ '%F'=>['2001-02-03',{:year=>2001,:mon=>2,:mday=>3}],
+ '%G'=>['2001',{:cwyear=>2001}],
+ '%g'=>['01',{:cwyear=>2001}],
+ '%h'=>['Feb',{:mon=>2}],
+ '%k'=>[' 0',{:hour=>0}],
+ '%L'=>['000',{:sec_fraction=>0}],
+ '%l'=>['12',{:hour=>0}],
+ '%N'=>['000000000',{:sec_fraction=>0}],
+ '%n'=>["\n",{}],
+ '%P'=>['am',{}],
+ '%Q'=>['981158400000',{:seconds=>981158400.to_r}],
+ '%R'=>['00:00',{:hour=>0,:min=>0}],
+ '%r'=>['12:00:00 AM',{:hour=>0,:min=>0,:sec=>0}],
+ '%s'=>['981158400',{:seconds=>981158400}],
+ '%T'=>['00:00:00',{:hour=>0,:min=>0,:sec=>0}],
+ '%t'=>["\t",{}],
+ '%u'=>['6',{:cwday=>6}],
+ '%V'=>['05',{:cweek=>5}],
+ '%v'=>[' 3-Feb-2001',{:mday=>3,:mon=>2,:year=>2001}],
+ '%z'=>['+0000',{:zone=>'+0000',:offset=>0}],
+ '%+'=>['Sat Feb 3 00:00:00 +00:00 2001',
+ {:wday=>6,:mon=>2,:mday=>3,
+ :hour=>0,:min=>0,:sec=>0,:zone=>'+00:00',:offset=>0,:year=>2001}],
+ }
+
+ STRFTIME_2001_02_03_CVS19 = {
+ }
+
+ STRFTIME_2001_02_03_GNUext = {
+ '%:z'=>['+00:00',{:zone=>'+00:00',:offset=>0}],
+ '%::z'=>['+00:00:00',{:zone=>'+00:00:00',:offset=>0}],
+ '%:::z'=>['+00',{:zone=>'+00',:offset=>0}],
+ }
+
+ STRFTIME_2001_02_03.update(STRFTIME_2001_02_03_CVS19)
+ STRFTIME_2001_02_03.update(STRFTIME_2001_02_03_GNUext)
+
+ def test_strftime
+ d = Date.new(2001,2,3)
+ STRFTIME_2001_02_03.each do |f, s|
+ assert_equal(s[0], d.strftime(f), [f, s].inspect)
+ case f[-1,1]
+ when 'c', 'C', 'x', 'X', 'y', 'Y'
+ f2 = f.sub(/\A%/, '%E')
+ assert_equal(s[0], d.strftime(f2), [f2, s].inspect)
+ else
+ f2 = f.sub(/\A%/, '%E')
+ assert_equal(f2, d.strftime(f2), [f2, s].inspect)
+ end
+ case f[-1,1]
+ when 'd', 'e', 'H', 'k', 'I', 'l', 'm', 'M', 'S', 'u', 'U', 'V', 'w', 'W', 'y'
+ f2 = f.sub(/\A%/, '%O')
+ assert_equal(s[0], d.strftime(f2), [f2, s].inspect)
+ else
+ f2 = f.sub(/\A%/, '%O')
+ assert_equal(f2, d.strftime(f2), [f2, s].inspect)
+ end
+ end
+ end
+
+ def test_strftime__2
+ d = Date.new(2001,2,3)
+ assert_equal('2001-02-03', d.strftime)
+
+ d = DateTime.new(2001,2,3)
+ assert_equal('2001-02-03T00:00:00+00:00', d.strftime)
+
+ assert_equal('', d.strftime(''))
+ assert_equal("\s"*3, d.strftime("\s"*3))
+ assert_equal("\tfoo\n\000\r", d.strftime("\tfoo\n\000\r"))
+ assert_equal("%\n", d.strftime("%\n")) # gnu
+ assert_equal('Saturday'*1024 + ',', d.strftime('%A'*1024 + ','))
+ assert_equal('%%', d.strftime('%%%'))
+ assert_equal('Anton von Webern', d.strftime('Anton von Webern'))
+
+ d = DateTime.new(2001,2,3, 1,2,3)
+ assert_equal('2001-02-03T01:02:03+00:00', d.strftime)
+ assert_equal('AM', d.strftime('%p'))
+ assert_equal('am', d.strftime('%P'))
+ d = DateTime.new(2001,2,3, 13,14,15)
+ assert_equal('2001-02-03T13:14:15+00:00', d.strftime)
+ assert_equal('PM', d.strftime('%p'))
+ assert_equal('pm', d.strftime('%P'))
+ end
+
+ def test_strftime__3_1
+ (Date.new(1970,1,1)..Date.new(2037,12,31)).each do |d|
+ t = Time.utc(d.year,d.mon,d.mday)
+ assert_equal(t.strftime('%U'), d.strftime('%U'))
+ assert_equal(t.strftime('%W'), d.strftime('%W'))
+ end
+ end
+
+ def test_strftime__3_2
+ s = Time.now.strftime('%G')
+ skip if s.empty? || s == '%G'
+ (Date.new(1970,1,1)..Date.new(2037,12,31)).each do |d|
+ t = Time.utc(d.year,d.mon,d.mday)
+ assert_equal(t.strftime('%G'), d.strftime('%G'))
+ assert_equal(t.strftime('%g'), d.strftime('%g'))
+ assert_equal(t.strftime('%V'), d.strftime('%V'))
+ assert_equal(t.strftime('%u'), d.strftime('%u'))
+ end
+ end
+
+ def test_strftime__4
+ s = '2006-08-08T23:15:33.123456789'
+ f = '%FT%T.%N'
+ d = DateTime.parse(s)
+ assert_equal(s, d.strftime(f))
+ d = DateTime.strptime(s, f)
+ assert_equal(s, d.strftime(f))
+
+ s = '2006-08-08T23:15:33.123456789'
+ f = '%FT%T.%N'
+ d = DateTime.parse(s + '123456789')
+ assert_equal(s, d.strftime(f))
+ d = DateTime.strptime(s + '123456789', f)
+ assert_equal(s, d.strftime(f))
+
+ si = '2006-08-08T23:15:33.9'
+ so = '2006-08-08T23:15:33.900000000'
+ f = '%FT%T.%N'
+ d = DateTime.parse(si)
+ assert_equal(so, d.strftime(f))
+ d = DateTime.strptime(si, f)
+ assert_equal(so, d.strftime(f))
+
+ s = '2006-08-08T23:15:33.123'
+ f = '%FT%T.%L'
+ d = DateTime.parse(s)
+ assert_equal(s, d.strftime(f))
+ d = DateTime.strptime(s, f)
+ assert_equal(s, d.strftime(f))
+
+ s = '2006-08-08T23:15:33.123'
+ f = '%FT%T.%L'
+ d = DateTime.parse(s + '123')
+ assert_equal(s, d.strftime(f))
+ d = DateTime.strptime(s + '123', f)
+ assert_equal(s, d.strftime(f))
+
+ si = '2006-08-08T23:15:33.9'
+ so = '2006-08-08T23:15:33.900'
+ f = '%FT%T.%L'
+ d = DateTime.parse(si)
+ assert_equal(so, d.strftime(f))
+ d = DateTime.strptime(si, f)
+ assert_equal(so, d.strftime(f))
+ end
+
+ def test_strftime__offset
+ s = '2006-08-08T23:15:33'
+ (-24..24).collect{|x| '%+.2d' % x}.each do |hh|
+ %w(00 30).each do |mm|
+ r = hh + mm
+ if r[-4,4] == '2430'
+ r = '+0000'
+ end
+ d = DateTime.parse(s + hh + mm)
+ assert_equal(r, d.strftime('%z'))
+ end
+ end
+ end
+
+ def test_strftime_milli
+ s = '1970-01-01T00:00:00.123456789'
+ d = DateTime.parse(s)
+ assert_equal('123', d.strftime('%Q'))
+ s = '1970-01-02T02:03:04.123456789'
+ d = DateTime.parse(s)
+ assert_equal('93784123', d.strftime('%Q'))
+ end
+
+ def test_strftime__minus
+ d = DateTime.new(1969, 12, 31, 23, 59, 59)
+ assert_equal('-1', d.strftime('%s'))
+ assert_equal('-1000', d.strftime('%Q'))
+ end
+
+ def test_strftime__gnuext # coreutils
+ d = DateTime.new(2006,8,8,23,15,33,9.to_r/24)
+
+ assert_equal('2006', d.strftime('%-Y'))
+ assert_equal('2006', d.strftime('%-5Y'))
+ assert_equal('02006', d.strftime('%5Y'))
+ assert_equal('2006', d.strftime('%_Y'))
+ assert_equal(' 2006', d.strftime('%_5Y'))
+ assert_equal('02006', d.strftime('%05Y'))
+
+ assert_equal('8', d.strftime('%-d'))
+ assert_equal('8', d.strftime('%-3d'))
+ assert_equal('008', d.strftime('%3d'))
+ assert_equal(' 8', d.strftime('%_d'))
+ assert_equal(' 8', d.strftime('%_3d'))
+ assert_equal('008', d.strftime('%03d'))
+
+ assert_equal('8', d.strftime('%-e'))
+ assert_equal('8', d.strftime('%-3e'))
+ assert_equal(' 8', d.strftime('%3e'))
+ assert_equal(' 8', d.strftime('%_e'))
+ assert_equal(' 8', d.strftime('%_3e'))
+ assert_equal('008', d.strftime('%03e'))
+
+ assert_equal('Tuesday', d.strftime('%-10A'))
+ assert_equal(' Tuesday', d.strftime('%10A'))
+ assert_equal(' Tuesday', d.strftime('%_10A'))
+ assert_equal('000Tuesday', d.strftime('%010A'))
+ assert_equal('TUESDAY', d.strftime('%^A'))
+ assert_equal('TUESDAY', d.strftime('%#A'))
+
+ assert_equal('Tue', d.strftime('%-6a'))
+ assert_equal(' Tue', d.strftime('%6a'))
+ assert_equal(' Tue', d.strftime('%_6a'))
+ assert_equal('000Tue', d.strftime('%06a'))
+ assert_equal('TUE', d.strftime('%^a'))
+ assert_equal('TUE', d.strftime('%#a'))
+ assert_equal(' TUE', d.strftime('%#6a'))
+
+ assert_equal('August', d.strftime('%-10B'))
+ assert_equal(' August', d.strftime('%10B'))
+ assert_equal(' August', d.strftime('%_10B'))
+ assert_equal('0000August', d.strftime('%010B'))
+ assert_equal('AUGUST', d.strftime('%^B'))
+ assert_equal('AUGUST', d.strftime('%#B'))
+
+ assert_equal('Aug', d.strftime('%-6b'))
+ assert_equal(' Aug', d.strftime('%6b'))
+ assert_equal(' Aug', d.strftime('%_6b'))
+ assert_equal('000Aug', d.strftime('%06b'))
+ assert_equal('AUG', d.strftime('%^b'))
+ assert_equal('AUG', d.strftime('%#b'))
+ assert_equal(' AUG', d.strftime('%#6b'))
+
+ assert_equal('Aug', d.strftime('%-6h'))
+ assert_equal(' Aug', d.strftime('%6h'))
+ assert_equal(' Aug', d.strftime('%_6h'))
+ assert_equal('000Aug', d.strftime('%06h'))
+ assert_equal('AUG', d.strftime('%^h'))
+ assert_equal('AUG', d.strftime('%#h'))
+ assert_equal(' AUG', d.strftime('%#6h'))
+
+ assert_equal('PM', d.strftime('%^p'))
+ assert_equal('pm', d.strftime('%#p'))
+ assert_equal('PM', d.strftime('%^P'))
+ assert_equal('PM', d.strftime('%#P'))
+
+ assert_equal('+000900', d.strftime('%7z'))
+ assert_equal(' +900', d.strftime('%_7z'))
+ assert_equal('+09:00', d.strftime('%:z'))
+ assert_equal('+0009:00', d.strftime('%8:z'))
+ assert_equal(' +9:00', d.strftime('%_8:z'))
+ assert_equal('+09:00:00', d.strftime('%::z'))
+ assert_equal('+0009:00:00', d.strftime('%11::z'))
+ assert_equal(' +9:00:00', d.strftime('%_11::z'))
+ assert_equal('+09', d.strftime('%:::z'))
+ assert_equal('+0009', d.strftime('%5:::z'))
+ assert_equal(' +9', d.strftime('%_5:::z'))
+ assert_equal('+9', d.strftime('%-:::z'))
+
+ d = DateTime.new(-200,8,8,23,15,33,9.to_r/24)
+
+ assert_equal('-0200', d.strftime('%Y'))
+ assert_equal('-200', d.strftime('%-Y'))
+ assert_equal('-200', d.strftime('%-5Y'))
+ assert_equal('-0200', d.strftime('%5Y'))
+ assert_equal(' -200', d.strftime('%_Y'))
+ assert_equal(' -200', d.strftime('%_5Y'))
+ assert_equal('-0200', d.strftime('%05Y'))
+
+ d = DateTime.new(-2000,8,8,23,15,33,9.to_r/24)
+
+ assert_equal('-2000', d.strftime('%Y'))
+ assert_equal('-2000', d.strftime('%-Y'))
+ assert_equal('-2000', d.strftime('%-5Y'))
+ assert_equal('-2000', d.strftime('%5Y'))
+ assert_equal('-2000', d.strftime('%_Y'))
+ assert_equal('-2000', d.strftime('%_5Y'))
+ assert_equal('-2000', d.strftime('%05Y'))
+ end
+
+ def test_strftime__gnuext_LN # coreutils
+ d = DateTime.parse('2008-11-25T00:11:22.0123456789')
+ assert_equal('012', d.strftime('%L'))
+ assert_equal('012', d.strftime('%0L'))
+ assert_equal('0', d.strftime('%1L'))
+ assert_equal('01', d.strftime('%2L'))
+ assert_equal('01234567890', d.strftime('%11L'))
+ assert_equal('01234567890', d.strftime('%011L'))
+ assert_equal('01234567890', d.strftime('%_11L'))
+ assert_equal('012345678', d.strftime('%N'))
+ assert_equal('012345678', d.strftime('%0N'))
+ assert_equal('0', d.strftime('%1N'))
+ assert_equal('01', d.strftime('%2N'))
+ assert_equal('01234567890', d.strftime('%11N'))
+ assert_equal('01234567890', d.strftime('%011N'))
+ assert_equal('01234567890', d.strftime('%_11N'))
+ end
+
+ def test_strftime__gnuext_z # coreutils
+ d = DateTime.parse('2006-08-08T23:15:33+09:08:07')
+ assert_equal('+0908', d.strftime('%z'))
+ assert_equal('+09:08', d.strftime('%:z'))
+ assert_equal('+09:08:07', d.strftime('%::z'))
+ assert_equal('+09:08:07', d.strftime('%:::z'))
+ end
+
+ def test_strftime__gnuext_complex
+ d = DateTime.parse('2001-02-03T04:05:06+09:00')
+ assert_equal('Sat Feb 3 04:05:06 2001', d.strftime('%-100c'))
+ assert_equal('Sat Feb 3 04:05:06 2001'.rjust(100), d.strftime('%100c'))
+ assert_equal('Sat Feb 3 04:05:06 2001'.rjust(100), d.strftime('%_100c'))
+ assert_equal('Sat Feb 3 04:05:06 2001'.rjust(100, '0'), d.strftime('%0100c'))
+ assert_equal('SAT FEB 3 04:05:06 2001', d.strftime('%^c'))
+
+ assert_equal('Sat Feb 3 04:05:06 +09:00 2001', d.strftime('%-100+'))
+ assert_equal('Sat Feb 3 04:05:06 +09:00 2001'.rjust(100), d.strftime('%100+'))
+ assert_equal('Sat Feb 3 04:05:06 +09:00 2001'.rjust(100), d.strftime('%_100+'))
+ assert_equal('Sat Feb 3 04:05:06 +09:00 2001'.rjust(100, '0'), d.strftime('%0100+'))
+ assert_equal('SAT FEB 3 04:05:06 +09:00 2001', d.strftime('%^+'))
+ end
+
+ def test__different_format
+ d = Date.new(2001,2,3)
+
+ assert_equal('Sat Feb 3 00:00:00 2001', d.ctime)
+ assert_equal(d.ctime, d.asctime)
+
+ assert_equal('2001-02-03', d.iso8601)
+ assert_equal(d.xmlschema, d.iso8601)
+ assert_equal('2001-02-03T00:00:00+00:00', d.rfc3339)
+ assert_equal('Sat, 3 Feb 2001 00:00:00 +0000', d.rfc2822)
+ assert_equal(d.rfc822, d.rfc2822)
+ assert_equal('Sat, 03 Feb 2001 00:00:00 GMT', d.httpdate)
+ assert_equal('H13.02.03', d.jisx0301)
+
+ d = DateTime.new(2001,2,3)
+
+ assert_equal('Sat Feb 3 00:00:00 2001', d.ctime)
+ assert_equal(d.ctime, d.asctime)
+
+ assert_equal('2001-02-03T00:00:00+00:00', d.iso8601)
+ assert_equal(d.rfc3339, d.iso8601)
+ assert_equal(d.xmlschema, d.iso8601)
+ assert_equal('Sat, 3 Feb 2001 00:00:00 +0000', d.rfc2822)
+ assert_equal(d.rfc822, d.rfc2822)
+ assert_equal('Sat, 03 Feb 2001 00:00:00 GMT', d.httpdate)
+ assert_equal('H13.02.03T00:00:00+00:00', d.jisx0301)
+
+ d2 = DateTime.parse('2001-02-03T04:05:06.123456')
+ assert_equal('2001-02-03T04:05:06.123+00:00', d2.iso8601(3))
+ assert_equal('2001-02-03T04:05:06.123+00:00', d2.rfc3339(3))
+ assert_equal('H13.02.03T04:05:06.123+00:00', d2.jisx0301(3))
+ assert_equal('2001-02-03T04:05:06.123+00:00', d2.iso8601(3.5))
+ assert_equal('2001-02-03T04:05:06.123+00:00', d2.rfc3339(3.5))
+ assert_equal('H13.02.03T04:05:06.123+00:00', d2.jisx0301(3.5))
+ assert_equal('2001-02-03T04:05:06.123456000+00:00', d2.iso8601(9))
+ assert_equal('2001-02-03T04:05:06.123456000+00:00', d2.rfc3339(9))
+ assert_equal('H13.02.03T04:05:06.123456000+00:00', d2.jisx0301(9))
+ assert_equal('2001-02-03T04:05:06.123456000+00:00', d2.iso8601(9.9))
+ assert_equal('2001-02-03T04:05:06.123456000+00:00', d2.rfc3339(9.9))
+ assert_equal('H13.02.03T04:05:06.123456000+00:00', d2.jisx0301(9.9))
+
+ assert_equal('1800-01-01T00:00:00+00:00', DateTime.new(1800).jisx0301)
+
+ assert_equal('1868-01-25', Date.parse('1868-01-25').jisx0301)
+ assert_equal('1872-12-31', Date.parse('1872-12-31').jisx0301)
+
+ assert_equal('M06.01.01', Date.parse('1873-01-01').jisx0301)
+ assert_equal('M45.07.29', Date.parse('1912-07-29').jisx0301)
+ assert_equal('T01.07.30', Date.parse('1912-07-30').jisx0301)
+ assert_equal('T15.12.24', Date.parse('1926-12-24').jisx0301)
+ assert_equal('S01.12.25', Date.parse('1926-12-25').jisx0301)
+ assert_equal('S64.01.07', Date.parse('1989-01-07').jisx0301)
+ assert_equal('H01.01.08', Date.parse('1989-01-08').jisx0301)
+ assert_equal('H18.09.01', Date.parse('2006-09-01').jisx0301)
+
+ %w(M06.01.01
+ M45.07.29
+ T01.07.30
+ T15.12.24
+ S01.12.25
+ S64.01.07
+ H01.01.08
+ H18.09.01).each do |s|
+ assert_equal(s, Date.parse(s).jisx0301)
+ end
+
+ end
+
+end
diff --git a/jni/ruby/test/date/test_date_strptime.rb b/jni/ruby/test/date/test_date_strptime.rb
new file mode 100644
index 0000000..ae149bb
--- /dev/null
+++ b/jni/ruby/test/date/test_date_strptime.rb
@@ -0,0 +1,512 @@
+require 'test/unit'
+require 'date'
+
+class TestDateStrptime < Test::Unit::TestCase
+
+ STRFTIME_2001_02_03 = {
+ '%A'=>['Saturday',{:wday=>6}],
+ '%a'=>['Sat',{:wday=>6}],
+ '%B'=>['February',{:mon=>2}],
+ '%b'=>['Feb',{:mon=>2}],
+ '%c'=>['Sat Feb 3 00:00:00 2001',
+ {:wday=>6,:mon=>2,:mday=>3,:hour=>0,:min=>0,:sec=>0,:year=>2001}],
+ '%d'=>['03',{:mday=>3}],
+ '%e'=>[' 3',{:mday=>3}],
+ '%H'=>['00',{:hour=>0}],
+ '%I'=>['12',{:hour=>0}],
+ '%j'=>['034',{:yday=>34}],
+ '%M'=>['00',{:min=>0}],
+ '%m'=>['02',{:mon=>2}],
+ '%p'=>['AM',{}],
+ '%S'=>['00',{:sec=>0}],
+ '%U'=>['04',{:wnum0=>4}],
+ '%W'=>['05',{:wnum1=>5}],
+ '%X'=>['00:00:00',{:hour=>0,:min=>0,:sec=>0}],
+ '%x'=>['02/03/01',{:mon=>2,:mday=>3,:year=>2001}],
+ '%Y'=>['2001',{:year=>2001}],
+ '%y'=>['01',{:year=>2001}],
+ '%Z'=>['+00:00',{:zone=>'+00:00',:offset=>0}],
+ '%%'=>['%',{}],
+ '%C'=>['20',{}],
+ '%D'=>['02/03/01',{:mon=>2,:mday=>3,:year=>2001}],
+ '%F'=>['2001-02-03',{:year=>2001,:mon=>2,:mday=>3}],
+ '%G'=>['2001',{:cwyear=>2001}],
+ '%g'=>['01',{:cwyear=>2001}],
+ '%h'=>['Feb',{:mon=>2}],
+ '%k'=>[' 0',{:hour=>0}],
+ '%L'=>['000',{:sec_fraction=>0}],
+ '%l'=>['12',{:hour=>0}],
+ '%N'=>['000000000',{:sec_fraction=>0}],
+ '%n'=>["\n",{}],
+ '%P'=>['am',{}],
+ '%Q'=>['981158400000',{:seconds=>981158400.to_r}],
+ '%R'=>['00:00',{:hour=>0,:min=>0}],
+ '%r'=>['12:00:00 AM',{:hour=>0,:min=>0,:sec=>0}],
+ '%s'=>['981158400',{:seconds=>981158400}],
+ '%T'=>['00:00:00',{:hour=>0,:min=>0,:sec=>0}],
+ '%t'=>["\t",{}],
+ '%u'=>['6',{:cwday=>6}],
+ '%V'=>['05',{:cweek=>5}],
+ '%v'=>[' 3-Feb-2001',{:mday=>3,:mon=>2,:year=>2001}],
+ '%z'=>['+0000',{:zone=>'+0000',:offset=>0}],
+ '%+'=>['Sat Feb 3 00:00:00 +00:00 2001',
+ {:wday=>6,:mon=>2,:mday=>3,
+ :hour=>0,:min=>0,:sec=>0,:zone=>'+00:00',:offset=>0,:year=>2001}],
+ }
+
+ STRFTIME_2001_02_03_CVS19 = {
+ }
+
+ STRFTIME_2001_02_03_GNUext = {
+ '%:z'=>['+00:00',{:zone=>'+00:00',:offset=>0}],
+ '%::z'=>['+00:00:00',{:zone=>'+00:00:00',:offset=>0}],
+ '%:::z'=>['+00',{:zone=>'+00',:offset=>0}],
+ }
+
+ STRFTIME_2001_02_03.update(STRFTIME_2001_02_03_CVS19)
+ STRFTIME_2001_02_03.update(STRFTIME_2001_02_03_GNUext)
+
+ def test__strptime
+ STRFTIME_2001_02_03.each do |f, s|
+ if (f == '%I' and s[0] == '12') or
+ (f == '%l' and s[0] == '12') # hour w/o merid
+ s[1][:hour] = 12
+ end
+ assert_equal(s[1], Date._strptime(s[0], f), [f, s].inspect)
+ case f[-1,1]
+ when 'c', 'C', 'x', 'X', 'y', 'Y'
+ f2 = f.sub(/\A%/, '%E')
+ assert_equal(s[1], Date._strptime(s[0], f2), [f2, s].inspect)
+ else
+ f2 = f.sub(/\A%/, '%E')
+ assert_equal(nil, Date._strptime(s[0], f2), [f2, s].inspect)
+ assert_equal({}, Date._strptime(f2, f2), [f2, s].inspect)
+ end
+ case f[-1,1]
+ when 'd', 'e', 'H', 'I', 'm', 'M', 'S', 'u', 'U', 'V', 'w', 'W', 'y'
+ f2 = f.sub(/\A%/, '%O')
+ assert_equal(s[1], Date._strptime(s[0], f2), [f2, s].inspect)
+ else
+ f2 = f.sub(/\A%/, '%O')
+ assert_equal(nil, Date._strptime(s[0], f2), [f2, s].inspect)
+ assert_equal({}, Date._strptime(f2, f2), [f2, s].inspect)
+ end
+ end
+ end
+
+ def test__strptime__2
+ h = Date._strptime('2001-02-03')
+ assert_equal([2001,2,3], h.values_at(:year,:mon,:mday))
+
+ h = DateTime._strptime('2001-02-03T12:13:14Z')
+ assert_equal([2001,2,3,12,13,14],
+ h.values_at(:year,:mon,:mday,:hour,:min,:sec))
+
+ assert_equal({}, Date._strptime('', ''))
+ assert_equal({:leftover=>"\s"*3}, Date._strptime("\s"*3, ''))
+ assert_equal({:leftover=>'x'}, Date._strptime("\nx", "\n"))
+ assert_equal({}, Date._strptime('', "\s"*3))
+ assert_equal({}, Date._strptime("\s"*3, "\s"*3))
+ assert_equal({}, Date._strptime("\tfoo\n\000\r", "\tfoo\n\000\r"))
+ assert_equal({}, Date._strptime("foo\n\nbar", "foo\sbar"))
+ assert_equal({}, Date._strptime("%\n", "%\n")) # gnu
+ assert_equal({}, Date._strptime('%%', '%%%'))
+ assert_equal({:wday=>6}, Date._strptime('Saturday'*1024 + ',', '%A'*1024 + ','))
+ assert_equal({:wday=>6}, Date._strptime('Saturday'*1024 + ',', '%a'*1024 + ','))
+ assert_equal({}, Date._strptime('Anton von Webern', 'Anton von Webern'))
+ end
+
+ def test__strptime__3
+ [
+ # iso8601
+ [['2001-02-03', '%Y-%m-%d'], [2001,2,3,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2001-02-03T23:59:60', '%Y-%m-%dT%H:%M:%S'], [2001,2,3,23,59,60,nil,nil,nil], __LINE__],
+ [['2001-02-03T23:59:60+09:00', '%Y-%m-%dT%H:%M:%S%Z'], [2001,2,3,23,59,60,'+09:00',9*3600,nil], __LINE__],
+ [['-2001-02-03T23:59:60+09:00', '%Y-%m-%dT%H:%M:%S%Z'], [-2001,2,3,23,59,60,'+09:00',9*3600,nil], __LINE__],
+ [['+012345-02-03T23:59:60+09:00', '%Y-%m-%dT%H:%M:%S%Z'], [12345,2,3,23,59,60,'+09:00',9*3600,nil], __LINE__],
+ [['-012345-02-03T23:59:60+09:00', '%Y-%m-%dT%H:%M:%S%Z'], [-12345,2,3,23,59,60,'+09:00',9*3600,nil], __LINE__],
+
+ # ctime(3), asctime(3)
+ [['Thu Jul 29 14:47:19 1999', '%c'], [1999,7,29,14,47,19,nil,nil,4], __LINE__],
+ [['Thu Jul 29 14:47:19 -1999', '%c'], [-1999,7,29,14,47,19,nil,nil,4], __LINE__],
+
+ # date(1)
+ [['Thu Jul 29 16:39:41 EST 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'EST',-5*3600,4], __LINE__],
+ [['Thu Jul 29 16:39:41 MET DST 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'MET DST',2*3600,4], __LINE__],
+ [['Thu Jul 29 16:39:41 AMT 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'AMT',nil,4], __LINE__],
+ [['Thu Jul 29 16:39:41 AMT -1999', '%a %b %d %H:%M:%S %Z %Y'], [-1999,7,29,16,39,41,'AMT',nil,4], __LINE__],
+ [['Thu Jul 29 16:39:41 GMT+09 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'GMT+09',9*3600,4], __LINE__],
+ [['Thu Jul 29 16:39:41 GMT+0908 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'GMT+0908',9*3600+8*60,4], __LINE__],
+ [['Thu Jul 29 16:39:41 GMT+090807 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'GMT+090807',9*3600+8*60+7,4], __LINE__],
+ [['Thu Jul 29 16:39:41 GMT-09 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'GMT-09',-9*3600,4], __LINE__],
+ [['Thu Jul 29 16:39:41 GMT-09:08 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'GMT-09:08',-9*3600-8*60,4], __LINE__],
+ [['Thu Jul 29 16:39:41 GMT-09:08:07 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'GMT-09:08:07',-9*3600-8*60-7,4], __LINE__],
+ [['Thu Jul 29 16:39:41 GMT-3.5 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'GMT-3.5',-3*3600-30*60,4], __LINE__],
+ [['Thu Jul 29 16:39:41 GMT-3,5 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'GMT-3,5',-3*3600-30*60,4], __LINE__],
+ [['Thu Jul 29 16:39:41 Mountain Daylight Time 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'Mountain Daylight Time',-6*3600,4], __LINE__],
+ [['Thu Jul 29 16:39:41 E. Australia Standard Time 1999', '%a %b %d %H:%M:%S %Z %Y'], [1999,7,29,16,39,41,'E. Australia Standard Time',10*3600,4], __LINE__],
+
+ # rfc822
+ [['Thu, 29 Jul 1999 09:54:21 UT', '%a, %d %b %Y %H:%M:%S %Z'], [1999,7,29,9,54,21,'UT',0,4], __LINE__],
+ [['Thu, 29 Jul 1999 09:54:21 GMT', '%a, %d %b %Y %H:%M:%S %Z'], [1999,7,29,9,54,21,'GMT',0,4], __LINE__],
+ [['Thu, 29 Jul 1999 09:54:21 PDT', '%a, %d %b %Y %H:%M:%S %Z'], [1999,7,29,9,54,21,'PDT',-7*3600,4], __LINE__],
+ [['Thu, 29 Jul 1999 09:54:21 z', '%a, %d %b %Y %H:%M:%S %Z'], [1999,7,29,9,54,21,'z',0,4], __LINE__],
+ [['Thu, 29 Jul 1999 09:54:21 +0900', '%a, %d %b %Y %H:%M:%S %Z'], [1999,7,29,9,54,21,'+0900',9*3600,4], __LINE__],
+ [['Thu, 29 Jul 1999 09:54:21 +0430', '%a, %d %b %Y %H:%M:%S %Z'], [1999,7,29,9,54,21,'+0430',4*3600+30*60,4], __LINE__],
+ [['Thu, 29 Jul 1999 09:54:21 -0430', '%a, %d %b %Y %H:%M:%S %Z'], [1999,7,29,9,54,21,'-0430',-4*3600-30*60,4], __LINE__],
+ [['Thu, 29 Jul -1999 09:54:21 -0430', '%a, %d %b %Y %H:%M:%S %Z'], [-1999,7,29,9,54,21,'-0430',-4*3600-30*60,4], __LINE__],
+
+ # etc
+ [['06-DEC-99', '%d-%b-%y'], [1999,12,6,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['sUnDay oCtoBer 31 01', '%A %B %d %y'], [2001,10,31,nil,nil,nil,nil,nil,0], __LINE__],
+ [["October\t\n\v\f\r 15,\t\n\v\f\r99", '%B %d, %y'], [1999,10,15,nil,nil,nil,nil,nil,nil], __LINE__],
+ [["October\t\n\v\f\r 15,\t\n\v\f\r99", '%B%t%d,%n%y'], [1999,10,15,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['09:02:11 AM', '%I:%M:%S %p'], [nil,nil,nil,9,2,11,nil,nil,nil], __LINE__],
+ [['09:02:11 A.M.', '%I:%M:%S %p'], [nil,nil,nil,9,2,11,nil,nil,nil], __LINE__],
+ [['09:02:11 PM', '%I:%M:%S %p'], [nil,nil,nil,21,2,11,nil,nil,nil], __LINE__],
+ [['09:02:11 P.M.', '%I:%M:%S %p'], [nil,nil,nil,21,2,11,nil,nil,nil], __LINE__],
+
+ [['12:33:44 AM', '%r'], [nil,nil,nil,0,33,44,nil,nil,nil], __LINE__],
+ [['01:33:44 AM', '%r'], [nil,nil,nil,1,33,44,nil,nil,nil], __LINE__],
+ [['11:33:44 AM', '%r'], [nil,nil,nil,11,33,44,nil,nil,nil], __LINE__],
+ [['12:33:44 PM', '%r'], [nil,nil,nil,12,33,44,nil,nil,nil], __LINE__],
+ [['01:33:44 PM', '%r'], [nil,nil,nil,13,33,44,nil,nil,nil], __LINE__],
+ [['11:33:44 PM', '%r'], [nil,nil,nil,23,33,44,nil,nil,nil], __LINE__],
+
+ [['11:33:44 PM AMT', '%I:%M:%S %p %Z'], [nil,nil,nil,23,33,44,'AMT',nil,nil], __LINE__],
+ [['11:33:44 P.M. AMT', '%I:%M:%S %p %Z'], [nil,nil,nil,23,33,44,'AMT',nil,nil], __LINE__],
+
+ [['fri1feb034pm+5', '%a%d%b%y%H%p%Z'], [2003,2,1,16,nil,nil,'+5',5*3600,5]]
+ ].each do |x, y|
+ h = Date._strptime(*x)
+ a = h.values_at(:year,:mon,:mday,:hour,:min,:sec,:zone,:offset,:wday)
+ if y[1] == -1
+ a[1] = -1
+ a[2] = h[:yday]
+ end
+ assert_equal(y, a, [x, y, a].inspect)
+ end
+ end
+
+ def test__strptime__width
+ [
+ [['99', '%y'], [1999,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['01', '%y'], [2001,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['19 99', '%C %y'], [1999,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['20 01', '%C %y'], [2001,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['30 99', '%C %y'], [3099,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['30 01', '%C %y'], [3001,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['1999', '%C%y'], [1999,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2001', '%C%y'], [2001,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['3099', '%C%y'], [3099,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['3001', '%C%y'], [3001,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['20060806', '%Y'], [20060806,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['20060806', "%Y\s"], [20060806,nil,nil,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['20060806', '%Y%m%d'], [2006,8,6,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['2006908906', '%Y9%m9%d'], [2006,8,6,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['12006 08 06', '%Y %m %d'], [12006,8,6,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['12006-08-06', '%Y-%m-%d'], [12006,8,6,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['200608 6', '%Y%m%e'], [2006,8,6,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['2006333', '%Y%j'], [2006,-1,333,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['20069333', '%Y9%j'], [2006,-1,333,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['12006 333', '%Y %j'], [12006,-1,333,nil,nil,nil,nil,nil,nil], __LINE__],
+ [['12006-333', '%Y-%j'], [12006,-1,333,nil,nil,nil,nil,nil,nil], __LINE__],
+
+ [['232425', '%H%M%S'], [nil,nil,nil,23,24,25,nil,nil,nil], __LINE__],
+ [['23924925', '%H9%M9%S'], [nil,nil,nil,23,24,25,nil,nil,nil], __LINE__],
+ [['23 24 25', '%H %M %S'], [nil,nil,nil,23,24,25,nil,nil,nil], __LINE__],
+ [['23:24:25', '%H:%M:%S'], [nil,nil,nil,23,24,25,nil,nil,nil], __LINE__],
+ [[' 32425', '%k%M%S'], [nil,nil,nil,3,24,25,nil,nil,nil], __LINE__],
+ [[' 32425', '%l%M%S'], [nil,nil,nil,3,24,25,nil,nil,nil], __LINE__],
+
+ [['FriAug', '%a%b'], [nil,8,nil,nil,nil,nil,nil,nil,5], __LINE__],
+ [['FriAug', '%A%B'], [nil,8,nil,nil,nil,nil,nil,nil,5], __LINE__],
+ [['FridayAugust', '%A%B'], [nil,8,nil,nil,nil,nil,nil,nil,5], __LINE__],
+ [['FridayAugust', '%a%b'], [nil,8,nil,nil,nil,nil,nil,nil,5], __LINE__],
+ ].each do |x,y,l|
+ h = Date._strptime(*x)
+ a = (h || {}).values_at(:year,:mon,:mday,:hour,:min,:sec,:zone,:offset,:wday)
+ if y[1] == -1
+ a[1] = -1
+ a[2] = h[:yday]
+ end
+ assert_equal(y, a, format('<failed at line %d>', l))
+ end
+ end
+
+ def test__strptime__fail
+ assert_not_nil(Date._strptime('2001.', '%Y.'))
+ assert_not_nil(Date._strptime("2001.\s", '%Y.'))
+ assert_not_nil(Date._strptime('2001.', "%Y.\s"))
+ assert_not_nil(Date._strptime("2001.\s", "%Y.\s"))
+
+ assert_nil(Date._strptime('2001', '%Y.'))
+ assert_nil(Date._strptime("2001\s", '%Y.'))
+ assert_nil(Date._strptime('2001', "%Y.\s"))
+ assert_nil(Date._strptime("2001\s", "%Y.\s"))
+
+ assert_nil(Date._strptime('2001-13-31', '%Y-%m-%d'))
+ assert_nil(Date._strptime('2001-12-00', '%Y-%m-%d'))
+ assert_nil(Date._strptime('2001-12-32', '%Y-%m-%d'))
+ assert_nil(Date._strptime('2001-12-00', '%Y-%m-%e'))
+ assert_nil(Date._strptime('2001-12-32', '%Y-%m-%e'))
+ assert_nil(Date._strptime('2001-12-31', '%y-%m-%d'))
+
+ assert_nil(Date._strptime('2004-000', '%Y-%j'))
+ assert_nil(Date._strptime('2004-367', '%Y-%j'))
+ assert_nil(Date._strptime('2004-366', '%y-%j'))
+
+ assert_not_nil(Date._strptime('24:59:59', '%H:%M:%S'))
+ assert_not_nil(Date._strptime('24:59:59', '%k:%M:%S'))
+ assert_not_nil(Date._strptime('24:59:60', '%H:%M:%S'))
+ assert_not_nil(Date._strptime('24:59:60', '%k:%M:%S'))
+
+ assert_nil(Date._strptime('24:60:59', '%H:%M:%S'))
+ assert_nil(Date._strptime('24:60:59', '%k:%M:%S'))
+ assert_nil(Date._strptime('24:59:61', '%H:%M:%S'))
+ assert_nil(Date._strptime('24:59:61', '%k:%M:%S'))
+ assert_nil(Date._strptime('00:59:59', '%I:%M:%S'))
+ assert_nil(Date._strptime('13:59:59', '%I:%M:%S'))
+ assert_nil(Date._strptime('00:59:59', '%l:%M:%S'))
+ assert_nil(Date._strptime('13:59:59', '%l:%M:%S'))
+
+ assert_not_nil(Date._strptime('0', '%U'))
+ assert_nil(Date._strptime('54', '%U'))
+ assert_not_nil(Date._strptime('0', '%W'))
+ assert_nil(Date._strptime('54', '%W'))
+ assert_nil(Date._strptime('0', '%V'))
+ assert_nil(Date._strptime('54', '%V'))
+ assert_nil(Date._strptime('0', '%u'))
+ assert_not_nil(Date._strptime('7', '%u'))
+ assert_not_nil(Date._strptime('0', '%w'))
+ assert_nil(Date._strptime('7', '%w'))
+
+ assert_nil(Date._strptime('Sanday', '%A'))
+ assert_nil(Date._strptime('Jenuary', '%B'))
+ assert_not_nil(Date._strptime('Sundai', '%A'))
+ assert_not_nil(Date._strptime('Januari', '%B'))
+ assert_nil(Date._strptime('Sundai,', '%A,'))
+ assert_nil(Date._strptime('Januari,', '%B,'))
+ end
+
+ def test_strptime
+ assert_equal(Date.new, Date.strptime)
+ d = Date.new(2002,3,14)
+ assert_equal(d, Date.strptime(d.to_s))
+ assert_equal(Date.new(2002,3,14), Date.strptime('2002-03-14'))
+
+ d = DateTime.new(2002,3,14,11,22,33, 0)
+ assert_equal(d, DateTime.strptime(d.to_s))
+ assert_equal(DateTime.new(2002,3,14,11,22,33, 0),
+ DateTime.strptime('2002-03-14T11:22:33Z'))
+ assert_equal(DateTime.new(2002,3,14,11,22,33, 0),
+ DateTime.strptime('2002-03-14T11:22:33Z', '%Y-%m-%dT%H:%M:%S%Z'))
+ assert_equal(DateTime.new(2002,3,14,11,22,33, 9.to_r/24),
+ DateTime.strptime('2002-03-14T11:22:33+09:00', '%Y-%m-%dT%H:%M:%S%Z'))
+ assert_equal(DateTime.new(2002,3,14,11,22,33, -9.to_r/24),
+ DateTime.strptime('2002-03-14T11:22:33-09:00', '%FT%T%Z'))
+ assert_equal(DateTime.new(2002,3,14,11,22,33, -9.to_r/24) + 123456789.to_r/1000000000/86400,
+ DateTime.strptime('2002-03-14T11:22:33.123456789-09:00', '%FT%T.%N%Z'))
+ end
+
+ def test_strptime__2
+ (Date.new(2006,6,1)..Date.new(2007,6,1)).each do |d|
+ [
+ '%Y %m %d',
+ '%C %y %m %d',
+
+ '%Y %j',
+ '%C %y %j',
+
+ '%G %V %w',
+ '%G %V %u',
+ '%C %g %V %w',
+ '%C %g %V %u',
+
+ '%Y %U %w',
+ '%Y %U %u',
+ '%Y %W %w',
+ '%Y %W %u',
+ '%C %y %U %w',
+ '%C %y %U %u',
+ '%C %y %W %w',
+ '%C %y %W %u',
+ ].each do |fmt|
+ s = d.strftime(fmt)
+ d2 = Date.strptime(s, fmt)
+ assert_equal(d, d2, [fmt, d.to_s, d2.to_s].inspect)
+ end
+
+ [
+ '%Y %m %d %H %M %S',
+ '%Y %m %d %H %M %S %N',
+ '%C %y %m %d %H %M %S',
+ '%C %y %m %d %H %M %S %N',
+
+ '%Y %j %H %M %S',
+ '%Y %j %H %M %S %N',
+ '%C %y %j %H %M %S',
+ '%C %y %j %H %M %S %N',
+
+ '%s',
+ '%s %N',
+ '%Q',
+ '%Q %N',
+ ].each do |fmt|
+ s = d.strftime(fmt)
+ d2 = DateTime.strptime(s, fmt)
+ assert_equal(d, d2, [fmt, d.to_s, d2.to_s].inspect)
+ end
+ end
+ end
+
+ def test_strptime__minus
+ d = DateTime.strptime('-1', '%s')
+ assert_equal([1969, 12, 31, 23, 59, 59],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('-86400', '%s')
+ assert_equal([1969, 12, 31, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+
+ d = DateTime.strptime('-999', '%Q')
+ assert_equal([1969, 12, 31, 23, 59, 59, 1.to_r/10**3],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.sec_fraction])
+ d = DateTime.strptime('-1000', '%Q')
+ assert_equal([1969, 12, 31, 23, 59, 59, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.sec_fraction])
+ end
+
+ def test_strptime__comp
+ n = DateTime.now
+
+ d = DateTime.strptime('073', '%j')
+ assert_equal([n.year, 73, 0, 0, 0],
+ [d.year, d.yday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('13', '%d')
+ assert_equal([n.year, n.mon, 13, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+
+ d = DateTime.strptime('Mar', '%b')
+ assert_equal([n.year, 3, 1, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('2004', '%Y')
+ assert_equal([2004, 1, 1, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+
+ d = DateTime.strptime('Mar 13', '%b %d')
+ assert_equal([n.year, 3, 13, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('Mar 2004', '%b %Y')
+ assert_equal([2004, 3, 1, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('23:55', '%H:%M')
+ assert_equal([n.year, n.mon, n.mday, 23, 55, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('23:55:30', '%H:%M:%S')
+ assert_equal([n.year, n.mon, n.mday, 23, 55, 30],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+
+ d = DateTime.strptime('Sun 23:55', '%a %H:%M')
+ d2 = d - d.wday
+ assert_equal([d2.year, d2.mon, d2.mday, 23, 55, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('Aug 23:55', '%b %H:%M')
+ assert_equal([n.year, 8, 1, 23, 55, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+
+ d = DateTime.strptime('2004', '%G')
+ assert_equal([2004, 1, 1, 0, 0, 0],
+ [d.cwyear, d.cweek, d.cwday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('11', '%V')
+ assert_equal([n.cwyear, 11, 1, 0, 0, 0],
+ [d.cwyear, d.cweek, d.cwday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('6', '%u')
+ assert_equal([n.cwyear, n.cweek, 6, 0, 0, 0],
+ [d.cwyear, d.cweek, d.cwday, d.hour, d.min, d.sec])
+
+ d = DateTime.strptime('11-6', '%V-%u')
+ assert_equal([n.cwyear, 11, 6, 0, 0, 0],
+ [d.cwyear, d.cweek, d.cwday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('2004-11', '%G-%V')
+ assert_equal([2004, 11, 1, 0, 0, 0],
+ [d.cwyear, d.cweek, d.cwday, d.hour, d.min, d.sec])
+
+ d = DateTime.strptime('11-6', '%U-%w')
+ assert_equal([n.year, 11, 6, 0, 0, 0],
+ [d.year, d.strftime('%U').to_i, d.wday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('2004-11', '%Y-%U')
+ assert_equal([2004, 11, 0, 0, 0, 0],
+ [d.year, d.strftime('%U').to_i, d.wday, d.hour, d.min, d.sec])
+
+ d = DateTime.strptime('11-6', '%W-%w')
+ assert_equal([n.year, 11, 6, 0, 0, 0],
+ [d.year, d.strftime('%W').to_i, d.wday, d.hour, d.min, d.sec])
+ d = DateTime.strptime('2004-11', '%Y-%W')
+ assert_equal([2004, 11, 1, 0, 0, 0],
+ [d.year, d.strftime('%W').to_i, d.wday, d.hour, d.min, d.sec])
+ end
+
+ def test_strptime__d_to_s
+ d = Date.new(2002,3,14)
+ assert_equal(d, Date.strptime(d.to_s))
+
+ d = DateTime.new(2002,3,14,11,22,33, 9.to_r/24)
+ assert_equal(d, DateTime.strptime(d.to_s))
+ end
+
+ def test_strptime__ex
+ assert_raise(ArgumentError) do
+ Date.strptime('')
+ end
+ assert_raise(ArgumentError) do
+ DateTime.strptime('')
+ end
+ assert_raise(ArgumentError) do
+ Date.strptime('2001-02-29', '%F')
+ end
+ assert_raise(ArgumentError) do
+ DateTime.strptime('2001-02-29T23:59:60', '%FT%T')
+ end
+ assert_nothing_raised(ArgumentError) do
+ DateTime.strptime('2001-03-01T23:59:60', '%FT%T')
+ end
+ assert_raise(ArgumentError) do
+ DateTime.strptime('2001-03-01T23:59:61', '%FT%T')
+ end
+ assert_raise(ArgumentError) do
+ Date.strptime('23:55', '%H:%M')
+ end
+ assert_raise(ArgumentError) do
+ Date.strptime('01-31-2011', '%m/%d/%Y')
+ end
+ end
+
+ def test_given_string
+ s = '2001-02-03T04:05:06Z'
+ s0 = s.dup
+
+ assert_not_equal({}, Date._strptime(s, '%FT%T%Z'))
+ assert_equal(s0, s)
+ end
+
+ def test_sz
+ d = DateTime.strptime('0 -0200', '%s %z')
+ assert_equal([1969, 12, 31, 22, 0, 0], [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ assert_equal(Rational(-2, 24), d.offset)
+ d = DateTime.strptime('9 +0200', '%s %z')
+ assert_equal([1970, 1, 1, 2, 0, 9], [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ assert_equal(Rational(2, 24), d.offset)
+
+ d = DateTime.strptime('0 -0200', '%Q %z')
+ assert_equal([1969, 12, 31, 22, 0, 0], [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ assert_equal(Rational(-2, 24), d.offset)
+ d = DateTime.strptime('9000 +0200', '%Q %z')
+ assert_equal([1970, 1, 1, 2, 0, 9], [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ assert_equal(Rational(2, 24), d.offset)
+
+ end
+
+end
diff --git a/jni/ruby/test/date/test_switch_hitter.rb b/jni/ruby/test/date/test_switch_hitter.rb
new file mode 100644
index 0000000..08e2301
--- /dev/null
+++ b/jni/ruby/test/date/test_switch_hitter.rb
@@ -0,0 +1,664 @@
+require 'test/unit'
+require 'date'
+
+class TestSH < Test::Unit::TestCase
+
+ def test_new
+ [Date.new,
+ Date.civil,
+ DateTime.new,
+ DateTime.civil
+ ].each do |d|
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ end
+
+ [Date.new(2001),
+ Date.civil(2001),
+ DateTime.new(2001),
+ DateTime.civil(2001)
+ ].each do |d|
+ assert_equal([2001, 1, 1], [d.year, d.mon, d.mday])
+ end
+
+ d = Date.new(2001, 2, 3)
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+ d = Date.new(2001, 2, Rational('3.5'))
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+ d = Date.new(2001,2, 3, Date::JULIAN)
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+ d = Date.new(2001,2, 3, Date::GREGORIAN)
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+
+ d = Date.new(2001,-12, -31)
+ assert_equal([2001, 1, 1], [d.year, d.mon, d.mday])
+ d = Date.new(2001,-12, -31, Date::JULIAN)
+ assert_equal([2001, 1, 1], [d.year, d.mon, d.mday])
+ d = Date.new(2001,-12, -31, Date::GREGORIAN)
+ assert_equal([2001, 1, 1], [d.year, d.mon, d.mday])
+
+ d = DateTime.new(2001, 2, 3, 4, 5, 6)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001, 2, 3, 4, 5, 6, 0)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001, 2, 3, 4, 5, 6, Rational(9,24))
+ assert_equal([2001, 2, 3, 4, 5, 6, Rational(9,24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001, 2, 3, 4, 5, 6, 0.375)
+ assert_equal([2001, 2, 3, 4, 5, 6, Rational(9,24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001, 2, 3, 4, 5, 6, '+09:00')
+ assert_equal([2001, 2, 3, 4, 5, 6, Rational(9,24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001, 2, 3, 4, 5, 6, '-09:00')
+ assert_equal([2001, 2, 3, 4, 5, 6, Rational(-9,24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001, -12, -31, -4, -5, -6, '-09:00')
+ assert_equal([2001, 1, 1, 20, 55, 54, Rational(-9,24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001, -12, -31, -4, -5, -6, '-09:00', Date::JULIAN)
+ assert_equal([2001, 1, 1, 20, 55, 54, Rational(-9,24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001, -12, -31, -4, -5, -6, '-09:00', Date::GREGORIAN)
+ assert_equal([2001, 1, 1, 20, 55, 54, Rational(-9,24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_jd
+ d = Date.jd
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ d = Date.jd(0)
+ assert_equal([-4712, 1, 1], [d.year, d.mon, d.mday])
+ d = Date.jd(2451944)
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+
+ d = DateTime.jd
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.jd(0)
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.jd(2451944)
+ assert_equal([2001, 2, 3, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.jd(2451944, 4, 5, 6)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.jd(2451944, 4, 5, 6, 0)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.jd(2451944, 4, 5, 6, '+9:00')
+ assert_equal([2001, 2, 3, 4, 5, 6, Rational(9, 24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.jd(2451944, -4, -5, -6, '-9:00')
+ assert_equal([2001, 2, 3, 20, 55, 54, Rational(-9, 24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_ordinal
+ d = Date.ordinal
+ assert_equal([-4712, 1], [d.year, d.yday])
+ d = Date.ordinal(-4712, 1)
+ assert_equal([-4712, 1], [d.year, d.yday])
+
+ d = Date.ordinal(2001, 2)
+ assert_equal([2001, 2], [d.year, d.yday])
+ d = Date.ordinal(2001, 2, Date::JULIAN)
+ assert_equal([2001, 2], [d.year, d.yday])
+ d = Date.ordinal(2001, 2, Date::GREGORIAN)
+ assert_equal([2001, 2], [d.year, d.yday])
+
+ d = Date.ordinal(2001, -2, Date::JULIAN)
+ assert_equal([2001, 364], [d.year, d.yday])
+ d = Date.ordinal(2001, -2, Date::GREGORIAN)
+ assert_equal([2001, 364], [d.year, d.yday])
+
+ d = DateTime.ordinal
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.ordinal(-4712, 1)
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.ordinal(2001, 34)
+ assert_equal([2001, 2, 3, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.ordinal(2001, 34, 4, 5, 6)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.ordinal(2001, 34, 4, 5, 6, 0)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.ordinal(2001, 34, 4, 5, 6, '+9:00')
+ assert_equal([2001, 2, 3, 4, 5, 6, Rational(9, 24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.ordinal(2001, 34, -4, -5, -6, '-9:00')
+ assert_equal([2001, 2, 3, 20, 55, 54, Rational(-9, 24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_commercial
+ d = Date.commercial
+ assert_equal([-4712, 1, 1], [d.cwyear, d.cweek, d.cwday])
+ d = Date.commercial(-4712, 1, 1)
+ assert_equal([-4712, 1, 1], [d.cwyear, d.cweek, d.cwday])
+
+ d = Date.commercial(2001, 2, 3)
+ assert_equal([2001, 2, 3], [d.cwyear, d.cweek, d.cwday])
+ d = Date.commercial(2001, 2, 3, Date::JULIAN)
+ assert_equal([2001, 2, 3], [d.cwyear, d.cweek, d.cwday])
+ d = Date.commercial(2001, 2, 3, Date::GREGORIAN)
+ assert_equal([2001, 2, 3], [d.cwyear, d.cweek, d.cwday])
+
+ d = Date.commercial(2001, -2, -3)
+ assert_equal([2001, 51, 5], [d.cwyear, d.cweek, d.cwday])
+ d = Date.commercial(2001, -2, -3, Date::JULIAN)
+ assert_equal([2001, 51, 5], [d.cwyear, d.cweek, d.cwday])
+ d = Date.commercial(2001, -2, -3, Date::GREGORIAN)
+ assert_equal([2001, 51, 5], [d.cwyear, d.cweek, d.cwday])
+
+ d = DateTime.commercial
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.commercial(-4712, 1, 1)
+ assert_equal([-4712, 1, 1, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.commercial(2001, 5, 6)
+ assert_equal([2001, 2, 3, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.commercial(2001, 5, 6, 4, 5, 6)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.commercial(2001, 5, 6, 4, 5, 6, 0)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.commercial(2001, 5, 6, 4, 5, 6, '+9:00')
+ assert_equal([2001, 2, 3, 4, 5, 6, Rational(9, 24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.commercial(2001, 5, 6, -4, -5, -6, '-9:00')
+ assert_equal([2001, 2, 3, 20, 55, 54, Rational(-9, 24)],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_fractional
+ d = Date.jd(2451944.0)
+ assert_equal(2451944, d.jd)
+ d = Date.jd(Rational(2451944))
+ assert_equal(2451944, d.jd)
+ d = Date.jd(2451944.5)
+ assert_equal([2451944, 12], [d.jd, d.send('hour')])
+ d = Date.jd(Rational('2451944.5'))
+ assert_equal([2451944, 12], [d.jd, d.send('hour')])
+
+ d = Date.civil(2001, 2, 3.0)
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+ d = Date.civil(2001, 2, Rational(3))
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+ d = Date.civil(2001, 2, 3.5)
+ assert_equal([2001, 2, 3, 12], [d.year, d.mon, d.mday, d.send('hour')])
+ d = Date.civil(2001, 2, Rational('3.5'))
+ assert_equal([2001, 2, 3, 12], [d.year, d.mon, d.mday, d.send('hour')])
+
+ d = Date.ordinal(2001, 2.0)
+ assert_equal([2001, 2], [d.year, d.yday])
+ d = Date.ordinal(2001, Rational(2))
+ assert_equal([2001, 2], [d.year, d.yday])
+
+ d = Date.commercial(2001, 2, 3.0)
+ assert_equal([2001, 2, 3], [d.cwyear, d.cweek, d.cwday])
+ d = Date.commercial(2001, 2, Rational(3))
+ assert_equal([2001, 2, 3], [d.cwyear, d.cweek, d.cwday])
+
+ d = DateTime.jd(2451944.0)
+ assert_equal(2451944, d.jd)
+ d = DateTime.jd(Rational(2451944))
+ assert_equal(2451944, d.jd)
+ d = DateTime.jd(2451944.5)
+ assert_equal([2451944, 12], [d.jd, d.hour])
+ d = DateTime.jd(Rational('2451944.5'))
+ assert_equal([2451944, 12], [d.jd, d.hour])
+
+ d = DateTime.civil(2001, 2, 3.0)
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+ d = DateTime.civil(2001, 2, Rational(3))
+ assert_equal([2001, 2, 3], [d.year, d.mon, d.mday])
+ d = DateTime.civil(2001, 2, 3.5)
+ assert_equal([2001, 2, 3, 12], [d.year, d.mon, d.mday, d.hour])
+ d = DateTime.civil(2001, 2, Rational('3.5'))
+ assert_equal([2001, 2, 3, 12], [d.year, d.mon, d.mday, d.hour])
+ d = DateTime.civil(2001, 2, 3, 4.5)
+ assert_equal([2001, 2, 3, 4, 30], [d.year, d.mon, d.mday, d.hour, d.min])
+ d = DateTime.civil(2001, 2, 3, Rational('4.5'))
+ assert_equal([2001, 2, 3, 4, 30], [d.year, d.mon, d.mday, d.hour, d.min])
+ d = DateTime.civil(2001, 2, 3, 4, 5.5)
+ assert_equal([2001, 2, 3, 4, 5, 30],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+ d = DateTime.civil(2001, 2, 3, 4, Rational('5.5'))
+ assert_equal([2001, 2, 3, 4, 5, 30],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec])
+
+ d = DateTime.ordinal(2001, 2.0)
+ assert_equal([2001, 2], [d.year, d.yday])
+ d = DateTime.ordinal(2001, Rational(2))
+ assert_equal([2001, 2], [d.year, d.yday])
+
+ d = DateTime.commercial(2001, 2, 3.0)
+ assert_equal([2001, 2, 3], [d.cwyear, d.cweek, d.cwday])
+ d = DateTime.commercial(2001, 2, Rational(3))
+ assert_equal([2001, 2, 3], [d.cwyear, d.cweek, d.cwday])
+
+ end
+
+ def test_canon24oc
+ d = DateTime.jd(2451943,24)
+ assert_equal([2001, 2, 3, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.ordinal(2001,33,24)
+ assert_equal([2001, 2, 3, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.new(2001,2,2,24)
+ assert_equal([2001, 2, 3, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ d = DateTime.commercial(2001,5,5,24)
+ assert_equal([2001, 2, 3, 0, 0, 0, 0],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.offset])
+ end
+
+ def test_zone
+ d = Date.new(2001, 2, 3)
+ assert_equal(Encoding::US_ASCII, d.send(:zone).encoding)
+ d = DateTime.new(2001, 2, 3)
+ assert_equal(Encoding::US_ASCII, d.send(:zone).encoding)
+ end
+
+ def test_to_s
+ d = Date.new(2001, 2, 3)
+ assert_equal(Encoding::US_ASCII, d.to_s.encoding)
+ assert_equal(Encoding::US_ASCII, d.strftime.encoding)
+ d = DateTime.new(2001, 2, 3)
+ assert_equal(Encoding::US_ASCII, d.to_s.encoding)
+ assert_equal(Encoding::US_ASCII, d.strftime.encoding)
+ end
+
+ def test_inspect
+ d = Date.new(2001, 2, 3)
+ assert_equal(Encoding::US_ASCII, d.inspect.encoding)
+ d = DateTime.new(2001, 2, 3)
+ assert_equal(Encoding::US_ASCII, d.inspect.encoding)
+ end
+
+ def test_strftime
+ assert_raise(Errno::ERANGE) do
+ Date.today.strftime('%100000z')
+ end
+ assert_raise(Errno::ERANGE) do
+ Date.new(1 << 10000).strftime('%Y')
+ end
+ assert_equal('-3786825600', Date.new(1850).strftime('%s'))
+ assert_equal('-3786825600000', Date.new(1850).strftime('%Q'))
+ end
+
+ def test_cmp
+ assert_equal(-1, Date.new(2001,2,3) <=> Date.new(2001,2,4))
+ assert_equal(0, Date.new(2001,2,3) <=> Date.new(2001,2,3))
+ assert_equal(1, Date.new(2001,2,3) <=> Date.new(2001,2,2))
+
+ assert_equal(-1, Date.new(2001,2,3) <=> 2451944.0)
+ assert_equal(-1, Date.new(2001,2,3) <=> 2451944)
+ assert_equal(0, Date.new(2001,2,3) <=> 2451943.5)
+ assert_equal(1, Date.new(2001,2,3) <=> 2451943.0)
+ assert_equal(1, Date.new(2001,2,3) <=> 2451943)
+
+ assert_equal(-1, Date.new(2001,2,3) <=> Rational('4903888/2'))
+ assert_equal(0, Date.new(2001,2,3) <=> Rational('4903887/2'))
+ assert_equal(1, Date.new(2001,2,3) <=> Rational('4903886/2'))
+
+ assert_equal(-1, Date.new(-4713,11,1,Date::GREGORIAN) <=> Date.new(-4713,12,1,Date::GREGORIAN))
+ end
+
+ def test_eqeqeq
+ assert_equal(false, Date.new(2001,2,3) === Date.new(2001,2,4))
+ assert_equal(true, Date.new(2001,2,3) === Date.new(2001,2,3))
+ assert_equal(false, Date.new(2001,2,3) === Date.new(2001,2,2))
+
+ assert_equal(true, Date.new(2001,2,3) === 2451944.0)
+ assert_equal(true, Date.new(2001,2,3) === 2451944)
+ assert_equal(false, Date.new(2001,2,3) === 2451943.5)
+ assert_equal(false, Date.new(2001,2,3) === 2451943.0)
+ assert_equal(false, Date.new(2001,2,3) === 2451943)
+
+ assert_equal(true, Date.new(2001,2,3) === Rational('4903888/2'))
+ assert_equal(false, Date.new(2001,2,3) === Rational('4903887/2'))
+ assert_equal(false, Date.new(2001,2,3) === Rational('4903886/2'))
+ end
+
+ def test_period
+ # -5000
+ d = Date.new(-5000,1,1)
+ assert_equal([-5000, 1, 1, 5], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([-5001, 11, 22, 5], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.new(-5000,1,1,Date::JULIAN)
+ assert_equal([-5000, 1, 1, 5], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([-5001, 11, 22, 5], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.new(-5000,1,1,Date::GREGORIAN)
+ assert_equal([-5000, 1, 1, 3], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.julian
+ assert_equal([-5000, 2, 10, 3], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(-105192)
+ assert_equal([-5000, 1, 1, 5], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([-5001, 11, 22, 5], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(-105192,Date::JULIAN)
+ assert_equal([-5000, 1, 1, 5], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([-5001, 11, 22, 5], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(-105152,Date::GREGORIAN)
+ assert_equal([-5000, 1, 1, 3], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.julian
+ assert_equal([-5000, 2, 10, 3], [d2.year, d2.mon, d2.mday, d.wday])
+
+ # -5000000
+ d = Date.new(-5_000_000,1,1)
+ assert_equal([-5_000_000, 1, 1, 3], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([-5_000_103, 4, 28, 3], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.new(-5_000_000,1,1,Date::JULIAN)
+ assert_equal([-5_000_000, 1, 1, 3], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([-5_000_103, 4, 28, 3], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.new(-5_000_000,1,1,Date::GREGORIAN)
+ assert_equal([-5_000_000, 1, 1, 6], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.julian
+ assert_equal([-4_999_898, 9, 4, 6], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(-1824528942)
+ assert_equal([-5_000_000, 1, 1, 3], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([-5_000_103, 4, 28, 3], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(-1824528942,Date::JULIAN)
+ assert_equal([-5_000_000, 1, 1, 3], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([-5_000_103, 4, 28, 3], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(-1824491440,Date::GREGORIAN)
+ assert_equal([-5_000_000, 1, 1, 6], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.julian
+ assert_equal([-4_999_898, 9, 4, 6], [d2.year, d2.mon, d2.mday, d.wday])
+
+ # 5000000
+ d = Date.new(5_000_000,1,1)
+ assert_equal([5_000_000, 1, 1, 6], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.julian
+ assert_equal([4_999_897, 5, 3, 6], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.new(5_000_000,1,1,Date::JULIAN)
+ assert_equal([5_000_000, 1, 1, 5], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([5_000_102, 9, 1, 5], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.new(5_000_000,1,1,Date::GREGORIAN)
+ assert_equal([5_000_000, 1, 1, 6], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.julian
+ assert_equal([4_999_897, 5, 3, 6], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(1827933560)
+ assert_equal([5_000_000, 1, 1, 6], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.julian
+ assert_equal([4_999_897, 5, 3, 6], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(1827971058,Date::JULIAN)
+ assert_equal([5_000_000, 1, 1, 5], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.gregorian
+ assert_equal([5_000_102, 9, 1, 5], [d2.year, d2.mon, d2.mday, d.wday])
+
+ d = Date.jd(1827933560,Date::GREGORIAN)
+ assert_equal([5_000_000, 1, 1, 6], [d.year, d.mon, d.mday, d.wday])
+ d2 = d.julian
+ assert_equal([4_999_897, 5, 3, 6], [d2.year, d2.mon, d2.mday, d.wday])
+
+ # dt
+ d = DateTime.new(-123456789,2,3,4,5,6,0)
+ assert_equal([-123456789, 2, 3, 4, 5, 6, 1],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.wday])
+ d2 = d.gregorian
+ assert_equal([-123459325, 12, 27, 4, 5, 6, 1],
+ [d2.year, d2.mon, d2.mday, d2.hour, d2.min, d2.sec, d.wday])
+
+ d = DateTime.new(123456789,2,3,4,5,6,0)
+ assert_equal([123456789, 2, 3, 4, 5, 6, 5],
+ [d.year, d.mon, d.mday, d.hour, d.min, d.sec, d.wday])
+ d2 = d.julian
+ assert_equal([123454254, 1, 19, 4, 5, 6, 5],
+ [d2.year, d2.mon, d2.mday, d2.hour, d2.min, d2.sec, d.wday])
+ end
+
+ def period2_iter2(from, to, sg)
+ (from..to).each do |j|
+ d = Date.jd(j, sg)
+ d2 = Date.new(d.year, d.mon, d.mday, sg)
+ assert_equal(d2.jd, j)
+ assert_equal(d2.ajd, d.ajd)
+ assert_equal(d2.year, d.year)
+
+ d = DateTime.jd(j, 12,0,0, '+12:00', sg)
+ d2 = DateTime.new(d.year, d.mon, d.mday,
+ d.hour, d.min, d.sec, d.offset, sg)
+ assert_equal(d2.jd, j)
+ assert_equal(d2.ajd, d.ajd)
+ assert_equal(d2.year, d.year)
+ end
+ end
+
+ def period2_iter(from, to)
+ period2_iter2(from, to, Date::GREGORIAN)
+ period2_iter2(from, to, Date::ITALY)
+ period2_iter2(from, to, Date::ENGLAND)
+ period2_iter2(from, to, Date::JULIAN)
+ end
+
+ def test_period2
+ cm_period0 = 71149239
+ cm_period = 0xfffffff.div(cm_period0) * cm_period0
+ period2_iter(-cm_period * (1 << 64) - 3, -cm_period * (1 << 64) + 3)
+ period2_iter(-cm_period - 3, -cm_period + 3)
+ period2_iter(0 - 3, 0 + 3)
+ period2_iter(+cm_period - 3, +cm_period + 3)
+ period2_iter(+cm_period * (1 << 64) - 3, +cm_period * (1 << 64) + 3)
+ end
+
+ def test_different_alignments
+ assert_equal(0, Date.jd(0) <=> Date.civil(-4713, 11, 24, Date::GREGORIAN))
+ assert_equal(0, Date.jd(213447717) <=> Date.civil(579687, 11, 24))
+ assert_equal(0, Date.jd(-213447717) <=> Date.civil(-589113, 11, 24, Date::GREGORIAN))
+
+ assert_equal(0, Date.jd(0) <=> DateTime.civil(-4713, 11, 24, 0, 0, 0, 0, Date::GREGORIAN))
+ assert_equal(0, Date.jd(213447717) <=> DateTime.civil(579687, 11, 24))
+ assert_equal(0, Date.jd(-213447717) <=> DateTime.civil(-589113, 11, 24, 0, 0, 0, 0, Date::GREGORIAN))
+
+ assert(Date.jd(0) == Date.civil(-4713, 11, 24, Date::GREGORIAN))
+ assert(Date.jd(213447717) == Date.civil(579687, 11, 24))
+ assert(Date.jd(-213447717) == Date.civil(-589113, 11, 24, Date::GREGORIAN))
+
+ assert(Date.jd(0) == DateTime.civil(-4713, 11, 24, 0, 0, 0, 0, Date::GREGORIAN))
+ assert(Date.jd(213447717) == DateTime.civil(579687, 11, 24))
+ assert(Date.jd(-213447717) == DateTime.civil(-589113, 11, 24, 0, 0, 0, 0, Date::GREGORIAN))
+
+ assert(Date.jd(0) === Date.civil(-4713, 11, 24, Date::GREGORIAN))
+ assert(Date.jd(213447717) === Date.civil(579687, 11, 24))
+ assert(Date.jd(-213447717) === Date.civil(-589113, 11, 24, Date::GREGORIAN))
+
+ assert(Date.jd(0) === DateTime.civil(-4713, 11, 24, 12, 0, 0, 0, Date::GREGORIAN))
+ assert(Date.jd(213447717) === DateTime.civil(579687, 11, 24, 12))
+ assert(Date.jd(-213447717) === DateTime.civil(-589113, 11, 24, 12, 0, 0, 0, Date::GREGORIAN))
+
+ a = Date.jd(0)
+ b = Date.civil(-4713, 11, 24, Date::GREGORIAN)
+ assert_equal(0, a <=> b)
+
+ a = Date.civil(-4712, 1, 1, Date::JULIAN)
+ b = Date.civil(-4713, 11, 24, Date::GREGORIAN)
+ a.jd; b.jd
+ assert_equal(0, a <=> b)
+
+ a = Date.jd(0)
+ b = Date.civil(-4713, 11, 24, Date::GREGORIAN)
+ assert(a == b)
+
+ a = Date.civil(-4712, 1, 1, Date::JULIAN)
+ b = Date.civil(-4713, 11, 24, Date::GREGORIAN)
+ a.jd; b.jd
+ assert(a == b)
+
+ a = Date.jd(0)
+ b = Date.civil(-4713, 11, 24, Date::GREGORIAN)
+ assert(a === b)
+
+ a = Date.civil(-4712, 1, 1, Date::JULIAN)
+ b = Date.civil(-4713, 11, 24, Date::GREGORIAN)
+ a.jd; b.jd
+ assert(a === b)
+ end
+
+ def test_marshal14
+ s = "\x04\x03u:\x01\x04Date\x01\v\x04\x03[\x01\x02i\x03\xE8i%T"
+ d = Marshal.load(s)
+ assert_equal(Rational(4903887,2), d.ajd)
+ assert_equal(0, d.send(:offset))
+ assert_equal(Date::GREGORIAN, d.start)
+ end
+
+ def test_marshal16
+ s = "\x04\x06u:\tDate\x0F\x04\x06[\ai\x03\xE8i%T"
+ d = Marshal.load(s)
+ assert_equal(Rational(4903887,2), d.ajd)
+ assert_equal(0, d.send(:offset))
+ assert_equal(Date::GREGORIAN, d.start)
+ end
+
+ def test_marshal18
+ s = "\x04\bu:\tDateP\x04\b[\bo:\rRational\a:\x0F@numeratori\x03\xCF\xD3J:\x11@denominatori\ai\x00o:\x13Date::Infinity\x06:\a@di\xFA"
+ d = Marshal.load(s)
+ assert_equal(Rational(4903887,2), d.ajd)
+ assert_equal(0, d.send(:offset))
+ assert_equal(Date::GREGORIAN, d.start)
+
+ s = "\x04\bu:\rDateTime`\x04\b[\bo:\rRational\a:\x0F@numeratorl+\b\xC9\xB0\x81\xBD\x02\x00:\x11@denominatori\x02\xC0\x12o;\x00\a;\x06i\b;\ai\ro:\x13Date::Infinity\x06:\a@di\xFA"
+ d = Marshal.load(s)
+ assert_equal(Rational(11769327817,4800), d.ajd)
+ assert_equal(Rational(9,24), d.offset)
+ assert_equal(Date::GREGORIAN, d.start)
+ end
+
+ def test_marshal192
+ s = "\x04\bU:\tDate[\bU:\rRational[\ai\x03\xCF\xD3Ji\ai\x00o:\x13Date::Infinity\x06:\a@di\xFA"
+ d = Marshal.load(s)
+ assert_equal(Rational(4903887,2), d.ajd)
+ assert_equal(Rational(0,24), d.send(:offset))
+ assert_equal(Date::GREGORIAN, d.start)
+
+ s = "\x04\bU:\rDateTime[\bU:\rRational[\al+\b\xC9\xB0\x81\xBD\x02\x00i\x02\xC0\x12U;\x06[\ai\bi\ro:\x13Date::Infinity\x06:\a@di\xFA"
+ d = Marshal.load(s)
+ assert_equal(Rational(11769327817,4800), d.ajd)
+ assert_equal(Rational(9,24), d.offset)
+ assert_equal(Date::GREGORIAN, d.start)
+ end
+
+ def test_taint
+ h = Date._strptime('15:43+09:00', '%R%z')
+ assert_equal(false, h[:zone].tainted?)
+ h = Date._strptime('15:43+09:00'.taint, '%R%z')
+ assert_equal(true, h[:zone].tainted?)
+
+ h = Date._strptime('1;1/0', '%d')
+ assert_equal(false, h[:leftover].tainted?)
+ h = Date._strptime('1;1/0'.taint, '%d')
+ assert_equal(true, h[:leftover].tainted?)
+
+ h = Date._parse('15:43+09:00')
+ assert_equal(false, h[:zone].tainted?)
+ h = Date._parse('15:43+09:00'.taint)
+ assert_equal(true, h[:zone].tainted?)
+
+ s = Date.today.strftime('new 105')
+ assert_equal(false, s.tainted?)
+ s = Date.today.strftime('new 105'.taint)
+ assert_equal(true, s.tainted?)
+ s = Date.today.strftime("new \000 105".taint)
+ assert_equal(true, s.tainted?)
+
+ s = DateTime.now.strftime('super $record')
+ assert_equal(false, s.tainted?)
+ s = DateTime.now.strftime('super $record'.taint)
+ assert_equal(true, s.tainted?)
+ end
+
+ def test_enc
+ Date::MONTHNAMES.each do |s|
+ assert_equal(Encoding::US_ASCII, s.encoding) if s
+ end
+ Date::DAYNAMES.each do |s|
+ assert_equal(Encoding::US_ASCII, s.encoding) if s
+ end
+ Date::ABBR_MONTHNAMES.each do |s|
+ assert_equal(Encoding::US_ASCII, s.encoding) if s
+ end
+ Date::ABBR_DAYNAMES.each do |s|
+ assert_equal(Encoding::US_ASCII, s.encoding) if s
+ end
+
+ h = Date._strptime('15:43+09:00'.force_encoding('euc-jp'), '%R%z')
+ assert_equal(Encoding::EUC_JP, h[:zone].encoding)
+ h = Date._strptime('15:43+09:00'.force_encoding('ascii-8bit'), '%R%z')
+ assert_equal(Encoding::ASCII_8BIT, h[:zone].encoding)
+
+ h = Date._strptime('1;1/0'.force_encoding('euc-jp'), '%d')
+ assert_equal(Encoding::EUC_JP, h[:leftover].encoding)
+ h = Date._strptime('1;1/0'.force_encoding('ascii-8bit'), '%d')
+ assert_equal(Encoding::ASCII_8BIT, h[:leftover].encoding)
+
+ h = Date._parse('15:43+09:00'.force_encoding('euc-jp'))
+ assert_equal(Encoding::EUC_JP, h[:zone].encoding)
+ h = Date._parse('15:43+09:00'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::ASCII_8BIT, h[:zone].encoding)
+
+ s = Date.today.strftime('new 105'.force_encoding('euc-jp'))
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ s = Date.today.strftime('new 105'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+
+ s = DateTime.now.strftime('super $record'.force_encoding('euc-jp'))
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ s = DateTime.now.strftime('super $record'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ end
+
+ def test_dup
+ d = Date.new(2001,2,3)
+ d2 = d.dup
+ assert_not_equal(d.object_id, d2.object_id)
+ assert_kind_of(Date, d2)
+ assert_equal(d, d2)
+
+ d = DateTime.new(2001,2,3)
+ d2 = d.dup
+ assert_not_equal(d.object_id, d2.object_id)
+ assert_kind_of(DateTime, d2)
+ assert_equal(d, d2)
+ end
+
+ def test_base
+ skip unless defined?(Date.test_all)
+ assert_equal(true, Date.test_all)
+ end
+
+end
diff --git a/jni/ruby/test/dbm/test_dbm.rb b/jni/ruby/test/dbm/test_dbm.rb
new file mode 100644
index 0000000..c783b49
--- /dev/null
+++ b/jni/ruby/test/dbm/test_dbm.rb
@@ -0,0 +1,631 @@
+require 'test/unit'
+require 'tmpdir'
+
+begin
+ require 'dbm'
+rescue LoadError
+end
+
+if defined? DBM
+ require 'tmpdir'
+ require 'fileutils'
+
+ class TestDBM_RDONLY < Test::Unit::TestCase
+ def TestDBM_RDONLY.uname_s
+ require 'rbconfig'
+ case RbConfig::CONFIG['target_os']
+ when 'cygwin'
+ require 'etc'
+ Etc.uname[:sysname]
+ else
+ RbConfig::CONFIG['target_os']
+ end
+ end
+ SYSTEM = uname_s
+
+ def setup
+ @tmpdir = Dir.mktmpdir("tmptest_dbm")
+ @prefix = "tmptest_dbm_#{$$}"
+ @path = "#{@tmpdir}/#{@prefix}_"
+
+ # prepare to make readonly DBM file
+ DBM.open("#{@tmpdir}/#{@prefix}_rdonly") {|dbm|
+ dbm['foo'] = 'FOO'
+ }
+
+ File.chmod(0400, *Dir.glob("#{@tmpdir}/#{@prefix}_rdonly.*"))
+
+ assert_instance_of(DBM, @dbm_rdonly = DBM.new("#{@tmpdir}/#{@prefix}_rdonly", nil))
+ end
+ def teardown
+ assert_nil(@dbm_rdonly.close)
+ ObjectSpace.each_object(DBM) do |obj|
+ obj.close unless obj.closed?
+ end
+ FileUtils.remove_entry_secure @tmpdir
+ end
+
+ def test_delete_rdonly
+ if /^CYGWIN_9/ !~ SYSTEM
+ assert_raise(DBMError) {
+ @dbm_rdonly.delete("foo")
+ }
+
+ assert_nil(@dbm_rdonly.delete("bar"))
+ end
+ end
+
+ def test_fetch_not_found
+ notfound = nil
+ result = Object.new
+ assert_same(result, @dbm_rdonly.fetch("bar") {|k| notfound = k; result})
+ assert_equal("bar", notfound)
+ assert_predicate(notfound, :tainted?)
+ end
+ end
+
+ class TestDBM < Test::Unit::TestCase
+ def setup
+ @tmpdir = Dir.mktmpdir("tmptest_dbm")
+ @prefix = "tmptest_dbm_#{$$}"
+ @path = "#{@tmpdir}/#{@prefix}_"
+ assert_instance_of(DBM, @dbm = DBM.new(@path))
+ end
+ def teardown
+ assert_nil(@dbm.close) unless @dbm.closed?
+ ObjectSpace.each_object(DBM) do |obj|
+ obj.close unless obj.closed?
+ end
+ FileUtils.remove_entry_secure @tmpdir
+ end
+
+ def check_size(expect, dbm=@dbm)
+ assert_equal(expect, dbm.size)
+ n = 0
+ dbm.each { n+=1 }
+ assert_equal(expect, n)
+ if expect == 0
+ assert_equal(true, dbm.empty?)
+ else
+ assert_equal(false, dbm.empty?)
+ end
+ end
+
+ def test_dbmfile_suffix
+ @dbm.close
+ prefix = File.basename(@path)
+ suffixes = Dir.entries(@tmpdir).grep(/\A#{Regexp.escape prefix}/) { $' }.sort
+ pagname = "#{@path}.pag"
+ dirname = "#{@path}.dir"
+ dbname = "#{@path}.db"
+ case DBM::VERSION
+ when /\bNDBM\b/
+ assert_equal(%w[.dir .pag], suffixes)
+ assert(File.zero?(pagname))
+ assert(File.zero?(dirname))
+ when /\bGDBM\b/
+ assert_equal(%w[.dir .pag], suffixes)
+ assert(!File.zero?(pagname))
+ assert(!File.zero?(dirname))
+ pag = File.binread(pagname, 16)
+ pag_magics = [
+ 0x13579ace, # GDBM_OMAGIC
+ 0x13579acd, # GDBM_MAGIC32
+ 0x13579acf, # GDBM_MAGIC64
+ ]
+ assert_operator(pag_magics, :include?,
+ pag.unpack("i")[0]) # native endian, native int.
+ if !File.identical?(pagname, dirname)
+ dir = File.binread(dirname, 16)
+ assert_equal("GDBM", dir[0, 4])
+ end
+ when /\bBerkeley DB\b/
+ assert_equal(%w[.db], suffixes)
+ assert(!File.zero?(dbname))
+ db = File.binread(dbname, 16)
+ assert(db[0,4].unpack("N") == [0x00061561] || # Berkeley DB 1
+ db[12,4].unpack("L") == [0x00061561]) # Berkeley DBM 2 or later.
+ when /\bQDBM\b/
+ assert_equal(%w[.dir .pag], suffixes)
+ assert(!File.zero?(pagname))
+ assert(!File.zero?(dirname))
+ dir = File.binread(dirname, 16)
+ assert_equal("[depot]\0\v", dir[0, 9])
+ pag = File.binread(pagname, 16)
+ if [1].pack("s") == "\x00\x01" # big endian
+ assert_equal("[DEPOT]\n\f", pag[0, 9])
+ else # little endian
+ assert_equal("[depot]\n\f", pag[0, 9])
+ end
+ end
+ if suffixes == %w[.db]
+ assert_match(/\bBerkeley DB\b/, DBM::VERSION)
+ end
+ end
+
+ def test_s_new_has_no_block
+ # DBM.new ignore the block
+ foo = true
+ assert_instance_of(DBM, dbm = DBM.new("#{@tmpdir}/#{@prefix}") { foo = false })
+ assert_equal(foo, true)
+ assert_nil(dbm.close)
+ end
+
+ def test_s_open_no_create
+ skip "dbm_open() is broken on libgdbm 1.8.0 or prior (#{DBM::VERSION})" if /GDBM version 1\.(?:[0-7]\b|8\.0)/ =~ DBM::VERSION
+ assert_nil(dbm = DBM.open("#{@tmpdir}/#{@prefix}", nil))
+ ensure
+ dbm.close if dbm
+ end
+
+ def test_s_open_with_block
+ assert_equal(DBM.open("#{@tmpdir}/#{@prefix}") { :foo }, :foo)
+ end
+
+ def test_close
+ assert_instance_of(DBM, dbm = DBM.open("#{@tmpdir}/#{@prefix}"))
+ assert_nil(dbm.close)
+
+ # closed DBM file
+ assert_raise(DBMError) { dbm.close }
+ end
+
+ def test_aref
+ assert_equal('bar', @dbm['foo'] = 'bar')
+ assert_equal('bar', @dbm['foo'])
+
+ assert_nil(@dbm['bar'])
+ end
+
+ def test_fetch
+ assert_equal('bar', @dbm['foo']='bar')
+ assert_equal('bar', @dbm.fetch('foo'))
+
+ # key not found
+ assert_raise(IndexError) {
+ @dbm.fetch('bar')
+ }
+
+ # test for `ifnone' arg
+ assert_equal('baz', @dbm.fetch('bar', 'baz'))
+
+ # test for `ifnone' block
+ assert_equal('foobar', @dbm.fetch('bar') {|key| 'foo' + key })
+ end
+
+ def test_aset
+ num = 0
+ 2.times {|i|
+ assert_equal('foo', @dbm['foo'] = 'foo')
+ assert_equal('foo', @dbm['foo'])
+ assert_equal('bar', @dbm['foo'] = 'bar')
+ assert_equal('bar', @dbm['foo'])
+
+ num += 1 if i == 0
+ assert_equal(num, @dbm.size)
+
+ # assign nil
+ assert_equal('', @dbm['bar'] = '')
+ assert_equal('', @dbm['bar'])
+
+ num += 1 if i == 0
+ assert_equal(num, @dbm.size)
+
+ # empty string
+ assert_equal('', @dbm[''] = '')
+ assert_equal('', @dbm[''])
+
+ num += 1 if i == 0
+ assert_equal(num, @dbm.size)
+
+ # Fixnum
+ assert_equal('200', @dbm['100'] = '200')
+ assert_equal('200', @dbm['100'])
+
+ num += 1 if i == 0
+ assert_equal(num, @dbm.size)
+
+ # Big key and value
+ assert_equal('y' * 100, @dbm['x' * 100] = 'y' * 100)
+ assert_equal('y' * 100, @dbm['x' * 100])
+
+ num += 1 if i == 0
+ assert_equal(num, @dbm.size)
+ }
+ end
+
+ def test_key
+ assert_equal('bar', @dbm['foo'] = 'bar')
+ assert_equal('foo', @dbm.key('bar'))
+ assert_nil(@dbm['bar'])
+ end
+
+ def test_values_at
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
+ assert_equal(values.reverse, @dbm.values_at(*keys.reverse))
+ end
+
+ def test_select_with_block
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
+ ret = @dbm.select {|k,v|
+ assert_equal(k.upcase, v)
+ k != "bar"
+ }
+ assert_equal([['baz', 'BAZ'], ['foo', 'FOO']],
+ ret.sort)
+ end
+
+ def test_length
+ num = 10
+ assert_equal(0, @dbm.size)
+ num.times {|i|
+ i = i.to_s
+ @dbm[i] = i
+ }
+ assert_equal(num, @dbm.size)
+
+ @dbm.shift
+
+ assert_equal(num - 1, @dbm.size)
+ end
+
+ def test_empty?
+ assert_equal(true, @dbm.empty?)
+ @dbm['foo'] = 'FOO'
+ assert_equal(false, @dbm.empty?)
+ end
+
+ def test_each_pair
+ n = 0
+ @dbm.each_pair { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
+
+ n = 0
+ ret = @dbm.each_pair {|key, val|
+ assert_not_nil(i = keys.index(key))
+ assert_equal(val, values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@dbm, ret)
+ end
+
+ def test_each_value
+ n = 0
+ @dbm.each_value { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
+
+ n = 0
+ ret = @dbm.each_value {|val|
+ assert_not_nil(key = @dbm.key(val))
+ assert_not_nil(i = keys.index(key))
+ assert_equal(val, values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@dbm, ret)
+ end
+
+ def test_each_key
+ n = 0
+ @dbm.each_key { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
+
+ n = 0
+ ret = @dbm.each_key {|key|
+ assert_not_nil(i = keys.index(key))
+ assert_equal(@dbm[key], values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@dbm, ret)
+ end
+
+ def test_keys
+ assert_equal([], @dbm.keys)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
+
+ assert_equal(keys.sort, @dbm.keys.sort)
+ assert_equal(values.sort, @dbm.values.sort)
+ end
+
+ def test_values
+ test_keys
+ end
+
+ def test_shift
+ assert_nil(@dbm.shift)
+ assert_equal(0, @dbm.size)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
+
+ ret_keys = []
+ ret_values = []
+ while ret = @dbm.shift
+ ret_keys.push ret[0]
+ ret_values.push ret[1]
+
+ assert_equal(keys.size - ret_keys.size, @dbm.size)
+ end
+
+ assert_equal(keys.sort, ret_keys.sort)
+ assert_equal(values.sort, ret_values.sort)
+ end
+
+ def test_delete
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ key = keys[1]
+
+ assert_nil(@dbm.delete(key))
+ assert_equal(0, @dbm.size)
+
+ @dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
+
+ assert_equal('BAR', @dbm.delete(key))
+ assert_nil(@dbm[key])
+ assert_equal(2, @dbm.size)
+
+ assert_nil(@dbm.delete(key))
+ end
+
+ def test_delete_with_block
+ key = 'no called block'
+ @dbm[key] = 'foo'
+ assert_equal('foo', @dbm.delete(key) {|k| k.replace 'called block'; :blockval})
+ assert_equal(0, @dbm.size)
+
+ key = 'no called block'
+ assert_equal(:blockval, @dbm.delete(key) {|k| k.replace 'called block'; :blockval})
+ assert_equal(0, @dbm.size)
+ end
+
+ def test_delete_if
+ v = "0"
+ 100.times {@dbm[v] = v; v = v.next}
+
+ ret = @dbm.delete_if {|key, val| key.to_i < 50}
+ assert_equal(@dbm, ret)
+ check_size(50, @dbm)
+
+ ret = @dbm.delete_if {|key, val| key.to_i >= 50}
+ assert_equal(@dbm, ret)
+ check_size(0, @dbm)
+
+ # break
+ v = "0"
+ 100.times {@dbm[v] = v; v = v.next}
+ check_size(100, @dbm)
+ n = 0;
+ @dbm.delete_if {|key, val|
+ break if n > 50
+ n+=1
+ true
+ }
+ assert_equal(51, n)
+ check_size(49, @dbm)
+
+ @dbm.clear
+
+ # raise
+ v = "0"
+ 100.times {@dbm[v] = v; v = v.next}
+ check_size(100, @dbm)
+ n = 0;
+ begin
+ @dbm.delete_if {|key, val|
+ raise "runtime error" if n > 50
+ n+=1
+ true
+ }
+ rescue RuntimeError
+ end
+ assert_equal(51, n)
+ check_size(49, @dbm)
+ end
+
+ def test_reject
+ v = "0"
+ 100.times {@dbm[v] = v; v = v.next}
+
+ hash = @dbm.reject {|key, val| key.to_i < 50}
+ assert_instance_of(Hash, hash)
+ assert_equal(100, @dbm.size)
+
+ assert_equal(50, hash.size)
+ hash.each_pair {|key,val|
+ assert_equal(false, key.to_i < 50)
+ assert_equal(key, val)
+ }
+
+ hash = @dbm.reject {|key, val| key.to_i < 100}
+ assert_instance_of(Hash, hash)
+ assert_equal(true, hash.empty?)
+ end
+
+ def test_clear
+ v = "1"
+ 100.times {v = v.next; @dbm[v] = v}
+
+ assert_equal(@dbm, @dbm.clear)
+
+ # validate DBM#size
+ i = 0
+ @dbm.each { i += 1 }
+ assert_equal(@dbm.size, i)
+ assert_equal(0, i)
+ end
+
+ def test_invert
+ v = "0"
+ 100.times {@dbm[v] = v; v = v.next}
+
+ hash = @dbm.invert
+ assert_instance_of(Hash, hash)
+ assert_equal(100, hash.size)
+ hash.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_update
+ hash = {}
+ v = "0"
+ 100.times {v = v.next; hash[v] = v}
+
+ @dbm["101"] = "101"
+ @dbm.update hash
+ assert_equal(101, @dbm.size)
+ @dbm.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_replace
+ hash = {}
+ v = "0"
+ 100.times {v = v.next; hash[v] = v}
+
+ @dbm["101"] = "101"
+ @dbm.replace hash
+ assert_equal(100, @dbm.size)
+ @dbm.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_haskey?
+ assert_equal('bar', @dbm['foo']='bar')
+ assert_equal(true, @dbm.has_key?('foo'))
+ assert_equal(false, @dbm.has_key?('bar'))
+ end
+
+ def test_has_value?
+ assert_equal('bar', @dbm['foo']='bar')
+ assert_equal(true, @dbm.has_value?('bar'))
+ assert_equal(false, @dbm.has_value?('foo'))
+ end
+
+ def test_to_a
+ v = "0"
+ 100.times {v = v.next; @dbm[v] = v}
+
+ ary = @dbm.to_a
+ assert_instance_of(Array, ary)
+ assert_equal(100, ary.size)
+ ary.each {|key,val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_to_hash
+ v = "0"
+ 100.times {v = v.next; @dbm[v] = v}
+
+ hash = @dbm.to_hash
+ assert_instance_of(Hash, hash)
+ assert_equal(100, hash.size)
+ hash.each {|key,val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+ end
+
+ class TestDBM2 < Test::Unit::TestCase
+ def setup
+ @tmproot = Dir.mktmpdir('ruby-dbm')
+ end
+
+ def teardown
+ FileUtils.remove_entry_secure @tmproot if File.directory?(@tmproot)
+ end
+
+ def test_version
+ assert_instance_of(String, DBM::VERSION)
+ end
+
+ def test_reader_open_notexist
+ assert_raise(Errno::ENOENT) {
+ DBM.open("#{@tmproot}/a", 0666, DBM::READER)
+ }
+ end
+
+ def test_writer_open_notexist
+ skip "dbm_open() is broken on libgdbm 1.8.0 or prior (#{DBM::VERSION})" if /GDBM version 1\.(?:[0-7]\b|8\.0)/ =~ DBM::VERSION
+ assert_raise(Errno::ENOENT) {
+ DBM.open("#{@tmproot}/a", 0666, DBM::WRITER)
+ }
+ end
+
+ def test_wrcreat_open_notexist
+ v = DBM.open("#{@tmproot}/a", 0666, DBM::WRCREAT)
+ assert_instance_of(DBM, v)
+ v.close
+ end
+
+ def test_newdb_open_notexist
+ v = DBM.open("#{@tmproot}/a", 0666, DBM::NEWDB)
+ assert_instance_of(DBM, v)
+ v.close
+ end
+
+ def test_reader_open
+ DBM.open("#{@tmproot}/a") {} # create a db.
+ v = DBM.open("#{@tmproot}/a", nil, DBM::READER) {|d|
+ # Errno::EPERM is raised on Solaris which use ndbm.
+ # DBMError is raised on Debian which use gdbm.
+ assert_raise(Errno::EPERM, DBMError) { d["k"] = "v" }
+ true
+ }
+ assert(v)
+ end
+
+ def test_newdb_open
+ DBM.open("#{@tmproot}/a") {|dbm|
+ dbm["k"] = "v"
+ }
+ v = DBM.open("#{@tmproot}/a", nil, DBM::NEWDB) {|d|
+ assert_equal(0, d.length)
+ assert_nil(d["k"])
+ true
+ }
+ assert(v)
+ end
+
+ def test_freeze
+ DBM.open("#{@tmproot}/a") {|d|
+ d.freeze
+ assert_raise(RuntimeError) { d["k"] = "v" }
+ }
+ end
+ end
+end
diff --git a/jni/ruby/test/digest/digest/foo.rb b/jni/ruby/test/digest/digest/foo.rb
new file mode 100644
index 0000000..d576ef0
--- /dev/null
+++ b/jni/ruby/test/digest/digest/foo.rb
@@ -0,0 +1,10 @@
+module Digest
+ Foo = nil
+
+ sleep 0.2
+
+ remove_const(:Foo)
+
+ class Foo < Class
+ end
+end
diff --git a/jni/ruby/test/digest/test_digest.rb b/jni/ruby/test/digest/test_digest.rb
new file mode 100644
index 0000000..efdc7af
--- /dev/null
+++ b/jni/ruby/test/digest/test_digest.rb
@@ -0,0 +1,272 @@
+# $RoughId: test.rb,v 1.4 2001/07/13 15:38:27 knu Exp $
+# $Id: test_digest.rb 48213 2014-10-31 13:21:51Z knu $
+
+require 'test/unit'
+require 'tempfile'
+
+require 'digest'
+%w[digest/md5 digest/rmd160 digest/sha1 digest/sha2 digest/bubblebabble].each do |lib|
+ begin
+ require lib
+ rescue LoadError
+ end
+end
+
+module TestDigest
+ Data1 = "abc"
+ Data2 = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+
+ def test_s_new
+ self.class::DATA.each do |str, hexdigest|
+ assert_raise(ArgumentError) { self.class::ALGO.new("") }
+ end
+ end
+
+ def test_s_hexdigest
+ self.class::DATA.each do |str, hexdigest|
+ actual = self.class::ALGO.hexdigest(str)
+ assert_equal(hexdigest, actual)
+ assert_equal(Encoding::US_ASCII, actual.encoding)
+ end
+ end
+
+ def test_s_base64digest
+ self.class::DATA.each do |str, hexdigest|
+ digest = [hexdigest].pack("H*")
+ actual = self.class::ALGO.base64digest(str)
+ assert_equal([digest].pack("m0"), actual)
+ assert_equal(Encoding::US_ASCII, actual.encoding)
+ end
+ end
+
+ def test_s_digest
+ self.class::DATA.each do |str, hexdigest|
+ digest = [hexdigest].pack("H*")
+ actual = self.class::ALGO.digest(str)
+ assert_equal(digest, actual)
+ assert_equal(Encoding::BINARY, actual.encoding)
+ end
+ end
+
+ def test_update
+ # This test is also for digest() and hexdigest()
+
+ str = "ABC"
+
+ md = self.class::ALGO.new
+ md.update str
+ assert_equal(self.class::ALGO.hexdigest(str), md.hexdigest)
+ assert_equal(self.class::ALGO.digest(str), md.digest)
+ end
+
+ def test_eq
+ # This test is also for clone()
+
+ md1 = self.class::ALGO.new
+ md1 << "ABC"
+
+ assert_equal(md1, md1.clone, self.class::ALGO)
+
+ bug9913 = '[ruby-core:62967] [Bug #9913]'
+ assert_not_equal(md1, nil, bug9913)
+
+ md2 = self.class::ALGO.new
+ md2 << "A"
+
+ assert_not_equal(md1, md2, self.class::ALGO)
+
+ md2 << "BC"
+
+ assert_equal(md1, md2, self.class::ALGO)
+ end
+
+ def test_s_file
+ Tempfile.create("test_digest_file", mode: File::BINARY) { |tmpfile|
+ str = "hello, world.\r\n"
+ tmpfile.print str
+ tmpfile.close
+
+ assert_equal self.class::ALGO.new.update(str), self.class::ALGO.file(tmpfile.path)
+ }
+ end
+
+ def test_instance_eval
+ assert_nothing_raised {
+ self.class::ALGO.new.instance_eval { update "a" }
+ }
+ end
+
+ def test_alignment
+ md = self.class::ALGO.new
+ assert_nothing_raised('#4320') {
+ md.update('a' * 97)
+ md.update('a' * 97)
+ md.hexdigest
+ }
+ end
+
+ def test_bubblebabble
+ expected = "xirek-hasol-fumik-lanax"
+ assert_equal expected, Digest.bubblebabble('message')
+ end
+
+ def test_bubblebabble_class
+ expected = "xopoh-fedac-fenyh-nehon-mopel-nivor-lumiz-rypon-gyfot-cosyz-rimez-lolyv-pekyz-rosud-ricob-surac-toxox"
+ assert_equal expected, Digest::SHA256.bubblebabble('message')
+ end
+
+ def test_bubblebabble_instance
+ expected = "xumor-boceg-dakuz-sulic-gukoz-rutas-mekek-zovud-gunap-vabov-genin-rygyg-sanun-hykac-ruvah-dovah-huxex"
+
+ hash = Digest::SHA256.new
+ assert_equal expected, hash.bubblebabble
+ end
+
+ class TestMD5 < Test::Unit::TestCase
+ include TestDigest
+ ALGO = Digest::MD5
+ DATA = {
+ Data1 => "900150983cd24fb0d6963f7d28e17f72",
+ Data2 => "8215ef0796a20bcaaae116d3876c664a",
+ }
+ end if defined?(Digest::MD5)
+
+ class TestSHA1 < Test::Unit::TestCase
+ include TestDigest
+ ALGO = Digest::SHA1
+ DATA = {
+ Data1 => "a9993e364706816aba3e25717850c26c9cd0d89d",
+ Data2 => "84983e441c3bd26ebaae4aa1f95129e5e54670f1",
+ }
+ end if defined?(Digest::SHA1)
+
+ class TestSHA256 < Test::Unit::TestCase
+ include TestDigest
+ ALGO = Digest::SHA256
+ DATA = {
+ Data1 => "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+ Data2 => "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1",
+ }
+ end if defined?(Digest::SHA256)
+
+ class TestSHA384 < Test::Unit::TestCase
+ include TestDigest
+ ALGO = Digest::SHA384
+ DATA = {
+ Data1 => "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7",
+ Data2 => "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b",
+ }
+ end if defined?(Digest::SHA384)
+
+ class TestSHA512 < Test::Unit::TestCase
+ include TestDigest
+ ALGO = Digest::SHA512
+ DATA = {
+ Data1 => "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+ Data2 => "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445",
+ }
+ end if defined?(Digest::SHA512)
+
+ class TestSHA2 < Test::Unit::TestCase
+
+ def test_s_file
+ Tempfile.create("test_digest_file") { |tmpfile|
+ str = Data1
+ tmpfile.print str
+ tmpfile.close
+
+ assert_equal "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", Digest::SHA2.file(tmpfile.path, 384).hexdigest
+ }
+ end
+
+ end if defined?(Digest::SHA2)
+
+ class TestRMD160 < Test::Unit::TestCase
+ include TestDigest
+ ALGO = Digest::RMD160
+ DATA = {
+ Data1 => "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc",
+ Data2 => "12a053384a9c0c88e405a06c27dcf49ada62eb2b",
+ }
+ end if defined?(Digest::RMD160)
+
+ class TestBase < Test::Unit::TestCase
+ def test_base
+ bug3810 = '[ruby-core:32231]'
+ assert_raise(NotImplementedError, bug3810) {Digest::Base.new}
+ end
+ end
+
+ class TestInitCopy < Test::Unit::TestCase
+ if defined?(Digest::MD5) and defined?(Digest::RMD160)
+ def test_initialize_copy_md5_rmd160
+ assert_separately(%w[-rdigest], <<-'end;')
+ md5 = Digest::MD5.allocate
+ rmd160 = Digest::RMD160.allocate
+ assert_raise(TypeError) {md5.__send__(:initialize_copy, rmd160)}
+ end;
+ end
+ end
+ end
+
+ class TestDigestParen < Test::Unit::TestCase
+ def test_sha2
+ assert_separately(%w[-rdigest], <<-'end;')
+ assert_nothing_raised {
+ Digest(:SHA256).new
+ Digest(:SHA384).new
+ Digest(:SHA512).new
+ }
+ end;
+ end
+
+ def test_no_lib
+ assert_separately(%w[-rdigest], <<-'end;')
+ class Digest::Nolib < Digest::Class
+ end
+
+ assert_nothing_raised {
+ Digest(:Nolib).new
+ }
+ end;
+ end
+
+ def test_no_lib_no_def
+ assert_separately(%w[-rdigest], <<-'end;')
+ assert_raise(LoadError) {
+ Digest(:Nodef).new
+ }
+ end;
+ end
+
+ def test_race
+ assert_separately(['-rdigest', "-I#{File.dirname(__FILE__)}"], <<-'end;')
+ assert_nothing_raised {
+ t = Thread.start {
+ sleep 0.1
+ Digest(:Foo).new
+ }
+ Digest(:Foo).new
+ t.join
+ }
+ end;
+ end
+
+ def test_race_mixed
+ assert_separately(['-rdigest', "-I#{File.dirname(__FILE__)}"], <<-'end;')
+ assert_nothing_raised {
+ t = Thread.start {
+ sleep 0.1
+ Digest::Foo.new
+ }
+ Digest(:Foo).new
+ begin
+ t.join
+ rescue NoMethodError, NameError
+ # NoMethodError is highly likely; NameError is listed just in case
+ end
+ }
+ end;
+ end
+ end
+end
diff --git a/jni/ruby/test/digest/test_digest_extend.rb b/jni/ruby/test/digest/test_digest_extend.rb
new file mode 100644
index 0000000..adc5613
--- /dev/null
+++ b/jni/ruby/test/digest/test_digest_extend.rb
@@ -0,0 +1,158 @@
+require 'test/unit'
+require 'digest'
+require_relative '../lib/with_different_ofs.rb'
+
+class TestDigestExtend < Test::Unit::TestCase
+ extend DifferentOFS
+
+ class MyDigest < Digest::Class
+ def initialize(*arg)
+ super
+ @buf = []
+ end
+
+ def initialize_copy(org)
+ @buf = org.buf.dup
+ end
+
+ def update(arg)
+ @buf << arg
+ self
+ end
+
+ alias << update
+
+ def finish
+ (@buf.join('').length % 256).chr
+ end
+
+ def reset
+ @buf.clear
+ self
+ end
+
+ protected
+
+ def buf
+ @buf
+ end
+ end
+
+ def setup
+ @MyDigest = Class.new(MyDigest)
+ end
+
+ def test_digest_s_hexencode
+ assert_equal('', Digest.hexencode(''))
+ assert_equal('0102', Digest.hexencode("\1\2"))
+ assert_equal(
+ (0..0xff).to_a.map { |c| sprintf("%02x", c ) }.join(''),
+ Digest.hexencode((0..0xff).to_a.map { |c| c.chr }.join(''))
+ )
+ assert_equal(Encoding::US_ASCII, Digest.hexencode("\1\2").encoding)
+ end
+
+ def test_class_reset
+ a = Digest::SHA1.new
+ base = a.to_s
+ assert_equal(base, a.reset.to_s)
+ b = a.new
+ assert_equal(base, b.to_s)
+ b.update('1')
+ assert_not_equal(base, b.to_s)
+ assert_equal(base, b.reset.to_s)
+ end
+
+ def test_digest
+ assert_equal("\3", MyDigest.digest("foo"))
+ end
+
+ def test_hexdigest
+ assert_equal("03", @MyDigest.hexdigest("foo"))
+ end
+
+ def test_context
+ digester = @MyDigest.new
+ digester.update("foo")
+ assert_equal("\3", digester.digest)
+ digester.update("foobar")
+ assert_equal("\t", digester.digest)
+ digester.update("foo")
+ assert_equal("\f", digester.digest)
+ end
+
+ def test_new
+ a = Digest::SHA1.new
+ b = a.new
+ obj = a.to_s
+ assert_equal(obj, a.to_s)
+ assert_equal(obj, b.to_s)
+ a.update('1')
+ assert_not_equal(obj, a.to_s)
+ assert_equal(obj, b.to_s)
+ end
+
+ def test_digest_hexdigest
+ [:digest, :hexdigest].each do |m|
+ exp_1st = "\3"; exp_1st = Digest.hexencode(exp_1st) if m == :hexdigest
+ exp_2nd = "\6"; exp_2nd = Digest.hexencode(exp_2nd) if m == :hexdigest
+ digester = @MyDigest.new
+ digester.update("foo")
+ obj = digester.send(m)
+ # digest w/o param does not reset the org digester.
+ assert_equal(exp_1st, obj)
+ digester.update("bar")
+ obj = digester.send(m)
+ assert_equal(exp_2nd, obj)
+ obj = digester.send(m, "baz")
+ # digest with param resets the org digester.
+ assert_equal(exp_1st, obj)
+ end
+ end
+
+ def test_digest_hexdigest_bang
+ [:digest!, :hexdigest!].each do |m|
+ exp_1st = "\3"; exp_1st = Digest.hexencode(exp_1st) if m == :hexdigest!
+ digester = @MyDigest.new
+ digester.update("foo")
+ obj = digester.send(m) # digest! always resets the org digester.
+ assert_equal(exp_1st, obj)
+ digester.update("bar")
+ obj = digester.send(m)
+ assert_equal(exp_1st, obj)
+ end
+ end
+
+ def test_to_s
+ digester = @MyDigest.new
+ digester.update("foo")
+ assert_equal("03", digester.to_s)
+ end
+
+ def test_length
+ @MyDigest.class_eval do
+ def digest_length
+ 2
+ end
+ end
+ digester = @MyDigest.new
+ assert_equal(2, digester.length)
+ assert_equal(2, digester.size)
+ end
+
+ def test_digest_length # breaks @MyDigest#digest_length
+ assert_equal(1, @MyDigest.new.digest_length)
+ @MyDigest.class_eval do
+ def digest_length
+ 2
+ end
+ end
+ assert_equal(2, @MyDigest.new.digest_length)
+ end
+
+ def test_block_length
+ assert_raises(RuntimeError) do
+ @MyDigest.new.block_length
+ end
+ end
+end
diff --git a/jni/ruby/test/drb/drbtest.rb b/jni/ruby/test/drb/drbtest.rb
new file mode 100644
index 0000000..aac705c
--- /dev/null
+++ b/jni/ruby/test/drb/drbtest.rb
@@ -0,0 +1,368 @@
+require 'test/unit'
+require 'drb/drb'
+require 'drb/extservm'
+require 'timeout'
+require 'shellwords'
+
+module DRbTests
+
+class DRbService
+ @@manager = DRb::ExtServManager.new
+ @@ruby = Shellwords.escape(EnvUtil.rubybin)
+ @@ruby += " -d" if $DEBUG
+ def self.add_service_command(nm)
+ dir = File.dirname(File.expand_path(__FILE__))
+ DRb::ExtServManager.command[nm] = [@@ruby, "#{dir}/#{nm}"]
+ end
+
+ %w(ut_drb.rb ut_array.rb ut_port.rb ut_large.rb ut_safe1.rb ut_eval.rb ut_eq.rb).each do |nm|
+ add_service_command(nm)
+ end
+ @server = @@server = DRb::DRbServer.new('druby://localhost:0', @@manager, {})
+ @@manager.uri = @@server.uri
+ def self.manager
+ @@manager
+ end
+ def self.server
+ @server || @@server
+ end
+ def self.ext_service(name)
+ timeout(100, RuntimeError) do
+ manager.service(name)
+ end
+ end
+ def self.finish
+ @server.instance_variable_get(:@grp).list.each {|th| th.join }
+ end
+end
+
+class Onecky
+ include DRbUndumped
+ def initialize(n)
+ @num = n
+ end
+
+ def to_i
+ @num.to_i
+ end
+
+ def sleep(n)
+ Kernel.sleep(n)
+ to_i
+ end
+end
+
+class FailOnecky < Onecky
+ class OneckyError < RuntimeError; end
+ def to_i
+ raise(OneckyError, @num.to_s)
+ end
+end
+
+class XArray < Array
+ def initialize(ary)
+ ary.each do |x|
+ self.push(x)
+ end
+ end
+end
+
+module DRbBase
+ def setup_service(service_name)
+ @service_name = service_name
+ @ext = DRbService.ext_service(@service_name)
+ @there = @ext.front
+ end
+
+ def teardown
+ @ext.stop_service if defined?(@ext) && @ext
+ DRbService.manager.unregist(@service_name)
+ while (@there&&@there.to_s rescue nil)
+ # nop
+ end
+ signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
+ Thread.list.each {|th|
+ if th.respond_to?(:pid) && th[:drb_service] == @service_name
+ 10.times do
+ begin
+ Process.kill signal, th.pid
+ break
+ rescue Errno::ESRCH
+ break
+ rescue Errno::EPERM # on Windows
+ sleep 0.1
+ retry
+ end
+ end
+ th.join
+ end
+ }
+ end
+end
+
+module DRbCore
+ include DRbBase
+
+ def test_00_DRbObject
+ ro = DRbObject.new(nil, 'druby://localhost:12345')
+ assert_equal('druby://localhost:12345', ro.__drburi)
+ assert_equal(nil, ro.__drbref)
+
+ ro = DRbObject.new_with_uri('druby://localhost:12345')
+ assert_equal('druby://localhost:12345', ro.__drburi)
+ assert_equal(nil, ro.__drbref)
+
+ ro = DRbObject.new_with_uri('druby://localhost:12345?foobar')
+ assert_equal('druby://localhost:12345', ro.__drburi)
+ assert_equal(DRb::DRbURIOption.new('foobar'), ro.__drbref)
+ end
+
+ def test_01
+ assert_equal("hello", @there.hello)
+ onecky = Onecky.new('3')
+ assert_equal(6, @there.sample(onecky, 1, 2))
+ ary = @there.to_a
+ assert_kind_of(DRb::DRbObject, ary)
+
+ assert(@there.respond_to?(:to_a, true))
+ assert(@there.respond_to?(:eval, true))
+ assert(! @there.respond_to?(:eval, false))
+ assert(! @there.respond_to?(:eval))
+ end
+
+ def test_01_02_loop
+ onecky = Onecky.new('3')
+ 50.times do
+ assert_equal(6, @there.sample(onecky, 1, 2))
+ ary = @there.to_a
+ assert_kind_of(DRb::DRbObject, ary)
+ end
+ end
+
+ def test_02_unknown
+ obj = @there.unknown_class
+ assert_kind_of(DRb::DRbUnknown, obj)
+ assert_equal('DRbTests::Unknown2', obj.name)
+
+ obj = @there.unknown_module
+ assert_kind_of(DRb::DRbUnknown, obj)
+ assert_equal('DRbTests::DRbEx::', obj.name)
+
+ assert_raise(DRb::DRbUnknownError) do
+ @there.unknown_error
+ end
+
+ onecky = FailOnecky.new('3')
+
+ assert_raise(FailOnecky::OneckyError) do
+ @there.sample(onecky, 1, 2)
+ end
+ end
+
+ def test_03
+ assert_equal(8, @there.sum(1, 1, 1, 1, 1, 1, 1, 1))
+ assert_raise(DRb::DRbConnError) do
+ @there.sum(1, 1, 1, 1, 1, 1, 1, 1, 1)
+ end
+ assert_raise(DRb::DRbConnError) do
+ @there.sum('1' * 4096)
+ end
+ end
+
+ def test_04
+ assert_respond_to(@there, 'sum')
+ assert(!(@there.respond_to? "foobar"))
+ end
+
+ def test_05_eq
+ a = @there.to_a[0]
+ b = @there.to_a[0]
+ assert(a.object_id != b.object_id)
+ assert(a == b)
+ assert_equal(a, b)
+ assert(a == @there)
+ assert_equal(a.hash, b.hash)
+ assert_equal(a.hash, @there.hash)
+ assert(a.eql?(b))
+ assert(a.eql?(@there))
+ end
+
+ def test_06_timeout
+ ten = Onecky.new(10)
+ assert_raise(Timeout::Error) do
+ @there.do_timeout(ten)
+ end
+ assert_raise(Timeout::Error) do
+ @there.do_timeout(ten)
+ end
+ end
+
+ def test_07_public_private_protected_missing
+ assert_nothing_raised() {
+ begin
+ @there.method_missing(:eval, 'nil')
+ rescue NoMethodError
+ assert_match(/^private method \`eval\'/, $!.message)
+ end
+ }
+ assert_nothing_raised() {
+ begin
+ @there.call_private_method
+ rescue NoMethodError
+ assert_equal(NoMethodError, $!.class)
+ assert_match(/^private method \`call_private_method\'/, $!.message)
+ end
+ }
+ assert_nothing_raised() {
+ begin
+ @there.call_protected_method
+ rescue NoMethodError
+ assert_equal(NoMethodError, $!.class)
+ assert_match(/^protected method \`call_protected_method\'/, $!.message)
+ end
+ }
+ assert_nothing_raised() {
+ begin
+ @there.method_missing(:undefined_method_test)
+ rescue NoMethodError
+ assert_equal(NoMethodError, $!.class)
+ assert_match(/^undefined method \`undefined_method_test\'/, $!.message)
+ end
+ }
+ assert_raise(DRb::DRbConnError) do
+ @there.method_missing(:__send__, :to_s)
+ end
+ assert_equal(true, @there.missing)
+ end
+
+ def test_08_here
+ ro = DRbObject.new(nil, DRb.uri)
+ assert_kind_of(String, ro.to_s)
+
+ ro = DRbObject.new_with_uri(DRb.uri)
+ assert_kind_of(String, ro.to_s)
+ end
+
+ def uri_concat_option(uri, opt)
+ "#{uri}?#{opt}"
+ end
+
+ def test_09_option
+ uri = uri_concat_option(@there.__drburi, "foo")
+ ro = DRbObject.new_with_uri(uri)
+ assert_equal(ro.__drburi, @there.__drburi)
+ assert_equal(3, ro.size)
+
+ uri = uri_concat_option(@there.__drburi, "")
+ ro = DRbObject.new_with_uri(uri)
+ assert_equal(ro.__drburi, @there.__drburi)
+ assert_equal(DRb::DRbURIOption.new(''), ro.__drbref)
+
+ uri = uri_concat_option(@there.__drburi, "hello?world")
+ ro = DRbObject.new_with_uri(uri)
+ assert_equal(DRb::DRbURIOption.new('hello?world'), ro.__drbref)
+
+ uri = uri_concat_option(@there.__drburi, "?hello?world")
+ ro = DRbObject.new_with_uri(uri)
+ assert_equal(DRb::DRbURIOption.new('?hello?world'), ro.__drbref)
+ end
+
+ def test_10_yield
+ @there.simple_hash.each do |k, v|
+ assert_kind_of(String, k)
+ assert_kind_of(Symbol, v)
+ end
+ end
+
+ def test_10_yield_undumped
+ @there.xarray2_hash.each do |k, v|
+ assert_kind_of(String, k)
+ assert_kind_of(DRbObject, v)
+ end
+ end
+
+ def test_11_remote_no_method_error
+ assert_raise(DRb::DRbRemoteError) do
+ @there.remote_no_method_error
+ end
+ begin
+ @there.remote_no_method_error
+ rescue
+ error = $!
+ assert_match(/^undefined method .*\(NoMethodError\)/, error.message)
+ assert_equal('NoMethodError', error.reason)
+ end
+ end
+end
+
+module DRbAry
+ include DRbBase
+
+ def test_01
+ assert_kind_of(DRb::DRbObject, @there)
+ end
+
+ def test_02_collect
+ ary = @there.collect do |x| x + x end
+ assert_kind_of(Array, ary)
+ assert_equal([2, 4, 'IIIIII', 8, 'fivefive', 12], ary)
+ end
+
+ def test_03_redo
+ ary = []
+ count = 0
+ @there.each do |x|
+ count += 1
+ ary.push x
+ redo if count == 3
+ end
+ assert_equal([1, 2, 'III', 'III', 4, 'five', 6], ary)
+ end
+
+ # retry in block is not supported on ruby 1.9
+ #def test_04_retry
+ # retried = false
+ # ary = []
+ # @there.each do |x|
+ # ary.push x
+ # if x == 4 && !retried
+ # retried = true
+ # retry
+ # end
+ # end
+ # assert_equal([1, 2, 'III', 4, 1, 2, 'III', 4, 'five', 6], ary)
+ #end
+
+ def test_05_break
+ ary = []
+ @there.each do |x|
+ ary.push x
+ break if x == 4
+ end
+ assert_equal([1, 2, 'III', 4], ary)
+ end
+
+ def test_06_next
+ ary = []
+ @there.each do |x|
+ next if String === x
+ ary.push x
+ end
+ assert_equal([1, 2, 4, 6], ary)
+ end
+
+ class_eval <<EOS
+ def test_07_break_18
+ ary = []
+ result = @there.each do |x|
+ ary.push x
+ break(:done) if x == 4
+ end
+ assert_equal([1, 2, 'III', 4], ary)
+ assert_equal(:done, result)
+ end
+EOS
+
+end
+
+end
diff --git a/jni/ruby/test/drb/ignore_test_drb.rb b/jni/ruby/test/drb/ignore_test_drb.rb
new file mode 100644
index 0000000..8b94650
--- /dev/null
+++ b/jni/ruby/test/drb/ignore_test_drb.rb
@@ -0,0 +1,13 @@
+require 'drbtest'
+
+module DRbTests
+
+class TestDRbReusePort < Test::Unit::TestCase
+ include DRbAry
+
+ def setup
+ setup_service 'ut_port.rb'
+ end
+end
+
+end
diff --git a/jni/ruby/test/drb/test_acl.rb b/jni/ruby/test/drb/test_acl.rb
new file mode 100644
index 0000000..a1ee1d4
--- /dev/null
+++ b/jni/ruby/test/drb/test_acl.rb
@@ -0,0 +1,197 @@
+# acltest.rb - ACL unit test
+# Copyright (c) 2000 Masatoshi SEKI
+#
+# acltest.rb is copyrighted free software by Masatoshi SEKI.
+# You can redistribute it and/or modify it under the same terms as Ruby.
+
+require 'test/unit'
+require 'drb/acl'
+
+module DRbTests
+
+class SampleHosts
+ def initialize
+ list = %w(127.0.0.1 localhost
+ 192.168.1.1 x68k.linux.or.jp
+ 192.168.1.2 lc630.macos.or.jp
+ 192.168.1.3 lib30.win32.or.jp
+ 192.168.1.4 ns00.linux.or.jp
+ 192.168.1.5 yum.macos.or.jp
+ ::ffff:192.168.1.5 ipv6.macos.or.jp
+ ::192.168.1.5 too.yumipv6.macos.or.jp
+ 192.168.1.254 comstarz.foo.or.jp)
+
+ @hostlist = Array.new(list.size / 2)
+ @hostlist.each_index do |idx|
+ @hostlist[idx] = ["AF_INET", 10000, list[idx * 2 + 1], list[idx * 2]]
+ end
+
+ @hosts = Hash.new
+ @hostlist.each do |h|
+ @hosts[h[2].split('.')[0]] = h
+ end
+ end
+ attr_reader(:hostlist, :hosts)
+end
+
+
+class ACLEntryTest < Test::Unit::TestCase
+ HOSTS = SampleHosts.new
+
+ def setup
+ @hostlist = HOSTS.hostlist
+ @hosts = HOSTS.hosts
+ end
+
+ def test_all
+ a = ACL::ACLEntry.new("*")
+ b = ACL::ACLEntry.new("all")
+ @hostlist.each do |h|
+ assert(a.match(h))
+ assert(b.match(h))
+ end
+ end
+
+ def test_ip_v6
+ a = ACL::ACLEntry.new('::ffff:192.0.0.0/104')
+ assert(! a.match(@hosts['localhost']))
+ assert(a.match(@hosts['yum']))
+ assert(a.match(@hosts['ipv6']))
+ assert(! a.match(@hosts['too']))
+ end
+
+ def test_ip
+ a = ACL::ACLEntry.new('192.0.0.0/8')
+ assert(! a.match(@hosts['localhost']))
+ assert(a.match(@hosts['yum']))
+
+ a = ACL::ACLEntry.new('192.168.0.1/255.255.0.255')
+ assert(! a.match(@hosts['localhost']))
+ assert(! a.match(@hosts['yum']))
+ assert(a.match(@hosts['x68k']))
+
+ a = ACL::ACLEntry.new('192.168.1.0/24')
+ assert(! a.match(@hosts['localhost']))
+ assert(a.match(@hosts['yum']))
+ assert(a.match(@hosts['x68k']))
+
+ a = ACL::ACLEntry.new('92.0.0.0/8')
+ assert(! a.match(@hosts['localhost']))
+ assert(! a.match(@hosts['yum']))
+ assert(! a.match(@hosts['x68k']))
+
+ a = ACL::ACLEntry.new('127.0.0.1/255.0.0.255')
+ assert(a.match(@hosts['localhost']))
+ assert(! a.match(@hosts['yum']))
+ assert(! a.match(@hosts['x68k']))
+ end
+
+ def test_name
+ a = ACL::ACLEntry.new('*.jp')
+ assert(! a.match(@hosts['localhost']))
+ assert(a.match(@hosts['yum']))
+
+ a = ACL::ACLEntry.new('yum.*.jp')
+ assert(a.match(@hosts['yum']))
+ assert(! a.match(@hosts['lc630']))
+
+ a = ACL::ACLEntry.new('*.macos.or.jp')
+ assert(a.match(@hosts['yum']))
+ assert(a.match(@hosts['lc630']))
+ assert(! a.match(@hosts['lib30']))
+ end
+end
+
+class ACLListTest < Test::Unit::TestCase
+ HOSTS = SampleHosts.new
+
+ def setup
+ @hostlist = HOSTS.hostlist
+ @hosts = HOSTS.hosts
+ end
+
+ private
+ def build(list)
+ acl= ACL::ACLList.new
+ list.each do |s|
+ acl.add s
+ end
+ acl
+ end
+
+ public
+ def test_all_1
+ a = build(%w(all))
+ @hostlist.each do |h|
+ assert(a.match(h))
+ end
+ end
+
+ def test_all_2
+ a = build(%w(localhost 127.0.0.0/8 yum.* *))
+ @hostlist.each do |h|
+ assert(a.match(h))
+ end
+ end
+
+ def test_1
+ a = build(%w(192.0.0.1/255.0.0.255 yum.*.jp))
+ assert(a.match(@hosts['yum']))
+ assert(a.match(@hosts['x68k']))
+ assert(! a.match(@hosts['lc630']))
+ end
+
+ def test_2
+ a = build(%w(*.linux.or.jp))
+ assert(!a.match(@hosts['yum']))
+ assert(a.match(@hosts['x68k']))
+ assert(!a.match(@hosts['lc630']))
+ end
+end
+
+class ACLTest < Test::Unit::TestCase
+ HOSTS = SampleHosts.new
+
+ def setup
+ @hostlist = HOSTS.hostlist
+ @hosts = HOSTS.hosts
+ end
+
+ def test_0
+ a = ACL.new
+ @hostlist.each do |h|
+ assert(a.allow_addr?(h))
+ end
+ end
+
+ def test_not_0
+ a = ACL.new([], ACL::ALLOW_DENY)
+ @hostlist.each do |h|
+ assert(! a.allow_addr?(h))
+ end
+ end
+
+ def test_1
+ data = %w(deny all
+ allow localhost
+ allow x68k.*)
+
+ a = ACL.new(data)
+ assert(a.allow_addr?(@hosts['x68k']))
+ assert(a.allow_addr?(@hosts['localhost']))
+ assert(! a.allow_addr?(@hosts['lc630']))
+ end
+
+ def test_not_1
+ data = %w(deny 192.0.0.0/8
+ allow localhost
+ allow x68k.*)
+
+ a = ACL.new(data, ACL::ALLOW_DENY)
+ assert(!a.allow_addr?(@hosts['x68k']))
+ assert(a.allow_addr?(@hosts['localhost']))
+ assert(! a.allow_addr?(@hosts['lc630']))
+ end
+end
+
+end
diff --git a/jni/ruby/test/drb/test_drb.rb b/jni/ruby/test/drb/test_drb.rb
new file mode 100644
index 0000000..ffc2935
--- /dev/null
+++ b/jni/ruby/test/drb/test_drb.rb
@@ -0,0 +1,357 @@
+require_relative 'drbtest'
+
+module DRbTests
+
+class TestDRbCore < Test::Unit::TestCase
+ include DRbCore
+
+ def setup
+ setup_service 'ut_drb.rb'
+ super
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+end
+
+class TestDRbYield < Test::Unit::TestCase
+ include DRbBase
+
+ def setup
+ setup_service 'ut_drb.rb'
+ super
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+
+ def test_01_one
+ @there.echo_yield_1([]) {|one|
+ assert_equal([], one)
+ }
+
+ @there.echo_yield_1(1) {|one|
+ assert_equal(1, one)
+ }
+
+ @there.echo_yield_1(nil) {|one|
+ assert_equal(nil, one)
+ }
+ end
+
+ def test_02_two
+ @there.echo_yield_2([], []) {|one, two|
+ assert_equal([], one)
+ assert_equal([], two)
+ }
+
+ @there.echo_yield_2(1, 2) {|one, two|
+ assert_equal(1, one)
+ assert_equal(2, two)
+ }
+
+ @there.echo_yield_2(3, nil) {|one, two|
+ assert_equal(3, one)
+ assert_equal(nil, two)
+ }
+
+ @there.echo_yield_1([:key, :value]) {|one, two|
+ assert_equal(:key, one)
+ assert_equal(:value, two)
+ }
+ end
+
+ def test_03_many
+ @there.echo_yield_0 {|*s|
+ assert_equal([], s)
+ }
+ @there.echo_yield(nil) {|*s|
+ assert_equal([nil], s)
+ }
+ @there.echo_yield(1) {|*s|
+ assert_equal([1], s)
+ }
+ @there.echo_yield(1, 2) {|*s|
+ assert_equal([1, 2], s)
+ }
+ @there.echo_yield(1, 2, 3) {|*s|
+ assert_equal([1, 2, 3], s)
+ }
+ @there.echo_yield([], []) {|*s|
+ assert_equal([[], []], s)
+ }
+ @there.echo_yield([]) {|*s|
+ assert_equal([[]], s) # !
+ }
+ end
+
+ def test_04_many_to_one
+ @there.echo_yield_0 {|*s|
+ assert_equal([], s)
+ }
+ @there.echo_yield(nil) {|*s|
+ assert_equal([nil], s)
+ }
+ @there.echo_yield(1) {|*s|
+ assert_equal([1], s)
+ }
+ @there.echo_yield(1, 2) {|*s|
+ assert_equal([1, 2], s)
+ }
+ @there.echo_yield(1, 2, 3) {|*s|
+ assert_equal([1, 2, 3], s)
+ }
+ @there.echo_yield([], []) {|*s|
+ assert_equal([[], []], s)
+ }
+ @there.echo_yield([]) {|*s|
+ assert_equal([[]], s)
+ }
+ end
+
+ def test_05_array_subclass
+ @there.xarray_each {|x| assert_kind_of(XArray, x)}
+ @there.xarray_each {|*x| assert_kind_of(XArray, x[0])}
+ end
+
+ def test_06_taint
+ x = proc {}
+ assert(! x.tainted?)
+ @there.echo_yield(x) {|o|
+ assert_equal(x, o)
+ assert(! x.tainted?)
+ }
+ end
+end
+
+class TestDRbRubyYield < TestDRbYield
+ def echo_yield(*arg)
+ yield(*arg)
+ end
+
+ def echo_yield_0
+ yield
+ end
+
+ def echo_yield_1(a)
+ yield(a)
+ end
+
+ def echo_yield_2(a, b)
+ yield(a, b)
+ end
+
+ def xarray_each
+ xary = [XArray.new([0])]
+ xary.each do |x|
+ yield(x)
+ end
+ end
+
+ def setup
+ @there = self
+ end
+
+ def teardown
+ end
+end
+
+class TestDRbRuby18Yield < TestDRbRubyYield
+ class YieldTest18
+ def echo_yield(*arg, &proc)
+ proc.call(*arg)
+ end
+
+ def echo_yield_0(&proc)
+ proc.call
+ end
+
+ def echo_yield_1(a, &proc)
+ proc.call(a)
+ end
+
+ def echo_yield_2(a, b, &proc)
+ proc.call(a, b)
+ end
+
+ def xarray_each(&proc)
+ xary = [XArray.new([0])]
+ xary.each(&proc)
+ end
+
+ end
+
+ def setup
+ @there = YieldTest18.new
+ end
+end
+
+class TestDRbAry < Test::Unit::TestCase
+ include DRbAry
+
+ def setup
+ setup_service 'ut_array.rb'
+ super
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+end
+
+class TestDRbMServer < Test::Unit::TestCase
+ include DRbBase
+
+ def setup
+ setup_service 'ut_drb.rb'
+ super
+ @server = (1..3).collect do |n|
+ DRb::DRbServer.new(nil, Onecky.new(n.to_s))
+ end
+ end
+
+ def teardown
+ @server.each do |s|
+ s.stop_service
+ end
+ super
+ DRbService.finish
+ end
+
+ def test_01
+ assert_equal(6, @there.sample(@server[0].front, @server[1].front, @server[2].front))
+ end
+end
+
+class TestDRbSafe1 < TestDRbAry
+ def setup
+ setup_service 'ut_safe1.rb'
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+end
+
+class TestDRbEval # < Test::Unit::TestCase
+ def setup
+ super
+ @ext = DRbService.ext_service('ut_eval.rb')
+ @there = @ext.front
+ end
+
+ def teardown
+ @ext.stop_service if @ext
+ end
+
+ def test_01_safe1_safe4_eval
+ assert_raise(SecurityError) do
+ @there.method_missing(:instance_eval, 'ENV.inspect')
+ end
+
+ assert_raise(SecurityError) do
+ @there.method_missing(:send, :eval, 'ENV.inspect')
+ end
+
+ remote_class = @there.remote_class
+
+ assert_raise(SecurityError) do
+ remote_class.class_eval('ENV.inspect')
+ end
+
+ assert_raise(SecurityError) do
+ remote_class.module_eval('ENV.inspect')
+ end
+
+ four = @there.four
+ assert_equal(1, four.method_missing(:send, :eval, '1'))
+
+ remote_class = four.remote_class
+
+ assert_equal(1, remote_class.class_eval('1'))
+
+ assert_equal(1, remote_class.module_eval('1'))
+
+ assert_raise(SecurityError) do
+ remote_class.class_eval('ENV = {}')
+ end
+
+ assert_raise(SecurityError) do
+ remote_class.module_eval('ENV = {}')
+ end
+ end
+end
+
+class TestDRbLarge < Test::Unit::TestCase
+ include DRbBase
+
+ def setup
+ setup_service 'ut_large.rb'
+ super
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+
+ def test_01_large_ary
+ ary = [2] * 10240
+ assert_equal(10240, @there.size(ary))
+ assert_equal(20480, @there.sum(ary))
+ end
+
+ def test_02_large_ary
+ ary = ["Hello, World"] * 10240
+ assert_equal(10240, @there.size(ary))
+ end
+
+ def test_03_large_ary
+ ary = [Thread.current] * 10240
+ assert_equal(10240, @there.size(ary))
+ end
+
+ def test_04_many_arg
+ assert_raise(DRb::DRbConnError) {
+ @there.arg_test(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
+ }
+ end
+
+ def test_05_too_large_ary
+ ary = ["Hello, World"] * 102400
+ exception = nil
+ begin
+ @there.size(ary)
+ rescue StandardError
+ exception = $!
+ end
+ assert_kind_of(StandardError, exception)
+ end
+end
+
+class TestBug4409 < Test::Unit::TestCase
+ include DRbBase
+
+ def setup
+ setup_service 'ut_eq.rb'
+ super
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+
+ def test_bug4409
+ foo = @there.foo
+ assert(@there.foo?(foo))
+ end
+end
+
+end
diff --git a/jni/ruby/test/drb/test_drbssl.rb b/jni/ruby/test/drb/test_drbssl.rb
new file mode 100644
index 0000000..bcf5f90
--- /dev/null
+++ b/jni/ruby/test/drb/test_drbssl.rb
@@ -0,0 +1,76 @@
+require_relative 'drbtest'
+
+begin
+ require 'drb/ssl'
+rescue LoadError
+end
+
+module DRbTests
+
+if Object.const_defined?("OpenSSL")
+
+
+class DRbSSLService < DRbService
+ %w(ut_drb_drbssl.rb ut_array_drbssl.rb).each do |nm|
+ add_service_command(nm)
+ end
+ config = Hash.new
+
+ config[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
+ config[:SSLVerifyCallback] = lambda{ |ok,x509_store|
+ true
+ }
+ begin
+ data = open("sample.key"){|io| io.read }
+ config[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(data)
+ data = open("sample.crt"){|io| io.read }
+ config[:SSLCertificate] = OpenSSL::X509::Certificate.new(data)
+ rescue
+ # $stderr.puts "Switching to use self-signed certificate"
+ config[:SSLCertName] =
+ [ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
+ end
+
+ uri = ARGV.shift if $0 == __FILE__
+ @server = DRb::DRbServer.new(uri || 'drbssl://:0', self.manager, config)
+end
+
+class TestDRbSSLCore < Test::Unit::TestCase
+ include DRbCore
+ def setup
+ setup_service 'ut_drb_drbssl.rb'
+ super
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+
+ def test_02_unknown
+ end
+
+ def test_01_02_loop
+ end
+
+ def test_05_eq
+ end
+end
+
+class TestDRbSSLAry < Test::Unit::TestCase
+ include DRbAry
+ def setup
+ setup_service 'ut_array_drbssl.rb'
+ super
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+end
+
+
+end
+
+end
diff --git a/jni/ruby/test/drb/test_drbunix.rb b/jni/ruby/test/drb/test_drbunix.rb
new file mode 100644
index 0000000..c422b7a
--- /dev/null
+++ b/jni/ruby/test/drb/test_drbunix.rb
@@ -0,0 +1,59 @@
+require_relative 'drbtest'
+
+begin
+ require 'drb/unix'
+rescue LoadError
+end
+
+module DRbTests
+
+if Object.const_defined?("UNIXServer")
+
+
+class DRbUNIXService < DRbService
+ %w(ut_drb_drbunix.rb ut_array_drbunix.rb).each do |nm|
+ add_service_command(nm)
+ end
+
+ uri = ARGV.shift if $0 == __FILE__
+ @server = DRb::DRbServer.new(uri || 'drbunix:', self.manager, {})
+end
+
+class TestDRbUNIXCore < Test::Unit::TestCase
+ include DRbCore
+ def setup
+ setup_service 'ut_drb_drbunix.rb'
+ super
+ end
+
+ def teardown
+ super
+ DRbService.finish
+ end
+
+ def test_02_unknown
+ end
+
+ def test_01_02_loop
+ end
+
+ def test_05_eq
+ end
+end
+
+class TestDRbUNIXAry < Test::Unit::TestCase
+ include DRbAry
+ def setup
+ setup_service 'ut_array_drbunix.rb'
+ super
+ end
+ def teardown
+ super
+ DRbService.finish
+ end
+end
+
+
+end
+
+end
diff --git a/jni/ruby/test/drb/ut_array.rb b/jni/ruby/test/drb/ut_array.rb
new file mode 100644
index 0000000..01599f1
--- /dev/null
+++ b/jni/ruby/test/drb/ut_array.rb
@@ -0,0 +1,16 @@
+require 'drb/drb'
+require 'drb/extserv'
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <uri> <name>" unless it
+ it
+ end
+
+ DRb.start_service('druby://localhost:0', [1, 2, 'III', 4, "five", 6])
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
+
diff --git a/jni/ruby/test/drb/ut_array_drbssl.rb b/jni/ruby/test/drb/ut_array_drbssl.rb
new file mode 100644
index 0000000..fa9afa6
--- /dev/null
+++ b/jni/ruby/test/drb/ut_array_drbssl.rb
@@ -0,0 +1,40 @@
+require 'drb/drb'
+require 'drb/extserv'
+require 'drb/ssl'
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <uri> <name>" unless it
+ it
+ end
+
+ module DRbTests
+
+ TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
+pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
+AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
+-----END DH PARAMETERS-----
+ _end_of_pem_
+
+ TEST_KEY_DH1024.priv_key = OpenSSL::BN.new("48561834C67E65FFD2A9B47F41E5E78FDC95C387428FDB1E4B0188B64D1643C3A8D3455B945B7E8C4D166010C7C2CE23BFB9BEF43D0348FE7FA5284B0225E7FE1537546D114E3D8A4411B9B9351AB451E1A358F50ED61B1F00DA29336EEBBD649980AC86D76AF8BBB065298C2052672EEF3EF13AB47A15275FC2836F3AC74CEA", 16)
+
+ end
+
+ config = Hash.new
+ config[:SSLTmpDhCallback] = proc { DRbTests::TEST_KEY_DH1024 }
+ config[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
+ config[:SSLVerifyCallback] = lambda{|ok,x509_store|
+ true
+ }
+ config[:SSLCertName] =
+ [ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
+
+ DRb.start_service('drbssl://localhost:0', [1, 2, 'III', 4, "five", 6], config)
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
+
diff --git a/jni/ruby/test/drb/ut_array_drbunix.rb b/jni/ruby/test/drb/ut_array_drbunix.rb
new file mode 100644
index 0000000..dd39790
--- /dev/null
+++ b/jni/ruby/test/drb/ut_array_drbunix.rb
@@ -0,0 +1,16 @@
+require 'drb/drb'
+require 'drb/extserv'
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <uri> <name>" unless it
+ it
+ end
+
+ DRb.start_service('drbunix:', [1, 2, 'III', 4, "five", 6])
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
+
diff --git a/jni/ruby/test/drb/ut_drb.rb b/jni/ruby/test/drb/ut_drb.rb
new file mode 100644
index 0000000..c2a5f62
--- /dev/null
+++ b/jni/ruby/test/drb/ut_drb.rb
@@ -0,0 +1,167 @@
+require 'drb/drb'
+require 'drb/extserv'
+require 'timeout'
+
+module DRbTests
+
+class XArray < Array
+ def initialize(ary)
+ ary.each do |x|
+ self.push(x)
+ end
+ end
+end
+
+class XArray2 < XArray
+ include DRbUndumped
+end
+
+class Unknown2
+ def initialize
+ @foo = 'unknown2'
+ end
+end
+
+class DRbEx
+ include DRbUndumped
+
+ class FooBar
+ def initialize
+ @foo = 'bar'
+ end
+ end
+
+ class UError < RuntimeError; end
+
+ def initialize
+ @xary2_hash = nil
+ @hash = nil
+ @hello = 'hello'
+ end
+ attr_reader :hello
+
+ def sample(a, b, c)
+ a.to_i + b.to_i + c.to_i
+ end
+
+ def sum(*a)
+ s = 0
+ a.each do |e|
+ s += e.to_i
+ end
+ s
+ end
+
+ def do_timeout(n)
+ timeout(0.1) do
+ n.sleep(2)
+ end
+ end
+
+ def unknown_module
+ FooBar.new
+ end
+
+ def unknown_class
+ Unknown2.new
+ end
+
+ def unknown_error
+ raise UError
+ end
+
+ def remote_no_method_error
+ invoke_no_method(self)
+ end
+
+ def test_yield
+ yield
+ yield([])
+ yield(*[])
+ end
+
+ def echo_yield(*arg)
+ yield(*arg)
+ nil
+ end
+
+ def echo_yield_0
+ yield
+ nil
+ end
+
+ def echo_yield_1(one)
+ yield(one)
+ nil
+ end
+
+ def echo_yield_2(one, two)
+ yield(one, two)
+ nil
+ end
+
+ def xarray_each
+ xary = [XArray.new([0])]
+ xary.each do |x|
+ yield(x)
+ end
+ nil
+ end
+
+ def xarray2_hash
+ unless @xary2_hash
+ @xary2_hash = { "a" => XArray2.new([0]), "b" => XArray2.new([1]) }
+ end
+ DRbObject.new(@xary2_hash)
+ end
+
+ def simple_hash
+ unless @hash
+ @hash = { 'a'=>:a, 'b'=>:b }
+ end
+ DRbObject.new(@hash)
+ end
+
+ def [](key)
+ key.to_s
+ end
+
+ def to_a
+ [self]
+ end
+
+ def method_missing(msg, *a, &b)
+ if msg == :missing
+ return true
+ else
+ super(msg, *a, &b)
+ end
+ end
+
+ private
+ def call_private_method
+ true
+ end
+
+ protected
+ def call_protected_method
+ true
+ end
+end
+
+end
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <manager-uri> <name>" unless it
+ it
+ end
+
+ DRb::DRbServer.default_argc_limit(8)
+ DRb::DRbServer.default_load_limit(4096)
+ DRb.start_service('druby://localhost:0', DRbTests::DRbEx.new)
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
diff --git a/jni/ruby/test/drb/ut_drb_drbssl.rb b/jni/ruby/test/drb/ut_drb_drbssl.rb
new file mode 100644
index 0000000..b6d1c12
--- /dev/null
+++ b/jni/ruby/test/drb/ut_drb_drbssl.rb
@@ -0,0 +1,41 @@
+require_relative "ut_drb"
+require 'drb/ssl'
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <manager-uri> <name>" unless it
+ it
+ end
+
+ module DRbTests
+
+ TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
+pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
+AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
+-----END DH PARAMETERS-----
+ _end_of_pem_
+
+ TEST_KEY_DH1024.priv_key = OpenSSL::BN.new("48561834C67E65FFD2A9B47F41E5E78FDC95C387428FDB1E4B0188B64D1643C3A8D3455B945B7E8C4D166010C7C2CE23BFB9BEF43D0348FE7FA5284B0225E7FE1537546D114E3D8A4411B9B9351AB451E1A358F50ED61B1F00DA29336EEBBD649980AC86D76AF8BBB065298C2052672EEF3EF13AB47A15275FC2836F3AC74CEA", 16)
+
+ end
+
+ config = Hash.new
+ config[:SSLTmpDhCallback] = proc { DRbTests::TEST_KEY_DH1024 }
+ config[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER
+ config[:SSLVerifyCallback] = lambda{|ok,x509_store|
+ true
+ }
+ config[:SSLCertName] =
+ [ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
+
+ DRb::DRbServer.default_argc_limit(8)
+ DRb::DRbServer.default_load_limit(4096)
+ DRb.start_service('drbssl://localhost:0', DRbTests::DRbEx.new, config)
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
+
diff --git a/jni/ruby/test/drb/ut_drb_drbunix.rb b/jni/ruby/test/drb/ut_drb_drbunix.rb
new file mode 100644
index 0000000..c997d24
--- /dev/null
+++ b/jni/ruby/test/drb/ut_drb_drbunix.rb
@@ -0,0 +1,17 @@
+require "#{File.dirname(File.expand_path(__FILE__))}/ut_drb"
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <manager-uri> <name>" unless it
+ it
+ end
+
+ DRb::DRbServer.default_argc_limit(8)
+ DRb::DRbServer.default_load_limit(4096)
+ DRb.start_service('drbunix:', DRbTests::DRbEx.new)
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
+
diff --git a/jni/ruby/test/drb/ut_eq.rb b/jni/ruby/test/drb/ut_eq.rb
new file mode 100644
index 0000000..cc86158
--- /dev/null
+++ b/jni/ruby/test/drb/ut_eq.rb
@@ -0,0 +1,36 @@
+require 'drb/drb'
+require 'drb/extserv'
+
+module DRbTests
+
+class Foo
+ include DRbUndumped
+end
+
+class Bar
+ include DRbUndumped
+ def initialize
+ @foo = Foo.new
+ end
+ attr_reader :foo
+
+ def foo?(foo)
+ @foo == foo
+ end
+end
+
+end
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <uri> <name>" unless it
+ it
+ end
+
+ DRb.start_service('druby://localhost:0', DRbTests::Bar.new)
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
+
diff --git a/jni/ruby/test/drb/ut_eval.rb b/jni/ruby/test/drb/ut_eval.rb
new file mode 100644
index 0000000..c456790
--- /dev/null
+++ b/jni/ruby/test/drb/ut_eval.rb
@@ -0,0 +1,36 @@
+require 'drb/drb'
+require 'drb/extserv'
+
+module DRbTests
+
+class EvalAttack
+ def initialize
+ @four = DRb::DRbServer.new('druby://localhost:0', self, {:safe_level => 4})
+ end
+
+ def four
+ DRbObject.new_with_uri(@four.uri)
+ end
+
+ def remote_class
+ DRbObject.new(self.class)
+ end
+end
+
+end
+
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <uri> <name>" unless it
+ it
+ end
+
+ $SAFE = 1
+
+ DRb.start_service('druby://localhost:0', DRbTests::EvalAttack.new, {:safe_level => 2})
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
diff --git a/jni/ruby/test/drb/ut_large.rb b/jni/ruby/test/drb/ut_large.rb
new file mode 100644
index 0000000..c2fbd3e
--- /dev/null
+++ b/jni/ruby/test/drb/ut_large.rb
@@ -0,0 +1,43 @@
+require 'drb/drb'
+require 'drb/extserv'
+require 'timeout'
+
+module DRbTests
+
+class DRbLarge
+ include DRbUndumped
+
+ def size(ary)
+ ary.size
+ end
+
+ def sum(ary)
+ sum = 0
+ ary.each do |e|
+ sum += e.to_i
+ end
+ sum
+ end
+
+ def arg_test(*arg)
+ # nop
+ end
+end
+
+end
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <manager-uri> <name>" unless it
+ it
+ end
+
+ DRb::DRbServer.default_argc_limit(3)
+ DRb::DRbServer.default_load_limit(100000)
+ DRb.start_service('druby://localhost:0', DRbTests::DRbLarge.new)
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
+
diff --git a/jni/ruby/test/drb/ut_port.rb b/jni/ruby/test/drb/ut_port.rb
new file mode 100644
index 0000000..3009db5
--- /dev/null
+++ b/jni/ruby/test/drb/ut_port.rb
@@ -0,0 +1,15 @@
+require 'drb/drb'
+require 'drb/extserv'
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <uri> <name>" unless it
+ it
+ end
+
+ DRb.start_service('druby://:8473', [1, 2, 'III', 4, "five", 6])
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
diff --git a/jni/ruby/test/drb/ut_safe1.rb b/jni/ruby/test/drb/ut_safe1.rb
new file mode 100644
index 0000000..43eb367
--- /dev/null
+++ b/jni/ruby/test/drb/ut_safe1.rb
@@ -0,0 +1,16 @@
+require 'drb/drb'
+require 'drb/extserv'
+
+if __FILE__ == $0
+ def ARGV.shift
+ it = super()
+ raise "usage: #{$0} <uri> <name>" unless it
+ it
+ end
+
+ DRb.start_service('druby://localhost:0', [1, 2, 'III', 4, "five", 6],
+ {:safe_level => 1})
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift)
+ DRb.thread.join
+ es.stop_service if es.alive?
+end
diff --git a/jni/ruby/test/drb/ut_timerholder.rb b/jni/ruby/test/drb/ut_timerholder.rb
new file mode 100644
index 0000000..6c62ea7
--- /dev/null
+++ b/jni/ruby/test/drb/ut_timerholder.rb
@@ -0,0 +1,53 @@
+require 'runit/testcase'
+require 'runit/cui/testrunner'
+require 'timerholder'
+
+module DRbTests
+
+class TimerHolderTest < RUNIT::TestCase
+ def do_test(timeout, keeper_sleep = nil)
+ holder = TimerHolder.new(timeout)
+ holder.keeper_sleep = keeper_sleep if keeper_sleep
+ key = holder.add(self)
+ sleep(timeout * 0.5)
+ assert_equal(holder.peek(key), self)
+ holder.delete(key)
+ assert(!holder.include?(key))
+ key = holder.add(self)
+ sleep(timeout+0.5)
+ assert_equal(holder.fetch(key), nil)
+ key = holder.add(self)
+ assert_equal(holder.fetch(key), self)
+ holder.store(key, true)
+ assert_equal(holder.fetch(key), true)
+ assert_equal(holder.include?(key), true)
+ sleep(timeout+0.5)
+ assert_exception(TimerHolder::InvalidIndexError) do
+ holder.store(key, 1)
+ end
+ assert_equal(holder.include?(key), false)
+ key = holder.add(self)
+ sleep(timeout * 0.5)
+ assert(holder.include?(key))
+ holder.extend(key, timeout)
+ sleep(timeout * 0.5)
+ assert(holder.include?(key))
+ sleep(timeout * 0.6)
+ assert(!holder.include?(key))
+ holder.delete(key)
+ end
+
+ def test_00
+ do_test(0.5)
+ end
+
+ def test_01
+ do_test(1, 0.5)
+ end
+end
+
+end
+
+if __FILE__ == $0
+ RUNIT::CUI::TestRunner.run(DRbTests::TimerHolderTest.suite)
+end
diff --git a/jni/ruby/test/dtrace/dummy.rb b/jni/ruby/test/dtrace/dummy.rb
new file mode 100644
index 0000000..e856142
--- /dev/null
+++ b/jni/ruby/test/dtrace/dummy.rb
@@ -0,0 +1 @@
+# this is a dummy file used by test/dtrace/test_require.rb
diff --git a/jni/ruby/test/dtrace/helper.rb b/jni/ruby/test/dtrace/helper.rb
new file mode 100644
index 0000000..ccc7081
--- /dev/null
+++ b/jni/ruby/test/dtrace/helper.rb
@@ -0,0 +1,50 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+require 'tempfile'
+
+if Process.euid == 0
+ ok = true
+elsif (sudo = ENV["SUDO"]) and !sudo.empty? and (`#{sudo} echo ok` rescue false)
+ ok = true
+else
+ ok = false
+end
+ok &= (`dtrace -V` rescue false)
+module DTrace
+ class TestCase < Test::Unit::TestCase
+ INCLUDE = File.expand_path('..', File.dirname(__FILE__))
+
+ def trap_probe d_program, ruby_program
+ d = Tempfile.new(%w'probe .d')
+ d.write d_program
+ d.flush
+
+ rb = Tempfile.new(%w'probed .rb')
+ rb.write ruby_program
+ rb.flush
+
+ d_path = d.path
+ rb_path = rb.path
+
+ cmd = ["dtrace", "-q", "-s", d_path, "-c", "#{EnvUtil.rubybin} -I#{INCLUDE} #{rb_path}"]
+ if sudo = @@sudo
+ [RbConfig::CONFIG["LIBPATHENV"], "RUBY", "RUBYOPT"].each do |name|
+ if name and val = ENV[name]
+ cmd.unshift("#{name}=#{val}")
+ end
+ end
+ cmd.unshift(sudo)
+ end
+ probes = IO.popen(cmd, err: [:child, :out]) do |io|
+ io.readlines
+ end
+ d.close(true)
+ rb.close(true)
+ yield(d_path, rb_path, probes)
+ end
+ end
+end if ok
+
+if ok
+ DTrace::TestCase.class_variable_set(:@@sudo, sudo)
+end
diff --git a/jni/ruby/test/dtrace/test_array_create.rb b/jni/ruby/test/dtrace/test_array_create.rb
new file mode 100644
index 0000000..d849bcc
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_array_create.rb
@@ -0,0 +1,35 @@
+require_relative 'helper'
+
+module DTrace
+ class TestArrayCreate < TestCase
+ def test_lit
+ trap_probe(probe, '[]') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '0'
+ }
+ assert_equal([rbfile], saw.map { |line| line[1] })
+ assert_equal(['1'], saw.map { |line| line[2] })
+ }
+ end
+
+ def test_many_lit
+ trap_probe(probe, '[1,2,3,4]') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '4' && line == '1'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ private
+ def probe type = 'array'
+ <<-eoprobe
+ruby$target:::#{type}-create
+/arg1/
+{
+ printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_cmethod.rb b/jni/ruby/test/dtrace/test_cmethod.rb
new file mode 100644
index 0000000..0a9107f
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_cmethod.rb
@@ -0,0 +1,49 @@
+require_relative 'helper'
+
+module DTrace
+ class TestCMethod < TestCase
+ def test_entry
+ probe = <<-eoprobe
+ruby$target:::cmethod-entry
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row[1] == 'times'
+ }
+
+ assert_equal 1, foo_calls.length
+ }
+ end
+
+ def test_exit
+ probe = <<-eoprobe
+ruby$target:::cmethod-return
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row[1] == 'times'
+ }
+
+ assert_equal 1, foo_calls.length
+ }
+ end
+
+ def ruby_program
+ <<-eoruby
+ class Foo
+ def self.foo; end
+ end
+ 10.times { Foo.foo }
+ eoruby
+ end
+ end
+end if defined?(DTrace::TestCase)
+
diff --git a/jni/ruby/test/dtrace/test_function_entry.rb b/jni/ruby/test/dtrace/test_function_entry.rb
new file mode 100644
index 0000000..74aee64
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_function_entry.rb
@@ -0,0 +1,87 @@
+require_relative 'helper'
+
+module DTrace
+ class TestFunctionEntry < TestCase
+ def test_function_entry
+ probe = <<-eoprobe
+ruby$target:::method-entry
+/arg0 && arg1 && arg2/
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == 'Foo' && row[1] == 'foo'
+ }
+
+ assert_equal 10, foo_calls.length
+ line = '2'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def test_function_return
+ probe = <<-eoprobe
+ruby$target:::method-return
+/arg0 && arg1 && arg2/
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == 'Foo' && row[1] == 'foo'
+ }
+
+ assert_equal 10, foo_calls.length
+ line = '2'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def test_return_from_raise
+ program = <<-eoruby
+ class Foo
+ def bar; raise; end
+ def baz
+ bar
+ rescue
+ end
+ end
+
+ Foo.new.baz
+ eoruby
+
+ probe = <<-eoprobe
+ruby$target:::method-return
+/arg0 && arg1 && arg2/
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == 'Foo' && row[1] == 'bar'
+ }
+ assert foo_calls.any?
+ }
+ end
+
+ private
+ def ruby_program
+ <<-eoruby
+ class Foo
+ def foo; end
+ end
+ x = Foo.new
+ 10.times { x.foo }
+ eoruby
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_gc.rb b/jni/ruby/test/dtrace/test_gc.rb
new file mode 100644
index 0000000..2f58a11
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_gc.rb
@@ -0,0 +1,26 @@
+require_relative 'helper'
+
+module DTrace
+ class TestGC < TestCase
+ %w{
+ gc-mark-begin
+ gc-mark-end
+ gc-sweep-begin
+ gc-sweep-end
+ }.each do |probe_name|
+ define_method(:"test_#{probe_name.gsub(/-/, '_')}") do
+ probe = "ruby$target:::#{probe_name} { printf(\"#{probe_name}\\n\"); }"
+
+ trap_probe(probe, ruby_program) { |_, _, saw|
+ assert_operator saw.length, :>, 0
+ }
+
+ end
+ end
+
+ private
+ def ruby_program
+ "100000.times { Object.new }"
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_hash_create.rb b/jni/ruby/test/dtrace/test_hash_create.rb
new file mode 100644
index 0000000..2cceded
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_hash_create.rb
@@ -0,0 +1,52 @@
+require_relative 'helper'
+
+module DTrace
+ class TestHashCreate < TestCase
+ def test_hash_new
+ trap_probe(probe, 'Hash.new') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '0'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ def test_hash_lit
+ trap_probe(probe, '{}') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '0'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ def test_hash_lit_elements
+ trap_probe(probe, '{ :foo => :bar }') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '2'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ def test_hash_lit_elements_string
+ trap_probe(probe, '{ :foo => :bar, :bar => "baz" }') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '4'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::hash-create
+/arg1/
+{
+ printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_load.rb b/jni/ruby/test/dtrace/test_load.rb
new file mode 100644
index 0000000..cceb0c2
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_load.rb
@@ -0,0 +1,52 @@
+require_relative 'helper'
+require 'tempfile'
+
+module DTrace
+ class TestLoad < TestCase
+ def setup
+ super
+ @rbfile = Tempfile.new(['omg', 'rb'])
+ @rbfile.write 'x = 10'
+ end
+
+ def teardown
+ super
+ @rbfile.close(true) if @rbfile
+ end
+
+ def test_load_entry
+ probe = <<-eoprobe
+ruby$target:::load-entry
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ trap_probe(probe, program) { |dpath, rbpath, saw|
+ saw = saw.map(&:split).find_all { |loaded, _, _|
+ loaded == @rbfile.path
+ }
+ assert_equal 10, saw.length
+ }
+ end
+
+ def test_load_return
+ probe = <<-eoprobe
+ruby$target:::load-return
+{
+ printf("%s\\n", copyinstr(arg0));
+}
+ eoprobe
+ trap_probe(probe, program) { |dpath, rbpath, saw|
+ saw = saw.map(&:split).find_all { |loaded, _, _|
+ loaded == @rbfile.path
+ }
+ assert_equal 10, saw.length
+ }
+ end
+
+ private
+ def program
+ "10.times { load '#{@rbfile.path}' }"
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_method_cache.rb b/jni/ruby/test/dtrace/test_method_cache.rb
new file mode 100644
index 0000000..d8ddf45
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_method_cache.rb
@@ -0,0 +1,28 @@
+require_relative 'helper'
+
+module DTrace
+ class TestMethodCacheClear < TestCase
+ def test_method_cache_clear
+ trap_probe(probe, <<-code) do |_,rbfile,lines|
+ class String; end
+ class String; def abc() end end
+ class Object; def abc() end end
+ code
+ assert_not_includes lines, "String #{rbfile} 1\n"
+ assert_includes lines, "String #{rbfile} 2\n"
+ assert_includes lines, "global #{rbfile} 3\n"
+ end
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::method-cache-clear
+/arg1/
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_object_create_start.rb b/jni/ruby/test/dtrace/test_object_create_start.rb
new file mode 100644
index 0000000..2be9611
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_object_create_start.rb
@@ -0,0 +1,35 @@
+require_relative 'helper'
+
+module DTrace
+ class TestObjectCreateStart < TestCase
+ def test_object_create_start
+ trap_probe(probe, '10.times { Object.new }') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |_, file, _|
+ file == rbfile
+ }
+ assert_equal 10, saw.length
+ }
+ end
+
+ def test_object_create_start_name
+ trap_probe(probe, 'Hash.new') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |klass, file, line|
+ file == rbfile
+ }
+ assert_equal(%w{ Hash }, saw.map(&:first))
+ assert_equal([rbfile], saw.map { |line| line[1] })
+ assert_equal(['1'], saw.map { |line| line[2] })
+ }
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::object-create
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_raise.rb b/jni/ruby/test/dtrace/test_raise.rb
new file mode 100644
index 0000000..48fdbf1
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_raise.rb
@@ -0,0 +1,29 @@
+require_relative 'helper'
+
+module DTrace
+ class TestRaise < TestCase
+ def test_raise
+ probe = <<-eoprobe
+ruby$target:::raise
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ trap_probe(probe, program) { |dpath, rbpath, saw|
+ saw = saw.map(&:split).find_all { |_, source_file, _|
+ source_file == rbpath
+ }
+ assert_equal 10, saw.length
+ saw.each do |klass, _, source_line|
+ assert_equal 'RuntimeError', klass
+ assert_equal '1', source_line
+ end
+ }
+ end
+
+ private
+ def program
+ '10.times { raise rescue nil }'
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_require.rb b/jni/ruby/test/dtrace/test_require.rb
new file mode 100644
index 0000000..46a1d76
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_require.rb
@@ -0,0 +1,34 @@
+require_relative 'helper'
+
+module DTrace
+ class TestRequire < TestCase
+ def test_require_entry
+ probe = <<-eoprobe
+ruby$target:::require-entry
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ trap_probe(probe, ruby_program) { |d_file, rb_file, saw|
+ required = saw.map { |s| s.split }.find_all do |(required, _)|
+ required == 'dtrace/dummy'
+ end
+ assert_equal 10, required.length
+ }
+ end
+
+ def test_require_return
+ probe = <<-eoprobe
+ruby$target:::require-return
+{
+ printf("%s\\n", copyinstr(arg0));
+}
+ eoprobe
+ end
+
+ private
+ def ruby_program
+ "10.times { require 'dtrace/dummy' }"
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_singleton_function.rb b/jni/ruby/test/dtrace/test_singleton_function.rb
new file mode 100644
index 0000000..9e118f6
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_singleton_function.rb
@@ -0,0 +1,55 @@
+require_relative 'helper'
+
+module DTrace
+ class TestSingletonFunctionEntry < TestCase
+ def test_entry
+ probe = <<-eoprobe
+ruby$target:::method-entry
+/strstr(copyinstr(arg0), "Foo") != NULL/
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == 'Foo' && row[1] == 'foo'
+ }
+
+ assert_equal 10, foo_calls.length
+ line = '2'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def test_exit
+ probe = <<-eoprobe
+ruby$target:::method-return
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == 'Foo' && row[1] == 'foo'
+ }
+
+ assert_equal 10, foo_calls.length
+ line = '2'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def ruby_program
+ <<-eoruby
+ class Foo
+ def self.foo; end
+ end
+ 10.times { Foo.foo }
+ eoruby
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/dtrace/test_string.rb b/jni/ruby/test/dtrace/test_string.rb
new file mode 100644
index 0000000..873d5ac
--- /dev/null
+++ b/jni/ruby/test/dtrace/test_string.rb
@@ -0,0 +1,27 @@
+require_relative 'helper'
+
+module DTrace
+ class TestStringProbes < TestCase
+ def test_object_create_start_string_lit
+ trap_probe(probe, '"omglolwutbbq"') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |klass, file, line, len|
+ file == rbfile && len == '12' && line == '1'
+ }
+ assert_equal(%w{ String }, saw.map(&:first))
+ assert_equal([rbfile], saw.map { |line| line[1] })
+ assert_equal(['1'], saw.map { |line| line[2] })
+ }
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::string-create
+/arg1/
+{
+ printf("String %s %d %d\\n", copyinstr(arg1), arg2, arg0);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/jni/ruby/test/erb/hello.erb b/jni/ruby/test/erb/hello.erb
new file mode 100644
index 0000000..d5ebcb7
--- /dev/null
+++ b/jni/ruby/test/erb/hello.erb
@@ -0,0 +1,4 @@
+= hello
+<% 3.times do |n| %>
+* <%= n %>
+<% end %>
diff --git a/jni/ruby/test/erb/test_erb.rb b/jni/ruby/test/erb/test_erb.rb
new file mode 100644
index 0000000..107ad1a
--- /dev/null
+++ b/jni/ruby/test/erb/test_erb.rb
@@ -0,0 +1,504 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+require 'erb'
+
+class TestERB < Test::Unit::TestCase
+ class MyError < RuntimeError ; end
+
+ def test_without_filename
+ erb = ERB.new("<% raise ::TestERB::MyError %>")
+ e = assert_raise(MyError) {
+ erb.result
+ }
+ assert_match(/\A\(erb\):1\b/, e.backtrace[0])
+ end
+
+ def test_with_filename
+ erb = ERB.new("<% raise ::TestERB::MyError %>")
+ erb.filename = "test filename"
+ e = assert_raise(MyError) {
+ erb.result
+ }
+ assert_match(/\Atest filename:1\b/, e.backtrace[0])
+ end
+
+ def test_without_filename_with_safe_level
+ erb = ERB.new("<% raise ::TestERB::MyError %>", 1)
+ e = assert_raise(MyError) {
+ erb.result
+ }
+ assert_match(/\A\(erb\):1\b/, e.backtrace[0])
+ end
+
+ def test_with_filename_and_safe_level
+ erb = ERB.new("<% raise ::TestERB::MyError %>", 1)
+ erb.filename = "test filename"
+ e = assert_raise(MyError) {
+ erb.result
+ }
+ assert_match(/\Atest filename:1\b/, e.backtrace[0])
+ end
+
+ def test_with_filename_lineno
+ erb = ERB.new("<% raise ::TestERB::MyError %>")
+ erb.filename = "test filename"
+ erb.lineno = 100
+ e = assert_raise(MyError) {
+ erb.result
+ }
+ assert_match(/\Atest filename:101\b/, e.backtrace[0])
+ end
+
+ def test_with_location
+ erb = ERB.new("<% raise ::TestERB::MyError %>")
+ erb.location = ["test filename", 200]
+ e = assert_raise(MyError) {
+ erb.result
+ }
+ assert_match(/\Atest filename:201\b/, e.backtrace[0])
+ end
+
+ def test_html_escape
+ assert_equal(" !&quot;\#$%&amp;&#39;()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
+ ERB::Util.html_escape(" !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"))
+
+ assert_equal("", ERB::Util.html_escape(""))
+ assert_equal("abc", ERB::Util.html_escape("abc"))
+ assert_equal("&lt;&lt;", ERB::Util.html_escape("<\<"))
+
+ assert_equal("", ERB::Util.html_escape(nil))
+ assert_equal("123", ERB::Util.html_escape(123))
+ end
+
+ def test_concurrent_default_binding
+ template1 = 'one <%= ERB.new(template2).result %>'
+
+ eval 'template2 = "two"', TOPLEVEL_BINDING
+
+ bug7046 = '[ruby-core:47638]'
+ assert_equal("one two", ERB.new(template1).result, bug7046)
+ end
+end
+
+class TestERBCore < Test::Unit::TestCase
+ def setup
+ @erb = ERB
+ end
+
+ def test_core
+ _test_core(nil)
+ _test_core(0)
+ _test_core(1)
+ _test_core(2)
+ orig = $VERBOSE
+ begin
+ $VERBOSE = false
+ _test_core(3)
+ ensure
+ $VERBOSE = orig
+ end
+ end
+
+ def _test_core(safe)
+ erb = @erb.new("hello")
+ assert_equal("hello", erb.result)
+
+ erb = @erb.new("hello", safe, 0)
+ assert_equal("hello", erb.result)
+
+ erb = @erb.new("hello", safe, 1)
+ assert_equal("hello", erb.result)
+
+ erb = @erb.new("hello", safe, 2)
+ assert_equal("hello", erb.result)
+
+ src = <<EOS
+%% hi
+= hello
+<% 3.times do |n| %>
+% n=0
+* <%= n %>
+<% end %>
+EOS
+
+ ans = <<EOS
+%% hi
+= hello
+
+% n=0
+* 0
+
+% n=0
+* 1
+
+% n=0
+* 2
+
+EOS
+ erb = @erb.new(src)
+ assert_equal(ans, erb.result)
+ erb = @erb.new(src, safe, 0)
+ assert_equal(ans, erb.result)
+ erb = @erb.new(src, safe, '')
+ assert_equal(ans, erb.result)
+
+ ans = <<EOS
+%% hi
+= hello
+% n=0
+* 0% n=0
+* 1% n=0
+* 2
+EOS
+ erb = @erb.new(src, safe, 1)
+ assert_equal(ans.chomp, erb.result)
+ erb = @erb.new(src, safe, '>')
+ assert_equal(ans.chomp, erb.result)
+
+ ans = <<EOS
+%% hi
+= hello
+% n=0
+* 0
+% n=0
+* 1
+% n=0
+* 2
+EOS
+
+ erb = @erb.new(src, safe, 2)
+ assert_equal(ans, erb.result)
+ erb = @erb.new(src, safe, '<>')
+ assert_equal(ans, erb.result)
+
+ ans = <<EOS
+% hi
+= hello
+
+* 0
+
+* 0
+
+* 0
+
+EOS
+ erb = @erb.new(src, safe, '%')
+ assert_equal(ans, erb.result)
+
+ ans = <<EOS
+% hi
+= hello
+* 0* 0* 0
+EOS
+ erb = @erb.new(src, safe, '%>')
+ assert_equal(ans.chomp, erb.result)
+
+ ans = <<EOS
+% hi
+= hello
+* 0
+* 0
+* 0
+EOS
+ erb = @erb.new(src, safe, '%<>')
+ assert_equal(ans, erb.result)
+ end
+
+ class Foo; end
+
+ def test_def_class
+ erb = @erb.new('hello')
+ cls = erb.def_class
+ assert_equal(Object, cls.superclass)
+ assert(cls.new.respond_to?('result'))
+ cls = erb.def_class(Foo)
+ assert_equal(Foo, cls.superclass)
+ assert(cls.new.respond_to?('result'))
+ cls = erb.def_class(Object, 'erb')
+ assert_equal(Object, cls.superclass)
+ assert(cls.new.respond_to?('erb'))
+ end
+
+ def test_percent
+ src = <<EOS
+%n = 1
+<%= n%>
+EOS
+ assert_equal("1\n", ERB.new(src, nil, '%').result(binding))
+
+ src = <<EOS
+<%
+%>
+EOS
+ ans = "\n"
+ assert_equal(ans, ERB.new(src, nil, '%').result(binding))
+
+ src = "<%\n%>"
+ # ans = "\n"
+ ans = ""
+ assert_equal(ans, ERB.new(src, nil, '%').result(binding))
+
+ src = <<EOS
+<%
+n = 1
+%><%= n%>
+EOS
+ assert_equal("1\n", ERB.new(src, nil, '%').result(binding))
+
+ src = <<EOS
+%n = 1
+%% <% n = 2
+n.times do |i|%>
+%% %%><%%<%= i%><%
+end%>
+%%%
+EOS
+ ans = <<EOS
+%\s
+% %%><%0
+% %%><%1
+%%
+EOS
+ assert_equal(ans, ERB.new(src, nil, '%').result(binding))
+ end
+
+ def test_def_erb_method
+ klass = Class.new
+ klass.module_eval do
+ extend ERB::DefMethod
+ fname = File.join(File.dirname(File.expand_path(__FILE__)), 'hello.erb')
+ def_erb_method('hello', fname)
+ end
+ assert(klass.new.respond_to?('hello'))
+
+ assert(! klass.new.respond_to?('hello_world'))
+ erb = @erb.new('hello, world')
+ klass.module_eval do
+ def_erb_method('hello_world', erb)
+ end
+ assert(klass.new.respond_to?('hello_world'))
+ end
+
+ def test_def_method_without_filename
+ klass = Class.new
+ erb = ERB.new("<% raise ::TestERB::MyError %>")
+ erb.filename = "test filename"
+ assert(! klass.new.respond_to?('my_error'))
+ erb.def_method(klass, 'my_error')
+ e = assert_raise(::TestERB::MyError) {
+ klass.new.my_error
+ }
+ assert_match(/\A\(ERB\):1\b/, e.backtrace[0])
+ end
+
+ def test_def_method_with_fname
+ klass = Class.new
+ erb = ERB.new("<% raise ::TestERB::MyError %>")
+ erb.filename = "test filename"
+ assert(! klass.new.respond_to?('my_error'))
+ erb.def_method(klass, 'my_error', 'test fname')
+ e = assert_raise(::TestERB::MyError) {
+ klass.new.my_error
+ }
+ assert_match(/\Atest fname:1\b/, e.backtrace[0])
+ end
+
+ def test_escape
+ src = <<EOS
+1.<%% : <%="<%%"%>
+2.%%> : <%="%%>"%>
+3.
+% x = "foo"
+<%=x%>
+4.
+%% print "foo"
+5.
+%% <%="foo"%>
+6.<%="
+% print 'foo'
+"%>
+7.<%="
+%% print 'foo'
+"%>
+EOS
+ ans = <<EOS
+1.<% : <%%
+2.%%> : %>
+3.
+foo
+4.
+% print "foo"
+5.
+% foo
+6.
+% print 'foo'
+
+7.
+%% print 'foo'
+
+EOS
+ assert_equal(ans, ERB.new(src, nil, '%').result)
+ end
+
+ def test_keep_lineno
+ src = <<EOS
+Hello,\s
+% x = "World"
+<%= x%>
+% raise("lineno")
+EOS
+
+ erb = ERB.new(src, nil, '%')
+ begin
+ erb.result
+ assert(false)
+ rescue
+ assert_match(/\A\(erb\):4\b/, $@[0].to_s)
+ end
+
+ src = <<EOS
+%>
+Hello,\s
+<% x = "World%%>
+"%>
+<%= x%>
+EOS
+
+ ans = <<EOS
+%>Hello,\s
+World%>
+EOS
+ assert_equal(ans, ERB.new(src, nil, '>').result)
+
+ ans = <<EOS
+%>
+Hello,\s
+
+World%>
+EOS
+ assert_equal(ans, ERB.new(src, nil, '<>').result)
+
+ ans = <<EOS
+%>
+Hello,\s
+
+World%>
+
+EOS
+ assert_equal(ans, ERB.new(src).result)
+
+ src = <<EOS
+Hello,\s
+<% x = "World%%>
+"%>
+<%= x%>
+<% raise("lineno") %>
+EOS
+
+ erb = ERB.new(src)
+ begin
+ erb.result
+ assert(false)
+ rescue
+ assert_match(/\A\(erb\):5\b/, $@[0].to_s)
+ end
+
+ erb = ERB.new(src, nil, '>')
+ begin
+ erb.result
+ assert(false)
+ rescue
+ assert_match(/\A\(erb\):5\b/, $@[0].to_s)
+ end
+
+ erb = ERB.new(src, nil, '<>')
+ begin
+ erb.result
+ assert(false)
+ rescue
+ assert_match(/\A\(erb\):5\b/, $@[0].to_s)
+ end
+
+ src = <<EOS
+% y = 'Hello'
+<%- x = "World%%>
+"-%>
+<%= x %><%- x = nil -%>\s
+<% raise("lineno") %>
+EOS
+
+ erb = ERB.new(src, nil, '-')
+ begin
+ erb.result
+ assert(false)
+ rescue
+ assert_match(/\A\(erb\):5\b/, $@[0].to_s)
+ end
+
+ erb = ERB.new(src, nil, '%-')
+ begin
+ erb.result
+ assert(false)
+ rescue
+ assert_match(/\A\(erb\):5\b/, $@[0].to_s)
+ end
+ end
+
+ def test_explicit
+ src = <<EOS
+<% x = %w(hello world) -%>
+NotSkip <%- y = x -%> NotSkip
+<% x.each do |w| -%>
+ <%- up = w.upcase -%>
+ * <%= up %>
+<% end -%>
+ <%- z = nil -%> NotSkip <%- z = x %>
+ <%- z.each do |w| -%>
+ <%- down = w.downcase -%>
+ * <%= down %>
+ <%- up = w.upcase -%>
+ * <%= up %>
+ <%- end -%>
+KeepNewLine <%- z = nil -%>\s
+EOS
+
+ ans = <<EOS
+NotSkip NotSkip
+ * HELLO
+ * WORLD
+ NotSkip\s
+ * hello
+ * HELLO
+ * world
+ * WORLD
+KeepNewLine \s
+EOS
+ assert_equal(ans, ERB.new(src, nil, '-').result)
+ assert_equal(ans, ERB.new(src, nil, '-%').result)
+ end
+
+ def test_url_encode
+ assert_equal("Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide",
+ ERB::Util.url_encode("Programming Ruby: The Pragmatic Programmer's Guide"))
+
+ assert_equal("%A5%B5%A5%F3%A5%D7%A5%EB",
+ ERB::Util.url_encode("\xA5\xB5\xA5\xF3\xA5\xD7\xA5\xEB".force_encoding("EUC-JP")))
+ end
+
+ def test_percent_after_etag
+ assert_equal("1%", @erb.new("<%= 1 %>%", nil, "%").result)
+ end
+end
+
+class TestERBCoreWOStrScan < TestERBCore
+ def setup
+ @save_map = ERB::Compiler::Scanner.instance_variable_get('@scanner_map')
+ map = {[nil, false]=>ERB::Compiler::SimpleScanner}
+ ERB::Compiler::Scanner.instance_variable_set('@scanner_map', map)
+ super
+ end
+
+ def teardown
+ ERB::Compiler::Scanner.instance_variable_set('@scanner_map', @save_map)
+ end
+end
diff --git a/jni/ruby/test/erb/test_erb_command.rb b/jni/ruby/test/erb/test_erb_command.rb
new file mode 100644
index 0000000..abc8fdb
--- /dev/null
+++ b/jni/ruby/test/erb/test_erb_command.rb
@@ -0,0 +1,11 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+
+class TestErbCommand < Test::Unit::TestCase
+ def test_var
+ assert_in_out_err(["-w",
+ File.expand_path("../../../bin/erb", __FILE__),
+ "var=hoge"],
+ "<%=var%>", ["hoge"])
+ end
+end
diff --git a/jni/ruby/test/erb/test_erb_m17n.rb b/jni/ruby/test/erb/test_erb_m17n.rb
new file mode 100644
index 0000000..f60dd10
--- /dev/null
+++ b/jni/ruby/test/erb/test_erb_m17n.rb
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+require 'test/unit'
+require 'erb'
+
+class TestERB < Test::Unit::TestCase
+ def test_result_encoding
+ erb = ERB.new("hello")
+ assert_equal __ENCODING__, erb.result.encoding
+
+ erb = ERB.new("こんにちは".encode("EUC-JP"))
+ assert_equal Encoding::EUC_JP, erb.result.encoding
+
+ erb = ERB.new("\xC4\xE3\xBA\xC3".force_encoding("EUC-CN"))
+ assert_equal Encoding::EUC_CN, erb.result.encoding
+
+ erb = ERB.new("γεια σας".encode("ISO-8859-7"))
+ assert_equal Encoding::ISO_8859_7, erb.result.encoding
+
+ assert_raise(ArgumentError, /ASCII compatible/) {
+ ERB.new("こんにちは".force_encoding("ISO-2022-JP")) # dummy encoding
+ }
+ end
+
+ def test_generate_magic_comment
+ erb = ERB.new("hello")
+ assert_match(/#coding:UTF-8/, erb.src)
+
+ erb = ERB.new("hello".force_encoding("EUC-JP"))
+ assert_match(/#coding:EUC-JP/, erb.src)
+
+ erb = ERB.new("hello".force_encoding("ISO-8859-9"))
+ assert_match(/#coding:ISO-8859-9/, erb.src)
+ end
+
+ def test_literal_encoding
+ erb = ERB.new("literal encoding is <%= 'hello'.encoding %>")
+ assert_match(/literal encoding is UTF-8/, erb.result)
+
+ erb = ERB.new("literal encoding is <%= 'こんにちは'.encoding %>".encode("EUC-JP"))
+ assert_match(/literal encoding is EUC-JP/, erb.result)
+
+ erb = ERB.new("literal encoding is <%= '\xC4\xE3\xBA\xC3'.encoding %>".force_encoding("EUC-CN"))
+ assert_match(/literal encoding is GB2312/, erb.result)
+ end
+
+ def test___ENCODING__
+ erb = ERB.new("__ENCODING__ is <%= __ENCODING__ %>")
+ assert_match(/__ENCODING__ is UTF-8/, erb.result)
+
+ erb = ERB.new("__ENCODING__ is <%= __ENCODING__ %>".force_encoding("EUC-JP"))
+ assert_match(/__ENCODING__ is EUC-JP/, erb.result)
+
+ erb = ERB.new("__ENCODING__ is <%= __ENCODING__ %>".force_encoding("Big5"))
+ assert_match(/__ENCODING__ is Big5/, erb.result)
+ end
+
+ def test_recognize_magic_comment
+ erb = ERB.new(<<-EOS.encode("EUC-KR"))
+<%# -*- coding: EUC-KR -*- %>
+안녕하세요
+ EOS
+ assert_match(/#coding:EUC-KR/, erb.src)
+ assert_equal Encoding::EUC_KR, erb.result.encoding
+
+ erb = ERB.new(<<-EOS.encode("EUC-KR").force_encoding("ASCII-8BIT"))
+<%#-*- coding: EUC-KR -*-%>
+안녕하세요
+ EOS
+ assert_match(/#coding:EUC-KR/, erb.src)
+ assert_equal Encoding::EUC_KR, erb.result.encoding
+
+ erb = ERB.new(<<-EOS.encode("EUC-KR").force_encoding("ASCII-8BIT"))
+<%# vim: tabsize=8 encoding=EUC-KR shiftwidth=2 expandtab %>
+안녕하세요
+ EOS
+ assert_match(/#coding:EUC-KR/, erb.src)
+ assert_equal Encoding::EUC_KR, erb.result.encoding
+
+ erb = ERB.new(<<-EOS.encode("EUC-KR").force_encoding("ASCII-8BIT"))
+<%#coding:EUC-KR %>
+안녕하세요
+ EOS
+ assert_match(/#coding:EUC-KR/, erb.src)
+ assert_equal Encoding::EUC_KR, erb.result.encoding
+
+ erb = ERB.new(<<-EOS.encode("EUC-KR").force_encoding("EUC-JP"))
+<%#coding:EUC-KR %>
+안녕하세요
+ EOS
+ assert_match(/#coding:EUC-KR/, erb.src)
+ assert_equal Encoding::EUC_KR, erb.result.encoding
+ end
+
+ module M; end
+ def test_method_with_encoding
+ obj = Object.new
+ obj.extend(M)
+
+ erb = ERB.new(<<-EOS.encode("EUC-JP").force_encoding("ASCII-8BIT"))
+<%#coding:EUC-JP %>
+literal encoding is <%= 'こんにちは'.encoding %>
+__ENCODING__ is <%= __ENCODING__ %>
+ EOS
+ erb.def_method(M, :m_from_magic_comment)
+
+ result = obj.m_from_magic_comment
+ assert_equal Encoding::EUC_JP, result.encoding
+ assert_match(/literal encoding is EUC-JP/, result)
+ assert_match(/__ENCODING__ is EUC-JP/, result)
+
+ erb = ERB.new(<<-EOS.encode("EUC-KR"))
+literal encoding is <%= '안녕하세요'.encoding %>
+__ENCODING__ is <%= __ENCODING__ %>
+EOS
+ erb.def_method(M, :m_from_eval_encoding)
+ result = obj.m_from_eval_encoding
+ assert_equal Encoding::EUC_KR, result.encoding
+ assert_match(/literal encoding is EUC-KR/, result)
+ assert_match(/__ENCODING__ is EUC-KR/, result)
+ end
+end
+
+# vim:fileencoding=UTF-8
diff --git a/jni/ruby/test/etc/test_etc.rb b/jni/ruby/test/etc/test_etc.rb
new file mode 100644
index 0000000..374b379
--- /dev/null
+++ b/jni/ruby/test/etc/test_etc.rb
@@ -0,0 +1,169 @@
+require "test/unit"
+require "etc"
+
+class TestEtc < Test::Unit::TestCase
+ def test_getlogin
+ s = Etc.getlogin
+ return if s == nil
+ assert(s.is_a?(String), "getlogin must return a String or nil")
+ assert_predicate(s, :valid_encoding?, "login name should be a valid string")
+ end
+
+ def test_passwd
+ Etc.passwd do |s|
+ assert_instance_of(String, s.name)
+ assert_instance_of(String, s.passwd) if s.respond_to?(:passwd)
+ assert_kind_of(Integer, s.uid)
+ assert_kind_of(Integer, s.gid)
+ assert_instance_of(String, s.gecos) if s.respond_to?(:gecos)
+ assert_instance_of(String, s.dir)
+ assert_instance_of(String, s.shell)
+ assert_kind_of(Integer, s.change) if s.respond_to?(:change)
+ assert_kind_of(Integer, s.quota) if s.respond_to?(:quota)
+ assert(s.age.is_a?(Integer) || s.age.is_a?(String)) if s.respond_to?(:age)
+ assert_instance_of(String, s.uclass) if s.respond_to?(:uclass)
+ assert_instance_of(String, s.comment) if s.respond_to?(:comment)
+ assert_kind_of(Integer, s.expire) if s.respond_to?(:expire)
+ end
+
+ Etc.passwd { assert_raise(RuntimeError) { Etc.passwd { } }; break }
+ end
+
+ def test_getpwuid
+ # password database is not unique on UID, and which entry will be
+ # returned by getpwuid() is not specified.
+ passwd = Hash.new {[]}
+ # on MacOSX, same entries are returned from /etc/passwd and Open
+ # Directory.
+ Etc.passwd {|s| passwd[s.uid] |= [s]}
+ passwd.each_pair do |uid, s|
+ assert_include(s, Etc.getpwuid(uid))
+ end
+ s = passwd[Process.euid]
+ unless s.empty?
+ assert_include(s, Etc.getpwuid)
+ end
+ end
+
+ def test_getpwnam
+ passwd = {}
+ Etc.passwd do |s|
+ passwd[s.name] ||= s unless /\A\+/ =~ s.name
+ end
+ passwd.each_value do |s|
+ assert_equal(s, Etc.getpwnam(s.name))
+ end
+ end
+
+ def test_passwd_with_low_level_api
+ a = []
+ Etc.passwd {|s| a << s }
+ b = []
+ Etc.setpwent
+ while s = Etc.getpwent
+ b << s
+ end
+ Etc.endpwent
+ assert_equal(a, b)
+ end
+
+ def test_group
+ Etc.group do |s|
+ assert_instance_of(String, s.name)
+ assert_instance_of(String, s.passwd) if s.respond_to?(:passwd)
+ assert_kind_of(Integer, s.gid)
+ end
+
+ Etc.group { assert_raise(RuntimeError) { Etc.group { } }; break }
+ end
+
+ def test_getgrgid
+ # group database is not unique on GID, and which entry will be
+ # returned by getgrgid() is not specified.
+ groups = Hash.new {[]}
+ # on MacOSX, same entries are returned from /etc/group and Open
+ # Directory.
+ Etc.group {|s| groups[s.gid] |= [s]}
+ groups.each_pair do |gid, s|
+ assert_include(s, Etc.getgrgid(gid))
+ end
+ s = groups[Process.egid]
+ unless s.empty?
+ assert_include(s, Etc.getgrgid)
+ end
+ end
+
+ def test_getgrnam
+ groups = {}
+ Etc.group do |s|
+ groups[s.name] ||= s unless /\A\+/ =~ s.name
+ end
+ groups.each_value do |s|
+ assert_equal(s, Etc.getgrnam(s.name))
+ end
+ end
+
+ def test_group_with_low_level_api
+ a = []
+ Etc.group {|s| a << s }
+ b = []
+ Etc.setgrent
+ while s = Etc.getgrent
+ b << s
+ end
+ Etc.endgrent
+ assert_equal(a, b)
+ end
+
+ def test_uname
+ begin
+ uname = Etc.uname
+ rescue NotImplementedError
+ return
+ end
+ assert_kind_of(Hash, uname)
+ [:sysname, :nodename, :release, :version, :machine].each {|sym|
+ assert_operator(uname, :has_key?, sym)
+ assert_kind_of(String, uname[sym])
+ }
+ end
+
+ def test_sysconf
+ begin
+ Etc.sysconf
+ rescue NotImplementedError
+ return
+ rescue ArgumentError
+ end
+ assert_kind_of(Integer, Etc.sysconf(Etc::SC_CLK_TCK))
+ end if defined?(Etc::SC_CLK_TCK)
+
+ def test_confstr
+ begin
+ Etc.confstr
+ rescue NotImplementedError
+ return
+ rescue ArgumentError
+ end
+ assert_kind_of(String, Etc.confstr(Etc::CS_PATH))
+ end if defined?(Etc::CS_PATH)
+
+ def test_pathconf
+ begin
+ Etc.confstr
+ rescue NotImplementedError
+ return
+ rescue ArgumentError
+ end
+ IO.pipe {|r, w|
+ val = w.pathconf(Etc::PC_PIPE_BUF)
+ assert(val.nil? || val.kind_of?(Integer))
+ }
+ end if defined?(Etc::PC_PIPE_BUF)
+
+ def test_nprocessors
+ n = Etc.nprocessors
+ assert_operator(1, :<=, n)
+ end
+
+end
diff --git a/jni/ruby/test/fiddle/helper.rb b/jni/ruby/test/fiddle/helper.rb
new file mode 100644
index 0000000..bc98f85
--- /dev/null
+++ b/jni/ruby/test/fiddle/helper.rb
@@ -0,0 +1,124 @@
+require 'minitest/autorun'
+require 'fiddle'
+
+# FIXME: this is stolen from DL and needs to be refactored.
+
+libc_so = libm_so = nil
+
+case RUBY_PLATFORM
+when /cygwin/
+ libc_so = "cygwin1.dll"
+ libm_so = "cygwin1.dll"
+when /x86_64-linux/
+ libc_so = "/lib64/libc.so.6"
+ libm_so = "/lib64/libm.so.6"
+when /linux/
+ libdir = '/lib'
+ case [0].pack('L!').size
+ when 4
+ # 32-bit ruby
+ libdir = '/lib32' if File.directory? '/lib32'
+ when 8
+ # 64-bit ruby
+ libdir = '/lib64' if File.directory? '/lib64'
+ end
+ libc_so = File.join(libdir, "libc.so.6")
+ libm_so = File.join(libdir, "libm.so.6")
+when /mingw/, /mswin/
+ require "rbconfig"
+ libc_so = libm_so = RbConfig::CONFIG["RUBY_SO_NAME"].split(/-/).find{|e| /^msvc/ =~ e} + ".dll"
+when /darwin/
+ libc_so = "/usr/lib/libc.dylib"
+ libm_so = "/usr/lib/libm.dylib"
+when /kfreebsd/
+ libc_so = "/lib/libc.so.0.1"
+ libm_so = "/lib/libm.so.1"
+when /gnu/ #GNU/Hurd
+ libc_so = "/lib/libc.so.0.3"
+ libm_so = "/lib/libm.so.6"
+when /mirbsd/
+ libc_so = "/usr/lib/libc.so.41.10"
+ libm_so = "/usr/lib/libm.so.7.0"
+when /freebsd/
+ libc_so = "/lib/libc.so.7"
+ libm_so = "/lib/libm.so.5"
+when /bsd|dragonfly/
+ libc_so = "/usr/lib/libc.so"
+ libm_so = "/usr/lib/libm.so"
+when /solaris/
+ libdir = '/lib'
+ case [0].pack('L!').size
+ when 4
+ # 32-bit ruby
+ libdir = '/lib' if File.directory? '/lib'
+ when 8
+ # 64-bit ruby
+ libdir = '/lib/64' if File.directory? '/lib/64'
+ end
+ libc_so = File.join(libdir, "libc.so")
+ libm_so = File.join(libdir, "libm.so")
+when /aix/
+ pwd=Dir.pwd
+ libc_so = libm_so = "#{pwd}/libaixdltest.so"
+ unless File.exist? libc_so
+ cobjs=%w!strcpy.o!
+ mobjs=%w!floats.o sin.o!
+ funcs=%w!sin sinf strcpy strncpy!
+ expfile='dltest.exp'
+ require 'tmpdir'
+ Dir.mktmpdir do |dir|
+ begin
+ Dir.chdir dir
+ %x!/usr/bin/ar x /usr/lib/libc.a #{cobjs.join(' ')}!
+ %x!/usr/bin/ar x /usr/lib/libm.a #{mobjs.join(' ')}!
+ %x!echo "#{funcs.join("\n")}\n" > #{expfile}!
+ require 'rbconfig'
+ if RbConfig::CONFIG["GCC"] = 'yes'
+ lflag='-Wl,'
+ else
+ lflag=''
+ end
+ flags="#{lflag}-bE:#{expfile} #{lflag}-bnoentry -lm"
+ %x!#{RbConfig::CONFIG["LDSHARED"]} -o #{libc_so} #{(cobjs+mobjs).join(' ')} #{flags}!
+ ensure
+ Dir.chdir pwd
+ end
+ end
+ end
+else
+ libc_so = ARGV[0] if ARGV[0] && ARGV[0][0] == ?/
+ libm_so = ARGV[1] if ARGV[1] && ARGV[1][0] == ?/
+ if( !(libc_so && libm_so) )
+ $stderr.puts("libc and libm not found: #{$0} <libc> <libm>")
+ end
+end
+
+libc_so = nil if !libc_so || (libc_so[0] == ?/ && !File.file?(libc_so))
+libm_so = nil if !libm_so || (libm_so[0] == ?/ && !File.file?(libm_so))
+
+if !libc_so || !libm_so
+ ruby = EnvUtil.rubybin
+ ldd = `ldd #{ruby}`
+ #puts ldd
+ libc_so = $& if !libc_so && %r{/\S*/libc\.so\S*} =~ ldd
+ libm_so = $& if !libm_so && %r{/\S*/libm\.so\S*} =~ ldd
+ #p [libc_so, libm_so]
+end
+
+Fiddle::LIBC_SO = libc_so
+Fiddle::LIBM_SO = libm_so
+
+module Fiddle
+ class TestCase < MiniTest::Unit::TestCase
+ def setup
+ @libc = Fiddle.dlopen(LIBC_SO)
+ @libm = Fiddle.dlopen(LIBM_SO)
+ end
+
+ def teardown
+ if /linux/ =~ RUBY_PLATFORM
+ GC.start
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/fiddle/test_c_struct_entry.rb b/jni/ruby/test/fiddle/test_c_struct_entry.rb
new file mode 100644
index 0000000..de5449b
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_c_struct_entry.rb
@@ -0,0 +1,76 @@
+begin
+ require_relative 'helper'
+ require 'fiddle/struct'
+rescue LoadError
+end
+
+module Fiddle
+ class TestCStructEntity < TestCase
+ def test_class_size
+ types = [TYPE_DOUBLE, TYPE_CHAR]
+
+ size = CStructEntity.size types
+
+ alignments = types.map { |type| PackInfo::ALIGN_MAP[type] }
+
+ expected = PackInfo.align 0, alignments[0]
+ expected += PackInfo::SIZE_MAP[TYPE_DOUBLE]
+
+ expected = PackInfo.align expected, alignments[1]
+ expected += PackInfo::SIZE_MAP[TYPE_CHAR]
+
+ expected = PackInfo.align expected, alignments.max
+
+ assert_equal expected, size
+ end
+
+ def test_class_size_with_count
+ size = CStructEntity.size([[TYPE_DOUBLE, 2], [TYPE_CHAR, 20]])
+
+ types = [TYPE_DOUBLE, TYPE_CHAR]
+ alignments = types.map { |type| PackInfo::ALIGN_MAP[type] }
+
+ expected = PackInfo.align 0, alignments[0]
+ expected += PackInfo::SIZE_MAP[TYPE_DOUBLE] * 2
+
+ expected = PackInfo.align expected, alignments[1]
+ expected += PackInfo::SIZE_MAP[TYPE_CHAR] * 20
+
+ expected = PackInfo.align expected, alignments.max
+
+ assert_equal expected, size
+ end
+
+ def test_set_ctypes
+ union = CStructEntity.malloc [TYPE_INT, TYPE_LONG]
+ union.assign_names %w[int long]
+
+ # this test is roundabout because the stored ctypes are not accessible
+ union['long'] = 1
+ union['int'] = 2
+
+ assert_equal 1, union['long']
+ assert_equal 2, union['int']
+ end
+
+ def test_aref_pointer_array
+ team = CStructEntity.malloc([[TYPE_VOIDP, 2]])
+ team.assign_names(["names"])
+ alice = Fiddle::Pointer.malloc(6)
+ alice[0, 6] = "Alice\0"
+ bob = Fiddle::Pointer.malloc(4)
+ bob[0, 4] = "Bob\0"
+ team["names"] = [alice, bob]
+ assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
+ end
+
+ def test_aref_pointer
+ user = CStructEntity.malloc([TYPE_VOIDP])
+ user.assign_names(["name"])
+ alice = Fiddle::Pointer.malloc(6)
+ alice[0, 6] = "Alice\0"
+ user["name"] = alice
+ assert_equal("Alice", user["name"].to_s)
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_c_union_entity.rb b/jni/ruby/test/fiddle/test_c_union_entity.rb
new file mode 100644
index 0000000..165c4ec
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_c_union_entity.rb
@@ -0,0 +1,34 @@
+begin
+ require_relative 'helper'
+ require 'fiddle/struct'
+rescue LoadError
+end
+
+
+module Fiddle
+ class TestCUnionEntity < TestCase
+ def test_class_size
+ size = CUnionEntity.size([TYPE_DOUBLE, TYPE_CHAR])
+
+ assert_equal SIZEOF_DOUBLE, size
+ end
+
+ def test_class_size_with_count
+ size = CUnionEntity.size([[TYPE_DOUBLE, 2], [TYPE_CHAR, 20]])
+
+ assert_equal SIZEOF_CHAR * 20, size
+ end
+
+ def test_set_ctypes
+ union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG]
+ union.assign_names %w[int long]
+
+ # this test is roundabout because the stored ctypes are not accessible
+ union['long'] = 1
+ assert_equal 1, union['long']
+
+ union['int'] = 1
+ assert_equal 1, union['int']
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_closure.rb b/jni/ruby/test/fiddle/test_closure.rb
new file mode 100644
index 0000000..56839e7
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_closure.rb
@@ -0,0 +1,84 @@
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestClosure < Fiddle::TestCase
+ def test_argument_errors
+ assert_raises(TypeError) do
+ Closure.new(TYPE_INT, TYPE_INT)
+ end
+
+ assert_raises(TypeError) do
+ Closure.new('foo', [TYPE_INT])
+ end
+
+ assert_raises(TypeError) do
+ Closure.new(TYPE_INT, ['meow!'])
+ end
+ end
+
+ def test_call
+ closure = Class.new(Closure) {
+ def call
+ 10
+ end
+ }.new(TYPE_INT, [])
+
+ func = Function.new(closure, [], TYPE_INT)
+ assert_equal 10, func.call
+ end
+
+ def test_returner
+ closure = Class.new(Closure) {
+ def call thing
+ thing
+ end
+ }.new(TYPE_INT, [TYPE_INT])
+
+ func = Function.new(closure, [TYPE_INT], TYPE_INT)
+ assert_equal 10, func.call(10)
+ end
+
+ def test_block_caller
+ cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
+ one
+ end
+ func = Function.new(cb, [TYPE_INT], TYPE_INT)
+ assert_equal 11, func.call(11)
+ end
+
+ def test_memsize
+ require 'objspace'
+ bug = '[ruby-dev:42480]'
+ n = 10000
+ assert_equal(n, n.times {ObjectSpace.memsize_of(Closure.allocate)}, bug)
+ end
+
+ %w[INT SHORT CHAR LONG LONG_LONG].each do |name|
+ type = Fiddle.const_get("TYPE_#{name}") rescue next
+ size = Fiddle.const_get("SIZEOF_#{name}")
+ [[type, size-1, name], [-type, size, "unsigned_"+name]].each do |t, s, n|
+ define_method("test_conversion_#{n.downcase}") do
+ arg = nil
+
+ clos = Class.new(Closure) do
+ define_method(:call) {|x| arg = x}
+ end.new(t, [t])
+
+ v = ~(~0 << (8*s))
+
+ arg = nil
+ assert_equal(v, clos.call(v))
+ assert_equal(arg, v, n)
+
+ arg = nil
+ func = Function.new(clos, [t], t)
+ assert_equal(v, func.call(v))
+ assert_equal(arg, v, n)
+ end
+ end
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_cparser.rb b/jni/ruby/test/fiddle/test_cparser.rb
new file mode 100644
index 0000000..666d8c8
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_cparser.rb
@@ -0,0 +1,35 @@
+begin
+ require_relative 'helper'
+ require 'fiddle/cparser'
+rescue LoadError
+end
+
+module Fiddle
+ class TestCParser < TestCase
+ include CParser
+
+ def test_uint_ctype
+ assert_equal(-TYPE_INT, parse_ctype('uint'))
+ end
+
+ def test_size_t_ctype
+ assert_equal(TYPE_SIZE_T, parse_ctype("size_t"))
+ end
+
+ def test_ssize_t_ctype
+ assert_equal(TYPE_SSIZE_T, parse_ctype("ssize_t"))
+ end
+
+ def test_ptrdiff_t_ctype
+ assert_equal(TYPE_PTRDIFF_T, parse_ctype("ptrdiff_t"))
+ end
+
+ def test_intptr_t_ctype
+ assert_equal(TYPE_INTPTR_T, parse_ctype("intptr_t"))
+ end
+
+ def test_uintptr_t_ctype
+ assert_equal(TYPE_UINTPTR_T, parse_ctype("uintptr_t"))
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_fiddle.rb b/jni/ruby/test/fiddle/test_fiddle.rb
new file mode 100644
index 0000000..4c6ab97
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_fiddle.rb
@@ -0,0 +1,16 @@
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+class TestFiddle < Fiddle::TestCase
+ def test_windows_constant
+ require 'rbconfig'
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
+ assert Fiddle::WINDOWS, "Fiddle::WINDOWS should be 'true' on Windows platforms"
+ else
+ refute Fiddle::WINDOWS, "Fiddle::WINDOWS should be 'false' on non-Windows platforms"
+ end
+ end
+
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_func.rb b/jni/ruby/test/fiddle/test_func.rb
new file mode 100644
index 0000000..529aaa8
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_func.rb
@@ -0,0 +1,92 @@
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestFunc < TestCase
+ def test_random
+ f = Function.new(@libc['srand'], [-TYPE_LONG], TYPE_VOID)
+ assert_nil f.call(10)
+ end
+
+ def test_syscall_with_tainted_string
+ f = Function.new(@libc['system'], [TYPE_VOIDP], TYPE_INT)
+ assert_raises(SecurityError) do
+ Thread.new {
+ $SAFE = 1
+ f.call("uname -rs".taint)
+ }.join
+ end
+ end
+
+ def test_sinf
+ begin
+ f = Function.new(@libm['sinf'], [TYPE_FLOAT], TYPE_FLOAT)
+ rescue Fiddle::DLError
+ skip "libm may not have sinf()"
+ end
+ assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
+ end
+
+ def test_sin
+ f = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
+ assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
+ end
+
+ def test_string
+ stress, GC.stress = GC.stress, true
+ f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
+ buff = "000"
+ str = f.call(buff, "123")
+ assert_equal("123", buff)
+ assert_equal("123", str.to_s)
+ ensure
+ GC.stress = stress
+ end
+
+ def test_isdigit
+ f = Function.new(@libc['isdigit'], [TYPE_INT], TYPE_INT)
+ r1 = f.call(?1.ord)
+ r2 = f.call(?2.ord)
+ rr = f.call(?r.ord)
+ assert_operator r1, :>, 0
+ assert_operator r2, :>, 0
+ assert_equal 0, rr
+ end
+
+ def test_atof
+ f = Function.new(@libc['atof'], [TYPE_VOIDP], TYPE_DOUBLE)
+ r = f.call("12.34")
+ assert_includes(12.00..13.00, r)
+ end
+
+ def test_strtod
+ f = Function.new(@libc['strtod'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_DOUBLE)
+ buff1 = Pointer["12.34"]
+ buff2 = buff1 + 4
+ r = f.call(buff1, - buff2)
+ assert_in_delta(12.34, r, 0.001)
+ end
+
+ def test_qsort1
+ cb = Class.new(Closure) {
+ def call(x, y)
+ Pointer.new(x)[0] <=> Pointer.new(y)[0]
+ end
+ }.new(TYPE_INT, [TYPE_VOIDP, TYPE_VOIDP])
+
+ qsort = Function.new(@libc['qsort'],
+ [TYPE_VOIDP, TYPE_SIZE_T, TYPE_SIZE_T, TYPE_VOIDP],
+ TYPE_VOID)
+ buff = "9341"
+ qsort.call(buff, buff.size, 1, cb)
+ assert_equal("1349", buff)
+
+ bug4929 = '[ruby-core:37395]'
+ buff = "9341"
+ EnvUtil.under_gc_stress {qsort.call(buff, buff.size, 1, cb)}
+ assert_equal("1349", buff, bug4929)
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_function.rb b/jni/ruby/test/fiddle/test_function.rb
new file mode 100644
index 0000000..de7c958
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_function.rb
@@ -0,0 +1,82 @@
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestFunction < Fiddle::TestCase
+ include Test::Unit::Assertions
+
+ def setup
+ super
+ Fiddle.last_error = nil
+ end
+
+ def test_default_abi
+ func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
+ assert_equal Function::DEFAULT, func.abi
+ end
+
+ def test_name
+ func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE, name: 'sin')
+ assert_equal 'sin', func.name
+ end
+
+ def test_argument_errors
+ assert_raises(TypeError) do
+ Function.new(@libm['sin'], TYPE_DOUBLE, TYPE_DOUBLE)
+ end
+
+ assert_raises(TypeError) do
+ Function.new(@libm['sin'], ['foo'], TYPE_DOUBLE)
+ end
+
+ assert_raises(TypeError) do
+ Function.new(@libm['sin'], [TYPE_DOUBLE], 'foo')
+ end
+ end
+
+ def test_call
+ func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
+ assert_in_delta 1.0, func.call(90 * Math::PI / 180), 0.0001
+ end
+
+ def test_argument_count
+ closure = Class.new(Closure) {
+ def call one
+ 10 + one
+ end
+ }.new(TYPE_INT, [TYPE_INT])
+ func = Function.new(closure, [TYPE_INT], TYPE_INT)
+
+ assert_raises(ArgumentError) do
+ func.call(1,2,3)
+ end
+ assert_raises(ArgumentError) do
+ func.call
+ end
+ end
+
+ def test_last_error
+ func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
+
+ assert_nil Fiddle.last_error
+ func.call("000", "123")
+ refute_nil Fiddle.last_error
+ end
+
+ def test_strcpy
+ f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
+ buff = "000"
+ str = f.call(buff, "123")
+ assert_equal("123", buff)
+ assert_equal("123", str.to_s)
+ end
+
+ def test_no_memory_leak
+ prep = 'r = Fiddle::Function.new(Fiddle.dlopen(nil)["rb_obj_tainted"], [Fiddle::TYPE_UINTPTR_T], Fiddle::TYPE_UINTPTR_T); a = "a"'
+ code = 'begin r.call(a); rescue TypeError; end'
+ assert_no_memory_leak(%w[-W0 -rfiddle], "#{prep}\n1000.times{#{code}}", "10_000.times {#{code}}", limit: 1.2)
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_handle.rb b/jni/ruby/test/fiddle/test_handle.rb
new file mode 100644
index 0000000..ffbde41
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_handle.rb
@@ -0,0 +1,195 @@
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestHandle < TestCase
+ include Fiddle
+
+ include Test::Unit::Assertions
+
+ def test_to_i
+ handle = Fiddle::Handle.new(LIBC_SO)
+ assert_kind_of Integer, handle.to_i
+ end
+
+ def test_static_sym_secure
+ assert_raises(SecurityError) do
+ Thread.new do
+ $SAFE = 2
+ Fiddle::Handle.sym('calloc')
+ end.join
+ end
+ end
+
+ def test_static_sym_unknown
+ assert_raises(DLError) { Fiddle::Handle.sym('fooo') }
+ assert_raises(DLError) { Fiddle::Handle['fooo'] }
+ end
+
+ def test_static_sym
+ skip "Fiddle::Handle.sym is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
+ begin
+ # Linux / Darwin / FreeBSD
+ refute_nil Fiddle::Handle.sym('dlopen')
+ assert_equal Fiddle::Handle.sym('dlopen'), Fiddle::Handle['dlopen']
+ rescue
+ # NetBSD
+ require 'objspace'
+ refute_nil Fiddle::Handle.sym('Init_objspace')
+ assert_equal Fiddle::Handle.sym('Init_objspace'), Fiddle::Handle['Init_objspace']
+ end
+ end
+
+ def test_sym_closed_handle
+ handle = Fiddle::Handle.new(LIBC_SO)
+ handle.close
+ assert_raises(DLError) { handle.sym("calloc") }
+ assert_raises(DLError) { handle["calloc"] }
+ end
+
+ def test_sym_unknown
+ handle = Fiddle::Handle.new(LIBC_SO)
+ assert_raises(DLError) { handle.sym('fooo') }
+ assert_raises(DLError) { handle['fooo'] }
+ end
+
+ def test_sym_with_bad_args
+ handle = Handle.new(LIBC_SO)
+ assert_raises(TypeError) { handle.sym(nil) }
+ assert_raises(TypeError) { handle[nil] }
+ end
+
+ def test_sym_secure
+ assert_raises(SecurityError) do
+ Thread.new do
+ $SAFE = 2
+ handle = Handle.new(LIBC_SO)
+ handle.sym('calloc')
+ end.join
+ end
+ end
+
+ def test_sym
+ handle = Handle.new(LIBC_SO)
+ refute_nil handle.sym('calloc')
+ refute_nil handle['calloc']
+ end
+
+ def test_handle_close
+ handle = Handle.new(LIBC_SO)
+ assert_equal 0, handle.close
+ end
+
+ def test_handle_close_twice
+ handle = Handle.new(LIBC_SO)
+ handle.close
+ assert_raises(DLError) do
+ handle.close
+ end
+ end
+
+ def test_dlopen_returns_handle
+ assert_instance_of Handle, dlopen(LIBC_SO)
+ end
+
+ def test_dlopen_safe
+ assert_raises(SecurityError) do
+ Thread.new do
+ $SAFE = 2
+ dlopen(LIBC_SO)
+ end.join
+ end
+ end
+
+ def test_initialize_safe
+ assert_raises(SecurityError) do
+ Thread.new do
+ $SAFE = 2
+ Handle.new(LIBC_SO)
+ end.join
+ end
+ end
+
+ def test_initialize_noargs
+ handle = Handle.new
+ refute_nil handle['rb_str_new']
+ end
+
+ def test_initialize_flags
+ handle = Handle.new(LIBC_SO, RTLD_LAZY | RTLD_GLOBAL)
+ refute_nil handle['calloc']
+ end
+
+ def test_enable_close
+ handle = Handle.new(LIBC_SO)
+ assert !handle.close_enabled?, 'close is enabled'
+
+ handle.enable_close
+ assert handle.close_enabled?, 'close is not enabled'
+ end
+
+ def test_disable_close
+ handle = Handle.new(LIBC_SO)
+
+ handle.enable_close
+ assert handle.close_enabled?, 'close is enabled'
+ handle.disable_close
+ assert !handle.close_enabled?, 'close is enabled'
+ end
+
+ def test_NEXT
+ begin
+ # Linux / Darwin
+ #
+ # There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find
+ # the first occurrence of the desired symbol using the default library search order. The
+ # latter will find the next occurrence of a function in the search order after the current
+ # library. This allows one to provide a wrapper around a function in another shared
+ # library.
+ # --- Ubuntu Linux 8.04 dlsym(3)
+ handle = Handle::NEXT
+ refute_nil handle['malloc']
+ rescue
+ # BSD
+ #
+ # If dlsym() is called with the special handle RTLD_NEXT, then the search
+ # for the symbol is limited to the shared objects which were loaded after
+ # the one issuing the call to dlsym(). Thus, if the function is called
+ # from the main program, all the shared libraries are searched. If it is
+ # called from a shared library, all subsequent shared libraries are
+ # searched. RTLD_NEXT is useful for implementing wrappers around library
+ # functions. For example, a wrapper function getpid() could access the
+ # "real" getpid() with dlsym(RTLD_NEXT, "getpid"). (Actually, the dlfunc()
+ # interface, below, should be used, since getpid() is a function and not a
+ # data object.)
+ # --- FreeBSD 8.0 dlsym(3)
+ require 'objspace'
+ handle = Handle::NEXT
+ refute_nil handle['Init_objspace']
+ end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_DEFAULT
+ skip "Handle::DEFAULT is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
+ handle = Handle::DEFAULT
+ refute_nil handle['malloc']
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_dlerror
+ # FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls
+ # getaddrinfo(3). And nsdispatch(3) doesn't call dlerror(3) even if
+ # it calls _nss_cache_cycle_prevention_function with dlsym(3).
+ # So our Fiddle::Handle#sym must call dlerror(3) before call dlsym.
+ # In general uses of dlerror(3) should call it before use it.
+ require 'socket'
+ Socket.gethostbyname("localhost")
+ Fiddle.dlopen("/lib/libc.so.7").sym('strcpy')
+ end if /freebsd/=~ RUBY_PLATFORM
+
+ def test_no_memory_leak
+ assert_no_memory_leak(%w[-W0 -rfiddle.so], '', '100_000.times {Fiddle::Handle.allocate}; GC.start', rss: true)
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_import.rb b/jni/ruby/test/fiddle/test_import.rb
new file mode 100644
index 0000000..c83f50f
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_import.rb
@@ -0,0 +1,150 @@
+# coding: US-ASCII
+begin
+ require_relative 'helper'
+ require 'fiddle/import'
+rescue LoadError
+end
+
+module Fiddle
+ module LIBC
+ extend Importer
+ dlload LIBC_SO, LIBM_SO
+
+ typealias 'string', 'char*'
+ typealias 'FILE*', 'void*'
+
+ extern "void *strcpy(char*, char*)"
+ extern "int isdigit(int)"
+ extern "double atof(string)"
+ extern "unsigned long strtoul(char*, char **, int)"
+ extern "int qsort(void*, unsigned long, unsigned long, void*)"
+ extern "int fprintf(FILE*, char*)"
+ extern "int gettimeofday(timeval*, timezone*)" rescue nil
+
+ BoundQsortCallback = bind("void *bound_qsort_callback(void*, void*)"){|ptr1,ptr2| ptr1[0] <=> ptr2[0]}
+ Timeval = struct [
+ "long tv_sec",
+ "long tv_usec",
+ ]
+ Timezone = struct [
+ "int tz_minuteswest",
+ "int tz_dsttime",
+ ]
+ MyStruct = struct [
+ "short num[5]",
+ "char c",
+ "unsigned char buff[7]",
+ ]
+
+ CallCallback = bind("void call_callback(void*, void*)"){ | ptr1, ptr2|
+ f = Function.new(ptr1.to_i, [TYPE_VOIDP], TYPE_VOID)
+ f.call(ptr2)
+ }
+ end
+
+ class TestImport < TestCase
+ def test_ensure_call_dlload
+ err = assert_raises(RuntimeError) do
+ Class.new do
+ extend Importer
+ extern "void *strcpy(char*, char*)"
+ end
+ end
+ assert_match(/call dlload before/, err.message)
+ end
+
+ def test_malloc()
+ s1 = LIBC::Timeval.malloc()
+ s2 = LIBC::Timeval.malloc()
+ refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
+ end
+
+ def test_sizeof()
+ assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
+ assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
+ assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct.malloc()))
+ assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long"))
+ end
+
+ Fiddle.constants.grep(/\ATYPE_(?!VOID\z)(.*)/) do
+ type = $&
+ size = Fiddle.const_get("SIZEOF_#{$1}")
+ name = $1.sub(/P\z/,"*").gsub(/_(?!T\z)/, " ").downcase
+ define_method("test_sizeof_#{name}") do
+ assert_equal(size, Fiddle::Importer.sizeof(name), type)
+ end
+ end
+
+ def test_unsigned_result()
+ d = (2 ** 31) + 1
+
+ r = LIBC.strtoul(d.to_s, 0, 0)
+ assert_equal(d, r)
+ end
+
+ def test_io()
+ if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM )
+ return
+ end
+ io_in,io_out = IO.pipe()
+ LIBC.fprintf(io_out, "hello")
+ io_out.flush()
+ io_out.close()
+ str = io_in.read()
+ io_in.close()
+ assert_equal("hello", str)
+ end
+
+ def test_value()
+ i = LIBC.value('int', 2)
+ assert_equal(2, i.value)
+
+ d = LIBC.value('double', 2.0)
+ assert_equal(2.0, d.value)
+
+ ary = LIBC.value('int[3]', [0,1,2])
+ assert_equal([0,1,2], ary.value)
+ end
+
+ def test_struct()
+ s = LIBC::MyStruct.malloc()
+ s.num = [0,1,2,3,4]
+ s.c = ?a.ord
+ s.buff = "012345\377"
+ assert_equal([0,1,2,3,4], s.num)
+ assert_equal(?a.ord, s.c)
+ assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
+ end
+
+ def test_gettimeofday()
+ if( defined?(LIBC.gettimeofday) )
+ timeval = LIBC::Timeval.malloc()
+ timezone = LIBC::Timezone.malloc()
+ LIBC.gettimeofday(timeval, timezone)
+ cur = Time.now()
+ assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
+ end
+ end
+
+ def test_strcpy()
+ buff = "000"
+ str = LIBC.strcpy(buff, "123")
+ assert_equal("123", buff)
+ assert_equal("123", str.to_s)
+ end
+
+ def test_isdigit
+ r1 = LIBC.isdigit(?1.ord)
+ r2 = LIBC.isdigit(?2.ord)
+ rr = LIBC.isdigit(?r.ord)
+ assert_operator(r1, :>, 0)
+ assert_operator(r2, :>, 0)
+ assert_equal(0, rr)
+ end
+
+ def test_atof
+ r = LIBC.atof("12.34")
+ assert_includes(12.00..13.00, r)
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fiddle/test_pointer.rb b/jni/ruby/test/fiddle/test_pointer.rb
new file mode 100644
index 0000000..3ea9bc8
--- /dev/null
+++ b/jni/ruby/test/fiddle/test_pointer.rb
@@ -0,0 +1,237 @@
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestPointer < TestCase
+ def dlwrap arg
+ Fiddle.dlwrap arg
+ end
+
+ include Test::Unit::Assertions
+
+ def test_cptr_to_int
+ null = Fiddle::NULL
+ assert_equal(null.to_i, null.to_int)
+ end
+
+ def test_malloc_free_func_int
+ free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
+ assert_equal free.to_i, Fiddle::RUBY_FREE.to_i
+
+ ptr = Pointer.malloc(10, free.to_i)
+ assert_equal 10, ptr.size
+ assert_equal free.to_i, ptr.free.to_i
+ end
+
+ def test_malloc_free_func
+ free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
+
+ ptr = Pointer.malloc(10, free)
+ assert_equal 10, ptr.size
+ assert_equal free.to_i, ptr.free.to_i
+ end
+
+ def test_to_str
+ str = "hello world"
+ ptr = Pointer[str]
+
+ assert_equal 3, ptr.to_str(3).length
+ assert_equal str, ptr.to_str
+
+ ptr[5] = 0
+ assert_equal "hello\0world", ptr.to_str
+ end
+
+ def test_to_s
+ str = "hello world"
+ ptr = Pointer[str]
+
+ assert_equal 3, ptr.to_s(3).length
+ assert_equal str, ptr.to_s
+
+ ptr[5] = 0
+ assert_equal 'hello', ptr.to_s
+ end
+
+ def test_minus
+ str = "hello world"
+ ptr = Pointer[str]
+ assert_equal ptr.to_s, (ptr + 3 - 3).to_s
+ end
+
+ # TODO: what if the pointer size is 0? raise an exception? do we care?
+ def test_plus
+ str = "hello world"
+ ptr = Pointer[str]
+ new_str = ptr + 3
+ assert_equal 'lo world', new_str.to_s
+ end
+
+ def test_inspect
+ ptr = Pointer.new(0)
+ inspect = ptr.inspect
+ assert_match(/size=#{ptr.size}/, inspect)
+ assert_match(/free=#{sprintf("%#x", ptr.free.to_i)}/, inspect)
+ assert_match(/ptr=#{sprintf("%#x", ptr.to_i)}/, inspect)
+ end
+
+ def test_to_ptr_string
+ str = "hello world"
+ ptr = Pointer[str]
+ assert ptr.tainted?, 'pointer should be tainted'
+ assert_equal str.length, ptr.size
+ assert_equal 'hello', ptr[0,5]
+ end
+
+ def test_to_ptr_io
+ buf = Pointer.malloc(10)
+ File.open(__FILE__, 'r') do |f|
+ ptr = Pointer.to_ptr f
+ fread = Function.new(@libc['fread'],
+ [TYPE_VOIDP, TYPE_INT, TYPE_INT, TYPE_VOIDP],
+ TYPE_INT)
+ fread.call(buf.to_i, Fiddle::SIZEOF_CHAR, buf.size - 1, ptr.to_i)
+ end
+
+ File.open(__FILE__, 'r') do |f|
+ assert_equal f.read(9), buf.to_s
+ end
+ end
+
+ def test_to_ptr_with_ptr
+ ptr = Pointer.new 0
+ ptr2 = Pointer.to_ptr Struct.new(:to_ptr).new(ptr)
+ assert_equal ptr, ptr2
+
+ assert_raises(Fiddle::DLError) do
+ Pointer.to_ptr Struct.new(:to_ptr).new(nil)
+ end
+ end
+
+ def test_to_ptr_with_num
+ ptr = Pointer.new 0
+ assert_equal ptr, Pointer[0]
+ end
+
+ def test_equals
+ ptr = Pointer.new 0
+ ptr2 = Pointer.new 0
+ assert_equal ptr2, ptr
+ end
+
+ def test_not_equals
+ ptr = Pointer.new 0
+ refute_equal 10, ptr, '10 should not equal the pointer'
+ end
+
+ def test_cmp
+ ptr = Pointer.new 0
+ assert_nil(ptr <=> 10, '10 should not be comparable')
+ end
+
+ def test_ref_ptr
+ ary = [0,1,2,4,5]
+ addr = Pointer.new(dlwrap(ary))
+ assert_equal addr.to_i, addr.ref.ptr.to_i
+
+ assert_equal addr.to_i, (+ (- addr)).to_i
+ end
+
+ def test_to_value
+ ary = [0,1,2,4,5]
+ addr = Pointer.new(dlwrap(ary))
+ assert_equal ary, addr.to_value
+ end
+
+ def test_free
+ ptr = Pointer.malloc(4)
+ assert_nil ptr.free
+ end
+
+ def test_free=
+ assert_normal_exit(<<-"End", '[ruby-dev:39269]')
+ require 'fiddle'
+ Fiddle::LIBC_SO = #{Fiddle::LIBC_SO.dump}
+ Fiddle::LIBM_SO = #{Fiddle::LIBM_SO.dump}
+ include Fiddle
+ @libc = dlopen(LIBC_SO)
+ @libm = dlopen(LIBM_SO)
+ free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
+ ptr = Fiddle::Pointer.malloc(4)
+ ptr.free = free
+ free.ptr
+ ptr.free.ptr
+ End
+
+ free = Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
+ ptr = Pointer.malloc(4)
+ ptr.free = free
+
+ assert_equal free.ptr, ptr.free.ptr
+ end
+
+ def test_null?
+ ptr = Pointer.new(0)
+ assert ptr.null?
+ end
+
+ def test_size
+ ptr = Pointer.malloc(4)
+ assert_equal 4, ptr.size
+ Fiddle.free ptr.to_i
+ end
+
+ def test_size=
+ ptr = Pointer.malloc(4)
+ ptr.size = 10
+ assert_equal 10, ptr.size
+ Fiddle.free ptr.to_i
+ end
+
+ def test_aref_aset
+ check = Proc.new{|str,ptr|
+ assert_equal(str.size(), ptr.size())
+ assert_equal(str, ptr.to_s())
+ assert_equal(str[0,2], ptr.to_s(2))
+ assert_equal(str[0,2], ptr[0,2])
+ assert_equal(str[1,2], ptr[1,2])
+ assert_equal(str[1,0], ptr[1,0])
+ assert_equal(str[0].ord, ptr[0])
+ assert_equal(str[1].ord, ptr[1])
+ }
+ str = 'abc'
+ ptr = Pointer[str]
+ check.call(str, ptr)
+
+ str[0] = "c"
+ assert_equal 'c'.ord, ptr[0] = "c".ord
+ check.call(str, ptr)
+
+ str[0,2] = "aa"
+ assert_equal 'aa', ptr[0,2] = "aa"
+ check.call(str, ptr)
+
+ ptr2 = Pointer['cdeeee']
+ str[0,2] = "cd"
+ assert_equal ptr2, ptr[0,2] = ptr2
+ check.call(str, ptr)
+
+ ptr3 = Pointer['vvvv']
+ str[0,2] = "vv"
+ assert_equal ptr3.to_i, ptr[0,2] = ptr3.to_i
+ check.call(str, ptr)
+ end
+
+ def test_null_pointer
+ nullpo = Pointer.new(0)
+ assert_raise(DLError) {nullpo[0]}
+ assert_raise(DLError) {nullpo[0] = 1}
+ end
+
+ def test_no_memory_leak
+ assert_no_memory_leak(%w[-W0 -rfiddle.so], '', '100_000.times {Fiddle::Pointer.allocate}', rss: true)
+ end
+ end
+end if defined?(Fiddle)
diff --git a/jni/ruby/test/fileutils/clobber.rb b/jni/ruby/test/fileutils/clobber.rb
new file mode 100644
index 0000000..9f94665
--- /dev/null
+++ b/jni/ruby/test/fileutils/clobber.rb
@@ -0,0 +1,91 @@
+require 'fileutils'
+require 'test/unit'
+require 'tmpdir'
+require_relative 'fileasserts'
+
+class TestFileUtils < Test::Unit::TestCase
+end
+
+module TestFileUtils::Clobber
+ include Test::Unit::FileAssertions
+
+ def my_rm_rf(path)
+ if File.exist?('/bin/rm')
+ system %Q[/bin/rm -rf "#{path}"]
+ else
+ FileUtils.rm_rf path
+ end
+ end
+
+ SRC = 'data/src'
+ COPY = 'data/copy'
+
+ def setup
+ @prevdir = Dir.pwd
+ class << (@fileutils_output = "")
+ alias puts <<
+ end
+ tmproot = "#{Dir.tmpdir}/fileutils.rb.#{$$}"
+ Dir.mkdir tmproot unless File.directory?(tmproot)
+ Dir.chdir tmproot
+ my_rm_rf 'data'; Dir.mkdir 'data'
+ my_rm_rf 'tmp'; Dir.mkdir 'tmp'
+ File.open(SRC, 'w') {|f| f.puts 'dummy' }
+ File.open(COPY, 'w') {|f| f.puts 'dummy' }
+ end
+
+ def teardown
+ tmproot = Dir.pwd
+ Dir.chdir @prevdir
+ my_rm_rf tmproot
+ end
+
+ def test_cp
+ cp SRC, 'tmp/cp'
+ check 'tmp/cp'
+ end
+
+ def test_mv
+ mv SRC, 'tmp/mv'
+ check 'tmp/mv'
+ end
+
+ def check(dest, message=nil)
+ assert_file_not_exist dest, message
+ assert_file_exist SRC, message
+ assert_same_file SRC, COPY, message
+ end
+
+ def test_rm
+ rm SRC
+ assert_file_exist SRC
+ assert_same_file SRC, COPY
+ end
+
+ def test_rm_f
+ rm_f SRC
+ assert_file_exist SRC
+ assert_same_file SRC, COPY
+ end
+
+ def test_rm_rf
+ rm_rf SRC
+ assert_file_exist SRC
+ assert_same_file SRC, COPY
+ end
+
+ def test_mkdir
+ mkdir 'dir'
+ assert_file_not_exist 'dir'
+ end
+
+ def test_mkdir_p
+ mkdir 'dir/dir/dir'
+ assert_file_not_exist 'dir'
+ end
+
+ def test_copy_entry
+ copy_entry SRC, 'tmp/copy_entry'
+ check 'tmp/copy_entry', bug4331 = '[ruby-dev:43129]'
+ end
+end
diff --git a/jni/ruby/test/fileutils/fileasserts.rb b/jni/ruby/test/fileutils/fileasserts.rb
new file mode 100644
index 0000000..cf79508
--- /dev/null
+++ b/jni/ruby/test/fileutils/fileasserts.rb
@@ -0,0 +1,111 @@
+# $Id: fileasserts.rb 44387 2013-12-24 14:20:47Z nobu $
+
+module Test
+ module Unit
+ module FileAssertions
+ def assert_same_file(from, to, message=nil)
+ assert_equal(File.read(from), File.read(to), "file #{from} != #{to}#{message&&': '}#{message||''}")
+ end
+
+ def assert_same_entry(from, to, message=nil)
+ a = File.stat(from)
+ b = File.stat(to)
+ msg = "#{message&&': '}#{message||''}"
+ assert_equal a.mode, b.mode, "mode #{a.mode} != #{b.mode}#{msg}"
+ #assert_equal a.atime, b.atime
+ assert_equal_timestamp a.mtime, b.mtime, "mtime #{a.mtime} != #{b.mtime}#{msg}"
+ assert_equal a.uid, b.uid, "uid #{a.uid} != #{b.uid}#{msg}"
+ assert_equal a.gid, b.gid, "gid #{a.gid} != #{b.gid}#{msg}"
+ end
+
+ def assert_file_exist(path, message=nil)
+ assert(File.exist?(path), "file not exist: #{path}#{message&&': '}#{message||''}")
+ end
+
+ def assert_file_not_exist(path, message=nil)
+ assert(!File.exist?(path), "file exist: #{path}#{message&&': '}#{message||''}")
+ end
+
+ def assert_directory(path, message=nil)
+ assert(File.directory?(path), "is not directory: #{path}#{message&&': '}#{message||''}")
+ end
+
+ def assert_symlink(path, message=nil)
+ assert(File.symlink?(path), "is not a symlink: #{path}#{message&&': '}#{message||''}")
+ end
+
+ def assert_not_symlink(path, message=nil)
+ assert(!File.symlink?(path), "is a symlink: #{path}#{message&&': '}#{message||''}")
+ end
+
+ def assert_equal_time(expected, actual, message=nil)
+ expected_str = expected.to_s
+ actual_str = actual.to_s
+ if expected_str == actual_str
+ expected_str << " (nsec=#{expected.nsec})"
+ actual_str << " (nsec=#{actual.nsec})"
+ end
+ full_message = build_message(message, <<EOT)
+<#{expected_str}> expected but was
+<#{actual_str}>.
+EOT
+ assert_equal(expected, actual, full_message)
+ end
+
+ def assert_equal_timestamp(expected, actual, message=nil)
+ expected_str = expected.to_s
+ actual_str = actual.to_s
+ if expected_str == actual_str
+ expected_str << " (nsec=#{expected.nsec})"
+ actual_str << " (nsec=#{actual.nsec})"
+ end
+ full_message = build_message(message, <<EOT)
+<#{expected_str}> expected but was
+<#{actual_str}>.
+EOT
+ # subsecond timestamp is not portable.
+ assert_equal(expected.tv_sec, actual.tv_sec, full_message)
+ end
+
+ def assert_filemode(expected, file, message=nil, mask: 07777)
+ width = ('%o' % mask).size
+ actual = File.stat(file).mode & mask
+ assert expected == actual, <<EOT
+File mode of "#{file}" unexpected:
+ Expected: <#{'%0*o' % [width, expected]}>
+ Actual: <#{'%0*o' % [width, actual]}>
+EOT
+ end
+
+ def assert_equal_filemode(file1, file2, message=nil, mask: 07777)
+ mode1, mode2 = [file1, file2].map { |file|
+ File.stat(file).mode & mask
+ }
+ width = ('%o' % mask).size
+ assert mode1 == mode2, <<EOT
+File modes expected to be equal:
+ <#{'%0*o' % [width, mode1]}>: "#{file1}"
+ <#{'%0*o' % [width, mode2]}>: "#{file2}"
+EOT
+ end
+
+ def assert_ownership_group(expected, file)
+ actual = File.stat(file).gid
+ assert expected == actual, <<EOT
+File group ownership of "#{file}" unexpected:
+ Expected: <#{expected}>
+ Actual: <#{actual}>
+EOT
+ end
+
+ def assert_ownership_user(expected, file)
+ actual = File.stat(file).uid
+ assert expected == actual, <<EOT
+File user ownership of "#{file}" unexpected:
+ Expected: <#{expected}>
+ Actual: <#{actual}>
+EOT
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/fileutils/test_dryrun.rb b/jni/ruby/test/fileutils/test_dryrun.rb
new file mode 100644
index 0000000..d51e06b
--- /dev/null
+++ b/jni/ruby/test/fileutils/test_dryrun.rb
@@ -0,0 +1,17 @@
+# $Id: test_dryrun.rb 39544 2013-03-01 02:09:42Z drbrain $
+
+require 'fileutils'
+require 'test/unit'
+require_relative 'visibility_tests'
+
+class TestFileUtilsDryRun < Test::Unit::TestCase
+
+ include FileUtils::DryRun
+ include TestFileUtils::Visibility
+
+ def setup
+ super
+ @fu_module = FileUtils::DryRun
+ end
+
+end
diff --git a/jni/ruby/test/fileutils/test_fileutils.rb b/jni/ruby/test/fileutils/test_fileutils.rb
new file mode 100644
index 0000000..27c80d7
--- /dev/null
+++ b/jni/ruby/test/fileutils/test_fileutils.rb
@@ -0,0 +1,1570 @@
+# $Id: test_fileutils.rb 50485 2015-05-13 15:32:22Z nagachika $
+
+require 'fileutils'
+require 'etc'
+require_relative 'fileasserts'
+require 'pathname'
+require 'tmpdir'
+require 'test/unit'
+
+class TestFileUtils < Test::Unit::TestCase
+ TMPROOT = "#{Dir.tmpdir}/fileutils.rb.#{$$}"
+ include Test::Unit::FileAssertions
+
+ def assert_output_lines(expected, fu = self, message=nil)
+ old = fu.instance_variable_get(:@fileutils_output)
+ IO.pipe {|read, write|
+ fu.instance_variable_set(:@fileutils_output, write)
+ th = Thread.new { read.read }
+ th2 = Thread.new {
+ yield
+ write.close
+ }
+ th_value, _ = assert_join_threads([th, th2])
+ lines = th_value.lines.map {|l| l.chomp }
+ assert_equal(expected, lines)
+ }
+ ensure
+ fu.instance_variable_set(:@fileutils_output, old) if old
+ end
+
+ m = Module.new do
+ def have_drive_letter?
+ /mswin(?!ce)|mingw|bcc|emx/ =~ RUBY_PLATFORM
+ end
+
+ def have_file_perm?
+ /mswin|mingw|bcc|emx/ !~ RUBY_PLATFORM
+ end
+
+ @@have_symlink = nil
+
+ def have_symlink?
+ if @@have_symlink == nil
+ @@have_symlink = check_have_symlink?
+ end
+ @@have_symlink
+ end
+
+ def check_have_symlink?
+ File.symlink nil, nil
+ rescue NotImplementedError
+ return false
+ rescue
+ return true
+ end
+
+ @@have_hardlink = nil
+
+ def have_hardlink?
+ if @@have_hardlink == nil
+ @@have_hardlink = check_have_hardlink?
+ end
+ @@have_hardlink
+ end
+
+ def check_have_hardlink?
+ File.link nil, nil
+ rescue NotImplementedError
+ return false
+ rescue
+ return true
+ end
+
+ def root_in_posix?
+ if Process.respond_to?('uid')
+ return Process.uid == 0
+ else
+ return false
+ end
+ end
+
+ def distinct_uids(n = 2)
+ return unless user = Etc.getpwent
+ uids = [user.uid]
+ while user = Etc.getpwent
+ uid = user.uid
+ unless uids.include?(uid)
+ uids << uid
+ break if uids.size >= n
+ end
+ end
+ uids
+ ensure
+ Etc.endpwent
+ end
+
+ begin
+ tmproot = TMPROOT
+ Dir.mkdir tmproot unless File.directory?(tmproot)
+ Dir.chdir tmproot do
+ Dir.mkdir("\n")
+ Dir.rmdir("\n")
+ end
+ def lf_in_path_allowed?
+ true
+ end
+ rescue
+ def lf_in_path_allowed?
+ false
+ end
+ ensure
+ Dir.rmdir tmproot
+ end
+ end
+ include m
+ extend m
+
+ include FileUtils
+
+ def check_singleton(name)
+ assert_respond_to ::FileUtils, name
+ end
+
+ def my_rm_rf(path)
+ if File.exist?('/bin/rm')
+ system %Q[/bin/rm -rf "#{path}"]
+ else
+ FileUtils.rm_rf path
+ end
+ end
+
+ def mymkdir(path)
+ Dir.mkdir path
+ File.chown nil, Process.gid, path if have_file_perm?
+ end
+
+ def setup
+ @prevdir = Dir.pwd
+ @groups = Process.groups if have_file_perm?
+ tmproot = TMPROOT
+ mymkdir tmproot unless File.directory?(tmproot)
+ Dir.chdir tmproot
+ my_rm_rf 'data'; mymkdir 'data'
+ my_rm_rf 'tmp'; mymkdir 'tmp'
+ prepare_data_file
+ end
+
+ def teardown
+ Dir.chdir @prevdir
+ my_rm_rf TMPROOT
+ end
+
+
+ TARGETS = %w( data/a data/all data/random data/zero )
+
+ def prepare_data_file
+ File.open('data/a', 'w') {|f|
+ 32.times do
+ f.puts 'a' * 50
+ end
+ }
+
+ all_chars = (0..255).map {|n| n.chr }.join('')
+ File.open('data/all', 'w') {|f|
+ 32.times do
+ f.puts all_chars
+ end
+ }
+
+ random_chars = (0...50).map { rand(256).chr }.join('')
+ File.open('data/random', 'w') {|f|
+ 32.times do
+ f.puts random_chars
+ end
+ }
+
+ File.open('data/zero', 'w') {|f|
+ ;
+ }
+ end
+
+ BIGFILE = 'data/big'
+
+ def prepare_big_file
+ File.open('data/big', 'w') {|f|
+ (4 * 1024 * 1024 / 256).times do # 4MB
+ f.print "aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa\n"
+ end
+ }
+ end
+
+ def prepare_time_data
+ File.open('data/old', 'w') {|f| f.puts 'dummy' }
+ File.open('data/newer', 'w') {|f| f.puts 'dummy' }
+ File.open('data/newest', 'w') {|f| f.puts 'dummy' }
+ t = Time.now
+ File.utime t-8, t-8, 'data/old'
+ File.utime t-4, t-4, 'data/newer'
+ end
+
+ def each_srcdest
+ TARGETS.each do |path|
+ yield path, "tmp/#{File.basename(path)}"
+ end
+ end
+
+ #
+ # Test Cases
+ #
+
+ def test_pwd
+ check_singleton :pwd
+
+ assert_equal Dir.pwd, pwd()
+
+ cwd = Dir.pwd
+ root = have_drive_letter? ? 'C:/' : '/'
+ cd(root) {
+ assert_equal root, pwd()
+ }
+ assert_equal cwd, pwd()
+ end
+
+ def test_cmp
+ check_singleton :cmp
+
+ TARGETS.each do |fname|
+ assert cmp(fname, fname), 'not same?'
+ end
+ assert_raise(ArgumentError) {
+ cmp TARGETS[0], TARGETS[0], :undefinedoption => true
+ }
+
+ # pathname
+ touch 'tmp/cmptmp'
+ assert_nothing_raised {
+ cmp Pathname.new('tmp/cmptmp'), 'tmp/cmptmp'
+ cmp 'tmp/cmptmp', Pathname.new('tmp/cmptmp')
+ cmp Pathname.new('tmp/cmptmp'), Pathname.new('tmp/cmptmp')
+ }
+ end
+
+ def test_cp
+ check_singleton :cp
+
+ each_srcdest do |srcpath, destpath|
+ cp srcpath, destpath
+ assert_same_file srcpath, destpath
+
+ cp srcpath, File.dirname(destpath)
+ assert_same_file srcpath, destpath
+
+ cp srcpath, File.dirname(destpath) + '/'
+ assert_same_file srcpath, destpath
+
+ cp srcpath, destpath, :preserve => true
+ assert_same_file srcpath, destpath
+ assert_same_entry srcpath, destpath
+ end
+
+ assert_raise(Errno::ENOENT) {
+ cp 'tmp/cptmp', 'tmp/cptmp_new'
+ }
+ assert_file_not_exist('tmp/cptmp_new')
+
+ # src==dest (1) same path
+ touch 'tmp/cptmp'
+ assert_raise(ArgumentError) {
+ cp 'tmp/cptmp', 'tmp/cptmp'
+ }
+ end
+
+ def test_cp_preserve_permissions
+ bug4507 = '[ruby-core:35518]'
+ touch 'tmp/cptmp'
+ chmod 0755, 'tmp/cptmp'
+ cp 'tmp/cptmp', 'tmp/cptmp2'
+ assert_equal_filemode('tmp/cptmp', 'tmp/cptmp2', bug4507)
+ end
+
+ def test_cp_preserve_permissions_dir
+ bug7246 = '[ruby-core:48603]'
+ mkdir 'tmp/cptmp'
+ mkdir 'tmp/cptmp/d1'
+ chmod 0745, 'tmp/cptmp/d1'
+ mkdir 'tmp/cptmp/d2'
+ chmod 0700, 'tmp/cptmp/d2'
+ cp_r 'tmp/cptmp', 'tmp/cptmp2', :preserve => true
+ assert_equal_filemode('tmp/cptmp/d1', 'tmp/cptmp2/d1', bug7246)
+ assert_equal_filemode('tmp/cptmp/d2', 'tmp/cptmp2/d2', bug7246)
+ end
+
+ def test_cp_symlink
+ touch 'tmp/cptmp'
+ # src==dest (2) symlink and its target
+ File.symlink 'cptmp', 'tmp/cptmp_symlink'
+ assert_raise(ArgumentError) {
+ cp 'tmp/cptmp', 'tmp/cptmp_symlink'
+ }
+ assert_raise(ArgumentError) {
+ cp 'tmp/cptmp_symlink', 'tmp/cptmp'
+ }
+ # src==dest (3) looped symlink
+ File.symlink 'symlink', 'tmp/symlink'
+ assert_raise(Errno::ELOOP) {
+ cp 'tmp/symlink', 'tmp/symlink'
+ }
+ end if have_symlink?
+
+ def test_cp_pathname
+ # pathname
+ touch 'tmp/cptmp'
+ assert_nothing_raised {
+ cp 'tmp/cptmp', Pathname.new('tmp/tmpdest')
+ cp Pathname.new('tmp/cptmp'), 'tmp/tmpdest'
+ cp Pathname.new('tmp/cptmp'), Pathname.new('tmp/tmpdest')
+ mkdir 'tmp/tmpdir'
+ cp ['tmp/cptmp', 'tmp/tmpdest'], Pathname.new('tmp/tmpdir')
+ }
+ end
+
+ def test_cp_r
+ check_singleton :cp_r
+
+ cp_r 'data', 'tmp'
+ TARGETS.each do |fname|
+ assert_same_file fname, "tmp/#{fname}"
+ end
+
+ cp_r 'data', 'tmp2', :preserve => true
+ TARGETS.each do |fname|
+ assert_same_entry fname, "tmp2/#{File.basename(fname)}"
+ assert_same_file fname, "tmp2/#{File.basename(fname)}"
+ end
+
+ # a/* -> b/*
+ mkdir 'tmp/cpr_src'
+ mkdir 'tmp/cpr_dest'
+ File.open('tmp/cpr_src/a', 'w') {|f| f.puts 'a' }
+ File.open('tmp/cpr_src/b', 'w') {|f| f.puts 'b' }
+ File.open('tmp/cpr_src/c', 'w') {|f| f.puts 'c' }
+ mkdir 'tmp/cpr_src/d'
+ cp_r 'tmp/cpr_src/.', 'tmp/cpr_dest'
+ assert_same_file 'tmp/cpr_src/a', 'tmp/cpr_dest/a'
+ assert_same_file 'tmp/cpr_src/b', 'tmp/cpr_dest/b'
+ assert_same_file 'tmp/cpr_src/c', 'tmp/cpr_dest/c'
+ assert_directory 'tmp/cpr_dest/d'
+ my_rm_rf 'tmp/cpr_src'
+ my_rm_rf 'tmp/cpr_dest'
+
+ bug3588 = '[ruby-core:31360]'
+ assert_nothing_raised(ArgumentError, bug3588) do
+ cp_r 'tmp', 'tmp2'
+ end
+ assert_directory 'tmp2/tmp'
+ assert_raise(ArgumentError, bug3588) do
+ cp_r 'tmp2', 'tmp2/new_tmp2'
+ end
+ end
+
+ def test_cp_r_symlink
+ # symlink in a directory
+ mkdir 'tmp/cpr_src'
+ ln_s 'SLdest', 'tmp/cpr_src/symlink'
+ cp_r 'tmp/cpr_src', 'tmp/cpr_dest'
+ assert_symlink 'tmp/cpr_dest/symlink'
+ assert_equal 'SLdest', File.readlink('tmp/cpr_dest/symlink')
+
+ # root is a symlink
+ ln_s 'cpr_src', 'tmp/cpr_src2'
+ cp_r 'tmp/cpr_src2', 'tmp/cpr_dest2'
+ assert_directory 'tmp/cpr_dest2'
+ assert_not_symlink 'tmp/cpr_dest2'
+ assert_symlink 'tmp/cpr_dest2/symlink'
+ assert_equal 'SLdest', File.readlink('tmp/cpr_dest2/symlink')
+ end if have_symlink?
+
+ def test_cp_r_symlink_preserve
+ mkdir 'tmp/cross'
+ mkdir 'tmp/cross/a'
+ mkdir 'tmp/cross/b'
+ touch 'tmp/cross/a/f'
+ touch 'tmp/cross/b/f'
+ ln_s '../a/f', 'tmp/cross/b/l'
+ ln_s '../b/f', 'tmp/cross/a/l'
+ assert_nothing_raised {
+ cp_r 'tmp/cross', 'tmp/cross2', :preserve => true
+ }
+ end if have_symlink?
+
+ def test_cp_r_pathname
+ # pathname
+ touch 'tmp/cprtmp'
+ assert_nothing_raised {
+ cp_r Pathname.new('tmp/cprtmp'), 'tmp/tmpdest'
+ cp_r 'tmp/cprtmp', Pathname.new('tmp/tmpdest')
+ cp_r Pathname.new('tmp/cprtmp'), Pathname.new('tmp/tmpdest')
+ }
+ end
+
+ def test_mv
+ check_singleton :mv
+
+ mkdir 'tmp/dest'
+ TARGETS.each do |fname|
+ cp fname, 'tmp/mvsrc'
+ mv 'tmp/mvsrc', 'tmp/mvdest'
+ assert_same_file fname, 'tmp/mvdest'
+
+ mv 'tmp/mvdest', 'tmp/dest/'
+ assert_same_file fname, 'tmp/dest/mvdest'
+
+ mv 'tmp/dest/mvdest', 'tmp'
+ assert_same_file fname, 'tmp/mvdest'
+ end
+
+ mkdir 'tmp/tmpdir'
+ mkdir_p 'tmp/dest2/tmpdir'
+ assert_raise_with_message(Errno::EEXIST, %r' - tmp/dest2/tmpdir\z',
+ '[ruby-core:68706] [Bug #11021]') {
+ mv 'tmp/tmpdir', 'tmp/dest2'
+ }
+ mkdir 'tmp/dest2/tmpdir/junk'
+ assert_raise(Errno::EEXIST, "[ruby-talk:124368]") {
+ mv 'tmp/tmpdir', 'tmp/dest2'
+ }
+
+ # src==dest (1) same path
+ touch 'tmp/cptmp'
+ assert_raise(ArgumentError) {
+ mv 'tmp/cptmp', 'tmp/cptmp'
+ }
+ end
+
+ def test_mv_symlink
+ touch 'tmp/cptmp'
+ # src==dest (2) symlink and its target
+ File.symlink 'cptmp', 'tmp/cptmp_symlink'
+ assert_raise(ArgumentError) {
+ mv 'tmp/cptmp', 'tmp/cptmp_symlink'
+ }
+ assert_raise(ArgumentError) {
+ mv 'tmp/cptmp_symlink', 'tmp/cptmp'
+ }
+ # src==dest (3) looped symlink
+ File.symlink 'symlink', 'tmp/symlink'
+ assert_raise(Errno::ELOOP) {
+ mv 'tmp/symlink', 'tmp/symlink'
+ }
+ # unexist symlink
+ File.symlink 'xxx', 'tmp/src'
+ assert_nothing_raised {
+ mv 'tmp/src', 'tmp/dest'
+ }
+ assert_equal true, File.symlink?('tmp/dest')
+ end if have_symlink?
+
+ def test_mv_pathname
+ # pathname
+ assert_nothing_raised {
+ touch 'tmp/mvtmpsrc'
+ mv Pathname.new('tmp/mvtmpsrc'), 'tmp/mvtmpdest'
+ touch 'tmp/mvtmpsrc'
+ mv 'tmp/mvtmpsrc', Pathname.new('tmp/mvtmpdest')
+ touch 'tmp/mvtmpsrc'
+ mv Pathname.new('tmp/mvtmpsrc'), Pathname.new('tmp/mvtmpdest')
+ }
+ end
+
+ def test_rm
+ check_singleton :rm
+
+ TARGETS.each do |fname|
+ cp fname, 'tmp/rmsrc'
+ rm 'tmp/rmsrc'
+ assert_file_not_exist 'tmp/rmsrc'
+ end
+
+ # pathname
+ touch 'tmp/rmtmp1'
+ touch 'tmp/rmtmp2'
+ touch 'tmp/rmtmp3'
+ assert_nothing_raised {
+ rm Pathname.new('tmp/rmtmp1')
+ rm [Pathname.new('tmp/rmtmp2'), Pathname.new('tmp/rmtmp3')]
+ }
+ assert_file_not_exist 'tmp/rmtmp1'
+ assert_file_not_exist 'tmp/rmtmp2'
+ assert_file_not_exist 'tmp/rmtmp3'
+ end
+
+ def test_rm_f
+ check_singleton :rm_f
+
+ TARGETS.each do |fname|
+ cp fname, 'tmp/rmsrc'
+ rm_f 'tmp/rmsrc'
+ assert_file_not_exist 'tmp/rmsrc'
+ end
+ end
+
+ def test_rm_symlink
+ File.open('tmp/lnf_symlink_src', 'w') {|f| f.puts 'dummy' }
+ File.symlink 'tmp/lnf_symlink_src', 'tmp/lnf_symlink_dest'
+ rm_f 'tmp/lnf_symlink_dest'
+ assert_file_not_exist 'tmp/lnf_symlink_dest'
+ assert_file_exist 'tmp/lnf_symlink_src'
+
+ rm_f 'notexistdatafile'
+ rm_f 'tmp/notexistdatafile'
+ my_rm_rf 'tmpdatadir'
+ Dir.mkdir 'tmpdatadir'
+ # rm_f 'tmpdatadir'
+ Dir.rmdir 'tmpdatadir'
+ end if have_symlink?
+
+ def test_rm_f_2
+ Dir.mkdir 'tmp/tmpdir'
+ File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' }
+ File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' }
+ rm_f ['tmp/tmpdir/a', 'tmp/tmpdir/b', 'tmp/tmpdir/c']
+ assert_file_not_exist 'tmp/tmpdir/a'
+ assert_file_not_exist 'tmp/tmpdir/c'
+ Dir.rmdir 'tmp/tmpdir'
+ end
+
+ def test_rm_pathname
+ # pathname
+ touch 'tmp/rmtmp1'
+ touch 'tmp/rmtmp2'
+ touch 'tmp/rmtmp3'
+ touch 'tmp/rmtmp4'
+ assert_nothing_raised {
+ rm_f Pathname.new('tmp/rmtmp1')
+ rm_f [Pathname.new('tmp/rmtmp2'), Pathname.new('tmp/rmtmp3')]
+ }
+ assert_file_not_exist 'tmp/rmtmp1'
+ assert_file_not_exist 'tmp/rmtmp2'
+ assert_file_not_exist 'tmp/rmtmp3'
+ assert_file_exist 'tmp/rmtmp4'
+
+ # [ruby-dev:39345]
+ touch 'tmp/[rmtmp]'
+ FileUtils.rm_f 'tmp/[rmtmp]'
+ assert_file_not_exist 'tmp/[rmtmp]'
+ end
+
+ def test_rm_r
+ check_singleton :rm_r
+
+ my_rm_rf 'tmpdatadir'
+
+ Dir.mkdir 'tmpdatadir'
+ rm_r 'tmpdatadir'
+ assert_file_not_exist 'tmpdatadir'
+
+ Dir.mkdir 'tmpdatadir'
+ rm_r 'tmpdatadir/'
+ assert_file_not_exist 'tmpdatadir'
+
+ Dir.mkdir 'tmp/tmpdir'
+ rm_r 'tmp/tmpdir/'
+ assert_file_not_exist 'tmp/tmpdir'
+ assert_file_exist 'tmp'
+
+ Dir.mkdir 'tmp/tmpdir'
+ rm_r 'tmp/tmpdir'
+ assert_file_not_exist 'tmp/tmpdir'
+ assert_file_exist 'tmp'
+
+ Dir.mkdir 'tmp/tmpdir'
+ File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' }
+ File.open('tmp/tmpdir/b', 'w') {|f| f.puts 'dummy' }
+ File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' }
+ rm_r 'tmp/tmpdir'
+ assert_file_not_exist 'tmp/tmpdir'
+ assert_file_exist 'tmp'
+
+ Dir.mkdir 'tmp/tmpdir'
+ File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' }
+ File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' }
+ rm_r ['tmp/tmpdir/a', 'tmp/tmpdir/b', 'tmp/tmpdir/c'], :force => true
+ assert_file_not_exist 'tmp/tmpdir/a'
+ assert_file_not_exist 'tmp/tmpdir/c'
+ Dir.rmdir 'tmp/tmpdir'
+ end
+
+ def test_rm_r_symlink
+ # [ruby-talk:94635] a symlink to the directory
+ Dir.mkdir 'tmp/tmpdir'
+ File.symlink '..', 'tmp/tmpdir/symlink_to_dir'
+ rm_r 'tmp/tmpdir'
+ assert_file_not_exist 'tmp/tmpdir'
+ assert_file_exist 'tmp'
+ end if have_symlink?
+
+ def test_rm_r_pathname
+ # pathname
+ Dir.mkdir 'tmp/tmpdir1'; touch 'tmp/tmpdir1/tmp'
+ Dir.mkdir 'tmp/tmpdir2'; touch 'tmp/tmpdir2/tmp'
+ Dir.mkdir 'tmp/tmpdir3'; touch 'tmp/tmpdir3/tmp'
+ assert_nothing_raised {
+ rm_r Pathname.new('tmp/tmpdir1')
+ rm_r [Pathname.new('tmp/tmpdir2'), Pathname.new('tmp/tmpdir3')]
+ }
+ assert_file_not_exist 'tmp/tmpdir1'
+ assert_file_not_exist 'tmp/tmpdir2'
+ assert_file_not_exist 'tmp/tmpdir3'
+ end
+
+ def test_remove_entry_secure
+ check_singleton :remove_entry_secure
+
+ my_rm_rf 'tmpdatadir'
+
+ Dir.mkdir 'tmpdatadir'
+ remove_entry_secure 'tmpdatadir'
+ assert_file_not_exist 'tmpdatadir'
+
+ Dir.mkdir 'tmpdatadir'
+ remove_entry_secure 'tmpdatadir/'
+ assert_file_not_exist 'tmpdatadir'
+
+ Dir.mkdir 'tmp/tmpdir'
+ remove_entry_secure 'tmp/tmpdir/'
+ assert_file_not_exist 'tmp/tmpdir'
+ assert_file_exist 'tmp'
+
+ Dir.mkdir 'tmp/tmpdir'
+ remove_entry_secure 'tmp/tmpdir'
+ assert_file_not_exist 'tmp/tmpdir'
+ assert_file_exist 'tmp'
+
+ Dir.mkdir 'tmp/tmpdir'
+ File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' }
+ File.open('tmp/tmpdir/b', 'w') {|f| f.puts 'dummy' }
+ File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' }
+ remove_entry_secure 'tmp/tmpdir'
+ assert_file_not_exist 'tmp/tmpdir'
+ assert_file_exist 'tmp'
+
+ Dir.mkdir 'tmp/tmpdir'
+ File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' }
+ File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' }
+ remove_entry_secure 'tmp/tmpdir/a', true
+ remove_entry_secure 'tmp/tmpdir/b', true
+ remove_entry_secure 'tmp/tmpdir/c', true
+ assert_file_not_exist 'tmp/tmpdir/a'
+ assert_file_not_exist 'tmp/tmpdir/c'
+ Dir.rmdir 'tmp/tmpdir'
+ end
+
+ def test_remove_entry_secure_symlink
+ # [ruby-talk:94635] a symlink to the directory
+ Dir.mkdir 'tmp/tmpdir'
+ File.symlink '..', 'tmp/tmpdir/symlink_to_dir'
+ remove_entry_secure 'tmp/tmpdir'
+ assert_file_not_exist 'tmp/tmpdir'
+ assert_file_exist 'tmp'
+ end if have_symlink?
+
+ def test_remove_entry_secure_pathname
+ # pathname
+ Dir.mkdir 'tmp/tmpdir1'; touch 'tmp/tmpdir1/tmp'
+ assert_nothing_raised {
+ remove_entry_secure Pathname.new('tmp/tmpdir1')
+ }
+ assert_file_not_exist 'tmp/tmpdir1'
+ end
+
+ def test_with_big_file
+ prepare_big_file
+
+ cp BIGFILE, 'tmp/cpdest'
+ assert_same_file BIGFILE, 'tmp/cpdest'
+ assert cmp(BIGFILE, 'tmp/cpdest'), 'orig != copied'
+
+ mv 'tmp/cpdest', 'tmp/mvdest'
+ assert_same_file BIGFILE, 'tmp/mvdest'
+ assert_file_not_exist 'tmp/cpdest'
+
+ rm 'tmp/mvdest'
+ assert_file_not_exist 'tmp/mvdest'
+ end
+
+ def test_ln
+ TARGETS.each do |fname|
+ ln fname, 'tmp/lndest'
+ assert_same_file fname, 'tmp/lndest'
+ File.unlink 'tmp/lndest'
+ end
+
+ ln TARGETS, 'tmp'
+ TARGETS.each do |fname|
+ assert_same_file fname, 'tmp/' + File.basename(fname)
+ end
+ TARGETS.each do |fname|
+ File.unlink 'tmp/' + File.basename(fname)
+ end
+
+ # src==dest (1) same path
+ touch 'tmp/cptmp'
+ assert_raise(Errno::EEXIST) {
+ ln 'tmp/cptmp', 'tmp/cptmp'
+ }
+ end if have_hardlink?
+
+ def test_ln_symlink
+ touch 'tmp/cptmp'
+ # src==dest (2) symlink and its target
+ File.symlink 'cptmp', 'tmp/symlink'
+ assert_raise(Errno::EEXIST) {
+ ln 'tmp/cptmp', 'tmp/symlink' # normal file -> symlink
+ }
+ assert_raise(Errno::EEXIST) {
+ ln 'tmp/symlink', 'tmp/cptmp' # symlink -> normal file
+ }
+ # src==dest (3) looped symlink
+ File.symlink 'cptmp_symlink', 'tmp/cptmp_symlink'
+ begin
+ ln 'tmp/cptmp_symlink', 'tmp/cptmp_symlink'
+ rescue => err
+ assert_kind_of SystemCallError, err
+ end
+ end if have_symlink?
+
+ def test_ln_pathname
+ # pathname
+ touch 'tmp/lntmp'
+ assert_nothing_raised {
+ ln Pathname.new('tmp/lntmp'), 'tmp/lndesttmp1'
+ ln 'tmp/lntmp', Pathname.new('tmp/lndesttmp2')
+ ln Pathname.new('tmp/lntmp'), Pathname.new('tmp/lndesttmp3')
+ }
+ end if have_hardlink?
+
+ def test_ln_s
+ check_singleton :ln_s
+
+ TARGETS.each do |fname|
+ ln_s fname, 'tmp/lnsdest'
+ assert FileTest.symlink?('tmp/lnsdest'), 'not symlink'
+ assert_equal fname, File.readlink('tmp/lnsdest')
+ rm_f 'tmp/lnsdest'
+ end
+ assert_nothing_raised {
+ ln_s 'symlink', 'tmp/symlink'
+ }
+ assert_symlink 'tmp/symlink'
+
+ # pathname
+ touch 'tmp/lnsdest'
+ assert_nothing_raised {
+ ln_s Pathname.new('lnsdest'), 'tmp/symlink_tmp1'
+ ln_s 'lnsdest', Pathname.new('tmp/symlink_tmp2')
+ ln_s Pathname.new('lnsdest'), Pathname.new('tmp/symlink_tmp3')
+ }
+ end if have_symlink?
+
+ def test_ln_sf
+ check_singleton :ln_sf
+
+ TARGETS.each do |fname|
+ ln_sf fname, 'tmp/lnsdest'
+ assert FileTest.symlink?('tmp/lnsdest'), 'not symlink'
+ assert_equal fname, File.readlink('tmp/lnsdest')
+ ln_sf fname, 'tmp/lnsdest'
+ ln_sf fname, 'tmp/lnsdest'
+ end
+ assert_nothing_raised {
+ ln_sf 'symlink', 'tmp/symlink'
+ }
+
+ # pathname
+ touch 'tmp/lns_dest'
+ assert_nothing_raised {
+ ln_sf Pathname.new('lns_dest'), 'tmp/symlink_tmp1'
+ ln_sf 'lns_dest', Pathname.new('tmp/symlink_tmp2')
+ ln_sf Pathname.new('lns_dest'), Pathname.new('tmp/symlink_tmp3')
+ }
+ end if have_symlink?
+
+ def test_mkdir
+ check_singleton :mkdir
+
+ my_rm_rf 'tmpdatadir'
+ mkdir 'tmpdatadir'
+ assert_directory 'tmpdatadir'
+ Dir.rmdir 'tmpdatadir'
+
+ mkdir 'tmpdatadir/'
+ assert_directory 'tmpdatadir'
+ Dir.rmdir 'tmpdatadir'
+
+ mkdir 'tmp/mkdirdest'
+ assert_directory 'tmp/mkdirdest'
+ Dir.rmdir 'tmp/mkdirdest'
+
+ mkdir 'tmp/tmp', :mode => 0700
+ assert_directory 'tmp/tmp'
+ assert_filemode 0700, 'tmp/tmp', mask: 0777 if have_file_perm?
+ Dir.rmdir 'tmp/tmp'
+
+ # EISDIR on OS X, FreeBSD; EEXIST on Linux; Errno::EACCES on Windows
+ assert_raise(Errno::EISDIR, Errno::EEXIST, Errno::EACCES) {
+ mkdir '/'
+ }
+ end
+
+ def test_mkdir_file_perm
+ mkdir 'tmp/tmp', :mode => 07777
+ assert_directory 'tmp/tmp'
+ assert_filemode 07777, 'tmp/tmp'
+ Dir.rmdir 'tmp/tmp'
+ end if have_file_perm?
+
+ def test_mkdir_lf_in_path
+ mkdir "tmp-first-line\ntmp-second-line"
+ assert_directory "tmp-first-line\ntmp-second-line"
+ Dir.rmdir "tmp-first-line\ntmp-second-line"
+ end if lf_in_path_allowed?
+
+ def test_mkdir_pathname
+ # pathname
+ assert_nothing_raised {
+ mkdir Pathname.new('tmp/tmpdirtmp')
+ mkdir [Pathname.new('tmp/tmpdirtmp2'), Pathname.new('tmp/tmpdirtmp3')]
+ }
+ end
+
+ def test_mkdir_p
+ check_singleton :mkdir_p
+
+ dirs = %w(
+ tmpdir/dir/
+ tmpdir/dir/./
+ tmpdir/dir/./.././dir/
+ tmpdir/a
+ tmpdir/a/
+ tmpdir/a/b
+ tmpdir/a/b/
+ tmpdir/a/b/c/
+ tmpdir/a/b/c
+ tmpdir/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
+ tmpdir/a/a
+ )
+ my_rm_rf 'tmpdir'
+ dirs.each do |d|
+ mkdir_p d
+ assert_directory d
+ assert_file_not_exist "#{d}/a"
+ assert_file_not_exist "#{d}/b"
+ assert_file_not_exist "#{d}/c"
+ my_rm_rf 'tmpdir'
+ end
+ dirs.each do |d|
+ mkdir_p d
+ assert_directory d
+ end
+ rm_rf 'tmpdir'
+ dirs.each do |d|
+ mkdir_p "#{Dir.pwd}/#{d}"
+ assert_directory d
+ end
+ rm_rf 'tmpdir'
+
+ mkdir_p 'tmp/tmp/tmp', :mode => 0700
+ assert_directory 'tmp/tmp'
+ assert_directory 'tmp/tmp/tmp'
+ assert_filemode 0700, 'tmp/tmp', mask: 0777 if have_file_perm?
+ assert_filemode 0700, 'tmp/tmp/tmp', mask: 0777 if have_file_perm?
+ rm_rf 'tmp/tmp'
+
+ mkdir_p 'tmp/tmp', :mode => 0
+ assert_directory 'tmp/tmp'
+ assert_filemode 0, 'tmp/tmp', mask: 0777 if have_file_perm?
+ # DO NOT USE rm_rf here.
+ # (rm(1) try to chdir to parent directory, it fails to remove directory.)
+ Dir.rmdir 'tmp/tmp'
+ Dir.rmdir 'tmp'
+
+ mkdir_p '/'
+ end
+
+ def test_mkdir_p_file_perm
+ mkdir_p 'tmp/tmp/tmp', :mode => 07777
+ assert_directory 'tmp/tmp/tmp'
+ assert_filemode 07777, 'tmp/tmp/tmp'
+ Dir.rmdir 'tmp/tmp/tmp'
+ Dir.rmdir 'tmp/tmp'
+ end if have_file_perm?
+
+ def test_mkdir_p_pathname
+ # pathname
+ assert_nothing_raised {
+ mkdir_p Pathname.new('tmp/tmp/tmp')
+ }
+ end
+
+ def test_install
+ check_singleton :install
+
+ File.open('tmp/aaa', 'w') {|f| f.puts 'aaa' }
+ File.open('tmp/bbb', 'w') {|f| f.puts 'bbb' }
+ install 'tmp/aaa', 'tmp/bbb', :mode => 0600
+ assert_equal "aaa\n", File.read('tmp/bbb')
+ assert_filemode 0600, 'tmp/bbb', mask: 0777 if have_file_perm?
+
+ t = File.mtime('tmp/bbb')
+ install 'tmp/aaa', 'tmp/bbb'
+ assert_equal "aaa\n", File.read('tmp/bbb')
+ assert_filemode 0600, 'tmp/bbb', mask: 0777 if have_file_perm?
+ assert_equal_time t, File.mtime('tmp/bbb')
+
+ File.unlink 'tmp/aaa'
+ File.unlink 'tmp/bbb'
+
+ # src==dest (1) same path
+ touch 'tmp/cptmp'
+ assert_raise(ArgumentError) {
+ install 'tmp/cptmp', 'tmp/cptmp'
+ }
+ end
+
+ def test_install_symlink
+ touch 'tmp/cptmp'
+ # src==dest (2) symlink and its target
+ File.symlink 'cptmp', 'tmp/cptmp_symlink'
+ assert_raise(ArgumentError) {
+ install 'tmp/cptmp', 'tmp/cptmp_symlink'
+ }
+ assert_raise(ArgumentError) {
+ install 'tmp/cptmp_symlink', 'tmp/cptmp'
+ }
+ # src==dest (3) looped symlink
+ File.symlink 'symlink', 'tmp/symlink'
+ assert_raise(Errno::ELOOP) {
+ # File#install invokes open(2), always ELOOP must be raised
+ install 'tmp/symlink', 'tmp/symlink'
+ }
+ end if have_symlink?
+
+ def test_install_pathname
+ # pathname
+ assert_nothing_raised {
+ rm_f 'tmp/a'; touch 'tmp/a'
+ install 'tmp/a', Pathname.new('tmp/b')
+ rm_f 'tmp/a'; touch 'tmp/a'
+ install Pathname.new('tmp/a'), 'tmp/b'
+ rm_f 'tmp/a'; touch 'tmp/a'
+ install Pathname.new('tmp/a'), Pathname.new('tmp/b')
+ rm_f 'tmp/a'
+ touch 'tmp/a'
+ touch 'tmp/b'
+ mkdir 'tmp/dest'
+ install [Pathname.new('tmp/a'), Pathname.new('tmp/b')], 'tmp/dest'
+ my_rm_rf 'tmp/dest'
+ mkdir 'tmp/dest'
+ install [Pathname.new('tmp/a'), Pathname.new('tmp/b')], Pathname.new('tmp/dest')
+ }
+ end
+
+ def test_chmod
+ check_singleton :chmod
+
+ touch 'tmp/a'
+ chmod 0700, 'tmp/a'
+ assert_filemode 0700, 'tmp/a'
+ chmod 0500, 'tmp/a'
+ assert_filemode 0500, 'tmp/a'
+ end if have_file_perm?
+
+ def test_chmod_symbol_mode
+ check_singleton :chmod
+
+ touch 'tmp/a'
+ chmod "u=wrx,g=rx,o=x", 'tmp/a'
+ assert_filemode 0751, 'tmp/a'
+ chmod "g+w-x", 'tmp/a'
+ assert_filemode 0761, 'tmp/a'
+ chmod "o+r,g=o+w,o-r,u-o", 'tmp/a' # 761 => 763 => 773 => 771 => 671
+ assert_filemode 0671, 'tmp/a'
+ chmod "go=u", 'tmp/a'
+ assert_filemode 0666, 'tmp/a'
+ chmod "u=wrx,g=,o=", 'tmp/a'
+ assert_filemode 0700, 'tmp/a'
+ chmod "u=rx,go=", 'tmp/a'
+ assert_filemode 0500, 'tmp/a'
+ chmod "+wrx", 'tmp/a'
+ assert_filemode 0777, 'tmp/a'
+ chmod "u+s,o=s", 'tmp/a'
+ assert_filemode 04770, 'tmp/a'
+ chmod "u-w,go-wrx", 'tmp/a'
+ assert_filemode 04500, 'tmp/a'
+ chmod "+s", 'tmp/a'
+ assert_filemode 06500, 'tmp/a'
+
+ # FreeBSD ufs and tmpfs don't allow to change sticky bit against
+ # regular file. It's slightly strange. Anyway it's no effect bit.
+ # see /usr/src/sys/ufs/ufs/ufs_chmod()
+ # NetBSD, OpenBSD, Solaris, and AIX also deny it.
+ if /freebsd|netbsd|openbsd|solaris|aix/ !~ RUBY_PLATFORM
+ chmod "u+t,o+t", 'tmp/a'
+ assert_filemode 07500, 'tmp/a'
+ chmod "a-t,a-s", 'tmp/a'
+ assert_filemode 0500, 'tmp/a'
+ end
+
+ assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) {
+ chmod "a", 'tmp/a'
+ }
+
+ assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) {
+ chmod "x+a", 'tmp/a'
+ }
+
+ assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) {
+ chmod "u+z", 'tmp/a'
+ }
+
+ assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) {
+ chmod ",+x", 'tmp/a'
+ }
+
+ assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) {
+ chmod "755", 'tmp/a'
+ }
+
+ end if have_file_perm?
+
+
+ def test_chmod_R
+ check_singleton :chmod_R
+
+ mkdir_p 'tmp/dir/dir'
+ touch %w( tmp/dir/file tmp/dir/dir/file )
+ chmod_R 0700, 'tmp/dir'
+ assert_filemode 0700, 'tmp/dir', mask: 0777
+ assert_filemode 0700, 'tmp/dir/file', mask: 0777
+ assert_filemode 0700, 'tmp/dir/dir', mask: 0777
+ assert_filemode 0700, 'tmp/dir/dir/file', mask: 0777
+ chmod_R 0500, 'tmp/dir'
+ assert_filemode 0500, 'tmp/dir', mask: 0777
+ assert_filemode 0500, 'tmp/dir/file', mask: 0777
+ assert_filemode 0500, 'tmp/dir/dir', mask: 0777
+ assert_filemode 0500, 'tmp/dir/dir/file', mask: 0777
+ chmod_R 0700, 'tmp/dir' # to remove
+ end if have_file_perm?
+
+ def test_chmod_symbol_mode_R
+ check_singleton :chmod_R
+
+ mkdir_p 'tmp/dir/dir'
+ touch %w( tmp/dir/file tmp/dir/dir/file )
+ chmod_R "u=wrx,g=,o=", 'tmp/dir'
+ assert_filemode 0700, 'tmp/dir', mask: 0777
+ assert_filemode 0700, 'tmp/dir/file', mask: 0777
+ assert_filemode 0700, 'tmp/dir/dir', mask: 0777
+ assert_filemode 0700, 'tmp/dir/dir/file', mask: 0777
+ chmod_R "u=xr,g+X,o=", 'tmp/dir'
+ assert_filemode 0510, 'tmp/dir', mask: 0777
+ assert_filemode 0500, 'tmp/dir/file', mask: 0777
+ assert_filemode 0510, 'tmp/dir/dir', mask: 0777
+ assert_filemode 0500, 'tmp/dir/dir/file', mask: 0777
+ chmod_R 0700, 'tmp/dir' # to remove
+ end if have_file_perm?
+
+ def test_chmod_verbose
+ check_singleton :chmod
+
+ assert_output_lines(["chmod 700 tmp/a", "chmod 500 tmp/a"]) {
+ touch 'tmp/a'
+ chmod 0700, 'tmp/a', verbose: true
+ assert_filemode 0700, 'tmp/a', mask: 0777
+ chmod 0500, 'tmp/a', verbose: true
+ assert_filemode 0500, 'tmp/a', mask: 0777
+ }
+ end if have_file_perm?
+
+ def test_s_chmod_verbose
+ assert_output_lines(["chmod 700 tmp/a"], FileUtils) {
+ touch 'tmp/a'
+ FileUtils.chmod 0700, 'tmp/a', verbose: true
+ assert_filemode 0700, 'tmp/a', mask: 0777
+ }
+ end if have_file_perm?
+
+ def test_chown
+ check_singleton :chown
+
+ return unless @groups[1]
+
+ input_group_1 = @groups[0]
+ assert_output_lines([]) {
+ touch 'tmp/a'
+ # integer input for group, nil for user
+ chown nil, input_group_1, 'tmp/a'
+ assert_ownership_group @groups[0], 'tmp/a'
+ }
+
+ input_group_2 = Etc.getgrgid(@groups[1]).name
+ assert_output_lines([]) {
+ touch 'tmp/b'
+ # string input for group, -1 for user
+ chown(-1, input_group_2, 'tmp/b')
+ assert_ownership_group @groups[1], 'tmp/b'
+ }
+ end if have_file_perm?
+
+ def test_chown_verbose
+ assert_output_lines(["chown :#{@groups[0]} tmp/a1 tmp/a2"]) {
+ touch 'tmp/a1'
+ touch 'tmp/a2'
+ chown nil, @groups[0], ['tmp/a1', 'tmp/a2'], verbose: true
+ assert_ownership_group @groups[0], 'tmp/a1'
+ assert_ownership_group @groups[0], 'tmp/a2'
+ }
+ end if have_file_perm?
+
+ def test_chown_noop
+ return unless @groups[1]
+ assert_output_lines([]) {
+ touch 'tmp/a'
+ chown nil, @groups[0], 'tmp/a', :noop => false
+ assert_ownership_group @groups[0], 'tmp/a'
+ chown nil, @groups[1], 'tmp/a', :noop => true
+ assert_ownership_group @groups[0], 'tmp/a'
+ chown nil, @groups[1], 'tmp/a'
+ assert_ownership_group @groups[1], 'tmp/a'
+ }
+ end if have_file_perm?
+
+ if have_file_perm?
+ def test_chown_error
+ uid, = distinct_uids(1)
+ return unless uid
+
+ touch 'tmp/a'
+
+ # getpwnam("") on Mac OS X doesn't err.
+ # passwd & group databases format is colon-separated, so user &
+ # group name can't contain a colon.
+
+ assert_raise_with_message(ArgumentError, "can't find user for :::") {
+ chown ":::", @groups[0], 'tmp/a'
+ }
+
+ assert_raise_with_message(ArgumentError, "can't find group for :::") {
+ chown uid, ":::", 'tmp/a'
+ }
+
+ assert_raise_with_message(Errno::ENOENT, /No such file or directory/) {
+ chown nil, @groups[0], ''
+ }
+ end
+
+ def test_chown_dir_group_ownership_not_recursive
+ return unless @groups[1]
+
+ input_group_1 = @groups[0]
+ input_group_2 = @groups[1]
+ assert_output_lines([]) {
+ mkdir 'tmp/dir'
+ touch 'tmp/dir/a'
+ chown nil, input_group_1, ['tmp/dir', 'tmp/dir/a']
+ assert_ownership_group @groups[0], 'tmp/dir'
+ assert_ownership_group @groups[0], 'tmp/dir/a'
+ chown nil, input_group_2, 'tmp/dir'
+ assert_ownership_group @groups[1], 'tmp/dir'
+ # Make sure FileUtils.chown does not chown recursively
+ assert_ownership_group @groups[0], 'tmp/dir/a'
+ }
+ end
+
+ def test_chown_R
+ check_singleton :chown_R
+
+ return unless @groups[1]
+
+ input_group_1 = @groups[0]
+ input_group_2 = @groups[1]
+ assert_output_lines([]) {
+ list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c']
+ mkdir_p 'tmp/dir/a/b/c'
+ touch 'tmp/d'
+ # string input
+ chown_R nil, input_group_1, 'tmp/dir'
+ list.each {|dir|
+ assert_ownership_group @groups[0], dir
+ }
+ chown_R nil, input_group_1, 'tmp/d'
+ assert_ownership_group @groups[0], 'tmp/d'
+ # list input
+ chown_R nil, input_group_2, ['tmp/dir', 'tmp/d']
+ list += ['tmp/d']
+ list.each {|dir|
+ assert_ownership_group @groups[1], dir
+ }
+ }
+ end
+
+ def test_chown_R_verbose
+ assert_output_lines(["chown -R :#{@groups[0]} tmp/dir tmp/d"]) {
+ list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c']
+ mkdir_p 'tmp/dir/a/b/c'
+ touch 'tmp/d'
+ chown_R nil, @groups[0], ['tmp/dir', 'tmp/d'], :verbose => true
+ list.each {|dir|
+ assert_ownership_group @groups[0], dir
+ }
+ }
+ end
+
+ def test_chown_R_noop
+ return unless @groups[1]
+
+ assert_output_lines([]) {
+ list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c']
+ mkdir_p 'tmp/dir/a/b/c'
+ chown_R nil, @groups[0], 'tmp/dir', :noop => false
+ list.each {|dir|
+ assert_ownership_group @groups[0], dir
+ }
+ chown_R nil, @groups[1], 'tmp/dir', :noop => true
+ list.each {|dir|
+ assert_ownership_group @groups[0], dir
+ }
+ }
+ end
+
+ def test_chown_R_force
+ assert_output_lines([]) {
+ list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c']
+ mkdir_p 'tmp/dir/a/b/c'
+ assert_raise_with_message(Errno::ENOENT, /No such file or directory/) {
+ chown_R nil, @groups[0], ['tmp/dir', 'invalid'], :force => false
+ }
+ chown_R nil, @groups[0], ['tmp/dir', 'invalid'], :force => true
+ list.each {|dir|
+ assert_ownership_group @groups[0], dir
+ }
+ }
+ end
+
+ if root_in_posix?
+ def test_chown_with_root
+ uid_1, uid_2 = distinct_uids(2)
+ return unless uid_1 and uid_2
+
+ gid = @groups[0] # Most of the time, root only has one group
+
+ files = ['tmp/a1', 'tmp/a2']
+ files.each {|file| touch file}
+ [uid_1, uid_2].each {|uid|
+ assert_output_lines(["chown #{uid}:#{gid} tmp/a1 tmp/a2"]) {
+ chown uid, gid, files, verbose: true
+ files.each {|file|
+ assert_ownership_group gid, file
+ assert_ownership_user uid, file
+ }
+ }
+ }
+ end
+
+ def test_chown_dir_user_ownership_not_recursive_with_root
+ uid_1, uid_2 = distinct_uids(2)
+ return unless uid_1 and uid_2
+
+ assert_output_lines([]) {
+ mkdir 'tmp/dir'
+ touch 'tmp/dir/a'
+ chown uid_1, nil, ['tmp/dir', 'tmp/dir/a']
+ assert_ownership_user uid_1, 'tmp/dir'
+ assert_ownership_user uid_1, 'tmp/dir/a'
+ chown uid_2, nil, 'tmp/dir'
+ assert_ownership_user uid_2, 'tmp/dir'
+ # Make sure FileUtils.chown does not chown recursively
+ assert_ownership_user uid_1, 'tmp/dir/a'
+ }
+ end
+
+ def test_chown_R_with_root
+ uid_1, uid_2 = distinct_uids(2)
+ return unless uid_1 and uid_2
+
+ assert_output_lines([]) {
+ list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c']
+ mkdir_p 'tmp/dir/a/b/c'
+ touch 'tmp/d'
+ # string input
+ chown_R uid_1, nil, 'tmp/dir'
+ list.each {|dir|
+ assert_ownership_user uid_1, dir
+ }
+ chown_R uid_1, nil, 'tmp/d'
+ assert_ownership_user uid_1, 'tmp/d'
+ # list input
+ chown_R uid_2, nil, ['tmp/dir', 'tmp/d']
+ list += ['tmp/d']
+ list.each {|dir|
+ assert_ownership_user uid_2, dir
+ }
+ }
+ end
+ else
+ def test_chown_without_permission
+ uid_1, uid_2 = distinct_uids(2)
+ return unless uid_1 and uid_2
+
+ touch 'tmp/a'
+ assert_raise(Errno::EPERM) {
+ chown uid_1, nil, 'tmp/a'
+ chown uid_2, nil, 'tmp/a'
+ }
+ end
+
+ def test_chown_R_without_permission
+ uid_1, uid_2 = distinct_uids(2)
+ return unless uid_1 and uid_2
+
+ touch 'tmp/a'
+ exception = assert_raise(Errno::EPERM) {
+ chown_R uid_1, nil, 'tmp/a'
+ chown_R uid_2, nil, 'tmp/a'
+ }
+ end
+ end
+ end
+
+ def test_copy_entry
+ check_singleton :copy_entry
+
+ each_srcdest do |srcpath, destpath|
+ copy_entry srcpath, destpath
+ assert_same_file srcpath, destpath
+ assert_equal File.stat(srcpath).ftype, File.stat(destpath).ftype
+ end
+ end
+
+ def test_copy_entry_symlink
+ # root is a symlink
+ File.symlink 'somewhere', 'tmp/symsrc'
+ copy_entry 'tmp/symsrc', 'tmp/symdest'
+ assert_symlink 'tmp/symdest'
+ assert_equal 'somewhere', File.readlink('tmp/symdest')
+
+ # content is a symlink
+ mkdir 'tmp/dir'
+ File.symlink 'somewhere', 'tmp/dir/sym'
+ copy_entry 'tmp/dir', 'tmp/dirdest'
+ assert_directory 'tmp/dirdest'
+ assert_not_symlink 'tmp/dirdest'
+ assert_symlink 'tmp/dirdest/sym'
+ assert_equal 'somewhere', File.readlink('tmp/dirdest/sym')
+ end if have_symlink?
+
+ def test_copy_file
+ check_singleton :copy_file
+
+ each_srcdest do |srcpath, destpath|
+ copy_file srcpath, destpath
+ assert_same_file srcpath, destpath
+ end
+ end
+
+ def test_copy_stream
+ check_singleton :copy_stream
+ # IO
+ each_srcdest do |srcpath, destpath|
+ File.open(srcpath, 'rb') {|src|
+ File.open(destpath, 'wb') {|dest|
+ copy_stream src, dest
+ }
+ }
+ assert_same_file srcpath, destpath
+ end
+ end
+
+ def test_copy_stream_duck
+ check_singleton :copy_stream
+ # duck typing test [ruby-dev:25369]
+ each_srcdest do |srcpath, destpath|
+ File.open(srcpath, 'rb') {|src|
+ File.open(destpath, 'wb') {|dest|
+ copy_stream Stream.new(src), Stream.new(dest)
+ }
+ }
+ assert_same_file srcpath, destpath
+ end
+ end
+
+ def test_remove_file
+ check_singleton :remove_file
+ File.open('data/tmp', 'w') {|f| f.puts 'dummy' }
+ remove_file 'data/tmp'
+ assert_file_not_exist 'data/tmp'
+ end
+
+ def test_remove_file_file_perm
+ File.open('data/tmp', 'w') {|f| f.puts 'dummy' }
+ File.chmod 0, 'data/tmp'
+ remove_file 'data/tmp'
+ assert_file_not_exist 'data/tmp'
+ end if have_file_perm?
+
+ def test_remove_dir
+ check_singleton :remove_dir
+ Dir.mkdir 'data/tmpdir'
+ File.open('data/tmpdir/a', 'w') {|f| f.puts 'dummy' }
+ remove_dir 'data/tmpdir'
+ assert_file_not_exist 'data/tmpdir'
+ end
+
+ def test_remove_dir_file_perm
+ Dir.mkdir 'data/tmpdir'
+ File.chmod 0555, 'data/tmpdir'
+ remove_dir 'data/tmpdir'
+ assert_file_not_exist 'data/tmpdir'
+ end if have_file_perm?
+
+ def test_compare_file
+ check_singleton :compare_file
+ # FIXME
+ end
+
+ def test_compare_stream
+ check_singleton :compare_stream
+ # FIXME
+ end
+
+ class Stream
+ def initialize(f)
+ @f = f
+ end
+
+ def read(*args)
+ @f.read(*args)
+ end
+
+ def write(str)
+ @f.write str
+ end
+ end
+
+ def test_uptodate?
+ check_singleton :uptodate?
+ prepare_time_data
+ Dir.chdir('data') {
+ assert( uptodate?('newest', %w(old newer notexist)) )
+ assert( ! uptodate?('newer', %w(old newest notexist)) )
+ assert( ! uptodate?('notexist', %w(old newest newer)) )
+ }
+
+ # pathname
+ touch 'tmp/a'
+ touch 'tmp/b'
+ touch 'tmp/c'
+ assert_nothing_raised {
+ uptodate? Pathname.new('tmp/a'), ['tmp/b', 'tmp/c']
+ uptodate? 'tmp/a', [Pathname.new('tmp/b'), 'tmp/c']
+ uptodate? 'tmp/a', ['tmp/b', Pathname.new('tmp/c')]
+ uptodate? Pathname.new('tmp/a'), [Pathname.new('tmp/b'), Pathname.new('tmp/c')]
+ }
+ # [Bug #6708] [ruby-core:46256]
+ assert_raise_with_message(ArgumentError, "wrong number of arguments (3 for 2)") {
+ uptodate?('new',['old', 'oldest'], {})
+ }
+ end
+
+ def test_cd
+ check_singleton :cd
+ end
+
+ def test_chdir
+ check_singleton :chdir
+ end
+
+ def test_getwd
+ check_singleton :getwd
+ end
+
+ def test_identical?
+ check_singleton :identical?
+ end
+
+ def test_link
+ check_singleton :link
+ end
+
+ def test_makedirs
+ check_singleton :makedirs
+ end
+
+ def test_mkpath
+ check_singleton :mkpath
+ end
+
+ def test_move
+ check_singleton :move
+ end
+
+ def test_rm_rf
+ check_singleton :rm_rf
+
+ return if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ mkdir 'tmpdatadir'
+ chmod 700, 'tmpdatadir'
+ rm_rf 'tmpdatadir'
+
+ assert_file_not_exist 'tmpdatadir'
+ end
+
+ def test_rmdir
+ check_singleton :rmdir
+
+ begin
+ Dir.rmdir '/'
+ rescue Errno::ENOTEMPTY
+ rescue => e
+ assert_raise(e.class) {
+ # Dir.rmdir('') raises Errno::ENOENT.
+ # FileUtils#rmdir ignores it.
+ # And this test failed as expected.
+ rmdir '/'
+ }
+ end
+
+ subdir = 'data/sub/dir'
+ mkdir_p(subdir)
+ assert_nothing_raised(Errno::ENOENT) {
+ rmdir(subdir, parents: true)
+ }
+ assert_file_not_exist(subdir)
+ assert_file_not_exist('data/sub')
+ assert_directory('data')
+ end
+
+ def test_rmtree
+ check_singleton :rmtree
+ end
+
+ def test_safe_unlink
+ check_singleton :safe_unlink
+ end
+
+ def test_symlink
+ check_singleton :symlink
+ end
+
+ def test_touch
+ check_singleton :touch
+ end
+
+ def test_collect_methods
+ end
+
+ def test_commands
+ end
+
+ def test_have_option?
+ end
+
+ def test_options
+ end
+
+ def test_options_of
+ end
+
+end
diff --git a/jni/ruby/test/fileutils/test_nowrite.rb b/jni/ruby/test/fileutils/test_nowrite.rb
new file mode 100644
index 0000000..c96d462
--- /dev/null
+++ b/jni/ruby/test/fileutils/test_nowrite.rb
@@ -0,0 +1,17 @@
+# $Id: test_nowrite.rb 39544 2013-03-01 02:09:42Z drbrain $
+
+require 'fileutils'
+require 'test/unit'
+require_relative 'visibility_tests'
+
+class TestFileUtilsNoWrite < Test::Unit::TestCase
+
+ include FileUtils::NoWrite
+ include TestFileUtils::Visibility
+
+ def setup
+ super
+ @fu_module = FileUtils::NoWrite
+ end
+
+end
diff --git a/jni/ruby/test/fileutils/test_verbose.rb b/jni/ruby/test/fileutils/test_verbose.rb
new file mode 100644
index 0000000..6453788
--- /dev/null
+++ b/jni/ruby/test/fileutils/test_verbose.rb
@@ -0,0 +1,17 @@
+# $Id: test_verbose.rb 39544 2013-03-01 02:09:42Z drbrain $
+
+require 'test/unit'
+require 'fileutils'
+require_relative 'visibility_tests'
+
+class TestFileUtilsVerbose < Test::Unit::TestCase
+
+ include FileUtils::Verbose
+ include TestFileUtils::Visibility
+
+ def setup
+ super
+ @fu_module = FileUtils::Verbose
+ end
+
+end
diff --git a/jni/ruby/test/fileutils/visibility_tests.rb b/jni/ruby/test/fileutils/visibility_tests.rb
new file mode 100644
index 0000000..a140614
--- /dev/null
+++ b/jni/ruby/test/fileutils/visibility_tests.rb
@@ -0,0 +1,41 @@
+require 'test/unit'
+require 'fileutils'
+
+class TestFileUtils < Test::Unit::TestCase
+end
+
+##
+# These tests are reused in the FileUtils::Verbose, FileUtils::NoWrite and
+# FileUtils::DryRun tests
+
+module TestFileUtils::Visibility
+
+ FileUtils::METHODS.each do |m|
+ define_method "test_singleton_visibility_#{m}" do
+ assert @fu_module.respond_to?(m, true),
+ "FileUtils::Verbose.#{m} is not defined"
+ assert @fu_module.respond_to?(m, false),
+ "FileUtils::Verbose.#{m} is not public"
+ end
+
+ define_method "test_visibility_#{m}" do
+ assert respond_to?(m, true),
+ "FileUtils::Verbose\##{m} is not defined"
+ assert @fu_module.private_method_defined?(m),
+ "FileUtils::Verbose\##{m} is not private"
+ end
+ end
+
+ FileUtils::StreamUtils_.private_instance_methods.each do |m|
+ define_method "test_singleton_visibility_#{m}" do
+ assert @fu_module.respond_to?(m, true),
+ "FileUtils::Verbose\##{m} is not defined"
+ end
+
+ define_method "test_visibility_#{m}" do
+ assert respond_to?(m, true),
+ "FileUtils::Verbose\##{m} is not defined"
+ end
+ end
+
+end
diff --git a/jni/ruby/test/gdbm/test_gdbm.rb b/jni/ruby/test/gdbm/test_gdbm.rb
new file mode 100644
index 0000000..d57e952
--- /dev/null
+++ b/jni/ruby/test/gdbm/test_gdbm.rb
@@ -0,0 +1,719 @@
+begin
+ require 'gdbm'
+rescue LoadError
+end
+
+if defined? GDBM
+ require 'test/unit'
+ require 'tmpdir'
+ require 'fileutils'
+
+ class TestGDBM_RDONLY < Test::Unit::TestCase
+ def TestGDBM_RDONLY.uname_s
+ require 'rbconfig'
+ case RbConfig::CONFIG['target_os']
+ when 'cygwin'
+ require 'etc'
+ Etc.uname[:sysname]
+ else
+ RbConfig::CONFIG['target_os']
+ end
+ end
+ SYSTEM = uname_s
+
+ def setup
+ @tmpdir = Dir.mktmpdir("tmptest_gdbm")
+ @prefix = "tmptest_gdbm_#{$$}"
+ @path = "#{@tmpdir}/#{@prefix}_"
+
+ # prepare to make readonly GDBM file
+ GDBM.open("#{@tmpdir}/#{@prefix}_rdonly", 0400) {|gdbm|
+ gdbm['foo'] = 'FOO'
+ }
+ assert_instance_of(GDBM, @gdbm_rdonly = GDBM.new("#{@tmpdir}/#{@prefix}_rdonly", nil))
+ end
+ def teardown
+ assert_nil(@gdbm_rdonly.close)
+ ObjectSpace.each_object(GDBM) do |obj|
+ obj.close unless obj.closed?
+ end
+ FileUtils.remove_entry_secure @tmpdir
+ end
+
+ def test_delete_rdonly
+ if /^CYGWIN_9/ !~ SYSTEM
+ assert_raise(GDBMError) {
+ @gdbm_rdonly.delete("foo")
+ }
+
+ assert_nil(@gdbm_rdonly.delete("bar"))
+ end
+ end
+ end
+
+ class TestGDBM < Test::Unit::TestCase
+ SYSTEM = TestGDBM_RDONLY::SYSTEM
+
+ def setup
+ @tmpdir = Dir.mktmpdir("tmptest_gdbm")
+ @prefix = "tmptest_gdbm_#{$$}"
+ @path = "#{@tmpdir}/#{@prefix}_"
+ assert_instance_of(GDBM, @gdbm = GDBM.new(@path))
+ end
+ def teardown
+ assert_nil(@gdbm.close)
+ ObjectSpace.each_object(GDBM) do |obj|
+ obj.close unless obj.closed?
+ end
+ FileUtils.remove_entry_secure @tmpdir
+ end
+
+ def check_size(expect, gdbm=@gdbm)
+ assert_equal(expect, gdbm.size)
+ n = 0
+ gdbm.each { n+=1 }
+ assert_equal(expect, n)
+ if expect == 0
+ assert_equal(true, gdbm.empty?)
+ else
+ assert_equal(false, gdbm.empty?)
+ end
+ end
+
+ def test_s_new_has_no_block
+ # GDBM.new ignore the block
+ foo = true
+ assert_instance_of(GDBM, gdbm = GDBM.new("#{@tmpdir}/#{@prefix}") { foo = false })
+ assert_equal(foo, true)
+ assert_nil(gdbm.close)
+ end
+ def test_s_open_create_new
+ return if /^CYGWIN_9/ =~ SYSTEM
+
+ save_mask = File.umask(0)
+ begin
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}"))
+ gdbm.close
+ assert_equal(File.stat("#{@tmpdir}/#{@prefix}").mode & 0777, 0666) unless /mswin|mingw/ =~ RUBY_PLATFORM
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}2", 0644))
+ gdbm.close
+ assert_equal(File.stat("#{@tmpdir}/#{@prefix}2").mode & 0777, 0644)
+ ensure
+ File.umask save_mask
+ end
+ end
+ def test_s_open_no_create
+ skip "gdbm_open(GDBM_WRITER) is broken on libgdbm 1.8.0" if /1\.8\.0/ =~ GDBM::VERSION
+ assert_nil(gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", nil))
+ ensure
+ gdbm.close if gdbm
+ end
+ def test_s_open_3rd_arg
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", 0644,
+ GDBM::FAST))
+ gdbm.close
+
+ # gdbm 1.8.0 specific
+ if defined? GDBM::SYNC
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", 0644,
+ GDBM::SYNC))
+ gdbm.close
+ end
+ # gdbm 1.8.0 specific
+ if defined? GDBM::NOLOCK
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", 0644,
+ GDBM::NOLOCK))
+ gdbm.close
+ end
+ end
+ def test_s_open_with_block
+ assert_equal(GDBM.open("#{@tmpdir}/#{@prefix}") { :foo }, :foo)
+ end
+
+ def open_db_child(dbname, *opts)
+ opts = [0644, *opts].map(&:inspect).join(', ')
+ args = [EnvUtil.rubybin, "-rgdbm", "-e", <<-SRC, dbname]
+ STDOUT.sync = true
+ gdbm = GDBM.open(ARGV.shift, #{opts})
+ puts gdbm.class
+ gets
+ SRC
+ IO.popen(args, "r+") do |f|
+ dbclass = f.gets
+ assert_equal("GDBM", dbclass.chomp)
+ yield
+ end
+ end
+
+ def test_s_open_lock
+ skip "GDBM.open would block when opening already locked gdbm file on platforms without flock and with lockf" if /solaris/ =~ RUBY_PLATFORM
+
+ dbname = "#{@tmpdir}/#{@prefix}"
+
+ open_db_child(dbname) do
+ assert_raise(Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EACCES) {
+ GDBM.open(dbname, 0644) {|gdbm|
+ assert_instance_of(GDBM, gdbm)
+ }
+ }
+ end
+ end
+
+=begin
+ # Is it guaranteed on many OS?
+ def test_s_open_lock_one_process
+ # locking on one process
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", 0644))
+ assert_raise(Errno::EWOULDBLOCK) {
+ begin
+ GDBM.open("#{@tmpdir}/#{@prefix}", 0644)
+ rescue Errno::EAGAIN
+ raise Errno::EWOULDBLOCK
+ end
+ }
+ end
+=end
+
+ def test_s_open_nolock
+ dbname = "#{@tmpdir}/#{@prefix}"
+
+ open_db_child(dbname, GDBM::NOLOCK) do
+ assert_nothing_raised(Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EACCES) {
+ GDBM.open(dbname, 0644) {|gdbm2|
+ assert_instance_of(GDBM, gdbm2)
+ }
+ }
+ end
+
+ STDERR.puts Dir.glob("#{dbname}*") if $DEBUG
+
+ # The following test fails on Windows because flock() implementation
+ # is different from Unix.
+ return if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ open_db_child(dbname) do
+ assert_nothing_raised(Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EACCES) {
+ # this test is failed on Cygwin98 (???)
+ GDBM.open(dbname, 0644, GDBM::NOLOCK) {|gdbm2|
+ assert_instance_of(GDBM, gdbm2)
+ }
+ }
+ end
+ end if defined? GDBM::NOLOCK # gdbm 1.8.0 specific
+
+ def test_s_open_error
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", 0))
+ assert_raise(Errno::EACCES, Errno::EWOULDBLOCK) {
+ GDBM.open("#{@tmpdir}/#{@prefix}", 0)
+ }
+ gdbm.close
+ end
+
+ def test_close
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}"))
+ assert_nil(gdbm.close)
+
+ # closed GDBM file
+ assert_raise(RuntimeError) { gdbm.close }
+ end
+
+ def test_aref
+ assert_equal('bar', @gdbm['foo'] = 'bar')
+ assert_equal('bar', @gdbm['foo'])
+
+ assert_nil(@gdbm['bar'])
+ end
+
+ def test_fetch
+ assert_equal('bar', @gdbm['foo']='bar')
+ assert_equal('bar', @gdbm.fetch('foo'))
+
+ # key not found
+ assert_raise(IndexError) {
+ @gdbm.fetch('bar')
+ }
+
+ # test for `ifnone' arg
+ assert_equal('baz', @gdbm.fetch('bar', 'baz'))
+
+ # test for `ifnone' block
+ assert_equal('foobar', @gdbm.fetch('bar') {|key| 'foo' + key })
+ end
+
+ def test_aset
+ num = 0
+ 2.times {|i|
+ assert_equal('foo', @gdbm['foo'] = 'foo')
+ assert_equal('foo', @gdbm['foo'])
+ assert_equal('bar', @gdbm['foo'] = 'bar')
+ assert_equal('bar', @gdbm['foo'])
+
+ num += 1 if i == 0
+ assert_equal(num, @gdbm.size)
+
+ # assign nil
+ assert_equal('', @gdbm['bar'] = '')
+ assert_equal('', @gdbm['bar'])
+
+ num += 1 if i == 0
+ assert_equal(num, @gdbm.size)
+
+ # empty string
+ assert_equal('', @gdbm[''] = '')
+ assert_equal('', @gdbm[''])
+
+ num += 1 if i == 0
+ assert_equal(num, @gdbm.size)
+
+ # Fixnum
+ assert_equal('200', @gdbm['100'] = '200')
+ assert_equal('200', @gdbm['100'])
+
+ num += 1 if i == 0
+ assert_equal(num, @gdbm.size)
+
+ # Big key and value
+ assert_equal('y' * 100, @gdbm['x' * 100] = 'y' * 100)
+ assert_equal('y' * 100, @gdbm['x' * 100])
+
+ num += 1 if i == 0
+ assert_equal(num, @gdbm.size)
+ }
+ end
+
+ def test_key
+ assert_equal('bar', @gdbm['foo'] = 'bar')
+ assert_equal('foo', @gdbm.key('bar'))
+ assert_nil(@gdbm['bar'])
+ end
+
+ def test_values_at
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ @gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
+ assert_equal(values.reverse, @gdbm.values_at(*keys.reverse))
+ end
+
+ def test_select_with_block
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ @gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
+ ret = @gdbm.select {|k,v|
+ assert_equal(k.upcase, v)
+ k != "bar"
+ }
+ assert_equal([['baz', 'BAZ'], ['foo', 'FOO']],
+ ret.sort)
+ end
+
+ def test_length
+ num = 10
+ assert_equal(0, @gdbm.size)
+ num.times {|i|
+ i = i.to_s
+ @gdbm[i] = i
+ }
+ assert_equal(num, @gdbm.size)
+
+ @gdbm.shift
+
+ assert_equal(num - 1, @gdbm.size)
+ end
+
+ def test_empty?
+ assert_equal(true, @gdbm.empty?)
+ @gdbm['foo'] = 'FOO'
+ assert_equal(false, @gdbm.empty?)
+ end
+
+ def test_each_pair
+ n = 0
+ @gdbm.each_pair { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
+
+ n = 0
+ ret = @gdbm.each_pair {|key, val|
+ assert_not_nil(i = keys.index(key))
+ assert_equal(val, values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@gdbm, ret)
+ end
+
+ def test_each_value
+ n = 0
+ @gdbm.each_value { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
+
+ n = 0
+ ret = @gdbm.each_value {|val|
+ assert_not_nil(key = @gdbm.key(val))
+ assert_not_nil(i = keys.index(key))
+ assert_equal(val, values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@gdbm, ret)
+ end
+
+ def test_each_key
+ n = 0
+ @gdbm.each_key { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
+
+ n = 0
+ ret = @gdbm.each_key {|key|
+ assert_not_nil(i = keys.index(key))
+ assert_equal(@gdbm[key], values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@gdbm, ret)
+ end
+
+ def test_each_key_without_block
+ assert_kind_of Enumerator, @gdbm.each_key
+ end
+
+ def test_keys
+ assert_equal([], @gdbm.keys)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
+
+ assert_equal(keys.sort, @gdbm.keys.sort)
+ assert_equal(values.sort, @gdbm.values.sort)
+ end
+
+ def test_values
+ test_keys
+ end
+
+ def test_shift
+ assert_nil(@gdbm.shift)
+ assert_equal(0, @gdbm.size)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
+
+ ret_keys = []
+ ret_values = []
+ while ret = @gdbm.shift
+ ret_keys.push ret[0]
+ ret_values.push ret[1]
+
+ assert_equal(keys.size - ret_keys.size, @gdbm.size)
+ end
+
+ assert_equal(keys.sort, ret_keys.sort)
+ assert_equal(values.sort, ret_values.sort)
+ end
+
+ def test_delete
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ key = keys[1]
+
+ assert_nil(@gdbm.delete(key))
+ assert_equal(0, @gdbm.size)
+
+ @gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
+
+ assert_equal('BAR', @gdbm.delete(key))
+ assert_nil(@gdbm[key])
+ assert_equal(2, @gdbm.size)
+
+ assert_nil(@gdbm.delete(key))
+ end
+
+ def test_delete_with_block
+ key = 'no called block'
+ @gdbm[key] = 'foo'
+ assert_equal('foo', @gdbm.delete(key) {|k| k.replace 'called block'})
+ assert_equal('no called block', key)
+ assert_equal(0, @gdbm.size)
+
+ key = 'no called block'
+ assert_equal(:blockval,
+ @gdbm.delete(key) {|k| k.replace 'called block'; :blockval})
+ assert_equal('called block', key)
+ assert_equal(0, @gdbm.size)
+ end
+
+ def test_delete_if
+ v = "0"
+ 100.times {@gdbm[v] = v; v = v.next}
+
+ ret = @gdbm.delete_if {|key, val| key.to_i < 50}
+ assert_equal(@gdbm, ret)
+ check_size(50, @gdbm)
+
+ ret = @gdbm.delete_if {|key, val| key.to_i >= 50}
+ assert_equal(@gdbm, ret)
+ check_size(0, @gdbm)
+
+ # break
+ v = "0"
+ 100.times {@gdbm[v] = v; v = v.next}
+ check_size(100, @gdbm)
+ n = 0;
+ @gdbm.delete_if {|key, val|
+ break if n > 50
+ n+=1
+ true
+ }
+ assert_equal(51, n)
+ check_size(49, @gdbm)
+
+ @gdbm.clear
+
+ # raise
+ v = "0"
+ 100.times {@gdbm[v] = v; v = v.next}
+ check_size(100, @gdbm)
+ n = 0;
+ begin
+ @gdbm.delete_if {|key, val|
+ raise "runtime error" if n > 50
+ n+=1
+ true
+ }
+ rescue RuntimeError
+ end
+ assert_equal(51, n)
+ check_size(49, @gdbm)
+ end
+
+ def test_reject
+ v = "0"
+ 100.times {@gdbm[v] = v; v = v.next}
+
+ hash = @gdbm.reject {|key, val| key.to_i < 50}
+ assert_instance_of(Hash, hash)
+ assert_equal(100, @gdbm.size)
+
+ assert_equal(50, hash.size)
+ hash.each_pair {|key,val|
+ assert_equal(false, key.to_i < 50)
+ assert_equal(key, val)
+ }
+
+ hash = @gdbm.reject {|key, val| key.to_i < 100}
+ assert_instance_of(Hash, hash)
+ assert_equal(true, hash.empty?)
+ end
+
+ def test_clear
+ v = "1"
+ 100.times {v = v.next; @gdbm[v] = v}
+
+ assert_equal(@gdbm, @gdbm.clear)
+
+ # validate GDBM#size
+ i = 0
+ @gdbm.each { i += 1 }
+ assert_equal(@gdbm.size, i)
+ assert_equal(0, i)
+ end
+
+ def test_invert
+ v = "0"
+ 100.times {@gdbm[v] = v; v = v.next}
+
+ hash = @gdbm.invert
+ assert_instance_of(Hash, hash)
+ assert_equal(100, hash.size)
+ hash.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_update
+ hash = {}
+ v = "0"
+ 100.times {v = v.next; hash[v] = v}
+
+ @gdbm["101"] = "101"
+ @gdbm.update hash
+ assert_equal(101, @gdbm.size)
+ @gdbm.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_replace
+ hash = {}
+ v = "0"
+ 100.times {v = v.next; hash[v] = v}
+
+ @gdbm["101"] = "101"
+ @gdbm.replace hash
+ assert_equal(100, @gdbm.size)
+ @gdbm.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_reorganize
+ size1 = File.size(@path)
+ i = "1"
+ 1000.times {i = i.next; @gdbm[i] = i}
+ @gdbm.clear
+ @gdbm.sync
+
+ size2 = File.size(@path)
+ @gdbm.reorganize
+ size3 = File.size(@path)
+
+ # p [size1, size2, size3]
+ assert_equal(true, size1 < size2)
+ # this test is failed on Cygwin98. `GDBM version 1.8.0, as of May 19, 1999'
+ assert_equal(true, size3 < size2)
+ assert_equal(size1, size3)
+ end
+
+ def test_sync
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", 0666, GDBM::FAST))
+ assert_equal(gdbm.sync, gdbm)
+ gdbm.close
+ assert_instance_of(GDBM, gdbm = GDBM.open("#{@tmpdir}/#{@prefix}", 0666))
+ assert_equal(gdbm.sync, gdbm)
+ gdbm.close
+ end
+
+ def test_cachesize=
+ assert_equal(@gdbm.cachesize = 1024, 1024)
+ end
+
+ def test_fastmode=
+ assert_equal(@gdbm.fastmode = true, true)
+ end
+
+ def test_syncmode=
+ assert_equal(@gdbm.syncmode = true, true)
+ end
+
+ def test_haskey?
+ assert_equal('bar', @gdbm['foo']='bar')
+ assert_equal(true, @gdbm.has_key?('foo'))
+ assert_equal(false, @gdbm.has_key?('bar'))
+ end
+
+ def test_has_value?
+ assert_equal('bar', @gdbm['foo']='bar')
+ assert_equal(true, @gdbm.has_value?('bar'))
+ assert_equal(false, @gdbm.has_value?('foo'))
+ end
+
+ def test_to_a
+ v = "0"
+ 100.times {v = v.next; @gdbm[v] = v}
+
+ ary = @gdbm.to_a
+ assert_instance_of(Array, ary)
+ assert_equal(100, ary.size)
+ ary.each {|key,val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_to_hash
+ v = "0"
+ 100.times {v = v.next; @gdbm[v] = v}
+
+ hash = @gdbm.to_hash
+ assert_instance_of(Hash, hash)
+ assert_equal(100, hash.size)
+ hash.each {|key,val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+ end
+
+ class TestGDBM2 < Test::Unit::TestCase
+ def setup
+ @tmproot = Dir.mktmpdir('ruby-gdbm')
+ end
+
+ def teardown
+ FileUtils.remove_entry_secure @tmproot if File.directory?(@tmproot)
+ end
+
+ def test_reader_open_notexist
+ assert_raise(Errno::ENOENT) {
+ GDBM.open("#{@tmproot}/a", 0666, GDBM::READER)
+ }
+ end
+
+ def test_writer_open_notexist
+ skip "gdbm_open(GDBM_WRITER) is broken on libgdbm 1.8.0" if /1\.8\.0/ =~ GDBM::VERSION
+
+ assert_raise(Errno::ENOENT) {
+ GDBM.open("#{@tmproot}/a", 0666, GDBM::WRITER)
+ }
+ end
+
+ def test_wrcreat_open_notexist
+ v = GDBM.open("#{@tmproot}/a", 0666, GDBM::WRCREAT)
+ assert_instance_of(GDBM, v)
+ v.close
+ end
+
+ def test_newdb_open_notexist
+ v = GDBM.open("#{@tmproot}/a", 0666, GDBM::NEWDB)
+ assert_instance_of(GDBM, v)
+ v.close
+ end
+
+ def test_reader_open
+ GDBM.open("#{@tmproot}/a.dbm") {} # create a db.
+ v = GDBM.open("#{@tmproot}/a.dbm", nil, GDBM::READER) {|d|
+ assert_raise(GDBMError) { d["k"] = "v" }
+ true
+ }
+ assert(v)
+ end
+
+ def test_newdb_open
+ GDBM.open("#{@tmproot}/a.dbm") {|dbm|
+ dbm["k"] = "v"
+ }
+ v = GDBM.open("#{@tmproot}/a.dbm", nil, GDBM::NEWDB) {|d|
+ assert_equal(0, d.length)
+ assert_nil(d["k"])
+ true
+ }
+ assert(v)
+ end
+
+ def test_freeze
+ GDBM.open("#{@tmproot}/a.dbm") {|d|
+ d.freeze
+ assert_raise(RuntimeError) { d["k"] = "v" }
+ }
+ end
+ end
+end
diff --git a/jni/ruby/test/io/console/test_io_console.rb b/jni/ruby/test/io/console/test_io_console.rb
new file mode 100644
index 0000000..3481a2b
--- /dev/null
+++ b/jni/ruby/test/io/console/test_io_console.rb
@@ -0,0 +1,332 @@
+begin
+ require 'io/console'
+ require 'test/unit'
+ require 'pty'
+rescue LoadError
+end
+
+class TestIO_Console < Test::Unit::TestCase
+ Bug6116 = '[ruby-dev:45309]'
+
+ def test_raw
+ helper {|m, s|
+ s.print "abc\n"
+ assert_equal("abc\r\n", m.gets)
+ assert_send([s, :echo?])
+ s.raw {
+ assert_not_send([s, :echo?], Bug6116)
+ s.print "def\n"
+ assert_equal("def\n", m.gets)
+ }
+ assert_send([s, :echo?])
+ s.print "ghi\n"
+ assert_equal("ghi\r\n", m.gets)
+ }
+ end
+
+ def test_raw_minchar
+ helper {|m, s|
+ len = 0
+ assert_equal([nil, 0], [s.getch(min: 0), len])
+ main = Thread.current
+ go = false
+ th = Thread.start {
+ len += 1
+ m.print("a")
+ m.flush
+ sleep 0.01 until go and main.stop?
+ len += 10
+ m.print("1234567890")
+ m.flush
+ }
+ begin
+ assert_equal(["a", 1], [s.getch(min: 1), len])
+ go = true
+ assert_equal(["1", 11], [s.getch, len])
+ ensure
+ th.join
+ end
+ }
+ end
+
+ def test_raw_timeout
+ helper {|m, s|
+ len = 0
+ assert_equal([nil, 0], [s.getch(min: 0, time: 0.1), len])
+ main = Thread.current
+ th = Thread.start {
+ sleep 0.01 until main.stop?
+ len += 2
+ m.print("ab")
+ }
+ begin
+ assert_equal(["a", 2], [s.getch(min: 1, time: 1), len])
+ assert_equal(["b", 2], [s.getch(time: 1), len])
+ ensure
+ th.join
+ end
+ }
+ end
+
+ def test_cooked
+ helper {|m, s|
+ assert_send([s, :echo?])
+ s.raw {
+ s.print "abc\n"
+ assert_equal("abc\n", m.gets)
+ assert_not_send([s, :echo?], Bug6116)
+ s.cooked {
+ assert_send([s, :echo?])
+ s.print "def\n"
+ assert_equal("def\r\n", m.gets)
+ }
+ assert_not_send([s, :echo?], Bug6116)
+ }
+ assert_send([s, :echo?])
+ s.print "ghi\n"
+ assert_equal("ghi\r\n", m.gets)
+ }
+ end
+
+ def test_echo
+ helper {|m, s|
+ assert_send([s, :echo?])
+ m.print "a"
+ assert_equal("a", m.readpartial(10))
+ }
+ end
+
+ def test_noecho
+ helper {|m, s|
+ s.noecho {
+ assert_not_send([s, :echo?])
+ m.print "a"
+ sleep 0.1
+ }
+ m.print "b"
+ assert_equal("b", m.readpartial(10))
+ }
+ end
+
+ def test_noecho2
+ helper {|m, s|
+ assert_send([s, :echo?])
+ m.print "a\n"
+ sleep 0.1
+ s.print "b\n"
+ sleep 0.1
+ assert_equal("a\r\nb\r\n", m.readpartial(10))
+ assert_equal("a\n", s.readpartial(10))
+ s.noecho {
+ assert_not_send([s, :echo?])
+ m.print "a\n"
+ s.print "b\n"
+ assert_equal("b\r\n", m.readpartial(10))
+ assert_equal("a\n", s.readpartial(10))
+ }
+ assert_send([s, :echo?])
+ m.print "a\n"
+ sleep 0.1
+ s.print "b\n"
+ sleep 0.1
+ assert_equal("a\r\nb\r\n", m.readpartial(10))
+ assert_equal("a\n", s.readpartial(10))
+ }
+ end
+
+ def test_setecho
+ helper {|m, s|
+ assert_send([s, :echo?])
+ s.echo = false
+ m.print "a"
+ sleep 0.1
+ s.echo = true
+ m.print "b"
+ assert_equal("b", m.readpartial(10))
+ }
+ end
+
+ def test_setecho2
+ helper {|m, s|
+ assert_send([s, :echo?])
+ m.print "a\n"
+ sleep 0.1
+ s.print "b\n"
+ sleep 0.1
+ assert_equal("a\r\nb\r\n", m.readpartial(10))
+ assert_equal("a\n", s.readpartial(10))
+ s.echo = false
+ assert_not_send([s, :echo?])
+ m.print "a\n"
+ s.print "b\n"
+ assert_equal("b\r\n", m.readpartial(10))
+ assert_equal("a\n", s.readpartial(10))
+ s.echo = true
+ assert_send([s, :echo?])
+ m.print "a\n"
+ sleep 0.1
+ s.print "b\n"
+ sleep 0.1
+ assert_equal("a\r\nb\r\n", m.readpartial(10))
+ assert_equal("a\n", s.readpartial(10))
+ }
+ end
+
+ def test_iflush
+ helper {|m, s|
+ m.print "a"
+ s.iflush
+ m.print "b\n"
+ assert_equal("b\n", s.readpartial(10))
+ }
+ end
+
+ def test_oflush
+ helper {|m, s|
+ s.print "a"
+ s.oflush # oflush may be issued after "a" is already sent.
+ s.print "b"
+ assert_include(["b", "ab"], m.readpartial(10))
+ }
+ end
+
+ def test_ioflush
+ helper {|m, s|
+ m.print "a"
+ s.ioflush
+ m.print "b\n"
+ assert_equal("b\n", s.readpartial(10))
+ }
+ end
+
+ def test_ioflush2
+ helper {|m, s|
+ s.print "a"
+ s.ioflush # ioflush may be issued after "a" is already sent.
+ s.print "b"
+ assert_include(["b", "ab"], m.readpartial(10))
+ }
+ end
+
+ def test_winsize
+ helper {|m, s|
+ begin
+ assert_equal([0, 0], s.winsize)
+ rescue Errno::EINVAL # OpenSolaris 2009.06 TIOCGWINSZ causes Errno::EINVAL before TIOCSWINSZ.
+ end
+ }
+ end
+
+ if IO.console
+ def test_close
+ IO.console.close
+ assert_kind_of(IO, IO.console)
+ assert_nothing_raised(IOError) {IO.console.fileno}
+
+ IO.console(:close)
+ assert(IO.console(:tty?))
+ ensure
+ IO.console(:close)
+ end
+
+ def test_sync
+ assert(IO.console.sync, "console should be unbuffered")
+ ensure
+ IO.console(:close)
+ end
+ else
+ def test_close
+ assert_equal(["true"], run_pty("IO.console.close; p IO.console.fileno >= 0"))
+ assert_equal(["true"], run_pty("IO.console(:close); p IO.console(:tty?)"))
+ end
+
+ def test_sync
+ assert_equal(["true"], run_pty("p IO.console.sync"))
+ end
+ end
+
+ private
+ def helper
+ m, s = PTY.open
+ rescue RuntimeError
+ skip $!
+ else
+ yield m, s
+ ensure
+ m.close if m
+ s.close if s
+ end
+
+ def run_pty(src, n = 1)
+ r, w, pid = PTY.spawn(EnvUtil.rubybin, "-rio/console", "-e", src)
+ rescue RuntimeError
+ skip $!
+ else
+ result = []
+ n.times {result << r.gets.chomp}
+ Process.wait(pid)
+ if block_given?
+ yield result
+ else
+ result
+ end
+ ensure
+ r.close if r
+ w.close if w
+ end
+end if defined?(PTY) and defined?(IO::console)
+
+class TestIO_Console < Test::Unit::TestCase
+ case
+ when Process.respond_to?(:daemon)
+ noctty = [EnvUtil.rubybin, "-e", "Process.daemon(true)"]
+ when !(rubyw = RbConfig::CONFIG["RUBYW_INSTALL_NAME"]).empty?
+ dir, base = File.split(EnvUtil.rubybin)
+ noctty = [File.join(dir, base.sub(/ruby/, rubyw))]
+ end
+
+ if noctty
+ require 'tempfile'
+ NOCTTY = noctty
+ def test_noctty
+ t = Tempfile.new("console")
+ t.close
+ t2 = Tempfile.new("console")
+ t2.close
+ cmd = [*NOCTTY[1..-1],
+ '-e', 'open(ARGV[0], "w") {|f|',
+ '-e', 'STDOUT.reopen(f)',
+ '-e', 'STDERR.reopen(f)',
+ '-e', 'require "io/console"',
+ '-e', 'f.puts IO.console.inspect',
+ '-e', 'f.flush',
+ '-e', 'File.unlink(ARGV[1])',
+ '-e', '}',
+ '--', t.path, t2.path]
+ assert_ruby_status(cmd, rubybin: NOCTTY[0])
+ 30.times do
+ break unless File.exist?(t2.path)
+ sleep 0.1
+ end
+ t.open
+ assert_equal("nil", t.gets(nil).chomp)
+ ensure
+ t.close! if t and !t.closed?
+ t2.close!
+ end
+ end
+end if defined?(IO.console)
+
+class TestIO_Console < Test::Unit::TestCase
+ def test_stringio_getch
+ assert_separately %w"--disable=gems -rstringio -rio/console", %q{
+ assert_operator(StringIO, :method_defined?, :getch)
+ }
+ assert_separately %w"--disable=gems -rio/console -rstringio", %q{
+ assert_operator(StringIO, :method_defined?, :getch)
+ }
+ assert_separately %w"--disable=gems -rstringio", %q{
+ assert_not_operator(StringIO, :method_defined?, :getch)
+ }
+ end
+end
diff --git a/jni/ruby/test/io/nonblock/test_flush.rb b/jni/ruby/test/io/nonblock/test_flush.rb
new file mode 100644
index 0000000..ab54205
--- /dev/null
+++ b/jni/ruby/test/io/nonblock/test_flush.rb
@@ -0,0 +1,52 @@
+require 'test/unit'
+require 'timeout'
+begin
+ require 'io/nonblock'
+rescue LoadError
+end
+
+class TestIONonblock < Test::Unit::TestCase
+ def test_flush
+ IO.pipe {|r, w|
+ return if flush_test(r, w)
+ }
+ require 'socket';
+ Socket.pair(:INET, :STREAM) {|s1, s2|
+ return if flush_test(s1, s2)
+ }
+ skip "nonblocking IO did not work"
+ end
+
+ def flush_test(r, w)
+ begin
+ w.nonblock = true
+ rescue Errno::EBADF
+ return false
+ end
+ w.sync = false
+ w << "b"
+ w.flush
+ w << "a" * 4096
+ result = ""
+ timeout(10) {
+ t0 = Thread.new {
+ Thread.pass
+ w.close
+ }
+ t = Thread.new {
+ while (Thread.pass; s = r.read(4096))
+ result << s
+ end
+ }
+ begin
+ w.flush # assert_raise(IOError, "[ruby-dev:24985]") {w.flush}
+ rescue Errno::EBADF, IOError
+ # ignore [ruby-dev:35638]
+ end
+ assert_nothing_raised {t.join}
+ t0.join
+ }
+ assert_equal(4097, result.size)
+ true
+ end
+end if IO.method_defined?(:nonblock)
diff --git a/jni/ruby/test/io/wait/test_io_wait.rb b/jni/ruby/test/io/wait/test_io_wait.rb
new file mode 100644
index 0000000..7729d45
--- /dev/null
+++ b/jni/ruby/test/io/wait/test_io_wait.rb
@@ -0,0 +1,112 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+require 'timeout'
+require 'socket'
+begin
+ require 'io/wait'
+rescue LoadError
+end
+
+class TestIOWait < Test::Unit::TestCase
+
+ def setup
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ @r, @w = Socket.pair(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ else
+ @r, @w = IO.pipe
+ end
+ end
+
+ def teardown
+ @r.close unless @r.closed?
+ @w.close unless @w.closed?
+ end
+
+ def test_nread
+ assert_equal 0, @r.nread
+ @w.syswrite "."
+ sleep 0.1
+ assert_equal 1, @r.nread
+ end
+
+ def test_nread_buffered
+ @w.syswrite ".\n!"
+ assert_equal ".\n", @r.gets
+ assert_equal 1, @r.nread
+ end
+
+ def test_ready?
+ refute @r.ready?, "shouldn't ready, but ready"
+ @w.syswrite "."
+ sleep 0.1
+ assert @r.ready?, "should ready, but not"
+ end
+
+ def test_buffered_ready?
+ @w.syswrite ".\n!"
+ assert_equal ".\n", @r.gets
+ assert @r.ready?
+ end
+
+ def test_wait
+ assert_nil @r.wait(0)
+ @w.syswrite "."
+ sleep 0.1
+ assert_equal @r, @r.wait(0)
+ end
+
+ def test_wait_buffered
+ @w.syswrite ".\n!"
+ assert_equal ".\n", @r.gets
+ assert_equal true, @r.wait(0)
+ end
+
+ def test_wait_forever
+ th = Thread.new { sleep 0.01; @w.syswrite "." }
+ assert_equal @r, @r.wait
+ ensure
+ th.join
+ end
+
+ def test_wait_eof
+ th = Thread.new { sleep 0.01; @w.close }
+ assert_nil @r.wait
+ ensure
+ th.join
+ end
+
+ def test_wait_writable
+ assert_equal @w, @w.wait_writable
+ end
+
+ def test_wait_writable_timeout
+ assert_equal @w, @w.wait_writable(0.01)
+ written = fill_pipe
+ assert_nil @w.wait_writable(0.01)
+ @r.read(written)
+ assert_equal @w, @w.wait_writable(0.01)
+ end
+
+ def test_wait_writable_EPIPE
+ fill_pipe
+ @r.close
+ assert_equal @w, @w.wait_writable
+ end
+
+ def test_wait_writable_closed
+ @w.close
+ assert_raises(IOError) { @w.wait_writable }
+ end
+
+private
+
+ def fill_pipe
+ written = 0
+ buf = " " * 4096
+ begin
+ written += @w.write_nonblock(buf)
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
+ return written
+ end while true
+ end
+end if IO.method_defined?(:wait)
diff --git a/jni/ruby/test/irb/test_completion.rb b/jni/ruby/test/irb/test_completion.rb
new file mode 100644
index 0000000..eeeda12
--- /dev/null
+++ b/jni/ruby/test/irb/test_completion.rb
@@ -0,0 +1,21 @@
+require 'test/unit'
+
+module TestIRB
+ class TestCompletion < Test::Unit::TestCase
+ def test_nonstring_module_name
+ begin
+ require "irb/completion"
+ bug5938 = '[ruby-core:42244]'
+ cmds = %W[-rirb -rirb/completion -e IRB.setup(__FILE__)
+ -e IRB.conf[:MAIN_CONTEXT]=IRB::Irb.new.context
+ -e module\sFoo;def\sself.name;//;end;end
+ -e IRB::InputCompletor::CompletionProc.call("[1].first.")
+ -- -f --]
+ status = assert_in_out_err(cmds, "", //, [], bug5938)
+ assert(status.success?, bug5938)
+ rescue LoadError
+ skip "cannot load irb/completion"
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/irb/test_option.rb b/jni/ruby/test/irb/test_option.rb
new file mode 100644
index 0000000..a7dd663
--- /dev/null
+++ b/jni/ruby/test/irb/test_option.rb
@@ -0,0 +1,11 @@
+require 'test/unit'
+
+module TestIRB
+ class TestOption < Test::Unit::TestCase
+ def test_end_of_option
+ bug4117 = '[ruby-core:33574]'
+ status = assert_in_out_err(%w[-rirb -e IRB.start(__FILE__) -- -f --], "", //, [], bug4117)
+ assert(status.success?, bug4117)
+ end
+ end
+end
diff --git a/jni/ruby/test/irb/test_raise_no_backtrace_exception.rb b/jni/ruby/test/irb/test_raise_no_backtrace_exception.rb
new file mode 100644
index 0000000..67fb5b6
--- /dev/null
+++ b/jni/ruby/test/irb/test_raise_no_backtrace_exception.rb
@@ -0,0 +1,13 @@
+require 'test/unit'
+
+module TestIRB
+ class TestRaiseNoBacktraceException < Test::Unit::TestCase
+ def test_raise_exception
+ status = assert_in_out_err(%w[-rirb -e IRB.start(__FILE__) -- -f --], <<-IRB, /Exception: foo/, [])
+ e = Exception.new("foo")
+ def e.backtrace; nil; end
+ raise e
+IRB
+ end
+ end
+end
diff --git a/jni/ruby/test/json/fixtures/fail1.json b/jni/ruby/test/json/fixtures/fail1.json
new file mode 100644
index 0000000..6216b86
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail1.json
@@ -0,0 +1 @@
+"A JSON payload should be an object or array, not a string." \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail10.json b/jni/ruby/test/json/fixtures/fail10.json
new file mode 100644
index 0000000..5d8c004
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail10.json
@@ -0,0 +1 @@
+{"Extra value after close": true} "misplaced quoted value" \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail11.json b/jni/ruby/test/json/fixtures/fail11.json
new file mode 100644
index 0000000..76eb95b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail11.json
@@ -0,0 +1 @@
+{"Illegal expression": 1 + 2} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail12.json b/jni/ruby/test/json/fixtures/fail12.json
new file mode 100644
index 0000000..77580a4
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail12.json
@@ -0,0 +1 @@
+{"Illegal invocation": alert()} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail13.json b/jni/ruby/test/json/fixtures/fail13.json
new file mode 100644
index 0000000..379406b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail13.json
@@ -0,0 +1 @@
+{"Numbers cannot have leading zeroes": 013} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail14.json b/jni/ruby/test/json/fixtures/fail14.json
new file mode 100644
index 0000000..0ed366b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail14.json
@@ -0,0 +1 @@
+{"Numbers cannot be hex": 0x14} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail18.json b/jni/ruby/test/json/fixtures/fail18.json
new file mode 100644
index 0000000..ebc11eb
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail18.json
@@ -0,0 +1 @@
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
diff --git a/jni/ruby/test/json/fixtures/fail19.json b/jni/ruby/test/json/fixtures/fail19.json
new file mode 100644
index 0000000..3b9c46f
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail19.json
@@ -0,0 +1 @@
+{"Missing colon" null} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail2.json b/jni/ruby/test/json/fixtures/fail2.json
new file mode 100644
index 0000000..6b7c11e
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail2.json
@@ -0,0 +1 @@
+["Unclosed array" \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail20.json b/jni/ruby/test/json/fixtures/fail20.json
new file mode 100644
index 0000000..27c1af3
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail20.json
@@ -0,0 +1 @@
+{"Double colon":: null} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail21.json b/jni/ruby/test/json/fixtures/fail21.json
new file mode 100644
index 0000000..6247457
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail21.json
@@ -0,0 +1 @@
+{"Comma instead of colon", null} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail22.json b/jni/ruby/test/json/fixtures/fail22.json
new file mode 100644
index 0000000..a775258
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail22.json
@@ -0,0 +1 @@
+["Colon instead of comma": false] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail23.json b/jni/ruby/test/json/fixtures/fail23.json
new file mode 100644
index 0000000..494add1
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail23.json
@@ -0,0 +1 @@
+["Bad value", truth] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail24.json b/jni/ruby/test/json/fixtures/fail24.json
new file mode 100644
index 0000000..caff239
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail24.json
@@ -0,0 +1 @@
+['single quote'] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail25.json b/jni/ruby/test/json/fixtures/fail25.json
new file mode 100644
index 0000000..2dfbd25
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail25.json
@@ -0,0 +1 @@
+["tab character in string "]
diff --git a/jni/ruby/test/json/fixtures/fail27.json b/jni/ruby/test/json/fixtures/fail27.json
new file mode 100644
index 0000000..6b01a2c
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail27.json
@@ -0,0 +1,2 @@
+["line
+break"] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail28.json b/jni/ruby/test/json/fixtures/fail28.json
new file mode 100644
index 0000000..621a010
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail28.json
@@ -0,0 +1,2 @@
+["line\
+break"] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail3.json b/jni/ruby/test/json/fixtures/fail3.json
new file mode 100644
index 0000000..168c81e
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail3.json
@@ -0,0 +1 @@
+{unquoted_key: "keys must be quoted"} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail4.json b/jni/ruby/test/json/fixtures/fail4.json
new file mode 100644
index 0000000..9de168b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail4.json
@@ -0,0 +1 @@
+["extra comma",] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail5.json b/jni/ruby/test/json/fixtures/fail5.json
new file mode 100644
index 0000000..ddf3ce3
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail5.json
@@ -0,0 +1 @@
+["double extra comma",,] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail6.json b/jni/ruby/test/json/fixtures/fail6.json
new file mode 100644
index 0000000..ed91580
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail6.json
@@ -0,0 +1 @@
+[ , "<-- missing value"] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail7.json b/jni/ruby/test/json/fixtures/fail7.json
new file mode 100644
index 0000000..8a96af3
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail7.json
@@ -0,0 +1 @@
+["Comma after the close"], \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail8.json b/jni/ruby/test/json/fixtures/fail8.json
new file mode 100644
index 0000000..b28479c
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail8.json
@@ -0,0 +1 @@
+["Extra close"]] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail9.json b/jni/ruby/test/json/fixtures/fail9.json
new file mode 100644
index 0000000..5815574
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail9.json
@@ -0,0 +1 @@
+{"Extra comma": true,} \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass1.json b/jni/ruby/test/json/fixtures/pass1.json
new file mode 100644
index 0000000..7828fcc
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass1.json
@@ -0,0 +1,56 @@
+[
+ "JSON Test Pattern pass1",
+ {"object with 1 member":["array with 1 element"]},
+ {},
+ [],
+ -42,
+ true,
+ false,
+ null,
+ {
+ "integer": 1234567890,
+ "real": -9876.543210,
+ "e": 0.123456789e-12,
+ "E": 1.234567890E+34,
+ "": 23456789012E666,
+ "zero": 0,
+ "one": 1,
+ "space": " ",
+ "quote": "\"",
+ "backslash": "\\",
+ "controls": "\b\f\n\r\t",
+ "slash": "/ & \/",
+ "alpha": "abcdefghijklmnopqrstuvwyz",
+ "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
+ "digit": "0123456789",
+ "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
+ "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
+ "true": true,
+ "false": false,
+ "null": null,
+ "array":[ ],
+ "object":{ },
+ "address": "50 St. James Street",
+ "url": "http://www.JSON.org/",
+ "comment": "// /* <!-- --",
+ "# -- --> */": " ",
+ " s p a c e d " :[1,2 , 3
+
+,
+
+4 , 5 , 6 ,7 ],
+ "compact": [1,2,3,4,5,6,7],
+ "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
+ "quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
+ "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
+: "A key can be any string"
+ },
+ 0.5 ,98.6
+,
+99.44
+,
+
+1066
+
+
+,"rosebud"] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass15.json b/jni/ruby/test/json/fixtures/pass15.json
new file mode 100644
index 0000000..fc8376b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass15.json
@@ -0,0 +1 @@
+["Illegal backslash escape: \x15"] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass16.json b/jni/ruby/test/json/fixtures/pass16.json
new file mode 100644
index 0000000..c43ae3c
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass16.json
@@ -0,0 +1 @@
+["Illegal backslash escape: \'"] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass17.json b/jni/ruby/test/json/fixtures/pass17.json
new file mode 100644
index 0000000..62b9214
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass17.json
@@ -0,0 +1 @@
+["Illegal backslash escape: \017"] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass2.json b/jni/ruby/test/json/fixtures/pass2.json
new file mode 100644
index 0000000..d3c63c7
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass2.json
@@ -0,0 +1 @@
+[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass26.json b/jni/ruby/test/json/fixtures/pass26.json
new file mode 100644
index 0000000..845d26a
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass26.json
@@ -0,0 +1 @@
+["tab\ character\ in\ string\ "] \ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass3.json b/jni/ruby/test/json/fixtures/pass3.json
new file mode 100644
index 0000000..4528d51
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass3.json
@@ -0,0 +1,6 @@
+{
+ "JSON Test Pattern pass3": {
+ "The outermost value": "must be an object or array.",
+ "In this test": "It is an object."
+ }
+}
diff --git a/jni/ruby/test/json/setup_variant.rb b/jni/ruby/test/json/setup_variant.rb
new file mode 100644
index 0000000..2dab184
--- /dev/null
+++ b/jni/ruby/test/json/setup_variant.rb
@@ -0,0 +1,11 @@
+case ENV['JSON']
+when 'pure'
+ $:.unshift 'lib'
+ require 'json/pure'
+when 'ext'
+ $:.unshift 'ext', 'lib'
+ require 'json/ext'
+else
+ $:.unshift 'ext', 'lib'
+ require 'json'
+end
diff --git a/jni/ruby/test/json/test_json.rb b/jni/ruby/test/json/test_json.rb
new file mode 100755
index 0000000..98ef970
--- /dev/null
+++ b/jni/ruby/test/json/test_json.rb
@@ -0,0 +1,557 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+require 'stringio'
+require 'tempfile'
+require 'ostruct'
+
+unless Array.method_defined?(:permutation)
+ begin
+ require 'enumerator'
+ require 'permutation'
+ class Array
+ def permutation
+ Permutation.for(self).to_enum.map { |x| x.project }
+ end
+ end
+ rescue LoadError
+ warn "Skipping permutation tests."
+ end
+end
+
+class TestJSON < Test::Unit::TestCase
+ include JSON
+
+ def setup
+ @ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true].map do
+ |x| [x]
+ end
+ @ary_to_parse = ["1", '"foo"', "3.14", "4711.0", "2.718", "null",
+ "[1,-2,3]", "false", "true"].map do
+ |x| "[#{x}]"
+ end
+ @hash = {
+ 'a' => 2,
+ 'b' => 3.141,
+ 'c' => 'c',
+ 'd' => [ 1, "b", 3.14 ],
+ 'e' => { 'foo' => 'bar' },
+ 'g' => "\"\0\037",
+ 'h' => 1000.0,
+ 'i' => 0.001
+ }
+ @json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\
+ '"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}'
+ end
+
+ def test_construction
+ parser = JSON::Parser.new('test')
+ assert_equal 'test', parser.source
+ end
+
+ def assert_equal_float(expected, is)
+ assert_in_delta(expected.first, is.first, 1e-2)
+ end
+
+ def test_parse_simple_arrays
+ assert_equal([], parse('[]'))
+ assert_equal([], parse(' [ ] '))
+ assert_equal([nil], parse('[null]'))
+ assert_equal([false], parse('[false]'))
+ assert_equal([true], parse('[true]'))
+ assert_equal([-23], parse('[-23]'))
+ assert_equal([23], parse('[23]'))
+ assert_equal([0.23], parse('[0.23]'))
+ assert_equal([0.0], parse('[0e0]'))
+ assert_raises(JSON::ParserError) { parse('[+23.2]') }
+ assert_raises(JSON::ParserError) { parse('[+23]') }
+ assert_raises(JSON::ParserError) { parse('[.23]') }
+ assert_raises(JSON::ParserError) { parse('[023]') }
+ assert_equal_float [3.141], parse('[3.141]')
+ assert_equal_float [-3.141], parse('[-3.141]')
+ assert_equal_float [3.141], parse('[3141e-3]')
+ assert_equal_float [3.141], parse('[3141.1e-3]')
+ assert_equal_float [3.141], parse('[3141E-3]')
+ assert_equal_float [3.141], parse('[3141.0E-3]')
+ assert_equal_float [-3.141], parse('[-3141.0e-3]')
+ assert_equal_float [-3.141], parse('[-3141e-3]')
+ assert_raises(ParserError) { parse('[NaN]') }
+ assert parse('[NaN]', :allow_nan => true).first.nan?
+ assert_raises(ParserError) { parse('[Infinity]') }
+ assert_equal [1.0/0], parse('[Infinity]', :allow_nan => true)
+ assert_raises(ParserError) { parse('[-Infinity]') }
+ assert_equal [-1.0/0], parse('[-Infinity]', :allow_nan => true)
+ assert_equal([""], parse('[""]'))
+ assert_equal(["foobar"], parse('["foobar"]'))
+ assert_equal([{}], parse('[{}]'))
+ end
+
+ def test_parse_simple_objects
+ assert_equal({}, parse('{}'))
+ assert_equal({}, parse(' { } '))
+ assert_equal({ "a" => nil }, parse('{ "a" : null}'))
+ assert_equal({ "a" => nil }, parse('{"a":null}'))
+ assert_equal({ "a" => false }, parse('{ "a" : false } '))
+ assert_equal({ "a" => false }, parse('{"a":false}'))
+ assert_raises(JSON::ParserError) { parse('{false}') }
+ assert_equal({ "a" => true }, parse('{"a":true}'))
+ assert_equal({ "a" => true }, parse(' { "a" : true } '))
+ assert_equal({ "a" => -23 }, parse(' { "a" : -23 } '))
+ assert_equal({ "a" => -23 }, parse(' { "a" : -23 } '))
+ assert_equal({ "a" => 23 }, parse('{"a":23 } '))
+ assert_equal({ "a" => 23 }, parse(' { "a" : 23 } '))
+ assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
+ assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
+ end
+
+ def test_parse_json_primitive_values
+ assert_raise(JSON::ParserError) { JSON.parse('') }
+ assert_raise(JSON::ParserError) { JSON.parse('', :quirks_mode => true) }
+ assert_raise(TypeError) { JSON::Parser.new(nil).parse }
+ assert_raise(TypeError) { JSON::Parser.new(nil, :quirks_mode => true).parse }
+ assert_raise(TypeError) { JSON.parse(nil) }
+ assert_raise(TypeError) { JSON.parse(nil, :quirks_mode => true) }
+ assert_raise(JSON::ParserError) { JSON.parse(' /* foo */ ') }
+ assert_raise(JSON::ParserError) { JSON.parse(' /* foo */ ', :quirks_mode => true) }
+ parser = JSON::Parser.new('null')
+ assert_equal false, parser.quirks_mode?
+ assert_raise(JSON::ParserError) { parser.parse }
+ assert_raise(JSON::ParserError) { JSON.parse('null') }
+ assert_equal nil, JSON.parse('null', :quirks_mode => true)
+ parser = JSON::Parser.new('null', :quirks_mode => true)
+ assert_equal true, parser.quirks_mode?
+ assert_equal nil, parser.parse
+ assert_raise(JSON::ParserError) { JSON.parse('false') }
+ assert_equal false, JSON.parse('false', :quirks_mode => true)
+ assert_raise(JSON::ParserError) { JSON.parse('true') }
+ assert_equal true, JSON.parse('true', :quirks_mode => true)
+ assert_raise(JSON::ParserError) { JSON.parse('23') }
+ assert_equal 23, JSON.parse('23', :quirks_mode => true)
+ assert_raise(JSON::ParserError) { JSON.parse('1') }
+ assert_equal 1, JSON.parse('1', :quirks_mode => true)
+ assert_raise(JSON::ParserError) { JSON.parse('3.141') }
+ assert_in_delta 3.141, JSON.parse('3.141', :quirks_mode => true), 1E-3
+ assert_raise(JSON::ParserError) { JSON.parse('18446744073709551616') }
+ assert_equal 2 ** 64, JSON.parse('18446744073709551616', :quirks_mode => true)
+ assert_raise(JSON::ParserError) { JSON.parse('"foo"') }
+ assert_equal 'foo', JSON.parse('"foo"', :quirks_mode => true)
+ assert_raise(JSON::ParserError) { JSON.parse('NaN', :allow_nan => true) }
+ assert JSON.parse('NaN', :quirks_mode => true, :allow_nan => true).nan?
+ assert_raise(JSON::ParserError) { JSON.parse('Infinity', :allow_nan => true) }
+ assert JSON.parse('Infinity', :quirks_mode => true, :allow_nan => true).infinite?
+ assert_raise(JSON::ParserError) { JSON.parse('-Infinity', :allow_nan => true) }
+ assert JSON.parse('-Infinity', :quirks_mode => true, :allow_nan => true).infinite?
+ assert_raise(JSON::ParserError) { JSON.parse('[ 1, ]', :quirks_mode => true) }
+ end
+
+ if Array.method_defined?(:permutation)
+ def test_parse_more_complex_arrays
+ a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
+ a.permutation.each do |perm|
+ json = pretty_generate(perm)
+ assert_equal perm, parse(json)
+ end
+ end
+
+ def test_parse_complex_objects
+ a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
+ a.permutation.each do |perm|
+ s = "a"
+ orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
+ json = pretty_generate(orig_obj)
+ assert_equal orig_obj, parse(json)
+ end
+ end
+ end
+
+ def test_parse_arrays
+ assert_equal([1,2,3], parse('[1,2,3]'))
+ assert_equal([1.2,2,3], parse('[1.2,2,3]'))
+ assert_equal([[],[[],[]]], parse('[[],[[],[]]]'))
+ end
+
+ def test_parse_values
+ assert_equal([""], parse('[""]'))
+ assert_equal(["\\"], parse('["\\\\"]'))
+ assert_equal(['"'], parse('["\""]'))
+ assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]'))
+ assert_equal(["\"\b\n\r\t\0\037"],
+ parse('["\"\b\n\r\t\u0000\u001f"]'))
+ for i in 0 ... @ary.size
+ assert_equal(@ary[i], parse(@ary_to_parse[i]))
+ end
+ end
+
+ def test_parse_array
+ assert_equal([], parse('[]'))
+ assert_equal([], parse(' [ ] '))
+ assert_equal([1], parse('[1]'))
+ assert_equal([1], parse(' [ 1 ] '))
+ assert_equal(@ary,
+ parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]]'\
+ ',[false],[true]]'))
+ assert_equal(@ary, parse(%Q{ [ [1] , ["foo"] , [3.14] \t , [47.11e+2]\s
+ , [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ] }))
+ end
+
+ class SubArray < Array
+ def <<(v)
+ @shifted = true
+ super
+ end
+
+ def shifted?
+ @shifted
+ end
+ end
+
+ class SubArray2 < Array
+ def to_json(*a)
+ {
+ JSON.create_id => self.class.name,
+ 'ary' => to_a,
+ }.to_json(*a)
+ end
+
+ def self.json_create(o)
+ o.delete JSON.create_id
+ o['ary']
+ end
+ end
+
+ class SubArrayWrapper
+ def initialize
+ @data = []
+ end
+
+ attr_reader :data
+
+ def [](index)
+ @data[index]
+ end
+
+ def <<(value)
+ @data << value
+ @shifted = true
+ end
+
+ def shifted?
+ @shifted
+ end
+ end
+
+ def test_parse_array_custom_array_derived_class
+ res = parse('[1,2]', :array_class => SubArray)
+ assert_equal([1,2], res)
+ assert_equal(SubArray, res.class)
+ assert res.shifted?
+ end
+
+ def test_parse_array_custom_non_array_derived_class
+ res = parse('[1,2]', :array_class => SubArrayWrapper)
+ assert_equal([1,2], res.data)
+ assert_equal(SubArrayWrapper, res.class)
+ assert res.shifted?
+ end
+
+ def test_parse_object
+ assert_equal({}, parse('{}'))
+ assert_equal({}, parse(' { } '))
+ assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}'))
+ assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } '))
+ end
+
+ class SubHash < Hash
+ def []=(k, v)
+ @item_set = true
+ super
+ end
+
+ def item_set?
+ @item_set
+ end
+ end
+
+ class SubHash2 < Hash
+ def to_json(*a)
+ {
+ JSON.create_id => self.class.name,
+ }.merge(self).to_json(*a)
+ end
+
+ def self.json_create(o)
+ o.delete JSON.create_id
+ self[o]
+ end
+ end
+
+ class SubOpenStruct < OpenStruct
+ def [](k)
+ __send__(k)
+ end
+
+ def []=(k, v)
+ @item_set = true
+ __send__("#{k}=", v)
+ end
+
+ def item_set?
+ @item_set
+ end
+ end
+
+ def test_parse_object_custom_hash_derived_class
+ res = parse('{"foo":"bar"}', :object_class => SubHash)
+ assert_equal({"foo" => "bar"}, res)
+ assert_equal(SubHash, res.class)
+ assert res.item_set?
+ end
+
+ def test_parse_object_custom_non_hash_derived_class
+ res = parse('{"foo":"bar"}', :object_class => SubOpenStruct)
+ assert_equal "bar", res.foo
+ assert_equal(SubOpenStruct, res.class)
+ assert res.item_set?
+ end
+
+ def test_parse_generic_object
+ res = parse('{"foo":"bar", "baz":{}}', :object_class => JSON::GenericObject)
+ assert_equal(JSON::GenericObject, res.class)
+ assert_equal "bar", res.foo
+ assert_equal "bar", res["foo"]
+ assert_equal "bar", res[:foo]
+ assert_equal "bar", res.to_hash[:foo]
+ assert_equal(JSON::GenericObject, res.baz.class)
+ end
+
+ def test_generate_core_subclasses_with_new_to_json
+ obj = SubHash2["foo" => SubHash2["bar" => true]]
+ obj_json = JSON(obj)
+ obj_again = JSON.parse(obj_json, :create_additions => true)
+ assert_kind_of SubHash2, obj_again
+ assert_kind_of SubHash2, obj_again['foo']
+ assert obj_again['foo']['bar']
+ assert_equal obj, obj_again
+ assert_equal ["foo"], JSON(JSON(SubArray2["foo"]), :create_additions => true)
+ end
+
+ def test_generate_core_subclasses_with_default_to_json
+ assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"])
+ assert_equal '["foo"]', JSON(SubArray["foo"])
+ end
+
+ def test_generate_of_core_subclasses
+ obj = SubHash["foo" => SubHash["bar" => true]]
+ obj_json = JSON(obj)
+ obj_again = JSON(obj_json)
+ assert_kind_of Hash, obj_again
+ assert_kind_of Hash, obj_again['foo']
+ assert obj_again['foo']['bar']
+ assert_equal obj, obj_again
+ end
+
+ def test_parser_reset
+ parser = Parser.new(@json)
+ assert_equal(@hash, parser.parse)
+ assert_equal(@hash, parser.parse)
+ end
+
+ def test_comments
+ json = <<EOT
+{
+ "key1":"value1", // eol comment
+ "key2":"value2" /* multi line
+ * comment */,
+ "key3":"value3" /* multi line
+ // nested eol comment
+ * comment */
+}
+EOT
+ assert_equal(
+ { "key1" => "value1", "key2" => "value2", "key3" => "value3" },
+ parse(json))
+ json = <<EOT
+{
+ "key1":"value1" /* multi line
+ // nested eol comment
+ /* illegal nested multi line comment */
+ * comment */
+}
+EOT
+ assert_raises(ParserError) { parse(json) }
+ json = <<EOT
+{
+ "key1":"value1" /* multi line
+ // nested eol comment
+ closed multi comment */
+ and again, throw an Error */
+}
+EOT
+ assert_raises(ParserError) { parse(json) }
+ json = <<EOT
+{
+ "key1":"value1" /*/*/
+}
+EOT
+ assert_equal({ "key1" => "value1" }, parse(json))
+ end
+
+ def test_backslash
+ data = [ '\\.(?i:gif|jpe?g|png)$' ]
+ json = '["\\\\.(?i:gif|jpe?g|png)$"]'
+ assert_equal json, JSON.generate(data)
+ assert_equal data, JSON.parse(json)
+ #
+ data = [ '\\"' ]
+ json = '["\\\\\""]'
+ assert_equal json, JSON.generate(data)
+ assert_equal data, JSON.parse(json)
+ #
+ json = '["/"]'
+ data = JSON.parse(json)
+ assert_equal ['/'], data
+ assert_equal json, JSON.generate(data)
+ #
+ json = '["\""]'
+ data = JSON.parse(json)
+ assert_equal ['"'], data
+ assert_equal json, JSON.generate(data)
+ json = '["\\\'"]'
+ data = JSON.parse(json)
+ assert_equal ["'"], data
+ assert_equal '["\'"]', JSON.generate(data)
+ end
+
+ def test_wrong_inputs
+ assert_raises(ParserError) { JSON.parse('"foo"') }
+ assert_raises(ParserError) { JSON.parse('123') }
+ assert_raises(ParserError) { JSON.parse('[] bla') }
+ assert_raises(ParserError) { JSON.parse('[] 1') }
+ assert_raises(ParserError) { JSON.parse('[] []') }
+ assert_raises(ParserError) { JSON.parse('[] {}') }
+ assert_raises(ParserError) { JSON.parse('{} []') }
+ assert_raises(ParserError) { JSON.parse('{} {}') }
+ assert_raises(ParserError) { JSON.parse('[NULL]') }
+ assert_raises(ParserError) { JSON.parse('[FALSE]') }
+ assert_raises(ParserError) { JSON.parse('[TRUE]') }
+ assert_raises(ParserError) { JSON.parse('[07] ') }
+ assert_raises(ParserError) { JSON.parse('[0a]') }
+ assert_raises(ParserError) { JSON.parse('[1.]') }
+ assert_raises(ParserError) { JSON.parse(' ') }
+ end
+
+ def test_nesting
+ assert_raises(JSON::NestingError) { JSON.parse '[[]]', :max_nesting => 1 }
+ assert_raises(JSON::NestingError) { JSON.parser.new('[[]]', :max_nesting => 1).parse }
+ assert_equal [[]], JSON.parse('[[]]', :max_nesting => 2)
+ too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
+ too_deep_ary = eval too_deep
+ assert_raises(JSON::NestingError) { JSON.parse too_deep }
+ assert_raises(JSON::NestingError) { JSON.parser.new(too_deep).parse }
+ assert_raises(JSON::NestingError) { JSON.parse too_deep, :max_nesting => 100 }
+ ok = JSON.parse too_deep, :max_nesting => 101
+ assert_equal too_deep_ary, ok
+ ok = JSON.parse too_deep, :max_nesting => nil
+ assert_equal too_deep_ary, ok
+ ok = JSON.parse too_deep, :max_nesting => false
+ assert_equal too_deep_ary, ok
+ ok = JSON.parse too_deep, :max_nesting => 0
+ assert_equal too_deep_ary, ok
+ assert_raises(JSON::NestingError) { JSON.generate [[]], :max_nesting => 1 }
+ assert_equal '[[]]', JSON.generate([[]], :max_nesting => 2)
+ assert_raises(JSON::NestingError) { JSON.generate too_deep_ary }
+ assert_raises(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 100 }
+ ok = JSON.generate too_deep_ary, :max_nesting => 101
+ assert_equal too_deep, ok
+ ok = JSON.generate too_deep_ary, :max_nesting => nil
+ assert_equal too_deep, ok
+ ok = JSON.generate too_deep_ary, :max_nesting => false
+ assert_equal too_deep, ok
+ ok = JSON.generate too_deep_ary, :max_nesting => 0
+ assert_equal too_deep, ok
+ end
+
+ def test_symbolize_names
+ assert_equal({ "foo" => "bar", "baz" => "quux" },
+ JSON.parse('{"foo":"bar", "baz":"quux"}'))
+ assert_equal({ :foo => "bar", :baz => "quux" },
+ JSON.parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true))
+ end
+
+ def test_load
+ assert_equal @hash, JSON.load(@json)
+ tempfile = Tempfile.open('json')
+ tempfile.write @json
+ tempfile.rewind
+ assert_equal @hash, JSON.load(tempfile)
+ stringio = StringIO.new(@json)
+ stringio.rewind
+ assert_equal @hash, JSON.load(stringio)
+ assert_equal nil, JSON.load(nil)
+ assert_equal nil, JSON.load('')
+ ensure
+ tempfile.close!
+ end
+
+ def test_load_with_options
+ small_hash = JSON("foo" => 'bar')
+ symbol_hash = { :foo => 'bar' }
+ assert_equal symbol_hash, JSON.load(small_hash, nil, :symbolize_names => true)
+ end
+
+ def test_dump
+ too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
+ assert_equal too_deep, JSON.dump(eval(too_deep))
+ assert_kind_of String, Marshal.dump(eval(too_deep))
+ assert_raises(ArgumentError) { JSON.dump(eval(too_deep), 100) }
+ assert_raises(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
+ assert_equal too_deep, JSON.dump(eval(too_deep), 101)
+ assert_kind_of String, Marshal.dump(eval(too_deep), 101)
+ output = StringIO.new
+ JSON.dump(eval(too_deep), output)
+ assert_equal too_deep, output.string
+ output = StringIO.new
+ JSON.dump(eval(too_deep), output, 101)
+ assert_equal too_deep, output.string
+ end
+
+ def test_big_integers
+ json1 = JSON([orig = (1 << 31) - 1])
+ assert_equal orig, JSON[json1][0]
+ json2 = JSON([orig = 1 << 31])
+ assert_equal orig, JSON[json2][0]
+ json3 = JSON([orig = (1 << 62) - 1])
+ assert_equal orig, JSON[json3][0]
+ json4 = JSON([orig = 1 << 62])
+ assert_equal orig, JSON[json4][0]
+ json5 = JSON([orig = 1 << 64])
+ assert_equal orig, JSON[json5][0]
+ end
+
+ if defined?(JSON::Ext::Parser)
+ def test_allocate
+ parser = JSON::Ext::Parser.new("{}")
+ assert_raise(TypeError, '[ruby-core:35079]') {parser.__send__(:initialize, "{}")}
+ parser = JSON::Ext::Parser.allocate
+ assert_raise(TypeError, '[ruby-core:35079]') {parser.source}
+ end
+ end
+
+ def test_argument_encoding
+ source = "{}".force_encoding("ascii-8bit")
+ JSON::Parser.new(source)
+ assert_equal Encoding::ASCII_8BIT, source.encoding
+ end if defined?(Encoding::ASCII_8BIT)
+
+ def test_error_message_encoding
+ bug10705 = '[ruby-core:67386] [Bug #10705]'
+ json = "\"\xE2\x88\x9A\"".force_encoding(Encoding::UTF_8)
+ e = assert_raise(JSON::ParserError) {
+ JSON.parse(json)
+ }
+ assert_equal(Encoding::UTF_8, e.message.encoding, bug10705)
+ assert_include(e.message, json, bug10705)
+ end if defined?(Encoding::UTF_8)
+end
diff --git a/jni/ruby/test/json/test_json_addition.rb b/jni/ruby/test/json/test_json_addition.rb
new file mode 100755
index 0000000..a30f06a
--- /dev/null
+++ b/jni/ruby/test/json/test_json_addition.rb
@@ -0,0 +1,196 @@
+#!/usr/bin/env ruby
+# -*- coding:utf-8 -*-
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+require 'json/add/core'
+require 'json/add/complex'
+require 'json/add/rational'
+require 'json/add/bigdecimal'
+require 'json/add/ostruct'
+require 'date'
+
+class TestJSONAddition < Test::Unit::TestCase
+ include JSON
+
+ class A
+ def initialize(a)
+ @a = a
+ end
+
+ attr_reader :a
+
+ def ==(other)
+ a == other.a
+ end
+
+ def self.json_create(object)
+ new(*object['args'])
+ end
+
+ def to_json(*args)
+ {
+ 'json_class' => self.class.name,
+ 'args' => [ @a ],
+ }.to_json(*args)
+ end
+ end
+
+ class A2 < A
+ def to_json(*args)
+ {
+ 'json_class' => self.class.name,
+ 'args' => [ @a ],
+ }.to_json(*args)
+ end
+ end
+
+ class B
+ def self.json_creatable?
+ false
+ end
+
+ def to_json(*args)
+ {
+ 'json_class' => self.class.name,
+ }.to_json(*args)
+ end
+ end
+
+ class C
+ def self.json_creatable?
+ false
+ end
+
+ def to_json(*args)
+ {
+ 'json_class' => 'TestJSONAddition::Nix',
+ }.to_json(*args)
+ end
+ end
+
+ def test_extended_json
+ a = A.new(666)
+ assert A.json_creatable?
+ json = generate(a)
+ a_again = JSON.parse(json, :create_additions => true)
+ assert_kind_of a.class, a_again
+ assert_equal a, a_again
+ end
+
+ def test_extended_json_default
+ a = A.new(666)
+ assert A.json_creatable?
+ json = generate(a)
+ a_hash = JSON.parse(json)
+ assert_kind_of Hash, a_hash
+ end
+
+ def test_extended_json_disabled
+ a = A.new(666)
+ assert A.json_creatable?
+ json = generate(a)
+ a_again = JSON.parse(json, :create_additions => true)
+ assert_kind_of a.class, a_again
+ assert_equal a, a_again
+ a_hash = JSON.parse(json, :create_additions => false)
+ assert_kind_of Hash, a_hash
+ assert_equal(
+ {"args"=>[666], "json_class"=>"TestJSONAddition::A"}.sort_by { |k,| k },
+ a_hash.sort_by { |k,| k }
+ )
+ end
+
+ def test_extended_json_fail1
+ b = B.new
+ assert !B.json_creatable?
+ json = generate(b)
+ assert_equal({ "json_class"=>"TestJSONAddition::B" }, JSON.parse(json))
+ end
+
+ def test_extended_json_fail2
+ c = C.new
+ assert !C.json_creatable?
+ json = generate(c)
+ assert_raises(ArgumentError, NameError) { JSON.parse(json, :create_additions => true) }
+ end
+
+ def test_raw_strings
+ raw = ''
+ raw.respond_to?(:encode!) and raw.encode!(Encoding::ASCII_8BIT)
+ raw_array = []
+ for i in 0..255
+ raw << i
+ raw_array << i
+ end
+ json = raw.to_json_raw
+ json_raw_object = raw.to_json_raw_object
+ hash = { 'json_class' => 'String', 'raw'=> raw_array }
+ assert_equal hash, json_raw_object
+ assert_match(/\A\{.*\}\z/, json)
+ assert_match(/"json_class":"String"/, json)
+ assert_match(/"raw":\[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255\]/, json)
+ raw_again = JSON.parse(json, :create_additions => true)
+ assert_equal raw, raw_again
+ end
+
+ MyJsonStruct = Struct.new 'MyJsonStruct', :foo, :bar
+
+ def test_core
+ t = Time.now
+ assert_equal t, JSON(JSON(t), :create_additions => true)
+ d = Date.today
+ assert_equal d, JSON(JSON(d), :create_additions => true)
+ d = DateTime.civil(2007, 6, 14, 14, 57, 10, Rational(1, 12), 2299161)
+ assert_equal d, JSON(JSON(d), :create_additions => true)
+ assert_equal 1..10, JSON(JSON(1..10), :create_additions => true)
+ assert_equal 1...10, JSON(JSON(1...10), :create_additions => true)
+ assert_equal "a".."c", JSON(JSON("a".."c"), :create_additions => true)
+ assert_equal "a"..."c", JSON(JSON("a"..."c"), :create_additions => true)
+ s = MyJsonStruct.new 4711, 'foot'
+ assert_equal s, JSON(JSON(s), :create_additions => true)
+ struct = Struct.new :foo, :bar
+ s = struct.new 4711, 'foot'
+ assert_raises(JSONError) { JSON(s) }
+ begin
+ raise TypeError, "test me"
+ rescue TypeError => e
+ e_json = JSON.generate e
+ e_again = JSON e_json, :create_additions => true
+ assert_kind_of TypeError, e_again
+ assert_equal e.message, e_again.message
+ assert_equal e.backtrace, e_again.backtrace
+ end
+ assert_equal(/foo/, JSON(JSON(/foo/), :create_additions => true))
+ assert_equal(/foo/i, JSON(JSON(/foo/i), :create_additions => true))
+ end
+
+ def test_utc_datetime
+ now = Time.now
+ d = DateTime.parse(now.to_s, :create_additions => true) # usual case
+ assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+ d = DateTime.parse(now.utc.to_s) # of = 0
+ assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+ d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(1,24))
+ assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+ d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(12,24))
+ assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+ end
+
+ def test_rational_complex
+ assert_equal Rational(2, 9), JSON.parse(JSON(Rational(2, 9)), :create_additions => true)
+ assert_equal Complex(2, 9), JSON.parse(JSON(Complex(2, 9)), :create_additions => true)
+ end
+
+ def test_bigdecimal
+ assert_equal BigDecimal('3.141', 23), JSON(JSON(BigDecimal('3.141', 23)), :create_additions => true)
+ assert_equal BigDecimal('3.141', 666), JSON(JSON(BigDecimal('3.141', 666)), :create_additions => true)
+ end
+
+ def test_ostruct
+ o = OpenStruct.new
+ # XXX this won't work; o.foo = { :bar => true }
+ o.foo = { 'bar' => true }
+ assert_equal o, JSON.parse(JSON(o), :create_additions => true)
+ end
+end
diff --git a/jni/ruby/test/json/test_json_encoding.rb b/jni/ruby/test/json/test_json_encoding.rb
new file mode 100644
index 0000000..fa7d878
--- /dev/null
+++ b/jni/ruby/test/json/test_json_encoding.rb
@@ -0,0 +1,65 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+
+class TestJSONEncoding < Test::Unit::TestCase
+ include JSON
+
+ def setup
+ @utf_8 = '["© ≠ €!"]'
+ @parsed = [ "© ≠ €!" ]
+ @generated = '["\u00a9 \u2260 \u20ac!"]'
+ if String.method_defined?(:encode)
+ @utf_16_data = [@parsed.first.encode('utf-16be', 'utf-8')]
+ @utf_8_ascii_8bit = @utf_8.dup.force_encoding(Encoding::ASCII_8BIT)
+ @utf_16be = @utf_8.encode('utf-16be', 'utf-8')
+ @utf_16be_ascii_8bit = @utf_16be.dup.force_encoding(Encoding::ASCII_8BIT)
+ @utf_16le = @utf_8.encode('utf-16le', 'utf-8')
+ @utf_16le_ascii_8bit = @utf_16le.dup.force_encoding(Encoding::ASCII_8BIT)
+ @utf_32be = @utf_8.encode('utf-32be', 'utf-8')
+ @utf_32be_ascii_8bit = @utf_32be.dup.force_encoding(Encoding::ASCII_8BIT)
+ @utf_32le = @utf_8.encode('utf-32le', 'utf-8')
+ @utf_32le_ascii_8bit = @utf_32le.dup.force_encoding(Encoding::ASCII_8BIT)
+ else
+ require 'iconv'
+ @utf_16_data = Iconv.iconv('utf-16be', 'utf-8', @parsed.first)
+ @utf_8_ascii_8bit = @utf_8.dup
+ @utf_16be, = Iconv.iconv('utf-16be', 'utf-8', @utf_8)
+ @utf_16be_ascii_8bit = @utf_16be.dup
+ @utf_16le, = Iconv.iconv('utf-16le', 'utf-8', @utf_8)
+ @utf_16le_ascii_8bit = @utf_16le.dup
+ @utf_32be, = Iconv.iconv('utf-32be', 'utf-8', @utf_8)
+ @utf_32be_ascii_8bit = @utf_32be.dup
+ @utf_32le, = Iconv.iconv('utf-32le', 'utf-8', @utf_8)
+ @utf_32le_ascii_8bit = @utf_32le.dup
+ end
+ end
+
+ def test_parse
+ assert_equal @parsed, JSON.parse(@utf_8)
+ assert_equal @parsed, JSON.parse(@utf_16be)
+ assert_equal @parsed, JSON.parse(@utf_16le)
+ assert_equal @parsed, JSON.parse(@utf_32be)
+ assert_equal @parsed, JSON.parse(@utf_32le)
+ end
+
+ def test_parse_ascii_8bit
+ assert_equal @parsed, JSON.parse(@utf_8_ascii_8bit)
+ assert_equal @parsed, JSON.parse(@utf_16be_ascii_8bit)
+ assert_equal @parsed, JSON.parse(@utf_16le_ascii_8bit)
+ assert_equal @parsed, JSON.parse(@utf_32be_ascii_8bit)
+ assert_equal @parsed, JSON.parse(@utf_32le_ascii_8bit)
+ end
+
+ def test_generate
+ assert_equal @generated, JSON.generate(@parsed, :ascii_only => true)
+ if defined?(::Encoding)
+ assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true)
+ else
+ # XXX checking of correct utf8 data is not as strict (yet?) without :ascii_only
+ assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data, :ascii_only => true) }
+ end
+ end
+end
diff --git a/jni/ruby/test/json/test_json_fixtures.rb b/jni/ruby/test/json/test_json_fixtures.rb
new file mode 100755
index 0000000..584dffd
--- /dev/null
+++ b/jni/ruby/test/json/test_json_fixtures.rb
@@ -0,0 +1,35 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+
+class TestJSONFixtures < Test::Unit::TestCase
+ def setup
+ fixtures = File.join(File.dirname(__FILE__), 'fixtures/*.json')
+ passed, failed = Dir[fixtures].partition { |f| f['pass'] }
+ @passed = passed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
+ @failed = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
+ end
+
+ def test_passing
+ for name, source in @passed
+ begin
+ assert JSON.parse(source),
+ "Did not pass for fixture '#{name}': #{source.inspect}"
+ rescue => e
+ warn "\nCaught #{e.class}(#{e}) for fixture '#{name}': #{source.inspect}\n#{e.backtrace * "\n"}"
+ raise e
+ end
+ end
+ end
+
+ def test_failing
+ for name, source in @failed
+ assert_raises(JSON::ParserError, JSON::NestingError,
+ "Did not fail for fixture '#{name}': #{source.inspect}") do
+ JSON.parse(source)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/json/test_json_generate.rb b/jni/ruby/test/json/test_json_generate.rb
new file mode 100755
index 0000000..2d4e1ee
--- /dev/null
+++ b/jni/ruby/test/json/test_json_generate.rb
@@ -0,0 +1,322 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+
+class TestJSONGenerate < Test::Unit::TestCase
+ include JSON
+
+ def setup
+ @hash = {
+ 'a' => 2,
+ 'b' => 3.141,
+ 'c' => 'c',
+ 'd' => [ 1, "b", 3.14 ],
+ 'e' => { 'foo' => 'bar' },
+ 'g' => "\"\0\037",
+ 'h' => 1000.0,
+ 'i' => 0.001
+ }
+ @json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
+ '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
+ @json3 = <<'EOT'.chomp
+{
+ "a": 2,
+ "b": 3.141,
+ "c": "c",
+ "d": [
+ 1,
+ "b",
+ 3.14
+ ],
+ "e": {
+ "foo": "bar"
+ },
+ "g": "\"\u0000\u001f",
+ "h": 1000.0,
+ "i": 0.001
+}
+EOT
+ end
+
+ def test_generate
+ json = generate(@hash)
+ assert_equal(JSON.parse(@json2), JSON.parse(json))
+ json = JSON[@hash]
+ assert_equal(JSON.parse(@json2), JSON.parse(json))
+ parsed_json = parse(json)
+ assert_equal(@hash, parsed_json)
+ json = generate({1=>2})
+ assert_equal('{"1":2}', json)
+ parsed_json = parse(json)
+ assert_equal({"1"=>2}, parsed_json)
+ assert_raise(GeneratorError) { generate(666) }
+ assert_equal '666', generate(666, :quirks_mode => true)
+ end
+
+ def test_generate_pretty
+ json = pretty_generate(@hash)
+ # hashes aren't (insertion) ordered on every ruby implementation assert_equal(@json3, json)
+ assert_equal(JSON.parse(@json3), JSON.parse(json))
+ parsed_json = parse(json)
+ assert_equal(@hash, parsed_json)
+ json = pretty_generate({1=>2})
+ assert_equal(<<'EOT'.chomp, json)
+{
+ "1": 2
+}
+EOT
+ parsed_json = parse(json)
+ assert_equal({"1"=>2}, parsed_json)
+ assert_raise(GeneratorError) { pretty_generate(666) }
+ assert_equal '666', pretty_generate(666, :quirks_mode => true)
+ end
+
+ def test_fast_generate
+ json = fast_generate(@hash)
+ assert_equal(JSON.parse(@json2), JSON.parse(json))
+ parsed_json = parse(json)
+ assert_equal(@hash, parsed_json)
+ json = fast_generate({1=>2})
+ assert_equal('{"1":2}', json)
+ parsed_json = parse(json)
+ assert_equal({"1"=>2}, parsed_json)
+ assert_raise(GeneratorError) { fast_generate(666) }
+ assert_equal '666', fast_generate(666, :quirks_mode => true)
+ end
+
+ def test_own_state
+ state = State.new
+ json = generate(@hash, state)
+ assert_equal(JSON.parse(@json2), JSON.parse(json))
+ parsed_json = parse(json)
+ assert_equal(@hash, parsed_json)
+ json = generate({1=>2}, state)
+ assert_equal('{"1":2}', json)
+ parsed_json = parse(json)
+ assert_equal({"1"=>2}, parsed_json)
+ assert_raise(GeneratorError) { generate(666, state) }
+ state.quirks_mode = true
+ assert state.quirks_mode?
+ assert_equal '666', generate(666, state)
+ end
+
+ def test_states
+ json = generate({1=>2}, nil)
+ assert_equal('{"1":2}', json)
+ s = JSON.state.new
+ assert s.check_circular?
+ assert s[:check_circular?]
+ h = { 1=>2 }
+ h[3] = h
+ assert_raises(JSON::NestingError) { generate(h) }
+ assert_raises(JSON::NestingError) { generate(h, s) }
+ s = JSON.state.new
+ a = [ 1, 2 ]
+ a << a
+ assert_raises(JSON::NestingError) { generate(a, s) }
+ assert s.check_circular?
+ assert s[:check_circular?]
+ end
+
+ def test_pretty_state
+ state = PRETTY_STATE_PROTOTYPE.dup
+ assert_equal({
+ :allow_nan => false,
+ :array_nl => "\n",
+ :ascii_only => false,
+ :buffer_initial_length => 1024,
+ :quirks_mode => false,
+ :depth => 0,
+ :indent => " ",
+ :max_nesting => 100,
+ :object_nl => "\n",
+ :space => " ",
+ :space_before => "",
+ }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
+ end
+
+ def test_safe_state
+ state = SAFE_STATE_PROTOTYPE.dup
+ assert_equal({
+ :allow_nan => false,
+ :array_nl => "",
+ :ascii_only => false,
+ :buffer_initial_length => 1024,
+ :quirks_mode => false,
+ :depth => 0,
+ :indent => "",
+ :max_nesting => 100,
+ :object_nl => "",
+ :space => "",
+ :space_before => "",
+ }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
+ end
+
+ def test_fast_state
+ state = FAST_STATE_PROTOTYPE.dup
+ assert_equal({
+ :allow_nan => false,
+ :array_nl => "",
+ :ascii_only => false,
+ :buffer_initial_length => 1024,
+ :quirks_mode => false,
+ :depth => 0,
+ :indent => "",
+ :max_nesting => 0,
+ :object_nl => "",
+ :space => "",
+ :space_before => "",
+ }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
+ end
+
+ def test_allow_nan
+ assert_raises(GeneratorError) { generate([JSON::NaN]) }
+ assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
+ assert_raises(GeneratorError) { fast_generate([JSON::NaN]) }
+ assert_raises(GeneratorError) { pretty_generate([JSON::NaN]) }
+ assert_equal "[\n NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
+ assert_raises(GeneratorError) { generate([JSON::Infinity]) }
+ assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
+ assert_raises(GeneratorError) { fast_generate([JSON::Infinity]) }
+ assert_raises(GeneratorError) { pretty_generate([JSON::Infinity]) }
+ assert_equal "[\n Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
+ assert_raises(GeneratorError) { generate([JSON::MinusInfinity]) }
+ assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
+ assert_raises(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
+ assert_raises(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
+ assert_equal "[\n -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
+ end
+
+ def test_depth
+ ary = []; ary << ary
+ assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
+ assert_raises(JSON::NestingError) { JSON.generate(ary) }
+ assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
+ assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
+ assert_raises(JSON::NestingError) { JSON.pretty_generate(ary) }
+ assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
+ s = JSON.state.new
+ assert_equal 0, s.depth
+ assert_raises(JSON::NestingError) { ary.to_json(s) }
+ assert_equal 100, s.depth
+ end
+
+ def test_buffer_initial_length
+ s = JSON.state.new
+ assert_equal 1024, s.buffer_initial_length
+ s.buffer_initial_length = 0
+ assert_equal 1024, s.buffer_initial_length
+ s.buffer_initial_length = -1
+ assert_equal 1024, s.buffer_initial_length
+ s.buffer_initial_length = 128
+ assert_equal 128, s.buffer_initial_length
+ end
+
+ def test_gc
+ assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], [])
+ bignum_too_long_to_embed_as_string = 1234567890123456789012345
+ expect = bignum_too_long_to_embed_as_string.to_s
+ GC.stress = true
+
+ 10.times do |i|
+ tmp = bignum_too_long_to_embed_as_string.to_json
+ raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
+ end
+ EOS
+ end if GC.respond_to?(:stress=)
+
+ def test_configure_using_configure_and_merge
+ numbered_state = {
+ :indent => "1",
+ :space => '2',
+ :space_before => '3',
+ :object_nl => '4',
+ :array_nl => '5'
+ }
+ state1 = JSON.state.new
+ state1.merge(numbered_state)
+ assert_equal '1', state1.indent
+ assert_equal '2', state1.space
+ assert_equal '3', state1.space_before
+ assert_equal '4', state1.object_nl
+ assert_equal '5', state1.array_nl
+ state2 = JSON.state.new
+ state2.configure(numbered_state)
+ assert_equal '1', state2.indent
+ assert_equal '2', state2.space
+ assert_equal '3', state2.space_before
+ assert_equal '4', state2.object_nl
+ assert_equal '5', state2.array_nl
+ end
+
+ def test_configure_hash_conversion
+ state = JSON.state.new
+ state.configure(:indent => '1')
+ assert_equal '1', state.indent
+ state = JSON.state.new
+ foo = 'foo'
+ assert_raise(TypeError) do
+ state.configure(foo)
+ end
+ def foo.to_h
+ { :indent => '2' }
+ end
+ state.configure(foo)
+ assert_equal '2', state.indent
+ end
+
+ if defined?(JSON::Ext::Generator)
+ def test_broken_bignum # [ruby-core:38867]
+ pid = fork do
+ Bignum.class_eval do
+ def to_s
+ end
+ end
+ begin
+ JSON::Ext::Generator::State.new.generate(1<<64)
+ exit 1
+ rescue TypeError
+ exit 0
+ end
+ end
+ _, status = Process.waitpid2(pid)
+ assert status.success?
+ rescue NotImplementedError
+ # forking to avoid modifying core class of a parent process and
+ # introducing race conditions of tests are run in parallel
+ end
+ end
+
+ def test_hash_likeness_set_symbol
+ state = JSON.state.new
+ assert_equal nil, state[:foo]
+ assert_equal nil.class, state[:foo].class
+ assert_equal nil, state['foo']
+ state[:foo] = :bar
+ assert_equal :bar, state[:foo]
+ assert_equal :bar, state['foo']
+ state_hash = state.to_hash
+ assert_kind_of Hash, state_hash
+ assert_equal :bar, state_hash[:foo]
+ end
+
+ def test_hash_likeness_set_string
+ state = JSON.state.new
+ assert_equal nil, state[:foo]
+ assert_equal nil, state['foo']
+ state['foo'] = :bar
+ assert_equal :bar, state[:foo]
+ assert_equal :bar, state['foo']
+ state_hash = state.to_hash
+ assert_kind_of Hash, state_hash
+ assert_equal :bar, state_hash[:foo]
+ end
+
+ def test_json_generate
+ assert_raise JSON::GeneratorError do
+ assert_equal true, JSON.generate(["\xea"])
+ end
+ end
+end
diff --git a/jni/ruby/test/json/test_json_generic_object.rb b/jni/ruby/test/json/test_json_generic_object.rb
new file mode 100644
index 0000000..c43c776
--- /dev/null
+++ b/jni/ruby/test/json/test_json_generic_object.rb
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+class TestJSONGenericObject < Test::Unit::TestCase
+ include JSON
+
+ def setup
+ @go = GenericObject[ :a => 1, :b => 2 ]
+ end
+
+ def test_attributes
+ assert_equal 1, @go.a
+ assert_equal 1, @go[:a]
+ assert_equal 2, @go.b
+ assert_equal 2, @go[:b]
+ assert_nil @go.c
+ assert_nil @go[:c]
+ end
+
+ def test_generate_json
+ switch_json_creatable do
+ assert_equal @go, JSON(JSON(@go), :create_additions => true)
+ end
+ end
+
+ def test_parse_json
+ assert_kind_of Hash, JSON('{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', :create_additions => true)
+ switch_json_creatable do
+ assert_equal @go, l = JSON('{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', :create_additions => true)
+ assert_equal 1, l.a
+ assert_equal @go, l = JSON('{ "a": 1, "b": 2 }', :object_class => GenericObject)
+ assert_equal 1, l.a
+ assert_equal GenericObject[:a => GenericObject[:b => 2]],
+ l = JSON('{ "a": { "b": 2 } }', :object_class => GenericObject)
+ assert_equal 2, l.a.b
+ end
+ end
+
+ def test_from_hash
+ result = GenericObject.from_hash(
+ :foo => { :bar => { :baz => true }, :quux => [ { :foobar => true } ] })
+ assert_kind_of GenericObject, result.foo
+ assert_kind_of GenericObject, result.foo.bar
+ assert_equal true, result.foo.bar.baz
+ assert_kind_of GenericObject, result.foo.quux.first
+ assert_equal true, result.foo.quux.first.foobar
+ assert_equal true, GenericObject.from_hash(true)
+ end
+
+ def test_json_generic_object_load
+ empty = JSON::GenericObject.load(nil)
+ assert_kind_of JSON::GenericObject, empty
+ simple_json = '{"json_class":"JSON::GenericObject","hello":"world"}'
+ simple = JSON::GenericObject.load(simple_json)
+ assert_kind_of JSON::GenericObject, simple
+ assert_equal "world", simple.hello
+ converting = JSON::GenericObject.load('{ "hello": "world" }')
+ assert_kind_of JSON::GenericObject, converting
+ assert_equal "world", converting.hello
+
+ json = JSON::GenericObject.dump(JSON::GenericObject[:hello => 'world'])
+ assert_equal JSON(json), JSON('{"json_class":"JSON::GenericObject","hello":"world"}')
+ end
+
+ private
+
+ def switch_json_creatable
+ JSON::GenericObject.json_creatable = true
+ yield
+ ensure
+ JSON::GenericObject.json_creatable = false
+ end
+end
diff --git a/jni/ruby/test/json/test_json_string_matching.rb b/jni/ruby/test/json/test_json_string_matching.rb
new file mode 100644
index 0000000..c233df8
--- /dev/null
+++ b/jni/ruby/test/json/test_json_string_matching.rb
@@ -0,0 +1,39 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+require 'stringio'
+require 'time'
+
+class TestJSONStringMatching < Test::Unit::TestCase
+ include JSON
+
+ class TestTime < ::Time
+ def self.json_create(string)
+ Time.parse(string)
+ end
+
+ def to_json(*)
+ %{"#{strftime('%FT%T%z')}"}
+ end
+
+ def ==(other)
+ to_i == other.to_i
+ end
+ end
+
+ def test_match_date
+ t = TestTime.new
+ t_json = [ t ].to_json
+ assert_equal [ t ],
+ JSON.parse(t_json, :create_additions => true,
+ :match_string => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
+ assert_equal [ t.strftime('%FT%T%z') ],
+ JSON.parse(t_json, :create_additions => true,
+ :match_string => { /\A\d{3}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
+ assert_equal [ t.strftime('%FT%T%z') ],
+ JSON.parse(t_json,
+ :match_string => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
+ end
+end
diff --git a/jni/ruby/test/json/test_json_unicode.rb b/jni/ruby/test/json/test_json_unicode.rb
new file mode 100755
index 0000000..8352d5c
--- /dev/null
+++ b/jni/ruby/test/json/test_json_unicode.rb
@@ -0,0 +1,72 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+
+class TestJSONUnicode < Test::Unit::TestCase
+ include JSON
+
+ def test_unicode
+ assert_equal '""', ''.to_json
+ assert_equal '"\\b"', "\b".to_json
+ assert_equal '"\u0001"', 0x1.chr.to_json
+ assert_equal '"\u001f"', 0x1f.chr.to_json
+ assert_equal '" "', ' '.to_json
+ assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
+ utf8 = [ "© ≠ €! \01" ]
+ json = '["© ≠ €! \u0001"]'
+ assert_equal json, utf8.to_json(:ascii_only => false)
+ assert_equal utf8, parse(json)
+ json = '["\u00a9 \u2260 \u20ac! \u0001"]'
+ assert_equal json, utf8.to_json(:ascii_only => true)
+ assert_equal utf8, parse(json)
+ utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
+ json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]"
+ assert_equal utf8, parse(json)
+ assert_equal json, utf8.to_json(:ascii_only => false)
+ utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
+ assert_equal utf8, parse(json)
+ json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]"
+ assert_equal json, utf8.to_json(:ascii_only => true)
+ assert_equal utf8, parse(json)
+ utf8 = ['საქართველო']
+ json = '["საქართველო"]'
+ assert_equal json, utf8.to_json(:ascii_only => false)
+ json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]"
+ assert_equal json, utf8.to_json(:ascii_only => true)
+ assert_equal utf8, parse(json)
+ assert_equal '["Ã"]', JSON.generate(["Ã"], :ascii_only => false)
+ assert_equal '["\\u00c3"]', JSON.generate(["Ã"], :ascii_only => true)
+ assert_equal ["€"], JSON.parse('["\u20ac"]')
+ utf8 = ["\xf0\xa0\x80\x81"]
+ json = "[\"\xf0\xa0\x80\x81\"]"
+ assert_equal json, JSON.generate(utf8, :ascii_only => false)
+ assert_equal utf8, JSON.parse(json)
+ json = '["\ud840\udc01"]'
+ assert_equal json, JSON.generate(utf8, :ascii_only => true)
+ assert_equal utf8, JSON.parse(json)
+ end
+
+ def test_chars
+ (0..0x7f).each do |i|
+ json = '["\u%04x"]' % i
+ if RUBY_VERSION >= "1.9."
+ i = i.chr
+ end
+ assert_equal i, JSON.parse(json).first[0]
+ if i == ?\b
+ generated = JSON.generate(["" << i])
+ assert '["\b"]' == generated || '["\10"]' == generated
+ elsif [?\n, ?\r, ?\t, ?\f].include?(i)
+ assert_equal '[' << ('' << i).dump << ']', JSON.generate(["" << i])
+ elsif i.chr < 0x20.chr
+ assert_equal json, JSON.generate(["" << i])
+ end
+ end
+ assert_raise(JSON::GeneratorError) do
+ JSON.generate(["\x80"], :ascii_only => true)
+ end
+ assert_equal "\302\200", JSON.parse('["\u0080"]').first
+ end
+end
diff --git a/jni/ruby/test/lib/envutil.rb b/jni/ruby/test/lib/envutil.rb
new file mode 100644
index 0000000..1193a1a
--- /dev/null
+++ b/jni/ruby/test/lib/envutil.rb
@@ -0,0 +1,604 @@
+# -*- coding: us-ascii -*-
+require "open3"
+require "timeout"
+require_relative "find_executable"
+
+module EnvUtil
+ def rubybin
+ if ruby = ENV["RUBY"]
+ return ruby
+ end
+ ruby = "ruby"
+ exeext = RbConfig::CONFIG["EXEEXT"]
+ rubyexe = (ruby + exeext if exeext and !exeext.empty?)
+ 3.times do
+ if File.exist? ruby and File.executable? ruby and !File.directory? ruby
+ return File.expand_path(ruby)
+ end
+ if rubyexe and File.exist? rubyexe and File.executable? rubyexe
+ return File.expand_path(rubyexe)
+ end
+ ruby = File.join("..", ruby)
+ end
+ if defined?(RbConfig.ruby)
+ RbConfig.ruby
+ else
+ "ruby"
+ end
+ end
+ module_function :rubybin
+
+ LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
+
+ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false,
+ encoding: nil, timeout: 10, reprieve: 1,
+ stdout_filter: nil, stderr_filter: nil,
+ rubybin: EnvUtil.rubybin,
+ **opt)
+ in_c, in_p = IO.pipe
+ out_p, out_c = IO.pipe if capture_stdout
+ err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout
+ opt[:in] = in_c
+ opt[:out] = out_c if capture_stdout
+ opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr
+ if encoding
+ out_p.set_encoding(encoding) if out_p
+ err_p.set_encoding(encoding) if err_p
+ end
+ c = "C"
+ child_env = {}
+ LANG_ENVS.each {|lc| child_env[lc] = c}
+ if Array === args and Hash === args.first
+ child_env.update(args.shift)
+ end
+ args = [args] if args.kind_of?(String)
+ pid = spawn(child_env, rubybin, *args, **opt)
+ in_c.close
+ out_c.close if capture_stdout
+ err_c.close if capture_stderr && capture_stderr != :merge_to_stdout
+ if block_given?
+ return yield in_p, out_p, err_p, pid
+ else
+ th_stdout = Thread.new { out_p.read } if capture_stdout
+ th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout
+ in_p.write stdin_data.to_str unless stdin_data.empty?
+ in_p.close
+ if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
+ stdout = th_stdout.value if capture_stdout
+ stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout
+ else
+ signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
+ case pgroup = opt[:pgroup]
+ when 0, true
+ pgroup = -pid
+ when nil, false
+ pgroup = pid
+ end
+ begin
+ Process.kill signal, pgroup
+ Timeout.timeout((reprieve unless signal == :KILL)) do
+ Process.wait(pid)
+ end
+ rescue Errno::ESRCH
+ break
+ rescue Timeout::Error
+ raise if signal == :KILL
+ signal = :KILL
+ else
+ break
+ end while true
+ bt = caller_locations
+ raise Timeout::Error, "execution of #{bt.shift.label} expired", bt.map(&:to_s)
+ end
+ out_p.close if capture_stdout
+ err_p.close if capture_stderr && capture_stderr != :merge_to_stdout
+ Process.wait pid
+ status = $?
+ stdout = stdout_filter.call(stdout) if stdout_filter
+ stderr = stderr_filter.call(stderr) if stderr_filter
+ return stdout, stderr, status
+ end
+ ensure
+ [th_stdout, th_stderr].each do |th|
+ th.kill if th
+ end
+ [in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
+ io.close if io && !io.closed?
+ end
+ [th_stdout, th_stderr].each do |th|
+ th.join if th
+ end
+ end
+ module_function :invoke_ruby
+
+ alias rubyexec invoke_ruby
+ class << self
+ alias rubyexec invoke_ruby
+ end
+
+ def verbose_warning
+ class << (stderr = "")
+ alias write <<
+ end
+ stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
+ yield stderr
+ return $stderr
+ ensure
+ stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
+ end
+ module_function :verbose_warning
+
+ def default_warning
+ verbose, $VERBOSE = $VERBOSE, false
+ yield
+ ensure
+ $VERBOSE = verbose
+ end
+ module_function :default_warning
+
+ def suppress_warning
+ verbose, $VERBOSE = $VERBOSE, nil
+ yield
+ ensure
+ $VERBOSE = verbose
+ end
+ module_function :suppress_warning
+
+ def under_gc_stress(stress = true)
+ stress, GC.stress = GC.stress, stress
+ yield
+ ensure
+ GC.stress = stress
+ end
+ module_function :under_gc_stress
+
+ def with_default_external(enc)
+ verbose, $VERBOSE = $VERBOSE, nil
+ origenc, Encoding.default_external = Encoding.default_external, enc
+ $VERBOSE = verbose
+ yield
+ ensure
+ verbose, $VERBOSE = $VERBOSE, nil
+ Encoding.default_external = origenc
+ $VERBOSE = verbose
+ end
+ module_function :with_default_external
+
+ def with_default_internal(enc)
+ verbose, $VERBOSE = $VERBOSE, nil
+ origenc, Encoding.default_internal = Encoding.default_internal, enc
+ $VERBOSE = verbose
+ yield
+ ensure
+ verbose, $VERBOSE = $VERBOSE, nil
+ Encoding.default_internal = origenc
+ $VERBOSE = verbose
+ end
+ module_function :with_default_internal
+
+ def labeled_module(name, &block)
+ Module.new do
+ singleton_class.class_eval {define_method(:to_s) {name}; alias inspect to_s}
+ class_eval(&block) if block
+ end
+ end
+ module_function :labeled_module
+
+ def labeled_class(name, superclass = Object, &block)
+ Class.new(superclass) do
+ singleton_class.class_eval {define_method(:to_s) {name}; alias inspect to_s}
+ class_eval(&block) if block
+ end
+ end
+ module_function :labeled_class
+
+ if /darwin/ =~ RUBY_PLATFORM
+ DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports")
+ DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'
+ def self.diagnostic_reports(signame, cmd, pid, now)
+ return unless %w[ABRT QUIT SEGV ILL].include?(signame)
+ cmd = File.basename(cmd)
+ path = DIAGNOSTIC_REPORTS_PATH
+ timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT
+ pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash"
+ first = true
+ 30.times do
+ first ? (first = false) : sleep(0.1)
+ Dir.glob(pat) do |name|
+ log = File.read(name) rescue next
+ if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log
+ File.unlink(name)
+ File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil
+ return log
+ end
+ end
+ end
+ nil
+ end
+ else
+ def self.diagnostic_reports(signame, cmd, pid, now)
+ end
+ end
+end
+
+module Test
+ module Unit
+ module Assertions
+ public
+ def assert_valid_syntax(code, fname = caller_locations(1, 1)[0], mesg = fname.to_s, verbose: nil)
+ code = code.dup.force_encoding("ascii-8bit")
+ code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
+ "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n"
+ }
+ code.force_encoding(Encoding::UTF_8)
+ verbose, $VERBOSE = $VERBOSE, verbose
+ yield if defined?(yield)
+ case
+ when Array === fname
+ fname, line = *fname
+ when defined?(fname.path) && defined?(fname.lineno)
+ fname, line = fname.path, fname.lineno
+ else
+ line = 0
+ end
+ assert_nothing_raised(SyntaxError, mesg) do
+ assert_equal(:ok, catch {|tag| eval(code, binding, fname, line)}, mesg)
+ end
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def assert_syntax_error(code, error, fname = caller_locations(1, 1)[0], mesg = fname.to_s)
+ code = code.dup.force_encoding("ascii-8bit")
+ code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
+ "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ng}\n"
+ }
+ code.force_encoding("us-ascii")
+ verbose, $VERBOSE = $VERBOSE, nil
+ yield if defined?(yield)
+ case
+ when Array === fname
+ fname, line = *fname
+ when defined?(fname.path) && defined?(fname.lineno)
+ fname, line = fname.path, fname.lineno
+ else
+ line = 0
+ end
+ e = assert_raise(SyntaxError, mesg) do
+ catch {|tag| eval(code, binding, fname, line)}
+ end
+ assert_match(error, e.message, mesg)
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def assert_normal_exit(testsrc, message = '', child_env: nil, **opt)
+ assert_valid_syntax(testsrc, caller_locations(1, 1)[0])
+ if child_env
+ child_env = [child_env]
+ else
+ child_env = []
+ end
+ out, _, status = EnvUtil.invoke_ruby(child_env + %W'-W0', testsrc, true, :merge_to_stdout, **opt)
+ assert !status.signaled?, FailDesc[status, message, out]
+ end
+
+ FailDesc = proc do |status, message = "", out = ""|
+ pid = status.pid
+ now = Time.now
+ faildesc = proc do
+ if signo = status.termsig
+ signame = Signal.signame(signo)
+ sigdesc = "signal #{signo}"
+ end
+ log = EnvUtil.diagnostic_reports(signame, EnvUtil.rubybin, pid, now)
+ if signame
+ sigdesc = "SIG#{signame} (#{sigdesc})"
+ end
+ if status.coredump?
+ sigdesc << " (core dumped)"
+ end
+ full_message = ''
+ if message and !message.empty?
+ full_message << message << "\n"
+ end
+ full_message << "pid #{pid} killed by #{sigdesc}"
+ if out and !out.empty?
+ full_message << "\n#{out.gsub(/^/, '| ')}"
+ full_message << "\n" if /\n\z/ !~ full_message
+ end
+ if log
+ full_message << "\n#{log.gsub(/^/, '| ')}"
+ end
+ full_message
+ end
+ faildesc
+ end
+
+ def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, **opt)
+ stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, **opt)
+ if signo = status.termsig
+ sleep 0.1
+ EnvUtil.diagnostic_reports(Signal.signame(signo), EnvUtil.rubybin, status.pid, Time.now)
+ end
+ if block_given?
+ raise "test_stdout ignored, use block only or without block" if test_stdout != []
+ raise "test_stderr ignored, use block only or without block" if test_stderr != []
+ yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp }, status)
+ else
+ errs = []
+ [[test_stdout, stdout], [test_stderr, stderr]].each do |exp, act|
+ begin
+ if exp.is_a?(Regexp)
+ assert_match(exp, act, message)
+ else
+ assert_equal(exp, act.lines.map {|l| l.chomp }, message)
+ end
+ rescue MiniTest::Assertion => e
+ errs << e.message
+ message = nil
+ end
+ end
+ raise MiniTest::Assertion, errs.join("\n---\n") unless errs.empty?
+ status
+ end
+ end
+
+ def assert_ruby_status(args, test_stdin="", message=nil, **opt)
+ out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt)
+ assert(!status.signaled?, FailDesc[status, message, out])
+ message ||= "ruby exit status is not success:"
+ assert(status.success?, "#{message} (#{status.inspect})")
+ end
+
+ ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV")
+
+ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt)
+ unless file and line
+ loc, = caller_locations(1,1)
+ file ||= loc.path
+ line ||= loc.lineno
+ end
+ line -= 5 # lines until src
+ src = <<eom
+# -*- coding: #{src.encoding}; -*-
+ require #{__dir__.dump}'/test/unit';include Test::Unit::Assertions
+ END {
+ puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}"
+ }
+#{src}
+ class Test::Unit::Runner
+ @@stop_auto_run = true
+ end
+eom
+ args = args.dup
+ args.insert((Hash === args.first ? 1 : 0), "--disable=gems", *$:.map {|l| "-I#{l}"})
+ stdout, stderr, status = EnvUtil.invoke_ruby(args, src, true, true, **opt)
+ abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig))
+ assert(!abort, FailDesc[status, nil, stderr])
+ self._assertions += stdout[/^assertions=(\d+)/, 1].to_i
+ begin
+ res = Marshal.load(stdout.unpack("m")[0])
+ rescue => marshal_error
+ ignore_stderr = nil
+ end
+ if res
+ if bt = res.backtrace
+ bt.each do |l|
+ l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"}
+ end
+ bt.concat(caller)
+ else
+ res.set_backtrace(caller)
+ end
+ raise res
+ end
+
+ # really is it succeed?
+ unless ignore_stderr
+ # the body of assert_separately must not output anything to detect error
+ assert_equal("", stderr, "assert_separately failed with error message")
+ end
+ assert_equal(0, status, "assert_separately failed: '#{stderr}'")
+ raise marshal_error if marshal_error
+ end
+
+ def assert_warning(pat, msg = nil)
+ stderr = EnvUtil.verbose_warning { yield }
+ msg = message(msg) {diff pat, stderr}
+ assert(pat === stderr, msg)
+ end
+
+ def assert_warn(*args)
+ assert_warning(*args) {$VERBOSE = false; yield}
+ end
+
+ case RUBY_PLATFORM
+ when /solaris2\.(?:9|[1-9][0-9])/i # Solaris 9, 10, 11,...
+ bits = [nil].pack('p').size == 8 ? 64 : 32
+ if ENV['LD_PRELOAD'].to_s.empty? &&
+ ENV["LD_PRELOAD_#{bits}"].to_s.empty? &&
+ (ENV['UMEM_OPTIONS'].to_s.empty? ||
+ ENV['UMEM_OPTIONS'] == 'backend=mmap') then
+ envs = {
+ 'LD_PRELOAD' => 'libumem.so',
+ 'UMEM_OPTIONS' => 'backend=mmap'
+ }
+ args = [
+ envs,
+ "--disable=gems",
+ "-v", "-",
+ ]
+ _, err, status = EnvUtil.invoke_ruby(args, "exit(0)", true, true)
+ if status.exitstatus == 0 && err.to_s.empty? then
+ NO_MEMORY_LEAK_ENVS = envs
+ end
+ end
+ end #case RUBY_PLATFORM
+
+ def assert_no_memory_leak(args, prepare, code, message=nil, limit: 1.5, rss: false, **opt)
+ require_relative 'memory_status'
+ token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m"
+ token_dump = token.dump
+ token_re = Regexp.quote(token)
+ envs = args.shift if Array === args and Hash === args.first
+ args = [
+ "--disable=gems",
+ "-r", File.expand_path("../memory_status", __FILE__),
+ *args,
+ "-v", "-",
+ ]
+ if defined? NO_MEMORY_LEAK_ENVS then
+ envs ||= {}
+ newenvs = envs.merge(NO_MEMORY_LEAK_ENVS) { |_, _, _| break }
+ envs = newenvs if newenvs
+ end
+ args.unshift(envs) if envs
+ cmd = [
+ 'END {STDERR.puts '"#{token_dump}"'"FINAL=#{Memory::Status.new}"}',
+ prepare,
+ 'STDERR.puts('"#{token_dump}"'"START=#{$initial_status = Memory::Status.new}")',
+ '$initial_size = $initial_status.size',
+ code,
+ 'GC.start',
+ ].join("\n")
+ _, err, status = EnvUtil.invoke_ruby(args, cmd, true, true, **opt)
+ before = err.sub!(/^#{token_re}START=(\{.*\})\n/, '') && Memory::Status.parse($1)
+ after = err.sub!(/^#{token_re}FINAL=(\{.*\})\n/, '') && Memory::Status.parse($1)
+ assert_equal([true, ""], [status.success?, err], message)
+ ([:size, (rss && :rss)] & after.members).each do |n|
+ b = before[n]
+ a = after[n]
+ next unless a > 0 and b > 0
+ assert_operator(a.fdiv(b), :<, limit, message(message) {"#{n}: #{b} => #{a}"})
+ end
+ rescue LoadError
+ skip
+ end
+
+ def assert_is_minus_zero(f)
+ assert(1.0/f == -Float::INFINITY, "#{f} is not -0.0")
+ end
+
+ def assert_file
+ AssertFile
+ end
+
+ # pattern_list is an array which contains regexp and :*.
+ # :* means any sequence.
+ #
+ # pattern_list is anchored.
+ # Use [:*, regexp, :*] for non-anchored match.
+ def assert_pattern_list(pattern_list, actual, message=nil)
+ rest = actual
+ anchored = true
+ pattern_list.each_with_index {|pattern, i|
+ if pattern == :*
+ anchored = false
+ else
+ if anchored
+ match = /\A#{pattern}/.match(rest)
+ else
+ match = pattern.match(rest)
+ end
+ unless match
+ msg = message(msg) {
+ expect_msg = "Expected #{mu_pp pattern}\n"
+ if /\n[^\n]/ =~ rest
+ actual_mesg = "to match\n"
+ rest.scan(/.*\n+/) {
+ actual_mesg << ' ' << $&.inspect << "+\n"
+ }
+ actual_mesg.sub!(/\+\n\z/, '')
+ else
+ actual_mesg = "to match #{mu_pp rest}"
+ end
+ actual_mesg << "\nafter #{i} patterns with #{actual.length - rest.length} characters"
+ expect_msg + actual_mesg
+ }
+ assert false, msg
+ end
+ rest = match.post_match
+ anchored = true
+ end
+ }
+ if anchored
+ assert_equal("", rest)
+ end
+ end
+
+ # threads should respond to shift method.
+ # Array can be used.
+ def assert_join_threads(threads, message = nil)
+ errs = []
+ values = []
+ while th = threads.shift
+ begin
+ values << th.value
+ rescue Exception
+ errs << [th, $!]
+ end
+ end
+ if !errs.empty?
+ msg = "exceptions on #{errs.length} threads:\n" +
+ errs.map {|t, err|
+ "#{t.inspect}:\n" +
+ err.backtrace.map.with_index {|line, i|
+ if i == 0
+ "#{line}: #{err.message} (#{err.class})"
+ else
+ "\tfrom #{line}"
+ end
+ }.join("\n")
+ }.join("\n---\n")
+ if message
+ msg = "#{message}\n#{msg}"
+ end
+ raise MiniTest::Assertion, msg
+ end
+ values
+ end
+
+ class << (AssertFile = Struct.new(:failure_message).new)
+ include Assertions
+ def assert_file_predicate(predicate, *args)
+ if /\Anot_/ =~ predicate
+ predicate = $'
+ neg = " not"
+ end
+ result = File.__send__(predicate, *args)
+ result = !result if neg
+ mesg = "Expected file " << args.shift.inspect
+ mesg << "#{neg} to be #{predicate}"
+ mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty?
+ mesg << " #{failure_message}" if failure_message
+ assert(result, mesg)
+ end
+ alias method_missing assert_file_predicate
+
+ def for(message)
+ clone.tap {|a| a.failure_message = message}
+ end
+ end
+ end
+ end
+end
+
+begin
+ require 'rbconfig'
+rescue LoadError
+else
+ module RbConfig
+ @ruby = EnvUtil.rubybin
+ class << self
+ undef ruby if method_defined?(:ruby)
+ attr_reader :ruby
+ end
+ dir = File.dirname(ruby)
+ name = File.basename(ruby, CONFIG['EXEEXT'])
+ CONFIG['bindir'] = dir
+ CONFIG['ruby_install_name'] = name
+ CONFIG['RUBY_INSTALL_NAME'] = name
+ Gem::ConfigMap[:bindir] = dir if defined?(Gem::ConfigMap)
+ end
+end
diff --git a/jni/ruby/test/lib/find_executable.rb b/jni/ruby/test/lib/find_executable.rb
new file mode 100644
index 0000000..4ba9a05
--- /dev/null
+++ b/jni/ruby/test/lib/find_executable.rb
@@ -0,0 +1,21 @@
+require "rbconfig"
+
+module EnvUtil
+ def find_executable(cmd, *args)
+ exts = RbConfig::CONFIG["EXECUTABLE_EXTS"].split | [RbConfig::CONFIG["EXEEXT"]]
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
+ next if path.empty?
+ path = File.join(path, cmd)
+ exts.each do |ext|
+ cmdline = [path + ext, *args]
+ begin
+ return cmdline if yield(IO.popen(cmdline, "r", err: [:child, :out], &:read))
+ rescue
+ next
+ end
+ end
+ end
+ nil
+ end
+ module_function :find_executable
+end
diff --git a/jni/ruby/test/lib/leakchecker.rb b/jni/ruby/test/lib/leakchecker.rb
new file mode 100644
index 0000000..37c87c9
--- /dev/null
+++ b/jni/ruby/test/lib/leakchecker.rb
@@ -0,0 +1,167 @@
+class LeakChecker
+ def initialize
+ @fd_info = find_fds
+ @tempfile_info = find_tempfiles
+ @thread_info = find_threads
+ end
+
+ def check(test_name)
+ leaked1 = check_fd_leak(test_name)
+ leaked2 = check_thread_leak(test_name)
+ leaked3 = check_tempfile_leak(test_name)
+ GC.start if leaked1 || leaked2 || leaked3
+ end
+
+ def find_fds
+ fd_dir = "/proc/self/fd"
+ if File.directory?(fd_dir)
+ fds = Dir.open(fd_dir) {|d|
+ a = d.grep(/\A\d+\z/, &:to_i)
+ if d.respond_to? :fileno
+ a -= [d.fileno]
+ end
+ a
+ }
+ fds.sort
+ else
+ []
+ end
+ end
+
+ def check_fd_leak(test_name)
+ leaked = false
+ live1 = @fd_info
+ if IO.respond_to?(:console)
+ IO.console(:close)
+ end
+ live2 = find_fds
+ fd_closed = live1 - live2
+ if !fd_closed.empty?
+ fd_closed.each {|fd|
+ puts "Closed file descriptor: #{test_name}: #{fd}"
+ }
+ end
+ fd_leaked = live2 - live1
+ if !fd_leaked.empty?
+ leaked = true
+ h = {}
+ ObjectSpace.each_object(IO) {|io|
+ inspect = io.inspect
+ begin
+ autoclose = io.autoclose?
+ fd = io.fileno
+ rescue IOError # closed IO object
+ next
+ end
+ (h[fd] ||= []) << [io, autoclose, inspect]
+ }
+ fd_leaked.each {|fd|
+ str = ''
+ if h[fd]
+ str << ' :'
+ h[fd].map {|io, autoclose, inspect|
+ s = ' ' + inspect
+ s << "(not-autoclose)" if !autoclose
+ s
+ }.sort.each {|s|
+ str << s
+ }
+ end
+ puts "Leaked file descriptor: #{test_name}: #{fd}#{str}"
+ }
+ #system("lsof -p #$$") if !fd_leaked.empty?
+ h.each {|fd, list|
+ next if list.length <= 1
+ if 1 < list.count {|io, autoclose, inspect| autoclose }
+ str = list.map {|io, autoclose, inspect| " #{inspect}" + (autoclose ? "(autoclose)" : "") }.sort.join
+ puts "Multiple autoclose IO object for a file descriptor:#{str}"
+ end
+ }
+ end
+ @fd_info = live2
+ return leaked
+ end
+
+ def extend_tempfile_counter
+ return if defined? LeakChecker::TempfileCounter
+ m = Module.new {
+ @count = 0
+ class << self
+ attr_accessor :count
+ end
+
+ def new(data)
+ LeakChecker::TempfileCounter.count += 1
+ super(data)
+ end
+ }
+ LeakChecker.const_set(:TempfileCounter, m)
+
+ class << Tempfile::Remover
+ prepend LeakChecker::TempfileCounter
+ end
+ end
+
+ def find_tempfiles(prev_count=-1)
+ return [prev_count, []] unless defined? Tempfile
+ extend_tempfile_counter
+ count = TempfileCounter.count
+ if prev_count == count
+ [prev_count, []]
+ else
+ tempfiles = ObjectSpace.each_object(Tempfile).find_all {|t| t.path }
+ [count, tempfiles]
+ end
+ end
+
+ def check_tempfile_leak(test_name)
+ return false, @tempfile_info unless defined? Tempfile
+ count1, initial_tempfiles = @tempfile_info
+ count2, current_tempfiles = find_tempfiles(count1)
+ leaked = false
+ tempfiles_leaked = current_tempfiles - initial_tempfiles
+ if !tempfiles_leaked.empty?
+ leaked = true
+ list = tempfiles_leaked.map {|t| t.inspect }.sort
+ list.each {|str|
+ puts "Leaked tempfile: #{test_name}: #{str}"
+ }
+ tempfiles_leaked.each {|t| t.close! }
+ end
+ @tempfile_info = [count2, initial_tempfiles]
+ return leaked
+ end
+
+ def find_threads
+ Thread.list.find_all {|t|
+ t != Thread.current && /\AWEBrick::/ !~ t.class.name && t.alive?
+ }
+ end
+
+ def check_thread_leak(test_name)
+ live1 = @thread_info
+ live2 = find_threads
+ thread_finished = live1 - live2
+ leaked = false
+ if !thread_finished.empty?
+ list = thread_finished.map {|t| t.inspect }.sort
+ list.each {|str|
+ puts "Finished thread: #{test_name}: #{str}"
+ }
+ end
+ thread_leaked = live2 - live1
+ if !thread_leaked.empty?
+ leaked = true
+ list = thread_leaked.map {|t| t.inspect }.sort
+ list.each {|str|
+ puts "Leaked thread: #{test_name}: #{str}"
+ }
+ end
+ @thread_info = live2
+ return leaked
+ end
+
+ def puts(*a)
+ MiniTest::Unit.output.puts(*a)
+ end
+end
diff --git a/jni/ruby/test/lib/memory_status.rb b/jni/ruby/test/lib/memory_status.rb
new file mode 100644
index 0000000..071c5f6
--- /dev/null
+++ b/jni/ruby/test/lib/memory_status.rb
@@ -0,0 +1,111 @@
+module Memory
+ keys = []
+ vals = []
+
+ case
+ when File.exist?(procfile = "/proc/self/status") && (pat = /^Vm(\w+):\s+(\d+)/) =~ File.binread(procfile)
+ PROC_FILE = procfile
+ VM_PAT = pat
+ def self.read_status
+ IO.foreach(PROC_FILE, encoding: Encoding::ASCII_8BIT) do |l|
+ yield($1.downcase.intern, $2.to_i * 1024) if VM_PAT =~ l
+ end
+ end
+
+ read_status {|k, v| keys << k; vals << v}
+
+ when /mswin|mingw/ =~ RUBY_PLATFORM
+ require 'fiddle/import'
+ require 'fiddle/types'
+
+ module Win32
+ extend Fiddle::Importer
+ dlload "kernel32.dll", "psapi.dll"
+ include Fiddle::Win32Types
+ typealias "SIZE_T", "size_t"
+
+ PROCESS_MEMORY_COUNTERS = struct [
+ "DWORD cb",
+ "DWORD PageFaultCount",
+ "SIZE_T PeakWorkingSetSize",
+ "SIZE_T WorkingSetSize",
+ "SIZE_T QuotaPeakPagedPoolUsage",
+ "SIZE_T QuotaPagedPoolUsage",
+ "SIZE_T QuotaPeakNonPagedPoolUsage",
+ "SIZE_T QuotaNonPagedPoolUsage",
+ "SIZE_T PagefileUsage",
+ "SIZE_T PeakPagefileUsage",
+ ]
+
+ typealias "PPROCESS_MEMORY_COUNTERS", "PROCESS_MEMORY_COUNTERS*"
+
+ extern "HANDLE GetCurrentProcess()", :stdcall
+ extern "BOOL GetProcessMemoryInfo(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD)", :stdcall
+
+ module_function
+ def memory_info
+ size = PROCESS_MEMORY_COUNTERS.size
+ data = PROCESS_MEMORY_COUNTERS.malloc
+ data.cb = size
+ data if GetProcessMemoryInfo(GetCurrentProcess(), data, size)
+ end
+ end
+
+ keys << :peak << :size
+ def self.read_status
+ if info = Win32.memory_info
+ yield :peak, info.PeakPagefileUsage
+ yield :size, info.PagefileUsage
+ end
+ end
+ else
+ PAT = /^\s*(\d+)\s+(\d+)$/
+ require_relative '../lib/find_executable'
+ if PSCMD = EnvUtil.find_executable("ps", "-ovsz=", "-orss=", "-p", $$.to_s) {|out| PAT =~ out}
+ PSCMD.pop
+ end
+ raise MiniTest::Skip, "ps command not found" unless PSCMD
+
+ keys << :size << :rss
+ def self.read_status
+ if PAT =~ IO.popen(PSCMD + [$$.to_s], "r", err: [:child, :out], &:read)
+ yield :size, $1.to_i*1024
+ yield :rss, $2.to_i*1024
+ end
+ end
+ end
+
+ Status = Struct.new(*keys)
+
+ class Status
+ def _update
+ Memory.read_status do |key, val|
+ self[key] = val
+ end
+ end
+ end
+
+ class Status
+ Header = members.map {|k| k.to_s.upcase.rjust(6)}.join('')
+ Format = "%6d"
+
+ def initialize
+ _update
+ end
+
+ def to_s
+ status = each_pair.map {|n,v|
+ "#{n}:#{v}"
+ }
+ "{#{status.join(",")}}"
+ end
+
+ def self.parse(str)
+ status = allocate
+ str.scan(/(?:\A\{|\G,)(#{members.join('|')}):(\d+)(?=,|\}\z)/) do
+ status[$1] = $2.to_i
+ end
+ status
+ end
+ end
+end
diff --git a/jni/ruby/test/lib/minitest/README.txt b/jni/ruby/test/lib/minitest/README.txt
new file mode 100644
index 0000000..368cc3a
--- /dev/null
+++ b/jni/ruby/test/lib/minitest/README.txt
@@ -0,0 +1,457 @@
+= minitest/{unit,spec,mock,benchmark}
+
+home :: https://github.com/seattlerb/minitest
+rdoc :: http://docs.seattlerb.org/minitest
+vim :: https://github.com/sunaku/vim-ruby-minitest
+
+== DESCRIPTION:
+
+minitest provides a complete suite of testing facilities supporting
+TDD, BDD, mocking, and benchmarking.
+
+ "I had a class with Jim Weirich on testing last week and we were
+ allowed to choose our testing frameworks. Kirk Haines and I were
+ paired up and we cracked open the code for a few test
+ frameworks...
+
+ I MUST say that minitest is *very* readable / understandable
+ compared to the 'other two' options we looked at. Nicely done and
+ thank you for helping us keep our mental sanity."
+
+ -- Wayne E. Seguin
+
+minitest/unit is a small and incredibly fast unit testing framework.
+It provides a rich set of assertions to make your tests clean and
+readable.
+
+minitest/spec is a functionally complete spec engine. It hooks onto
+minitest/unit and seamlessly bridges test assertions over to spec
+expectations.
+
+minitest/benchmark is an awesome way to assert the performance of your
+algorithms in a repeatable manner. Now you can assert that your newb
+co-worker doesn't replace your linear algorithm with an exponential
+one!
+
+minitest/mock by Steven Baker, is a beautifully tiny mock (and stub)
+object framework.
+
+minitest/pride shows pride in testing and adds coloring to your test
+output. I guess it is an example of how to write IO pipes too. :P
+
+minitest/unit is meant to have a clean implementation for language
+implementors that need a minimal set of methods to bootstrap a working
+test suite. For example, there is no magic involved for test-case
+discovery.
+
+ "Again, I can't praise enough the idea of a testing/specing
+ framework that I can actually read in full in one sitting!"
+
+ -- Piotr Szotkowski
+
+Comparing to rspec:
+
+ rspec is a testing DSL. minitest is ruby.
+
+ -- Adam Hawkins, "Bow Before MiniTest"
+
+minitest doesn't reinvent anything that ruby already provides, like:
+classes, modules, inheritance, methods. This means you only have to
+learn ruby to use minitest and all of your regular OO practices like
+extract-method refactorings still apply.
+
+== FEATURES/PROBLEMS:
+
+* minitest/autorun - the easy and explicit way to run all your tests.
+* minitest/unit - a very fast, simple, and clean test system.
+* minitest/spec - a very fast, simple, and clean spec system.
+* minitest/mock - a simple and clean mock/stub system.
+* minitest/benchmark - an awesome way to assert your algorithm's performance.
+* minitest/pride - show your pride in testing!
+* Incredibly small and fast runner, but no bells and whistles.
+
+== RATIONALE:
+
+See design_rationale.rb to see how specs and tests work in minitest.
+
+== SYNOPSIS:
+
+Given that you'd like to test the following class:
+
+ class Meme
+ def i_can_has_cheezburger?
+ "OHAI!"
+ end
+
+ def will_it_blend?
+ "YES!"
+ end
+ end
+
+=== Unit tests
+
+ require 'minitest/autorun'
+
+ class TestMeme < MiniTest::Unit::TestCase
+ def setup
+ @meme = Meme.new
+ end
+
+ def test_that_kitty_can_eat
+ assert_equal "OHAI!", @meme.i_can_has_cheezburger?
+ end
+
+ def test_that_it_will_not_blend
+ refute_match /^no/i, @meme.will_it_blend?
+ end
+
+ def test_that_will_be_skipped
+ skip "test this later"
+ end
+ end
+
+=== Specs
+
+ require 'minitest/autorun'
+
+ describe Meme do
+ before do
+ @meme = Meme.new
+ end
+
+ describe "when asked about cheeseburgers" do
+ it "must respond positively" do
+ @meme.i_can_has_cheezburger?.must_equal "OHAI!"
+ end
+ end
+
+ describe "when asked about blending possibilities" do
+ it "won't say no" do
+ @meme.will_it_blend?.wont_match /^no/i
+ end
+ end
+ end
+
+For matchers support check out:
+
+https://github.com/zenspider/minitest-matchers
+
+=== Benchmarks
+
+Add benchmarks to your regular unit tests. If the unit tests fail, the
+benchmarks won't run.
+
+ # optionally run benchmarks, good for CI-only work!
+ require 'minitest/benchmark' if ENV["BENCH"]
+
+ class TestMeme < MiniTest::Unit::TestCase
+ # Override self.bench_range or default range is [1, 10, 100, 1_000, 10_000]
+ def bench_my_algorithm
+ assert_performance_linear 0.9999 do |n| # n is a range value
+ @obj.my_algorithm(n)
+ end
+ end
+ end
+
+Or add them to your specs. If you make benchmarks optional, you'll
+need to wrap your benchmarks in a conditional since the methods won't
+be defined.
+
+ describe Meme do
+ if ENV["BENCH"] then
+ bench_performance_linear "my_algorithm", 0.9999 do |n|
+ 100.times do
+ @obj.my_algorithm(n)
+ end
+ end
+ end
+ end
+
+outputs something like:
+
+ # Running benchmarks:
+
+ TestBlah 100 1000 10000
+ bench_my_algorithm 0.006167 0.079279 0.786993
+ bench_other_algorithm 0.061679 0.792797 7.869932
+
+Output is tab-delimited to make it easy to paste into a spreadsheet.
+
+=== Mocks
+
+ class MemeAsker
+ def initialize(meme)
+ @meme = meme
+ end
+
+ def ask(question)
+ method = question.tr(" ","_") + "?"
+ @meme.__send__(method)
+ end
+ end
+
+ require 'minitest/autorun'
+
+ describe MemeAsker do
+ before do
+ @meme = MiniTest::Mock.new
+ @meme_asker = MemeAsker.new @meme
+ end
+
+ describe "#ask" do
+ describe "when passed an unpunctuated question" do
+ it "should invoke the appropriate predicate method on the meme" do
+ @meme.expect :will_it_blend?, :return_value
+ @meme_asker.ask "will it blend"
+ @meme.verify
+ end
+ end
+ end
+ end
+
+=== Stubs
+
+ def test_stale_eh
+ obj_under_test = Something.new
+
+ refute obj_under_test.stale?
+
+ Time.stub :now, Time.at(0) do # stub goes away once the block is done
+ assert obj_under_test.stale?
+ end
+ end
+
+A note on stubbing: In order to stub a method, the method must
+actually exist prior to stubbing. Use a singleton method to create a
+new non-existing method:
+
+ def obj_under_test.fake_method
+ ...
+ end
+
+=== Customizable Test Runner Types:
+
+MiniTest::Unit.runner=(runner) provides an easy way of creating custom
+test runners for specialized needs. Justin Weiss provides the
+following real-world example to create an alternative to regular
+fixture loading:
+
+ class MiniTestWithHooks::Unit < MiniTest::Unit
+ def before_suites
+ end
+
+ def after_suites
+ end
+
+ def _run_suites(suites, type)
+ begin
+ before_suites
+ super(suites, type)
+ ensure
+ after_suites
+ end
+ end
+
+ def _run_suite(suite, type)
+ begin
+ suite.before_suite
+ super(suite, type)
+ ensure
+ suite.after_suite
+ end
+ end
+ end
+
+ module MiniTestWithTransactions
+ class Unit < MiniTestWithHooks::Unit
+ include TestSetupHelper
+
+ def before_suites
+ super
+ setup_nested_transactions
+ # load any data we want available for all tests
+ end
+
+ def after_suites
+ teardown_nested_transactions
+ super
+ end
+ end
+ end
+
+ MiniTest::Unit.runner = MiniTestWithTransactions::Unit.new
+
+== FAQ
+
+=== How to test SimpleDelegates?
+
+The following implementation and test:
+
+ class Worker < SimpleDelegator
+ def work
+ end
+ end
+
+ describe Worker do
+ before do
+ @worker = Worker.new(Object.new)
+ end
+
+ it "must respond to work" do
+ @worker.must_respond_to :work
+ end
+ end
+
+outputs a failure:
+
+ 1) Failure:
+ Worker#test_0001_must respond to work [bug11.rb:16]:
+ Expected #<Object:0x007f9e7184f0a0> (Object) to respond to #work.
+
+Worker is a SimpleDelegate which in 1.9+ is a subclass of BasicObject.
+Expectations are put on Object (one level down) so the Worker
+(SimpleDelegate) hits `method_missing` and delegates down to the
+`Object.new` instance. That object doesn't respond to work so the test
+fails.
+
+You can bypass `SimpleDelegate#method_missing` by extending the worker
+with `MiniTest::Expectations`. You can either do that in your setup at
+the instance level, like:
+
+ before do
+ @worker = Worker.new(Object.new)
+ @worker.extend MiniTest::Expectations
+ end
+
+or you can extend the Worker class (within the test file!), like:
+
+ class Worker
+ include ::MiniTest::Expectations
+ end
+
+== Known Extensions:
+
+capybara_minitest_spec :: Bridge between Capybara RSpec matchers and MiniTest::Spec expectations (e.g. page.must_have_content('Title')).
+minispec-metadata :: Metadata for describe/it blocks
+ (e.g. `it 'requires JS driver', js: true do`)
+minitest-ansi :: Colorize minitest output with ANSI colors.
+minitest-around :: Around block for minitest. An alternative to setup/teardown dance.
+minitest-capistrano :: Assertions and expectations for testing Capistrano recipes
+minitest-capybara :: Capybara matchers support for minitest unit and spec
+minitest-chef-handler :: Run Minitest suites as Chef report handlers
+minitest-ci :: CI reporter plugin for MiniTest.
+minitest-colorize :: Colorize MiniTest output and show failing tests instantly.
+minitest-context :: Defines contexts for code reuse in MiniTest
+ specs that share common expectations.
+minitest-debugger :: Wraps assert so failed assertions drop into
+ the ruby debugger.
+minitest-display :: Patches MiniTest to allow for an easily configurable output.
+minitest-emoji :: Print out emoji for your test passes, fails, and skips.
+minitest-english :: Semantically symmetric aliases for assertions and expectations.
+minitest-excludes :: Clean API for excluding certain tests you
+ don't want to run under certain conditions.
+minitest-firemock :: Makes your MiniTest mocks more resilient.
+minitest-great_expectations :: Generally useful additions to minitest's assertions and expectations
+minitest-growl :: Test notifier for minitest via growl.
+minitest-implicit-subject :: Implicit declaration of the test subject.
+minitest-instrument :: Instrument ActiveSupport::Notifications when
+ test method is executed
+minitest-instrument-db :: Store information about speed of test
+ execution provided by minitest-instrument in database
+minitest-libnotify :: Test notifier for minitest via libnotify.
+minitest-macruby :: Provides extensions to minitest for macruby UI testing.
+minitest-matchers :: Adds support for RSpec-style matchers to minitest.
+minitest-metadata :: Annotate tests with metadata (key-value).
+minitest-mongoid :: Mongoid assertion matchers for MiniTest
+minitest-must_not :: Provides must_not as an alias for wont in MiniTest
+minitest-nc :: Test notifier for minitest via Mountain Lion's Notification Center
+minitest-predicates :: Adds support for .predicate? methods
+minitest-rails :: MiniTest integration for Rails 3.x
+minitest-rails-capybara :: Capybara integration for MiniTest::Rails
+minitest-reporters :: Create customizable MiniTest output formats
+minitest-should_syntax :: RSpec-style +x.should == y+ assertions for MiniTest
+minitest-shouldify :: Adding all manner of shoulds to MiniTest (bad idea)
+minitest-spec-context :: Provides rspec-ish context method to MiniTest::Spec
+minitest-spec-magic :: Minitest::Spec extensions for Rails and beyond
+minitest-spec-rails :: Drop in MiniTest::Spec superclass for ActiveSupport::TestCase.
+minitest-stub-const :: Stub constants for the duration of a block
+minitest-tags :: add tags for minitest
+minitest-wscolor :: Yet another test colorizer.
+minitest_owrapper :: Get tests results as a TestResult object.
+minitest_should :: Shoulda style syntax for minitest test::unit.
+minitest_tu_shim :: minitest_tu_shim bridges between test/unit and minitest.
+mongoid-minitest :: MiniTest matchers for Mongoid.
+pry-rescue :: A pry plugin w/ minitest support. See pry-rescue/minitest.rb.
+
+== Unknown Extensions:
+
+Authors... Please send me a pull request with a description of your minitest extension.
+
+* assay-minitest
+* detroit-minitest
+* em-minitest-spec
+* flexmock-minitest
+* guard-minitest
+* guard-minitest-decisiv
+* minitest-activemodel
+* minitest-ar-assertions
+* minitest-capybara-unit
+* minitest-colorer
+* minitest-deluxe
+* minitest-extra-assertions
+* minitest-rails-shoulda
+* minitest-spec
+* minitest-spec-should
+* minitest-sugar
+* minitest_should
+* mongoid-minitest
+* spork-minitest
+
+== REQUIREMENTS:
+
+* Ruby 1.8, maybe even 1.6 or lower. No magic is involved.
+
+== INSTALL:
+
+ sudo gem install minitest
+
+On 1.9, you already have it. To get newer candy you can still install
+the gem, but you'll need to activate the gem explicitly to use it:
+
+ require 'rubygems'
+ gem 'minitest' # ensures you're using the gem, and not the built in MT
+ require 'minitest/autorun'
+
+ # ... usual testing stuffs ...
+
+DO NOTE: There is a serious problem with the way that ruby 1.9/2.0
+packages their own gems. They install a gem specification file, but
+don't install the gem contents in the gem path. This messes up
+Gem.find_files and many other things (gem which, gem contents, etc).
+
+Just install minitest as a gem for real and you'll be happier.
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) Ryan Davis, seattle.rb
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/jni/ruby/test/lib/minitest/autorun.rb b/jni/ruby/test/lib/minitest/autorun.rb
new file mode 100644
index 0000000..88b1e05
--- /dev/null
+++ b/jni/ruby/test/lib/minitest/autorun.rb
@@ -0,0 +1,13 @@
+# encoding: utf-8
+
+begin
+ require 'rubygems'
+ gem 'minitest'
+rescue Gem::LoadError
+ # do nothing
+end
+
+require 'minitest/unit'
+require 'minitest/mock'
+
+MiniTest::Unit.autorun
diff --git a/jni/ruby/test/lib/minitest/benchmark.rb b/jni/ruby/test/lib/minitest/benchmark.rb
new file mode 100644
index 0000000..5195fc0
--- /dev/null
+++ b/jni/ruby/test/lib/minitest/benchmark.rb
@@ -0,0 +1,417 @@
+# encoding: utf-8
+
+require 'minitest/unit'
+
+class MiniTest::Unit # :nodoc:
+ def run_benchmarks # :nodoc:
+ _run_anything :benchmark
+ end
+
+ def benchmark_suite_header suite # :nodoc:
+ "\n#{suite}\t#{suite.bench_range.join("\t")}"
+ end
+
+ class TestCase
+ ##
+ # Returns a set of ranges stepped exponentially from +min+ to
+ # +max+ by powers of +base+. Eg:
+ #
+ # bench_exp(2, 16, 2) # => [2, 4, 8, 16]
+
+ def self.bench_exp min, max, base = 10
+ min = (Math.log10(min) / Math.log10(base)).to_i
+ max = (Math.log10(max) / Math.log10(base)).to_i
+
+ (min..max).map { |m| base ** m }.to_a
+ end
+
+ ##
+ # Returns a set of ranges stepped linearly from +min+ to +max+ by
+ # +step+. Eg:
+ #
+ # bench_linear(20, 40, 10) # => [20, 30, 40]
+
+ def self.bench_linear min, max, step = 10
+ (min..max).step(step).to_a
+ rescue LocalJumpError # 1.8.6
+ r = []; (min..max).step(step) { |n| r << n }; r
+ end
+
+ ##
+ # Returns the benchmark methods (methods that start with bench_)
+ # for that class.
+
+ def self.benchmark_methods # :nodoc:
+ public_instance_methods(true).grep(/^bench_/).map { |m| m.to_s }.sort
+ end
+
+ ##
+ # Returns all test suites that have benchmark methods.
+
+ def self.benchmark_suites
+ TestCase.test_suites.reject { |s| s.benchmark_methods.empty? }
+ end
+
+ ##
+ # Specifies the ranges used for benchmarking for that class.
+ # Defaults to exponential growth from 1 to 10k by powers of 10.
+ # Override if you need different ranges for your benchmarks.
+ #
+ # See also: ::bench_exp and ::bench_linear.
+
+ def self.bench_range
+ bench_exp 1, 10_000
+ end
+
+ ##
+ # Runs the given +work+, gathering the times of each run. Range
+ # and times are then passed to a given +validation+ proc. Outputs
+ # the benchmark name and times in tab-separated format, making it
+ # easy to paste into a spreadsheet for graphing or further
+ # analysis.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # validation = proc { |x, y| ... }
+ # assert_performance validation do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance validation, &work
+ range = self.class.bench_range
+
+ io.print "#{__name__}"
+
+ times = []
+
+ range.each do |x|
+ GC.start
+ t0 = Time.now
+ instance_exec(x, &work)
+ t = Time.now - t0
+
+ io.print "\t%9.6f" % t
+ times << t
+ end
+ io.puts
+
+ validation[range, times]
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered fit to
+ # match a constant rate (eg, linear slope == 0) within a given
+ # +threshold+. Note: because we're testing for a slope of 0, R^2
+ # is not a good determining factor for the fit, so the threshold
+ # is applied against the slope itself. As such, you probably want
+ # to tighten it from the default.
+ #
+ # See http://www.graphpad.com/curvefit/goodness_of_fit.htm for
+ # more details.
+ #
+ # Fit is calculated by #fit_linear.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_constant 0.9999 do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance_constant threshold = 0.99, &work
+ validation = proc do |range, times|
+ a, b, rr = fit_linear range, times
+ assert_in_delta 0, b, 1 - threshold
+ [a, b, rr]
+ end
+
+ assert_performance validation, &work
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered fit to
+ # match a exponential curve within a given error +threshold+.
+ #
+ # Fit is calculated by #fit_exponential.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_exponential 0.9999 do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance_exponential threshold = 0.99, &work
+ assert_performance validation_for_fit(:exponential, threshold), &work
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered fit to
+ # match a logarithmic curve within a given error +threshold+.
+ #
+ # Fit is calculated by #fit_logarithmic.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_logarithmic 0.9999 do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance_logarithmic threshold = 0.99, &work
+ assert_performance validation_for_fit(:logarithmic, threshold), &work
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered fit to
+ # match a straight line within a given error +threshold+.
+ #
+ # Fit is calculated by #fit_linear.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_linear 0.9999 do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def assert_performance_linear threshold = 0.99, &work
+ assert_performance validation_for_fit(:linear, threshold), &work
+ end
+
+ ##
+ # Runs the given +work+ and asserts that the times gathered curve
+ # fit to match a power curve within a given error +threshold+.
+ #
+ # Fit is calculated by #fit_power.
+ #
+ # Ranges are specified by ::bench_range.
+ #
+ # Eg:
+ #
+ # def bench_algorithm
+ # assert_performance_power 0.9999 do |x|
+ # @obj.algorithm
+ # end
+ # end
+
+ def assert_performance_power threshold = 0.99, &work
+ assert_performance validation_for_fit(:power, threshold), &work
+ end
+
+ ##
+ # Takes an array of x/y pairs and calculates the general R^2 value.
+ #
+ # See: http://en.wikipedia.org/wiki/Coefficient_of_determination
+
+ def fit_error xys
+ y_bar = sigma(xys) { |x, y| y } / xys.size.to_f
+ ss_tot = sigma(xys) { |x, y| (y - y_bar) ** 2 }
+ ss_err = sigma(xys) { |x, y| (yield(x) - y) ** 2 }
+
+ 1 - (ss_err / ss_tot)
+ end
+
+ ##
+ # To fit a functional form: y = ae^(bx).
+ #
+ # Takes x and y values and returns [a, b, r^2].
+ #
+ # See: http://mathworld.wolfram.com/LeastSquaresFittingExponential.html
+
+ def fit_exponential xs, ys
+ n = xs.size
+ xys = xs.zip(ys)
+ sxlny = sigma(xys) { |x,y| x * Math.log(y) }
+ slny = sigma(xys) { |x,y| Math.log(y) }
+ sx2 = sigma(xys) { |x,y| x * x }
+ sx = sigma xs
+
+ c = n * sx2 - sx ** 2
+ a = (slny * sx2 - sx * sxlny) / c
+ b = ( n * sxlny - sx * slny ) / c
+
+ return Math.exp(a), b, fit_error(xys) { |x| Math.exp(a + b * x) }
+ end
+
+ ##
+ # To fit a functional form: y = a + b*ln(x).
+ #
+ # Takes x and y values and returns [a, b, r^2].
+ #
+ # See: http://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html
+
+ def fit_logarithmic xs, ys
+ n = xs.size
+ xys = xs.zip(ys)
+ slnx2 = sigma(xys) { |x,y| Math.log(x) ** 2 }
+ slnx = sigma(xys) { |x,y| Math.log(x) }
+ sylnx = sigma(xys) { |x,y| y * Math.log(x) }
+ sy = sigma(xys) { |x,y| y }
+
+ c = n * slnx2 - slnx ** 2
+ b = ( n * sylnx - sy * slnx ) / c
+ a = (sy - b * slnx) / n
+
+ return a, b, fit_error(xys) { |x| a + b * Math.log(x) }
+ end
+
+
+ ##
+ # Fits the functional form: a + bx.
+ #
+ # Takes x and y values and returns [a, b, r^2].
+ #
+ # See: http://mathworld.wolfram.com/LeastSquaresFitting.html
+
+ def fit_linear xs, ys
+ n = xs.size
+ xys = xs.zip(ys)
+ sx = sigma xs
+ sy = sigma ys
+ sx2 = sigma(xs) { |x| x ** 2 }
+ sxy = sigma(xys) { |x,y| x * y }
+
+ c = n * sx2 - sx**2
+ a = (sy * sx2 - sx * sxy) / c
+ b = ( n * sxy - sx * sy ) / c
+
+ return a, b, fit_error(xys) { |x| a + b * x }
+ end
+
+ ##
+ # To fit a functional form: y = ax^b.
+ #
+ # Takes x and y values and returns [a, b, r^2].
+ #
+ # See: http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
+
+ def fit_power xs, ys
+ n = xs.size
+ xys = xs.zip(ys)
+ slnxlny = sigma(xys) { |x, y| Math.log(x) * Math.log(y) }
+ slnx = sigma(xs) { |x | Math.log(x) }
+ slny = sigma(ys) { | y| Math.log(y) }
+ slnx2 = sigma(xs) { |x | Math.log(x) ** 2 }
+
+ b = (n * slnxlny - slnx * slny) / (n * slnx2 - slnx ** 2);
+ a = (slny - b * slnx) / n
+
+ return Math.exp(a), b, fit_error(xys) { |x| (Math.exp(a) * (x ** b)) }
+ end
+
+ ##
+ # Enumerates over +enum+ mapping +block+ if given, returning the
+ # sum of the result. Eg:
+ #
+ # sigma([1, 2, 3]) # => 1 + 2 + 3 => 7
+ # sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14
+
+ def sigma enum, &block
+ enum = enum.map(&block) if block
+ enum.inject { |sum, n| sum + n }
+ end
+
+ ##
+ # Returns a proc that calls the specified fit method and asserts
+ # that the error is within a tolerable threshold.
+
+ def validation_for_fit msg, threshold
+ proc do |range, times|
+ a, b, rr = send "fit_#{msg}", range, times
+ assert_operator rr, :>=, threshold
+ [a, b, rr]
+ end
+ end
+ end
+end
+
+class MiniTest::Spec
+ ##
+ # This is used to define a new benchmark method. You usually don't
+ # use this directly and is intended for those needing to write new
+ # performance curve fits (eg: you need a specific polynomial fit).
+ #
+ # See ::bench_performance_linear for an example of how to use this.
+
+ def self.bench name, &block
+ define_method "bench_#{name.gsub(/\W+/, '_')}", &block
+ end
+
+ ##
+ # Specifies the ranges used for benchmarking for that class.
+ #
+ # bench_range do
+ # bench_exp(2, 16, 2)
+ # end
+ #
+ # See Unit::TestCase.bench_range for more details.
+
+ def self.bench_range &block
+ return super unless block
+
+ meta = (class << self; self; end)
+ meta.send :define_method, "bench_range", &block
+ end
+
+ ##
+ # Create a benchmark that verifies that the performance is linear.
+ #
+ # describe "my class" do
+ # bench_performance_linear "fast_algorithm", 0.9999 do |n|
+ # @obj.fast_algorithm(n)
+ # end
+ # end
+
+ def self.bench_performance_linear name, threshold = 0.99, &work
+ bench name do
+ assert_performance_linear threshold, &work
+ end
+ end
+
+ ##
+ # Create a benchmark that verifies that the performance is constant.
+ #
+ # describe "my class" do
+ # bench_performance_constant "zoom_algorithm!" do |n|
+ # @obj.zoom_algorithm!(n)
+ # end
+ # end
+
+ def self.bench_performance_constant name, threshold = 0.99, &work
+ bench name do
+ assert_performance_constant threshold, &work
+ end
+ end
+
+ ##
+ # Create a benchmark that verifies that the performance is exponential.
+ #
+ # describe "my class" do
+ # bench_performance_exponential "algorithm" do |n|
+ # @obj.algorithm(n)
+ # end
+ # end
+
+ def self.bench_performance_exponential name, threshold = 0.99, &work
+ bench name do
+ assert_performance_exponential threshold, &work
+ end
+ end
+end
diff --git a/jni/ruby/test/lib/minitest/mock.rb b/jni/ruby/test/lib/minitest/mock.rb
new file mode 100644
index 0000000..19acaff
--- /dev/null
+++ b/jni/ruby/test/lib/minitest/mock.rb
@@ -0,0 +1,195 @@
+# encoding: utf-8
+
+class MockExpectationError < StandardError; end # :nodoc:
+
+##
+# A simple and clean mock object framework.
+
+module MiniTest # :nodoc:
+
+ ##
+ # All mock objects are an instance of Mock
+
+ class Mock
+ alias :__respond_to? :respond_to?
+
+ skip_methods = %w(object_id respond_to_missing? inspect === to_s)
+
+ instance_methods.each do |m|
+ undef_method m unless skip_methods.include?(m.to_s) || m =~ /^__/
+ end
+
+ def initialize # :nodoc:
+ @expected_calls = Hash.new { |calls, name| calls[name] = [] }
+ @actual_calls = Hash.new { |calls, name| calls[name] = [] }
+ end
+
+ ##
+ # Expect that method +name+ is called, optionally with +args+ or a
+ # +blk+, and returns +retval+.
+ #
+ # @mock.expect(:meaning_of_life, 42)
+ # @mock.meaning_of_life # => 42
+ #
+ # @mock.expect(:do_something_with, true, [some_obj, true])
+ # @mock.do_something_with(some_obj, true) # => true
+ #
+ # @mock.expect(:do_something_else, true) do |a1, a2|
+ # a1 == "buggs" && a2 == :bunny
+ # end
+ #
+ # +args+ is compared to the expected args using case equality (ie, the
+ # '===' operator), allowing for less specific expectations.
+ #
+ # @mock.expect(:uses_any_string, true, [String])
+ # @mock.uses_any_string("foo") # => true
+ # @mock.verify # => true
+ #
+ # @mock.expect(:uses_one_string, true, ["foo"]
+ # @mock.uses_one_string("bar") # => true
+ # @mock.verify # => raises MockExpectationError
+
+ def expect(name, retval, args=[], &blk)
+ if block_given?
+ raise ArgumentError, "args ignored when block given" unless args.empty?
+ @expected_calls[name] << { :retval => retval, :block => blk }
+ else
+ raise ArgumentError, "args must be an array" unless Array === args
+ @expected_calls[name] << { :retval => retval, :args => args }
+ end
+ self
+ end
+
+ def __call name, data # :nodoc:
+ case data
+ when Hash then
+ "#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}"
+ else
+ data.map { |d| __call name, d }.join ", "
+ end
+ end
+
+ ##
+ # Verify that all methods were called as expected. Raises
+ # +MockExpectationError+ if the mock object was not called as
+ # expected.
+
+ def verify
+ @expected_calls.each do |name, calls|
+ calls.each do |expected|
+ msg1 = "expected #{__call name, expected}"
+ msg2 = "#{msg1}, got [#{__call name, @actual_calls[name]}]"
+
+ raise MockExpectationError, msg2 if
+ @actual_calls.has_key?(name) and
+ not @actual_calls[name].include?(expected)
+
+ raise MockExpectationError, msg1 unless
+ @actual_calls.has_key?(name) and
+ @actual_calls[name].include?(expected)
+ end
+ end
+ true
+ end
+
+ def method_missing(sym, *args) # :nodoc:
+ unless @expected_calls.has_key?(sym) then
+ raise NoMethodError, "unmocked method %p, expected one of %p" %
+ [sym, @expected_calls.keys.sort_by(&:to_s)]
+ end
+
+ index = @actual_calls[sym].length
+ expected_call = @expected_calls[sym][index]
+
+ unless expected_call then
+ raise MockExpectationError, "No more expects available for %p: %p" %
+ [sym, args]
+ end
+
+ expected_args, retval, val_block =
+ expected_call.values_at(:args, :retval, :block)
+
+ if val_block then
+ raise MockExpectationError, "mocked method %p failed block w/ %p" %
+ [sym, args] unless val_block.call(args)
+
+ # keep "verify" happy
+ @actual_calls[sym] << expected_call
+ return retval
+ end
+
+ if expected_args.size != args.size then
+ raise ArgumentError, "mocked method %p expects %d arguments, got %d" %
+ [sym, expected_args.size, args.size]
+ end
+
+ fully_matched = expected_args.zip(args).all? { |mod, a|
+ mod === a or mod == a
+ }
+
+ unless fully_matched then
+ raise MockExpectationError, "mocked method %p called with unexpected arguments %p" %
+ [sym, args]
+ end
+
+ @actual_calls[sym] << {
+ :retval => retval,
+ :args => expected_args.zip(args).map { |mod, a| mod === a ? mod : a }
+ }
+
+ retval
+ end
+
+ def respond_to?(sym, include_private = false) # :nodoc:
+ return true if @expected_calls.has_key?(sym.to_sym)
+ return __respond_to?(sym, include_private)
+ end
+ end
+end
+
+class Object # :nodoc:
+
+ ##
+ # Add a temporary stubbed method replacing +name+ for the duration
+ # of the +block+. If +val_or_callable+ responds to #call, then it
+ # returns the result of calling it, otherwise returns the value
+ # as-is. Cleans up the stub at the end of the +block+. The method
+ # +name+ must exist before stubbing.
+ #
+ # def test_stale_eh
+ # obj_under_test = Something.new
+ # refute obj_under_test.stale?
+ #
+ # Time.stub :now, Time.at(0) do
+ # assert obj_under_test.stale?
+ # end
+ # end
+
+ def stub name, val_or_callable, &block
+ new_name = "__minitest_stub__#{name}"
+
+ metaclass = class << self; self; end
+
+ if respond_to? name and not methods.map(&:to_s).include? name.to_s then
+ metaclass.send :define_method, name do |*args|
+ super(*args)
+ end
+ end
+
+ metaclass.send :alias_method, new_name, name
+
+ metaclass.send :define_method, name do |*args|
+ if val_or_callable.respond_to? :call then
+ val_or_callable.call(*args)
+ else
+ val_or_callable
+ end
+ end
+
+ yield self
+ ensure
+ metaclass.send :undef_method, name
+ metaclass.send :alias_method, name, new_name
+ metaclass.send :undef_method, new_name
+ end
+end
diff --git a/jni/ruby/test/lib/minitest/unit.rb b/jni/ruby/test/lib/minitest/unit.rb
new file mode 100644
index 0000000..0a6558d
--- /dev/null
+++ b/jni/ruby/test/lib/minitest/unit.rb
@@ -0,0 +1,1419 @@
+# encoding: utf-8
+
+require "optparse"
+require "rbconfig"
+require "leakchecker"
+
+##
+# Minimal (mostly drop-in) replacement for test-unit.
+#
+# :include: README.txt
+
+module MiniTest
+
+ def self.const_missing name # :nodoc:
+ case name
+ when :MINI_DIR then
+ msg = "MiniTest::MINI_DIR was removed. Don't violate other's internals."
+ warn "WAR\NING: #{msg}"
+ warn "WAR\NING: Used by #{caller.first}."
+ const_set :MINI_DIR, "bad value"
+ else
+ super
+ end
+ end
+
+ ##
+ # Assertion base class
+
+ class Assertion < Exception; end
+
+ ##
+ # Assertion raised when skipping a test
+
+ class Skip < Assertion; end
+
+ class << self
+ ##
+ # Filter object for backtraces.
+
+ attr_accessor :backtrace_filter
+ end
+
+ class BacktraceFilter # :nodoc:
+ def filter bt
+ return ["No backtrace"] unless bt
+
+ new_bt = []
+
+ unless $DEBUG then
+ bt.each do |line|
+ break if line =~ /lib\/minitest/
+ new_bt << line
+ end
+
+ new_bt = bt.reject { |line| line =~ /lib\/minitest/ } if new_bt.empty?
+ new_bt = bt.dup if new_bt.empty?
+ else
+ new_bt = bt.dup
+ end
+
+ new_bt
+ end
+ end
+
+ self.backtrace_filter = BacktraceFilter.new
+
+ def self.filter_backtrace bt # :nodoc:
+ backtrace_filter.filter bt
+ end
+
+ ##
+ # MiniTest Assertions. All assertion methods accept a +msg+ which is
+ # printed if the assertion fails.
+
+ module Assertions
+ UNDEFINED = Object.new # :nodoc:
+
+ def UNDEFINED.inspect # :nodoc:
+ "UNDEFINED" # again with the rdoc bugs... :(
+ end
+
+ ##
+ # Returns the diff command to use in #diff. Tries to intelligently
+ # figure out what diff to use.
+
+ def self.diff
+ @diff = if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ &&
+ system("diff.exe", __FILE__, __FILE__)) then
+ "diff.exe -u"
+ elsif Minitest::Unit::Guard.maglev? then # HACK
+ "diff -u"
+ elsif system("gdiff", __FILE__, __FILE__)
+ "gdiff -u" # solaris and kin suck
+ elsif system("diff", __FILE__, __FILE__)
+ "diff -u"
+ else
+ nil
+ end unless defined? @diff
+
+ @diff
+ end
+
+ ##
+ # Set the diff command to use in #diff.
+
+ def self.diff= o
+ @diff = o
+ end
+
+ ##
+ # Returns a diff between +exp+ and +act+. If there is no known
+ # diff command or if it doesn't make sense to diff the output
+ # (single line, short output), then it simply returns a basic
+ # comparison between the two.
+
+ def diff exp, act
+ require "tempfile"
+
+ expect = mu_pp_for_diff exp
+ butwas = mu_pp_for_diff act
+ result = nil
+
+ need_to_diff =
+ MiniTest::Assertions.diff &&
+ (expect.include?("\n") ||
+ butwas.include?("\n") ||
+ expect.size > 30 ||
+ butwas.size > 30 ||
+ expect == butwas)
+
+ return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
+ need_to_diff
+
+ tempfile_a = nil
+ tempfile_b = nil
+
+ Tempfile.open("expect") do |a|
+ tempfile_a = a
+ a.puts expect
+ a.flush
+
+ Tempfile.open("butwas") do |b|
+ tempfile_b = b
+ b.puts butwas
+ b.flush
+
+ result = `#{MiniTest::Assertions.diff} #{a.path} #{b.path}`
+ result.sub!(/^\-\-\- .+/, "--- expected")
+ result.sub!(/^\+\+\+ .+/, "+++ actual")
+
+ if result.empty? then
+ klass = exp.class
+ result = [
+ "No visible difference in the #{klass}#inspect output.\n",
+ "You should look at the implementation of #== on ",
+ "#{klass} or its members.\n",
+ expect,
+ ].join
+ end
+ end
+ end
+
+ result
+ ensure
+ tempfile_a.close! if tempfile_a
+ tempfile_b.close! if tempfile_b
+ end
+
+ ##
+ # This returns a human-readable version of +obj+. By default
+ # #inspect is called. You can override this to use #pretty_print
+ # if you want.
+
+ def mu_pp obj
+ s = obj.inspect
+ s = s.encode Encoding.default_external if defined? Encoding
+ s
+ end
+
+ ##
+ # This returns a diff-able human-readable version of +obj+. This
+ # differs from the regular mu_pp because it expands escaped
+ # newlines and makes hex-values generic (like object_ids). This
+ # uses mu_pp to do the first pass and then cleans it up.
+
+ def mu_pp_for_diff obj
+ mu_pp(obj).gsub(/\\n/, "\n").gsub(/:0x[a-fA-F0-9]{4,}/m, ':0xXXXXXX')
+ end
+
+ def _assertions= n # :nodoc:
+ @_assertions = n
+ end
+
+ def _assertions # :nodoc:
+ @_assertions ||= 0
+ end
+
+ ##
+ # Fails unless +test+ is a true value.
+
+ def assert test, msg = nil
+ msg ||= "Failed assertion, no message given."
+ self._assertions += 1
+ unless test then
+ msg = msg.call if Proc === msg
+ raise MiniTest::Assertion, msg
+ end
+ true
+ end
+
+ ##
+ # Fails unless +obj+ is empty.
+
+ def assert_empty obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
+ assert_respond_to obj, :empty?
+ assert obj.empty?, msg
+ end
+
+ ##
+ # Fails unless <tt>exp == act</tt> printing the difference between
+ # the two, if possible.
+ #
+ # If there is no visible difference but the assertion fails, you
+ # should suspect that your #== is buggy, or your inspect output is
+ # missing crucial details.
+ #
+ # For floats use assert_in_delta.
+ #
+ # See also: MiniTest::Assertions.diff
+
+ def assert_equal exp, act, msg = nil
+ msg = message(msg, "") { diff exp, act }
+ assert exp == act, msg
+ end
+
+ ##
+ # For comparing Floats. Fails unless +exp+ and +act+ are within +delta+
+ # of each other.
+ #
+ # assert_in_delta Math::PI, (22.0 / 7.0), 0.01
+
+ def assert_in_delta exp, act, delta = 0.001, msg = nil
+ n = (exp - act).abs
+ msg = message(msg) {
+ "Expected |#{exp} - #{act}| (#{n}) to be <= #{delta}"
+ }
+ assert delta >= n, msg
+ end
+
+ ##
+ # For comparing Floats. Fails unless +exp+ and +act+ have a relative
+ # error less than +epsilon+.
+
+ def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
+ assert_in_delta a, b, [a.abs, b.abs].min * epsilon, msg
+ end
+
+ ##
+ # Fails unless +collection+ includes +obj+.
+
+ def assert_includes collection, obj, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
+ }
+ assert_respond_to collection, :include?
+ assert collection.include?(obj), msg
+ end
+
+ ##
+ # Fails unless +obj+ is an instance of +cls+.
+
+ def assert_instance_of cls, obj, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}"
+ }
+
+ assert obj.instance_of?(cls), msg
+ end
+
+ ##
+ # Fails unless +obj+ is a kind of +cls+.
+
+ def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
+
+ assert obj.kind_of?(cls), msg
+ end
+
+ ##
+ # Fails unless +matcher+ <tt>=~</tt> +obj+.
+
+ def assert_match matcher, obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" }
+ assert_respond_to matcher, :"=~"
+ matcher = Regexp.new Regexp.escape matcher if String === matcher
+ assert matcher =~ obj, msg
+ end
+
+ ##
+ # Fails unless +obj+ is nil
+
+ def assert_nil obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
+ assert obj.nil?, msg
+ end
+
+ ##
+ # For testing with binary operators.
+ #
+ # assert_operator 5, :<=, 4
+
+ def assert_operator o1, op, o2 = UNDEFINED, msg = nil
+ return assert_predicate o1, op, msg if UNDEFINED == o2
+ msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
+ assert o1.__send__(op, o2), msg
+ end
+
+ ##
+ # Fails if stdout or stderr do not output the expected results.
+ # Pass in nil if you don't care about that streams output. Pass in
+ # "" if you require it to be silent. Pass in a regexp if you want
+ # to pattern match.
+ #
+ # NOTE: this uses #capture_io, not #capture_subprocess_io.
+ #
+ # See also: #assert_silent
+
+ def assert_output stdout = nil, stderr = nil
+ out, err = capture_io do
+ yield
+ end
+
+ err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
+ out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
+
+ y = send err_msg, stderr, err, "In stderr" if err_msg
+ x = send out_msg, stdout, out, "In stdout" if out_msg
+
+ (!stdout || x) && (!stderr || y)
+ end
+
+ ##
+ # For testing with predicates.
+ #
+ # assert_predicate str, :empty?
+ #
+ # This is really meant for specs and is front-ended by assert_operator:
+ #
+ # str.must_be :empty?
+
+ def assert_predicate o1, op, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" }
+ assert o1.__send__(op), msg
+ end
+
+ ##
+ # Fails unless the block raises one of +exp+. Returns the
+ # exception matched so you can check the message, attributes, etc.
+
+ def assert_raises *exp
+ msg = "#{exp.pop}.\n" if String === exp.last
+
+ begin
+ yield
+ rescue MiniTest::Skip => e
+ return e if exp.include? MiniTest::Skip
+ raise e
+ rescue Exception => e
+ expected = exp.any? { |ex|
+ if ex.instance_of? Module then
+ e.kind_of? ex
+ else
+ e.instance_of? ex
+ end
+ }
+
+ assert expected, proc {
+ exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not")
+ }
+
+ return e
+ end
+
+ exp = exp.first if exp.size == 1
+
+ flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised."
+ end
+
+ ##
+ # Fails unless +obj+ responds to +meth+.
+
+ def assert_respond_to obj, meth, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
+ }
+ assert obj.respond_to?(meth), msg
+ end
+
+ ##
+ # Fails unless +exp+ and +act+ are #equal?
+
+ def assert_same exp, act, msg = nil
+ msg = message(msg) {
+ data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
+ "Expected %s (oid=%d) to be the same as %s (oid=%d)" % data
+ }
+ assert exp.equal?(act), msg
+ end
+
+ ##
+ # +send_ary+ is a receiver, message and arguments.
+ #
+ # Fails unless the call returns a true value
+ # TODO: I should prolly remove this from specs
+
+ def assert_send send_ary, m = nil
+ recv, msg, *args = send_ary
+ m = message(m) {
+ "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
+ assert recv.__send__(msg, *args), m
+ end
+
+ ##
+ # Fails if the block outputs anything to stderr or stdout.
+ #
+ # See also: #assert_output
+
+ def assert_silent
+ assert_output "", "" do
+ yield
+ end
+ end
+
+ ##
+ # Fails unless the block throws +sym+
+
+ def assert_throws sym, msg = nil
+ default = "Expected #{mu_pp(sym)} to have been thrown"
+ caught = true
+ catch(sym) do
+ begin
+ yield
+ rescue ThreadError => e # wtf?!? 1.8 + threads == suck
+ default += ", not \:#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
+ rescue ArgumentError => e # 1.9 exception
+ default += ", not #{e.message.split(/ /).last}"
+ rescue NameError => e # 1.8 exception
+ default += ", not #{e.name.inspect}"
+ end
+ caught = false
+ end
+
+ assert caught, message(msg) { default }
+ end
+
+ ##
+ # Captures $stdout and $stderr into strings:
+ #
+ # out, err = capture_io do
+ # puts "Some info"
+ # warn "You did a bad thing"
+ # end
+ #
+ # assert_match %r%info%, out
+ # assert_match %r%bad%, err
+ #
+ # NOTE: For efficiency, this method uses StringIO and does not
+ # capture IO for subprocesses. Use #capture_subprocess_io for
+ # that.
+
+ def capture_io
+ require 'stringio'
+
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
+
+ synchronize do
+ orig_stdout, orig_stderr = $stdout, $stderr
+ $stdout, $stderr = captured_stdout, captured_stderr
+
+ begin
+ yield
+ ensure
+ $stdout = orig_stdout
+ $stderr = orig_stderr
+ end
+ end
+
+ return captured_stdout.string, captured_stderr.string
+ end
+
+ ##
+ # Captures $stdout and $stderr into strings, using Tempfile to
+ # ensure that subprocess IO is captured as well.
+ #
+ # out, err = capture_subprocess_io do
+ # system "echo Some info"
+ # system "echo You did a bad thing 1>&2"
+ # end
+ #
+ # assert_match %r%info%, out
+ # assert_match %r%bad%, err
+ #
+ # NOTE: This method is approximately 10x slower than #capture_io so
+ # only use it when you need to test the output of a subprocess.
+
+ def capture_subprocess_io
+ require 'tempfile'
+
+ captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
+
+ synchronize do
+ orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
+ $stdout.reopen captured_stdout
+ $stderr.reopen captured_stderr
+
+ begin
+ yield
+
+ $stdout.rewind
+ $stderr.rewind
+
+ [captured_stdout.read, captured_stderr.read]
+ ensure
+ $stdout.reopen orig_stdout
+ $stderr.reopen orig_stderr
+ orig_stdout.close
+ orig_stderr.close
+ captured_stdout.close!
+ captured_stderr.close!
+ end
+ end
+ end
+
+ ##
+ # Returns details for exception +e+
+
+ def exception_details e, msg
+ [
+ "#{msg}",
+ "Class: <#{e.class}>",
+ "Message: <#{e.message.inspect}>",
+ "---Backtrace---",
+ "#{MiniTest::filter_backtrace(e.backtrace).join("\n")}",
+ "---------------",
+ ].join "\n"
+ end
+
+ ##
+ # Fails with +msg+
+
+ def flunk msg = nil
+ msg ||= "Epic Fail!"
+ assert false, msg
+ end
+
+ ##
+ # Returns a proc that will output +msg+ along with the default message.
+
+ def message msg = nil, ending = ".", &default
+ proc {
+ msg = msg.call.chomp(".") if Proc === msg
+ custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
+ "#{custom_message}#{default.call}#{ending}"
+ }
+ end
+
+ ##
+ # used for counting assertions
+
+ def pass msg = nil
+ assert true
+ end
+
+ ##
+ # Fails if +test+ is a true value
+
+ def refute test, msg = nil
+ msg ||= "Failed refutation, no message given"
+ not assert(! test, msg)
+ end
+
+ ##
+ # Fails if +obj+ is empty.
+
+ def refute_empty obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
+ assert_respond_to obj, :empty?
+ refute obj.empty?, msg
+ end
+
+ ##
+ # Fails if <tt>exp == act</tt>.
+ #
+ # For floats use refute_in_delta.
+
+ def refute_equal exp, act, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
+ }
+ refute exp == act, msg
+ end
+
+ ##
+ # For comparing Floats. Fails if +exp+ is within +delta+ of +act+.
+ #
+ # refute_in_delta Math::PI, (22.0 / 7.0)
+
+ def refute_in_delta exp, act, delta = 0.001, msg = nil
+ n = (exp - act).abs
+ msg = message(msg) {
+ "Expected |#{exp} - #{act}| (#{n}) to not be <= #{delta}"
+ }
+ refute delta >= n, msg
+ end
+
+ ##
+ # For comparing Floats. Fails if +exp+ and +act+ have a relative error
+ # less than +epsilon+.
+
+ def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
+ refute_in_delta a, b, a * epsilon, msg
+ end
+
+ ##
+ # Fails if +collection+ includes +obj+.
+
+ def refute_includes collection, obj, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
+ }
+ assert_respond_to collection, :include?
+ refute collection.include?(obj), msg
+ end
+
+ ##
+ # Fails if +obj+ is an instance of +cls+.
+
+ def refute_instance_of cls, obj, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} to not be an instance of #{cls}"
+ }
+ refute obj.instance_of?(cls), msg
+ end
+
+ ##
+ # Fails if +obj+ is a kind of +cls+.
+
+ def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
+ refute obj.kind_of?(cls), msg
+ end
+
+ ##
+ # Fails if +matcher+ <tt>=~</tt> +obj+.
+
+ def refute_match matcher, obj, msg = nil
+ msg = message(msg) {"Expected #{mu_pp matcher} to not match #{mu_pp obj}"}
+ assert_respond_to matcher, :"=~"
+ matcher = Regexp.new Regexp.escape matcher if String === matcher
+ refute matcher =~ obj, msg
+ end
+
+ ##
+ # Fails if +obj+ is nil.
+
+ def refute_nil obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
+ refute obj.nil?, msg
+ end
+
+ ##
+ # Fails if +o1+ is not +op+ +o2+. Eg:
+ #
+ # refute_operator 1, :>, 2 #=> pass
+ # refute_operator 1, :<, 2 #=> fail
+
+ def refute_operator o1, op, o2 = UNDEFINED, msg = nil
+ return refute_predicate o1, op, msg if UNDEFINED == o2
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}"}
+ refute o1.__send__(op, o2), msg
+ end
+
+ ##
+ # For testing with predicates.
+ #
+ # refute_predicate str, :empty?
+ #
+ # This is really meant for specs and is front-ended by refute_operator:
+ #
+ # str.wont_be :empty?
+
+ def refute_predicate o1, op, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" }
+ refute o1.__send__(op), msg
+ end
+
+ ##
+ # Fails if +obj+ responds to the message +meth+.
+
+ def refute_respond_to obj, meth, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
+
+ refute obj.respond_to?(meth), msg
+ end
+
+ ##
+ # Fails if +exp+ is the same (by object identity) as +act+.
+
+ def refute_same exp, act, msg = nil
+ msg = message(msg) {
+ data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
+ "Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data
+ }
+ refute exp.equal?(act), msg
+ end
+
+ ##
+ # Skips the current test. Gets listed at the end of the run but
+ # doesn't cause a failure exit code.
+
+ def skip msg = nil, bt = caller
+ msg ||= "Skipped, no message given"
+ @skip = true
+ raise MiniTest::Skip, msg, bt
+ end
+
+ ##
+ # Was this testcase skipped? Meant for #teardown.
+
+ def skipped?
+ defined?(@skip) and @skip
+ end
+
+ ##
+ # Takes a block and wraps it with the runner's shared mutex.
+
+ def synchronize
+ Minitest::Unit.runner.synchronize do
+ yield
+ end
+ end
+ end
+
+ class Unit # :nodoc:
+ VERSION = "4.7.5" # :nodoc:
+
+ attr_accessor :report, :failures, :errors, :skips # :nodoc:
+ attr_accessor :assertion_count # :nodoc:
+ attr_writer :test_count # :nodoc:
+ attr_accessor :start_time # :nodoc:
+ attr_accessor :help # :nodoc:
+ attr_accessor :verbose # :nodoc:
+ attr_writer :options # :nodoc:
+
+ ##
+ # :attr:
+ #
+ # if true, installs an "INFO" signal handler (only available to BSD and
+ # OS X users) which prints diagnostic information about the test run.
+ #
+ # This is auto-detected by default but may be overridden by custom
+ # runners.
+
+ attr_accessor :info_signal
+
+ ##
+ # Lazy accessor for options.
+
+ def options
+ @options ||= {}
+ end
+
+ @@installed_at_exit ||= false
+ @@out = $stdout
+ @@after_tests = []
+
+ ##
+ # A simple hook allowing you to run a block of code after _all_ of
+ # the tests are done. Eg:
+ #
+ # MiniTest::Unit.after_tests { p $debugging_info }
+
+ def self.after_tests &block
+ @@after_tests << block
+ end
+
+ ##
+ # Registers MiniTest::Unit to run tests at process exit
+
+ def self.autorun
+ at_exit {
+ # don't run if there was a non-exit exception
+ next if $! and not $!.kind_of? SystemExit
+
+ # the order here is important. The at_exit handler must be
+ # installed before anyone else gets a chance to install their
+ # own, that way we can be assured that our exit will be last
+ # to run (at_exit stacks).
+ exit_code = nil
+
+ at_exit {
+ @@after_tests.reverse_each(&:call)
+ exit false if exit_code && exit_code != 0
+ }
+
+ exit_code = MiniTest::Unit.new.run ARGV
+ } unless @@installed_at_exit
+ @@installed_at_exit = true
+ end
+
+ ##
+ # Returns the stream to use for output.
+
+ def self.output
+ @@out
+ end
+
+ ##
+ # Sets MiniTest::Unit to write output to +stream+. $stdout is the default
+ # output
+
+ def self.output= stream
+ @@out = stream
+ end
+
+ ##
+ # Tells MiniTest::Unit to delegate to +runner+, an instance of a
+ # MiniTest::Unit subclass, when MiniTest::Unit#run is called.
+
+ def self.runner= runner
+ @@runner = runner
+ end
+
+ ##
+ # Returns the MiniTest::Unit subclass instance that will be used
+ # to run the tests. A MiniTest::Unit instance is the default
+ # runner.
+
+ def self.runner
+ @@runner ||= self.new
+ end
+
+ ##
+ # Return all plugins' run methods (methods that start with "run_").
+
+ def self.plugins
+ @@plugins ||= (["run_tests"] +
+ public_instance_methods(false).
+ grep(/^run_/).map { |s| s.to_s }).uniq
+ end
+
+ ##
+ # Return the IO for output.
+
+ def output
+ self.class.output
+ end
+
+ def puts *a # :nodoc:
+ output.puts(*a)
+ end
+
+ def print *a # :nodoc:
+ output.print(*a)
+ end
+
+ def test_count # :nodoc:
+ @test_count ||= 0
+ end
+
+ ##
+ # Runner for a given +type+ (eg, test vs bench).
+
+ def _run_anything type
+ suites = TestCase.send "#{type}_suites"
+ return if suites.empty?
+
+ start = Time.now
+
+ puts
+ puts "# Running #{type}s:"
+ puts
+
+ @test_count, @assertion_count = 0, 0
+ sync = output.respond_to? :"sync=" # stupid emacs
+ old_sync, output.sync = output.sync, true if sync
+
+ results = _run_suites suites, type
+
+ @test_count = results.inject(0) { |sum, (tc, _)| sum + tc }
+ @assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac }
+
+ output.sync = old_sync if sync
+
+ t = Time.now - start
+
+ puts
+ puts
+ puts "Finished #{type}s in %.6fs, %.4f tests/s, %.4f assertions/s." %
+ [t, test_count / t, assertion_count / t]
+
+ report.each_with_index do |msg, i|
+ puts "\n%3d) %s" % [i + 1, msg]
+ end
+
+ puts
+
+ status
+ end
+
+ ##
+ # Runs all the +suites+ for a given +type+.
+ #
+
+ def _run_suites suites, type
+ suites.map { |suite| _run_suite suite, type }
+ end
+
+ ##
+ # Run a single +suite+ for a given +type+.
+
+ def _run_suite suite, type
+ header = "#{type}_suite_header"
+ puts send(header, suite) if respond_to? header
+
+ filter = options[:filter] || '/./'
+ filter = Regexp.new $1 if filter =~ /\/(.*)\//
+
+ all_test_methods = suite.send "#{type}_methods"
+
+ filtered_test_methods = all_test_methods.find_all { |m|
+ filter === m || filter === "#{suite}##{m}"
+ }
+
+ leakchecker = LeakChecker.new
+
+ assertions = filtered_test_methods.map { |method|
+ inst = suite.new method
+ inst._assertions = 0
+
+ print "#{suite}##{method} = " if @verbose
+
+ start_time = Time.now if @verbose
+ result = inst.run self
+
+ print "%.2f s = " % (Time.now - start_time) if @verbose
+ print result
+ puts if @verbose
+ $stdout.flush
+
+ leakchecker.check("#{inst.class}\##{inst.__name__}")
+
+ inst._assertions
+ }
+
+ return assertions.size, assertions.inject(0) { |sum, n| sum + n }
+ end
+
+ ##
+ # Record the result of a single test. Makes it very easy to gather
+ # information. Eg:
+ #
+ # class StatisticsRecorder < MiniTest::Unit
+ # def record suite, method, assertions, time, error
+ # # ... record the results somewhere ...
+ # end
+ # end
+ #
+ # MiniTest::Unit.runner = StatisticsRecorder.new
+ #
+ # NOTE: record might be sent more than once per test. It will be
+ # sent once with the results from the test itself. If there is a
+ # failure or error in teardown, it will be sent again with the
+ # error or failure.
+
+ def record suite, method, assertions, time, error
+ end
+
+ def location e # :nodoc:
+ last_before_assertion = ""
+ e.backtrace.reverse_each do |s|
+ break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
+ last_before_assertion = s
+ end
+ last_before_assertion.sub(/:in .*$/, '')
+ end
+
+ ##
+ # Writes status for failed test +meth+ in +klass+ which finished with
+ # exception +e+
+
+ def puke klass, meth, e
+ e = case e
+ when MiniTest::Skip then
+ @skips += 1
+ return "S" unless @verbose
+ "Skipped:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n"
+ when MiniTest::Assertion then
+ @failures += 1
+ "Failure:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n"
+ else
+ @errors += 1
+ bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
+ "Error:\n#{klass}##{meth}:\n#{e.class}: #{e.message}\n #{bt}\n"
+ end
+ @report << e
+ e[0, 1]
+ end
+
+ def initialize # :nodoc:
+ @report = []
+ @errors = @failures = @skips = 0
+ @verbose = false
+ @mutex = defined?(Mutex) ? Mutex.new : nil
+ @info_signal = Signal.list['INFO']
+ end
+
+ def synchronize # :nodoc:
+ if @mutex then
+ @mutex.synchronize { yield }
+ else
+ yield
+ end
+ end
+
+ def process_args args = [] # :nodoc:
+ options = {}
+ orig_args = args.dup
+
+ OptionParser.new do |opts|
+ opts.banner = 'minitest options:'
+ opts.version = MiniTest::Unit::VERSION
+
+ opts.on '-h', '--help', 'Display this help.' do
+ puts opts
+ exit
+ end
+
+ opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
+ options[:seed] = m.to_i
+ end
+
+ opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
+ options[:verbose] = true
+ end
+
+ opts.on '-n', '--name PATTERN', "Filter test names on pattern (e.g. /foo/)" do |a|
+ options[:filter] = a
+ end
+
+ opts.parse! args
+ orig_args -= args
+ end
+
+ unless options[:seed] then
+ srand
+ options[:seed] = srand % 0xFFFF
+ orig_args << "--seed" << options[:seed].to_s
+ end
+
+ srand options[:seed]
+
+ self.verbose = options[:verbose]
+ @help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
+
+ options
+ end
+
+ ##
+ # Begins the full test run. Delegates to +runner+'s #_run method.
+
+ def run args = []
+ self.class.runner._run(args)
+ end
+
+ ##
+ # Top level driver, controls all output and filtering.
+
+ def _run args = []
+ args = process_args args # ARGH!! blame test/unit process_args
+ self.options.merge! args
+
+ puts "Run options: #{help}"
+
+ self.class.plugins.each do |plugin|
+ send plugin
+ break unless report.empty?
+ end
+
+ return failures + errors if self.test_count > 0 # or return nil...
+ rescue Interrupt
+ abort 'Interrupted'
+ end
+
+ ##
+ # Runs test suites matching +filter+.
+
+ def run_tests
+ _run_anything :test
+ end
+
+ ##
+ # Writes status to +io+
+
+ def status io = self.output
+ format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
+ io.puts format % [test_count, assertion_count, failures, errors, skips]
+ end
+
+ ##
+ # Provides a simple set of guards that you can use in your tests
+ # to skip execution if it is not applicable. These methods are
+ # mixed into TestCase as both instance and class methods so you
+ # can use them inside or outside of the test methods.
+ #
+ # def test_something_for_mri
+ # skip "bug 1234" if jruby?
+ # # ...
+ # end
+ #
+ # if windows? then
+ # # ... lots of test methods ...
+ # end
+
+ module Guard
+
+ ##
+ # Is this running on jruby?
+
+ def jruby? platform = RUBY_PLATFORM
+ "java" == platform
+ end
+
+ ##
+ # Is this running on mri?
+
+ def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
+ "maglev" == platform
+ end
+
+ module_function :maglev?
+
+ ##
+ # Is this running on mri?
+
+ def mri? platform = RUBY_DESCRIPTION
+ /^ruby/ =~ platform
+ end
+
+ ##
+ # Is this running on rubinius?
+
+ def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
+ "rbx" == platform
+ end
+
+ ##
+ # Is this running on windows?
+
+ def windows? platform = RUBY_PLATFORM
+ /mswin|mingw/ =~ platform
+ end
+ end
+
+ ##
+ # Provides before/after hooks for setup and teardown. These are
+ # meant for library writers, NOT for regular test authors. See
+ # #before_setup for an example.
+
+ module LifecycleHooks
+ ##
+ # Runs before every test, after setup. This hook is meant for
+ # libraries to extend minitest. It is not meant to be used by
+ # test developers.
+ #
+ # See #before_setup for an example.
+
+ def after_setup; end
+
+ ##
+ # Runs before every test, before setup. This hook is meant for
+ # libraries to extend minitest. It is not meant to be used by
+ # test developers.
+ #
+ # As a simplistic example:
+ #
+ # module MyMinitestPlugin
+ # def before_setup
+ # super
+ # # ... stuff to do before setup is run
+ # end
+ #
+ # def after_setup
+ # # ... stuff to do after setup is run
+ # super
+ # end
+ #
+ # def before_teardown
+ # super
+ # # ... stuff to do before teardown is run
+ # end
+ #
+ # def after_teardown
+ # # ... stuff to do after teardown is run
+ # super
+ # end
+ # end
+ #
+ # class MiniTest::Unit::TestCase
+ # include MyMinitestPlugin
+ # end
+
+ def before_setup; end
+
+ ##
+ # Runs after every test, before teardown. This hook is meant for
+ # libraries to extend minitest. It is not meant to be used by
+ # test developers.
+ #
+ # See #before_setup for an example.
+
+ def before_teardown; end
+
+ ##
+ # Runs after every test, after teardown. This hook is meant for
+ # libraries to extend minitest. It is not meant to be used by
+ # test developers.
+ #
+ # See #before_setup for an example.
+
+ def after_teardown; end
+ end
+
+ ##
+ # Subclass TestCase to create your own tests. Typically you'll want a
+ # TestCase subclass per implementation class.
+ #
+ # See MiniTest::Assertions
+
+ class TestCase
+ include LifecycleHooks
+ include Guard
+ extend Guard
+
+ attr_reader :__name__ # :nodoc:
+
+ PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException,
+ Interrupt, SystemExit] # :nodoc:
+
+ ##
+ # Runs the tests reporting the status to +runner+
+
+ def run runner
+ trap "INFO" do
+ runner.report.each_with_index do |msg, i|
+ warn "\n%3d) %s" % [i + 1, msg]
+ end
+ warn ''
+ time = runner.start_time ? Time.now - runner.start_time : 0
+ warn "Current Test: %s#%s %.2fs" % [self.class, self.__name__, time]
+ runner.status $stderr
+ end if runner.info_signal
+
+ start_time = Time.now
+
+ result = ""
+ begin
+ @passed = nil
+ self.before_setup
+ self.setup
+ self.after_setup
+ self.run_test self.__name__
+ result = "." unless io?
+ time = Time.now - start_time
+ runner.record self.class, self.__name__, self._assertions, time, nil
+ @passed = true
+ rescue *PASSTHROUGH_EXCEPTIONS
+ raise
+ rescue Exception => e
+ @passed = Skip === e
+ time = Time.now - start_time
+ runner.record self.class, self.__name__, self._assertions, time, e
+ result = runner.puke self.class, self.__name__, e
+ ensure
+ %w{ before_teardown teardown after_teardown }.each do |hook|
+ begin
+ self.send hook
+ rescue *PASSTHROUGH_EXCEPTIONS
+ raise
+ rescue Exception => e
+ @passed = false
+ runner.record self.class, self.__name__, self._assertions, time, e
+ result = runner.puke self.class, self.__name__, e
+ end
+ end
+ trap 'INFO', 'DEFAULT' if runner.info_signal
+ end
+ result
+ end
+
+ alias :run_test :__send__
+
+ def initialize name # :nodoc:
+ @__name__ = name
+ @__io__ = nil
+ @passed = nil
+ @@current = self # FIX: make thread local
+ end
+
+ def self.current # :nodoc:
+ @@current # FIX: make thread local
+ end
+
+ ##
+ # Return the output IO object
+
+ def io
+ @__io__ = true
+ MiniTest::Unit.output
+ end
+
+ ##
+ # Have we hooked up the IO yet?
+
+ def io?
+ @__io__
+ end
+
+ def self.reset # :nodoc:
+ @@test_suites = {}
+ end
+
+ reset
+
+ ##
+ # Call this at the top of your tests when you absolutely
+ # positively need to have ordered tests. In doing so, you're
+ # admitting that you suck and your tests are weak.
+
+ def self.i_suck_and_my_tests_are_order_dependent!
+ class << self
+ undef_method :test_order if method_defined? :test_order
+ define_method :test_order do :alpha end
+ end
+ end
+
+ ##
+ # Make diffs for this TestCase use #pretty_inspect so that diff
+ # in assert_equal can be more details. NOTE: this is much slower
+ # than the regular inspect but much more usable for complex
+ # objects.
+
+ def self.make_my_diffs_pretty!
+ require 'pp'
+
+ define_method :mu_pp do |o|
+ o.pretty_inspect
+ end
+ end
+
+ def self.inherited klass # :nodoc:
+ @@test_suites[klass] = true
+ super
+ end
+
+ def self.test_order # :nodoc:
+ :random
+ end
+
+ def self.test_suites # :nodoc:
+ @@test_suites.keys.sort_by { |ts| ts.name.to_s }
+ end
+
+ def self.test_methods # :nodoc:
+ methods = public_instance_methods(true).grep(/^test/).map { |m| m.to_s }
+
+ case self.test_order
+ when :parallel
+ max = methods.size
+ ParallelEach.new methods.sort.sort_by { rand max }
+ when :random then
+ max = methods.size
+ methods.sort.sort_by { rand max }
+ when :alpha, :sorted then
+ methods.sort
+ else
+ raise "Unknown test_order: #{self.test_order.inspect}"
+ end
+ end
+
+ ##
+ # Returns true if the test passed.
+
+ def passed?
+ @passed
+ end
+
+ ##
+ # Runs before every test. Use this to set up before each test
+ # run.
+
+ def setup; end
+
+ ##
+ # Runs after every test. Use this to clean up after each test
+ # run.
+
+ def teardown; end
+
+ include MiniTest::Assertions
+ end # class TestCase
+ end # class Unit
+
+ Test = Unit::TestCase
+end # module MiniTest
+
+Minitest = MiniTest # :nodoc: because ugh... I typo this all the time
diff --git a/jni/ruby/test/lib/profile_test_all.rb b/jni/ruby/test/lib/profile_test_all.rb
new file mode 100644
index 0000000..08de7bb
--- /dev/null
+++ b/jni/ruby/test/lib/profile_test_all.rb
@@ -0,0 +1,90 @@
+#
+# purpose:
+# Profile memory usage of each tests.
+#
+# usage:
+# RUBY_TEST_ALL_PROFILE=[file] make test-all
+#
+# output:
+# [file] specified by RUBY_TEST_ALL_PROFILE
+# If [file] is 'true', then it is ./test_all_profile
+#
+# collected information:
+# - ObjectSpace.memsize_of_all
+# - GC.stat
+# - /proc/meminfo (some fields, if exists)
+# - /proc/self/status (some fields, if exists)
+# - /proc/self/statm (if exists)
+#
+
+require 'objspace'
+
+class MiniTest::Unit::TestCase
+ alias orig_run run
+
+ file = ENV['RUBY_TEST_ALL_PROFILE']
+ file = 'test-all-profile-result' if file == 'true'
+ TEST_ALL_PROFILE_OUT = open(file, 'w')
+ TEST_ALL_PROFILE_GC_STAT_HASH = {}
+ TEST_ALL_PROFILE_BANNER = ['name']
+ TEST_ALL_PROFILE_PROCS = []
+
+ def self.add *name, &b
+ TEST_ALL_PROFILE_BANNER.concat name
+ TEST_ALL_PROFILE_PROCS << b
+ end
+
+ add 'failed?' do |result, tc|
+ result << (tc.passed? ? 0 : 1)
+ end
+
+ add 'memsize_of_all' do |result, *|
+ result << ObjectSpace.memsize_of_all
+ end
+
+ add *GC.stat.keys do |result, *|
+ GC.stat(TEST_ALL_PROFILE_GC_STAT_HASH)
+ result.concat TEST_ALL_PROFILE_GC_STAT_HASH.values
+ end
+
+ def self.add_proc_meminfo file, fields
+ return unless FileTest.exist?(file)
+ regexp = /(#{fields.join("|")}):\s*(\d+) kB/
+ # check = {}; fields.each{|e| check[e] = true}
+ add *fields do |result, *|
+ text = File.read(file)
+ text.scan(regexp){
+ # check.delete $1
+ result << $2
+ ''
+ }
+ # raise check.inspect unless check.empty?
+ end
+ end
+
+ add_proc_meminfo '/proc/meminfo', %w(MemTotal MemFree)
+ add_proc_meminfo '/proc/self/status', %w(VmPeak VmSize VmHWM VmRSS)
+
+ if FileTest.exist?('/proc/self/statm')
+ add *%w(size resident share text lib data dt) do |result, *|
+ result.concat File.read('/proc/self/statm').split(/\s+/)
+ end
+ end
+
+ def memprofile_test_all_result_result
+ result = ["#{self.class}\##{self.__name__.to_s.gsub(/\s+/, '')}"]
+ TEST_ALL_PROFILE_PROCS.each{|proc|
+ proc.call(result, self)
+ }
+ result.join("\t")
+ end
+
+ def run runner
+ result = orig_run(runner)
+ TEST_ALL_PROFILE_OUT.puts memprofile_test_all_result_result
+ TEST_ALL_PROFILE_OUT.flush
+ result
+ end
+
+ TEST_ALL_PROFILE_OUT.puts TEST_ALL_PROFILE_BANNER.join("\t")
+end
diff --git a/jni/ruby/test/lib/test/unit.rb b/jni/ruby/test/lib/test/unit.rb
new file mode 100644
index 0000000..629f3e7
--- /dev/null
+++ b/jni/ruby/test/lib/test/unit.rb
@@ -0,0 +1,896 @@
+begin
+ gem 'minitest', '< 5.0.0' if defined? Gem
+rescue Gem::LoadError
+end
+require 'minitest/unit'
+require 'test/unit/assertions'
+require_relative '../envutil'
+require 'test/unit/testcase'
+require 'optparse'
+
+# See Test::Unit
+module Test
+ ##
+ # Test::Unit is an implementation of the xUnit testing framework for Ruby.
+ #
+ # If you are writing new test code, please use MiniTest instead of Test::Unit.
+ #
+ # Test::Unit has been left in the standard library to support legacy test
+ # suites.
+ module Unit
+ TEST_UNIT_IMPLEMENTATION = 'test/unit compatibility layer using minitest' # :nodoc:
+
+ module RunCount # :nodoc: all
+ @@run_count = 0
+
+ def self.have_run?
+ @@run_count.nonzero?
+ end
+
+ def run(*)
+ @@run_count += 1
+ super
+ end
+
+ def run_once
+ return if have_run?
+ return if $! # don't run if there was an exception
+ yield
+ end
+ module_function :run_once
+ end
+
+ module Options # :nodoc: all
+ def initialize(*, &block)
+ @init_hook = block
+ @options = nil
+ super(&nil)
+ end
+
+ def option_parser
+ @option_parser ||= OptionParser.new
+ end
+
+ def process_args(args = [])
+ return @options if @options
+ orig_args = args.dup
+ options = {}
+ opts = option_parser
+ setup_options(opts, options)
+ opts.parse!(args)
+ orig_args -= args
+ args = @init_hook.call(args, options) if @init_hook
+ non_options(args, options)
+ @help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
+ @options = options
+ if @options[:parallel]
+ @files = args
+ @args = orig_args
+ end
+ options
+ end
+
+ private
+ def setup_options(opts, options)
+ opts.separator 'minitest options:'
+ opts.version = MiniTest::Unit::VERSION
+
+ options[:retry] = true
+ options[:job_status] = nil
+ options[:hide_skip] = true
+
+ opts.on '-h', '--help', 'Display this help.' do
+ puts opts
+ exit
+ end
+
+ opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
+ options[:seed] = m
+ end
+
+ opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
+ options[:verbose] = true
+ self.verbose = options[:verbose]
+ end
+
+ opts.on '-n', '--name PATTERN', "Filter test names on pattern." do |a|
+ options[:filter] = a
+ end
+
+ opts.on '--jobs-status [TYPE]', [:normal, :replace],
+ "Show status of jobs every file; Disabled when --jobs isn't specified." do |type|
+ options[:job_status] = type || :normal
+ end
+
+ opts.on '-j N', '--jobs N', "Allow run tests with N jobs at once" do |a|
+ if /^t/ =~ a
+ options[:testing] = true # For testing
+ options[:parallel] = a[1..-1].to_i
+ else
+ options[:parallel] = a.to_i
+ end
+ end
+
+ opts.on '--separate', "Restart job process after one testcase has done" do
+ options[:parallel] ||= 1
+ options[:separate] = true
+ end
+
+ opts.on '--retry', "Retry running testcase when --jobs specified" do
+ options[:retry] = true
+ end
+
+ opts.on '--no-retry', "Disable --retry" do
+ options[:retry] = false
+ end
+
+ opts.on '--ruby VAL', "Path to ruby; It'll have used at -j option" do |a|
+ options[:ruby] = a.split(/ /).reject(&:empty?)
+ end
+
+ opts.on '-q', '--hide-skip', 'Hide skipped tests' do
+ options[:hide_skip] = true
+ end
+
+ opts.on '--show-skip', 'Show skipped tests' do
+ options[:hide_skip] = false
+ end
+
+ opts.on '--color[=WHEN]',
+ [:always, :never, :auto],
+ "colorize the output. WHEN defaults to 'always'", "or can be 'never' or 'auto'." do |c|
+ options[:color] = c || :always
+ end
+
+ opts.on '--tty[=WHEN]',
+ [:yes, :no],
+ "force to output tty control. WHEN defaults to 'yes'", "or can be 'no'." do |c|
+ @tty = c != :no
+ end
+ end
+
+ def non_options(files, options)
+ begin
+ require "rbconfig"
+ rescue LoadError
+ warn "#{caller(1)[0]}: warning: Parallel running disabled because can't get path to ruby; run specify with --ruby argument"
+ options[:parallel] = nil
+ else
+ options[:ruby] ||= [RbConfig.ruby]
+ end
+
+ true
+ end
+ end
+
+ module GlobOption # :nodoc: all
+ @@testfile_prefix = "test"
+
+ def setup_options(parser, options)
+ super
+ parser.on '-b', '--basedir=DIR', 'Base directory of test suites.' do |dir|
+ options[:base_directory] = dir
+ end
+ parser.on '-x', '--exclude PATTERN', 'Exclude test files on pattern.' do |pattern|
+ (options[:reject] ||= []) << pattern
+ end
+ end
+
+ def non_options(files, options)
+ paths = [options.delete(:base_directory), nil].uniq
+ if reject = options.delete(:reject)
+ reject_pat = Regexp.union(reject.map {|r| /#{r}/ })
+ end
+ files.map! {|f|
+ f = f.tr(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
+ ((paths if /\A\.\.?(?:\z|\/)/ !~ f) || [nil]).any? do |prefix|
+ if prefix
+ path = f.empty? ? prefix : "#{prefix}/#{f}"
+ else
+ next if f.empty?
+ path = f
+ end
+ if !(match = Dir["#{path}/**/#{@@testfile_prefix}_*.rb"]).empty?
+ if reject
+ match.reject! {|n|
+ n[(prefix.length+1)..-1] if prefix
+ reject_pat =~ n
+ }
+ end
+ break match
+ elsif !reject or reject_pat !~ f and File.exist? path
+ break path
+ end
+ end or
+ raise ArgumentError, "file not found: #{f}"
+ }
+ files.flatten!
+ super(files, options)
+ end
+ end
+
+ module LoadPathOption # :nodoc: all
+ def setup_options(parser, options)
+ super
+ parser.on '-Idirectory', 'Add library load path' do |dirs|
+ dirs.split(':').each { |d| $LOAD_PATH.unshift d }
+ end
+ end
+ end
+
+ module GCStressOption # :nodoc: all
+ def setup_options(parser, options)
+ super
+ parser.on '--[no-]gc-stress', 'Set GC.stress as true' do |flag|
+ options[:gc_stress] = flag
+ end
+ end
+
+ def non_options(files, options)
+ if options.delete(:gc_stress)
+ MiniTest::Unit::TestCase.class_eval do
+ oldrun = instance_method(:run)
+ define_method(:run) do |runner|
+ begin
+ gc_stress, GC.stress = GC.stress, true
+ oldrun.bind(self).call(runner)
+ ensure
+ GC.stress = gc_stress
+ end
+ end
+ end
+ end
+ super
+ end
+ end
+
+ module RequireFiles # :nodoc: all
+ def non_options(files, options)
+ return false if !super
+ errors = {}
+ result = false
+ files.each {|f|
+ d = File.dirname(path = File.realpath(f))
+ unless $:.include? d
+ $: << d
+ end
+ begin
+ require path unless options[:parallel]
+ result = true
+ rescue LoadError
+ next if errors[$!.message]
+ errors[$!.message] = true
+ puts "#{f}: #{$!}"
+ end
+ }
+ result
+ end
+ end
+
+ class Runner < MiniTest::Unit # :nodoc: all
+ include Test::Unit::Options
+ include Test::Unit::GlobOption
+ include Test::Unit::LoadPathOption
+ include Test::Unit::GCStressOption
+ include Test::Unit::RunCount
+
+ class Worker
+ def self.launch(ruby,args=[])
+ io = IO.popen([*ruby,
+ "#{File.dirname(__FILE__)}/unit/parallel.rb",
+ *args], "rb+")
+ new(io, io.pid, :waiting)
+ end
+
+ attr_reader :quit_called
+
+ def initialize(io, pid, status)
+ @io = io
+ @pid = pid
+ @status = status
+ @file = nil
+ @real_file = nil
+ @loadpath = []
+ @hooks = {}
+ @quit_called = false
+ end
+
+ def puts(*args)
+ @io.puts(*args)
+ end
+
+ def run(task,type)
+ @file = File.basename(task, ".rb")
+ @real_file = task
+ begin
+ puts "loadpath #{[Marshal.dump($:-@loadpath)].pack("m0")}"
+ @loadpath = $:.dup
+ puts "run #{task} #{type}"
+ @status = :prepare
+ rescue Errno::EPIPE
+ died
+ rescue IOError
+ raise unless ["stream closed","closed stream"].include? $!.message
+ died
+ end
+ end
+
+ def hook(id,&block)
+ @hooks[id] ||= []
+ @hooks[id] << block
+ self
+ end
+
+ def read
+ res = (@status == :quit) ? @io.read : @io.gets
+ res && res.chomp
+ end
+
+ def close
+ @io.close unless @io.closed?
+ self
+ rescue IOError
+ end
+
+ def quit
+ return if @io.closed?
+ @quit_called = true
+ @io.puts "quit"
+ @io.close
+ end
+
+ def kill
+ Process.kill(:KILL, @pid)
+ rescue Errno::ESRCH
+ end
+
+ def died(*additional)
+ @status = :quit
+ @io.close
+
+ call_hook(:dead,*additional)
+ end
+
+ def to_s
+ if @file
+ "#{@pid}=#{@file}"
+ else
+ "#{@pid}:#{@status.to_s.ljust(7)}"
+ end
+ end
+
+ attr_reader :io, :pid
+ attr_accessor :status, :file, :real_file, :loadpath
+
+ private
+
+ def call_hook(id,*additional)
+ @hooks[id] ||= []
+ @hooks[id].each{|hook| hook[self,additional] }
+ self
+ end
+
+ end
+
+ class << self; undef autorun; end
+
+ @@stop_auto_run = false
+ def self.autorun
+ at_exit {
+ Test::Unit::RunCount.run_once {
+ exit(Test::Unit::Runner.new.run(ARGV) || true)
+ } unless @@stop_auto_run
+ } unless @@installed_at_exit
+ @@installed_at_exit = true
+ end
+
+ def after_worker_down(worker, e=nil, c=false)
+ return unless @options[:parallel]
+ return if @interrupt
+ warn e if e
+ @need_quit = true
+ warn ""
+ warn "Some worker was crashed. It seems ruby interpreter's bug"
+ warn "or, a bug of test/unit/parallel.rb. try again without -j"
+ warn "option."
+ warn ""
+ STDERR.flush
+ exit c
+ end
+
+ def terminal_width
+ unless @terminal_width ||= nil
+ begin
+ require 'io/console'
+ width = $stdout.winsize[1]
+ rescue LoadError, NoMethodError, Errno::ENOTTY, Errno::EBADF, Errno::EINVAL
+ width = ENV["COLUMNS"].to_i.nonzero? || 80
+ end
+ width -= 1 if /mswin|mingw/ =~ RUBY_PLATFORM
+ @terminal_width = width
+ end
+ @terminal_width
+ end
+
+ def del_status_line
+ @status_line_size ||= 0
+ unless @options[:job_status] == :replace
+ $stdout.puts
+ return
+ end
+ print "\r"+" "*@status_line_size+"\r"
+ $stdout.flush
+ @status_line_size = 0
+ end
+
+ def put_status(line)
+ unless @options[:job_status] == :replace
+ print(line)
+ return
+ end
+ @status_line_size ||= 0
+ del_status_line
+ $stdout.flush
+ line = line[0...terminal_width]
+ print line
+ $stdout.flush
+ @status_line_size = line.size
+ end
+
+ def add_status(line)
+ unless @options[:job_status] == :replace
+ print(line)
+ return
+ end
+ @status_line_size ||= 0
+ line = line[0...(terminal_width-@status_line_size)]
+ print line
+ $stdout.flush
+ @status_line_size += line.size
+ end
+
+ def jobs_status
+ return unless @options[:job_status]
+ puts "" unless @options[:verbose] or @options[:job_status] == :replace
+ status_line = @workers.map(&:to_s).join(" ")
+ update_status(status_line) or (puts; nil)
+ end
+
+ def del_jobs_status
+ return unless @options[:job_status] == :replace && @status_line_size.nonzero?
+ del_status_line
+ end
+
+ def after_worker_quit(worker)
+ return unless @options[:parallel]
+ return if @interrupt
+ @workers.delete(worker)
+ @dead_workers << worker
+ @ios = @workers.map(&:io)
+ end
+
+ def launch_worker
+ begin
+ worker = Worker.launch(@options[:ruby],@args)
+ rescue => e
+ abort "ERROR: Failed to launch job process - #{e.class}: #{e.message}"
+ end
+ worker.hook(:dead) do |w,info|
+ after_worker_quit w
+ after_worker_down w, *info if !info.empty? && !worker.quit_called
+ end
+ @workers << worker
+ @ios << worker.io
+ @workers_hash[worker.io] = worker
+ worker
+ end
+
+ def delete_worker(worker)
+ @workers_hash.delete worker.io
+ @workers.delete worker
+ @ios.delete worker.io
+ end
+
+ def quit_workers
+ return if @workers.empty?
+ @workers.reject! do |worker|
+ begin
+ timeout(1) do
+ worker.quit
+ end
+ rescue Errno::EPIPE
+ rescue Timeout::Error
+ end
+ worker.close
+ end
+
+ return if @workers.empty?
+ begin
+ timeout(0.2 * @workers.size) do
+ Process.waitall
+ end
+ rescue Timeout::Error
+ @workers.each do |worker|
+ worker.kill
+ end
+ @worker.clear
+ end
+ end
+
+ def start_watchdog
+ Thread.new do
+ while stat = Process.wait2
+ break if @interrupt # Break when interrupt
+ pid, stat = stat
+ w = (@workers + @dead_workers).find{|x| pid == x.pid }
+ next unless w
+ w = w.dup
+ if w.status != :quit && !w.quit_called?
+ # Worker down
+ w.died(nil, !stat.signaled? && stat.exitstatus)
+ end
+ end
+ end
+ end
+
+ def deal(io, type, result, rep, shutting_down = false)
+ worker = @workers_hash[io]
+ cmd = worker.read
+ cmd.sub!(/\A\.+/, '')
+ case cmd
+ when ''
+ # just only dots, ignore
+ when /^okay$/
+ worker.status = :running
+ jobs_status
+ when /^ready(!)?$/
+ bang = $1
+ worker.status = :ready
+
+ return nil unless task = @tasks.shift
+ if @options[:separate] and not bang
+ worker.quit
+ worker = add_worker
+ end
+ worker.run(task, type)
+ @test_count += 1
+
+ jobs_status
+ when /^done (.+?)$/
+ begin
+ r = Marshal.load($1.unpack("m")[0])
+ rescue
+ print "unknown object: #{$1.unpack("m")[0].dump}"
+ return true
+ end
+ result << r[0..1] unless r[0..1] == [nil,nil]
+ rep << {file: worker.real_file, report: r[2], result: r[3], testcase: r[5]}
+ $:.push(*r[4]).uniq!
+ return true
+ when /^p (.+?)$/
+ del_jobs_status
+ print $1.unpack("m")[0]
+ jobs_status if @options[:job_status] == :replace
+ when /^after (.+?)$/
+ @warnings << Marshal.load($1.unpack("m")[0])
+ when /^bye (.+?)$/
+ after_worker_down worker, Marshal.load($1.unpack("m")[0])
+ when /^bye$/, nil
+ if shutting_down || worker.quit_called
+ after_worker_quit worker
+ else
+ after_worker_down worker
+ end
+ else
+ print "unknown command: #{cmd.dump}\n"
+ end
+ return false
+ end
+
+ def _run_parallel suites, type, result
+ if @options[:parallel] < 1
+ warn "Error: parameter of -j option should be greater than 0."
+ return
+ end
+
+ # Require needed things for parallel running
+ require 'thread'
+ require 'timeout'
+ @tasks = @files.dup # Array of filenames.
+ @need_quit = false
+ @dead_workers = [] # Array of dead workers.
+ @warnings = []
+ @total_tests = @tasks.size.to_s(10)
+ rep = [] # FIXME: more good naming
+
+ @workers = [] # Array of workers.
+ @workers_hash = {} # out-IO => worker
+ @ios = [] # Array of worker IOs
+ begin
+ # Thread: watchdog
+ watchdog = start_watchdog
+
+ @options[:parallel].times {launch_worker}
+
+ while _io = IO.select(@ios)[0]
+ break if _io.any? do |io|
+ @need_quit or
+ (deal(io, type, result, rep).nil? and
+ !@workers.any? {|x| [:running, :prepare].include? x.status})
+ end
+ end
+ rescue Interrupt => ex
+ @interrupt = ex
+ return result
+ ensure
+ watchdog.kill if watchdog
+ if @interrupt
+ @ios.select!{|x| @workers_hash[x].status == :running }
+ while !@ios.empty? && (__io = IO.select(@ios,[],[],10))
+ __io[0].reject! {|io| deal(io, type, result, rep, true)}
+ end
+ end
+
+ quit_workers
+
+ unless @interrupt || !@options[:retry] || @need_quit
+ @options[:parallel] = false
+ suites, rep = rep.partition {|r| r[:testcase] && r[:file] && r[:report].any? {|e| !e[2].is_a?(MiniTest::Skip)}}
+ suites.map {|r| r[:file]}.uniq.each {|file| require file}
+ suites.map! {|r| eval("::"+r[:testcase])}
+ del_status_line or puts
+ unless suites.empty?
+ puts "Retrying..."
+ _run_suites(suites, type)
+ end
+ end
+ unless @options[:retry]
+ del_status_line or puts
+ end
+ unless rep.empty?
+ rep.each do |r|
+ r[:report].each do |f|
+ puke(*f) if f
+ end
+ end
+ if @options[:retry]
+ @errors += rep.map{|x| x[:result][0] }.inject(:+)
+ @failures += rep.map{|x| x[:result][1] }.inject(:+)
+ @skips += rep.map{|x| x[:result][2] }.inject(:+)
+ end
+ end
+ unless @warnings.empty?
+ warn ""
+ @warnings.uniq! {|w| w[1].message}
+ @warnings.each do |w|
+ warn "#{w[0]}: #{w[1].message} (#{w[1].class})"
+ end
+ warn ""
+ end
+ end
+ end
+
+ def _run_suites suites, type
+ _prepare_run(suites, type)
+ @interrupt = nil
+ result = []
+ GC.start
+ if @options[:parallel]
+ _run_parallel suites, type, result
+ else
+ suites.each {|suite|
+ begin
+ result << _run_suite(suite, type)
+ rescue Interrupt => e
+ @interrupt = e
+ break
+ end
+ }
+ end
+ report.reject!{|r| r.start_with? "Skipped:" } if @options[:hide_skip]
+ report.sort_by!{|r| r.start_with?("Skipped:") ? 0 : \
+ (r.start_with?("Failure:") ? 1 : 2) }
+ result
+ end
+
+ alias mini_run_suite _run_suite
+
+ def output
+ (@output ||= nil) || super
+ end
+
+ def _prepare_run(suites, type)
+ options[:job_status] ||= :replace if @tty && !@verbose
+ case options[:color]
+ when :always
+ color = true
+ when :auto, nil
+ color = @options[:job_status] == :replace && /dumb/ !~ ENV["TERM"]
+ else
+ color = false
+ end
+ if color
+ # dircolors-like style
+ colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:]*)/)] : {}
+ @passed_color = "\e[#{colors["pass"] || "32"}m"
+ @failed_color = "\e[#{colors["fail"] || "31"}m"
+ @skipped_color = "\e[#{colors["skip"] || "33"}m"
+ @reset_color = "\e[m"
+ else
+ @passed_color = @failed_color = @skipped_color = @reset_color = ""
+ end
+ if color or @options[:job_status] == :replace
+ @verbose = !options[:parallel]
+ @output = StatusLineOutput.new(self)
+ end
+ if /\A\/(.*)\/\z/ =~ (filter = options[:filter])
+ filter = Regexp.new($1)
+ end
+ type = "#{type}_methods"
+ total = if filter
+ suites.inject(0) {|n, suite| n + suite.send(type).grep(filter).size}
+ else
+ suites.inject(0) {|n, suite| n + suite.send(type).size}
+ end
+ @test_count = 0
+ @total_tests = total.to_s(10)
+ end
+
+ def new_test(s)
+ @test_count += 1
+ update_status(s)
+ end
+
+ def update_status(s)
+ count = @test_count.to_s(10).rjust(@total_tests.size)
+ put_status("#{@passed_color}[#{count}/#{@total_tests}]#{@reset_color} #{s}")
+ end
+
+ def _print(s); $stdout.print(s); end
+ def succeed; del_status_line; end
+
+ def failed(s)
+ sep = "\n"
+ @report_count ||= 0
+ report.each do |msg|
+ if msg.start_with? "Skipped:"
+ if @options[:hide_skip]
+ del_status_line
+ next
+ end
+ color = @skipped_color
+ else
+ color = @failed_color
+ end
+ msg = msg.split(/$/, 2)
+ $stdout.printf("%s%s%3d) %s%s%s\n",
+ sep, color, @report_count += 1,
+ msg[0], @reset_color, msg[1])
+ sep = nil
+ end
+ report.clear
+ end
+
+ # Overriding of MiniTest::Unit#puke
+ def puke klass, meth, e
+ # TODO:
+ # this overriding is for minitest feature that skip messages are
+ # hidden when not verbose (-v), note this is temporally.
+ n = report.size
+ rep = super
+ if MiniTest::Skip === e and /no message given\z/ =~ e.message
+ report.slice!(n..-1)
+ rep = "."
+ end
+ rep
+ end
+
+ def initialize
+ super
+ @tty = $stdout.tty?
+ end
+
+ def status(*args)
+ result = super
+ raise @interrupt if @interrupt
+ result
+ end
+
+ def run(*args)
+ result = super
+ puts "\nruby -v: #{RUBY_DESCRIPTION}"
+ result
+ end
+ end
+
+ class StatusLineOutput < Struct.new(:runner) # :nodoc: all
+ def puts(*a) $stdout.puts(*a) unless a.empty? end
+ def respond_to_missing?(*a) $stdout.respond_to?(*a) end
+ def method_missing(*a, &b) $stdout.__send__(*a, &b) end
+
+ def print(s)
+ case s
+ when /\A(.*\#.*) = \z/
+ runner.new_test($1)
+ when /\A(.* s) = \z/
+ runner.add_status(" = "+$1.chomp)
+ when /\A\.+\z/
+ runner.succeed
+ when /\A[EFS]\z/
+ runner.failed(s)
+ else
+ $stdout.print(s)
+ end
+ end
+ end
+
+ class AutoRunner # :nodoc: all
+ class Runner < Test::Unit::Runner
+ include Test::Unit::RequireFiles
+ end
+
+ attr_accessor :to_run, :options
+
+ def initialize(force_standalone = false, default_dir = nil, argv = ARGV)
+ @force_standalone = force_standalone
+ @runner = Runner.new do |files, options|
+ options[:base_directory] ||= default_dir
+ files << default_dir if files.empty? and default_dir
+ @to_run = files
+ yield self if block_given?
+ files
+ end
+ Runner.runner = @runner
+ @options = @runner.option_parser
+ if @force_standalone
+ @options.banner.sub!(/\[options\]/, '\& tests...')
+ end
+ @argv = argv
+ end
+
+ def process_args(*args)
+ @runner.process_args(*args)
+ !@to_run.empty?
+ end
+
+ def run
+ if @force_standalone and not process_args(@argv)
+ abort @options.banner
+ end
+ @runner.run(@argv) || true
+ end
+
+ def self.run(*args)
+ new(*args).run
+ end
+ end
+
+ class ProxyError < StandardError # :nodoc: all
+ def initialize(ex)
+ @message = ex.message
+ @backtrace = ex.backtrace
+ end
+
+ attr_accessor :message, :backtrace
+ end
+ end
+end
+
+module MiniTest # :nodoc: all
+ class Unit
+ end
+end
+
+class MiniTest::Unit::TestCase # :nodoc: all
+ undef run_test
+ RUN_TEST_TRACE = "#{__FILE__}:#{__LINE__+3}:in `run_test'".freeze
+ def run_test(name)
+ progname, $0 = $0, "#{$0}: #{self.class}##{name}"
+ self.__send__(name)
+ ensure
+ $@.delete(RUN_TEST_TRACE) if $@
+ $0 = progname
+ end
+end
+
+Test::Unit::Runner.autorun
diff --git a/jni/ruby/test/lib/test/unit/assertions.rb b/jni/ruby/test/lib/test/unit/assertions.rb
new file mode 100644
index 0000000..727c54c
--- /dev/null
+++ b/jni/ruby/test/lib/test/unit/assertions.rb
@@ -0,0 +1,465 @@
+require 'minitest/unit'
+require 'pp'
+
+module Test
+ module Unit
+ module Assertions
+ include MiniTest::Assertions
+
+ def mu_pp(obj) #:nodoc:
+ obj.pretty_inspect.chomp
+ end
+
+ MINI_DIR = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), "minitest") #:nodoc:
+
+ # :call-seq:
+ # assert(test, [failure_message])
+ #
+ #Tests if +test+ is true.
+ #
+ #+msg+ may be a String or a Proc. If +msg+ is a String, it will be used
+ #as the failure message. Otherwise, the result of calling +msg+ will be
+ #used as the message if the assertion fails.
+ #
+ #If no +msg+ is given, a default message will be used.
+ #
+ # assert(false, "This was expected to be true")
+ def assert(test, *msgs)
+ case msg = msgs.first
+ when String, Proc
+ when nil
+ msgs.shift
+ else
+ bt = caller.reject { |s| s.start_with?(MINI_DIR) }
+ raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt
+ end unless msgs.empty?
+ super
+ end
+
+ # :call-seq:
+ # assert_block( failure_message = nil )
+ #
+ #Tests the result of the given block. If the block does not return true,
+ #the assertion will fail. The optional +failure_message+ argument is the same as in
+ #Assertions#assert.
+ #
+ # assert_block do
+ # [1, 2, 3].any? { |num| num < 1 }
+ # end
+ def assert_block(*msgs)
+ assert yield, *msgs
+ end
+
+ # :call-seq:
+ # assert_raise( *args, &block )
+ #
+ #Tests if the given block raises an exception. Acceptable exception
+ #types may be given as optional arguments. If the last argument is a
+ #String, it will be used as the error message.
+ #
+ # assert_raise do #Fails, no Exceptions are raised
+ # end
+ #
+ # assert_raise NameError do
+ # puts x #Raises NameError, so assertion succeeds
+ # end
+ def assert_raise(*exp, &b)
+ case exp.last
+ when String, Proc
+ msg = exp.pop
+ end
+
+ begin
+ yield
+ rescue MiniTest::Skip => e
+ return e if exp.include? MiniTest::Skip
+ raise e
+ rescue Exception => e
+ expected = exp.any? { |ex|
+ if ex.instance_of? Module then
+ e.kind_of? ex
+ else
+ e.instance_of? ex
+ end
+ }
+
+ assert expected, proc {
+ exception_details(e, message(msg) {"#{mu_pp(exp)} exception expected, not"}.call)
+ }
+
+ return e
+ end
+
+ exp = exp.first if exp.size == 1
+
+ flunk(message(msg) {"#{mu_pp(exp)} expected but nothing was raised"})
+ end
+
+ # :call-seq:
+ # assert_raise_with_message(exception, expected, msg = nil, &block)
+ #
+ #Tests if the given block raises an exception with the expected
+ #message.
+ #
+ # assert_raise_with_message(RuntimeError, "foo") do
+ # nil #Fails, no Exceptions are raised
+ # end
+ #
+ # assert_raise_with_message(RuntimeError, "foo") do
+ # raise ArgumentError, "foo" #Fails, different Exception is raised
+ # end
+ #
+ # assert_raise_with_message(RuntimeError, "foo") do
+ # raise "bar" #Fails, RuntimeError is raised but the message differs
+ # end
+ #
+ # assert_raise_with_message(RuntimeError, "foo") do
+ # raise "foo" #Raises RuntimeError with the message, so assertion succeeds
+ # end
+ def assert_raise_with_message(exception, expected, msg = nil, &block)
+ case expected
+ when String
+ assert = :assert_equal
+ when Regexp
+ assert = :assert_match
+ else
+ raise TypeError, "Expected #{expected.inspect} to be a kind of String or Regexp, not #{expected.class}"
+ end
+
+ ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) {yield}
+ msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"}
+
+ if assert == :assert_equal
+ assert_equal(expected, ex.message, msg)
+ else
+ msg = message(msg) { "Expected #{mu_pp expected} to match #{mu_pp ex.message}" }
+ assert expected =~ ex.message, msg
+ block.binding.eval("proc{|_|$~=_}").call($~)
+ end
+ ex
+ end
+
+ # :call-seq:
+ # assert_nothing_raised( *args, &block )
+ #
+ #If any exceptions are given as arguments, the assertion will
+ #fail if one of those exceptions are raised. Otherwise, the test fails
+ #if any exceptions are raised.
+ #
+ #The final argument may be a failure message.
+ #
+ # assert_nothing_raised RuntimeError do
+ # raise Exception #Assertion passes, Exception is not a RuntimeError
+ # end
+ #
+ # assert_nothing_raised do
+ # raise Exception #Assertion fails
+ # end
+ def assert_nothing_raised(*args)
+ self._assertions += 1
+ if Module === args.last
+ msg = nil
+ else
+ msg = args.pop
+ end
+ begin
+ line = __LINE__; yield
+ rescue MiniTest::Skip
+ raise
+ rescue Exception => e
+ bt = e.backtrace
+ as = e.instance_of?(MiniTest::Assertion)
+ if as
+ ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o
+ bt.reject! {|ln| ans =~ ln}
+ end
+ if ((args.empty? && !as) ||
+ args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a })
+ msg = message(msg) { "Exception raised:\n<#{mu_pp(e)}>" }
+ raise MiniTest::Assertion, msg.call, bt
+ else
+ raise
+ end
+ end
+ nil
+ end
+
+ # :call-seq:
+ # assert_nothing_thrown( failure_message = nil, &block )
+ #
+ #Fails if the given block uses a call to Kernel#throw, and
+ #returns the result of the block otherwise.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # assert_nothing_thrown "Something was thrown!" do
+ # throw :problem?
+ # end
+ def assert_nothing_thrown(msg=nil)
+ begin
+ ret = yield
+ rescue ArgumentError => error
+ raise error if /\Auncaught throw (.+)\z/m !~ error.message
+ msg = message(msg) { "<#{$1}> was thrown when nothing was expected" }
+ flunk(msg)
+ end
+ assert(true, "Expected nothing to be thrown")
+ ret
+ end
+
+ # :call-seq:
+ # assert_throw( tag, failure_message = nil, &block )
+ #
+ #Fails unless the given block throws +tag+, returns the caught
+ #value otherwise.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # tag = Object.new
+ # assert_throw(tag, "#{tag} was not thrown!") do
+ # throw tag
+ # end
+ def assert_throw(tag, msg = nil)
+ ret = catch(tag) do
+ begin
+ yield(tag)
+ rescue UncaughtThrowError => e
+ thrown = e.tag
+ end
+ msg = message(msg) {
+ "Expected #{mu_pp(tag)} to have been thrown"\
+ "#{%Q[, not #{thrown}] if thrown}"
+ }
+ assert(false, msg)
+ end
+ assert(true)
+ ret
+ end
+
+ # :call-seq:
+ # assert_equal( expected, actual, failure_message = nil )
+ #
+ #Tests if +expected+ is equal to +actual+.
+ #
+ #An optional failure message may be provided as the final argument.
+ def assert_equal(exp, act, msg = nil)
+ msg = message(msg) {
+ exp_str = mu_pp(exp)
+ act_str = mu_pp(act)
+ exp_comment = ''
+ act_comment = ''
+ if exp_str == act_str
+ if (exp.is_a?(String) && act.is_a?(String)) ||
+ (exp.is_a?(Regexp) && act.is_a?(Regexp))
+ exp_comment = " (#{exp.encoding})"
+ act_comment = " (#{act.encoding})"
+ elsif exp.is_a?(Float) && act.is_a?(Float)
+ exp_str = "%\#.#{Float::DIG+2}g" % exp
+ act_str = "%\#.#{Float::DIG+2}g" % act
+ elsif exp.is_a?(Time) && act.is_a?(Time)
+ if exp.subsec * 1000_000_000 == exp.nsec
+ exp_comment = " (#{exp.nsec}[ns])"
+ else
+ exp_comment = " (subsec=#{exp.subsec})"
+ end
+ if act.subsec * 1000_000_000 == act.nsec
+ act_comment = " (#{act.nsec}[ns])"
+ else
+ act_comment = " (subsec=#{act.subsec})"
+ end
+ elsif exp.class != act.class
+ # a subclass of Range, for example.
+ exp_comment = " (#{exp.class})"
+ act_comment = " (#{act.class})"
+ end
+ elsif !Encoding.compatible?(exp_str, act_str)
+ if exp.is_a?(String) && act.is_a?(String)
+ exp_str = exp.dump
+ act_str = act.dump
+ exp_comment = " (#{exp.encoding})"
+ act_comment = " (#{act.encoding})"
+ else
+ exp_str = exp_str.dump
+ act_str = act_str.dump
+ end
+ end
+ "<#{exp_str}>#{exp_comment} expected but was\n<#{act_str}>#{act_comment}"
+ }
+ assert(exp == act, msg)
+ end
+
+ # :call-seq:
+ # assert_not_nil( expression, failure_message = nil )
+ #
+ #Tests if +expression+ is not nil.
+ #
+ #An optional failure message may be provided as the final argument.
+ def assert_not_nil(exp, msg=nil)
+ msg = message(msg) { "<#{mu_pp(exp)}> expected to not be nil" }
+ assert(!exp.nil?, msg)
+ end
+
+ # :call-seq:
+ # assert_not_equal( expected, actual, failure_message = nil )
+ #
+ #Tests if +expected+ is not equal to +actual+.
+ #
+ #An optional failure message may be provided as the final argument.
+ def assert_not_equal(exp, act, msg=nil)
+ msg = message(msg) { "<#{mu_pp(exp)}> expected to be != to\n<#{mu_pp(act)}>" }
+ assert(exp != act, msg)
+ end
+
+ # :call-seq:
+ # assert_no_match( regexp, string, failure_message = nil )
+ #
+ #Tests if the given Regexp does not match a given String.
+ #
+ #An optional failure message may be provided as the final argument.
+ def assert_no_match(regexp, string, msg=nil)
+ assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
+ self._assertions -= 1
+ msg = message(msg) { "<#{mu_pp(regexp)}> expected to not match\n<#{mu_pp(string)}>" }
+ assert(regexp !~ string, msg)
+ end
+
+ # :call-seq:
+ # assert_not_same( expected, actual, failure_message = nil )
+ #
+ #Tests if +expected+ is not the same object as +actual+.
+ #This test uses Object#equal? to test equality.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # assert_not_same("x", "x") #Succeeds
+ def assert_not_same(expected, actual, message="")
+ msg = message(msg) { build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__) }
+<?>
+with id <?> expected to not be equal\\? to
+<?>
+with id <?>.
+EOT
+ assert(!actual.equal?(expected), msg)
+ end
+
+ # :call-seq:
+ # assert_respond_to( object, method, failure_message = nil )
+ #
+ #Tests if the given Object responds to +method+.
+ #
+ #An optional failure message may be provided as the final argument.
+ #
+ # assert_respond_to("hello", :reverse) #Succeeds
+ # assert_respond_to("hello", :does_not_exist) #Fails
+ def assert_respond_to obj, (meth, priv), msg = nil
+ if priv
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv}"
+ }
+ return assert obj.respond_to?(meth, priv), msg
+ end
+ #get rid of overcounting
+ super if !caller[0].rindex(MINI_DIR, 0) || !obj.respond_to?(meth)
+ end
+
+ # :call-seq:
+ # assert_send( +send_array+, failure_message = nil )
+ #
+ # Passes if the method send returns a true value.
+ #
+ # +send_array+ is composed of:
+ # * A receiver
+ # * A method
+ # * Arguments to the method
+ #
+ # Example:
+ # assert_send(["Hello world", :include?, "Hello"]) # -> pass
+ # assert_send(["Hello world", :include?, "Goodbye"]) # -> fail
+ def assert_send send_ary, m = nil
+ recv, msg, *args = send_ary
+ m = message(m) {
+ if args.empty?
+ argsstr = ""
+ else
+ (argsstr = mu_pp(args)).sub!(/\A\[(.*)\]\z/m, '(\1)')
+ end
+ "Expected #{mu_pp(recv)}.#{msg}#{argsstr} to return true"
+ }
+ assert recv.__send__(msg, *args), m
+ end
+
+ # :call-seq:
+ # assert_not_send( +send_array+, failure_message = nil )
+ #
+ # Passes if the method send doesn't return a true value.
+ #
+ # +send_array+ is composed of:
+ # * A receiver
+ # * A method
+ # * Arguments to the method
+ #
+ # Example:
+ # assert_not_send([[1, 2], :member?, 1]) # -> fail
+ # assert_not_send([[1, 2], :member?, 4]) # -> pass
+ def assert_not_send send_ary, m = nil
+ recv, msg, *args = send_ary
+ m = message(m) {
+ if args.empty?
+ argsstr = ""
+ else
+ (argsstr = mu_pp(args)).sub!(/\A\[(.*)\]\z/m, '(\1)')
+ end
+ "Expected #{mu_pp(recv)}.#{msg}#{argsstr} to return false"
+ }
+ assert !recv.__send__(msg, *args), m
+ end
+
+ ms = instance_methods(true).map {|sym| sym.to_s }
+ ms.grep(/\Arefute_/) do |m|
+ mname = ('assert_not_' << m.to_s[/.*?_(.*)/, 1])
+ alias_method(mname, m) unless ms.include? mname
+ end
+ alias assert_include assert_includes
+ alias assert_not_include assert_not_includes
+
+ def assert_all?(obj, m = nil, &blk)
+ failed = []
+ obj.each do |*a, &b|
+ unless blk.call(*a, &b)
+ failed << (a.size > 1 ? a : a[0])
+ end
+ end
+ assert(failed.empty?, message(m) {failed.pretty_inspect})
+ end
+
+ def assert_not_all?(obj, m = nil, &blk)
+ failed = []
+ obj.each do |*a, &b|
+ if blk.call(*a, &b)
+ failed << a.size > 1 ? a : a[0]
+ end
+ end
+ assert(failed.empty?, message(m) {failed.pretty_inspect})
+ end
+
+ def build_message(head, template=nil, *arguments) #:nodoc:
+ template &&= template.chomp
+ template.gsub(/\G((?:[^\\]|\\.)*?)(\\)?\?/) { $1 + ($2 ? "?" : mu_pp(arguments.shift)) }
+ end
+
+ def message(msg = nil, *args, &default) # :nodoc:
+ if Proc === msg
+ super(nil, *args) do
+ ary = [msg.call, (default.call if default)].compact.reject(&:empty?)
+ if 1 < ary.length
+ ary[0...-1] = ary[0...-1].map {|str| str.sub(/(?<!\.)\z/, '.') }
+ end
+ ary.join("\n")
+ end
+ else
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/lib/test/unit/parallel.rb b/jni/ruby/test/lib/test/unit/parallel.rb
new file mode 100644
index 0000000..ea809f2
--- /dev/null
+++ b/jni/ruby/test/lib/test/unit/parallel.rb
@@ -0,0 +1,190 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../.."
+require 'test/unit'
+
+module Test
+ module Unit
+ class Worker < Runner # :nodoc:
+ class << self
+ undef autorun
+ end
+
+ alias orig_run_suite mini_run_suite
+ undef _run_suite
+ undef _run_suites
+ undef run
+
+ def increment_io(orig) # :nodoc:
+ *rest, io = 32.times.inject([orig.dup]){|ios, | ios << ios.last.dup }
+ rest.each(&:close)
+ io
+ end
+
+ def _run_suites(suites, type) # :nodoc:
+ suites.map do |suite|
+ _run_suite(suite, type)
+ end
+ end
+
+ def _run_suite(suite, type) # :nodoc:
+ @partial_report = []
+ orig_testout = MiniTest::Unit.output
+ i,o = IO.pipe
+
+ MiniTest::Unit.output = o
+ orig_stdin, orig_stdout = $stdin, $stdout
+
+ th = Thread.new do
+ begin
+ while buf = (self.verbose ? i.gets : i.readpartial(1024))
+ _report "p", buf
+ end
+ rescue IOError
+ rescue Errno::EPIPE
+ end
+ end
+
+ e, f, s = @errors, @failures, @skips
+
+ begin
+ result = orig_run_suite(suite, type)
+ rescue Interrupt
+ @need_exit = true
+ result = [nil,nil]
+ end
+
+ MiniTest::Unit.output = orig_testout
+ $stdin = orig_stdin
+ $stdout = orig_stdout
+
+ o.close
+ begin
+ th.join
+ rescue IOError
+ raise unless ["stream closed","closed stream"].include? $!.message
+ end
+ i.close
+
+ result << @partial_report
+ @partial_report = nil
+ result << [@errors-e,@failures-f,@skips-s]
+ result << ($: - @old_loadpath)
+ result << suite.name
+
+ begin
+ _report "done", Marshal.dump(result)
+ rescue Errno::EPIPE; end
+ return result
+ ensure
+ MiniTest::Unit.output = orig_stdout
+ $stdin = orig_stdin if orig_stdin
+ $stdout = orig_stdout if orig_stdout
+ o.close if o && !o.closed?
+ i.close if i && !i.closed?
+ end
+
+ def run(args = []) # :nodoc:
+ process_args args
+ @@stop_auto_run = true
+ @opts = @options.dup
+ @need_exit = false
+
+ @old_loadpath = []
+ begin
+ begin
+ @stdout = increment_io(STDOUT)
+ @stdin = increment_io(STDIN)
+ rescue
+ exit 2
+ end
+ exit 2 unless @stdout && @stdin
+
+ @stdout.sync = true
+ _report "ready!"
+ while buf = @stdin.gets
+ case buf.chomp
+ when /^loadpath (.+?)$/
+ @old_loadpath = $:.dup
+ $:.push(*Marshal.load($1.unpack("m")[0].force_encoding("ASCII-8BIT"))).uniq!
+ when /^run (.+?) (.+?)$/
+ _report "okay"
+
+ @options = @opts.dup
+ suites = MiniTest::Unit::TestCase.test_suites
+
+ begin
+ require $1
+ rescue LoadError
+ _report "after", Marshal.dump([$1, ProxyError.new($!)])
+ _report "ready"
+ next
+ end
+ _run_suites MiniTest::Unit::TestCase.test_suites-suites, $2.to_sym
+
+ if @need_exit
+ begin
+ _report "bye"
+ rescue Errno::EPIPE; end
+ exit
+ else
+ _report "ready"
+ end
+ when /^quit$/
+ begin
+ _report "bye"
+ rescue Errno::EPIPE; end
+ exit
+ end
+ end
+ rescue Errno::EPIPE
+ rescue Exception => e
+ begin
+ trace = e.backtrace
+ err = ["#{trace.shift}: #{e.message} (#{e.class})"] + trace.map{|t| t.prepend("\t") }
+
+ _report "bye", Marshal.dump(err.join("\n"))
+ rescue Errno::EPIPE;end
+ exit
+ ensure
+ @stdin.close if @stdin
+ @stdout.close if @stdout
+ end
+ end
+
+ def _report(res, *args) # :nodoc:
+ res = "#{res} #{args.pack("m0")}" unless args.empty?
+ @stdout.puts(res)
+ end
+
+ def puke(klass, meth, e) # :nodoc:
+ if e.is_a?(MiniTest::Skip)
+ new_e = MiniTest::Skip.new(e.message)
+ new_e.set_backtrace(e.backtrace)
+ e = new_e
+ end
+ @partial_report << [klass.name, meth, e.is_a?(MiniTest::Assertion) ? e : ProxyError.new(e)]
+ super
+ end
+ end
+ end
+end
+
+if $0 == __FILE__
+ module Test
+ module Unit
+ class TestCase < MiniTest::Unit::TestCase # :nodoc: all
+ undef on_parallel_worker?
+ def on_parallel_worker?
+ true
+ end
+ end
+ end
+ end
+ require 'rubygems'
+ module Gem # :nodoc:
+ end
+ class Gem::TestCase < MiniTest::Unit::TestCase # :nodoc:
+ @@project_dir = File.expand_path('../../../..', __FILE__)
+ end
+
+ Test::Unit::Worker.new.run(ARGV)
+end
diff --git a/jni/ruby/test/lib/test/unit/testcase.rb b/jni/ruby/test/lib/test/unit/testcase.rb
new file mode 100644
index 0000000..984f08d
--- /dev/null
+++ b/jni/ruby/test/lib/test/unit/testcase.rb
@@ -0,0 +1,34 @@
+require 'test/unit/assertions'
+
+module Test
+ module Unit
+ # remove silly TestCase class
+ remove_const(:TestCase) if defined?(self::TestCase)
+
+ class TestCase < MiniTest::Unit::TestCase # :nodoc: all
+ include Assertions
+
+ def on_parallel_worker?
+ false
+ end
+
+ def run runner
+ @options = runner.options
+ super runner
+ end
+
+ def self.test_order
+ :sorted
+ end
+
+ def self.method_added(name)
+ return unless name.to_s.start_with?("test_")
+ @test_methods ||= {}
+ if @test_methods[name]
+ warn "test/unit warning: method #{ self }##{ name } is redefined"
+ end
+ @test_methods[name] = true
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/lib/tracepointchecker.rb b/jni/ruby/test/lib/tracepointchecker.rb
new file mode 100644
index 0000000..4612e62
--- /dev/null
+++ b/jni/ruby/test/lib/tracepointchecker.rb
@@ -0,0 +1,118 @@
+module TracePointChecker
+ STATE = {
+ count: 0,
+ running: false,
+ }
+
+ module ZombieTraceHunter
+ def before_setup
+ @tracepoint_captured_stat = TracePoint.stat.map{|k, (activated, _deleted)| [k, activated]}
+
+ super
+ end
+
+ def after_teardown
+ super
+
+ # detect zombie traces.
+ assert_equal(
+ @tracepoint_captured_stat,
+ TracePoint.stat.map{|k, (activated, _deleted)| [k, activated]},
+ "The number of active trace events was changed"
+ )
+ # puts "TracePoint - deleted: #{deleted}" if deleted > 0
+
+ TracePointChecker.check if STATE[:running]
+ end
+ end
+
+ MAIN_THREAD = Thread.current
+ TRACES = []
+
+ def self.prefix event
+ case event
+ when :call, :return
+ :n
+ when :c_call, :c_return
+ :c
+ when :b_call, :b_return
+ :b
+ end
+ end
+
+ def self.clear_call_stack
+ Thread.current[:call_stack] = []
+ end
+
+ def self.call_stack
+ stack = Thread.current[:call_stack]
+ stack = clear_call_stack unless stack
+ stack
+ end
+
+ def self.verbose_out label, method
+ puts label => call_stack, :count => STATE[:count], :method => method
+ end
+
+ def self.method_label tp
+ "#{prefix(tp.event)}##{tp.method_id}"
+ end
+
+ def self.start verbose: false, stop_at_failure: false
+ call_events = %i(a_call)
+ return_events = %i(a_return)
+ clear_call_stack
+
+ STATE[:running] = true
+
+ TRACES << TracePoint.new(*call_events){|tp|
+ next if Thread.current != MAIN_THREAD
+
+ method = method_label(tp)
+ call_stack.push method
+ STATE[:count] += 1
+
+ verbose_out :psuh, method if verbose
+ }
+
+ TRACES << TracePoint.new(*return_events){|tp|
+ next if Thread.current != MAIN_THREAD
+ STATE[:count] += 1
+
+ method = "#{prefix(tp.event)}##{tp.method_id}"
+ verbose_out :pop1, method if verbose
+
+ stored_method = call_stack.pop
+ next if stored_method.nil?
+
+ verbose_out :pop2, method if verbose
+
+ if stored_method != method
+ stop if stop_at_failure
+ RubyVM::SDR() if defined? RubyVM::SDR()
+ call_stack.clear
+ raise "#{stored_method} is expected, but #{method} (count: #{STATE[:count]})"
+ end
+ }
+
+ TRACES.each{|trace| trace.enable}
+ end
+
+ def self.stop
+ STATE[:running] = true
+ TRACES.each{|trace| trace.disable}
+ TRACES.clear
+ end
+
+ def self.check
+ TRACES.each{|trace|
+ raise "trace #{trace} should not be deactivated" unless trace.enabled?
+ }
+ end
+end
+
+class ::Test::Unit::TestCase
+ include TracePointChecker::ZombieTraceHunter
+end
+
+# TracePointChecker.start verbose: false
diff --git a/jni/ruby/test/lib/with_different_ofs.rb b/jni/ruby/test/lib/with_different_ofs.rb
new file mode 100644
index 0000000..76dfa68
--- /dev/null
+++ b/jni/ruby/test/lib/with_different_ofs.rb
@@ -0,0 +1,17 @@
+module DifferentOFS
+ module WithDifferentOFS
+ def setup
+ super
+ @ofs, $, = $,, "-"
+ end
+ def teardown
+ $, = @ofs
+ super
+ end
+ end
+
+ def self.extended(klass)
+ super(klass)
+ klass.const_set(:DifferentOFS, Class.new(klass).class_eval {include WithDifferentOFS}).name
+ end
+end
diff --git a/jni/ruby/test/logger/test_logdevice.rb b/jni/ruby/test/logger/test_logdevice.rb
new file mode 100644
index 0000000..a563635
--- /dev/null
+++ b/jni/ruby/test/logger/test_logdevice.rb
@@ -0,0 +1,419 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'logger'
+require 'tempfile'
+require 'tmpdir'
+
+class TestLogDevice < Test::Unit::TestCase
+ class LogExcnRaiser
+ def write(*arg)
+ raise 'disk is full'
+ end
+
+ def close
+ end
+
+ def stat
+ Object.new
+ end
+ end
+
+ def setup
+ @tempfile = Tempfile.new("logger")
+ @tempfile.close
+ @filename = @tempfile.path
+ File.unlink(@filename)
+ end
+
+ def teardown
+ @tempfile.close(true)
+ end
+
+ def d(log, opt = {})
+ Logger::LogDevice.new(log, opt)
+ end
+
+ def test_initialize
+ logdev = d(STDERR)
+ assert_equal(STDERR, logdev.dev)
+ assert_nil(logdev.filename)
+ assert_raises(TypeError) do
+ d(nil)
+ end
+ #
+ logdev = d(@filename)
+ begin
+ assert(File.exist?(@filename))
+ assert(logdev.dev.sync)
+ assert_equal(@filename, logdev.filename)
+ logdev.write('hello')
+ ensure
+ logdev.close
+ end
+ # create logfile whitch is already exist.
+ logdev = d(@filename)
+ begin
+ logdev.write('world')
+ logfile = File.read(@filename)
+ assert_equal(2, logfile.split(/\n/).size)
+ assert_match(/^helloworld$/, logfile)
+ ensure
+ logdev.close
+ end
+ end
+
+ def test_write
+ r, w = IO.pipe
+ logdev = d(w)
+ logdev.write("msg2\n\n")
+ IO.select([r], nil, nil, 0.1)
+ w.close
+ msg = r.read
+ r.close
+ assert_equal("msg2\n\n", msg)
+ #
+ logdev = d(LogExcnRaiser.new)
+ class << (stderr = '')
+ alias write <<
+ end
+ $stderr, stderr = stderr, $stderr
+ begin
+ assert_nothing_raised do
+ logdev.write('hello')
+ end
+ ensure
+ logdev.close
+ $stderr, stderr = stderr, $stderr
+ end
+ assert_equal "log writing failed. disk is full\n", stderr
+ end
+
+ def test_close
+ r, w = IO.pipe
+ logdev = d(w)
+ logdev.write("msg2\n\n")
+ IO.select([r], nil, nil, 0.1)
+ assert(!w.closed?)
+ logdev.close
+ assert(w.closed?)
+ r.close
+ end
+
+ def test_shifting_size
+ tmpfile = Tempfile.new([File.basename(__FILE__, '.*'), '_1.log'])
+ logfile = tmpfile.path
+ logfile0 = logfile + '.0'
+ logfile1 = logfile + '.1'
+ logfile2 = logfile + '.2'
+ logfile3 = logfile + '.3'
+ tmpfile.close(true)
+ File.unlink(logfile) if File.exist?(logfile)
+ File.unlink(logfile0) if File.exist?(logfile0)
+ File.unlink(logfile1) if File.exist?(logfile1)
+ File.unlink(logfile2) if File.exist?(logfile2)
+ logger = Logger.new(logfile, 4, 100)
+ logger.error("0" * 15)
+ assert(File.exist?(logfile))
+ assert(!File.exist?(logfile0))
+ logger.error("0" * 15)
+ assert(File.exist?(logfile0))
+ assert(!File.exist?(logfile1))
+ logger.error("0" * 15)
+ assert(File.exist?(logfile1))
+ assert(!File.exist?(logfile2))
+ logger.error("0" * 15)
+ assert(File.exist?(logfile2))
+ assert(!File.exist?(logfile3))
+ logger.error("0" * 15)
+ assert(!File.exist?(logfile3))
+ logger.error("0" * 15)
+ assert(!File.exist?(logfile3))
+ logger.close
+ File.unlink(logfile)
+ File.unlink(logfile0)
+ File.unlink(logfile1)
+ File.unlink(logfile2)
+
+ tmpfile = Tempfile.new([File.basename(__FILE__, '.*'), '_2.log'])
+ logfile = tmpfile.path
+ logfile0 = logfile + '.0'
+ logfile1 = logfile + '.1'
+ logfile2 = logfile + '.2'
+ logfile3 = logfile + '.3'
+ tmpfile.close(true)
+ logger = Logger.new(logfile, 4, 150)
+ logger.error("0" * 15)
+ assert(File.exist?(logfile))
+ assert(!File.exist?(logfile0))
+ logger.error("0" * 15)
+ assert(!File.exist?(logfile0))
+ logger.error("0" * 15)
+ assert(File.exist?(logfile0))
+ assert(!File.exist?(logfile1))
+ logger.error("0" * 15)
+ assert(!File.exist?(logfile1))
+ logger.error("0" * 15)
+ assert(File.exist?(logfile1))
+ assert(!File.exist?(logfile2))
+ logger.error("0" * 15)
+ assert(!File.exist?(logfile2))
+ logger.error("0" * 15)
+ assert(File.exist?(logfile2))
+ assert(!File.exist?(logfile3))
+ logger.error("0" * 15)
+ assert(!File.exist?(logfile3))
+ logger.error("0" * 15)
+ assert(!File.exist?(logfile3))
+ logger.error("0" * 15)
+ assert(!File.exist?(logfile3))
+ logger.close
+ File.unlink(logfile)
+ File.unlink(logfile0)
+ File.unlink(logfile1)
+ File.unlink(logfile2)
+ end
+
+ def test_shifting_age_variants
+ logger = Logger.new(@filename, 'daily')
+ logger.info('daily')
+ logger.close
+ logger = Logger.new(@filename, 'weekly')
+ logger.info('weekly')
+ logger.close
+ logger = Logger.new(@filename, 'monthly')
+ logger.info('monthly')
+ logger.close
+ end
+
+ def test_shifting_age
+ # shift_age other than 'daily', 'weekly', and 'monthly' means 'everytime'
+ yyyymmdd = Time.now.strftime("%Y%m%d")
+ filename1 = @filename + ".#{yyyymmdd}"
+ filename2 = @filename + ".#{yyyymmdd}.1"
+ filename3 = @filename + ".#{yyyymmdd}.2"
+ begin
+ logger = Logger.new(@filename, 'now')
+ assert(File.exist?(@filename))
+ assert(!File.exist?(filename1))
+ assert(!File.exist?(filename2))
+ assert(!File.exist?(filename3))
+ logger.info("0" * 15)
+ assert(File.exist?(@filename))
+ assert(File.exist?(filename1))
+ assert(!File.exist?(filename2))
+ assert(!File.exist?(filename3))
+ logger.warn("0" * 15)
+ assert(File.exist?(@filename))
+ assert(File.exist?(filename1))
+ assert(File.exist?(filename2))
+ assert(!File.exist?(filename3))
+ logger.error("0" * 15)
+ assert(File.exist?(@filename))
+ assert(File.exist?(filename1))
+ assert(File.exist?(filename2))
+ assert(File.exist?(filename3))
+ ensure
+ logger.close if logger
+ [filename1, filename2, filename3].each do |filename|
+ File.unlink(filename) if File.exist?(filename)
+ end
+ end
+ end
+
+ def test_shifting_size_in_multiprocess
+ tmpfile = Tempfile.new([File.basename(__FILE__, '.*'), '_1.log'])
+ logfile = tmpfile.path
+ logfile0 = logfile + '.0'
+ logfile1 = logfile + '.1'
+ logfile2 = logfile + '.2'
+ tmpfile.close(true)
+ File.unlink(logfile) if File.exist?(logfile)
+ File.unlink(logfile0) if File.exist?(logfile0)
+ File.unlink(logfile1) if File.exist?(logfile1)
+ File.unlink(logfile2) if File.exist?(logfile2)
+ begin
+ stderr = run_children(2, [logfile], <<-'END')
+ logger = Logger.new(ARGV[0], 4, 10)
+ 10.times do
+ logger.info '0' * 15
+ end
+ END
+ assert_no_match(/log shifting failed/, stderr)
+ assert_no_match(/log writing failed/, stderr)
+ assert_no_match(/log rotation inter-process lock failed/, stderr)
+ ensure
+ File.unlink(logfile) if File.exist?(logfile)
+ File.unlink(logfile0) if File.exist?(logfile0)
+ File.unlink(logfile1) if File.exist?(logfile1)
+ File.unlink(logfile2) if File.exist?(logfile2)
+ end
+ end
+
+ def test_shifting_age_in_multiprocess
+ yyyymmdd = Time.now.strftime("%Y%m%d")
+ begin
+ stderr = run_children(2, [@filename], <<-'END')
+ logger = Logger.new(ARGV[0], 'now')
+ 10.times do
+ logger.info '0' * 15
+ end
+ END
+ assert_no_match(/log shifting failed/, stderr)
+ assert_no_match(/log writing failed/, stderr)
+ assert_no_match(/log rotation inter-process lock failed/, stderr)
+ ensure
+ Dir.glob("#{@filename}.#{yyyymmdd}{,.[1-9]*}") do |filename|
+ File.unlink(filename) if File.exist?(filename)
+ end
+ end
+ end
+
+ def test_open_logfile_in_multiprocess
+ tmpfile = Tempfile.new([File.basename(__FILE__, '.*'), '_1.log'])
+ logfile = tmpfile.path
+ tmpfile.close(true)
+ begin
+ 20.times do
+ run_children(2, [logfile], <<-'END')
+ logfile = ARGV[0]
+ logdev = Logger::LogDevice.new(logfile)
+ logdev.send(:open_logfile, logfile)
+ END
+ assert_equal(1, File.readlines(logfile).grep(/# Logfile created on/).size)
+ File.unlink(logfile)
+ end
+ ensure
+ File.unlink(logfile) if File.exist?(logfile)
+ end
+ end
+
+ def test_shifting_size_not_rotate_too_much
+ logdev0 = d(@filename)
+ logdev0.__send__(:add_log_header, @tempfile)
+ header_size = @tempfile.size
+ message = "*" * 99 + "\n"
+ shift_size = header_size + message.size * 3 - 1
+ opt = {shift_age: 1, shift_size: shift_size}
+
+ Dir.mktmpdir do |tmpdir|
+ begin
+ log = File.join(tmpdir, "log")
+ logdev1 = d(log, opt)
+ logdev2 = d(log, opt)
+
+ assert_file.identical?(log, logdev1.dev)
+ assert_file.identical?(log, logdev2.dev)
+
+ 3.times{logdev1.write(message)}
+ assert_file.identical?(log, logdev1.dev)
+ assert_file.identical?(log, logdev2.dev)
+
+ logdev1.write(message)
+ assert_file.identical?(log, logdev1.dev)
+ assert_file.identical?(log + ".0", logdev2.dev)
+
+ logdev2.write(message)
+ assert_file.identical?(log, logdev1.dev)
+ assert_file.identical?(log, logdev2.dev)
+
+ logdev1.write(message)
+ assert_file.identical?(log, logdev1.dev)
+ assert_file.identical?(log, logdev2.dev)
+ ensure
+ logdev1.close if logdev1
+ logdev2.close if logdev2
+ end
+ end
+ ensure
+ logdev0.close
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_shifting_midnight
+ Dir.mktmpdir do |tmpdir|
+ assert_in_out_err([*%W"--disable=gems -rlogger -C#{tmpdir} -"], <<-'end;')
+ begin
+ module FakeTime
+ attr_accessor :now
+ end
+
+ class << Time
+ prepend FakeTime
+ end
+
+ log = "log"
+ File.open(log, "w") {}
+ File.utime(*[Time.mktime(2014, 1, 1, 23, 59, 59)]*2, log)
+
+ Time.now = Time.mktime(2014, 1, 2, 23, 59, 59, 999000)
+ dev = Logger::LogDevice.new(log, shift_age: 'daily')
+ dev.write("#{Time.now} hello-1\n")
+
+ Time.now = Time.mktime(2014, 1, 3, 1, 1, 1)
+ dev.write("#{Time.now} hello-2\n")
+ ensure
+ dev.close
+ end
+ end;
+
+ bug = '[GH-539]'
+ log = File.join(tmpdir, "log")
+ cont = File.read(log)
+ assert_match(/hello-2/, cont)
+ assert_not_match(/hello-1/, cont)
+ assert_file.for(bug).exist?(log+".20140102")
+ assert_match(/hello-1/, File.read(log+".20140102"), bug)
+ end
+ end
+
+ def test_shifting_dst_change
+ Dir.mktmpdir do |tmpdir|
+ assert_in_out_err([{"TZ"=>"Europe/London"}, *%W"--disable=gems -rlogger -C#{tmpdir} -"], <<-'end;')
+ begin
+ module FakeTime
+ attr_accessor :now
+ end
+
+ class << Time
+ prepend FakeTime
+ end
+
+ log = "log"
+ File.open(log, "w") {}
+
+ Time.now = Time.mktime(2014, 3, 30, 0, 1, 1)
+ File.utime(Time.now, Time.now, log)
+
+ dev = Logger::LogDevice.new(log, shift_age: 'daily')
+ dev.write("#{Time.now} hello-1\n")
+ File.utime(*[Time.mktime(2014, 3, 30, 0, 2, 3)]*2, log)
+
+ Time.now = Time.mktime(2014, 3, 31, 0, 1, 1)
+ File.utime(Time.now, Time.now, log)
+ dev.write("#{Time.now} hello-2\n")
+ ensure
+ dev.close
+ end
+ end;
+
+ log = File.join(tmpdir, "log")
+ cont = File.read(log)
+ assert_match(/hello-2/, cont)
+ assert_not_match(/hello-1/, cont)
+ assert_file.exist?(log+".20140330")
+ end
+ end if /linux|darwin|freebsd/ =~ RUBY_PLATFORM # borrow from test/ruby/test_time_tz.rb
+
+ private
+
+ def run_children(n, args, src)
+ r, w = IO.pipe
+ [w, *(1..n).map do
+ f = IO.popen([EnvUtil.rubybin, *%w[--disable=gems -rlogger -], *args], "w", err: w)
+ f.puts(src)
+ f
+ end].each(&:close)
+ stderr = r.read
+ r.close
+ stderr
+ end
+end
diff --git a/jni/ruby/test/logger/test_logger.rb b/jni/ruby/test/logger/test_logger.rb
new file mode 100644
index 0000000..dc1a155
--- /dev/null
+++ b/jni/ruby/test/logger/test_logger.rb
@@ -0,0 +1,244 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'logger'
+require 'tempfile'
+
+class TestLogger < Test::Unit::TestCase
+ include Logger::Severity
+
+ def setup
+ @logger = Logger.new(nil)
+ end
+
+ class Log
+ attr_reader :label, :datetime, :pid, :severity, :progname, :msg
+ def initialize(line)
+ /\A(\w+), \[([^#]*)#(\d+)\]\s+(\w+) -- (\w*): ([\x0-\xff]*)/ =~ line
+ @label, @datetime, @pid, @severity, @progname, @msg = $1, $2, $3, $4, $5, $6
+ end
+ end
+
+ def log_add(logger, severity, msg, progname = nil, &block)
+ log(logger, :add, severity, msg, progname, &block)
+ end
+
+ def log(logger, msg_id, *arg, &block)
+ Log.new(log_raw(logger, msg_id, *arg, &block))
+ end
+
+ def log_raw(logger, msg_id, *arg, &block)
+ Tempfile.create(File.basename(__FILE__) + '.log') {|logdev|
+ logger.instance_eval { @logdev = Logger::LogDevice.new(logdev) }
+ logger.__send__(msg_id, *arg, &block)
+ logdev.rewind
+ logdev.read
+ }
+ end
+
+ def test_level
+ @logger.level = UNKNOWN
+ assert_equal(UNKNOWN, @logger.level)
+ @logger.level = INFO
+ assert_equal(INFO, @logger.level)
+ @logger.sev_threshold = ERROR
+ assert_equal(ERROR, @logger.sev_threshold)
+ @logger.sev_threshold = WARN
+ assert_equal(WARN, @logger.sev_threshold)
+ assert_equal(WARN, @logger.level)
+
+ @logger.level = DEBUG
+ assert(@logger.debug?)
+ assert(@logger.info?)
+ @logger.level = INFO
+ assert(!@logger.debug?)
+ assert(@logger.info?)
+ assert(@logger.warn?)
+ @logger.level = WARN
+ assert(!@logger.info?)
+ assert(@logger.warn?)
+ assert(@logger.error?)
+ @logger.level = ERROR
+ assert(!@logger.warn?)
+ assert(@logger.error?)
+ assert(@logger.fatal?)
+ @logger.level = FATAL
+ assert(!@logger.error?)
+ assert(@logger.fatal?)
+ @logger.level = UNKNOWN
+ assert(!@logger.error?)
+ assert(!@logger.fatal?)
+ end
+
+ def test_progname
+ assert_nil(@logger.progname)
+ @logger.progname = "name"
+ assert_equal("name", @logger.progname)
+ end
+
+ def test_datetime_format
+ dummy = STDERR
+ logger = Logger.new(dummy)
+ log = log_add(logger, INFO, "foo")
+ assert_match(/^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\s*\d+ $/, log.datetime)
+ logger.datetime_format = "%d%b%Y@%H:%M:%S"
+ log = log_add(logger, INFO, "foo")
+ assert_match(/^\d\d\w\w\w\d\d\d\d@\d\d:\d\d:\d\d$/, log.datetime)
+ logger.datetime_format = ""
+ log = log_add(logger, INFO, "foo")
+ assert_match(/^$/, log.datetime)
+ end
+
+ def test_formatter
+ dummy = STDERR
+ logger = Logger.new(dummy)
+ # default
+ log = log(logger, :info, "foo")
+ assert_equal("foo\n", log.msg)
+ # config
+ logger.formatter = proc { |severity, timestamp, progname, msg|
+ "#{severity}:#{msg}\n\n"
+ }
+ line = log_raw(logger, :info, "foo")
+ assert_equal("INFO:foo\n\n", line)
+ # recover
+ logger.formatter = nil
+ log = log(logger, :info, "foo")
+ assert_equal("foo\n", log.msg)
+ # again
+ o = Object.new
+ def o.call(severity, timestamp, progname, msg)
+ "<<#{severity}-#{msg}>>\n"
+ end
+ logger.formatter = o
+ line = log_raw(logger, :info, "foo")
+ assert_equal("<""<INFO-foo>>\n", line)
+ end
+
+ def test_initialize
+ logger = Logger.new(STDERR)
+ assert_nil(logger.progname)
+ assert_equal(DEBUG, logger.level)
+ assert_nil(logger.datetime_format)
+ end
+
+ def test_add
+ logger = Logger.new(nil)
+ logger.progname = "my_progname"
+ assert(logger.add(INFO))
+ log = log_add(logger, nil, "msg")
+ assert_equal("ANY", log.severity)
+ assert_equal("my_progname", log.progname)
+ logger.level = WARN
+ assert(logger.log(INFO))
+ assert_nil(log_add(logger, INFO, "msg").msg)
+ log = log_add(logger, WARN, nil) { "msg" }
+ assert_equal("msg\n", log.msg)
+ log = log_add(logger, WARN, "") { "msg" }
+ assert_equal("\n", log.msg)
+ assert_equal("my_progname", log.progname)
+ log = log_add(logger, WARN, nil, "progname?")
+ assert_equal("progname?\n", log.msg)
+ assert_equal("my_progname", log.progname)
+ end
+
+ def test_level_log
+ logger = Logger.new(nil)
+ logger.progname = "my_progname"
+ log = log(logger, :debug, "custom_progname") { "msg" }
+ assert_equal("msg\n", log.msg)
+ assert_equal("custom_progname", log.progname)
+ assert_equal("DEBUG", log.severity)
+ assert_equal("D", log.label)
+ #
+ log = log(logger, :debug) { "msg_block" }
+ assert_equal("msg_block\n", log.msg)
+ assert_equal("my_progname", log.progname)
+ log = log(logger, :debug, "msg_inline")
+ assert_equal("msg_inline\n", log.msg)
+ assert_equal("my_progname", log.progname)
+ #
+ log = log(logger, :info, "custom_progname") { "msg" }
+ assert_equal("msg\n", log.msg)
+ assert_equal("custom_progname", log.progname)
+ assert_equal("INFO", log.severity)
+ assert_equal("I", log.label)
+ #
+ log = log(logger, :warn, "custom_progname") { "msg" }
+ assert_equal("msg\n", log.msg)
+ assert_equal("custom_progname", log.progname)
+ assert_equal("WARN", log.severity)
+ assert_equal("W", log.label)
+ #
+ log = log(logger, :error, "custom_progname") { "msg" }
+ assert_equal("msg\n", log.msg)
+ assert_equal("custom_progname", log.progname)
+ assert_equal("ERROR", log.severity)
+ assert_equal("E", log.label)
+ #
+ log = log(logger, :fatal, "custom_progname") { "msg" }
+ assert_equal("msg\n", log.msg)
+ assert_equal("custom_progname", log.progname)
+ assert_equal("FATAL", log.severity)
+ assert_equal("F", log.label)
+ #
+ log = log(logger, :unknown, "custom_progname") { "msg" }
+ assert_equal("msg\n", log.msg)
+ assert_equal("custom_progname", log.progname)
+ assert_equal("ANY", log.severity)
+ assert_equal("A", log.label)
+ end
+
+ def test_close
+ r, w = IO.pipe
+ assert(!w.closed?)
+ logger = Logger.new(w)
+ logger.close
+ assert(w.closed?)
+ r.close
+ end
+
+ class MyError < StandardError
+ end
+
+ class MyMsg
+ def inspect
+ "my_msg"
+ end
+ end
+
+ def test_format
+ logger = Logger.new(nil)
+ log = log_add(logger, INFO, "msg\n")
+ assert_equal("msg\n\n", log.msg)
+ begin
+ raise MyError.new("excn")
+ rescue MyError => e
+ log = log_add(logger, INFO, e)
+ assert_match(/^excn \(TestLogger::MyError\)/, log.msg)
+ # expects backtrace is dumped across multi lines. 10 might be changed.
+ assert(log.msg.split(/\n/).size >= 10)
+ end
+ log = log_add(logger, INFO, MyMsg.new)
+ assert_equal("my_msg\n", log.msg)
+ end
+
+ def test_lshift
+ r, w = IO.pipe
+ logger = Logger.new(w)
+ logger << "msg"
+ read_ready, = IO.select([r], nil, nil, 0.1)
+ w.close
+ msg = r.read
+ r.close
+ assert_equal("msg", msg)
+ #
+ r, w = IO.pipe
+ logger = Logger.new(w)
+ logger << "msg2\n\n"
+ read_ready, = IO.select([r], nil, nil, 0.1)
+ w.close
+ msg = r.read
+ r.close
+ assert_equal("msg2\n\n", msg)
+ end
+end
diff --git a/jni/ruby/test/logger/test_severity.rb b/jni/ruby/test/logger/test_severity.rb
new file mode 100644
index 0000000..045461c
--- /dev/null
+++ b/jni/ruby/test/logger/test_severity.rb
@@ -0,0 +1,15 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'logger'
+
+class TestLoggerSeverity < Test::Unit::TestCase
+ def test_enum
+ logger_levels = Logger.constants
+ levels = ["WARN", "UNKNOWN", "INFO", "FATAL", "DEBUG", "ERROR"]
+ Logger::Severity.constants.each do |level|
+ assert(levels.include?(level.to_s))
+ assert(logger_levels.include?(level))
+ end
+ assert_equal(levels.size, Logger::Severity.constants.size)
+ end
+end
diff --git a/jni/ruby/test/matrix/test_matrix.rb b/jni/ruby/test/matrix/test_matrix.rb
new file mode 100644
index 0000000..3fdef3b
--- /dev/null
+++ b/jni/ruby/test/matrix/test_matrix.rb
@@ -0,0 +1,579 @@
+require 'test/unit'
+require 'matrix'
+
+class SubMatrix < Matrix
+end
+
+class TestMatrix < Test::Unit::TestCase
+ def setup
+ @m1 = Matrix[[1,2,3], [4,5,6]]
+ @m2 = Matrix[[1,2,3], [4,5,6]]
+ @m3 = @m1.clone
+ @m4 = Matrix[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
+ @n1 = Matrix[[2,3,4], [5,6,7]]
+ @c1 = Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
+ @e1 = Matrix.empty(2,0)
+ @e2 = Matrix.empty(0,3)
+ @a3 = Matrix[[4, 1, -3], [0, 3, 7], [11, -4, 2]]
+ @a5 = Matrix[[2, 0, 9, 3, 9], [8, 7, 0, 1, 9], [7, 5, 6, 6, 5], [0, 7, 8, 3, 0], [7, 8, 2, 3, 1]]
+ @b3 = Matrix[[-7, 7, -10], [9, -3, -2], [-1, 3, 9]]
+ end
+
+ def test_matrix
+ assert_equal(1, @m1[0, 0])
+ assert_equal(2, @m1[0, 1])
+ assert_equal(3, @m1[0, 2])
+ assert_equal(4, @m1[1, 0])
+ assert_equal(5, @m1[1, 1])
+ assert_equal(6, @m1[1, 2])
+ end
+
+ def test_identity
+ assert_same @m1, @m1
+ assert_not_same @m1, @m2
+ assert_not_same @m1, @m3
+ assert_not_same @m1, @m4
+ assert_not_same @m1, @n1
+ end
+
+ def test_equality
+ assert_equal @m1, @m1
+ assert_equal @m1, @m2
+ assert_equal @m1, @m3
+ assert_equal @m1, @m4
+ assert_not_equal @m1, @n1
+ end
+
+ def test_hash_equality
+ assert @m1.eql?(@m1)
+ assert @m1.eql?(@m2)
+ assert @m1.eql?(@m3)
+ assert !@m1.eql?(@m4)
+ assert !@m1.eql?(@n1)
+
+ hash = { @m1 => :value }
+ assert hash.key?(@m1)
+ assert hash.key?(@m2)
+ assert hash.key?(@m3)
+ assert !hash.key?(@m4)
+ assert !hash.key?(@n1)
+ end
+
+ def test_hash
+ assert_equal @m1.hash, @m1.hash
+ assert_equal @m1.hash, @m2.hash
+ assert_equal @m1.hash, @m3.hash
+ end
+
+ def test_uplus
+ assert_equal(@m1, +@m1)
+ end
+
+ def test_negate
+ assert_equal(Matrix[[-1, -2, -3], [-4, -5, -6]], -@m1)
+ assert_equal(@m1, -(-@m1))
+ end
+
+ def test_rank
+ [
+ [[0]],
+ [[0], [0]],
+ [[0, 0], [0, 0]],
+ [[0, 0], [0, 0], [0, 0]],
+ [[0, 0, 0]],
+ [[0, 0, 0], [0, 0, 0]],
+ [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ ].each do |rows|
+ assert_equal 0, Matrix[*rows].rank
+ end
+
+ [
+ [[1], [0]],
+ [[1, 0], [0, 0]],
+ [[1, 0], [1, 0]],
+ [[0, 0], [1, 0]],
+ [[1, 0], [0, 0], [0, 0]],
+ [[0, 0], [1, 0], [0, 0]],
+ [[0, 0], [0, 0], [1, 0]],
+ [[1, 0], [1, 0], [0, 0]],
+ [[0, 0], [1, 0], [1, 0]],
+ [[1, 0], [1, 0], [1, 0]],
+ [[1, 0, 0]],
+ [[1, 0, 0], [0, 0, 0]],
+ [[0, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[0, 0, 0], [1, 0, 0], [0, 0, 0]],
+ [[0, 0, 0], [0, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [1, 0, 0], [0, 0, 0]],
+ [[0, 0, 0], [1, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [0, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [1, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[1, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[1, 0, 0], [1, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[1, 0, 0], [0, 0, 0], [1, 0, 0], [0, 0, 0]],
+ [[1, 0, 0], [0, 0, 0], [0, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [1, 0, 0], [1, 0, 0], [0, 0, 0]],
+ [[1, 0, 0], [0, 0, 0], [1, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [1, 0, 0], [0, 0, 0], [1, 0, 0]],
+ [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]],
+
+ [[1]],
+ [[1], [1]],
+ [[1, 1]],
+ [[1, 1], [1, 1]],
+ [[1, 1], [1, 1], [1, 1]],
+ [[1, 1, 1]],
+ [[1, 1, 1], [1, 1, 1]],
+ [[1, 1, 1], [1, 1, 1], [1, 1, 1]],
+ [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
+ ].each do |rows|
+ matrix = Matrix[*rows]
+ assert_equal 1, matrix.rank
+ assert_equal 1, matrix.transpose.rank
+ end
+
+ [
+ [[1, 0], [0, 1]],
+ [[1, 0], [0, 1], [0, 0]],
+ [[1, 0], [0, 1], [0, 1]],
+ [[1, 0], [0, 1], [1, 1]],
+ [[1, 0, 0], [0, 1, 0]],
+ [[1, 0, 0], [0, 0, 1]],
+ [[1, 0, 0], [0, 1, 0], [0, 0, 0]],
+ [[1, 0, 0], [0, 0, 1], [0, 0, 0]],
+
+ [[1, 0, 0], [0, 0, 0], [0, 1, 0]],
+ [[1, 0, 0], [0, 0, 0], [0, 0, 1]],
+
+ [[1, 0], [1, 1]],
+ [[1, 2], [1, 1]],
+ [[1, 2], [0, 1], [1, 1]],
+ ].each do |rows|
+ m = Matrix[*rows]
+ assert_equal 2, m.rank
+ assert_equal 2, m.transpose.rank
+ end
+
+ [
+ [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
+ [[1, 1, 0], [0, 1, 1], [1, 0, 1]],
+ [[1, 1, 0], [0, 1, 1], [1, 0, 1]],
+ [[1, 1, 0], [0, 1, 1], [1, 0, 1], [0, 0, 0]],
+ [[1, 1, 0], [0, 1, 1], [1, 0, 1], [1, 1, 1]],
+ [[1, 1, 1], [1, 1, 2], [1, 3, 1], [4, 1, 1]],
+ ].each do |rows|
+ m = Matrix[*rows]
+ assert_equal 3, m.rank
+ assert_equal 3, m.transpose.rank
+ end
+ end
+
+ def test_inverse
+ assert_equal(Matrix.empty(0, 0), Matrix.empty.inverse)
+ assert_equal(Matrix[[-1, 1], [0, -1]], Matrix[[-1, -1], [0, -1]].inverse)
+ assert_raise(ExceptionForMatrix::ErrDimensionMismatch) { @m1.inverse }
+ end
+
+ def test_determinant
+ assert_equal(45, Matrix[[7,6], [3,9]].determinant)
+ assert_equal(-18, Matrix[[2,0,1],[0,-2,2],[1,2,3]].determinant)
+ end
+
+ def test_new_matrix
+ assert_raise(TypeError) { Matrix[Object.new] }
+ o = Object.new
+ def o.to_ary; [1,2,3]; end
+ assert_equal(@m1, Matrix[o, [4,5,6]])
+ end
+
+ def test_rows
+ assert_equal(@m1, Matrix.rows([[1, 2, 3], [4, 5, 6]]))
+ end
+
+ def test_rows_copy
+ rows1 = [[1], [1]]
+ rows2 = [[1], [1]]
+
+ m1 = Matrix.rows(rows1, copy = false)
+ m2 = Matrix.rows(rows2, copy = true)
+
+ rows1.uniq!
+ rows2.uniq!
+
+ assert_equal([[1]], m1.to_a)
+ assert_equal([[1], [1]], m2.to_a)
+ end
+
+ def test_columns
+ assert_equal(@m1, Matrix.columns([[1, 4], [2, 5], [3, 6]]))
+ end
+
+ def test_diagonal
+ assert_equal(Matrix.empty(0, 0), Matrix.diagonal( ))
+ assert_equal(Matrix[[3,0,0],[0,2,0],[0,0,1]], Matrix.diagonal(3, 2, 1))
+ assert_equal(Matrix[[4,0,0,0],[0,3,0,0],[0,0,2,0],[0,0,0,1]], Matrix.diagonal(4, 3, 2, 1))
+ end
+
+ def test_scalar
+ assert_equal(Matrix.empty(0, 0), Matrix.scalar(0, 1))
+ assert_equal(Matrix[[2,0,0],[0,2,0],[0,0,2]], Matrix.scalar(3, 2))
+ assert_equal(Matrix[[2,0,0,0],[0,2,0,0],[0,0,2,0],[0,0,0,2]], Matrix.scalar(4, 2))
+ end
+
+ def test_identity2
+ assert_equal(Matrix[[1,0,0],[0,1,0],[0,0,1]], Matrix.identity(3))
+ assert_equal(Matrix[[1,0,0],[0,1,0],[0,0,1]], Matrix.unit(3))
+ assert_equal(Matrix[[1,0,0],[0,1,0],[0,0,1]], Matrix.I(3))
+ assert_equal(Matrix[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]], Matrix.identity(4))
+ end
+
+ def test_zero
+ assert_equal(Matrix[[0,0,0],[0,0,0],[0,0,0]], Matrix.zero(3))
+ assert_equal(Matrix[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]], Matrix.zero(4))
+ assert_equal(Matrix[[0]], Matrix.zero(1))
+ end
+
+ def test_row_vector
+ assert_equal(Matrix[[1,2,3,4]], Matrix.row_vector([1,2,3,4]))
+ end
+
+ def test_column_vector
+ assert_equal(Matrix[[1],[2],[3],[4]], Matrix.column_vector([1,2,3,4]))
+ end
+
+ def test_empty
+ m = Matrix.empty(2, 0)
+ assert_equal(Matrix[ [], [] ], m)
+ n = Matrix.empty(0, 3)
+ assert_equal(Matrix.columns([ [], [], [] ]), n)
+ assert_equal(Matrix[[0, 0, 0], [0, 0, 0]], m * n)
+ end
+
+ def test_row
+ assert_equal(Vector[1, 2, 3], @m1.row(0))
+ assert_equal(Vector[4, 5, 6], @m1.row(1))
+ a = []; @m1.row(0) {|x| a << x }
+ assert_equal([1, 2, 3], a)
+ end
+
+ def test_column
+ assert_equal(Vector[1, 4], @m1.column(0))
+ assert_equal(Vector[2, 5], @m1.column(1))
+ assert_equal(Vector[3, 6], @m1.column(2))
+ a = []; @m1.column(0) {|x| a << x }
+ assert_equal([1, 4], a)
+ end
+
+ def test_collect
+ assert_equal(Matrix[[1, 4, 9], [16, 25, 36]], @m1.collect {|x| x ** 2 })
+ end
+
+ def test_minor
+ assert_equal(Matrix[[1, 2], [4, 5]], @m1.minor(0..1, 0..1))
+ assert_equal(Matrix[[2], [5]], @m1.minor(0..1, 1..1))
+ assert_equal(Matrix[[4, 5]], @m1.minor(1..1, 0..1))
+ assert_equal(Matrix[[1, 2], [4, 5]], @m1.minor(0, 2, 0, 2))
+ assert_equal(Matrix[[4, 5]], @m1.minor(1, 1, 0, 2))
+ assert_equal(Matrix[[2], [5]], @m1.minor(0, 2, 1, 1))
+ assert_raise(ArgumentError) { @m1.minor(0) }
+ end
+
+ def test_first_minor
+ assert_equal(Matrix.empty(0, 0), Matrix[[1]].first_minor(0, 0))
+ assert_equal(Matrix.empty(0, 2), Matrix[[1, 4, 2]].first_minor(0, 1))
+ assert_equal(Matrix[[1, 3]], @m1.first_minor(1, 1))
+ assert_equal(Matrix[[4, 6]], @m1.first_minor(0, 1))
+ assert_equal(Matrix[[1, 2]], @m1.first_minor(1, 2))
+ assert_raise(RuntimeError) { Matrix.empty(0, 0).first_minor(0, 0) }
+ assert_raise(ArgumentError) { @m1.first_minor(4, 0) }
+ assert_raise(ArgumentError) { @m1.first_minor(0, -1) }
+ assert_raise(ArgumentError) { @m1.first_minor(-1, 4) }
+ end
+
+ def test_cofactor
+ assert_equal(1, Matrix[[1]].cofactor(0, 0))
+ assert_equal(9, Matrix[[7,6],[3,9]].cofactor(0, 0))
+ assert_equal(0, Matrix[[0,0],[0,0]].cofactor(0, 0))
+ assert_equal(3, Matrix[[0,0,1],[0,7,6],[1,3,9]].cofactor(1, 0))
+ assert_equal(-21, Matrix[[7,0,1,0,12],[8,1,1,9,1],[4,0,0,-7,17],[-1,0,0,-4,8],[10,1,1,8,6]].cofactor(2, 3))
+ assert_raise(RuntimeError) { Matrix.empty(0, 0).cofactor(0, 0) }
+ assert_raise(ArgumentError) { Matrix[[0,0],[0,0]].cofactor(-1, 4) }
+ assert_raise(ExceptionForMatrix::ErrDimensionMismatch) { Matrix[[2,0,1],[0,-2,2]].cofactor(0, 0) }
+ end
+
+ def test_adjugate
+ assert_equal(Matrix.empty, Matrix.empty.adjugate)
+ assert_equal(Matrix[[1]], Matrix[[5]].adjugate)
+ assert_equal(Matrix[[9,-6],[-3,7]], Matrix[[7,6],[3,9]].adjugate)
+ assert_equal(Matrix[[45,3,-7],[6,-1,0],[-7,0,0]], Matrix[[0,0,1],[0,7,6],[1,3,9]].adjugate)
+ assert_equal(Matrix.identity(5), (@a5.adjugate * @a5) / @a5.det)
+ assert_equal(Matrix.I(3), Matrix.I(3).adjugate)
+ assert_equal((@a3 * @b3).adjugate, @b3.adjugate * @a3.adjugate)
+ assert_equal(4**(@a3.row_count-1) * @a3.adjugate, (4 * @a3).adjugate)
+ assert_raise(ExceptionForMatrix::ErrDimensionMismatch) { @m1.adjugate }
+ end
+
+ def test_laplace_expansion
+ assert_equal(1, Matrix[[1]].laplace_expansion(row: 0))
+ assert_equal(45, Matrix[[7,6], [3,9]].laplace_expansion(row: 1))
+ assert_equal(0, Matrix[[0,0],[0,0]].laplace_expansion(column: 0))
+ assert_equal(-7, Matrix[[0,0,1],[0,7,6],[1,3,9]].laplace_expansion(column: 2))
+
+ assert_equal(Vector[3, -2], Matrix[[Vector[1, 0], Vector[0, 1]], [2, 3]].laplace_expansion(row: 0))
+
+ assert_raise(ExceptionForMatrix::ErrDimensionMismatch) { @m1.laplace_expansion(row: 1) }
+ assert_raise(ArgumentError) { Matrix[[7,6], [3,9]].laplace_expansion() }
+ assert_raise(ArgumentError) { Matrix[[7,6], [3,9]].laplace_expansion(foo: 1) }
+ assert_raise(ArgumentError) { Matrix[[7,6], [3,9]].laplace_expansion(row: 1, column: 1) }
+ assert_raise(ArgumentError) { Matrix[[7,6], [3,9]].laplace_expansion(row: 2) }
+ assert_raise(ArgumentError) { Matrix[[0,0,1],[0,7,6],[1,3,9]].laplace_expansion(column: -1) }
+
+ assert_raise(RuntimeError) { Matrix.empty(0, 0).laplace_expansion(row: 0) }
+ end
+
+ def test_regular?
+ assert(Matrix[[1, 0], [0, 1]].regular?)
+ assert(Matrix[[1, 0, 0], [0, 1, 0], [0, 0, 1]].regular?)
+ assert(!Matrix[[1, 0, 0], [0, 0, 1], [0, 0, 1]].regular?)
+ end
+
+ def test_singular?
+ assert(!Matrix[[1, 0], [0, 1]].singular?)
+ assert(!Matrix[[1, 0, 0], [0, 1, 0], [0, 0, 1]].singular?)
+ assert(Matrix[[1, 0, 0], [0, 0, 1], [0, 0, 1]].singular?)
+ end
+
+ def test_square?
+ assert(Matrix[[1, 0], [0, 1]].square?)
+ assert(Matrix[[1, 0, 0], [0, 1, 0], [0, 0, 1]].square?)
+ assert(Matrix[[1, 0, 0], [0, 0, 1], [0, 0, 1]].square?)
+ assert(!Matrix[[1, 0, 0], [0, 1, 0]].square?)
+ end
+
+ def test_mul
+ assert_equal(Matrix[[2,4],[6,8]], Matrix[[2,4],[6,8]] * Matrix.I(2))
+ assert_equal(Matrix[[4,8],[12,16]], Matrix[[2,4],[6,8]] * 2)
+ assert_equal(Matrix[[4,8],[12,16]], 2 * Matrix[[2,4],[6,8]])
+ assert_equal(Matrix[[14,32],[32,77]], @m1 * @m1.transpose)
+ assert_equal(Matrix[[17,22,27],[22,29,36],[27,36,45]], @m1.transpose * @m1)
+ assert_equal(Vector[14,32], @m1 * Vector[1,2,3])
+ o = Object.new
+ def o.coerce(m)
+ [m, m.transpose]
+ end
+ assert_equal(Matrix[[14,32],[32,77]], @m1 * o)
+ end
+
+ def test_add
+ assert_equal(Matrix[[6,0],[-4,12]], Matrix.scalar(2,5) + Matrix[[1,0],[-4,7]])
+ assert_equal(Matrix[[3,5,7],[9,11,13]], @m1 + @n1)
+ assert_equal(Matrix[[3,5,7],[9,11,13]], @n1 + @m1)
+ assert_equal(Matrix[[2],[4],[6]], Matrix[[1],[2],[3]] + Vector[1,2,3])
+ assert_raise(Matrix::ErrOperationNotDefined) { @m1 + 1 }
+ o = Object.new
+ def o.coerce(m)
+ [m, m]
+ end
+ assert_equal(Matrix[[2,4,6],[8,10,12]], @m1 + o)
+ end
+
+ def test_sub
+ assert_equal(Matrix[[4,0],[4,-2]], Matrix.scalar(2,5) - Matrix[[1,0],[-4,7]])
+ assert_equal(Matrix[[-1,-1,-1],[-1,-1,-1]], @m1 - @n1)
+ assert_equal(Matrix[[1,1,1],[1,1,1]], @n1 - @m1)
+ assert_equal(Matrix[[0],[0],[0]], Matrix[[1],[2],[3]] - Vector[1,2,3])
+ assert_raise(Matrix::ErrOperationNotDefined) { @m1 - 1 }
+ o = Object.new
+ def o.coerce(m)
+ [m, m]
+ end
+ assert_equal(Matrix[[0,0,0],[0,0,0]], @m1 - o)
+ end
+
+ def test_div
+ assert_equal(Matrix[[0,1,1],[2,2,3]], @m1 / 2)
+ assert_equal(Matrix[[1,1],[1,1]], Matrix[[2,2],[2,2]] / Matrix.scalar(2,2))
+ o = Object.new
+ def o.coerce(m)
+ [m, Matrix.scalar(2,2)]
+ end
+ assert_equal(Matrix[[1,1],[1,1]], Matrix[[2,2],[2,2]] / o)
+ end
+
+ def test_exp
+ assert_equal(Matrix[[67,96],[48,99]], Matrix[[7,6],[3,9]] ** 2)
+ assert_equal(Matrix.I(5), Matrix.I(5) ** -1)
+ assert_raise(Matrix::ErrOperationNotDefined) { Matrix.I(5) ** Object.new }
+ end
+
+ def test_det
+ assert_equal(45, Matrix[[7,6],[3,9]].det)
+ assert_equal(0, Matrix[[0,0],[0,0]].det)
+ assert_equal(-7, Matrix[[0,0,1],[0,7,6],[1,3,9]].det)
+ assert_equal(42, Matrix[[7,0,1,0,12],[8,1,1,9,1],[4,0,0,-7,17],[-1,0,0,-4,8],[10,1,1,8,6]].det)
+ end
+
+ def test_rank2
+ assert_equal(2, Matrix[[7,6],[3,9]].rank)
+ assert_equal(0, Matrix[[0,0],[0,0]].rank)
+ assert_equal(3, Matrix[[0,0,1],[0,7,6],[1,3,9]].rank)
+ assert_equal(1, Matrix[[0,1],[0,1],[0,1]].rank)
+ assert_equal(2, @m1.rank)
+ end
+
+ def test_trace
+ assert_equal(1+5+9, Matrix[[1,2,3],[4,5,6],[7,8,9]].trace)
+ end
+
+ def test_transpose
+ assert_equal(Matrix[[1,4],[2,5],[3,6]], @m1.transpose)
+ end
+
+ def test_conjugate
+ assert_equal(Matrix[[Complex(1,-2), Complex(0,-1), 0], [1, 2, 3]], @c1.conjugate)
+ end
+
+ def test_eigensystem
+ m = Matrix[[1, 2], [3, 4]]
+ v, d, v_inv = m.eigensystem
+ assert(d.diagonal?)
+ assert_equal(v.inv, v_inv)
+ assert_equal((v * d * v_inv).round(5), m)
+ end
+
+ def test_imaginary
+ assert_equal(Matrix[[2, 1, 0], [0, 0, 0]], @c1.imaginary)
+ end
+
+ def test_lup
+ m = Matrix[[1, 2], [3, 4]]
+ l, u, p = m.lup
+ assert(l.lower_triangular?)
+ assert(u.upper_triangular?)
+ assert(p.permutation?)
+ assert(l * u == p * m)
+ assert_equal(m.lup.solve([2, 5]), Vector[1, Rational(1,2)])
+ end
+
+ def test_real
+ assert_equal(Matrix[[1, 0, 0], [1, 2, 3]], @c1.real)
+ end
+
+ def test_rect
+ assert_equal([Matrix[[1, 0, 0], [1, 2, 3]], Matrix[[2, 1, 0], [0, 0, 0]]], @c1.rect)
+ end
+
+ def test_row_vectors
+ assert_equal([Vector[1,2,3], Vector[4,5,6]], @m1.row_vectors)
+ end
+
+ def test_column_vectors
+ assert_equal([Vector[1,4], Vector[2,5], Vector[3,6]], @m1.column_vectors)
+ end
+
+ def test_to_s
+ assert_equal("Matrix[[1, 2, 3], [4, 5, 6]]", @m1.to_s)
+ assert_equal("Matrix.empty(0, 0)", Matrix[].to_s)
+ assert_equal("Matrix.empty(1, 0)", Matrix[[]].to_s)
+ end
+
+ def test_inspect
+ assert_equal("Matrix[[1, 2, 3], [4, 5, 6]]", @m1.inspect)
+ assert_equal("Matrix.empty(0, 0)", Matrix[].inspect)
+ assert_equal("Matrix.empty(1, 0)", Matrix[[]].inspect)
+ end
+
+ def test_scalar_add
+ s1 = @m1.coerce(1).first
+ assert_equal(Matrix[[1]], (s1 + 0) * Matrix[[1]])
+ assert_raise(Matrix::ErrOperationNotDefined) { s1 + Vector[0] }
+ assert_raise(Matrix::ErrOperationNotDefined) { s1 + Matrix[[0]] }
+ o = Object.new
+ def o.coerce(x)
+ [1, 1]
+ end
+ assert_equal(2, s1 + o)
+ end
+
+ def test_scalar_sub
+ s1 = @m1.coerce(1).first
+ assert_equal(Matrix[[1]], (s1 - 0) * Matrix[[1]])
+ assert_raise(Matrix::ErrOperationNotDefined) { s1 - Vector[0] }
+ assert_raise(Matrix::ErrOperationNotDefined) { s1 - Matrix[[0]] }
+ o = Object.new
+ def o.coerce(x)
+ [1, 1]
+ end
+ assert_equal(0, s1 - o)
+ end
+
+ def test_scalar_mul
+ s1 = @m1.coerce(1).first
+ assert_equal(Matrix[[1]], (s1 * 1) * Matrix[[1]])
+ assert_equal(Vector[2], s1 * Vector[2])
+ assert_equal(Matrix[[2]], s1 * Matrix[[2]])
+ o = Object.new
+ def o.coerce(x)
+ [1, 1]
+ end
+ assert_equal(1, s1 * o)
+ end
+
+ def test_scalar_div
+ s1 = @m1.coerce(1).first
+ assert_equal(Matrix[[1]], (s1 / 1) * Matrix[[1]])
+ assert_raise(Matrix::ErrOperationNotDefined) { s1 / Vector[0] }
+ assert_equal(Matrix[[Rational(1,2)]], s1 / Matrix[[2]])
+ o = Object.new
+ def o.coerce(x)
+ [1, 1]
+ end
+ assert_equal(1, s1 / o)
+ end
+
+ def test_scalar_pow
+ s1 = @m1.coerce(1).first
+ assert_equal(Matrix[[1]], (s1 ** 1) * Matrix[[1]])
+ assert_raise(Matrix::ErrOperationNotDefined) { s1 ** Vector[0] }
+ assert_raise(Matrix::ErrOperationNotImplemented) { s1 ** Matrix[[1]] }
+ o = Object.new
+ def o.coerce(x)
+ [1, 1]
+ end
+ assert_equal(1, s1 ** o)
+ end
+
+ def test_hstack
+ assert_equal Matrix[[1,2,3,2,3,4,1,2,3], [4,5,6,5,6,7,4,5,6]],
+ @m1.hstack(@n1, @m1)
+ # Error checking:
+ assert_raise(TypeError) { @m1.hstack(42) }
+ assert_raise(TypeError) { Matrix.hstack(42, @m1) }
+ assert_raise(Matrix::ErrDimensionMismatch) { @m1.hstack(Matrix.identity(3)) }
+ assert_raise(Matrix::ErrDimensionMismatch) { @e1.hstack(@e2) }
+ # Corner cases:
+ assert_equal @m1, @m1.hstack
+ assert_equal @e1, @e1.hstack(@e1)
+ assert_equal Matrix.empty(0,6), @e2.hstack(@e2)
+ assert_equal SubMatrix, SubMatrix.hstack(@e1).class
+ end
+
+ def test_vstack
+ assert_equal Matrix[[1,2,3], [4,5,6], [2,3,4], [5,6,7], [1,2,3], [4,5,6]],
+ @m1.vstack(@n1, @m1)
+ # Error checking:
+ assert_raise(TypeError) { @m1.vstack(42) }
+ assert_raise(TypeError) { Matrix.vstack(42, @m1) }
+ assert_raise(Matrix::ErrDimensionMismatch) { @m1.vstack(Matrix.identity(2)) }
+ assert_raise(Matrix::ErrDimensionMismatch) { @e1.vstack(@e2) }
+ # Corner cases:
+ assert_equal @m1, @m1.vstack
+ assert_equal Matrix.empty(4,0), @e1.vstack(@e1)
+ assert_equal @e2, @e2.vstack(@e2)
+ assert_equal SubMatrix, SubMatrix.vstack(@e1).class
+ end
+end
diff --git a/jni/ruby/test/matrix/test_vector.rb b/jni/ruby/test/matrix/test_vector.rb
new file mode 100644
index 0000000..3275310
--- /dev/null
+++ b/jni/ruby/test/matrix/test_vector.rb
@@ -0,0 +1,215 @@
+require 'test/unit'
+require 'matrix'
+
+class TestVector < Test::Unit::TestCase
+ def setup
+ @v1 = Vector[1,2,3]
+ @v2 = Vector[1,2,3]
+ @v3 = @v1.clone
+ @v4 = Vector[1.0, 2.0, 3.0]
+ @w1 = Vector[2,3,4]
+ end
+
+ def test_basis
+ assert_equal(Vector[1, 0, 0], Vector.basis(size: 3, index: 0))
+ assert_raise(ArgumentError) { Vector.basis(size: -1, index: 2) }
+ assert_raise(ArgumentError) { Vector.basis(size: 4, index: -1) }
+ assert_raise(ArgumentError) { Vector.basis(size: 3, index: 3) }
+ assert_raise(ArgumentError) { Vector.basis(size: 3) }
+ assert_raise(ArgumentError) { Vector.basis(index: 3) }
+ end
+
+ def test_identity
+ assert_same @v1, @v1
+ assert_not_same @v1, @v2
+ assert_not_same @v1, @v3
+ assert_not_same @v1, @v4
+ assert_not_same @v1, @w1
+ end
+
+ def test_equality
+ assert_equal @v1, @v1
+ assert_equal @v1, @v2
+ assert_equal @v1, @v3
+ assert_equal @v1, @v4
+ assert_not_equal @v1, @w1
+ end
+
+ def test_hash_equality
+ assert @v1.eql?(@v1)
+ assert @v1.eql?(@v2)
+ assert @v1.eql?(@v3)
+ assert !@v1.eql?(@v4)
+ assert !@v1.eql?(@w1)
+
+ hash = { @v1 => :value }
+ assert hash.key?(@v1)
+ assert hash.key?(@v2)
+ assert hash.key?(@v3)
+ assert !hash.key?(@v4)
+ assert !hash.key?(@w1)
+ end
+
+ def test_hash
+ assert_equal @v1.hash, @v1.hash
+ assert_equal @v1.hash, @v2.hash
+ assert_equal @v1.hash, @v3.hash
+ end
+
+ def test_aref
+ assert_equal(1, @v1[0])
+ assert_equal(2, @v1[1])
+ assert_equal(3, @v1[2])
+ assert_equal(3, @v1[-1])
+ assert_equal(nil, @v1[3])
+ end
+
+ def test_size
+ assert_equal(3, @v1.size)
+ end
+
+ def test_each2
+ a = []
+ @v1.each2(@v4) {|x, y| a << [x, y] }
+ assert_equal([[1,1.0],[2,2.0],[3,3.0]], a)
+ end
+
+ def test_collect
+ a = @v1.collect {|x| x + 1 }
+ assert_equal(Vector[2,3,4], a)
+ end
+
+ def test_collect2
+ a = @v1.collect2(@v4) {|x, y| x + y }
+ assert_equal([2.0,4.0,6.0], a)
+ end
+
+ def test_map2
+ a = @v1.map2(@v4) {|x, y| x + y }
+ assert_equal(Vector[2.0,4.0,6.0], a)
+ end
+
+ def test_independent?
+ assert(Vector.independent?(@v1, @w1))
+ assert(
+ Vector.independent?(
+ Vector.basis(size: 3, index: 0),
+ Vector.basis(size: 3, index: 1),
+ Vector.basis(size: 3, index: 2),
+ )
+ )
+
+ refute(Vector.independent?(@v1, Vector[2,4,6]))
+ refute(Vector.independent?(Vector[2,4], Vector[1,3], Vector[5,6]))
+
+ assert_raise(TypeError) { Vector.independent?(@v1, 3) }
+ assert_raise(Vector::ErrDimensionMismatch) { Vector.independent?(@v1, Vector[2,4]) }
+
+ assert(@v1.independent?(Vector[1,2,4], Vector[1,3,4]))
+ end
+
+ def test_mul
+ assert_equal(Vector[2,4,6], @v1 * 2)
+ assert_equal(Matrix[[1, 4, 9], [2, 8, 18], [3, 12, 27]], @v1 * Matrix[[1,4,9]])
+ assert_raise(Matrix::ErrOperationNotDefined) { @v1 * Vector[1,4,9] }
+ o = Object.new
+ def o.coerce(x)
+ [1, 1]
+ end
+ assert_equal(1, Vector[1, 2, 3] * o)
+ end
+
+ def test_add
+ assert_equal(Vector[2,4,6], @v1 + @v1)
+ assert_equal(Matrix[[2],[6],[12]], @v1 + Matrix[[1],[4],[9]])
+ o = Object.new
+ def o.coerce(x)
+ [1, 1]
+ end
+ assert_equal(2, Vector[1, 2, 3] + o)
+ end
+
+ def test_sub
+ assert_equal(Vector[0,0,0], @v1 - @v1)
+ assert_equal(Matrix[[0],[-2],[-6]], @v1 - Matrix[[1],[4],[9]])
+ o = Object.new
+ def o.coerce(x)
+ [1, 1]
+ end
+ assert_equal(0, Vector[1, 2, 3] - o)
+ end
+
+ def test_uplus
+ assert_equal(@v1, +@v1)
+ end
+
+ def test_negate
+ assert_equal(Vector[-1, -2, -3], -@v1)
+ assert_equal(@v1, -(-@v1))
+ end
+
+ def test_inner_product
+ assert_equal(1+4+9, @v1.inner_product(@v1))
+ assert_equal(1+4+9, @v1.dot(@v1))
+ end
+
+ def test_r
+ assert_equal(5, Vector[3, 4].r)
+ end
+
+ def test_covector
+ assert_equal(Matrix[[1,2,3]], @v1.covector)
+ end
+
+ def test_to_s
+ assert_equal("Vector[1, 2, 3]", @v1.to_s)
+ end
+
+ def test_inspect
+ assert_equal("Vector[1, 2, 3]", @v1.inspect)
+ end
+
+ def test_magnitude
+ assert_in_epsilon(3.7416573867739413, @v1.norm)
+ assert_in_epsilon(3.7416573867739413, @v4.norm)
+ end
+
+ def test_complex_magnitude
+ bug6966 = '[ruby-dev:46100]'
+ v = Vector[Complex(0,1), 0]
+ assert_equal(1.0, v.norm, bug6966)
+ end
+
+ def test_rational_magnitude
+ v = Vector[Rational(1,2), 0]
+ assert_equal(0.5, v.norm)
+ end
+
+ def test_cross_product
+ v = Vector[1, 0, 0].cross_product Vector[0, 1, 0]
+ assert_equal(Vector[0, 0, 1], v)
+ v2 = Vector[1, 2].cross_product
+ assert_equal(Vector[-2, 1], v2)
+ v3 = Vector[3, 5, 2, 1].cross(Vector[4, 3, 1, 8], Vector[2, 9, 4, 3])
+ assert_equal(Vector[16, -65, 139, -1], v3)
+ assert_equal Vector[0, 0, 0, 1],
+ Vector[1, 0, 0, 0].cross(Vector[0, 1, 0, 0], Vector[0, 0, 1, 0])
+ assert_equal Vector[0, 0, 0, 0, 1],
+ Vector[1, 0, 0, 0, 0].cross(Vector[0, 1, 0, 0, 0], Vector[0, 0, 1, 0, 0], Vector[0, 0, 0, 1, 0])
+ assert_raise(Vector::ErrDimensionMismatch) { Vector[1, 2, 3].cross_product(Vector[1, 4]) }
+ assert_raise(TypeError) { Vector[1, 2, 3].cross_product(42) }
+ assert_raise(ArgumentError) { Vector[1, 2].cross_product(Vector[2, -1]) }
+ assert_raise(Vector::ErrOperationNotDefined) { Vector[1].cross_product }
+ end
+
+ def test_angle_with
+ assert_in_epsilon(Math::PI, Vector[1, 0].angle_with(Vector[-1, 0]))
+ assert_in_epsilon(Math::PI/2, Vector[1, 0].angle_with(Vector[0, -1]))
+ assert_in_epsilon(Math::PI/4, Vector[2, 2].angle_with(Vector[0, 1]))
+ assert_in_delta(0.0, Vector[1, 1].angle_with(Vector[1, 1]), 0.00001)
+
+ assert_raise(Vector::ZeroVectorError) { Vector[1, 1].angle_with(Vector[0, 0]) }
+ assert_raise(Vector::ZeroVectorError) { Vector[0, 0].angle_with(Vector[1, 1]) }
+ assert_raise(Matrix::ErrDimensionMismatch) { Vector[1, 2, 3].angle_with(Vector[0, 1]) }
+ end
+end
diff --git a/jni/ruby/test/minitest/metametameta.rb b/jni/ruby/test/minitest/metametameta.rb
new file mode 100644
index 0000000..87bc56c
--- /dev/null
+++ b/jni/ruby/test/minitest/metametameta.rb
@@ -0,0 +1,69 @@
+# encoding: utf-8
+
+require 'tempfile'
+require 'stringio'
+require 'minitest/autorun'
+
+class MiniTest::Unit::TestCase
+ def clean s
+ s.gsub(/^ {6}/, '')
+ end
+end
+
+class MetaMetaMetaTestCase < MiniTest::Unit::TestCase
+ def assert_report expected, flags = %w[--seed 42]
+ header = clean <<-EOM
+ Run options: #{flags.map { |s| s =~ /\|/ ? s.inspect : s }.join " "}
+
+ # Running tests:
+
+ EOM
+
+ with_output do
+ @tu.run flags
+ end
+
+ output = @output.string.dup
+ output.sub!(/Finished tests in .*/, "Finished tests in 0.00")
+ output.sub!(/Loaded suite .*/, 'Loaded suite blah')
+
+ output.gsub!(/ = \d+.\d\d s = /, ' = 0.00 s = ')
+ output.gsub!(/0x[A-Fa-f0-9]+/, '0xXXX')
+
+ if windows? then
+ output.gsub!(/\[(?:[A-Za-z]:)?[^\]:]+:\d+\]/, '[FILE:LINE]')
+ output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in/, '\1FILE:LINE:in')
+ else
+ output.gsub!(/\[[^\]:]+:\d+\]/, '[FILE:LINE]')
+ output.gsub!(/^(\s+)[^:]+:\d+:in/, '\1FILE:LINE:in')
+ end
+
+ assert_equal header + expected, output
+ end
+
+ def setup
+ super
+ srand 42
+ MiniTest::Unit::TestCase.reset
+ @tu = MiniTest::Unit.new
+
+ MiniTest::Unit.runner = nil # protect the outer runner from the inner tests
+ end
+
+ def teardown
+ super
+ end
+
+ def with_output
+ synchronize do
+ begin
+ @output = StringIO.new("")
+ MiniTest::Unit.output = @output
+
+ yield
+ ensure
+ MiniTest::Unit.output = STDOUT
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/minitest/test_minitest_benchmark.rb b/jni/ruby/test/minitest/test_minitest_benchmark.rb
new file mode 100644
index 0000000..d04bb9a
--- /dev/null
+++ b/jni/ruby/test/minitest/test_minitest_benchmark.rb
@@ -0,0 +1,130 @@
+# encoding: utf-8
+
+require 'minitest/autorun'
+require 'minitest/benchmark'
+
+##
+# Used to verify data:
+# http://www.wolframalpha.com/examples/RegressionAnalysis.html
+
+class TestMiniTestBenchmark < MiniTest::Unit::TestCase
+ def test_cls_bench_exp
+ assert_equal [2, 4, 8, 16, 32], self.class.bench_exp(2, 32, 2)
+ end
+
+ def test_cls_bench_linear
+ assert_equal [2, 4, 6, 8, 10], self.class.bench_linear(2, 10, 2)
+ end
+
+ def test_cls_benchmark_methods
+ assert_equal [], self.class.benchmark_methods
+
+ c = Class.new(MiniTest::Unit::TestCase) do
+ def bench_blah
+ end
+ end
+
+ assert_equal ["bench_blah"], c.benchmark_methods
+ end
+
+ def test_cls_bench_range
+ assert_equal [1, 10, 100, 1_000, 10_000], self.class.bench_range
+ end
+
+ def test_fit_exponential_clean
+ x = [1.0, 2.0, 3.0, 4.0, 5.0]
+ y = x.map { |n| 1.1 * Math.exp(2.1 * n) }
+
+ assert_fit :exponential, x, y, 1.0, 1.1, 2.1
+ end
+
+ def test_fit_exponential_noisy
+ x = [1.0, 1.9, 2.6, 3.4, 5.0]
+ y = [12, 10, 8.2, 6.9, 5.9]
+
+ # verified with Numbers and R
+ assert_fit :exponential, x, y, 0.95, 13.81148, -0.1820
+ end
+
+ def test_fit_logarithmic_clean
+ x = [1.0, 2.0, 3.0, 4.0, 5.0]
+ y = x.map { |n| 1.1 + 2.1 * Math.log(n) }
+
+ assert_fit :logarithmic, x, y, 1.0, 1.1, 2.1
+ end
+
+ def test_fit_logarithmic_noisy
+ x = [1.0, 2.0, 3.0, 4.0, 5.0]
+ # Generated with
+ # y = x.map { |n| jitter = 0.999 + 0.002 * rand; (Math.log(n) ) * jitter }
+ y = [0.0, 0.6935, 1.0995, 1.3873, 1.6097]
+
+ assert_fit :logarithmic, x, y, 0.95, 0, 1
+ end
+
+ def test_fit_constant_clean
+ x = (1..5).to_a
+ y = [5.0, 5.0, 5.0, 5.0, 5.0]
+
+ assert_fit :linear, x, y, nil, 5.0, 0
+ end
+
+ def test_fit_constant_noisy
+ x = (1..5).to_a
+ y = [1.0, 1.2, 1.0, 0.8, 1.0]
+
+ # verified in numbers and R
+ assert_fit :linear, x, y, nil, 1.12, -0.04
+ end
+
+ def test_fit_linear_clean
+ # y = m * x + b where m = 2.2, b = 3.1
+ x = (1..5).to_a
+ y = x.map { |n| 2.2 * n + 3.1 }
+
+ assert_fit :linear, x, y, 1.0, 3.1, 2.2
+ end
+
+ def test_fit_linear_noisy
+ x = [ 60, 61, 62, 63, 65]
+ y = [3.1, 3.6, 3.8, 4.0, 4.1]
+
+ # verified in numbers and R
+ assert_fit :linear, x, y, 0.8315, -7.9635, 0.1878
+ end
+
+ def test_fit_power_clean
+ # y = A x ** B, where B = b and A = e ** a
+ # if, A = 1, B = 2, then
+
+ x = [1.0, 2.0, 3.0, 4.0, 5.0]
+ y = [1.0, 4.0, 9.0, 16.0, 25.0]
+
+ assert_fit :power, x, y, 1.0, 1.0, 2.0
+ end
+
+ def test_fit_power_noisy
+ # from www.engr.uidaho.edu/thompson/courses/ME330/lecture/least_squares.html
+ x = [10, 12, 15, 17, 20, 22, 25, 27, 30, 32, 35]
+ y = [95, 105, 125, 141, 173, 200, 253, 298, 385, 459, 602]
+
+ # verified in numbers
+ assert_fit :power, x, y, 0.90, 2.6217, 1.4556
+
+ # income to % of households below income amount
+ # http://library.wolfram.com/infocenter/Conferences/6461/PowerLaws.nb
+ x = [15000, 25000, 35000, 50000, 75000, 100000]
+ y = [0.154, 0.283, 0.402, 0.55, 0.733, 0.843]
+
+ # verified in numbers
+ assert_fit :power, x, y, 0.96, 3.119e-5, 0.8959
+ end
+
+ def assert_fit msg, x, y, fit, exp_a, exp_b
+ a, b, rr = send "fit_#{msg}", x, y
+
+ assert_operator rr, :>=, fit if fit
+ assert_in_delta exp_a, a
+ assert_in_delta exp_b, b
+ end
+end
diff --git a/jni/ruby/test/minitest/test_minitest_mock.rb b/jni/ruby/test/minitest/test_minitest_mock.rb
new file mode 100644
index 0000000..062cda5
--- /dev/null
+++ b/jni/ruby/test/minitest/test_minitest_mock.rb
@@ -0,0 +1,403 @@
+# encoding: utf-8
+
+require 'minitest/autorun'
+
+class TestMiniTestMock < MiniTest::Unit::TestCase
+ def setup
+ @mock = MiniTest::Mock.new.expect(:foo, nil)
+ @mock.expect(:meaning_of_life, 42)
+ end
+
+ def test_create_stub_method
+ assert_nil @mock.foo
+ end
+
+ def test_allow_return_value_specification
+ assert_equal 42, @mock.meaning_of_life
+ end
+
+ def test_blow_up_if_not_called
+ @mock.foo
+
+ util_verify_bad "expected meaning_of_life() => 42, got []"
+ end
+
+ def test_not_blow_up_if_everything_called
+ @mock.foo
+ @mock.meaning_of_life
+
+ assert @mock.verify
+ end
+
+ def test_allow_expectations_to_be_added_after_creation
+ @mock.expect(:bar, true)
+ assert @mock.bar
+ end
+
+ def test_not_verify_if_new_expected_method_is_not_called
+ @mock.foo
+ @mock.meaning_of_life
+ @mock.expect(:bar, true)
+
+ util_verify_bad "expected bar() => true, got []"
+ end
+
+ def test_blow_up_on_wrong_number_of_arguments
+ @mock.foo
+ @mock.meaning_of_life
+ @mock.expect(:sum, 3, [1, 2])
+
+ e = assert_raises ArgumentError do
+ @mock.sum
+ end
+
+ assert_equal "mocked method :sum expects 2 arguments, got 0", e.message
+ end
+
+ def test_return_mock_does_not_raise
+ retval = MiniTest::Mock.new
+ mock = MiniTest::Mock.new
+ mock.expect(:foo, retval)
+ mock.foo
+
+ assert mock.verify
+ end
+
+ def test_mock_args_does_not_raise
+ skip "non-opaque use of ==" if maglev?
+
+ arg = MiniTest::Mock.new
+ mock = MiniTest::Mock.new
+ mock.expect(:foo, nil, [arg])
+ mock.foo(arg)
+
+ assert mock.verify
+ end
+
+ def test_blow_up_on_wrong_arguments
+ @mock.foo
+ @mock.meaning_of_life
+ @mock.expect(:sum, 3, [1, 2])
+
+ e = assert_raises MockExpectationError do
+ @mock.sum(2, 4)
+ end
+
+ exp = "mocked method :sum called with unexpected arguments [2, 4]"
+ assert_equal exp, e.message
+ end
+
+ def test_expect_with_non_array_args
+ e = assert_raises ArgumentError do
+ @mock.expect :blah, 3, false
+ end
+
+ assert_equal "args must be an array", e.message
+ end
+
+ def test_respond_appropriately
+ assert @mock.respond_to?(:foo)
+ assert @mock.respond_to?(:foo, true)
+ assert @mock.respond_to?('foo')
+ assert !@mock.respond_to?(:bar)
+ end
+
+ def test_no_method_error_on_unexpected_methods
+ e = assert_raises NoMethodError do
+ @mock.bar
+ end
+
+ expected = "unmocked method :bar, expected one of [:foo, :meaning_of_life]"
+
+ assert_equal expected, e.message
+ end
+
+ def test_assign_per_mock_return_values
+ a = MiniTest::Mock.new
+ b = MiniTest::Mock.new
+
+ a.expect(:foo, :a)
+ b.expect(:foo, :b)
+
+ assert_equal :a, a.foo
+ assert_equal :b, b.foo
+ end
+
+ def test_do_not_create_stub_method_on_new_mocks
+ a = MiniTest::Mock.new
+ a.expect(:foo, :a)
+
+ assert !MiniTest::Mock.new.respond_to?(:foo)
+ end
+
+ def test_mock_is_a_blank_slate
+ @mock.expect :kind_of?, true, [Fixnum]
+ @mock.expect :==, true, [1]
+
+ assert @mock.kind_of?(Fixnum), "didn't mock :kind_of\?"
+ assert @mock == 1, "didn't mock :=="
+ end
+
+ def test_verify_allows_called_args_to_be_loosely_specified
+ mock = MiniTest::Mock.new
+ mock.expect :loose_expectation, true, [Integer]
+ mock.loose_expectation 1
+
+ assert mock.verify
+ end
+
+ def test_verify_raises_with_strict_args
+ mock = MiniTest::Mock.new
+ mock.expect :strict_expectation, true, [2]
+
+ e = assert_raises MockExpectationError do
+ mock.strict_expectation 1
+ end
+
+ exp = "mocked method :strict_expectation called with unexpected arguments [1]"
+ assert_equal exp, e.message
+ end
+
+ def test_method_missing_empty
+ mock = MiniTest::Mock.new
+
+ mock.expect :a, nil
+
+ mock.a
+
+ e = assert_raises MockExpectationError do
+ mock.a
+ end
+
+ assert_equal "No more expects available for :a: []", e.message
+ end
+
+ def test_same_method_expects_are_verified_when_all_called
+ mock = MiniTest::Mock.new
+ mock.expect :foo, nil, [:bar]
+ mock.expect :foo, nil, [:baz]
+
+ mock.foo :bar
+ mock.foo :baz
+
+ assert mock.verify
+ end
+
+ def test_same_method_expects_blow_up_when_not_all_called
+ mock = MiniTest::Mock.new
+ mock.expect :foo, nil, [:bar]
+ mock.expect :foo, nil, [:baz]
+
+ mock.foo :bar
+
+ e = assert_raises(MockExpectationError) { mock.verify }
+
+ exp = "expected foo(:baz) => nil, got [foo(:bar) => nil]"
+
+ assert_equal exp, e.message
+ end
+
+ def test_verify_passes_when_mock_block_returns_true
+ mock = MiniTest::Mock.new
+ mock.expect :foo, nil do
+ true
+ end
+
+ mock.foo
+
+ assert mock.verify
+ end
+
+ def test_mock_block_is_passed_function_params
+ arg1, arg2, arg3 = :bar, [1,2,3], {:a => 'a'}
+ mock = MiniTest::Mock.new
+ mock.expect :foo, nil do |a1, a2, a3|
+ a1 == arg1 &&
+ a2 == arg2 &&
+ a3 == arg3
+ end
+
+ mock.foo arg1, arg2, arg3
+
+ assert mock.verify
+ end
+
+ def test_verify_fails_when_mock_block_returns_false
+ mock = MiniTest::Mock.new
+ mock.expect :foo, nil do
+ false
+ end
+
+ e = assert_raises(MockExpectationError) { mock.foo }
+ exp = "mocked method :foo failed block w/ []"
+
+ assert_equal exp, e.message
+ end
+
+ def test_mock_block_throws_if_args_passed
+ mock = MiniTest::Mock.new
+
+ e = assert_raises(ArgumentError) do
+ mock.expect :foo, nil, [:a, :b, :c] do
+ true
+ end
+ end
+
+ exp = "args ignored when block given"
+
+ assert_equal exp, e.message
+ end
+
+ def test_mock_returns_retval_when_called_with_block
+ mock = MiniTest::Mock.new
+ mock.expect(:foo, 32) do
+ true
+ end
+
+ rs = mock.foo
+
+ assert_equal rs, 32
+ end
+
+ def util_verify_bad exp
+ e = assert_raises MockExpectationError do
+ @mock.verify
+ end
+
+ assert_equal exp, e.message
+ end
+end
+
+require "minitest/metametameta"
+
+class TestMiniTestStub < MiniTest::Unit::TestCase
+ def setup
+ super
+ MiniTest::Unit::TestCase.reset
+
+ @tc = MiniTest::Unit::TestCase.new 'fake tc'
+ @assertion_count = 1
+ end
+
+ def teardown
+ super
+ assert_equal @assertion_count, @tc._assertions
+ end
+
+ class Time
+ def self.now
+ 24
+ end
+ end
+
+ def assert_stub val_or_callable
+ @assertion_count += 1
+
+ t = Time.now.to_i
+
+ Time.stub :now, val_or_callable do
+ @tc.assert_equal 42, Time.now
+ end
+
+ @tc.assert_operator Time.now.to_i, :>=, t
+ end
+
+ def test_stub_private_module_method
+ @assertion_count += 1
+
+ t0 = Time.now
+
+ self.stub :sleep, nil do
+ @tc.assert_nil sleep(10)
+ end
+
+ @tc.assert_operator Time.now - t0, :<=, 1
+ end
+
+ def test_stub_private_module_method_indirect
+ @assertion_count += 1
+
+ slow_clapper = Class.new do
+ def slow_clap
+ sleep 3
+ :clap
+ end
+ end.new
+
+ slow_clapper.stub :sleep, nil do |fast_clapper|
+ @tc.assert_equal :clap, fast_clapper.slow_clap # either form works
+ @tc.assert_equal :clap, slow_clapper.slow_clap # yay closures
+ end
+ end
+
+ def test_stub_public_module_method
+ Math.stub(:log10, 42.0) do
+ @tc.assert_in_delta 42.0, Math.log10(1000)
+ end
+ end
+
+ def test_stub_value
+ assert_stub 42
+ end
+
+ def test_stub_block
+ assert_stub lambda { 42 }
+ end
+
+ def test_stub_block_args
+ @assertion_count += 1
+
+ t = Time.now.to_i
+
+ Time.stub :now, lambda { |n| n * 2 } do
+ @tc.assert_equal 42, Time.now(21)
+ end
+
+ @tc.assert_operator Time.now.to_i, :>=, t
+ end
+
+ def test_stub_callable
+ obj = Object.new
+
+ def obj.call
+ 42
+ end
+
+ assert_stub obj
+ end
+
+ def test_stub_yield_self
+ obj = "foo"
+
+ val = obj.stub :to_s, "bar" do |s|
+ s.to_s
+ end
+
+ @tc.assert_equal "bar", val
+ end
+
+ def test_dynamic_method
+ @assertion_count = 2
+
+ dynamic = Class.new do
+ def self.respond_to?(meth)
+ meth == :found
+ end
+
+ def self.method_missing(meth, *args, &block)
+ if meth == :found
+ false
+ else
+ super
+ end
+ end
+ end
+
+ val = dynamic.stub(:found, true) do |s|
+ s.found
+ end
+
+ @tc.assert_equal true, val
+ @tc.assert_equal false, dynamic.found
+ end
+end
diff --git a/jni/ruby/test/minitest/test_minitest_unit.rb b/jni/ruby/test/minitest/test_minitest_unit.rb
new file mode 100644
index 0000000..6a01b6c
--- /dev/null
+++ b/jni/ruby/test/minitest/test_minitest_unit.rb
@@ -0,0 +1,1803 @@
+# encoding: utf-8
+
+require 'pathname'
+require 'minitest/metametameta'
+
+module MyModule; end
+class AnError < StandardError; include MyModule; end
+class ImmutableString < String; def inspect; super.freeze; end; end
+
+class TestMiniTestUnit < MetaMetaMetaTestCase
+ pwd = Pathname.new File.expand_path Dir.pwd
+ basedir = Pathname.new(File.expand_path "lib/minitest") + 'mini'
+ basedir = basedir.relative_path_from(pwd).to_s
+ MINITEST_BASE_DIR = basedir[/\A\./] ? basedir : "./#{basedir}"
+ BT_MIDDLE = ["#{MINITEST_BASE_DIR}/test.rb:161:in `each'",
+ "#{MINITEST_BASE_DIR}/test.rb:158:in `each'",
+ "#{MINITEST_BASE_DIR}/test.rb:139:in `run'",
+ "#{MINITEST_BASE_DIR}/test.rb:106:in `run'"]
+
+ def test_class_puke_with_assertion_failed
+ exception = MiniTest::Assertion.new "Oh no!"
+ exception.set_backtrace ["unhappy"]
+ assert_equal 'F', @tu.puke('SomeClass', 'method_name', exception)
+ assert_equal 1, @tu.failures
+ assert_match(/^Failure.*Oh no!/m, @tu.report.first)
+ assert_match("SomeClass#method_name [unhappy]", @tu.report.first)
+ end
+
+ def test_class_puke_with_assertion_failed_and_long_backtrace
+ bt = (["test/test_some_class.rb:615:in `method_name'",
+ "#{MINITEST_BASE_DIR}/unit.rb:140:in `assert_raises'",
+ "test/test_some_class.rb:615:in `each'",
+ "test/test_some_class.rb:614:in `test_method_name'",
+ "#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] +
+ BT_MIDDLE +
+ ["#{MINITEST_BASE_DIR}/test.rb:29"])
+ bt = util_expand_bt bt
+
+ ex_location = util_expand_bt(["test/test_some_class.rb:615"]).first
+
+ exception = MiniTest::Assertion.new "Oh no!"
+ exception.set_backtrace bt
+ assert_equal 'F', @tu.puke('TestSomeClass', 'test_method_name', exception)
+ assert_equal 1, @tu.failures
+ assert_match(/^Failure.*Oh no!/m, @tu.report.first)
+ assert_match("TestSomeClass#test_method_name [#{ex_location}]", @tu.report.first)
+ end
+
+ def test_class_puke_with_assertion_failed_and_user_defined_assertions
+ bt = (["lib/test/my/util.rb:16:in `another_method_name'",
+ "#{MINITEST_BASE_DIR}/unit.rb:140:in `assert_raises'",
+ "lib/test/my/util.rb:15:in `block in assert_something'",
+ "lib/test/my/util.rb:14:in `each'",
+ "lib/test/my/util.rb:14:in `assert_something'",
+ "test/test_some_class.rb:615:in `each'",
+ "test/test_some_class.rb:614:in `test_method_name'",
+ "#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] +
+ BT_MIDDLE +
+ ["#{MINITEST_BASE_DIR}/test.rb:29"])
+ bt = util_expand_bt bt
+
+ ex_location = util_expand_bt(["test/test_some_class.rb:615"]).first
+
+ exception = MiniTest::Assertion.new "Oh no!"
+ exception.set_backtrace bt
+ assert_equal 'F', @tu.puke('TestSomeClass', 'test_method_name', exception)
+ assert_equal 1, @tu.failures
+ assert_match(/^Failure.*Oh no!/m, @tu.report.first)
+ assert_match("TestSomeClass#test_method_name [#{ex_location}]", @tu.report.first)
+ end
+
+ def test_class_puke_with_failure_and_flunk_in_backtrace
+ exception = begin
+ MiniTest::Unit::TestCase.new('fake tc').flunk
+ rescue MiniTest::Assertion => failure
+ failure
+ end
+ assert_equal 'F', @tu.puke('SomeClass', 'method_name', exception)
+ refute @tu.report.any?{|line| line =~ /in .flunk/}
+ end
+
+ def test_class_puke_with_flunk_and_user_defined_assertions
+ bt = (["lib/test/my/util.rb:16:in `flunk'",
+ "#{MINITEST_BASE_DIR}/unit.rb:140:in `assert_raises'",
+ "lib/test/my/util.rb:15:in `block in assert_something'",
+ "lib/test/my/util.rb:14:in `each'",
+ "lib/test/my/util.rb:14:in `assert_something'",
+ "test/test_some_class.rb:615:in `each'",
+ "test/test_some_class.rb:614:in `test_method_name'",
+ "#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] +
+ BT_MIDDLE +
+ ["#{MINITEST_BASE_DIR}/test.rb:29"])
+ bt = util_expand_bt bt
+
+ ex_location = util_expand_bt(["test/test_some_class.rb:615"]).first
+
+ exception = MiniTest::Assertion.new "Oh no!"
+ exception.set_backtrace bt
+ assert_equal 'F', @tu.puke('TestSomeClass', 'test_method_name', exception)
+ assert_equal 1, @tu.failures
+ assert_match(/^Failure.*Oh no!/m, @tu.report.first)
+ assert_match("TestSomeClass#test_method_name [#{ex_location}]", @tu.report.first)
+ end
+
+ def test_class_puke_with_non_failure_exception
+ exception = Exception.new("Oh no again!")
+ assert_equal 'E', @tu.puke('SomeClass', 'method_name', exception)
+ assert_equal 1, @tu.errors
+ assert_match(/^Exception.*Oh no again!/m, @tu.report.first)
+ end
+
+ def test_filter_backtrace
+ # this is a semi-lame mix of relative paths.
+ # I cheated by making the autotest parts not have ./
+ bt = (["lib/autotest.rb:571:in `add_exception'",
+ "test/test_autotest.rb:62:in `test_add_exception'",
+ "#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] +
+ BT_MIDDLE +
+ ["#{MINITEST_BASE_DIR}/test.rb:29",
+ "test/test_autotest.rb:422"])
+ bt = util_expand_bt bt
+
+ ex = ["lib/autotest.rb:571:in `add_exception'",
+ "test/test_autotest.rb:62:in `test_add_exception'"]
+ ex = util_expand_bt ex
+
+ fu = MiniTest::filter_backtrace(bt)
+
+ assert_equal ex, fu
+ end
+
+ def test_filter_backtrace_all_unit
+ bt = (["#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] +
+ BT_MIDDLE +
+ ["#{MINITEST_BASE_DIR}/test.rb:29"])
+ ex = bt.clone
+ fu = MiniTest::filter_backtrace(bt)
+ assert_equal ex, fu
+ end
+
+ def test_filter_backtrace_unit_starts
+ bt = (["#{MINITEST_BASE_DIR}/test.rb:165:in `__send__'"] +
+ BT_MIDDLE +
+ ["#{MINITEST_BASE_DIR}/mini/test.rb:29",
+ "-e:1"])
+
+ bt = util_expand_bt bt
+
+ ex = ["-e:1"]
+ fu = MiniTest::filter_backtrace bt
+ assert_equal ex, fu
+ end
+
+ def test_default_runner_is_minitest_unit
+ assert_instance_of MiniTest::Unit, MiniTest::Unit.runner
+ end
+
+
+ def test_passed_eh_teardown_good
+ test_class = Class.new MiniTest::Unit::TestCase do
+ def teardown; assert true; end
+ def test_omg; assert true; end
+ end
+
+ test = test_class.new :test_omg
+ test.run @tu
+ assert test.passed?
+ end
+
+ def test_passed_eh_teardown_skipped
+ test_class = Class.new MiniTest::Unit::TestCase do
+ def teardown; assert true; end
+ def test_omg; skip "bork"; end
+ end
+
+ test = test_class.new :test_omg
+ test.run @tu
+ assert test.passed?
+ end
+
+ def test_passed_eh_teardown_flunked
+ test_class = Class.new MiniTest::Unit::TestCase do
+ def teardown; flunk; end
+ def test_omg; assert true; end
+ end
+
+ test = test_class.new :test_omg
+ test.run @tu
+ refute test.passed?
+ end
+
+ def util_expand_bt bt
+ bt.map { |f| (f =~ /^\./) ? File.expand_path(f) : f }
+ end
+end
+
+class TestMiniTestUnitInherited < MetaMetaMetaTestCase
+ def with_overridden_include
+ Class.class_eval do
+ def inherited_with_hacks klass
+ throw :inherited_hook
+ end
+
+ alias inherited_without_hacks inherited
+ alias inherited inherited_with_hacks
+ alias IGNORE_ME! inherited # 1.8 bug. god I love venture bros
+ end
+
+ yield
+ ensure
+ Class.class_eval do
+ alias inherited inherited_without_hacks
+
+ undef_method :inherited_with_hacks
+ undef_method :inherited_without_hacks
+ end
+
+ refute_respond_to Class, :inherited_with_hacks
+ refute_respond_to Class, :inherited_without_hacks
+ end
+
+ def test_inherited_hook_plays_nice_with_others
+ with_overridden_include do
+ assert_throws :inherited_hook do
+ Class.new MiniTest::Unit::TestCase
+ end
+ end
+ end
+end
+
+class TestMiniTestRunner < MetaMetaMetaTestCase
+ # do not parallelize this suite... it just can't handle it.
+
+ def test_class_test_suites
+ @assertion_count = 0
+
+ tc = Class.new(MiniTest::Unit::TestCase)
+
+ assert_equal 1, MiniTest::Unit::TestCase.test_suites.size
+ assert_equal [tc], MiniTest::Unit::TestCase.test_suites
+ end
+
+ def test_run_test
+ Class.new MiniTest::Unit::TestCase do
+ attr_reader :foo
+
+ def run_test name
+ @foo = "hi mom!"
+ super
+ @foo = "okay"
+ end
+
+ def test_something
+ assert_equal "hi mom!", foo
+ end
+ end
+
+ expected = clean <<-EOM
+ .
+
+ Finished tests in 0.00
+
+ 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
+ EOM
+
+ assert_report expected
+ end
+
+ def test_run_error
+ Class.new MiniTest::Unit::TestCase do
+ def test_something
+ assert true
+ end
+
+ def test_error
+ raise "unhandled exception"
+ end
+ end
+
+ expected = clean <<-EOM
+ E.
+
+ Finished tests in 0.00
+
+ 1) Error:
+ #<Class:0xXXX>#test_error:
+ RuntimeError: unhandled exception
+ FILE:LINE:in \`test_error\'
+
+ 2 tests, 1 assertions, 0 failures, 1 errors, 0 skips
+ EOM
+
+ assert_report expected
+ end
+
+ def test_run_error_teardown
+ Class.new MiniTest::Unit::TestCase do
+ def test_something
+ assert true
+ end
+
+ def teardown
+ raise "unhandled exception"
+ end
+ end
+
+ expected = clean <<-EOM
+ E
+
+ Finished tests in 0.00
+
+ 1) Error:
+ #<Class:0xXXX>#test_something:
+ RuntimeError: unhandled exception
+ FILE:LINE:in \`teardown\'
+
+ 1 tests, 1 assertions, 0 failures, 1 errors, 0 skips
+ EOM
+
+ assert_report expected
+ end
+
+ def test_run_failing
+ Class.new MiniTest::Unit::TestCase do
+ def test_something
+ assert true
+ end
+
+ def test_failure
+ assert false
+ end
+ end
+
+ expected = clean <<-EOM
+ F.
+
+ Finished tests in 0.00
+
+ 1) Failure:
+ #<Class:0xXXX>#test_failure [FILE:LINE]:
+ Failed assertion, no message given.
+
+ 2 tests, 2 assertions, 1 failures, 0 errors, 0 skips
+ EOM
+
+ assert_report expected
+ end
+
+ def test_run_failing_filtered
+ Class.new MiniTest::Unit::TestCase do
+ def test_something
+ assert true
+ end
+
+ def test_failure
+ assert false
+ end
+ end
+
+ expected = clean <<-EOM
+ .
+
+ Finished tests in 0.00
+
+ 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
+ EOM
+
+ assert_report expected, %w[--name /some|thing/ --seed 42]
+ end
+
+ def assert_filtering name, expected, a = false
+ args = %W[--name #{name} --seed 42]
+
+ alpha = Class.new MiniTest::Unit::TestCase do
+ define_method :test_something do
+ assert a
+ end
+ end
+ Object.const_set(:Alpha, alpha)
+
+ beta = Class.new MiniTest::Unit::TestCase do
+ define_method :test_something do
+ assert true
+ end
+ end
+ Object.const_set(:Beta, beta)
+
+ assert_report expected, args
+ ensure
+ Object.send :remove_const, :Alpha
+ Object.send :remove_const, :Beta
+ end
+
+ def test_run_filtered_including_suite_name
+ expected = clean <<-EOM
+ .
+
+ Finished tests in 0.00
+
+ 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
+ EOM
+
+ assert_filtering "/Beta#test_something/", expected
+ end
+
+ def test_run_filtered_including_suite_name_string
+ expected = clean <<-EOM
+ .
+
+ Finished tests in 0.00
+
+ 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
+ EOM
+
+ assert_filtering "Beta#test_something", expected
+ end
+
+ def test_run_filtered_string_method_only
+ expected = clean <<-EOM
+ ..
+
+ Finished tests in 0.00
+
+ 2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
+ EOM
+
+ assert_filtering "test_something", expected, :pass
+ end
+
+ def test_run_passing
+ Class.new MiniTest::Unit::TestCase do
+ def test_something
+ assert true
+ end
+ end
+
+ expected = clean <<-EOM
+ .
+
+ Finished tests in 0.00
+
+ 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
+ EOM
+
+ assert_report expected
+ end
+
+ def test_run_skip
+ Class.new MiniTest::Unit::TestCase do
+ def test_something
+ assert true
+ end
+
+ def test_skip
+ skip "not yet"
+ end
+ end
+
+ expected = clean <<-EOM
+ S.
+
+ Finished tests in 0.00
+
+ 2 tests, 1 assertions, 0 failures, 0 errors, 1 skips
+ EOM
+
+ assert_report expected
+ end
+
+ def test_run_skip_verbose
+ Class.new MiniTest::Unit::TestCase do
+ def test_something
+ assert true
+ end
+
+ def test_skip
+ skip "not yet"
+ end
+ end
+
+ expected = clean <<-EOM
+ #<Class:0xXXX>#test_skip = 0.00 s = S
+ #<Class:0xXXX>#test_something = 0.00 s = .
+
+
+ Finished tests in 0.00
+
+ 1) Skipped:
+ #<Class:0xXXX>#test_skip [FILE:LINE]:
+ not yet
+
+ 2 tests, 1 assertions, 0 failures, 0 errors, 1 skips
+ EOM
+
+ assert_report expected, %w[--seed 42 --verbose]
+ end
+
+ def test_run_with_other_runner
+ MiniTest::Unit.runner = Class.new MiniTest::Unit do
+ def _run_suite suite, type
+ suite.before_suite # Run once before each suite
+ super suite, type
+ end
+ end.new
+
+ Class.new MiniTest::Unit::TestCase do
+ def self.name; "wacky!" end
+
+ def self.before_suite
+ MiniTest::Unit.output.puts "Running #{self.name} tests"
+ @@foo = 1
+ end
+
+ def test_something
+ assert_equal 1, @@foo
+ end
+
+ def test_something_else
+ assert_equal 1, @@foo
+ end
+ end
+
+ expected = clean <<-EOM
+ Running wacky! tests
+ ..
+
+ Finished tests in 0.00
+
+ 2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
+ EOM
+
+ assert_report expected
+ end
+
+ require 'monitor'
+
+ class Latch
+ def initialize count = 1
+ @count = count
+ @lock = Monitor.new
+ @cv = @lock.new_cond
+ end
+
+ def release
+ @lock.synchronize do
+ @count -= 1 if @count > 0
+ @cv.broadcast if @count == 0
+ end
+ end
+
+ def await
+ @lock.synchronize { @cv.wait_while { @count > 0 } }
+ end
+ end
+end
+
+class TestMiniTestUnitOrder < MetaMetaMetaTestCase
+ # do not parallelize this suite... it just can't handle it.
+
+ def test_before_setup
+ call_order = []
+ Class.new MiniTest::Unit::TestCase do
+ define_method :setup do
+ super()
+ call_order << :setup
+ end
+
+ define_method :before_setup do
+ call_order << :before_setup
+ end
+
+ def test_omg; assert true; end
+ end
+
+ with_output do
+ @tu.run %w[--seed 42]
+ end
+
+ expected = [:before_setup, :setup]
+ assert_equal expected, call_order
+ end
+
+ def test_after_teardown
+ call_order = []
+ Class.new MiniTest::Unit::TestCase do
+ define_method :teardown do
+ super()
+ call_order << :teardown
+ end
+
+ define_method :after_teardown do
+ call_order << :after_teardown
+ end
+
+ def test_omg; assert true; end
+ end
+
+ with_output do
+ @tu.run %w[--seed 42]
+ end
+
+ expected = [:teardown, :after_teardown]
+ assert_equal expected, call_order
+ end
+
+ def test_all_teardowns_are_guaranteed_to_run
+ call_order = []
+ Class.new MiniTest::Unit::TestCase do
+ define_method :after_teardown do
+ super()
+ call_order << :after_teardown
+ raise
+ end
+
+ define_method :teardown do
+ super()
+ call_order << :teardown
+ raise
+ end
+
+ define_method :before_teardown do
+ super()
+ call_order << :before_teardown
+ raise
+ end
+
+ def test_omg; assert true; end
+ end
+
+ with_output do
+ @tu.run %w[--seed 42]
+ end
+
+ expected = [:before_teardown, :teardown, :after_teardown]
+ assert_equal expected, call_order
+ end
+
+ def test_setup_and_teardown_survive_inheritance
+ call_order = []
+
+ parent = Class.new MiniTest::Unit::TestCase do
+ define_method :setup do
+ call_order << :setup_method
+ end
+
+ define_method :teardown do
+ call_order << :teardown_method
+ end
+
+ define_method :test_something do
+ call_order << :test
+ end
+ end
+
+ _ = Class.new parent
+
+ with_output do
+ @tu.run %w[--seed 42]
+ end
+
+ # Once for the parent class, once for the child
+ expected = [:setup_method, :test, :teardown_method] * 2
+
+ assert_equal expected, call_order
+ end
+end
+
+class TestMiniTestUnitTestCase < MiniTest::Unit::TestCase
+ # do not call parallelize_me! - teardown accesses @tc._assertions
+ # which is not threadsafe. Nearly every method in here is an
+ # assertion test so it isn't worth splitting it out further.
+
+ RUBY18 = ! defined? Encoding
+
+ def setup
+ super
+
+ MiniTest::Unit::TestCase.reset
+
+ @tc = MiniTest::Unit::TestCase.new 'fake tc'
+ @zomg = "zomg ponies!"
+ @assertion_count = 1
+ end
+
+ def teardown
+ assert_equal(@assertion_count, @tc._assertions,
+ "expected #{@assertion_count} assertions to be fired during the test, not #{@tc._assertions}") if @tc.passed?
+ end
+
+ def non_verbose
+ orig_verbose = $VERBOSE
+ $VERBOSE = false
+
+ yield
+ ensure
+ $VERBOSE = orig_verbose
+ end
+
+ def test_assert
+ @assertion_count = 2
+
+ @tc.assert_equal true, @tc.assert(true), "returns true on success"
+ end
+
+ def test_assert__triggered
+ util_assert_triggered "Failed assertion, no message given." do
+ @tc.assert false
+ end
+ end
+
+ def test_assert__triggered_message
+ util_assert_triggered @zomg do
+ @tc.assert false, @zomg
+ end
+ end
+
+ def test_assert_empty
+ @assertion_count = 2
+
+ @tc.assert_empty []
+ end
+
+ def test_assert_empty_triggered
+ @assertion_count = 2
+
+ util_assert_triggered "Expected [1] to be empty." do
+ @tc.assert_empty [1]
+ end
+ end
+
+ def test_assert_equal
+ @tc.assert_equal 1, 1
+ end
+
+ def test_assert_equal_different_collection_array_hex_invisible
+ object1 = Object.new
+ object2 = Object.new
+ msg = "No visible difference in the Array#inspect output.
+ You should look at the implementation of #== on Array or its members.
+ [#<Object:0xXXXXXX>]".gsub(/^ +/, "")
+ util_assert_triggered msg do
+ @tc.assert_equal [object1], [object2]
+ end
+ end
+
+ def test_assert_equal_different_collection_hash_hex_invisible
+ h1, h2 = {}, {}
+ h1[1] = Object.new
+ h2[1] = Object.new
+ msg = "No visible difference in the Hash#inspect output.
+ You should look at the implementation of #== on Hash or its members.
+ {1=>#<Object:0xXXXXXX>}".gsub(/^ +/, "")
+
+ util_assert_triggered msg do
+ @tc.assert_equal h1, h2
+ end
+ end
+
+ def test_assert_equal_different_diff_deactivated
+ skip "https://github.com/MagLev/maglev/issues/209" if maglev?
+
+ without_diff do
+ util_assert_triggered util_msg("haha" * 10, "blah" * 10) do
+ o1 = "haha" * 10
+ o2 = "blah" * 10
+
+ @tc.assert_equal o1, o2
+ end
+ end
+ end
+
+ def test_assert_equal_different_hex
+ c = Class.new do
+ def initialize s; @name = s; end
+ end
+
+ o1 = c.new "a"
+ o2 = c.new "b"
+ msg = "--- expected
+ +++ actual
+ @@ -1 +1 @@
+ -#<#<Class:0xXXXXXX>:0xXXXXXX @name=\"a\">
+ +#<#<Class:0xXXXXXX>:0xXXXXXX @name=\"b\">
+ ".gsub(/^ +/, "")
+
+ util_assert_triggered msg do
+ @tc.assert_equal o1, o2
+ end
+ end
+
+ def test_assert_equal_different_hex_invisible
+ o1 = Object.new
+ o2 = Object.new
+
+ msg = "No visible difference in the Object#inspect output.
+ You should look at the implementation of #== on Object or its members.
+ #<Object:0xXXXXXX>".gsub(/^ +/, "")
+
+ util_assert_triggered msg do
+ @tc.assert_equal o1, o2
+ end
+ end
+
+ def test_assert_equal_different_long
+ msg = "--- expected
+ +++ actual
+ @@ -1 +1 @@
+ -\"hahahahahahahahahahahahahahahahahahahaha\"
+ +\"blahblahblahblahblahblahblahblahblahblah\"
+ ".gsub(/^ +/, "")
+
+ util_assert_triggered msg do
+ o1 = "haha" * 10
+ o2 = "blah" * 10
+
+ @tc.assert_equal o1, o2
+ end
+ end
+
+ def test_assert_equal_different_long_invisible
+ msg = "No visible difference in the String#inspect output.
+ You should look at the implementation of #== on String or its members.
+ \"blahblahblahblahblahblahblahblahblahblah\"".gsub(/^ +/, "")
+
+ util_assert_triggered msg do
+ o1 = "blah" * 10
+ o2 = "blah" * 10
+ def o1.== o
+ false
+ end
+ @tc.assert_equal o1, o2
+ end
+ end
+
+ def test_assert_equal_different_long_msg
+ msg = "message.
+ --- expected
+ +++ actual
+ @@ -1 +1 @@
+ -\"hahahahahahahahahahahahahahahahahahahaha\"
+ +\"blahblahblahblahblahblahblahblahblahblah\"
+ ".gsub(/^ +/, "")
+
+ util_assert_triggered msg do
+ o1 = "haha" * 10
+ o2 = "blah" * 10
+ @tc.assert_equal o1, o2, "message"
+ end
+ end
+
+ def test_assert_equal_different_short
+ util_assert_triggered util_msg(1, 2) do
+ @tc.assert_equal 1, 2
+ end
+ end
+
+ def test_assert_equal_different_short_msg
+ util_assert_triggered util_msg(1, 2, "message") do
+ @tc.assert_equal 1, 2, "message"
+ end
+ end
+
+ def test_assert_equal_different_short_multiline
+ msg = "--- expected\n+++ actual\n@@ -1,2 +1,2 @@\n \"a\n-b\"\n+c\"\n"
+ util_assert_triggered msg do
+ @tc.assert_equal "a\nb", "a\nc"
+ end
+ end
+
+ def test_assert_in_delta
+ @tc.assert_in_delta 0.0, 1.0 / 1000, 0.1
+ end
+
+ def test_delta_consistency
+ @tc.assert_in_delta 0, 1, 1
+
+ util_assert_triggered "Expected |0 - 1| (1) to not be <= 1." do
+ @tc.refute_in_delta 0, 1, 1
+ end
+ end
+
+ def test_assert_in_delta_triggered
+ x = maglev? ? "9.999999xxxe-07" : "1.0e-06"
+ util_assert_triggered "Expected |0.0 - 0.001| (0.001) to be <= #{x}." do
+ @tc.assert_in_delta 0.0, 1.0 / 1000, 0.000001
+ end
+ end
+
+ def test_assert_in_epsilon
+ @assertion_count = 10
+
+ @tc.assert_in_epsilon 10000, 9991
+ @tc.assert_in_epsilon 9991, 10000
+ @tc.assert_in_epsilon 1.0, 1.001
+ @tc.assert_in_epsilon 1.001, 1.0
+
+ @tc.assert_in_epsilon 10000, 9999.1, 0.0001
+ @tc.assert_in_epsilon 9999.1, 10000, 0.0001
+ @tc.assert_in_epsilon 1.0, 1.0001, 0.0001
+ @tc.assert_in_epsilon 1.0001, 1.0, 0.0001
+
+ @tc.assert_in_epsilon(-1, -1)
+ @tc.assert_in_epsilon(-10000, -9991)
+ end
+
+ def test_epsilon_consistency
+ @tc.assert_in_epsilon 1.0, 1.001
+
+ msg = "Expected |1.0 - 1.001| (0.000999xxx) to not be <= 0.001."
+ util_assert_triggered msg do
+ @tc.refute_in_epsilon 1.0, 1.001
+ end
+ end
+
+ def test_assert_in_epsilon_triggered
+ util_assert_triggered 'Expected |10000 - 9990| (10) to be <= 9.99.' do
+ @tc.assert_in_epsilon 10000, 9990
+ end
+ end
+
+ def test_assert_in_epsilon_triggered_negative_case
+ x = (RUBY18 and not maglev?) ? "0.1" : "0.100000xxx"
+ y = maglev? ? "0.100000xxx" : "0.1"
+ util_assert_triggered "Expected |-1.1 - -1| (#{x}) to be <= #{y}." do
+ @tc.assert_in_epsilon(-1.1, -1, 0.1)
+ end
+ end
+
+ def test_assert_includes
+ @assertion_count = 2
+
+ @tc.assert_includes [true], true
+ end
+
+ def test_assert_includes_triggered
+ @assertion_count = 3
+
+ e = @tc.assert_raises MiniTest::Assertion do
+ @tc.assert_includes [true], false
+ end
+
+ expected = "Expected [true] to include false."
+ assert_equal expected, e.message
+ end
+
+ def test_assert_instance_of
+ @tc.assert_instance_of String, "blah"
+ end
+
+ def test_assert_instance_of_triggered
+ util_assert_triggered 'Expected "blah" to be an instance of Array, not String.' do
+ @tc.assert_instance_of Array, "blah"
+ end
+ end
+
+ def test_assert_kind_of
+ @tc.assert_kind_of String, "blah"
+ end
+
+ def test_assert_kind_of_triggered
+ util_assert_triggered 'Expected "blah" to be a kind of Array, not String.' do
+ @tc.assert_kind_of Array, "blah"
+ end
+ end
+
+ def test_assert_match
+ @assertion_count = 2
+ @tc.assert_match(/\w+/, "blah blah blah")
+ end
+
+ def test_assert_match_matcher_object
+ @assertion_count = 2
+
+ pattern = Object.new
+ def pattern.=~(other) true end
+
+ @tc.assert_match pattern, 5
+ end
+
+ def test_assert_match_matchee_to_str
+ @assertion_count = 2
+
+ obj = Object.new
+ def obj.to_str; "blah" end
+
+ @tc.assert_match "blah", obj
+ end
+
+ def test_assert_match_object_triggered
+ @assertion_count = 2
+
+ pattern = Object.new
+ def pattern.=~(other) false end
+ def pattern.inspect; "[Object]" end
+
+ util_assert_triggered 'Expected [Object] to match 5.' do
+ @tc.assert_match pattern, 5
+ end
+ end
+
+ def test_assert_match_triggered
+ @assertion_count = 2
+ util_assert_triggered 'Expected /\d+/ to match "blah blah blah".' do
+ @tc.assert_match(/\d+/, "blah blah blah")
+ end
+ end
+
+ def test_assert_nil
+ @tc.assert_nil nil
+ end
+
+ def test_assert_nil_triggered
+ util_assert_triggered 'Expected 42 to be nil.' do
+ @tc.assert_nil 42
+ end
+ end
+
+ def test_assert_operator
+ @tc.assert_operator 2, :>, 1
+ end
+
+ def test_assert_operator_bad_object
+ bad = Object.new
+ def bad.==(other) true end
+
+ @tc.assert_operator bad, :equal?, bad
+ end
+
+ def test_assert_operator_triggered
+ util_assert_triggered "Expected 2 to be < 1." do
+ @tc.assert_operator 2, :<, 1
+ end
+ end
+
+ def test_assert_output_both
+ @assertion_count = 2
+
+ @tc.assert_output "yay", "blah" do
+ print "yay"
+ $stderr.print "blah"
+ end
+ end
+
+ def test_assert_output_both_regexps
+ @assertion_count = 4
+
+ @tc.assert_output(/y.y/, /bl.h/) do
+ print "yay"
+ $stderr.print "blah"
+ end
+ end
+
+ def test_assert_output_err
+ @tc.assert_output nil, "blah" do
+ $stderr.print "blah"
+ end
+ end
+
+ def test_assert_output_neither
+ @assertion_count = 0
+
+ @tc.assert_output do
+ # do nothing
+ end
+ end
+
+ def test_assert_output_out
+ @tc.assert_output "blah" do
+ print "blah"
+ end
+ end
+
+ def test_assert_output_triggered_both
+ util_assert_triggered util_msg("blah", "blah blah", "In stderr") do
+ @tc.assert_output "yay", "blah" do
+ print "boo"
+ $stderr.print "blah blah"
+ end
+ end
+ end
+
+ def test_assert_output_triggered_err
+ util_assert_triggered util_msg("blah", "blah blah", "In stderr") do
+ @tc.assert_output nil, "blah" do
+ $stderr.print "blah blah"
+ end
+ end
+ end
+
+ def test_assert_output_triggered_out
+ util_assert_triggered util_msg("blah", "blah blah", "In stdout") do
+ @tc.assert_output "blah" do
+ print "blah blah"
+ end
+ end
+ end
+
+ def test_assert_predicate
+ @tc.assert_predicate "", :empty?
+ end
+
+ def test_assert_predicate_triggered
+ util_assert_triggered 'Expected "blah" to be empty?.' do
+ @tc.assert_predicate "blah", :empty?
+ end
+ end
+
+ def test_assert_raises
+ @tc.assert_raises RuntimeError do
+ raise "blah"
+ end
+ end
+
+ def test_assert_raises_module
+ @tc.assert_raises MyModule do
+ raise AnError
+ end
+ end
+
+ ##
+ # *sigh* This is quite an odd scenario, but it is from real (albeit
+ # ugly) test code in ruby-core:
+ #
+ # http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=29259
+
+ def test_assert_raises_skip
+ @assertion_count = 0
+
+ util_assert_triggered "skipped", MiniTest::Skip do
+ @tc.assert_raises ArgumentError do
+ begin
+ raise "blah"
+ rescue
+ skip "skipped"
+ end
+ end
+ end
+ end
+
+ def test_assert_raises_triggered_different
+ e = assert_raises MiniTest::Assertion do
+ @tc.assert_raises RuntimeError do
+ raise SyntaxError, "icky"
+ end
+ end
+
+ expected = clean <<-EOM.chomp
+ [RuntimeError] exception expected, not
+ Class: <SyntaxError>
+ Message: <\"icky\">
+ ---Backtrace---
+ FILE:LINE:in \`test_assert_raises_triggered_different\'
+ ---------------
+ EOM
+
+ actual = e.message.gsub(/^.+:\d+/, 'FILE:LINE')
+ actual.gsub!(/block \(\d+ levels\) in /, '') if RUBY_VERSION >= '1.9.0'
+
+ assert_equal expected, actual
+ end
+
+ def test_assert_raises_triggered_different_msg
+ e = assert_raises MiniTest::Assertion do
+ @tc.assert_raises RuntimeError, "XXX" do
+ raise SyntaxError, "icky"
+ end
+ end
+
+ expected = clean <<-EOM
+ XXX.
+ [RuntimeError] exception expected, not
+ Class: <SyntaxError>
+ Message: <\"icky\">
+ ---Backtrace---
+ FILE:LINE:in \`test_assert_raises_triggered_different_msg\'
+ ---------------
+ EOM
+
+ actual = e.message.gsub(/^.+:\d+/, 'FILE:LINE')
+ actual.gsub!(/block \(\d+ levels\) in /, '') if RUBY_VERSION >= '1.9.0'
+
+ assert_equal expected.chomp, actual
+ end
+
+ def test_assert_raises_triggered_none
+ e = assert_raises MiniTest::Assertion do
+ @tc.assert_raises MiniTest::Assertion do
+ # do nothing
+ end
+ end
+
+ expected = "MiniTest::Assertion expected but nothing was raised."
+
+ assert_equal expected, e.message
+ end
+
+ def test_assert_raises_triggered_none_msg
+ e = assert_raises MiniTest::Assertion do
+ @tc.assert_raises MiniTest::Assertion, "XXX" do
+ # do nothing
+ end
+ end
+
+ expected = "XXX.\nMiniTest::Assertion expected but nothing was raised."
+
+ assert_equal expected, e.message
+ end
+
+ def test_assert_raises_triggered_subclass
+ e = assert_raises MiniTest::Assertion do
+ @tc.assert_raises StandardError do
+ raise AnError
+ end
+ end
+
+ expected = clean <<-EOM.chomp
+ [StandardError] exception expected, not
+ Class: <AnError>
+ Message: <\"AnError\">
+ ---Backtrace---
+ FILE:LINE:in \`test_assert_raises_triggered_subclass\'
+ ---------------
+ EOM
+
+ actual = e.message.gsub(/^.+:\d+/, 'FILE:LINE')
+ actual.gsub!(/block \(\d+ levels\) in /, '') if RUBY_VERSION >= '1.9.0'
+
+ assert_equal expected, actual
+ end
+
+ def test_assert_respond_to
+ @tc.assert_respond_to "blah", :empty?
+ end
+
+ def test_assert_respond_to_triggered
+ util_assert_triggered 'Expected "blah" (String) to respond to #rawr!.' do
+ @tc.assert_respond_to "blah", :rawr!
+ end
+ end
+
+ def test_assert_same
+ @assertion_count = 3
+
+ o = "blah"
+ @tc.assert_same 1, 1
+ @tc.assert_same :blah, :blah
+ @tc.assert_same o, o
+ end
+
+ def test_assert_same_triggered
+ @assertion_count = 2
+
+ util_assert_triggered 'Expected 2 (oid=N) to be the same as 1 (oid=N).' do
+ @tc.assert_same 1, 2
+ end
+
+ s1 = "blah"
+ s2 = "blah"
+
+ util_assert_triggered 'Expected "blah" (oid=N) to be the same as "blah" (oid=N).' do
+ @tc.assert_same s1, s2
+ end
+ end
+
+ def test_assert_send
+ @tc.assert_send [1, :<, 2]
+ end
+
+ def test_assert_send_bad
+ util_assert_triggered "Expected 1.>(*[2]) to return true." do
+ @tc.assert_send [1, :>, 2]
+ end
+ end
+
+ def test_assert_silent
+ @assertion_count = 2
+
+ @tc.assert_silent do
+ # do nothing
+ end
+ end
+
+ def test_assert_silent_triggered_err
+ util_assert_triggered util_msg("", "blah blah", "In stderr") do
+ @tc.assert_silent do
+ $stderr.print "blah blah"
+ end
+ end
+ end
+
+ def test_assert_silent_triggered_out
+ @assertion_count = 2
+
+ util_assert_triggered util_msg("", "blah blah", "In stdout") do
+ @tc.assert_silent do
+ print "blah blah"
+ end
+ end
+ end
+
+ def test_assert_throws
+ @tc.assert_throws :blah do
+ throw :blah
+ end
+ end
+
+ def test_assert_throws_different
+ util_assert_triggered 'Expected :blah to have been thrown, not :not_blah.' do
+ @tc.assert_throws :blah do
+ throw :not_blah
+ end
+ end
+ end
+
+ def test_assert_throws_unthrown
+ util_assert_triggered 'Expected :blah to have been thrown.' do
+ @tc.assert_throws :blah do
+ # do nothing
+ end
+ end
+ end
+
+ def test_capture_io
+ @assertion_count = 0
+
+ non_verbose do
+ out, err = capture_io do
+ puts 'hi'
+ $stderr.puts 'bye!'
+ end
+
+ assert_equal "hi\n", out
+ assert_equal "bye!\n", err
+ end
+ end
+
+ def test_capture_subprocess_io
+ @assertion_count = 0
+
+ non_verbose do
+ out, err = capture_subprocess_io do
+ system("echo", "hi")
+ system("echo", "bye!", out: :err)
+ end
+
+ assert_equal "hi\n", out
+ assert_equal "bye!\n", err
+ end
+ end
+
+ def test_class_asserts_match_refutes
+ @assertion_count = 0
+
+ methods = MiniTest::Assertions.public_instance_methods
+ methods.map! { |m| m.to_s } if Symbol === methods.first
+
+ # These don't have corresponding refutes _on purpose_. They're
+ # useless and will never be added, so don't bother.
+ ignores = %w[assert_output assert_raises assert_send
+ assert_silent assert_throws]
+
+ # These are test/unit methods. I'm not actually sure why they're still here
+ ignores += %w[assert_no_match assert_not_equal assert_not_nil
+ assert_not_same assert_nothing_raised
+ assert_nothing_thrown assert_raise]
+
+ asserts = methods.grep(/^assert/).sort - ignores
+ refutes = methods.grep(/^refute/).sort - ignores
+
+ assert_empty refutes.map { |n| n.sub(/^refute/, 'assert') } - asserts
+ assert_empty asserts.map { |n| n.sub(/^assert/, 'refute') } - refutes
+ end
+
+ def test_flunk
+ util_assert_triggered 'Epic Fail!' do
+ @tc.flunk
+ end
+ end
+
+ def test_flunk_message
+ util_assert_triggered @zomg do
+ @tc.flunk @zomg
+ end
+ end
+
+ def test_message
+ @assertion_count = 0
+
+ assert_equal "blah2.", @tc.message { "blah2" }.call
+ assert_equal "blah2.", @tc.message("") { "blah2" }.call
+ assert_equal "blah1.\nblah2.", @tc.message(:blah1) { "blah2" }.call
+ assert_equal "blah1.\nblah2.", @tc.message("blah1") { "blah2" }.call
+
+ message = proc { "blah1" }
+ assert_equal "blah1.\nblah2.", @tc.message(message) { "blah2" }.call
+
+ message = @tc.message { "blah1" }
+ assert_equal "blah1.\nblah2.", @tc.message(message) { "blah2" }.call
+ end
+
+ def test_message_message
+ util_assert_triggered "whoops.\nExpected: 1\n Actual: 2" do
+ @tc.assert_equal 1, 2, message { "whoops" }
+ end
+ end
+
+ def test_message_lambda
+ util_assert_triggered "whoops.\nExpected: 1\n Actual: 2" do
+ @tc.assert_equal 1, 2, lambda { "whoops" }
+ end
+ end
+
+ def test_message_deferred
+ @assertion_count, var = 0, nil
+
+ msg = message { var = "blah" }
+
+ assert_nil var
+
+ msg.call
+
+ assert_equal "blah", var
+ end
+
+ def test_pass
+ @tc.pass
+ end
+
+ def test_prints
+ printer = Class.new { extend MiniTest::Assertions }
+ @tc.assert_equal '"test"', printer.mu_pp(ImmutableString.new 'test')
+ end
+
+ def test_refute
+ @assertion_count = 2
+
+ @tc.assert_equal false, @tc.refute(false), "returns false on success"
+ end
+
+ def test_refute_empty
+ @assertion_count = 2
+
+ @tc.refute_empty [1]
+ end
+
+ def test_refute_empty_triggered
+ @assertion_count = 2
+
+ util_assert_triggered "Expected [] to not be empty." do
+ @tc.refute_empty []
+ end
+ end
+
+ def test_refute_equal
+ @tc.refute_equal "blah", "yay"
+ end
+
+ def test_refute_equal_triggered
+ util_assert_triggered 'Expected "blah" to not be equal to "blah".' do
+ @tc.refute_equal "blah", "blah"
+ end
+ end
+
+ def test_refute_in_delta
+ @tc.refute_in_delta 0.0, 1.0 / 1000, 0.000001
+ end
+
+ def test_refute_in_delta_triggered
+ x = maglev? ? "0.100000xxx" : "0.1"
+ util_assert_triggered "Expected |0.0 - 0.001| (0.001) to not be <= #{x}." do
+ @tc.refute_in_delta 0.0, 1.0 / 1000, 0.1
+ end
+ end
+
+ def test_refute_in_epsilon
+ @tc.refute_in_epsilon 10000, 9990-1
+ end
+
+ def test_refute_in_epsilon_triggered
+ util_assert_triggered 'Expected |10000 - 9990| (10) to not be <= 10.0.' do
+ @tc.refute_in_epsilon 10000, 9990
+ fail
+ end
+ end
+
+ def test_refute_includes
+ @assertion_count = 2
+
+ @tc.refute_includes [true], false
+ end
+
+ def test_refute_includes_triggered
+ @assertion_count = 3
+
+ e = @tc.assert_raises MiniTest::Assertion do
+ @tc.refute_includes [true], true
+ end
+
+ expected = "Expected [true] to not include true."
+ assert_equal expected, e.message
+ end
+
+ def test_refute_instance_of
+ @tc.refute_instance_of Array, "blah"
+ end
+
+ def test_refute_instance_of_triggered
+ util_assert_triggered 'Expected "blah" to not be an instance of String.' do
+ @tc.refute_instance_of String, "blah"
+ end
+ end
+
+ def test_refute_kind_of
+ @tc.refute_kind_of Array, "blah"
+ end
+
+ def test_refute_kind_of_triggered
+ util_assert_triggered 'Expected "blah" to not be a kind of String.' do
+ @tc.refute_kind_of String, "blah"
+ end
+ end
+
+ def test_refute_match
+ @assertion_count = 2
+ @tc.refute_match(/\d+/, "blah blah blah")
+ end
+
+ def test_refute_match_matcher_object
+ @assertion_count = 2
+ @tc.refute_match Object.new, 5 # default #=~ returns false
+ end
+
+ def test_refute_match_object_triggered
+ @assertion_count = 2
+
+ pattern = Object.new
+ def pattern.=~(other) true end
+ def pattern.inspect; "[Object]" end
+
+ util_assert_triggered 'Expected [Object] to not match 5.' do
+ @tc.refute_match pattern, 5
+ end
+ end
+
+ def test_refute_match_triggered
+ @assertion_count = 2
+ util_assert_triggered 'Expected /\w+/ to not match "blah blah blah".' do
+ @tc.refute_match(/\w+/, "blah blah blah")
+ end
+ end
+
+ def test_refute_nil
+ @tc.refute_nil 42
+ end
+
+ def test_refute_nil_triggered
+ util_assert_triggered 'Expected nil to not be nil.' do
+ @tc.refute_nil nil
+ end
+ end
+
+ def test_refute_predicate
+ @tc.refute_predicate "42", :empty?
+ end
+
+ def test_refute_predicate_triggered
+ util_assert_triggered 'Expected "" to not be empty?.' do
+ @tc.refute_predicate "", :empty?
+ end
+ end
+
+ def test_refute_operator
+ @tc.refute_operator 2, :<, 1
+ end
+
+ def test_refute_operator_bad_object
+ bad = Object.new
+ def bad.==(other) true end
+
+ @tc.refute_operator true, :equal?, bad
+ end
+
+ def test_refute_operator_triggered
+ util_assert_triggered "Expected 2 to not be > 1." do
+ @tc.refute_operator 2, :>, 1
+ end
+ end
+
+ def test_refute_respond_to
+ @tc.refute_respond_to "blah", :rawr!
+ end
+
+ def test_refute_respond_to_triggered
+ util_assert_triggered 'Expected "blah" to not respond to empty?.' do
+ @tc.refute_respond_to "blah", :empty?
+ end
+ end
+
+ def test_refute_same
+ @tc.refute_same 1, 2
+ end
+
+ def test_refute_same_triggered
+ util_assert_triggered 'Expected 1 (oid=N) to not be the same as 1 (oid=N).' do
+ @tc.refute_same 1, 1
+ end
+ end
+
+ def test_skip
+ @assertion_count = 0
+
+ util_assert_triggered "haha!", MiniTest::Skip do
+ @tc.skip "haha!"
+ end
+ end
+
+ def test_test_methods_random
+ @assertion_count = 0
+
+ sample_test_case = Class.new MiniTest::Unit::TestCase do
+ def self.test_order; :random; end
+ def test_test1; assert "does not matter" end
+ def test_test2; assert "does not matter" end
+ def test_test3; assert "does not matter" end
+ end
+
+ srand 42
+ expected = case
+ when maglev? then
+ %w(test_test2 test_test3 test_test1)
+ else
+ %w(test_test2 test_test1 test_test3)
+ end
+ assert_equal expected, sample_test_case.test_methods
+ end
+
+ def test_test_methods_sorted
+ @assertion_count = 0
+
+ sample_test_case = Class.new MiniTest::Unit::TestCase do
+ def self.test_order; :sorted end
+ def test_test3; assert "does not matter" end
+ def test_test2; assert "does not matter" end
+ def test_test1; assert "does not matter" end
+ end
+
+ expected = %w(test_test1 test_test2 test_test3)
+ assert_equal expected, sample_test_case.test_methods
+ end
+
+ def test_i_suck_and_my_tests_are_order_dependent_bang_sets_test_order_alpha
+ @assertion_count = 0
+
+ shitty_test_case = Class.new MiniTest::Unit::TestCase
+
+ shitty_test_case.i_suck_and_my_tests_are_order_dependent!
+
+ assert_equal :alpha, shitty_test_case.test_order
+ end
+
+ def test_i_suck_and_my_tests_are_order_dependent_bang_does_not_warn
+ @assertion_count = 0
+
+ shitty_test_case = Class.new MiniTest::Unit::TestCase
+
+ def shitty_test_case.test_order ; :lol end
+
+ assert_silent do
+ shitty_test_case.i_suck_and_my_tests_are_order_dependent!
+ end
+ end
+
+ def util_assert_triggered expected, klass = MiniTest::Assertion
+ e = assert_raises klass do
+ yield
+ end
+
+ msg = e.message.sub(/(---Backtrace---).*/m, '\1')
+ msg.gsub!(/\(oid=[-0-9]+\)/, '(oid=N)')
+ msg.gsub!(/(\d\.\d{6})\d+/, '\1xxx') # normalize: ruby version, impl, platform
+
+ assert_equal expected, msg
+ end
+
+ def util_msg exp, act, msg = nil
+ s = "Expected: #{exp.inspect}\n Actual: #{act.inspect}"
+ s = "#{msg}.\n#{s}" if msg
+ s
+ end
+
+ def without_diff
+ old_diff = MiniTest::Assertions.diff
+ MiniTest::Assertions.diff = nil
+
+ yield
+ ensure
+ MiniTest::Assertions.diff = old_diff
+ end
+end
+
+class TestMiniTestGuard < MiniTest::Unit::TestCase
+ def test_mri_eh
+ assert self.class.mri? "ruby blah"
+ assert self.mri? "ruby blah"
+ end
+
+ def test_jruby_eh
+ assert self.class.jruby? "java"
+ assert self.jruby? "java"
+ end
+
+ def test_rubinius_eh
+ assert self.class.rubinius? "rbx"
+ assert self.rubinius? "rbx"
+ end
+
+ def test_windows_eh
+ assert self.class.windows? "mswin"
+ assert self.windows? "mswin"
+ end
+end
+
+class TestMiniTestUnitRecording < MetaMetaMetaTestCase
+ # do not parallelize this suite... it just can't handle it.
+
+ def assert_run_record(*expected, &block)
+ def @tu.record suite, method, assertions, time, error
+ recording[method] << error
+ end
+
+ def @tu.recording
+ @recording ||= Hash.new { |h,k| h[k] = [] }
+ end
+
+ MiniTest::Unit.runner = @tu
+
+ Class.new MiniTest::Unit::TestCase, &block
+
+ with_output do
+ @tu.run
+ end
+
+ recorded = @tu.recording.fetch("test_method").map(&:class)
+
+ assert_equal expected, recorded
+ end
+
+ def test_record_passing
+ assert_run_record NilClass do
+ def test_method
+ assert true
+ end
+ end
+ end
+
+ def test_record_failing
+ assert_run_record MiniTest::Assertion do
+ def test_method
+ assert false
+ end
+ end
+ end
+
+ def test_record_error
+ assert_run_record RuntimeError do
+ def test_method
+ raise "unhandled exception"
+ end
+ end
+ end
+
+ def test_record_error_teardown
+ assert_run_record NilClass, RuntimeError do
+ def test_method
+ assert true
+ end
+
+ def teardown
+ raise "unhandled exception"
+ end
+ end
+ end
+
+ def test_record_error_in_test_and_teardown
+ assert_run_record AnError, RuntimeError do
+ def test_method
+ raise AnError
+ end
+
+ def teardown
+ raise "unhandled exception"
+ end
+ end
+ end
+
+ def test_record_skip
+ assert_run_record MiniTest::Skip do
+ def test_method
+ skip "not yet"
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/misc/test_ruby_mode.rb b/jni/ruby/test/misc/test_ruby_mode.rb
new file mode 100644
index 0000000..5be0174
--- /dev/null
+++ b/jni/ruby/test/misc/test_ruby_mode.rb
@@ -0,0 +1,182 @@
+require 'test/unit'
+require 'tempfile'
+
+class TestRubyMode < Test::Unit::TestCase
+ MISCDIR = File.expand_path("../../../misc", __FILE__)
+ e = ENV["EMACS"] || "emacs"
+ emacs = %W"#{e} -q --no-site-file --batch --load #{MISCDIR}/ruby-mode.el"
+ begin
+ raise if IO.popen([e, "--version", :err=>[:child, :out]]) {|f| f.read}[/[0-9]+/].to_i < 23
+ IO.popen([*emacs, :err=>[:child, :out]]) {|f| f.read}
+ rescue
+ EMACS = nil
+ else
+ EMACS = (emacs if $? and $?.success?)
+ end
+end
+
+class TestRubyMode
+ EVAL_OPT = "--eval"
+ EXPR_SAVE = "(save-buffer)"
+ EXPR_RUBYMODE = "(ruby-mode)"
+ EXPR_NOBACKUP = "(progn" \
+ " (set (make-local-variable \'backup-inhibited) t)" \
+ " (set-buffer-modified-p t)" \
+ ")"
+
+ def run_emacs(src, *exprs)
+ tmp = Tempfile.new(%w"ruby-mode.test. .rb")
+ tmp.puts(src)
+ tmp.close
+ exprs = exprs.map {|expr| [EVAL_OPT, expr]}.flatten
+ exprs.unshift(EVAL_OPT, EXPR_RUBYMODE)
+ exprs.unshift(EVAL_OPT, EXPR_NOBACKUP)
+ output = IO.popen([*EMACS, tmp.path, *exprs, err:[:child, :out]], "r") {|e| e.read}
+ tmp.open
+ result = tmp.read
+ return result, output
+ ensure
+ tmp.close!
+ end
+
+ class TestIndent < self
+ EXPR_INDENT = "(indent-region (point-min) (point-max))"
+
+ def assert_indent(expected, source, *message)
+ if space = expected[/\A\n?(\s*\|)/, 1]
+ space = /^#{Regexp.quote(space)}/m
+ expected.gsub!(space, '')
+ source.gsub!(space, '')
+ end
+ result, output = run_emacs(source, EXPR_INDENT, EXPR_SAVE)
+ assert_match(/^Wrote /, output)
+ assert_equal(expected, result, message(*message) {diff expected, result})
+ end
+
+ def test_simple
+ assert_indent('
+ |if foo
+ | bar
+ |end
+ |zot
+ |', '
+ |if foo
+ |bar
+ | end
+ | zot
+ |')
+ end
+
+ def test_keyword_label
+ assert_indent('
+ |bar(class: XXX) do
+ | foo
+ |end
+ |bar
+ |', '
+ |bar(class: XXX) do
+ | foo
+ | end
+ | bar
+ |')
+ end
+
+ def test_method_with_question_mark
+ assert_indent('
+ |if x.is_a?(XXX)
+ | foo
+ |end
+ |', '
+ |if x.is_a?(XXX)
+ | foo
+ | end
+ |')
+ end
+
+ def test_expr_in_regexp
+ assert_indent('
+ |if /#{foo}/ =~ s
+ | x = 1
+ |end
+ |', '
+ |if /#{foo}/ =~ s
+ | x = 1
+ | end
+ |')
+ end
+
+ def test_singleton_class
+ skip("pending")
+ assert_indent('
+ |class<<bar
+ | foo
+ |end
+ |', '
+ |class<<bar
+ |foo
+ | end
+ |')
+ end
+
+ def test_array_literal
+ assert_indent('
+ |foo = [
+ | bar
+ |]
+ |', '
+ |foo = [
+ | bar
+ | ]
+ |')
+ assert_indent('
+ |foo do
+ | [bar]
+ |end
+ |', '
+ |foo do
+ |[bar]
+ | end
+ |')
+ end
+
+ def test_begin_end
+ assert_indent('
+ |begin
+ | a[b]
+ |end
+ |', '
+ |begin
+ | a[b]
+ | end
+ |')
+ end
+
+ def test_array_after_paren_and_space
+ assert_indent('
+ |class A
+ | def foo
+ | foo( [])
+ | end
+ |end
+ |', '
+ |class A
+ | def foo
+ |foo( [])
+ |end
+ | end
+ |')
+ end
+
+ def test_spread_arguments
+ assert_indent('
+ |foo(1,
+ | 2,
+ | 3)
+ |', '
+ |foo(1,
+ | 2,
+ | 3)
+ |')
+ end
+ end
+end if TestRubyMode::EMACS
diff --git a/jni/ruby/test/mkmf/base.rb b/jni/ruby/test/mkmf/base.rb
new file mode 100644
index 0000000..ea6a9d8
--- /dev/null
+++ b/jni/ruby/test/mkmf/base.rb
@@ -0,0 +1,139 @@
+$extmk = true
+
+require 'test/unit'
+require 'mkmf'
+require 'tmpdir'
+
+$extout = '$(topdir)/'+RbConfig::CONFIG["EXTOUT"]
+RbConfig::CONFIG['topdir'] = CONFIG['topdir'] = File.expand_path(CONFIG['topdir'])
+RbConfig::CONFIG["extout"] = CONFIG["extout"] = $extout
+$INCFLAGS << " -I."
+$extout_prefix = "$(extout)$(target_prefix)/"
+
+class TestMkmf < Test::Unit::TestCase
+ MKMFLOG = proc {File.read("mkmf.log") rescue ""}
+
+ class Capture
+ attr_accessor :origin
+ def initialize
+ @buffer = ""
+ @filter = nil
+ @out = true
+ @origin = nil
+ end
+ def clear
+ @buffer.clear
+ end
+ def flush
+ STDOUT.print @filter ? @filter.call(@buffer) : @buffer
+ clear
+ end
+ def reopen(io)
+ case io
+ when Capture
+ initialize_copy(io)
+ when File
+ @out = false
+ @origin.reopen(io) if @origin
+ when IO
+ @out = true
+ @origin.reopen(io) if @origin
+ else
+ @out = false
+ end
+ end
+ def filter(&block)
+ @filter = block
+ end
+ def write(s)
+ @buffer << s if @out
+ end
+ end
+end
+
+module TestMkmf::Base
+ attr_reader :stdout
+
+ def mkmflog(msg)
+ proc {MKMFLOG[] << msg}
+ end
+
+ def setup
+ @rbconfig = rbconfig0 = RbConfig::CONFIG
+ @mkconfig = mkconfig0 = RbConfig::MAKEFILE_CONFIG
+ rbconfig = {
+ "hdrdir" => $hdrdir,
+ "srcdir" => $srcdir,
+ "topdir" => $topdir,
+ }
+ mkconfig = {
+ "hdrdir" => "$(top_srcdir)/include",
+ "srcdir" => "$(top_srcdir)",
+ "topdir" => $topdir,
+ }
+ rbconfig0.each_pair {|key, val| rbconfig[key] ||= val.dup}
+ mkconfig0.each_pair {|key, val| mkconfig[key] ||= val.dup}
+ RbConfig.module_eval {
+ remove_const(:CONFIG)
+ const_set(:CONFIG, rbconfig)
+ remove_const(:MAKEFILE_CONFIG)
+ const_set(:MAKEFILE_CONFIG, mkconfig)
+ }
+ MakeMakefile.class_eval {
+ remove_const(:CONFIG)
+ const_set(:CONFIG, mkconfig)
+ }
+ @tmpdir = Dir.mktmpdir
+ @curdir = Dir.pwd
+ @mkmfobj = Object.new
+ @stdout = TestMkmf::Capture.new
+ Dir.chdir(@tmpdir)
+ @quiet, Logging.quiet = Logging.quiet, true
+ init_mkmf
+ $INCFLAGS[0, 0] = "-I. "
+ end
+
+ def teardown
+ rbconfig0 = @rbconfig
+ mkconfig0 = @mkconfig
+ RbConfig.module_eval {
+ remove_const(:CONFIG)
+ const_set(:CONFIG, rbconfig0)
+ remove_const(:MAKEFILE_CONFIG)
+ const_set(:MAKEFILE_CONFIG, mkconfig0)
+ }
+ MakeMakefile.class_eval {
+ remove_const(:CONFIG)
+ const_set(:CONFIG, mkconfig0)
+ }
+ Logging.quiet = @quiet
+ Logging.log_close
+ FileUtils.rm_f("mkmf.log")
+ Dir.chdir(@curdir)
+ FileUtils.rm_rf(@tmpdir)
+ end
+
+ def mkmf(*args, &block)
+ @stdout.clear
+ stdout, @stdout.origin, $stdout = @stdout.origin, $stdout, @stdout
+ @mkmfobj.instance_eval(*args, &block)
+ ensure
+ $stdout, @stdout.origin = @stdout.origin, stdout
+ end
+
+ def config_value(name)
+ create_tmpsrc("---config-value=#{name}")
+ xpopen(cpp_command('')) do |f|
+ f.grep(/^---config-value=(.*)/) {return $1}
+ end
+ nil
+ end
+end
+
+class TestMkmf
+ include TestMkmf::Base
+
+ def assert_separately(args, src, *rest)
+ super(args + ["-r#{__FILE__}"], "extend TestMkmf::Base; setup\n#{src}", *rest)
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_config.rb b/jni/ruby/test/mkmf/test_config.rb
new file mode 100644
index 0000000..7bf537e
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_config.rb
@@ -0,0 +1,16 @@
+$extmk = true
+
+require 'test/unit'
+require 'mkmf'
+
+class TestMkmf < Test::Unit::TestCase
+ class TestConfig < Test::Unit::TestCase
+ def test_dir_config
+ bug8074 = '[Bug #8074]'
+ lib = RbConfig.expand(RbConfig::MAKEFILE_CONFIG["libdir"], "exec_prefix"=>"")
+ assert_separately %w[-rmkmf - -- --with-foo-dir=/test/foo], %{
+ assert_equal(%w[/test/foo/include /test/foo#{lib}], dir_config("foo"), #{bug8074.dump})
+ }
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_constant.rb b/jni/ruby/test/mkmf/test_constant.rb
new file mode 100644
index 0000000..fd1f940
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_constant.rb
@@ -0,0 +1,37 @@
+require_relative 'base'
+
+class TestMkmf
+ class TestTryConstant < TestMkmf
+ def test_simple
+ assert_equal( 0, mkmf {try_constant("0")}, MKMFLOG)
+ assert_equal( 1, mkmf {try_constant("1")}, MKMFLOG)
+ assert_equal(-1, mkmf {try_constant("-1")}, MKMFLOG)
+ end
+
+ def test_sizeof
+ assert_equal(config_value("SIZEOF_INT").to_i, mkmf {try_constant("sizeof(int)")}, MKMFLOG)
+ assert_equal(config_value("SIZEOF_LONG").to_i, mkmf {try_constant("sizeof(long)")}, MKMFLOG)
+ assert_equal(config_value("SIZEOF_VOIDP").to_i, mkmf {try_constant("sizeof(void*)")}, MKMFLOG)
+ assert_equal(config_value("SIZEOF_VALUE").to_i, mkmf {try_constant("sizeof(Qnil)")}, MKMFLOG)
+ end
+
+ def test_long
+ sizeof_int = config_value("SIZEOF_INT").to_i
+ sizeof_long = config_value("SIZEOF_LONG").to_i
+ if sizeof_long > sizeof_int
+ type = 'long'
+ else
+ sizeof_long_long = config_value("SIZEOF_LONG_LONG").to_i
+ return if !sizeof_long_long or sizeof_long_long <= sizeof_int
+ type = 'LONG_LONG'
+ end
+
+ decl = "#define CONFTEST_VALUE (unsigned #{type})(((unsigned #{type})1)<<(CHAR_BIT*sizeof(int)))"
+ assert_operator(mkmf {try_constant("CONFTEST_VALUE", [[decl]])}, :>, 0, MKMFLOG)
+ end
+
+ def test_large_unsigned
+ assert_operator(mkmf {try_constant("1U<<(CHAR_BIT*sizeof(int)-1)")}, :>, 0, MKMFLOG)
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_convertible.rb b/jni/ruby/test/mkmf/test_convertible.rb
new file mode 100644
index 0000000..eec2d12
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_convertible.rb
@@ -0,0 +1,34 @@
+require_relative 'base'
+
+class TestMkmf
+ class TestConvertible < TestMkmf
+ def test_typeof_builtin
+ ["", ["signed ", ""], "unsigned "].each do |signed, prefix|
+ %w[short int long].each do |type|
+ assert_equal((prefix || signed)+type,
+ mkmf {convertible_int(signed+type)}, MKMFLOG)
+ end
+ end
+ end
+
+ def test_typeof_typedef
+ ["", ["signed ", ""], "unsigned "].each do |signed, prefix|
+ %w[short int long].each do |type|
+ open("confdefs.h", "w") {|f|
+ f.puts "typedef #{signed}#{type} test1_t;"
+ }
+ $defs.clear
+ assert_equal((prefix || signed)+type,
+ mkmf {convertible_int("test1_t", "confdefs.h")}, MKMFLOG)
+ (u = signed[/^u/]) and u.upcase!
+ assert_include($defs, "-DTYPEOF_TEST1_T="+"#{prefix||signed}#{type}".quote)
+ assert_include($defs, "-DPRI_TEST1T_PREFIX=PRI_#{type.upcase}_PREFIX")
+ assert_include($defs, "-DTEST1T2NUM=#{u}#{type.upcase}2NUM")
+ assert_include($defs, "-DNUM2TEST1T=NUM2#{u}#{type.upcase}")
+ end
+ end
+ ensure
+ File.unlink("confdefs.h")
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_find_executable.rb b/jni/ruby/test/mkmf/test_find_executable.rb
new file mode 100644
index 0000000..fe45ef2
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_find_executable.rb
@@ -0,0 +1,50 @@
+require_relative 'base'
+
+class TestMkmf
+ class TestFindExecutable < TestMkmf
+ def setup
+ super
+ @path, ENV["PATH"] = ENV["PATH"], @tmpdir
+ end
+
+ def teardown
+ ENV["PATH"] = @path
+ super
+ end
+
+ def test_find_executable
+ bug2669 = '[ruby-core:27912]'
+ name = "foobar#{$$}#{rand(1000)}"
+ exts = mkmf {self.class::CONFIG['EXECUTABLE_EXTS']}.split
+ stdout.filter {|s| s.sub(name, "<executable>")}
+ exts[0] ||= ""
+ exts.each do |ext|
+ full = name+ext
+ begin
+ open(full, "w") {|ff| ff.chmod(0755)}
+ result = mkmf {find_executable(name)}
+ ensure
+ File.unlink(full)
+ end
+ assert_equal("#{@tmpdir}/#{name}#{ext}", result, bug2669)
+ end
+ end
+
+ def test_find_executable_dir
+ name = "foobar#{$$}#{rand(1000)}"
+ exts = mkmf {self.class::CONFIG['EXECUTABLE_EXTS']}.split
+ stdout.filter {|s| s.sub(name, "<executable>")}
+ exts[0] ||= ""
+ exts.each do |ext|
+ full = name+ext
+ begin
+ Dir.mkdir(full)
+ result = mkmf {find_executable(name)}
+ ensure
+ Dir.rmdir(full)
+ end
+ assert_nil(result)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_flags.rb b/jni/ruby/test/mkmf/test_flags.rb
new file mode 100644
index 0000000..e49d474
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_flags.rb
@@ -0,0 +1,56 @@
+require_relative 'base'
+
+class TestMkmf
+ class TestFlags < TestMkmf
+ def test_valid_warnflags
+ val = $extmk
+ warnflags = $warnflags
+ makefile = mkmf do
+ $extmk = false
+ self.class::CONFIG['warnflags'] = %w"-Wextra
+ -Wno-unused-parameter -Wno-parentheses -Wno-long-long
+ -Wno-missing-field-initializers -Werror=pointer-arith
+ -Werror=write-strings -Werror=declaration-after-statement
+ -Werror=shorten-64-to-32
+ -Werror-implicit-function-declaration
+ ".join(' ')
+ self.class::CONFIG['GCC'] = 'yes'
+ init_mkmf(self.class::CONFIG)
+ configuration '.'
+ end
+ generated_flags = makefile.grep(/warnflags/).first[/^warnflags = (.*)$/, 1].split
+
+ assert_equal %w"
+ -Wextra -Wno-unused-parameter -Wno-parentheses
+ -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith
+ -Wwrite-strings -Wdeclaration-after-statement
+ -Wshorten-64-to-32 -Wimplicit-function-declaration
+ ", generated_flags
+
+ ensure
+ $warnflags = warnflags
+ $extmk = val
+ end
+
+ def test_try_ldflag_invalid_opt
+ assert_separately([], <<-'end;') #do
+ assert(!try_ldflags("nosuch.c"), TestMkmf::MKMFLOG)
+ assert(have_devel?, TestMkmf::MKMFLOG)
+ end;
+ end
+
+ def test_try_cflag_invalid_opt
+ assert_separately([], <<-'end;') #do
+ assert(!try_cflags("nosuch.c"), TestMkmf::MKMFLOG)
+ assert(have_devel?, TestMkmf::MKMFLOG)
+ end;
+ end
+
+ def test_try_cppflag_invalid_opt
+ assert_separately([], <<-'end;') #do
+ assert(!try_cppflags("nosuch.c"), TestMkmf::MKMFLOG)
+ assert(have_devel?, TestMkmf::MKMFLOG)
+ end;
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_framework.rb b/jni/ruby/test/mkmf/test_framework.rb
new file mode 100644
index 0000000..cad6b05
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_framework.rb
@@ -0,0 +1,48 @@
+require_relative 'base'
+
+class TestMkmf
+ class TestHaveFramework < TestMkmf
+ def create_framework(fw, hdrname = "#{fw}.h")
+ Dir.mktmpdir("frameworks") do |dir|
+ fwdir = "#{dir}/#{fw}.framework"
+ hdrdir = "#{fwdir}/Headers"
+ FileUtils.mkdir_p(hdrdir)
+ File.write("#{hdrdir}/#{hdrname}", "")
+ src = "#{fwdir}/main.c"
+ File.write(src, "void #{fw}(void) {}")
+ cmd = LINK_SO.dup
+ RbConfig.expand(cmd, RbConfig::CONFIG.merge("OBJS"=>src))
+ cmd.gsub!("$@", "#{fwdir}/#{fw}")
+ cmd.gsub!(/ -bundle /, ' -dynamiclib ')
+ assert(xsystem(cmd), MKMFLOG)
+ $INCFLAGS << " " << "-F#{dir}".quote
+ yield fw, hdrname
+ end
+ end
+
+ def test_core_foundation_framework
+ assert(have_framework("CoreFoundation"), mkmflog("try as Objective-C"))
+ end
+
+ def test_multi_frameworks
+ assert(have_framework("CoreFoundation"), mkmflog("try as Objective-C"))
+ create_framework("MkmfTest") do |fw|
+ assert(have_framework(fw), MKMFLOG)
+ end
+ end
+
+ def test_empty_framework
+ create_framework("MkmfTest") do |fw|
+ assert(have_framework(fw), MKMFLOG)
+ end
+ end
+
+ def test_different_name_header
+ _bug8593 = '[ruby-core:55745] [Bug #8593]'
+ create_framework("MkmfTest", "test_mkmf.h") do |fw, hdrname|
+ assert(!have_framework(fw), MKMFLOG)
+ assert(have_framework([fw, hdrname]), MKMFLOG)
+ end
+ end
+ end
+end if /darwin/ =~ RUBY_PLATFORM
diff --git a/jni/ruby/test/mkmf/test_have_func.rb b/jni/ruby/test/mkmf/test_have_func.rb
new file mode 100644
index 0000000..8049ffb
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_have_func.rb
@@ -0,0 +1,14 @@
+require_relative 'base'
+require 'tempfile'
+
+class TestMkmf
+ class TestHaveFunc < TestMkmf
+ def test_have_func
+ assert_equal(true, have_func("ruby_init"), MKMFLOG)
+ end
+
+ def test_not_have_func
+ assert_equal(false, have_func("no_ruby_init"), MKMFLOG)
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_have_library.rb b/jni/ruby/test/mkmf/test_have_library.rb
new file mode 100644
index 0000000..bf17b85
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_have_library.rb
@@ -0,0 +1,55 @@
+require_relative 'base'
+require 'tempfile'
+
+class TestMkmf
+ class TestHaveLibrary < TestMkmf
+ LIBRARY_NAME = 'mkmftest'
+ HEADER_NAME = "#{LIBRARY_NAME}.h"
+ FUNC_NAME = 'ruby_mkmftest_foo'
+ ARPREFIX = config_string('LIBRUBY_A') {|lib| lib[/\A\w+/]}
+
+ def create_library(libname = LIBRARY_NAME)
+ lib = "#{ARPREFIX}#{libname}.#{$LIBEXT}"
+ open(HEADER_NAME, "w") do |hdr|
+ hdr.puts "void #{FUNC_NAME}(void);"
+ hdr.puts "void #{FUNC_NAME}_fake(void);"
+ end
+ create_tmpsrc("#include \"#{HEADER_NAME}\"\n""void #{FUNC_NAME}(void) {}")
+ assert(xsystem(cc_command), "compile failed: #{cc_command}")
+ command = "#{CONFIG['AR']} #{config_string('ARFLAGS') || 'cru '}#{lib} #{CONFTEST}.#{$OBJEXT}"
+ assert(xsystem(command), "making library failed: #{command}")
+ File.unlink("#{CONFTEST}.#{$OBJEXT}")
+ config_string('RANLIB') do |ranlib|
+ command = "#{ranlib} #{lib}"
+ assert(xsystem(command), "ranlib failed: #{command}")
+ end
+ end
+
+ def assert_have_library(*args)
+ assert_equal(true, have_library(LIBRARY_NAME, *args), MKMFLOG)
+ end
+
+ def assert_not_have_library(*args)
+ assert_equal(false, have_library(LIBRARY_NAME, *args), MKMFLOG)
+ end
+
+ def test_have_library
+ create_library
+ assert_have_library
+ end
+
+ def test_have_library_with_name
+ create_library
+ assert_have_library(FUNC_NAME, HEADER_NAME)
+ end
+
+ def test_not_have_library
+ assert_not_have_library
+ end
+
+ def test_not_have_library_with_name
+ create_library
+ assert_not_have_library("#{FUNC_NAME}_fake", HEADER_NAME)
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_have_macro.rb b/jni/ruby/test/mkmf/test_have_macro.rb
new file mode 100644
index 0000000..43c4029
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_have_macro.rb
@@ -0,0 +1,35 @@
+require_relative 'base'
+require 'tempfile'
+
+class TestMkmf
+ class TestHaveMacro < TestMkmf
+ MACRO_NAME = "RUBY_MKMFTEST_FOOBAR"
+
+ def test_have_macro_opt
+ assert_equal(true, have_macro(MACRO_NAME, nil, "-D#{MACRO_NAME}"), MKMFLOG)
+ end
+
+ def test_have_macro_header
+ Tempfile.create(%w"test_mkmf .h", ".") do |tmp|
+ tmp.puts("#undef #{MACRO_NAME}")
+ tmp.puts("#define #{MACRO_NAME} 1")
+ tmp.close
+ base = File.basename(tmp.path)
+ assert_equal(true, have_macro(MACRO_NAME, base, "-I."), MKMFLOG)
+ end
+ end
+
+ def test_not_have_macro_opt
+ assert_equal(false, have_macro(MACRO_NAME, nil, "-U#{MACRO_NAME}"), MKMFLOG)
+ end
+
+ def test_not_have_macro_header
+ Tempfile.create(%w"test_mkmf .h", ".") do |tmp|
+ tmp.puts("#undef #{MACRO_NAME}")
+ tmp.close
+ base = File.basename(tmp.path)
+ assert_equal(false, have_macro(MACRO_NAME, base, "-I."), MKMFLOG)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_libs.rb b/jni/ruby/test/mkmf/test_libs.rb
new file mode 100644
index 0000000..27674df
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_libs.rb
@@ -0,0 +1,86 @@
+require_relative 'base'
+
+class TestMkmf
+ class TestLibs < TestMkmf
+ def test_split_libs
+ assert_equal(%w[-lfoo -lbar], split_libs("-lfoo -lbar"))
+ assert_equal(%w[-ObjC -framework\ Ruby], split_libs("-ObjC -framework Ruby"), 'Bug #6987')
+ end
+
+ def assert_in_order(array, x, y, mesg = nil)
+ mesg = "#{x} must proceed to #{y}#{': ' if mesg}#{mesg}"
+ assert_operator(array.index(x), :<, array.rindex(y), mesg)
+ end
+
+ def test_merge_simple
+ bug = '[ruby-dev:21765]'
+ assert_equal([], merge_libs(%w[]))
+ assert_equal(%w[a b], merge_libs(%w[a], %w[b]))
+ array = merge_libs(%w[a c], %w[b])
+ assert_in_order(array, "a", "c", bug)
+ end
+
+ def test_merge_seq
+ bug = '[ruby-dev:21765]'
+ array = merge_libs(%w[a c d], %w[c b e])
+ assert_in_order(array, "a", "c", bug)
+ assert_in_order(array, "c", "d", bug)
+ assert_in_order(array, "c", "b", bug)
+ assert_in_order(array, "b", "e", bug)
+ end
+
+ def test_merge_seq_pre
+ bug = '[ruby-dev:21765]'
+ array = merge_libs(%w[a c d], %w[b c d e])
+ assert_in_order(array, "a", "c", bug)
+ assert_in_order(array, "c", "d", bug)
+ assert_in_order(array, "b", "c", bug)
+ assert_in_order(array, "d", "e", bug)
+ end
+
+ def test_merge_cyclic
+ bug = '[ruby-dev:21765]'
+ array = merge_libs(%w[a c d], %w[b c b])
+ assert_in_order(array, "a", "c", bug)
+ assert_in_order(array, "c", "d", bug)
+ assert_in_order(array, "b", "c", bug)
+ assert_in_order(array, "c", "b", bug)
+ end
+
+ def test_merge_cyclic_2
+ bug = '[ruby-dev:21765]'
+ array = merge_libs(%w[a c a d], %w[b c b])
+ assert_in_order(array, "a", "c", bug)
+ assert_in_order(array, "c", "a", bug)
+ assert_in_order(array, "c", "d", bug)
+ assert_in_order(array, "a", "d", bug)
+ assert_in_order(array, "b", "c", bug)
+ assert_in_order(array, "c", "b", bug)
+ end
+
+ def test_merge_reversal
+ bug = '[ruby-dev:22440]'
+ array = merge_libs(%w[a b c], %w[c d a])
+ assert_in_order(array, "a", "b" , bug)
+ assert_in_order(array, "c", "d" , bug)
+ ## assume that a and c have no dependency
+ end
+
+ def test_merge_reversal_followed
+ bug7467 = '[ruby-core:50314] [Bug #7467]'
+ array = nil
+ assert_nothing_raised(bug7467) {
+ array = merge_libs(%w[a b c d e f g h], %w[d c d e], [])
+ }
+ assert_in_order(array, "a", "b", bug7467)
+ assert_in_order(array, "b", "c", bug7467)
+ assert_in_order(array, "c", "d", bug7467)
+ assert_in_order(array, "d", "e", bug7467)
+ assert_in_order(array, "e", "f", bug7467)
+ assert_in_order(array, "f", "g", bug7467)
+ assert_in_order(array, "g", "h", bug7467)
+ assert_in_order(array, "d", "c", bug7467)
+ assert_in_order(array, "c", "e", bug7467)
+ end
+ end
+end if RUBY_ENGINE == "ruby"
diff --git a/jni/ruby/test/mkmf/test_signedness.rb b/jni/ruby/test/mkmf/test_signedness.rb
new file mode 100644
index 0000000..8d58073
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_signedness.rb
@@ -0,0 +1,29 @@
+require_relative 'base'
+
+class TestMkmf
+ class TestSignedness < TestMkmf
+ def test_typeof_builtin
+ bug4144 = '[ruby-dev:42731]'
+ [["", "-1"], ["signed ", "-1"], ["unsigned ", "+1"]].each do |signed, expect|
+ %w[short int long].each do |type|
+ assert_equal(expect.to_i, mkmf {check_signedness(signed+type)}, mkmflog(bug4144))
+ end
+ end
+ end
+
+ def test_typeof_typedef
+ [["", "-1"], ["signed ", "-1"], ["unsigned ", "+1"]].each do |signed, expect|
+ %w[short int long].each do |type|
+ open("confdefs.h", "w") {|f|
+ f.puts "typedef #{signed}#{type} test1_t;"
+ }
+ $defs.clear
+ assert_equal(expect.to_i, mkmf {check_signedness("test1_t", "confdefs.h")}, MKMFLOG)
+ assert_include($defs, "-DSIGNEDNESS_OF_TEST1_T=#{expect}")
+ end
+ end
+ ensure
+ File.unlink("confdefs.h")
+ end
+ end
+end
diff --git a/jni/ruby/test/mkmf/test_sizeof.rb b/jni/ruby/test/mkmf/test_sizeof.rb
new file mode 100644
index 0000000..c014422
--- /dev/null
+++ b/jni/ruby/test/mkmf/test_sizeof.rb
@@ -0,0 +1,47 @@
+require_relative 'base'
+
+class TestMkmf
+ class TestSizeof < TestMkmf
+ def setup
+ super
+ @sizeof_short = config_value("SIZEOF_SHORT").to_i
+ @sizeof_int = config_value("SIZEOF_INT").to_i
+ @sizeof_long = config_value("SIZEOF_LONG").to_i
+ @sizeof_long_long = config_value("SIZEOF_LONG_LONG").to_i
+ @sizeof___int64 = config_value("SIZEOF___INT64").to_i
+ end
+
+ def test_sizeof_builtin
+ %w[char short int long float double void*].each do |type|
+ assert_kind_of(Integer, mkmf {check_sizeof(type)}, MKMFLOG)
+ end
+ assert_operator(@sizeof_short, :<=, @sizeof_int)
+ assert_operator(@sizeof_int, :<=, @sizeof_long)
+ assert_operator(@sizeof_long, :<=, @sizeof_long_long) unless @sizeof_long_long.zero?
+ assert_equal(8, @sizeof___int64) unless @sizeof___int64.zero?
+ end
+
+ def test_sizeof_struct
+ open("confdefs.h", "w") {|f|
+ f.puts "typedef struct {char x;} test1_t;"
+ }
+ assert_equal(1, mkmf {check_sizeof("test1_t", "confdefs.h")}, MKMFLOG)
+
+ open("confdefs.h", "w") {|f|
+ f.puts "typedef struct {char x, y;} test1_t;"
+ }
+ assert_equal(2, mkmf {check_sizeof("test1_t", "confdefs.h")}, MKMFLOG)
+
+ open("confdefs.h", "w") {|f|
+ f.puts "typedef struct {int x;} test1_t;"
+ }
+ assert_equal(@sizeof_int, mkmf {check_sizeof("test1_t", "confdefs.h")}, MKMFLOG)
+ open("confdefs.h", "w") {|f|
+ f.puts "typedef struct {int x, y;} test1_t;"
+ }
+ assert_equal(2 * @sizeof_int, mkmf {check_sizeof("test1_t", "confdefs.h")}, MKMFLOG)
+ ensure
+ File.unlink("confdefs.h")
+ end
+ end
+end
diff --git a/jni/ruby/test/monitor/test_monitor.rb b/jni/ruby/test/monitor/test_monitor.rb
new file mode 100644
index 0000000..451e26c
--- /dev/null
+++ b/jni/ruby/test/monitor/test_monitor.rb
@@ -0,0 +1,209 @@
+require "monitor"
+require "thread"
+
+require "test/unit"
+
+class TestMonitor < Test::Unit::TestCase
+ def setup
+ @monitor = Monitor.new
+ end
+
+ def test_enter
+ ary = []
+ queue = Queue.new
+ th = Thread.start {
+ queue.pop
+ @monitor.enter
+ for i in 6 .. 10
+ ary.push(i)
+ Thread.pass
+ end
+ @monitor.exit
+ }
+ th2 = Thread.start {
+ @monitor.enter
+ queue.enq(nil)
+ for i in 1 .. 5
+ ary.push(i)
+ Thread.pass
+ end
+ @monitor.exit
+ }
+ assert_join_threads([th, th2])
+ assert_equal((1..10).to_a, ary)
+ end
+
+ def test_synchronize
+ ary = []
+ queue = Queue.new
+ th = Thread.start {
+ queue.pop
+ @monitor.synchronize do
+ for i in 6 .. 10
+ ary.push(i)
+ Thread.pass
+ end
+ end
+ }
+ th2 = Thread.start {
+ @monitor.synchronize do
+ queue.enq(nil)
+ for i in 1 .. 5
+ ary.push(i)
+ Thread.pass
+ end
+ end
+ }
+ assert_join_threads([th, th2])
+ assert_equal((1..10).to_a, ary)
+ end
+
+ def test_killed_thread_in_synchronize
+ ary = []
+ queue = Queue.new
+ t1 = Thread.start {
+ queue.pop
+ @monitor.synchronize {
+ ary << :t1
+ }
+ }
+ t2 = Thread.start {
+ queue.pop
+ @monitor.synchronize {
+ ary << :t2
+ }
+ }
+ t3 = Thread.start {
+ @monitor.synchronize do
+ queue.enq(nil)
+ queue.enq(nil)
+ assert_equal([], ary)
+ t1.kill
+ t2.kill
+ ary << :main
+ end
+ assert_equal([:main], ary)
+ }
+ assert_join_threads([t1, t2, t3])
+ end
+
+ def test_try_enter
+ queue1 = Queue.new
+ queue2 = Queue.new
+ th = Thread.start {
+ queue1.deq
+ @monitor.enter
+ queue2.enq(nil)
+ queue1.deq
+ @monitor.exit
+ queue2.enq(nil)
+ }
+ th2 = Thread.start {
+ assert_equal(true, @monitor.try_enter)
+ @monitor.exit
+ queue1.enq(nil)
+ queue2.deq
+ assert_equal(false, @monitor.try_enter)
+ queue1.enq(nil)
+ queue2.deq
+ assert_equal(true, @monitor.try_enter)
+ }
+ assert_join_threads([th, th2])
+ end
+
+ def test_cond
+ cond = @monitor.new_cond
+
+ a = "foo"
+ queue1 = Queue.new
+ th = Thread.start do
+ queue1.deq
+ @monitor.synchronize do
+ a = "bar"
+ cond.signal
+ end
+ end
+ th2 = Thread.start do
+ @monitor.synchronize do
+ queue1.enq(nil)
+ assert_equal("foo", a)
+ result1 = cond.wait
+ assert_equal(true, result1)
+ assert_equal("bar", a)
+ end
+ end
+ assert_join_threads([th, th2])
+ end
+
+ def test_timedwait
+ cond = @monitor.new_cond
+ b = "foo"
+ queue2 = Queue.new
+ th = Thread.start do
+ queue2.deq
+ @monitor.synchronize do
+ b = "bar"
+ cond.signal
+ end
+ end
+ th2 = Thread.start do
+ @monitor.synchronize do
+ queue2.enq(nil)
+ assert_equal("foo", b)
+ result2 = cond.wait(0.1)
+ assert_equal(true, result2)
+ assert_equal("bar", b)
+ end
+ end
+ assert_join_threads([th, th2])
+
+ c = "foo"
+ queue3 = Queue.new
+ th = Thread.start do
+ queue3.deq
+ @monitor.synchronize do
+ c = "bar"
+ cond.signal
+ end
+ end
+ th2 = Thread.start do
+ @monitor.synchronize do
+ assert_equal("foo", c)
+ result3 = cond.wait(0.1)
+ assert_equal(true, result3) # wait always returns true in Ruby 1.9
+ assert_equal("foo", c)
+ queue3.enq(nil)
+ result4 = cond.wait
+ assert_equal(true, result4)
+ assert_equal("bar", c)
+ end
+ end
+ assert_join_threads([th, th2])
+
+# d = "foo"
+# cumber_thread = Thread.start {
+# loop do
+# @monitor.synchronize do
+# d = "foo"
+# end
+# end
+# }
+# queue3 = Queue.new
+# Thread.start do
+# queue3.pop
+# @monitor.synchronize do
+# d = "bar"
+# cond.signal
+# end
+# end
+# @monitor.synchronize do
+# queue3.enq(nil)
+# assert_equal("foo", d)
+# result5 = cond.wait
+# assert_equal(true, result5)
+# # this thread has priority over cumber_thread
+# assert_equal("bar", d)
+# end
+# cumber_thread.kill
+ end
+end
diff --git a/jni/ruby/test/net/ftp/test_buffered_socket.rb b/jni/ruby/test/net/ftp/test_buffered_socket.rb
new file mode 100644
index 0000000..f9eefcd
--- /dev/null
+++ b/jni/ruby/test/net/ftp/test_buffered_socket.rb
@@ -0,0 +1,40 @@
+require "net/ftp"
+require "test/unit"
+require "ostruct"
+require "stringio"
+
+class FTPTest < Test::Unit::TestCase
+ def test_gets_empty
+ sock = create_buffered_socket("")
+ assert_equal(nil, sock.gets)
+ end
+
+ def test_gets_one_line
+ sock = create_buffered_socket("foo\n")
+ assert_equal("foo\n", sock.gets)
+ end
+
+ def test_gets_one_line_without_term
+ sock = create_buffered_socket("foo")
+ assert_equal("foo", sock.gets)
+ end
+
+ def test_gets_two_lines
+ sock = create_buffered_socket("foo\nbar\n")
+ assert_equal("foo\n", sock.gets)
+ assert_equal("bar\n", sock.gets)
+ end
+
+ def test_gets_two_lines_without_term
+ sock = create_buffered_socket("foo\nbar")
+ assert_equal("foo\n", sock.gets)
+ assert_equal("bar", sock.gets)
+ end
+
+ private
+
+ def create_buffered_socket(s)
+ io = StringIO.new(s)
+ return Net::FTP::BufferedSocket.new(io)
+ end
+end
diff --git a/jni/ruby/test/net/ftp/test_ftp.rb b/jni/ruby/test/net/ftp/test_ftp.rb
new file mode 100644
index 0000000..eaed3b0
--- /dev/null
+++ b/jni/ruby/test/net/ftp/test_ftp.rb
@@ -0,0 +1,833 @@
+require "net/ftp"
+require "test/unit"
+require "ostruct"
+require "stringio"
+
+class FTPTest < Test::Unit::TestCase
+ SERVER_ADDR = "127.0.0.1"
+
+ def setup
+ @thread = nil
+ end
+
+ def teardown
+ if @thread
+ @thread.join
+ end
+ end
+
+ def test_not_connected
+ ftp = Net::FTP.new
+ assert_raise(Net::FTPConnectionError) do
+ ftp.quit
+ end
+ end
+
+ def test_connect_fail
+ server = create_ftp_server { |sock|
+ sock.print("421 Service not available, closing control connection.\r\n")
+ }
+ begin
+ ftp = Net::FTP.new
+ assert_raise(Net::FTPTempError){ ftp.connect(SERVER_ADDR, server.port) }
+ ensure
+ ftp.close if ftp
+ server.close
+ end
+ end
+
+ def test_parse227
+ ftp = Net::FTP.new
+ host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
+ assert_equal("192.168.0.1", host)
+ assert_equal(3106, port)
+ assert_raise(Net::FTPReplyError) do
+ ftp.send(:parse227, "500 Syntax error")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 Entering Passive Mode")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34,56)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 ) foo bar (")
+ end
+ end
+
+ def test_parse228
+ ftp = Net::FTP.new
+ host, port = ftp.send(:parse228, "228 Entering Long Passive Mode (4,4,192,168,0,1,2,12,34)")
+ assert_equal("192.168.0.1", host)
+ assert_equal(3106, port)
+ host, port = ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34)")
+ assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
+ assert_equal(3106, port)
+ assert_raise(Net::FTPReplyError) do
+ ftp.send(:parse228, "500 Syntax error")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Passive Mode")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (6,4,192,168,0,1,2,12,34)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (4,4,192,168,0,1,3,12,34,56)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (4,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,3,12,34,56)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34,56)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse227, "227 ) foo bar (")
+ end
+ end
+
+ def test_parse229
+ ftp = Net::FTP.new
+ sock = OpenStruct.new
+ sock.peeraddr = [nil, nil, nil, "1080:0000:0000:0000:0008:0800:200c:417a"]
+ ftp.instance_variable_set(:@sock, sock)
+ host, port = ftp.send(:parse229, "229 Entering Passive Mode (|||3106|)")
+ assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
+ assert_equal(3106, port)
+ host, port = ftp.send(:parse229, "229 Entering Passive Mode (!!!3106!)")
+ assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
+ assert_equal(3106, port)
+ host, port = ftp.send(:parse229, "229 Entering Passive Mode (~~~3106~)")
+ assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
+ assert_equal(3106, port)
+ assert_raise(Net::FTPReplyError) do
+ ftp.send(:parse229, "500 Syntax error")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 Entering Passive Mode")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 Entering Passive Mode (|!!3106!)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 Entering Passive Mode ( 3106 )")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 Entering Passive Mode (\x7f\x7f\x7f3106\x7f)")
+ end
+ assert_raise(Net::FTPProtoError) do
+ ftp.send(:parse229, "229 ) foo bar (")
+ end
+ end
+
+ def test_parse_pasv_port
+ ftp = Net::FTP.new
+ assert_equal(12, ftp.send(:parse_pasv_port, "12"))
+ assert_equal(3106, ftp.send(:parse_pasv_port, "12,34"))
+ assert_equal(795192, ftp.send(:parse_pasv_port, "12,34,56"))
+ assert_equal(203569230, ftp.send(:parse_pasv_port, "12,34,56,78"))
+ end
+
+ def test_login
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_login_fail1
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("502 Command not implemented.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ assert_raise(Net::FTPPermError){ ftp.login }
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_login_fail2
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("530 Not logged in.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.connect(SERVER_ADDR, server.port)
+ assert_raise(Net::FTPPermError){ ftp.login }
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ # TODO: How can we test open_timeout? sleep before accept cannot delay
+ # connections.
+ def _test_open_timeout_exceeded
+ commands = []
+ server = create_ftp_server(0.2) { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.open_timeout = 0.1
+ ftp.connect(SERVER_ADDR, server.port)
+ assert_raise(Net::OpenTimeout) do
+ ftp.login
+ end
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_read_timeout_exceeded
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sleep(0.3)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ assert_raise(Net::ReadTimeout) do
+ ftp.login
+ end
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_read_timeout_not_exceeded
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sleep(0.1)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close
+ assert_equal(0.2, ftp.read_timeout)
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_list_read_timeout_exceeded
+ commands = []
+ list_lines = [
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 foo.txt",
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 bar.txt",
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 baz.txt"
+ ]
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Here comes the directory listing.\r\n")
+ begin
+ conn = TCPSocket.new(host, port)
+ list_lines.each_with_index do |l, i|
+ if i == 1
+ sleep(0.5)
+ else
+ sleep(0.1)
+ end
+ conn.print(l, "\r\n")
+ end
+ rescue Errno::EPIPE
+ ensure
+ assert_nil($!)
+ conn.close
+ end
+ sock.print("226 Directory send OK.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::ReadTimeout) do
+ ftp.list
+ end
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("LIST\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_list_read_timeout_not_exceeded
+ commands = []
+ list_lines = [
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 foo.txt",
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 bar.txt",
+ "-rw-r--r-- 1 0 0 0 Mar 30 11:22 baz.txt"
+ ]
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Here comes the directory listing.\r\n")
+ conn = TCPSocket.new(host, port)
+ list_lines.each do |l|
+ sleep(0.1)
+ conn.print(l, "\r\n")
+ end
+ conn.close
+ sock.print("226 Directory send OK.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(list_lines, ftp.list)
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("LIST\r\n", commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_list_fail
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("553 Requested action not taken.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::FTPPermError){ ftp.list }
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("LIST\r\n", commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_open_data_port_fail_no_leak
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to ASCII mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("421 Service not available, closing control connection.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::FTPTempError){ ftp.list }
+ assert_equal("TYPE A\r\n", commands.shift)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_retrbinary_read_timeout_exceeded
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ sleep(0.1)
+ conn.print(binary_data[0,1024])
+ sleep(0.5)
+ conn.print(binary_data[1024, 1024]) rescue nil # may raise EPIPE or something
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ""
+ assert_raise(Net::ReadTimeout) do
+ ftp.retrbinary("RETR foo", 1024) do |s|
+ buf << s
+ end
+ end
+ assert_equal(1024, buf.bytesize)
+ assert_equal(binary_data[0, 1024], buf)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close unless ftp.closed?
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_retrbinary_read_timeout_not_exceeded
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = TCPSocket.new(host, port)
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ sleep(0.1)
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ""
+ ftp.retrbinary("RETR foo", 1024) do |s|
+ buf << s
+ end
+ assert_equal(binary_data.bytesize, buf.bytesize)
+ assert_equal(binary_data, buf)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_retrbinary_fail
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("550 Requested action not taken.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::FTPPermError){ ftp.retrbinary("RETR foo", 1024) }
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_storbinary
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ stored_data = nil
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ port_args = line.slice(/\APORT (.*)/, 1).split(/,/)
+ host = port_args[0, 4].join(".")
+ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo\r\n")
+ conn = TCPSocket.new(host, port)
+ stored_data = conn.read
+ conn.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024)
+ assert_equal(binary_data, stored_data)
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("STOR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_storbinary_fail
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("200 PORT command successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("452 Requested file action aborted.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(Net::FTPTempError){ ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024) }
+ assert_match(/\APORT /, commands.shift)
+ assert_equal("STOR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_abort
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.recv(1024))
+ sock.print("225 No transfer to ABOR.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ #ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.abort
+ assert_equal("ABOR\r", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_status
+ commands = []
+ server = create_ftp_server { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ commands.push(sock.recv(1024))
+ sock.print("211 End of status\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.read_timeout = 0.2
+ ftp.connect(SERVER_ADDR, server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ ftp.status
+ assert_equal("STAT\r", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ private
+
+
+ def create_ftp_server(sleep_time = nil)
+ server = TCPServer.new(SERVER_ADDR, 0)
+ @thread = Thread.start do
+ if sleep_time
+ sleep(sleep_time)
+ end
+ sock = server.accept
+ begin
+ yield(sock)
+ sock.shutdown(Socket::SHUT_WR)
+ sock.read unless sock.eof?
+ ensure
+ sock.close
+ end
+ end
+ def server.port
+ addr[1]
+ end
+ return server
+ end
+end
diff --git a/jni/ruby/test/net/http/test_buffered_io.rb b/jni/ruby/test/net/http/test_buffered_io.rb
new file mode 100644
index 0000000..e24e7c1
--- /dev/null
+++ b/jni/ruby/test/net/http/test_buffered_io.rb
@@ -0,0 +1,17 @@
+require 'test/unit'
+require 'net/http'
+require 'stringio'
+
+require_relative 'utils'
+
+module Net
+ class TestBufferedIO < Test::Unit::TestCase
+ def test_eof?
+ s = StringIO.new
+ assert s.eof?
+ bio = BufferedIO.new(s)
+ assert_equal s, bio.io
+ assert_equal s.eof?, bio.eof?
+ end
+ end
+end
diff --git a/jni/ruby/test/net/http/test_http.rb b/jni/ruby/test/net/http/test_http.rb
new file mode 100644
index 0000000..9d5cf39
--- /dev/null
+++ b/jni/ruby/test/net/http/test_http.rb
@@ -0,0 +1,933 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'net/http'
+require 'stringio'
+require_relative 'utils'
+
+class TestNetHTTP < Test::Unit::TestCase
+
+ def test_class_Proxy
+ no_proxy_class = Net::HTTP.Proxy nil
+
+ assert_equal Net::HTTP, no_proxy_class
+
+ proxy_class = Net::HTTP.Proxy 'proxy.example', 8000, 'user', 'pass'
+
+ refute_equal Net::HTTP, proxy_class
+
+ assert_operator proxy_class, :<, Net::HTTP
+
+ assert_equal 'proxy.example', proxy_class.proxy_address
+ assert_equal 8000, proxy_class.proxy_port
+ assert_equal 'user', proxy_class.proxy_user
+ assert_equal 'pass', proxy_class.proxy_pass
+
+ http = proxy_class.new 'example'
+
+ refute http.proxy_from_env?
+
+
+ proxy_class = Net::HTTP.Proxy 'proxy.example'
+ assert_equal 'proxy.example', proxy_class.proxy_address
+ assert_equal 80, proxy_class.proxy_port
+ end
+
+ def test_class_Proxy_from_ENV
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ # These are ignored on purpose. See Bug 4388 and Feature 6546
+ ENV['http_proxy_user'] = 'user'
+ ENV['http_proxy_pass'] = 'pass'
+
+ proxy_class = Net::HTTP.Proxy :ENV
+
+ refute_equal Net::HTTP, proxy_class
+
+ assert_operator proxy_class, :<, Net::HTTP
+
+ assert_nil proxy_class.proxy_address
+ assert_nil proxy_class.proxy_user
+ assert_nil proxy_class.proxy_pass
+
+ refute_equal 8000, proxy_class.proxy_port
+
+ http = proxy_class.new 'example'
+
+ assert http.proxy_from_env?
+ end
+ end
+
+ def test_edit_path
+ http = Net::HTTP.new 'example', nil, nil
+
+ edited = http.send :edit_path, '/path'
+
+ assert_equal '/path', edited
+
+ http.use_ssl = true
+
+ edited = http.send :edit_path, '/path'
+
+ assert_equal '/path', edited
+ end
+
+ def test_edit_path_proxy
+ http = Net::HTTP.new 'example', nil, 'proxy.example'
+
+ edited = http.send :edit_path, '/path'
+
+ assert_equal 'http://example/path', edited
+
+ http.use_ssl = true
+
+ edited = http.send :edit_path, '/path'
+
+ assert_equal '/path', edited
+ end
+
+ def test_proxy_address
+ clean_http_proxy_env do
+ http = Net::HTTP.new 'example', nil, 'proxy.example'
+ assert_equal 'proxy.example', http.proxy_address
+
+ http = Net::HTTP.new 'example', nil
+ assert_equal nil, http.proxy_address
+ end
+ end
+
+ def test_proxy_address_ENV
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ http = Net::HTTP.new 'example'
+
+ assert_equal 'proxy.example', http.proxy_address
+ end
+ end
+
+ def test_proxy_eh_no_proxy
+ clean_http_proxy_env do
+ assert_equal false, Net::HTTP.new('example', nil, nil).proxy?
+ end
+ end
+
+ def test_proxy_eh_ENV
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ http = Net::HTTP.new 'example'
+
+ assert_equal true, http.proxy?
+ end
+ end
+
+ def test_proxy_eh_ENV_none_set
+ clean_http_proxy_env do
+ assert_equal false, Net::HTTP.new('example').proxy?
+ end
+ end
+
+ def test_proxy_eh_ENV_no_proxy
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+ ENV['no_proxy'] = 'example'
+
+ assert_equal false, Net::HTTP.new('example').proxy?
+ end
+ end
+
+ def test_proxy_port
+ clean_http_proxy_env do
+ http = Net::HTTP.new 'exmaple', nil, 'proxy.example'
+ assert_equal 'proxy.example', http.proxy_address
+ assert_equal 80, http.proxy_port
+ http = Net::HTTP.new 'exmaple', nil, 'proxy.example', 8000
+ assert_equal 8000, http.proxy_port
+ http = Net::HTTP.new 'exmaple', nil
+ assert_equal nil, http.proxy_port
+ end
+ end
+
+ def test_proxy_port_ENV
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ http = Net::HTTP.new 'example'
+
+ assert_equal 8000, http.proxy_port
+ end
+ end
+
+ def test_newobj
+ clean_http_proxy_env do
+ ENV['http_proxy'] = 'http://proxy.example:8000'
+
+ http = Net::HTTP.newobj 'example'
+
+ assert_equal false, http.proxy?
+ end
+ end
+
+ def clean_http_proxy_env
+ orig = {
+ 'http_proxy' => ENV['http_proxy'],
+ 'http_proxy_user' => ENV['http_proxy_user'],
+ 'http_proxy_pass' => ENV['http_proxy_pass'],
+ 'no_proxy' => ENV['no_proxy'],
+ }
+
+ orig.each_key do |key|
+ ENV.delete key
+ end
+
+ yield
+ ensure
+ orig.each do |key, value|
+ ENV[key] = value
+ end
+ end
+
+end
+
+module TestNetHTTP_version_1_1_methods
+
+ def test_s_get
+ assert_equal $test_net_http_data,
+ Net::HTTP.get(config('host'), '/', config('port'))
+ end
+
+ def test_head
+ start {|http|
+ res = http.head('/')
+ assert_kind_of Net::HTTPResponse, res
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_equal $test_net_http_data.size, res['Content-Length'].to_i
+ end
+ }
+ end
+
+ def test_get
+ start {|http|
+ _test_get__get http
+ _test_get__iter http
+ _test_get__chunked http
+ }
+ end
+
+ def _test_get__get(http)
+ res = http.get('/')
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+
+ assert_nothing_raised {
+ http.get('/', { 'User-Agent' => 'test' }.freeze)
+ }
+
+ assert res.decode_content, '[Bug #7924]' if Net::HTTP::HAVE_ZLIB
+ end
+
+ def _test_get__iter(http)
+ buf = ''
+ res = http.get('/') {|s| buf << s }
+ assert_kind_of Net::HTTPResponse, res
+ # assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_equal $test_net_http_data.size, buf.size
+ assert_equal $test_net_http_data, buf
+ # assert_equal $test_net_http_data.size, res.body.size
+ # assert_equal $test_net_http_data, res.body
+ end
+
+ def _test_get__chunked(http)
+ buf = ''
+ res = http.get('/') {|s| buf << s }
+ assert_kind_of Net::HTTPResponse, res
+ # assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_equal $test_net_http_data.size, buf.size
+ assert_equal $test_net_http_data, buf
+ # assert_equal $test_net_http_data.size, res.body.size
+ # assert_equal $test_net_http_data, res.body
+ end
+
+ def test_get__break
+ i = 0
+ start {|http|
+ http.get('/') do |str|
+ i += 1
+ break
+ end
+ }
+ assert_equal 1, i
+ @log_tester = nil # server may encount ECONNRESET
+ end
+
+ def test_get__implicit_start
+ res = new().get('/')
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+ end
+
+ def test_get2
+ start {|http|
+ http.get2('/') {|res|
+ EnvUtil.suppress_warning do
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of Net::HTTPResponse, res.header
+ end
+
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ end
+ assert_equal $test_net_http_data_type, res['Content-Type']
+ assert_kind_of String, res.body
+ assert_kind_of String, res.entity
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+ assert_equal $test_net_http_data, res.entity
+ }
+ }
+ end
+
+ def test_post
+ start {|http|
+ _test_post__base http
+ _test_post__file http
+ _test_post__no_data http
+ }
+ end
+
+ def _test_post__base(http)
+ uheader = {}
+ uheader['Accept'] = 'application/octet-stream'
+ uheader['Content-Type'] = 'application/x-www-form-urlencoded'
+ data = 'post data'
+ res = http.post('/', data, uheader)
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ assert_equal data, res.body
+ assert_equal data, res.entity
+ end
+
+ def _test_post__file(http)
+ data = 'post data'
+ f = StringIO.new
+ http.post('/', data, {'content-type' => 'application/x-www-form-urlencoded'}, f)
+ assert_equal data, f.string
+ end
+
+ def _test_post__no_data(http)
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ EnvUtil.suppress_warning do
+ data = nil
+ res = http.post('/', data)
+ assert_not_equal '411', res.code
+ end
+ end
+ end
+
+ def test_s_post_form
+ url = "http://#{config('host')}:#{config('port')}/"
+ res = Net::HTTP.post_form(
+ URI.parse(url),
+ "a" => "x")
+ assert_equal ["a=x"], res.body.split(/[;&]/).sort
+
+ res = Net::HTTP.post_form(
+ URI.parse(url),
+ "a" => "x",
+ "b" => "y")
+ assert_equal ["a=x", "b=y"], res.body.split(/[;&]/).sort
+
+ res = Net::HTTP.post_form(
+ URI.parse(url),
+ "a" => ["x1", "x2"],
+ "b" => "y")
+ assert_equal url, res['X-request-uri']
+ assert_equal ["a=x1", "a=x2", "b=y"], res.body.split(/[;&]/).sort
+
+ res = Net::HTTP.post_form(
+ URI.parse(url + '?a=x'),
+ "b" => "y")
+ assert_equal url + '?a=x', res['X-request-uri']
+ assert_equal ["b=y"], res.body.split(/[;&]/).sort
+ end
+
+ def test_patch
+ start {|http|
+ _test_patch__base http
+ }
+ end
+
+ def _test_patch__base(http)
+ uheader = {}
+ uheader['Accept'] = 'application/octet-stream'
+ uheader['Content-Type'] = 'application/x-www-form-urlencoded'
+ data = 'patch data'
+ res = http.patch('/', data, uheader)
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ assert_equal data, res.body
+ assert_equal data, res.entity
+ end
+
+ def test_timeout_during_HTTP_session
+ bug4246 = "expected the HTTP session to have timed out but have not. c.f. [ruby-core:34203]"
+
+ th = nil
+ # listen for connections... but deliberately do not read
+ TCPServer.open('localhost', 0) {|server|
+ port = server.addr[1]
+
+ conn = Net::HTTP.new('localhost', port)
+ conn.read_timeout = 0.01
+ conn.open_timeout = 0.1
+
+ th = Thread.new do
+ assert_raise(Net::ReadTimeout) {
+ conn.get('/')
+ }
+ end
+ assert th.join(10), bug4246
+ }
+ ensure
+ th.kill
+ th.join
+ end
+end
+
+
+module TestNetHTTP_version_1_2_methods
+
+ def test_request
+ start {|http|
+ _test_request__GET http
+ _test_request__accept_encoding http
+ _test_request__file http
+ # _test_request__range http # WEBrick does not support Range: header.
+ _test_request__HEAD http
+ _test_request__POST http
+ _test_request__stream_body http
+ _test_request__uri http
+ _test_request__uri_host http
+ }
+ end
+
+ def _test_request__GET(http)
+ req = Net::HTTP::Get.new('/')
+ http.request(req) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+
+ assert res.decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB
+ }
+ end
+
+ def _test_request__accept_encoding(http)
+ req = Net::HTTP::Get.new('/', 'accept-encoding' => 'deflate')
+ http.request(req) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_equal $test_net_http_data.size, res.body.size
+ assert_equal $test_net_http_data, res.body
+
+ refute res.decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB
+ }
+ end
+
+ def _test_request__file(http)
+ req = Net::HTTP::Get.new('/')
+ http.request(req) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ f = StringIO.new("".force_encoding("ASCII-8BIT"))
+ res.read_body f
+ assert_equal $test_net_http_data.bytesize, f.string.bytesize
+ assert_equal $test_net_http_data.encoding, f.string.encoding
+ assert_equal $test_net_http_data, f.string
+ }
+ end
+
+ def _test_request__range(http)
+ req = Net::HTTP::Get.new('/')
+ req['range'] = 'bytes=0-5'
+ assert_equal $test_net_http_data[0,6], http.request(req).body
+ end
+
+ def _test_request__HEAD(http)
+ req = Net::HTTP::Head.new('/')
+ http.request(req) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_nil res.body
+ }
+ end
+
+ def _test_request__POST(http)
+ data = 'post data'
+ req = Net::HTTP::Post.new('/')
+ req['Accept'] = $test_net_http_data_type
+ req['Content-Type'] = 'application/x-www-form-urlencoded'
+ http.request(req, data) {|res|
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_equal data.size, res['content-length'].to_i
+ end
+ assert_kind_of String, res.body
+ assert_equal data, res.body
+ }
+ end
+
+ def _test_request__stream_body(http)
+ req = Net::HTTP::Post.new('/')
+ data = $test_net_http_data
+ req.content_length = data.size
+ req['Content-Type'] = 'application/x-www-form-urlencoded'
+ req.body_stream = StringIO.new(data)
+ res = http.request(req)
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ assert_equal data.size, res.body.size
+ assert_equal data, res.body
+ end
+
+ def _test_request__path(http)
+ uri = URI 'https://example/'
+ req = Net::HTTP::Get.new('/')
+
+ res = http.request(req)
+
+ assert_kind_of URI::Generic, req.uri
+
+ refute_equal uri, req.uri
+
+ assert_equal uri, res.uri
+
+ refute_same uri, req.uri
+ refute_same req.uri, res.uri
+ end
+
+ def _test_request__uri(http)
+ uri = URI 'https://example/'
+ req = Net::HTTP::Get.new(uri)
+
+ res = http.request(req)
+
+ assert_kind_of URI::Generic, req.uri
+
+ refute_equal uri, req.uri
+
+ assert_equal req.uri, res.uri
+
+ refute_same uri, req.uri
+ refute_same req.uri, res.uri
+ end
+
+ def _test_request__uri_host(http)
+ uri = URI 'http://other.example/'
+
+ req = Net::HTTP::Get.new(uri)
+ req['host'] = 'example'
+
+ res = http.request(req)
+
+ assert_kind_of URI::Generic, req.uri
+
+ assert_equal URI("http://example:#{http.port}"), res.uri
+ end
+
+ def test_send_request
+ start {|http|
+ _test_send_request__GET http
+ _test_send_request__HEAD http
+ _test_send_request__POST http
+ }
+ end
+
+ def _test_send_request__GET(http)
+ res = http.send_request('GET', '/')
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_kind_of String, res.body
+ assert_equal $test_net_http_data, res.body
+ end
+
+ def _test_send_request__HEAD(http)
+ res = http.send_request('HEAD', '/')
+ assert_kind_of Net::HTTPResponse, res
+ unless self.is_a?(TestNetHTTP_v1_2_chunked)
+ assert_not_nil res['content-length']
+ assert_equal $test_net_http_data.size, res['content-length'].to_i
+ end
+ assert_nil res.body
+ end
+
+ def _test_send_request__POST(http)
+ data = 'aaabbb cc ddddddddddd lkjoiu4j3qlkuoa'
+ res = http.send_request('POST', '/', data, 'content-type' => 'application/x-www-form-urlencoded')
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ assert_equal data.size, res.body.size
+ assert_equal data, res.body
+ end
+
+ def test_set_form
+ require 'tempfile'
+ Tempfile.create('ruby-test') {|file|
+ file << "\u{30c7}\u{30fc}\u{30bf}"
+ data = [
+ ['name', 'Gonbei Nanashi'],
+ ['name', "\u{540d}\u{7121}\u{3057}\u{306e}\u{6a29}\u{5175}\u{885b}"],
+ ['s"i\o', StringIO.new("\u{3042 3044 4e9c 925b}")],
+ ["file", file, filename: "ruby-test"]
+ ]
+ expected = <<"__EOM__".gsub(/\n/, "\r\n")
+--<boundary>
+Content-Disposition: form-data; name="name"
+
+Gonbei Nanashi
+--<boundary>
+Content-Disposition: form-data; name="name"
+
+\xE5\x90\x8D\xE7\x84\xA1\xE3\x81\x97\xE3\x81\xAE\xE6\xA8\xA9\xE5\x85\xB5\xE8\xA1\x9B
+--<boundary>
+Content-Disposition: form-data; name="s\\"i\\\\o"
+
+\xE3\x81\x82\xE3\x81\x84\xE4\xBA\x9C\xE9\x89\x9B
+--<boundary>
+Content-Disposition: form-data; name="file"; filename="ruby-test"
+Content-Type: application/octet-stream
+
+\xE3\x83\x87\xE3\x83\xBC\xE3\x82\xBF
+--<boundary>--
+__EOM__
+ start {|http|
+ _test_set_form_urlencoded(http, data.reject{|k,v|!v.is_a?(String)})
+ _test_set_form_multipart(http, false, data, expected)
+ _test_set_form_multipart(http, true, data, expected)
+ }
+ }
+ end
+
+ def _test_set_form_urlencoded(http, data)
+ req = Net::HTTP::Post.new('/')
+ req.set_form(data)
+ res = http.request req
+ assert_equal "name=Gonbei+Nanashi&name=%E5%90%8D%E7%84%A1%E3%81%97%E3%81%AE%E6%A8%A9%E5%85%B5%E8%A1%9B", res.body
+ end
+
+ def _test_set_form_multipart(http, chunked_p, data, expected)
+ data.each{|k,v|v.rewind rescue nil}
+ req = Net::HTTP::Post.new('/')
+ req.set_form(data, 'multipart/form-data')
+ req['Transfer-Encoding'] = 'chunked' if chunked_p
+ res = http.request req
+ body = res.body
+ assert_match(/\A--(?<boundary>\S+)/, body)
+ /\A--(?<boundary>\S+)/ =~ body
+ expected = expected.gsub(/<boundary>/, boundary)
+ assert_equal(expected, body)
+ end
+
+ def test_set_form_with_file
+ require 'tempfile'
+ Tempfile.create('ruby-test') {|file|
+ file.binmode
+ file << $test_net_http_data
+ filename = File.basename(file.to_path)
+ data = [['file', file]]
+ expected = <<"__EOM__".gsub(/\n/, "\r\n")
+--<boundary>
+Content-Disposition: form-data; name="file"; filename="<filename>"
+Content-Type: application/octet-stream
+
+<data>
+--<boundary>--
+__EOM__
+ expected.sub!(/<filename>/, filename)
+ expected.sub!(/<data>/, $test_net_http_data)
+ start {|http|
+ data.each{|k,v|v.rewind rescue nil}
+ req = Net::HTTP::Post.new('/')
+ req.set_form(data, 'multipart/form-data')
+ res = http.request req
+ body = res.body
+ header, _ = body.split(/\r\n\r\n/, 2)
+ assert_match(/\A--(?<boundary>\S+)/, body)
+ /\A--(?<boundary>\S+)/ =~ body
+ expected = expected.gsub(/<boundary>/, boundary)
+ assert_match(/^--(?<boundary>\S+)\r\n/, header)
+ assert_match(
+ /^Content-Disposition: form-data; name="file"; filename="#{filename}"\r\n/,
+ header)
+ assert_equal(expected, body)
+
+ data.each{|k,v|v.rewind rescue nil}
+ req['Transfer-Encoding'] = 'chunked'
+ res = http.request req
+ #assert_equal(expected, res.body)
+ }
+ }
+ end
+end
+
+class TestNetHTTP_v1_2 < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ }
+
+ include TestNetHTTPUtils
+ include TestNetHTTP_version_1_1_methods
+ include TestNetHTTP_version_1_2_methods
+
+ def new
+ Net::HTTP.version_1_2
+ super
+ end
+end
+
+class TestNetHTTP_v1_2_chunked < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'chunked' => true,
+ }
+
+ include TestNetHTTPUtils
+ include TestNetHTTP_version_1_1_methods
+ include TestNetHTTP_version_1_2_methods
+
+ def new
+ Net::HTTP.version_1_2
+ super
+ end
+
+ def test_chunked_break
+ assert_nothing_raised("[ruby-core:29229]") {
+ start {|http|
+ http.request_get('/') {|res|
+ res.read_body {|chunk|
+ break
+ }
+ }
+ }
+ }
+ end
+end
+
+class TestNetHTTPContinue < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'chunked' => true,
+ }
+
+ include TestNetHTTPUtils
+
+ def logfile
+ @debug = StringIO.new('')
+ end
+
+ def mount_proc(&block)
+ @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc))
+ end
+
+ def test_expect_continue
+ mount_proc {|req, res|
+ req.continue
+ res.body = req.query['body']
+ }
+ start {|http|
+ uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
+ http.continue_timeout = 0.2
+ http.request_post('/continue', 'body=BODY', uheader) {|res|
+ assert_equal('BODY', res.read_body)
+ }
+ }
+ assert_match(/Expect: 100-continue/, @debug.string)
+ assert_match(/HTTP\/1.1 100 continue/, @debug.string)
+ end
+
+ def test_expect_continue_timeout
+ mount_proc {|req, res|
+ sleep 0.2
+ req.continue # just ignored because it's '100'
+ res.body = req.query['body']
+ }
+ start {|http|
+ uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
+ http.continue_timeout = 0
+ http.request_post('/continue', 'body=BODY', uheader) {|res|
+ assert_equal('BODY', res.read_body)
+ }
+ }
+ assert_match(/Expect: 100-continue/, @debug.string)
+ assert_match(/HTTP\/1.1 100 continue/, @debug.string)
+ end
+
+ def test_expect_continue_error
+ mount_proc {|req, res|
+ res.status = 501
+ res.body = req.query['body']
+ }
+ start {|http|
+ uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
+ http.continue_timeout = 0
+ http.request_post('/continue', 'body=ERROR', uheader) {|res|
+ assert_equal('ERROR', res.read_body)
+ }
+ }
+ assert_match(/Expect: 100-continue/, @debug.string)
+ assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
+ end
+
+ def test_expect_continue_error_while_waiting
+ mount_proc {|req, res|
+ res.status = 501
+ res.body = req.query['body']
+ }
+ start {|http|
+ uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
+ http.continue_timeout = 0.5
+ http.request_post('/continue', 'body=ERROR', uheader) {|res|
+ assert_equal('ERROR', res.read_body)
+ }
+ }
+ assert_match(/Expect: 100-continue/, @debug.string)
+ assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
+ end
+end
+
+class TestNetHTTPKeepAlive < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'RequestTimeout' => 1,
+ }
+
+ include TestNetHTTPUtils
+
+ def test_keep_alive_get_auto_reconnect
+ start {|http|
+ res = http.get('/')
+ http.keep_alive_timeout = 1
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ sleep 1.5
+ assert_nothing_raised {
+ res = http.get('/')
+ }
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ }
+ end
+
+ def test_keep_alive_get_auto_retry
+ start {|http|
+ res = http.get('/')
+ http.keep_alive_timeout = 5
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ sleep 1.5
+ res = http.get('/')
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ }
+ end
+
+ def test_keep_alive_server_close
+ def @server.run(sock)
+ sock.close
+ end
+
+ start {|http|
+ assert_raises(EOFError, Errno::ECONNRESET, IOError) {
+ http.get('/')
+ }
+ }
+ end
+end
+
+class TestNetHTTPLocalBind < Test::Unit::TestCase
+ CONFIG = {
+ 'host' => 'localhost',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ }
+
+ include TestNetHTTPUtils
+
+ def test_bind_to_local_host
+ @server.mount_proc('/show_ip') { |req, res| res.body = req.remote_ip }
+
+ http = Net::HTTP.new(config('host'), config('port'))
+ http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address
+ assert_not_nil(http.local_host)
+ assert_nil(http.local_port)
+
+ res = http.get('/show_ip')
+ assert_equal(http.local_host, res.body)
+ end
+
+ def test_bind_to_local_port
+ @server.mount_proc('/show_port') { |req, res| res.body = req.peeraddr[1].to_s }
+
+ http = Net::HTTP.new(config('host'), config('port'))
+ http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address
+ http.local_port = Addrinfo.tcp(config('host'), 0).bind {|s|
+ s.local_address.ip_port.to_s
+ }
+ assert_not_nil(http.local_host)
+ assert_not_nil(http.local_port)
+
+ res = http.get('/show_port')
+ assert_equal(http.local_port, res.body)
+ end
+end
+
diff --git a/jni/ruby/test/net/http/test_http_request.rb b/jni/ruby/test/net/http/test_http_request.rb
new file mode 100644
index 0000000..1dcb847
--- /dev/null
+++ b/jni/ruby/test/net/http/test_http_request.rb
@@ -0,0 +1,79 @@
+require 'net/http'
+require 'test/unit'
+require 'stringio'
+
+class HTTPRequestTest < Test::Unit::TestCase
+
+ def test_initialize_GET
+ req = Net::HTTP::Get.new '/'
+
+ assert_equal 'GET', req.method
+ refute req.request_body_permitted?
+ assert req.response_body_permitted?
+
+ expected = {
+ 'accept' => %w[*/*],
+ 'user-agent' => %w[Ruby],
+ }
+
+ expected['accept-encoding'] = %w[gzip;q=1.0,deflate;q=0.6,identity;q=0.3] if
+ Net::HTTP::HAVE_ZLIB
+
+ assert_equal expected, req.to_hash
+ end
+
+ def test_initialize_GET_range
+ req = Net::HTTP::Get.new '/', 'Range' => 'bytes=0-9'
+
+ assert_equal 'GET', req.method
+ refute req.request_body_permitted?
+ assert req.response_body_permitted?
+
+ expected = {
+ 'accept' => %w[*/*],
+ 'user-agent' => %w[Ruby],
+ 'range' => %w[bytes=0-9],
+ }
+
+ assert_equal expected, req.to_hash
+ end
+
+ def test_initialize_HEAD
+ req = Net::HTTP::Head.new '/'
+
+ assert_equal 'HEAD', req.method
+ refute req.request_body_permitted?
+ refute req.response_body_permitted?
+
+ expected = {
+ 'accept' => %w[*/*],
+ 'user-agent' => %w[Ruby],
+ }
+
+ assert_equal expected, req.to_hash
+ end
+
+ def test_initialize_accept_encoding
+ req1 = Net::HTTP::Get.new '/'
+
+ assert req1.decode_content, 'Bug #7831 - automatically decode content'
+
+ req2 = Net::HTTP::Get.new '/', 'accept-encoding' => 'identity'
+
+ refute req2.decode_content,
+ 'Bug #7381 - do not decode content if the user overrides'
+ end if Net::HTTP::HAVE_ZLIB
+
+ def test_header_set
+ req = Net::HTTP::Get.new '/'
+
+ assert req.decode_content, 'Bug #7831 - automatically decode content'
+
+ req['accept-encoding'] = 'identity'
+
+ refute req.decode_content,
+ 'Bug #7831 - do not decode content if the user overrides'
+ end if Net::HTTP::HAVE_ZLIB
+
+end
+
diff --git a/jni/ruby/test/net/http/test_httpheader.rb b/jni/ruby/test/net/http/test_httpheader.rb
new file mode 100644
index 0000000..0623871
--- /dev/null
+++ b/jni/ruby/test/net/http/test_httpheader.rb
@@ -0,0 +1,334 @@
+require 'net/http'
+require 'test/unit'
+
+class HTTPHeaderTest < Test::Unit::TestCase
+
+ class C
+ include Net::HTTPHeader
+ def initialize
+ initialize_http_header({})
+ end
+ attr_accessor :body
+ end
+
+ def setup
+ @c = C.new
+ end
+
+ def test_size
+ assert_equal 0, @c.size
+ @c['a'] = 'a'
+ assert_equal 1, @c.size
+ @c['b'] = 'b'
+ assert_equal 2, @c.size
+ @c['b'] = 'b'
+ assert_equal 2, @c.size
+ @c['c'] = 'c'
+ assert_equal 3, @c.size
+ end
+
+ def test_ASET
+ @c['My-Header'] = 'test string'
+ @c['my-Header'] = 'test string'
+ @c['My-header'] = 'test string'
+ @c['my-header'] = 'test string'
+ @c['MY-HEADER'] = 'test string'
+ assert_equal 1, @c.size
+
+ @c['AaA'] = 'aaa'
+ @c['aaA'] = 'aaa'
+ @c['AAa'] = 'aaa'
+ assert_equal 2, @c.length
+ end
+
+ def test_AREF
+ @c['My-Header'] = 'test string'
+ assert_equal 'test string', @c['my-header']
+ assert_equal 'test string', @c['MY-header']
+ assert_equal 'test string', @c['my-HEADER']
+
+ @c['Next-Header'] = 'next string'
+ assert_equal 'next string', @c['next-header']
+ end
+
+ def test_add_field
+ @c.add_field 'My-Header', 'a'
+ assert_equal 'a', @c['My-Header']
+ assert_equal ['a'], @c.get_fields('My-Header')
+ @c.add_field 'My-Header', 'b'
+ assert_equal 'a, b', @c['My-Header']
+ assert_equal ['a', 'b'], @c.get_fields('My-Header')
+ @c.add_field 'My-Header', 'c'
+ assert_equal 'a, b, c', @c['My-Header']
+ assert_equal ['a', 'b', 'c'], @c.get_fields('My-Header')
+ @c.add_field 'My-Header', 'd, d'
+ assert_equal 'a, b, c, d, d', @c['My-Header']
+ assert_equal ['a', 'b', 'c', 'd, d'], @c.get_fields('My-Header')
+ end
+
+ def test_get_fields
+ @c['My-Header'] = 'test string'
+ assert_equal ['test string'], @c.get_fields('my-header')
+ assert_equal ['test string'], @c.get_fields('My-header')
+ assert_equal ['test string'], @c.get_fields('my-Header')
+
+ assert_nil @c.get_fields('not-found')
+ assert_nil @c.get_fields('Not-Found')
+
+ @c.get_fields('my-header').push 'junk'
+ assert_equal ['test string'], @c.get_fields('my-header')
+ @c.get_fields('my-header').clear
+ assert_equal ['test string'], @c.get_fields('my-header')
+ end
+
+ def test_delete
+ @c['My-Header'] = 'test'
+ assert_equal 'test', @c['My-Header']
+ assert_nil @c['not-found']
+ @c.delete 'My-Header'
+ assert_nil @c['My-Header']
+ assert_nil @c['not-found']
+ @c.delete 'My-Header'
+ @c.delete 'My-Header'
+ assert_nil @c['My-Header']
+ assert_nil @c['not-found']
+ end
+
+ def test_each
+ @c['My-Header'] = 'test'
+ @c.each do |k, v|
+ assert_equal 'my-header', k
+ assert_equal 'test', v
+ end
+ @c.each do |k, v|
+ assert_equal 'my-header', k
+ assert_equal 'test', v
+ end
+ end
+
+ def test_each_key
+ @c['My-Header'] = 'test'
+ @c.each_key do |k|
+ assert_equal 'my-header', k
+ end
+ @c.each_key do |k|
+ assert_equal 'my-header', k
+ end
+ end
+
+ def test_each_value
+ @c['My-Header'] = 'test'
+ @c.each_value do |v|
+ assert_equal 'test', v
+ end
+ @c.each_value do |v|
+ assert_equal 'test', v
+ end
+ end
+
+ def test_canonical_each
+ @c['my-header'] = ['a', 'b']
+ @c.canonical_each do |k,v|
+ assert_equal 'My-Header', k
+ assert_equal 'a, b', v
+ end
+ end
+
+ def test_each_capitalized
+ @c['my-header'] = ['a', 'b']
+ @c.each_capitalized do |k,v|
+ assert_equal 'My-Header', k
+ assert_equal 'a, b', v
+ end
+ end
+
+ def test_key?
+ @c['My-Header'] = 'test'
+ assert_equal true, @c.key?('My-Header')
+ assert_equal true, @c.key?('my-header')
+ assert_equal false, @c.key?('Not-Found')
+ assert_equal false, @c.key?('not-found')
+ assert_equal false, @c.key?('')
+ assert_equal false, @c.key?('x' * 1024)
+ end
+
+ def test_to_hash
+ end
+
+ def test_range
+ try_range([1..5], '1-5')
+ try_invalid_range('5-1')
+ try_range([234..567], '234-567')
+ try_range([-5..-1], '-5')
+ try_invalid_range('-0')
+ try_range([1..-1], '1-')
+ try_range([0..0,-1..-1], '0-0,-1')
+ try_range([1..2, 3..4], '1-2,3-4')
+ try_range([1..2, 3..4], '1-2 , 3-4')
+ try_range([1..2, 1..4], '1-2,1-4')
+
+ try_invalid_range('invalid')
+ try_invalid_range(' 12-')
+ try_invalid_range('12- ')
+ try_invalid_range('123-abc')
+ try_invalid_range('abc-123')
+ end
+
+ def try_range(r, s)
+ @c['range'] = "bytes=#{s}"
+ assert_equal r, @c.range
+ end
+
+ def try_invalid_range(s)
+ @c['range'] = "bytes=#{s}"
+ assert_raise(Net::HTTPHeaderSyntaxError, s){ @c.range }
+ end
+
+ def test_range=
+ @c.range = 0..499
+ assert_equal 'bytes=0-499', @c['range']
+ @c.range = 0...500
+ assert_equal 'bytes=0-499', @c['range']
+ @c.range = 300
+ assert_equal 'bytes=0-299', @c['range']
+ @c.range = -400
+ assert_equal 'bytes=-400', @c['range']
+ @c.set_range 0, 500
+ assert_equal 'bytes=0-499', @c['range']
+ end
+
+ def test_content_range
+ end
+
+ def test_range_length
+ @c['Content-Range'] = "bytes 0-499/1000"
+ assert_equal 500, @c.range_length
+ @c['Content-Range'] = "bytes 1-500/1000"
+ assert_equal 500, @c.range_length
+ @c['Content-Range'] = "bytes 1-1/1000"
+ assert_equal 1, @c.range_length
+ end
+
+ def test_chunked?
+ try_chunked true, 'chunked'
+ try_chunked true, ' chunked '
+ try_chunked true, '(OK)chunked'
+
+ try_chunked false, 'not-chunked'
+ try_chunked false, 'chunked-but-not-chunked'
+ end
+
+ def try_chunked(bool, str)
+ @c['transfer-encoding'] = str
+ assert_equal bool, @c.chunked?
+ end
+
+ def test_content_length
+ @c.delete('content-length')
+ assert_nil @c['content-length']
+
+ try_content_length 500, '500'
+ try_content_length 10000_0000_0000, '1000000000000'
+ try_content_length 123, ' 123'
+ try_content_length 1, '1 23'
+ try_content_length 500, '(OK)500'
+ assert_raise(Net::HTTPHeaderSyntaxError, 'here is no digit, but') {
+ @c['content-length'] = 'no digit'
+ @c.content_length
+ }
+ end
+
+ def try_content_length(len, str)
+ @c['content-length'] = str
+ assert_equal len, @c.content_length
+ end
+
+ def test_content_length=
+ @c.content_length = 0
+ assert_equal 0, @c.content_length
+ @c.content_length = 1
+ assert_equal 1, @c.content_length
+ @c.content_length = 999
+ assert_equal 999, @c.content_length
+ @c.content_length = 10000000000000
+ assert_equal 10000000000000, @c.content_length
+ end
+
+ def test_content_type
+ assert_nil @c.content_type
+ @c.content_type = 'text/html'
+ assert_equal 'text/html', @c.content_type
+ @c.content_type = 'application/pdf'
+ assert_equal 'application/pdf', @c.content_type
+ @c.set_content_type 'text/html', {'charset' => 'iso-2022-jp'}
+ assert_equal 'text/html', @c.content_type
+ @c.content_type = 'text'
+ assert_equal 'text', @c.content_type
+ end
+
+ def test_main_type
+ assert_nil @c.main_type
+ @c.content_type = 'text/html'
+ assert_equal 'text', @c.main_type
+ @c.content_type = 'application/pdf'
+ assert_equal 'application', @c.main_type
+ @c.set_content_type 'text/html', {'charset' => 'iso-2022-jp'}
+ assert_equal 'text', @c.main_type
+ @c.content_type = 'text'
+ assert_equal 'text', @c.main_type
+ end
+
+ def test_sub_type
+ assert_nil @c.sub_type
+ @c.content_type = 'text/html'
+ assert_equal 'html', @c.sub_type
+ @c.content_type = 'application/pdf'
+ assert_equal 'pdf', @c.sub_type
+ @c.set_content_type 'text/html', {'charset' => 'iso-2022-jp'}
+ assert_equal 'html', @c.sub_type
+ @c.content_type = 'text'
+ assert_nil @c.sub_type
+ end
+
+ def test_type_params
+ assert_equal({}, @c.type_params)
+ @c.content_type = 'text/html'
+ assert_equal({}, @c.type_params)
+ @c.content_type = 'application/pdf'
+ assert_equal({}, @c.type_params)
+ @c.set_content_type 'text/html', {'charset' => 'iso-2022-jp'}
+ assert_equal({'charset' => 'iso-2022-jp'}, @c.type_params)
+ @c.content_type = 'text'
+ assert_equal({}, @c.type_params)
+ end
+
+ def test_set_content_type
+ end
+
+ def test_form_data=
+ @c.form_data = {"cmd"=>"search", "q"=>"ruby", "max"=>"50"}
+ assert_equal 'application/x-www-form-urlencoded', @c.content_type
+ assert_equal %w( cmd=search max=50 q=ruby ), @c.body.split('&').sort
+ end
+
+ def test_set_form_data
+ @c.set_form_data "cmd"=>"search", "q"=>"ruby", "max"=>"50"
+ assert_equal 'application/x-www-form-urlencoded', @c.content_type
+ assert_equal %w( cmd=search max=50 q=ruby ), @c.body.split('&').sort
+
+ @c.set_form_data "cmd"=>"search", "q"=>"ruby", "max"=>50
+ assert_equal 'application/x-www-form-urlencoded', @c.content_type
+ assert_equal %w( cmd=search max=50 q=ruby ), @c.body.split('&').sort
+
+ @c.set_form_data({"cmd"=>"search", "q"=>"ruby", "max"=>"50"}, ';')
+ assert_equal 'application/x-www-form-urlencoded', @c.content_type
+ assert_equal %w( cmd=search max=50 q=ruby ), @c.body.split(';').sort
+ end
+
+ def test_basic_auth
+ end
+
+ def test_proxy_basic_auth
+ end
+
+end
diff --git a/jni/ruby/test/net/http/test_httpresponse.rb b/jni/ruby/test/net/http/test_httpresponse.rb
new file mode 100644
index 0000000..404c7ae
--- /dev/null
+++ b/jni/ruby/test/net/http/test_httpresponse.rb
@@ -0,0 +1,394 @@
+# coding: US-ASCII
+require 'net/http'
+require 'test/unit'
+require 'stringio'
+
+class HTTPResponseTest < Test::Unit::TestCase
+ def test_singleline_header
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('5', res['content-length'])
+ assert_equal('close', res['connection'])
+ end
+
+ def test_multiline_header
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+X-Foo: XXX
+ YYY
+X-Bar:
+ XXX
+\tYYY
+
+hello
+EOS
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('XXX YYY', res['x-foo'])
+ assert_equal('XXX YYY', res['x-bar'])
+ end
+
+ def test_read_body
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Length: 5
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ assert_equal 'hello', body
+ end
+
+ def test_read_body_block
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Length: 5
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ body = ''
+
+ res.reading_body io, true do
+ res.read_body do |chunk|
+ body << chunk
+ end
+ end
+
+ assert_equal 'hello', body
+ end
+
+ def test_read_body_content_encoding_deflate
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+Content-Length: 13
+
+x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal 'hello', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_uppercase
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: DEFLATE
+Content-Length: 13
+
+x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal 'hello', body
+ else
+ assert_equal 'DEFLATE', res['content-encoding']
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_chunked
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+Transfer-Encoding: chunked
+
+6
+x\x9C\xCBH\xCD\xC9
+7
+\xC9\a\x00\x06,\x02\x15
+0
+
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal 'hello', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_disabled
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+Content-Length: 13
+
+x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = false # user set accept-encoding in request
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ assert_equal 'deflate', res['content-encoding'], 'Bug #7831'
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15", body, 'Bug #7381'
+ end
+
+ def test_read_body_content_encoding_deflate_no_length
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+
+x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal 'hello', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15\r\n", body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_content_range
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Accept-Ranges: bytes
+Connection: close
+Content-Encoding: gzip
+Content-Length: 10
+Content-Range: bytes 0-9/55
+
+\x1F\x8B\b\x00\x00\x00\x00\x00\x00\x03
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ assert_equal "\x1F\x8B\b\x00\x00\x00\x00\x00\x00\x03", body
+ end
+
+ def test_read_body_content_encoding_deflate_empty_body
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+Content-Length: 0
+
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal '', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal '', body
+ end
+ end
+
+ def test_read_body_content_encoding_deflate_empty_body_no_length
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Encoding: deflate
+
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ res.decode_content = true
+
+ body = nil
+
+ res.reading_body io, true do
+ body = res.read_body
+ end
+
+ if Net::HTTP::HAVE_ZLIB
+ assert_equal nil, res['content-encoding']
+ assert_equal '', body
+ else
+ assert_equal 'deflate', res['content-encoding']
+ assert_equal '', body
+ end
+ end
+
+ def test_read_body_string
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Connection: close
+Content-Length: 5
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+
+ body = ''
+
+ res.reading_body io, true do
+ res.read_body body
+ end
+
+ assert_equal 'hello', body
+ end
+
+ def test_uri_equals
+ uri = URI 'http://example'
+
+ response = Net::HTTPResponse.new '1.1', 200, 'OK'
+
+ response.uri = nil
+
+ assert_nil response.uri
+
+ response.uri = uri
+
+ assert_equal uri, response.uri
+ refute_same uri, response.uri
+ end
+
+ def test_ensure_zero_space_does_not_regress
+ io = dummy_io(<<EOS)
+HTTP/1.1 200OK
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+
+ assert_raises Net::HTTPBadResponse do
+ Net::HTTPResponse.read_new(io)
+ end
+ end
+
+ def test_allow_trailing_space_after_status
+ io = dummy_io(<<EOS)
+HTTP/1.1 200\s
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('1.1', res.http_version)
+ assert_equal('200', res.code)
+ assert_equal('', res.message)
+ end
+
+ def test_normal_status_line
+ io = dummy_io(<<EOS)
+HTTP/1.1 200 OK
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('1.1', res.http_version)
+ assert_equal('200', res.code)
+ assert_equal('OK', res.message)
+ end
+
+ def test_allow_empty_reason_code
+ io = dummy_io(<<EOS)
+HTTP/1.1 200
+Content-Length: 5
+Connection: close
+
+hello
+EOS
+
+ res = Net::HTTPResponse.read_new(io)
+ assert_equal('1.1', res.http_version)
+ assert_equal('200', res.code)
+ assert_equal(nil, res.message)
+ end
+
+private
+
+ def dummy_io(str)
+ str = str.gsub(/\n/, "\r\n")
+
+ Net::BufferedIO.new(StringIO.new(str))
+ end
+end
diff --git a/jni/ruby/test/net/http/test_httpresponses.rb b/jni/ruby/test/net/http/test_httpresponses.rb
new file mode 100644
index 0000000..bf7fbee
--- /dev/null
+++ b/jni/ruby/test/net/http/test_httpresponses.rb
@@ -0,0 +1,24 @@
+require 'net/http'
+require 'test/unit'
+
+class HTTPResponsesTest < Test::Unit::TestCase
+ def test_status_code_classes
+ Net::HTTPResponse::CODE_TO_OBJ.each_pair { |code, klass|
+ case code
+ when /\A1\d\d\z/
+ group = Net::HTTPInformation
+ when /\A2\d\d\z/
+ group = Net::HTTPSuccess
+ when /\A3\d\d\z/
+ group = Net::HTTPRedirection
+ when /\A4\d\d\z/
+ group = Net::HTTPClientError
+ when /\A5\d\d\z/
+ group = Net::HTTPServerError
+ else
+ flunk "Unknown HTTP status code: #{code} => #{klass.name}"
+ end
+ assert(klass < group, "#{klass.name} (#{code}) must inherit from #{group.name}")
+ }
+ end
+end
diff --git a/jni/ruby/test/net/http/test_https.rb b/jni/ruby/test/net/http/test_https.rb
new file mode 100644
index 0000000..8177d94
--- /dev/null
+++ b/jni/ruby/test/net/http/test_https.rb
@@ -0,0 +1,194 @@
+require "test/unit"
+begin
+ require 'net/https'
+ require 'stringio'
+ require 'timeout'
+ require File.expand_path("../../openssl/utils", File.dirname(__FILE__))
+ require File.expand_path("utils", File.dirname(__FILE__))
+rescue LoadError
+ # should skip this test
+end
+
+class TestNetHTTPS < Test::Unit::TestCase
+ include TestNetHTTPUtils
+
+ subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+ exts = [
+ ["keyUsage", "keyEncipherment,digitalSignature", true],
+ ]
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ cert = OpenSSL::TestUtils.issue_cert(
+ subject, key, 1, Time.now, Time.now + 3600, exts,
+ nil, nil, OpenSSL::Digest::SHA1.new
+ )
+
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'ssl_enable' => true,
+ 'ssl_certificate' => cert,
+ 'ssl_private_key' => key,
+ }
+
+ def test_get
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ rescue SystemCallError
+ skip $!
+ end
+
+ def test_post
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ data = config('ssl_private_key').to_der
+ http.request_post("/", data, {'content-type' => 'application/x-www-form-urlencoded'}) {|res|
+ assert_equal(data, res.body)
+ }
+ rescue SystemCallError
+ skip $!
+ end
+
+ def test_session_reuse
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+
+ http.start
+ http.get("/")
+ http.finish
+
+ http.start
+ http.get("/")
+ http.finish # three times due to possible bug in OpenSSL 0.9.8
+
+ sid = http.instance_variable_get(:@ssl_session).id
+
+ http.start
+ http.get("/")
+
+ socket = http.instance_variable_get(:@socket).io
+
+ assert socket.session_reused?
+
+ assert_equal sid, http.instance_variable_get(:@ssl_session).id
+
+ http.finish
+ rescue SystemCallError
+ skip $!
+ end
+
+ def test_session_reuse_but_expire
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+
+ http.ssl_timeout = -1
+ http.start
+ http.get("/")
+ http.finish
+
+ sid = http.instance_variable_get(:@ssl_session).id
+
+ http.start
+ http.get("/")
+
+ socket = http.instance_variable_get(:@socket).io
+ assert_equal false, socket.session_reused?
+
+ assert_not_equal sid, http.instance_variable_get(:@ssl_session).id
+
+ http.finish
+ rescue SystemCallError
+ skip $!
+ end
+
+ if ENV["RUBY_OPENSSL_TEST_ALL"]
+ def test_verify
+ http = Net::HTTP.new("ssl.netlab.jp", 443)
+ http.use_ssl = true
+ assert(
+ (http.request_head("/"){|res| } rescue false),
+ "The system may not have default CA certificate store."
+ )
+ end
+ end
+
+ def test_verify_none
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ rescue SystemCallError
+ skip $!
+ end
+
+ def test_certificate_verify_failure
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ ex = assert_raise(OpenSSL::SSL::SSLError){
+ begin
+ http.request_get("/") {|res| }
+ rescue SystemCallError
+ skip $!
+ end
+ }
+ assert_match(/certificate verify failed/, ex.message)
+ unless /mswin|mingw/ =~ RUBY_PLATFORM
+ # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by
+ # WEBrick
+ @log_tester = lambda {|log|
+ assert_equal(1, log.length)
+ assert_match(/ERROR OpenSSL::SSL::SSLError:/, log[0])
+ }
+ end
+ end
+
+ def test_identity_verify_failure
+ http = Net::HTTP.new("127.0.0.1", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ ex = assert_raise(OpenSSL::SSL::SSLError){
+ http.request_get("/") {|res| }
+ }
+ assert_match(/hostname \"127.0.0.1\" does not match/, ex.message)
+ end
+
+ def test_timeout_during_SSL_handshake
+ bug4246 = "expected the SSL connection to have timed out but have not. [ruby-core:34203]"
+
+ # listen for connections... but deliberately do not complete SSL handshake
+ TCPServer.open('localhost', 0) {|server|
+ port = server.addr[1]
+
+ conn = Net::HTTP.new('localhost', port)
+ conn.use_ssl = true
+ conn.read_timeout = 0.01
+ conn.open_timeout = 0.01
+
+ th = Thread.new do
+ assert_raise(Net::OpenTimeout) {
+ conn.get('/')
+ }
+ end
+ assert th.join(10), bug4246
+ }
+ end
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/net/http/test_https_proxy.rb b/jni/ruby/test/net/http/test_https_proxy.rb
new file mode 100644
index 0000000..1c8503b
--- /dev/null
+++ b/jni/ruby/test/net/http/test_https_proxy.rb
@@ -0,0 +1,46 @@
+begin
+ require 'net/https'
+rescue LoadError
+end
+require 'test/unit'
+
+class HTTPSProxyTest < Test::Unit::TestCase
+ def test_https_proxy_authentication
+ begin
+ OpenSSL
+ rescue LoadError
+ skip 'autoload problem. see [ruby-dev:45021][Bug #5786]'
+ end
+
+ TCPServer.open("127.0.0.1", 0) {|serv|
+ _, port, _, _ = serv.addr
+ client_thread = Thread.new {
+ proxy = Net::HTTP.Proxy("127.0.0.1", port, 'user', 'password')
+ http = proxy.new("foo.example.org", 8000)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ begin
+ http.start
+ rescue EOFError
+ end
+ }
+ server_thread = Thread.new {
+ sock = serv.accept
+ begin
+ proxy_request = sock.gets("\r\n\r\n")
+ assert_equal(
+ "CONNECT foo.example.org:8000 HTTP/1.1\r\n" +
+ "Host: foo.example.org:8000\r\n" +
+ "Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n" +
+ "\r\n",
+ proxy_request,
+ "[ruby-dev:25673]")
+ ensure
+ sock.close
+ end
+ }
+ assert_join_threads([client_thread, server_thread])
+ }
+ end
+end if defined?(OpenSSL)
+
diff --git a/jni/ruby/test/net/http/utils.rb b/jni/ruby/test/net/http/utils.rb
new file mode 100644
index 0000000..dcd9469
--- /dev/null
+++ b/jni/ruby/test/net/http/utils.rb
@@ -0,0 +1,108 @@
+require 'webrick'
+begin
+ require "webrick/https"
+rescue LoadError
+ # SSL features cannot be tested
+end
+require 'webrick/httpservlet/abstract'
+
+module TestNetHTTPUtils
+ def start(&block)
+ new().start(&block)
+ end
+
+ def new
+ klass = Net::HTTP::Proxy(config('proxy_host'), config('proxy_port'))
+ http = klass.new(config('host'), config('port'))
+ http.set_debug_output logfile()
+ http
+ end
+
+ def config(key)
+ @config ||= self.class::CONFIG
+ @config[key]
+ end
+
+ def logfile
+ $DEBUG ? $stderr : NullWriter.new
+ end
+
+ def setup
+ spawn_server
+ end
+
+ def teardown
+ if @server
+ @server.shutdown
+ @server_thread.join
+ end
+ @log_tester.call(@log) if @log_tester
+ # resume global state
+ Net::HTTP.version_1_2
+ end
+
+ def spawn_server
+ @log = []
+ @log_tester = lambda {|log| assert_equal([], log ) }
+ @config = self.class::CONFIG
+ server_config = {
+ :BindAddress => config('host'),
+ :Port => 0,
+ :Logger => WEBrick::Log.new(@log, WEBrick::BasicLog::WARN),
+ :AccessLog => [],
+ :ServerType => Thread,
+ }
+ server_config[:OutputBufferSize] = 4 if config('chunked')
+ server_config[:RequestTimeout] = config('RequestTimeout') if config('RequestTimeout')
+ if defined?(OpenSSL) and config('ssl_enable')
+ server_config.update({
+ :SSLEnable => true,
+ :SSLCertificate => config('ssl_certificate'),
+ :SSLPrivateKey => config('ssl_private_key'),
+ :SSLTmpDhCallback => proc { OpenSSL::TestUtils::TEST_KEY_DH1024 },
+ })
+ end
+ @server = WEBrick::HTTPServer.new(server_config)
+ @server.mount('/', Servlet, config('chunked'))
+ @server_thread = @server.start
+ @config['port'] = @server[:Port]
+ end
+
+ $test_net_http = nil
+ $test_net_http_data = (0...256).to_a.map {|i| i.chr }.join('') * 64
+ $test_net_http_data.force_encoding("ASCII-8BIT")
+ $test_net_http_data_type = 'application/octet-stream'
+
+ class Servlet < WEBrick::HTTPServlet::AbstractServlet
+ def initialize(this, chunked = false)
+ @chunked = chunked
+ end
+
+ def do_GET(req, res)
+ res['Content-Type'] = $test_net_http_data_type
+ res.body = $test_net_http_data
+ res.chunked = @chunked
+ end
+
+ # echo server
+ def do_POST(req, res)
+ res['Content-Type'] = req['Content-Type']
+ res['X-request-uri'] = req.request_uri.to_s
+ res.body = req.body
+ res.chunked = @chunked
+ end
+
+ def do_PATCH(req, res)
+ res['Content-Type'] = req['Content-Type']
+ res.body = req.body
+ res.chunked = @chunked
+ end
+ end
+
+ class NullWriter
+ def <<(s) end
+ def puts(*args) end
+ def print(*args) end
+ def printf(*args) end
+ end
+end
diff --git a/jni/ruby/test/net/imap/Makefile b/jni/ruby/test/net/imap/Makefile
new file mode 100644
index 0000000..b2bc9c7
--- /dev/null
+++ b/jni/ruby/test/net/imap/Makefile
@@ -0,0 +1,15 @@
+all:
+
+regen_certs:
+ touch server.key
+ make server.crt
+
+cacert.pem: server.key
+ openssl req -new -x509 -days 1825 -key server.key -out cacert.pem -text -subj "/C=JP/ST=Shimane/L=Matz-e city/O=Ruby Core Team/CN=Ruby Test CA/emailAddress=security@ruby-lang.org"
+
+server.csr:
+ openssl req -new -key server.key -out server.csr -text -subj "/C=JP/ST=Shimane/O=Ruby Core Team/OU=Ruby Test/CN=localhost"
+
+server.crt: server.csr cacert.pem
+ openssl x509 -days 1825 -CA cacert.pem -CAkey server.key -set_serial 00 -in server.csr -req -text -out server.crt
+ rm server.csr
diff --git a/jni/ruby/test/net/imap/cacert.pem b/jni/ruby/test/net/imap/cacert.pem
new file mode 100644
index 0000000..7073387
--- /dev/null
+++ b/jni/ruby/test/net/imap/cacert.pem
@@ -0,0 +1,66 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ b9:90:a2:bf:62:69:17:9c
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ Validity
+ Not Before: Jan 3 01:34:17 2014 GMT
+ Not After : Jan 2 01:34:17 2019 GMT
+ Subject: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:db:75:d0:45:de:b1:df:bf:71:a0:0e:b0:a5:e6:
+ bc:f4:1c:9d:e5:25:67:64:c5:7b:cb:f1:af:c6:be:
+ 9a:aa:ea:7e:0f:cc:05:af:ef:40:69:06:b2:c9:13:
+ 9d:7e:eb:a2:06:e2:ea:7d:07:c7:c7:99:c7:fb:d5:
+ b8:eb:63:77:62:2b:18:12:c3:53:58:d0:f5:c7:40:
+ 0c:01:d1:26:82:34:16:09:e3:dc:65:f4:dc:bb:5d:
+ a5:41:60:e7:a9:74:ba:d7:4c:b6:a3:9c:c5:8c:89:
+ af:cb:e8:9f:05:fe:ea:fe:64:24:bf:e7:ed:e3:f6:
+ d0:fc:d6:eb:fc:06:82:10:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ E8:7E:58:AC:13:7B:03:22:8D:9E:AF:32:0B:84:89:80:80:0C:1E:C2
+ X509v3 Authority Key Identifier:
+ keyid:E8:7E:58:AC:13:7B:03:22:8D:9E:AF:32:0B:84:89:80:80:0C:1E:C2
+ DirName:/C=JP/ST=Shimane/L=Matz-e city/O=Ruby Core Team/CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ serial:B9:90:A2:BF:62:69:17:9C
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 8f:77:06:4e:31:72:12:ee:68:09:70:27:d4:31:85:ef:10:95:
+ f9:0f:2b:66:63:08:37:88:6e:b7:9b:40:3e:18:77:33:86:e8:
+ 61:6a:b7:3c:cb:c7:a6:d6:d5:92:6a:1f:56:d0:9f:5c:32:56:
+ d3:37:52:fe:0e:20:c2:7a:0d:fe:2d:3c:81:da:b8:7f:4d:6a:
+ 08:01:d9:be:7a:a2:15:be:a6:ce:49:64:90:8c:9a:ca:6e:2e:
+ 84:48:1d:94:19:56:94:46:aa:25:9b:68:c2:80:60:bf:cb:2e:
+ 35:03:ea:0a:65:5a:33:38:c6:cc:81:46:c0:bc:36:86:96:39:
+ 10:7d
+-----BEGIN CERTIFICATE-----
+MIIDjTCCAvagAwIBAgIJALmQor9iaRecMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD
+VQQGEwJKUDEQMA4GA1UECBMHU2hpbWFuZTEUMBIGA1UEBxMLTWF0ei1lIGNpdHkx
+FzAVBgNVBAoTDlJ1YnkgQ29yZSBUZWFtMRUwEwYDVQQDEwxSdWJ5IFRlc3QgQ0Ex
+JTAjBgkqhkiG9w0BCQEWFnNlY3VyaXR5QHJ1YnktbGFuZy5vcmcwHhcNMTQwMTAz
+MDEzNDE3WhcNMTkwMTAyMDEzNDE3WjCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgT
+B1NoaW1hbmUxFDASBgNVBAcTC01hdHotZSBjaXR5MRcwFQYDVQQKEw5SdWJ5IENv
+cmUgVGVhbTEVMBMGA1UEAxMMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
+ZWN1cml0eUBydWJ5LWxhbmcub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDbddBF3rHfv3GgDrCl5rz0HJ3lJWdkxXvL8a/Gvpqq6n4PzAWv70BpBrLJE51+
+66IG4up9B8fHmcf71bjrY3diKxgSw1NY0PXHQAwB0SaCNBYJ49xl9Ny7XaVBYOep
+dLrXTLajnMWMia/L6J8F/ur+ZCS/5+3j9tD81uv8BoIQ+wIDAQABo4H0MIHxMB0G
+A1UdDgQWBBToflisE3sDIo2erzILhImAgAwewjCBwQYDVR0jBIG5MIG2gBToflis
+E3sDIo2erzILhImAgAwewqGBkqSBjzCBjDELMAkGA1UEBhMCSlAxEDAOBgNVBAgT
+B1NoaW1hbmUxFDASBgNVBAcTC01hdHotZSBjaXR5MRcwFQYDVQQKEw5SdWJ5IENv
+cmUgVGVhbTEVMBMGA1UEAxMMUnVieSBUZXN0IENBMSUwIwYJKoZIhvcNAQkBFhZz
+ZWN1cml0eUBydWJ5LWxhbmcub3JnggkAuZCiv2JpF5wwDAYDVR0TBAUwAwEB/zAN
+BgkqhkiG9w0BAQUFAAOBgQCPdwZOMXIS7mgJcCfUMYXvEJX5DytmYwg3iG63m0A+
+GHczhuhharc8y8em1tWSah9W0J9cMlbTN1L+DiDCeg3+LTyB2rh/TWoIAdm+eqIV
+vqbOSWSQjJrKbi6ESB2UGVaURqolm2jCgGC/yy41A+oKZVozOMbMgUbAvDaGljkQ
+fQ==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/net/imap/server.crt b/jni/ruby/test/net/imap/server.crt
new file mode 100644
index 0000000..fa4f994
--- /dev/null
+++ b/jni/ruby/test/net/imap/server.crt
@@ -0,0 +1,48 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 0 (0x0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Shimane, L=Matz-e city, O=Ruby Core Team, CN=Ruby Test CA/emailAddress=security@ruby-lang.org
+ Validity
+ Not Before: Jan 3 01:34:17 2014 GMT
+ Not After : Jan 2 01:34:17 2019 GMT
+ Subject: C=JP, ST=Shimane, O=Ruby Core Team, OU=Ruby Test, CN=localhost
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:db:75:d0:45:de:b1:df:bf:71:a0:0e:b0:a5:e6:
+ bc:f4:1c:9d:e5:25:67:64:c5:7b:cb:f1:af:c6:be:
+ 9a:aa:ea:7e:0f:cc:05:af:ef:40:69:06:b2:c9:13:
+ 9d:7e:eb:a2:06:e2:ea:7d:07:c7:c7:99:c7:fb:d5:
+ b8:eb:63:77:62:2b:18:12:c3:53:58:d0:f5:c7:40:
+ 0c:01:d1:26:82:34:16:09:e3:dc:65:f4:dc:bb:5d:
+ a5:41:60:e7:a9:74:ba:d7:4c:b6:a3:9c:c5:8c:89:
+ af:cb:e8:9f:05:fe:ea:fe:64:24:bf:e7:ed:e3:f6:
+ d0:fc:d6:eb:fc:06:82:10:fb
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 85:f5:d3:05:8b:8c:f4:43:1c:88:f2:8f:b2:f2:93:77:b7:3d:
+ 95:c6:a0:34:bc:33:6a:d8:85:5f:3e:86:08:10:c5:5c:c1:76:
+ a3:53:3c:dc:38:98:23:97:e7:da:21:ac:e8:4d:3c:96:70:29:
+ ff:ff:1e:4a:9a:17:2b:db:04:62:b9:ef:ab:ea:a7:a5:e8:7c:
+ b1:d5:ed:30:a8:6c:78:de:51:7e:e3:8a:c2:a4:64:a8:63:a2:
+ bc:fd:43:9c:f3:55:7d:54:c9:6a:d8:53:1c:4b:6b:03:aa:b6:
+ 19:e6:a4:4f:47:00:96:c5:42:59:85:4e:c3:4e:cd:41:82:53:
+ 10:f8
+-----BEGIN CERTIFICATE-----
+MIICXDCCAcUCAQAwDQYJKoZIhvcNAQEFBQAwgYwxCzAJBgNVBAYTAkpQMRAwDgYD
+VQQIEwdTaGltYW5lMRQwEgYDVQQHEwtNYXR6LWUgY2l0eTEXMBUGA1UEChMOUnVi
+eSBDb3JlIFRlYW0xFTATBgNVBAMTDFJ1YnkgVGVzdCBDQTElMCMGCSqGSIb3DQEJ
+ARYWc2VjdXJpdHlAcnVieS1sYW5nLm9yZzAeFw0xNDAxMDMwMTM0MTdaFw0xOTAx
+MDIwMTM0MTdaMGAxCzAJBgNVBAYTAkpQMRAwDgYDVQQIEwdTaGltYW5lMRcwFQYD
+VQQKEw5SdWJ5IENvcmUgVGVhbTESMBAGA1UECxMJUnVieSBUZXN0MRIwEAYDVQQD
+Ewlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANt10EXesd+/
+caAOsKXmvPQcneUlZ2TFe8vxr8a+mqrqfg/MBa/vQGkGsskTnX7rogbi6n0Hx8eZ
+x/vVuOtjd2IrGBLDU1jQ9cdADAHRJoI0Fgnj3GX03LtdpUFg56l0utdMtqOcxYyJ
+r8vonwX+6v5kJL/n7eP20PzW6/wGghD7AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEA
+hfXTBYuM9EMciPKPsvKTd7c9lcagNLwzatiFXz6GCBDFXMF2o1M83DiYI5fn2iGs
+6E08lnAp//8eSpoXK9sEYrnvq+qnpeh8sdXtMKhseN5RfuOKwqRkqGOivP1DnPNV
+fVTJathTHEtrA6q2GeakT0cAlsVCWYVOw07NQYJTEPg=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/net/imap/server.key b/jni/ruby/test/net/imap/server.key
new file mode 100644
index 0000000..7c57546
--- /dev/null
+++ b/jni/ruby/test/net/imap/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDbddBF3rHfv3GgDrCl5rz0HJ3lJWdkxXvL8a/Gvpqq6n4PzAWv
+70BpBrLJE51+66IG4up9B8fHmcf71bjrY3diKxgSw1NY0PXHQAwB0SaCNBYJ49xl
+9Ny7XaVBYOepdLrXTLajnMWMia/L6J8F/ur+ZCS/5+3j9tD81uv8BoIQ+wIDAQAB
+AoGAGtYHR+P5gFDaxiXFuCPFC1zMeg7e29XCU6gURIteQnQ2QhxCvcbV64HkLu51
+HeYWhB0Pa4aeCWxmpgb2e+JH4MEoIjeJSGyZQeqwkQLgWJDdvkgWx5am58QzA60I
+ipkZ9QHcPffSs5RiGx4yfr58KqAmwFphGCY8W7v4LqaENdECQQD9H5VTW9g4gj1c
+j3uNYvSI/D7a9P7gfI+ziczuwMm5xsBx3D/t5TAr3SJKNne3sl1E6ZERCUbzxf+C
+k58EiHx1AkEA3fRLGqDOq7EcQhbjTcA/v/t5MwlGEUsS9+XrqOWn50YuoIwRZJ3v
+qHRQzfQfFNklGtfBvwQ4md3irXjMeGVprwJBAMEAuwiDiHuV+xm/ofKtmE13IKot
+ksYy1BOOp/8IawhHXueyi+BmF/PqOkIiA+jCjNGF0oIN89beizPSQbbgJx0CQG/K
+qL1bu1ys0y/SeWBi8XkP/0aeaCUzq/UiYCTsrzoEll2UzvnftqMhGsXxLGqCyHaR
+r2s3hA6zvIVlL4+AfM8CQQClq+WDrC5VKciLYakZNWJjV1m+H2Ut/0fXdUjKHajE
+FWLcsrOhADf6bkTb71GwPxnKRkkRmud5upP0ZYYTqM4X
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/net/imap/test_imap.rb b/jni/ruby/test/net/imap/test_imap.rb
new file mode 100644
index 0000000..da33533
--- /dev/null
+++ b/jni/ruby/test/net/imap/test_imap.rb
@@ -0,0 +1,550 @@
+require "net/imap"
+require "test/unit"
+
+class IMAPTest < Test::Unit::TestCase
+ CA_FILE = File.expand_path("cacert.pem", File.dirname(__FILE__))
+ SERVER_KEY = File.expand_path("server.key", File.dirname(__FILE__))
+ SERVER_CERT = File.expand_path("server.crt", File.dirname(__FILE__))
+
+ SERVER_ADDR = "127.0.0.1"
+
+ def setup
+ @do_not_reverse_lookup = Socket.do_not_reverse_lookup
+ Socket.do_not_reverse_lookup = true
+ @threads = []
+ end
+
+ def teardown
+ if !@threads.empty?
+ assert_join_threads(@threads)
+ end
+ ensure
+ Socket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ def test_encode_utf7
+ assert_equal("foo", Net::IMAP.encode_utf7("foo"))
+ assert_equal("&-", Net::IMAP.encode_utf7("&"))
+
+ utf8 = "\357\274\241\357\274\242\357\274\243".force_encoding("UTF-8")
+ s = Net::IMAP.encode_utf7(utf8)
+ assert_equal("&,yH,Iv8j-", s)
+ s = Net::IMAP.encode_utf7("foo&#{utf8}-bar".encode("EUC-JP"))
+ assert_equal("foo&-&,yH,Iv8j--bar", s)
+
+ utf8 = "\343\201\202&".force_encoding("UTF-8")
+ s = Net::IMAP.encode_utf7(utf8)
+ assert_equal("&MEI-&-", s)
+ s = Net::IMAP.encode_utf7(utf8.encode("EUC-JP"))
+ assert_equal("&MEI-&-", s)
+ end
+
+ def test_decode_utf7
+ assert_equal("&", Net::IMAP.decode_utf7("&-"))
+ assert_equal("&-", Net::IMAP.decode_utf7("&--"))
+
+ s = Net::IMAP.decode_utf7("&,yH,Iv8j-")
+ utf8 = "\357\274\241\357\274\242\357\274\243".force_encoding("UTF-8")
+ assert_equal(utf8, s)
+ end
+
+ def test_format_date
+ time = Time.mktime(2009, 7, 24)
+ s = Net::IMAP.format_date(time)
+ assert_equal("24-Jul-2009", s)
+ end
+
+ def test_format_datetime
+ time = Time.mktime(2009, 7, 24, 1, 23, 45)
+ s = Net::IMAP.format_datetime(time)
+ assert_match(/\A24-Jul-2009 01:23 [+\-]\d{4}\z/, s)
+ end
+
+ if defined?(OpenSSL::SSL::SSLError)
+ def test_imaps_unknown_ca
+ assert_raise(OpenSSL::SSL::SSLError) do
+ imaps_test do |port|
+ begin
+ Net::IMAP.new("localhost",
+ :port => port,
+ :ssl => true)
+ rescue SystemCallError
+ skip $!
+ end
+ end
+ end
+ end
+
+ def test_imaps_with_ca_file
+ assert_nothing_raised do
+ imaps_test do |port|
+ begin
+ Net::IMAP.new("localhost",
+ :port => port,
+ :ssl => { :ca_file => CA_FILE })
+ rescue SystemCallError
+ skip $!
+ end
+ end
+ end
+ end
+
+ def test_imaps_verify_none
+ assert_nothing_raised do
+ imaps_test do |port|
+ Net::IMAP.new(SERVER_ADDR,
+ :port => port,
+ :ssl => { :verify_mode => OpenSSL::SSL::VERIFY_NONE })
+ end
+ end
+ end
+
+ def test_imaps_post_connection_check
+ assert_raise(OpenSSL::SSL::SSLError) do
+ imaps_test do |port|
+ # SERVER_ADDR is different from the hostname in the certificate,
+ # so the following code should raise a SSLError.
+ Net::IMAP.new(SERVER_ADDR,
+ :port => port,
+ :ssl => { :ca_file => CA_FILE })
+ end
+ end
+ end
+ end
+
+ if defined?(OpenSSL::SSL)
+ def test_starttls
+ imap = nil
+ starttls_test do |port|
+ imap = Net::IMAP.new("localhost", :port => port)
+ imap.starttls(:ca_file => CA_FILE)
+ imap
+ end
+ rescue SystemCallError
+ skip $!
+ ensure
+ if imap && !imap.disconnected?
+ imap.disconnect
+ end
+ end
+ end
+
+ def test_unexpected_eof
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+# sock.print("* BYE terminating connection\r\n")
+# sock.print("RUBY0001 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ assert_raise(EOFError) do
+ imap.logout
+ end
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_idle
+ server = create_tcp_server
+ port = server.addr[1]
+ requests = []
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ requests.push(sock.gets)
+ sock.print("+ idling\r\n")
+ sock.print("* 3 EXISTS\r\n")
+ sock.print("* 2 EXPUNGE\r\n")
+ requests.push(sock.gets)
+ sock.print("RUBY0001 OK IDLE terminated\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0002 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ responses = []
+ imap.idle do |res|
+ responses.push(res)
+ if res.name == "EXPUNGE"
+ imap.idle_done
+ end
+ end
+ assert_equal(3, responses.length)
+ assert_instance_of(Net::IMAP::ContinuationRequest, responses[0])
+ assert_equal("EXISTS", responses[1].name)
+ assert_equal(3, responses[1].data)
+ assert_equal("EXPUNGE", responses[2].name)
+ assert_equal(2, responses[2].data)
+ assert_equal(2, requests.length)
+ assert_equal("RUBY0001 IDLE\r\n", requests[0])
+ assert_equal("DONE\r\n", requests[1])
+ imap.logout
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_exception_during_idle
+ server = create_tcp_server
+ port = server.addr[1]
+ requests = []
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ requests.push(sock.gets)
+ sock.print("+ idling\r\n")
+ sock.print("* 3 EXISTS\r\n")
+ sock.print("* 2 EXPUNGE\r\n")
+ requests.push(sock.gets)
+ sock.print("RUBY0001 OK IDLE terminated\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0002 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ begin
+ th = Thread.current
+ m = Monitor.new
+ in_idle = false
+ exception_raised = false
+ c = m.new_cond
+ @threads << Thread.start do
+ m.synchronize do
+ until in_idle
+ c.wait(0.1)
+ end
+ end
+ th.raise(Interrupt)
+ exception_raised = true
+ end
+ imap.idle do |res|
+ m.synchronize do
+ in_idle = true
+ c.signal
+ until exception_raised
+ c.wait(0.1)
+ end
+ end
+ end
+ rescue Interrupt
+ end
+ assert_equal(2, requests.length)
+ assert_equal("RUBY0001 IDLE\r\n", requests[0])
+ assert_equal("DONE\r\n", requests[1])
+ imap.logout
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_idle_done_not_during_idle
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ assert_raise(Net::IMAP::Error) do
+ imap.idle_done
+ end
+ ensure
+ imap.disconnect if imap
+ end
+ end
+
+ def test_unexpected_bye
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK Gimap ready for requests from 75.101.246.151 33if2752585qyk.26\r\n")
+ sock.gets
+ sock.print("* BYE System Error 33if2752585qyk.26\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ assert_raise(Net::IMAP::ByeResponseError) do
+ imap.login("user", "password")
+ end
+ end
+ end
+
+ def test_exception_during_shutdown
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0001 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ imap.instance_eval do
+ def @sock.shutdown(*args)
+ super
+ ensure
+ raise "error"
+ end
+ end
+ imap.logout
+ ensure
+ assert_raise(RuntimeError) do
+ imap.disconnect
+ end
+ end
+ end
+
+ def test_connection_closed_during_idle
+ server = create_tcp_server
+ port = server.addr[1]
+ requests = []
+ sock = nil
+ threads = []
+ threads << Thread.start do
+ begin
+ sock = server.accept
+ sock.print("* OK test server\r\n")
+ requests.push(sock.gets)
+ sock.print("+ idling\r\n")
+ rescue IOError # sock is closed by another thread
+ ensure
+ server.close
+ end
+ end
+ threads << Thread.start do
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ begin
+ m = Monitor.new
+ in_idle = false
+ exception_raised = false
+ c = m.new_cond
+ threads << Thread.start do
+ m.synchronize do
+ until in_idle
+ c.wait(0.1)
+ end
+ end
+ sock.close
+ exception_raised = true
+ end
+ assert_raise(Net::IMAP::Error) do
+ imap.idle do |res|
+ m.synchronize do
+ in_idle = true
+ c.signal
+ until exception_raised
+ c.wait(0.1)
+ end
+ end
+ end
+ end
+ assert_equal(1, requests.length)
+ assert_equal("RUBY0001 IDLE\r\n", requests[0])
+ ensure
+ imap.disconnect if imap
+ end
+ end
+ assert_join_threads(threads)
+ ensure
+ if sock && !sock.closed?
+ sock.close
+ end
+ end
+
+ def test_connection_closed_without_greeting
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ begin
+ sock = server.accept
+ sock.close
+ ensure
+ server.close
+ end
+ end
+ assert_raise(Net::IMAP::Error) do
+ Net::IMAP.new(SERVER_ADDR, :port => port)
+ end
+ end
+
+ def test_default_port
+ assert_equal(143, Net::IMAP.default_port)
+ assert_equal(143, Net::IMAP.default_imap_port)
+ assert_equal(993, Net::IMAP.default_tls_port)
+ assert_equal(993, Net::IMAP.default_ssl_port)
+ assert_equal(993, Net::IMAP.default_imaps_port)
+ end
+
+ def test_send_invalid_number
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("RUBY0001 OK TEST completed\r\n")
+ sock.gets
+ sock.print("RUBY0002 OK TEST completed\r\n")
+ sock.gets
+ sock.print("RUBY0003 OK TEST completed\r\n")
+ sock.gets
+ sock.print("RUBY0004 OK TEST completed\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0005 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = Net::IMAP.new(SERVER_ADDR, :port => port)
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", -1)
+ end
+ imap.send(:send_command, "TEST", 0)
+ imap.send(:send_command, "TEST", 4294967295)
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", 4294967296)
+ end
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(-1))
+ end
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(0))
+ end
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(1))
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(4294967295))
+ assert_raise(Net::IMAP::DataFormatError) do
+ imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(4294967296))
+ end
+ imap.logout
+ ensure
+ imap.disconnect
+ end
+ end
+
+ private
+
+ def imaps_test
+ server = create_tcp_server
+ port = server.addr[1]
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ ssl_server = OpenSSL::SSL::SSLServer.new(server, ctx)
+ ths = Thread.start do
+ begin
+ sock = ssl_server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0001 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ end
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
+ end
+ end
+ begin
+ begin
+ imap = yield(port)
+ imap.logout
+ ensure
+ imap.disconnect if imap
+ end
+ ensure
+ ssl_server.close
+ ths.join
+ end
+ end
+
+ def starttls_test
+ server = create_tcp_server
+ port = server.addr[1]
+ @threads << Thread.start do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("RUBY0001 OK completed\r\n")
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = CA_FILE
+ ctx.key = File.open(SERVER_KEY) { |f|
+ OpenSSL::PKey::RSA.new(f)
+ }
+ ctx.cert = File.open(SERVER_CERT) { |f|
+ OpenSSL::X509::Certificate.new(f)
+ }
+ sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ sock.sync_close = true
+ sock.accept
+ sock.gets
+ sock.print("* BYE terminating connection\r\n")
+ sock.print("RUBY0002 OK LOGOUT completed\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = yield(port)
+ imap.logout if !imap.disconnected?
+ ensure
+ imap.disconnect if imap && !imap.disconnected?
+ end
+ end
+
+ def create_tcp_server
+ return TCPServer.new(SERVER_ADDR, 0)
+ end
+end
diff --git a/jni/ruby/test/net/imap/test_imap_response_parser.rb b/jni/ruby/test/net/imap/test_imap_response_parser.rb
new file mode 100644
index 0000000..1612d78
--- /dev/null
+++ b/jni/ruby/test/net/imap/test_imap_response_parser.rb
@@ -0,0 +1,292 @@
+require "net/imap"
+require "test/unit"
+
+class IMAPResponseParserTest < Test::Unit::TestCase
+ def setup
+ @do_not_reverse_lookup = Socket.do_not_reverse_lookup
+ Socket.do_not_reverse_lookup = true
+ if Net::IMAP.respond_to?(:max_flag_count)
+ @max_flag_count = Net::IMAP.max_flag_count
+ Net::IMAP.max_flag_count = 3
+ end
+ end
+
+ def teardown
+ Socket.do_not_reverse_lookup = @do_not_reverse_lookup
+ if Net::IMAP.respond_to?(:max_flag_count)
+ Net::IMAP.max_flag_count = @max_flag_count
+ end
+ end
+
+ def test_flag_list_safe
+ parser = Net::IMAP::ResponseParser.new
+ response = lambda {
+ $SAFE = 1
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* LIST (\\HasChildren) "." "INBOX"
+EOF
+ }.call
+ assert_equal [:Haschildren], response.data.attr
+ end
+
+ def test_flag_list_too_many_flags
+ parser = Net::IMAP::ResponseParser.new
+ assert_nothing_raised do
+ 3.times do |i|
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* LIST (\\Foo#{i}) "." "INBOX"
+EOF
+ end
+ end
+ assert_raise(Net::IMAP::FlagCountError) do
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* LIST (\\Foo3) "." "INBOX"
+EOF
+ end
+ end
+
+ def test_flag_list_many_same_flags
+ parser = Net::IMAP::ResponseParser.new
+ assert_nothing_raised do
+ 100.times do
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* LIST (\\Foo) "." "INBOX"
+EOF
+ end
+ end
+ end
+
+ def test_flag_xlist_inbox
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* XLIST (\\Inbox) "." "INBOX"
+EOF
+ assert_equal [:Inbox], response.data.attr
+ end
+
+ def test_resp_text_code
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* OK [CLOSED] Previous mailbox closed.
+EOF
+ assert_equal "CLOSED", response.data.code.name
+ end
+
+ def test_search_response
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH
+EOF
+ assert_equal [], response.data
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH 1
+EOF
+ assert_equal [1], response.data
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH 1 2 3
+EOF
+ assert_equal [1, 2, 3], response.data
+ end
+
+ def test_search_response_of_yahoo
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH 1\s
+EOF
+ assert_equal [1], response.data
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* SEARCH 1 2 3\s
+EOF
+ assert_equal [1, 2, 3], response.data
+ end
+
+ def test_msg_att_extra_space
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 1 FETCH (UID 92285)
+EOF
+ assert_equal 92285, response.data.attr["UID"]
+
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 1 FETCH (UID 92285 )
+EOF
+ assert_equal 92285, response.data.attr["UID"]
+ end
+
+ def test_msg_att_parse_error
+ parser = Net::IMAP::ResponseParser.new
+ e = assert_raise(Net::IMAP::ResponseParseError) {
+ parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 123 FETCH (UNKNOWN 92285)
+EOF
+ }
+ assert_match(/ for \{123\}/, e.message)
+ end
+
+ def test_msg_att_rfc822_text
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 123 FETCH (RFC822 {5}
+foo
+)
+EOF
+ assert_equal("foo\r\n", response.data.attr["RFC822"])
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 123 FETCH (RFC822[] {5}
+foo
+)
+EOF
+ assert_equal("foo\r\n", response.data.attr["RFC822"])
+ end
+
+ # [Bug #6397] [ruby-core:44849]
+ def test_body_type_attachment
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 980 FETCH (UID 2862 BODYSTRUCTURE ((("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 416 21 NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 1493 32 NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "Boundary_(ID_IaecgfnXwG5bn3x8lIeGIQ)") NIL NIL)("MESSAGE" "RFC822" ("NAME" "Fw_ ____ _____ ____.eml") NIL NIL "7BIT" 1980088 NIL ("ATTACHMENT" ("FILENAME" "Fw_ ____ _____ ____.eml")) NIL) "MIXED" ("BOUNDARY" "Boundary_(ID_eDdLc/j0mBIzIlR191pHjA)") NIL NIL))
+EOF
+ assert_equal("Fw_ ____ _____ ____.eml",
+ response.data.attr["BODYSTRUCTURE"].parts[1].body.param["FILENAME"])
+ end
+
+ def assert_parseable(s)
+ parser = Net::IMAP::ResponseParser.new
+ parser.parse(s.gsub(/\n/, "\r\n").taint)
+ end
+
+ # [Bug #7146]
+ def test_msg_delivery_status
+ # This was part of a larger response that caused crashes, but this was the
+ # minimal test case to demonstrate it
+ assert_parseable <<EOF
+* 4902 FETCH (BODY (("MESSAGE" "DELIVERY-STATUS" NIL NIL NIL "7BIT" 324) "REPORT"))
+EOF
+ end
+
+ # [Bug #7147]
+ def test_msg_with_message_rfc822_attachment
+ assert_parseable <<EOF
+* 5441 FETCH (BODY ((("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 69 1)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 455 12) "ALTERNATIVE")("MESSAGE" "RFC822" ("NAME" "ATT00026.eml") NIL NIL "7BIT" 4079755) "MIXED"))
+EOF
+ end
+
+ # [Bug #7153]
+ def test_msg_body_mixed
+ assert_parseable <<EOF
+* 1038 FETCH (BODY ("MIXED"))
+EOF
+ end
+
+ # [Bug #8167]
+ def test_msg_delivery_status_with_extra_data
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* 29021 FETCH (RFC822.SIZE 3162 UID 113622 RFC822.HEADER {1155}
+Return-path: <>
+Envelope-to: info@xxxxxxxx.si
+Delivery-date: Tue, 26 Mar 2013 12:42:58 +0100
+Received: from mail by xxxx.xxxxxxxxxxx.net with spam-scanned (Exim 4.76)
+ id 1UKSHI-000Cwl-AR
+ for info@xxxxxxxx.si; Tue, 26 Mar 2013 12:42:58 +0100
+X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on xxxx.xxxxxxxxxxx.net
+X-Spam-Level: **
+X-Spam-Status: No, score=2.1 required=7.0 tests=DKIM_ADSP_NXDOMAIN,RDNS_NONE
+ autolearn=no version=3.3.1
+Received: from [xx.xxx.xxx.xx] (port=56890 helo=xxxxxx.localdomain)
+ by xxxx.xxxxxxxxxxx.net with esmtp (Exim 4.76)
+ id 1UKSHI-000Cwi-9j
+ for info@xxxxxxxx.si; Tue, 26 Mar 2013 12:42:56 +0100
+Received: by xxxxxx.localdomain (Postfix)
+ id 72725BEA64A; Tue, 26 Mar 2013 12:42:55 +0100 (CET)
+Date: Tue, 26 Mar 2013 12:42:55 +0100 (CET)
+From: MAILER-DAEMON@xxxxxx.localdomain (Mail Delivery System)
+Subject: Undelivered Mail Returned to Sender
+To: info@xxxxxxxx.si
+Auto-Submitted: auto-replied
+MIME-Version: 1.0
+Content-Type: multipart/report; report-type=delivery-status;
+ boundary="27797BEA649.1364298175/xxxxxx.localdomain"
+Message-Id: <20130326114255.72725BEA64A@xxxxxx.localdomain>
+
+ BODYSTRUCTURE (("text" "plain" ("charset" "us-ascii") NIL "Notification" "7bit" 510 14 NIL NIL NIL NIL)("message" "delivery-status" NIL NIL "Delivery report" "7bit" 410 NIL NIL NIL NIL)("text" "rfc822-headers" ("charset" "us-ascii") NIL "Undelivered Message Headers" "7bit" 612 15 NIL NIL NIL NIL) "report" ("report-type" "delivery-status" "boundary" "27797BEA649.1364298175/xxxxxx.localdomain") NIL NIL NIL))
+EOF
+ delivery_status = response.data.attr["BODYSTRUCTURE"].parts[1]
+ assert_equal("MESSAGE", delivery_status.media_type)
+ assert_equal("DELIVERY-STATUS", delivery_status.subtype)
+ assert_equal(nil, delivery_status.param)
+ assert_equal(nil, delivery_status.content_id)
+ assert_equal("Delivery report", delivery_status.description)
+ assert_equal("7BIT", delivery_status.encoding)
+ assert_equal(410, delivery_status.size)
+ end
+
+ # [Bug #8281]
+ def test_acl
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse(<<EOF.gsub(/\n/, "\r\n").taint)
+* ACL "INBOX/share" "imshare2copy1366146467@xxxxxxxxxxxxxxxxxx.com" lrswickxteda
+EOF
+ assert_equal("ACL", response.name)
+ assert_equal(1, response.data.length)
+ assert_equal("INBOX/share", response.data[0].mailbox)
+ assert_equal("imshare2copy1366146467@xxxxxxxxxxxxxxxxxx.com",
+ response.data[0].user)
+ assert_equal("lrswickxteda", response.data[0].rights)
+ end
+
+ # [Bug #8415]
+ def test_capability
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* CAPABILITY st11p00mm-iscream009 1Q49 XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN\r\n")
+ assert_equal("CAPABILITY", response.name)
+ assert_equal("AUTH=PLAIN", response.data.last)
+ response = parser.parse("* CAPABILITY st11p00mm-iscream009 1Q49 XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN \r\n")
+ assert_equal("CAPABILITY", response.name)
+ assert_equal("AUTH=PLAIN", response.data.last)
+ end
+
+ def test_mixed_boundry
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* 2688 FETCH (UID 179161 BODYSTRUCTURE (" \
+ "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"iso-8859-1\") NIL NIL \"QUOTED-PRINTABLE\" 200 4 NIL NIL NIL)" \
+ "(\"MESSAGE\" \"DELIVERY-STATUS\" NIL NIL NIL \"7BIT\" 318 NIL NIL NIL)" \
+ "(\"MESSAGE\" \"RFC822\" NIL NIL NIL \"7BIT\" 2177" \
+ " (\"Tue, 11 May 2010 18:28:16 -0400\" \"Re: Welcome letter\" (" \
+ "(\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
+ "((\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
+ "((\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
+ "((\"Doretha\" NIL \"doretha.info\" \"xxxxxxxx.si\")) " \
+ "NIL NIL " \
+ "\"<AC1D15E06EA82F47BDE18E851CC32F330717704E@localdomain>\" " \
+ "\"<AANLkTikKMev1I73L2E7XLjRs67IHrEkb23f7ZPmD4S_9@localdomain>\")" \
+ " (\"MIXED\" (\"BOUNDARY\" \"000e0cd29212e3e06a0486590ae2\") NIL NIL)" \
+ " 37 NIL NIL NIL)" \
+ " \"REPORT\" (\"BOUNDARY\" \"16DuG.4XbaNOvCi.9ggvq.8Ipnyp3\" \"REPORT-TYPE\" \"delivery-status\") NIL NIL))\r\n")
+ empty_part = response.data.attr['BODYSTRUCTURE'].parts[2]
+ assert_equal(empty_part.lines, 37)
+ assert_equal(empty_part.body.media_type, 'MULTIPART')
+ assert_equal(empty_part.body.subtype, 'MIXED')
+ assert_equal(empty_part.body.param['BOUNDARY'], '000e0cd29212e3e06a0486590ae2')
+ end
+
+ # [Bug #10112]
+ def test_search_modseq
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* SEARCH 87216 87221 (MODSEQ 7667567)\r\n")
+ assert_equal("SEARCH", response.name)
+ assert_equal([87216, 87221], response.data)
+ end
+
+ # [Bug #11128]
+ def test_body_ext_mpart_without_lang
+ parser = Net::IMAP::ResponseParser.new
+ response = parser.parse("* 4 FETCH (BODY (((\"text\" \"plain\" (\"charset\" \"utf-8\") NIL NIL \"7bit\" 257 9 NIL NIL NIL NIL)(\"text\" \"html\" (\"charset\" \"utf-8\") NIL NIL \"quoted-printable\" 655 9 NIL NIL NIL NIL) \"alternative\" (\"boundary\" \"001a1137a5047848dd05157ddaa1\") NIL)(\"application\" \"pdf\" (\"name\" \"test.xml\" \"x-apple-part-url\" \"9D00D9A2-98AB-4EFB-85BA-FB255F8BF3D7\") NIL NIL \"base64\" 4383638 NIL (\"attachment\" (\"filename\" \"test.xml\")) NIL NIL) \"mixed\" (\"boundary\" \"001a1137a5047848e405157ddaa3\") NIL))\r\n")
+ assert_equal("FETCH", response.name)
+ body = response.data.attr["BODY"]
+ assert_equal(nil, body.parts[0].disposition)
+ assert_equal(nil, body.parts[0].language)
+ assert_equal("ATTACHMENT", body.parts[1].disposition.dsp_type)
+ assert_equal("test.xml", body.parts[1].disposition.param["FILENAME"])
+ assert_equal(nil, body.parts[1].language)
+ end
+end
diff --git a/jni/ruby/test/net/pop/test_pop.rb b/jni/ruby/test/net/pop/test_pop.rb
new file mode 100644
index 0000000..7aa65a4
--- /dev/null
+++ b/jni/ruby/test/net/pop/test_pop.rb
@@ -0,0 +1,136 @@
+require 'net/pop'
+require 'test/unit'
+require 'digest/md5'
+
+class TestPOP < Test::Unit::TestCase
+ def setup
+ @users = {'user' => 'pass' }
+ @ok_user = 'user'
+ @stamp_base = "#{$$}.#{Time.now.to_i}@localhost"
+ end
+
+ def test_pop_auth_ok
+ pop_test(false) do |pop|
+ assert_instance_of Net::POP3, pop
+ assert_nothing_raised do
+ pop.start(@ok_user, @users[@ok_user])
+ end
+ end
+ end
+
+ def test_pop_auth_ng
+ pop_test(false) do |pop|
+ assert_instance_of Net::POP3, pop
+ assert_raise Net::POPAuthenticationError do
+ pop.start(@ok_user, 'bad password')
+ end
+ end
+ end
+
+ def test_apop_ok
+ pop_test(@stamp_base) do |pop|
+ assert_instance_of Net::APOP, pop
+ assert_nothing_raised do
+ pop.start(@ok_user, @users[@ok_user])
+ end
+ end
+ end
+
+ def test_apop_ng
+ pop_test(@stamp_base) do |pop|
+ assert_instance_of Net::APOP, pop
+ assert_raise Net::POPAuthenticationError do
+ pop.start(@ok_user, 'bad password')
+ end
+ end
+ end
+
+ def test_apop_invalid
+ pop_test("\x80"+@stamp_base) do |pop|
+ assert_instance_of Net::APOP, pop
+ assert_raise Net::POPAuthenticationError do
+ pop.start(@ok_user, @users[@ok_user])
+ end
+ end
+ end
+
+ def test_apop_invalid_at
+ pop_test(@stamp_base.sub('@', '.')) do |pop|
+ assert_instance_of Net::APOP, pop
+ assert_raise Net::POPAuthenticationError do
+ pop.start(@ok_user, @users[@ok_user])
+ end
+ end
+ end
+
+ def pop_test(apop=false)
+ host = 'localhost'
+ server = TCPServer.new(host, 0)
+ port = server.addr[1]
+ server_thread = Thread.start do
+ sock = server.accept
+ begin
+ pop_server_loop(sock, apop)
+ ensure
+ sock.close
+ end
+ end
+ client_thread = Thread.start do
+ begin
+ begin
+ pop = Net::POP3::APOP(apop).new(host, port)
+ #pop.set_debug_output $stderr
+ yield pop
+ ensure
+ begin
+ pop.finish
+ rescue IOError
+ raise unless $!.message == "POP session not yet started"
+ end
+ end
+ ensure
+ server.close
+ end
+ end
+ assert_join_threads([client_thread, server_thread])
+ end
+
+ def pop_server_loop(sock, apop)
+ if apop
+ sock.print "+OK ready <#{apop}>\r\n"
+ else
+ sock.print "+OK ready\r\n"
+ end
+ user = nil
+ while line = sock.gets
+ case line
+ when /^USER (.+)\r\n/
+ user = $1
+ if @users.key?(user)
+ sock.print "+OK\r\n"
+ else
+ sock.print "-ERR unknown user\r\n"
+ end
+ when /^PASS (.+)\r\n/
+ if @users[user] == $1
+ sock.print "+OK\r\n"
+ else
+ sock.print "-ERR invalid password\r\n"
+ end
+ when /^APOP (.+) (.+)\r\n/
+ user = $1
+ if apop && Digest::MD5.hexdigest("<#{apop}>#{@users[user]}") == $2
+ sock.print "+OK\r\n"
+ else
+ sock.print "-ERR authentication failed\r\n"
+ end
+ when /^QUIT/
+ sock.print "+OK bye\r\n"
+ return
+ else
+ sock.print "-ERR command not recognized\r\n"
+ return
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/net/protocol/test_protocol.rb b/jni/ruby/test/net/protocol/test_protocol.rb
new file mode 100644
index 0000000..4453422
--- /dev/null
+++ b/jni/ruby/test/net/protocol/test_protocol.rb
@@ -0,0 +1,28 @@
+require "test/unit"
+require "net/protocol"
+require "stringio"
+
+class TestProtocol < Test::Unit::TestCase
+ def test_should_properly_dot_stuff_period_with_no_endline
+ bug9627 = '[ruby-core:61441] [Bug #9627]'
+ sio = StringIO.new("")
+ imio = Net::InternetMessageIO.new(sio)
+ email = "To: bob@aol.com\nlook, a period with no endline\n."
+ imio.write_message(email)
+ assert_equal("To: bob@aol.com\r\nlook, a period with no endline\r\n..\r\n.\r\n", sio.string, bug9627)
+ end
+
+ def test_each_crlf_line
+ assert_output('', '') do
+ sio = StringIO.new("")
+ imio = Net::InternetMessageIO.new(sio)
+ assert_equal(23, imio.write_message("\u3042\r\u3044\n\u3046\r\n\u3048"))
+ assert_equal("\u3042\r\n\u3044\r\n\u3046\r\n\u3048\r\n.\r\n", sio.string)
+
+ sio = StringIO.new("")
+ imio = Net::InternetMessageIO.new(sio)
+ assert_equal(8, imio.write_message("\u3042\r"))
+ assert_equal("\u3042\r\n.\r\n", sio.string)
+ end
+ end
+end
diff --git a/jni/ruby/test/net/smtp/test_response.rb b/jni/ruby/test/net/smtp/test_response.rb
new file mode 100644
index 0000000..e7011e0
--- /dev/null
+++ b/jni/ruby/test/net/smtp/test_response.rb
@@ -0,0 +1,99 @@
+require 'net/smtp'
+require 'minitest/autorun'
+
+module Net
+ class SMTP
+ class TestResponse < MiniTest::Unit::TestCase
+ def test_capabilities
+ res = Response.parse("250-ubuntu-desktop\n250-PIPELINING\n250-SIZE 10240000\n250-VRFY\n250-ETRN\n250-STARTTLS\n250-ENHANCEDSTATUSCODES\n250 DSN\n")
+
+ capabilities = res.capabilities
+ %w{ PIPELINING SIZE VRFY STARTTLS ENHANCEDSTATUSCODES DSN}.each do |str|
+ assert capabilities.key?(str), str
+ end
+ end
+
+ def test_capabilities_default
+ res = Response.parse("250-ubuntu-desktop\n250-PIPELINING\n250 DSN\n")
+ assert_equal [], res.capabilities['PIPELINING']
+ end
+
+ def test_capabilities_value
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1234\n250 DSN\n")
+ assert_equal ['1234'], res.capabilities['SIZE']
+ end
+
+ def test_capabilities_multi
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert_equal %w{1 2 3}, res.capabilities['SIZE']
+ end
+
+ def test_bad_string
+ res = Response.parse("badstring")
+ assert_equal({}, res.capabilities)
+ end
+
+ def test_success?
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert res.success?
+ assert !res.continue?
+ end
+
+ # RFC 2821, Section 4.2.1
+ def test_continue?
+ res = Response.parse("3yz-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert !res.success?
+ assert res.continue?
+ end
+
+ def test_status_type_char
+ res = Response.parse("3yz-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert_equal '3', res.status_type_char
+
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert_equal '2', res.status_type_char
+ end
+
+ def test_message
+ res = Response.parse("250-ubuntu-desktop\n250-SIZE 1 2 3\n250 DSN\n")
+ assert_equal "250-ubuntu-desktop\n", res.message
+ end
+
+ def test_server_busy_exception
+ res = Response.parse("400 omg busy")
+ assert_equal Net::SMTPServerBusy, res.exception_class
+ res = Response.parse("410 omg busy")
+ assert_equal Net::SMTPServerBusy, res.exception_class
+ end
+
+ def test_syntax_error_exception
+ res = Response.parse("500 omg syntax error")
+ assert_equal Net::SMTPSyntaxError, res.exception_class
+
+ res = Response.parse("501 omg syntax error")
+ assert_equal Net::SMTPSyntaxError, res.exception_class
+ end
+
+ def test_authentication_exception
+ res = Response.parse("530 omg auth error")
+ assert_equal Net::SMTPAuthenticationError, res.exception_class
+
+ res = Response.parse("531 omg auth error")
+ assert_equal Net::SMTPAuthenticationError, res.exception_class
+ end
+
+ def test_fatal_error
+ res = Response.parse("510 omg fatal error")
+ assert_equal Net::SMTPFatalError, res.exception_class
+
+ res = Response.parse("511 omg fatal error")
+ assert_equal Net::SMTPFatalError, res.exception_class
+ end
+
+ def test_default_exception
+ res = Response.parse("250 omg fatal error")
+ assert_equal Net::SMTPUnknownError, res.exception_class
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/net/smtp/test_smtp.rb b/jni/ruby/test/net/smtp/test_smtp.rb
new file mode 100644
index 0000000..748b20d
--- /dev/null
+++ b/jni/ruby/test/net/smtp/test_smtp.rb
@@ -0,0 +1,54 @@
+require 'net/smtp'
+require 'stringio'
+require 'minitest/autorun'
+
+module Net
+ class TestSMTP < MiniTest::Unit::TestCase
+ class FakeSocket
+ def initialize out = "250 OK\n"
+ @write_io = StringIO.new
+ @read_io = StringIO.new out
+ end
+
+ def writeline line
+ @write_io.write "#{line}\r\n"
+ end
+
+ def readline
+ line = @read_io.gets
+ raise 'ran out of input' unless line
+ line.chop
+ end
+ end
+
+ def test_critical
+ smtp = Net::SMTP.new 'localhost', 25
+
+ assert_raises RuntimeError do
+ smtp.send :critical do
+ raise 'fail on purpose'
+ end
+ end
+
+ assert_kind_of Net::SMTP::Response, smtp.send(:critical),
+ '[Bug #9125]'
+ end
+
+ def test_esmtp
+ smtp = Net::SMTP.new 'localhost', 25
+ assert smtp.esmtp
+ assert smtp.esmtp?
+
+ smtp.esmtp = 'omg'
+ assert_equal 'omg', smtp.esmtp
+ assert_equal 'omg', smtp.esmtp?
+ end
+
+ def test_rset
+ smtp = Net::SMTP.new 'localhost', 25
+ smtp.instance_variable_set :@socket, FakeSocket.new
+
+ assert smtp.rset
+ end
+ end
+end
diff --git a/jni/ruby/test/net/smtp/test_ssl_socket.rb b/jni/ruby/test/net/smtp/test_ssl_socket.rb
new file mode 100644
index 0000000..dc8b03e
--- /dev/null
+++ b/jni/ruby/test/net/smtp/test_ssl_socket.rb
@@ -0,0 +1,91 @@
+require 'net/smtp'
+require 'minitest/autorun'
+
+module Net
+ class TestSSLSocket < MiniTest::Unit::TestCase
+ class MySMTP < SMTP
+ attr_accessor :fake_tcp, :fake_ssl
+
+ def tcp_socket address, port
+ fake_tcp
+ end
+
+ def ssl_socket socket, context
+ fake_ssl
+ end
+ end
+
+ require 'stringio'
+ class SSLSocket < StringIO
+ attr_accessor :sync_close, :connected, :closed
+
+ def initialize(*args)
+ @connected = false
+ @closed = true
+ super
+ end
+
+ def connect
+ self.connected = true
+ self.closed = false
+ end
+
+ def close
+ self.closed = true
+ end
+
+ def post_connection_check omg
+ end
+ end
+
+ def test_ssl_socket_close_on_post_connection_check_fail
+ tcp_socket = StringIO.new success_response
+
+ ssl_socket = SSLSocket.new.extend Module.new {
+ def post_connection_check omg
+ raise OpenSSL::SSL::SSLError, 'hostname was not match with the server certificate'
+ end
+ }
+
+ connection = MySMTP.new('localhost', 25)
+ connection.enable_starttls_auto
+ connection.fake_tcp = tcp_socket
+ connection.fake_ssl = ssl_socket
+
+ assert_raises(OpenSSL::SSL::SSLError) do
+ connection.start
+ end
+ assert_equal true, ssl_socket.closed
+ end
+
+ def test_ssl_socket_open_on_post_connection_check_success
+ tcp_socket = StringIO.new success_response
+
+ ssl_socket = SSLSocket.new success_response
+
+ connection = MySMTP.new('localhost', 25)
+ connection.enable_starttls_auto
+ connection.fake_tcp = tcp_socket
+ connection.fake_ssl = ssl_socket
+
+ connection.start
+ assert_equal false, ssl_socket.closed
+ end
+
+ def success_response
+ [
+ '220 smtp.example.com ESMTP Postfix',
+ "250-ubuntu-desktop",
+ "250-PIPELINING",
+ "250-SIZE 10240000",
+ "250-VRFY",
+ "250-ETRN",
+ "250-STARTTLS",
+ "250-ENHANCEDSTATUSCODES",
+ "250-8BITMIME",
+ "250 DSN",
+ "220 2.0.0 Ready to start TLS",
+ ].join("\r\n") + "\r\n"
+ end
+ end
+end if defined?(OpenSSL)
diff --git a/jni/ruby/test/nkf/test_kconv.rb b/jni/ruby/test/nkf/test_kconv.rb
new file mode 100644
index 0000000..09c0ce8
--- /dev/null
+++ b/jni/ruby/test/nkf/test_kconv.rb
@@ -0,0 +1,81 @@
+require 'test/unit'
+require 'kconv'
+
+class TestKconv < Test::Unit::TestCase
+ def setup
+ @euc_str = "\
+\xa5\xaa\xa5\xd6\xa5\xb8\xa5\xa7\xa5\xaf\xa5\xc8\xbb\xd8\xb8\xfe\
+\xa5\xd7\xa5\xed\xa5\xb0\xa5\xe9\xa5\xdf\xa5\xf3\xa5\xb0\xb8\xc0\xb8\xec
+\x52\x75\x62\x79".force_encoding('EUC-JP')
+ @utf8_str = "\
+\xe3\x82\xaa\xe3\x83\x96\xe3\x82\xb8\xe3\x82\xa7\
+\xe3\x82\xaf\xe3\x83\x88\xe6\x8c\x87\xe5\x90\x91\
+\xe3\x83\x97\xe3\x83\xad\xe3\x82\xb0\xe3\x83\xa9\xe3\x83\x9f\
+\xe3\x83\xb3\xe3\x82\xb0\xe8\xa8\x80\xe8\xaa\x9e
+\x52\x75\x62\x79".force_encoding('UTF-8')
+ @sjis_str = "\
+\x83\x49\x83\x75\x83\x57\x83\x46\x83\x4e\x83\x67\x8e\x77\x8c\xfc\
+\x83\x76\x83\x8d\x83\x4f\x83\x89\x83\x7e\x83\x93\x83\x4f\x8c\xbe\x8c\xea
+\x52\x75\x62\x79".force_encoding('Shift_JIS')
+ @jis_str = "\
+\x1b\x24\x42\x25\x2a\x25\x56\x25\x38\x25\x27\x25\x2f\x25\x48\x3b\x58\x38\x7e\
+\x25\x57\x25\x6d\x25\x30\x25\x69\x25\x5f\x25\x73\x25\x30\x38\x40\x38\x6c\x1b\x28\x42
+\x52\x75\x62\x79".force_encoding('ISO-2022-JP')
+ end
+
+
+ def test_eucjp
+ assert(@euc_str.iseuc)
+ assert_equal(::Kconv::EUC, Kconv.guess(@euc_str))
+ assert_equal(@euc_str, @euc_str.toeuc)
+ assert_equal(@euc_str, @sjis_str.toeuc)
+ assert_equal(@euc_str, @utf8_str.toeuc)
+ assert_equal(@euc_str, @jis_str.toeuc)
+ assert_equal(@euc_str, @euc_str.kconv(::NKF::EUC))
+ assert_equal(@euc_str, @sjis_str.kconv(::NKF::EUC))
+ assert_equal(@euc_str, @utf8_str.kconv(::NKF::EUC))
+ assert_equal(@euc_str, @jis_str.kconv(::NKF::EUC))
+ end
+ def test_shiftjis
+ assert(@sjis_str.issjis)
+ assert_equal(::Kconv::SJIS, Kconv.guess(@sjis_str))
+ assert_equal(@sjis_str, @euc_str.tosjis)
+ assert_equal(@sjis_str, @sjis_str.tosjis)
+ assert_equal(@sjis_str, @utf8_str.tosjis)
+ assert_equal(@sjis_str, @jis_str.tosjis)
+ assert_equal(@sjis_str, @euc_str.kconv(::NKF::SJIS))
+ assert_equal(@sjis_str, @sjis_str.kconv(::NKF::SJIS))
+ assert_equal(@sjis_str, @utf8_str.kconv(::NKF::SJIS))
+ assert_equal(@sjis_str, @jis_str.kconv(::NKF::SJIS))
+ end
+ def test_utf8
+ assert(@utf8_str.isutf8)
+ assert_equal(::Kconv::UTF8, Kconv.guess(@utf8_str))
+ assert_equal(@utf8_str, @euc_str.toutf8)
+ assert_equal(@utf8_str, @sjis_str.toutf8)
+ assert_equal(@utf8_str, @utf8_str.toutf8)
+ assert_equal(@utf8_str, @jis_str.toutf8)
+ assert_equal(@utf8_str, @euc_str.kconv(::NKF::UTF8))
+ assert_equal(@utf8_str, @sjis_str.kconv(::NKF::UTF8))
+ assert_equal(@utf8_str, @utf8_str.kconv(::NKF::UTF8))
+ assert_equal(@utf8_str, @jis_str.kconv(::NKF::UTF8))
+ end
+ def test_jis
+ assert_equal(::Kconv::JIS, Kconv.guess(@jis_str))
+ assert_equal(@jis_str, @euc_str.tojis)
+ assert_equal(@jis_str, @sjis_str.tojis)
+ assert_equal(@jis_str, @utf8_str.tojis)
+ assert_equal(@jis_str, @jis_str.tojis)
+ assert_equal(@jis_str, @euc_str.kconv(::NKF::JIS))
+ assert_equal(@jis_str, @sjis_str.kconv(::NKF::JIS))
+ assert_equal(@jis_str, @utf8_str.kconv(::NKF::JIS))
+ assert_equal(@jis_str, @jis_str.kconv(::NKF::JIS))
+ end
+ def test_kconv
+ str = "\xc2\xa1"
+ %w/UTF-8 EUC-JP/.each do |enc|
+ s = str.dup.force_encoding(enc)
+ assert_equal(s, s.kconv(enc))
+ end
+ end
+end
diff --git a/jni/ruby/test/nkf/test_nkf.rb b/jni/ruby/test/nkf/test_nkf.rb
new file mode 100644
index 0000000..7329e75
--- /dev/null
+++ b/jni/ruby/test/nkf/test_nkf.rb
@@ -0,0 +1,22 @@
+require 'test/unit'
+require 'nkf'
+
+class TestNKF < Test::Unit::TestCase
+ EUC_STR = "\xa5\xaa\xa5\xd6\xa5\xb8\xa5\xa7\xa5\xaf\xa5\xc8\xbb\xd8\xb8\xfe\
+\xa5\xb9\xa5\xaf\xa5\xea\xa5\xd7\xa5\xc8\xb8\xc0\xb8\xec\
+Ruby"
+
+ def test_guess
+ str_euc = EUC_STR
+ str_jis = NKF.nkf('-j', str_euc)
+ assert_equal(::NKF::JIS, NKF.guess(str_jis))
+ assert_equal(::NKF::EUC, NKF.guess(str_euc))
+ end
+
+ def test_ruby_dev_36909
+ assert_nothing_raised do
+ 100.times { NKF.nkf("--oc=eucJP-nkf", "foo") }
+ end
+ end
+
+end
diff --git a/jni/ruby/test/objspace/test_objspace.rb b/jni/ruby/test/objspace/test_objspace.rb
new file mode 100644
index 0000000..868a46e
--- /dev/null
+++ b/jni/ruby/test/objspace/test_objspace.rb
@@ -0,0 +1,288 @@
+require "test/unit"
+require "objspace"
+
+class TestObjSpace < Test::Unit::TestCase
+ def test_memsize_of
+ assert_equal(0, ObjectSpace.memsize_of(true))
+ assert_equal(0, ObjectSpace.memsize_of(nil))
+ assert_equal(0, ObjectSpace.memsize_of(1))
+ assert_kind_of(Integer, ObjectSpace.memsize_of(Object.new))
+ assert_kind_of(Integer, ObjectSpace.memsize_of(Class))
+ assert_kind_of(Integer, ObjectSpace.memsize_of(""))
+ assert_kind_of(Integer, ObjectSpace.memsize_of([]))
+ assert_kind_of(Integer, ObjectSpace.memsize_of({}))
+ assert_kind_of(Integer, ObjectSpace.memsize_of(//))
+ f = File.new(__FILE__)
+ assert_kind_of(Integer, ObjectSpace.memsize_of(f))
+ f.close
+ assert_kind_of(Integer, ObjectSpace.memsize_of(/a/.match("a")))
+ assert_kind_of(Integer, ObjectSpace.memsize_of(Struct.new(:a)))
+
+ assert_operator(ObjectSpace.memsize_of(Regexp.new("(a)"*1000).match("a"*1000)),
+ :>,
+ ObjectSpace.memsize_of(//.match("")))
+ end
+
+ def test_memsize_of_root_shared_string
+ a = "hello" * 5
+ b = a.dup
+ c = nil
+ ObjectSpace.each_object(String) {|x| break c = x if x == a and x.frozen?}
+ rv_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
+ assert_equal([rv_size, rv_size, 26 + rv_size], [a, b, c].map {|x| ObjectSpace.memsize_of(x)})
+ end
+
+ def test_argf_memsize
+ size = ObjectSpace.memsize_of(ARGF)
+ assert_kind_of(Integer, size)
+ assert_operator(size, :>, 0)
+ argf = ARGF.dup
+ argf.inplace_mode = nil
+ size = ObjectSpace.memsize_of(argf)
+ argf.inplace_mode = "inplace_mode_suffix"
+ assert_equal(size + 20, ObjectSpace.memsize_of(argf))
+ end
+
+ def test_memsize_of_all
+ assert_kind_of(Integer, a = ObjectSpace.memsize_of_all)
+ assert_kind_of(Integer, b = ObjectSpace.memsize_of_all(String))
+ assert(a > b)
+ assert(a > 0)
+ assert(b > 0)
+ assert_raise(TypeError) {ObjectSpace.memsize_of_all('error')}
+ end
+
+ def test_count_objects_size
+ res = ObjectSpace.count_objects_size
+ assert_equal(false, res.empty?)
+ assert_equal(true, res[:TOTAL] > 0)
+ arg = {}
+ ObjectSpace.count_objects_size(arg)
+ assert_equal(false, arg.empty?)
+ end
+
+ def test_count_nodes
+ res = ObjectSpace.count_nodes
+ assert_equal(false, res.empty?)
+ arg = {}
+ ObjectSpace.count_nodes(arg)
+ assert_not_empty(arg)
+ bug8014 = '[ruby-core:53130] [Bug #8014]'
+ assert_empty(arg.select {|k, v| !(Symbol === k && Integer === v)}, bug8014)
+ end
+
+ def test_count_tdata_objects
+ res = ObjectSpace.count_tdata_objects
+ assert_equal(false, res.empty?)
+ arg = {}
+ ObjectSpace.count_tdata_objects(arg)
+ assert_equal(false, arg.empty?)
+ end
+
+ def test_reachable_objects_from
+ assert_separately %w[--disable-gem -robjspace], __FILE__, __LINE__, <<-'eom'
+ assert_equal(nil, ObjectSpace.reachable_objects_from(nil))
+ assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c']))
+
+ assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a']))
+ assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v]))
+ assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v]))
+
+ long_ary = Array.new(1_000){''}
+ max = 0
+
+ ObjectSpace.each_object{|o|
+ refs = ObjectSpace.reachable_objects_from(o)
+ max = [refs.size, max].max
+
+ unless refs.nil?
+ refs.each_with_index {|ro, i|
+ assert_not_nil(ro, "#{i}: this referenced object is internal object")
+ }
+ end
+ }
+ assert_operator(max, :>=, long_ary.size+1, "1000 elems + Array class")
+ eom
+ end
+
+ def test_reachable_objects_from_root
+ root_objects = ObjectSpace.reachable_objects_from_root
+
+ assert_operator(root_objects.size, :>, 0)
+
+ root_objects.each{|category, objects|
+ assert_kind_of(String, category)
+ assert_kind_of(Array, objects)
+ assert_operator(objects.size, :>, 0)
+ }
+ end
+
+ def test_reachable_objects_size
+ assert_separately %w[--disable-gem -robjspace], __FILE__, __LINE__, <<-'eom'
+ ObjectSpace.each_object{|o|
+ ObjectSpace.reachable_objects_from(o).each{|reached_obj|
+ size = ObjectSpace.memsize_of(reached_obj)
+ assert_kind_of(Integer, size)
+ assert_operator(size, :>=, 0)
+ }
+ }
+ eom
+ end
+
+ def test_trace_object_allocations
+ Class.name
+ o0 = Object.new
+ ObjectSpace.trace_object_allocations{
+ o1 = Object.new; line1 = __LINE__; c1 = GC.count
+ o2 = "xyzzy" ; line2 = __LINE__; c2 = GC.count
+ o3 = [1, 2] ; line3 = __LINE__; c3 = GC.count
+
+ assert_equal(nil, ObjectSpace.allocation_sourcefile(o0))
+ assert_equal(nil, ObjectSpace.allocation_sourceline(o0))
+ assert_equal(nil, ObjectSpace.allocation_generation(o0))
+
+ assert_equal(line1, ObjectSpace.allocation_sourceline(o1))
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o1))
+ assert_equal(c1, ObjectSpace.allocation_generation(o1))
+ assert_equal(Class.name, ObjectSpace.allocation_class_path(o1))
+ assert_equal(:new, ObjectSpace.allocation_method_id(o1))
+
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o2))
+ assert_equal(line2, ObjectSpace.allocation_sourceline(o2))
+ assert_equal(c2, ObjectSpace.allocation_generation(o2))
+ assert_equal(self.class.name, ObjectSpace.allocation_class_path(o2))
+ assert_equal(__method__, ObjectSpace.allocation_method_id(o2))
+
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o3))
+ assert_equal(line3, ObjectSpace.allocation_sourceline(o3))
+ assert_equal(c3, ObjectSpace.allocation_generation(o3))
+ assert_equal(self.class.name, ObjectSpace.allocation_class_path(o3))
+ assert_equal(__method__, ObjectSpace.allocation_method_id(o3))
+ }
+ end
+
+ def test_trace_object_allocations_start_stop_clear
+ begin
+ ObjectSpace.trace_object_allocations_start
+ begin
+ ObjectSpace.trace_object_allocations_start
+ begin
+ ObjectSpace.trace_object_allocations_start
+ obj0 = Object.new
+ ensure
+ ObjectSpace.trace_object_allocations_stop
+ obj1 = Object.new
+ end
+ ensure
+ ObjectSpace.trace_object_allocations_stop
+ obj2 = Object.new
+ end
+ ensure
+ ObjectSpace.trace_object_allocations_stop
+ obj3 = Object.new
+ end
+
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj0))
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj1))
+ assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj2))
+ assert_equal(nil , ObjectSpace.allocation_sourcefile(obj3)) # after tracing
+
+ ObjectSpace.trace_object_allocations_clear
+ assert_equal(nil, ObjectSpace.allocation_sourcefile(obj0))
+ assert_equal(nil, ObjectSpace.allocation_sourcefile(obj1))
+ assert_equal(nil, ObjectSpace.allocation_sourcefile(obj2))
+ assert_equal(nil, ObjectSpace.allocation_sourcefile(obj3))
+ end
+
+ def test_dump_flags
+ info = ObjectSpace.dump("foo".freeze)
+ assert_match /"wb_protected":true, "old":true/, info
+ assert_match /"fstring":true/, info
+ end
+
+ def test_dump_to_default
+ line = nil
+ info = nil
+ ObjectSpace.trace_object_allocations do
+ line = __LINE__ + 1
+ str = "hello world"
+ info = ObjectSpace.dump(str)
+ end
+ assert_dump_object(info, line)
+ end
+
+ def test_dump_to_io
+ line = nil
+ info = IO.pipe do |r, w|
+ th = Thread.start {r.read}
+ ObjectSpace.trace_object_allocations do
+ line = __LINE__ + 1
+ str = "hello world"
+ ObjectSpace.dump(str, output: w)
+ end
+ w.close
+ th.value
+ end
+ assert_dump_object(info, line)
+ end
+
+ def assert_dump_object(info, line)
+ loc = caller_locations(1, 1)[0]
+ assert_match /"type":"STRING"/, info
+ assert_match /"embedded":true, "bytesize":11, "value":"hello world", "encoding":"UTF-8"/, info
+ assert_match /"file":"#{Regexp.escape __FILE__}", "line":#{line}/, info
+ assert_match /"method":"#{loc.base_label}"/, info
+ end
+
+ def test_dump_special_consts
+ # [ruby-core:69692] [Bug #11291]
+ assert_equal('{}', ObjectSpace.dump(nil))
+ assert_equal('{}', ObjectSpace.dump(true))
+ assert_equal('{}', ObjectSpace.dump(false))
+ assert_equal('{}', ObjectSpace.dump(0))
+ assert_equal('{}', ObjectSpace.dump(:foo))
+ end
+
+ def test_dump_all
+ entry = /"bytesize":11, "value":"TEST STRING", "encoding":"UTF-8", "file":"-", "line":4, "method":"dump_my_heap_please", "generation":/
+
+ assert_in_out_err(%w[-robjspace], <<-'end;') do |output, error|
+ def dump_my_heap_please
+ ObjectSpace.trace_object_allocations_start
+ GC.start
+ str = "TEST STRING".force_encoding("UTF-8")
+ ObjectSpace.dump_all(output: :stdout)
+ end
+
+ dump_my_heap_please
+ end;
+ assert_match(entry, output.grep(/TEST STRING/).join("\n"))
+ end
+
+ assert_in_out_err(%w[-robjspace], <<-'end;') do |(output), (error)|
+ def dump_my_heap_please
+ ObjectSpace.trace_object_allocations_start
+ GC.start
+ str = "TEST STRING".force_encoding("UTF-8")
+ ObjectSpace.dump_all().path
+ end
+
+ puts dump_my_heap_please
+ end;
+ skip if /is not supported/ =~ error
+ skip error unless output
+ assert_match(entry, File.readlines(output).grep(/TEST STRING/).join("\n"))
+ File.unlink(output)
+ end
+ end
+
+ def test_dump_uninitialized_file
+ assert_in_out_err(%[-robjspace], <<-RUBY) do |(output), (error)|
+ puts ObjectSpace.dump(File.allocate)
+ RUBY
+ assert_nil error
+ assert_match /"type":"FILE"/, output
+ assert_not_match /"fd":/, output
+ end
+ end
+end
diff --git a/jni/ruby/test/open-uri/test_open-uri.rb b/jni/ruby/test/open-uri/test_open-uri.rb
new file mode 100644
index 0000000..3545f5d
--- /dev/null
+++ b/jni/ruby/test/open-uri/test_open-uri.rb
@@ -0,0 +1,832 @@
+require 'test/unit'
+require 'open-uri'
+require 'webrick'
+require 'webrick/httpproxy'
+begin
+ require 'zlib'
+rescue LoadError
+end
+
+class TestOpenURI < Test::Unit::TestCase
+
+ NullLog = Object.new
+ def NullLog.<<(arg)
+ end
+
+ def with_http(log_tester=lambda {|log| assert_equal([], log) })
+ log = []
+ logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN)
+ Dir.mktmpdir {|dr|
+ srv = WEBrick::HTTPServer.new({
+ :DocumentRoot => dr,
+ :ServerType => Thread,
+ :Logger => logger,
+ :AccessLog => [[NullLog, ""]],
+ :BindAddress => '127.0.0.1',
+ :Port => 0})
+ _, port, _, host = srv.listeners[0].addr
+ server_thread = srv.start
+ server_thread2 = Thread.new {
+ server_thread.join
+ if log_tester
+ log_tester.call(log)
+ end
+ }
+ client_thread = Thread.new {
+ begin
+ yield srv, dr, "http://#{host}:#{port}", server_thread, log
+ ensure
+ srv.shutdown
+ end
+ }
+ assert_join_threads([client_thread, server_thread2])
+ }
+ end
+
+ def with_env(h)
+ begin
+ old = {}
+ h.each_key {|k| old[k] = ENV[k] }
+ h.each {|k, v| ENV[k] = v }
+ yield
+ ensure
+ h.each_key {|k| ENV[k] = old[k] }
+ end
+ end
+
+ def setup
+ @proxies = %w[http_proxy HTTP_PROXY ftp_proxy FTP_PROXY no_proxy]
+ @old_proxies = @proxies.map {|k| ENV[k] }
+ @proxies.each {|k| ENV[k] = nil }
+ end
+
+ def teardown
+ @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] }
+ end
+
+ def test_200
+ with_http {|srv, dr, url|
+ srv.mount_proc("/foo200", lambda { |req, res| res.body = "foo200" } )
+ open("#{url}/foo200") {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("foo200", f.read)
+ }
+ }
+ end
+
+ def test_200big
+ with_http {|srv, dr, url|
+ content = "foo200big"*10240
+ srv.mount_proc("/foo200big", lambda { |req, res| res.body = content } )
+ open("#{url}/foo200big") {|f|
+ assert_equal("200", f.status[0])
+ assert_equal(content, f.read)
+ }
+ }
+ end
+
+ def test_404
+ log_tester = lambda {|server_log|
+ assert_equal(1, server_log.length)
+ assert_match(%r{ERROR `/not-exist' not found}, server_log[0])
+ }
+ with_http(log_tester) {|srv, dr, url, server_thread, server_log|
+ exc = assert_raise(OpenURI::HTTPError) { open("#{url}/not-exist") {} }
+ assert_equal("404", exc.io.status[0])
+ }
+ end
+
+ def test_open_uri
+ with_http {|srv, dr, url|
+ srv.mount_proc("/foo_ou", lambda { |req, res| res.body = "foo_ou" } )
+ u = URI("#{url}/foo_ou")
+ open(u) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("foo_ou", f.read)
+ }
+ }
+ end
+
+ def test_open_too_many_arg
+ assert_raise(ArgumentError) { open("http://192.0.2.1/tma", "r", 0666, :extra) {} }
+ end
+
+ def test_read_timeout
+ TCPServer.open("127.0.0.1", 0) {|serv|
+ port = serv.addr[1]
+ th = Thread.new {
+ sock = serv.accept
+ begin
+ req = sock.gets("\r\n\r\n")
+ assert_match(%r{\AGET /foo/bar }, req)
+ sock.print "HTTP/1.0 200 OK\r\n"
+ sock.print "Content-Length: 4\r\n\r\n"
+ sleep 1
+ sock.print "ab\r\n"
+ ensure
+ sock.close
+ end
+ }
+ begin
+ assert_raise(Net::ReadTimeout) { URI("http://127.0.0.1:#{port}/foo/bar").read(:read_timeout=>0.1) }
+ ensure
+ Thread.kill(th)
+ th.join
+ end
+ }
+ end
+
+ def test_open_timeout
+ assert_raises(Net::OpenTimeout) do
+ URI("http://example.com/").read(open_timeout: 0.000001)
+ end if false # avoid external resources in tests
+
+ with_http {|srv, dr, url|
+ url += '/'
+ srv.mount_proc('/', lambda { |_, res| res.body = 'hi' })
+ begin
+ URI(url).read(open_timeout: 0.000001)
+ rescue Net::OpenTimeout
+ # not guaranteed to fire, since the kernel negotiates the
+ # TCP connection even if the server thread is sleeping
+ end
+ assert_equal 'hi', URI(url).read(open_timeout: 60), 'should not timeout'
+ }
+ end
+
+ def test_invalid_option
+ assert_raise(ArgumentError) { open("http://127.0.0.1/", :invalid_option=>true) {} }
+ end
+
+ def test_mode
+ with_http {|srv, dr, url|
+ srv.mount_proc("/mode", lambda { |req, res| res.body = "mode" } )
+ open("#{url}/mode", "r") {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("mode", f.read)
+ }
+ open("#{url}/mode", "r", 0600) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("mode", f.read)
+ }
+ assert_raise(ArgumentError) { open("#{url}/mode", "a") {} }
+ open("#{url}/mode", "r:us-ascii") {|f|
+ assert_equal(Encoding::US_ASCII, f.read.encoding)
+ }
+ open("#{url}/mode", "r:utf-8") {|f|
+ assert_equal(Encoding::UTF_8, f.read.encoding)
+ }
+ assert_raise(ArgumentError) { open("#{url}/mode", "r:invalid-encoding") {} }
+ }
+ end
+
+ def test_without_block
+ with_http {|srv, dr, url|
+ srv.mount_proc("/without_block", lambda { |req, res| res.body = "without_block" } )
+ begin
+ f = open("#{url}/without_block")
+ assert_equal("200", f.status[0])
+ assert_equal("without_block", f.read)
+ ensure
+ f.close if f && !f.closed?
+ end
+ }
+ end
+
+ def test_close_in_block_small
+ with_http {|srv, dr, url|
+ srv.mount_proc("/close200", lambda { |req, res| res.body = "close200" } )
+ assert_nothing_raised {
+ open("#{url}/close200") {|f|
+ f.close
+ }
+ }
+ }
+ end
+
+ def test_close_in_block_big
+ with_http {|srv, dr, url|
+ content = "close200big"*10240
+ srv.mount_proc("/close200big", lambda { |req, res| res.body = content } )
+ assert_nothing_raised {
+ open("#{url}/close200big") {|f|
+ f.close
+ }
+ }
+ }
+ end
+
+ def test_header
+ myheader1 = 'barrrr'
+ myheader2 = nil
+ with_http {|srv, dr, url|
+ srv.mount_proc("/h/") {|req, res| myheader2 = req['myheader']; res.body = "foo" }
+ open("#{url}/h/", 'MyHeader'=>myheader1) {|f|
+ assert_equal("foo", f.read)
+ assert_equal(myheader1, myheader2)
+ }
+ }
+ end
+
+ def test_multi_proxy_opt
+ assert_raise(ArgumentError) {
+ open("http://127.0.0.1/", :proxy_http_basic_authentication=>true, :proxy=>true) {}
+ }
+ end
+
+ def test_non_http_proxy
+ assert_raise(RuntimeError) {
+ open("http://127.0.0.1/", :proxy=>URI("ftp://127.0.0.1/")) {}
+ }
+ end
+
+ def test_proxy
+ with_http {|srv, dr, url|
+ proxy_log = StringIO.new('')
+ proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN)
+ proxy_auth_log = ''
+ proxy = WEBrick::HTTPProxyServer.new({
+ :ServerType => Thread,
+ :Logger => proxy_logger,
+ :AccessLog => [[NullLog, ""]],
+ :ProxyAuthProc => lambda {|req, res|
+ proxy_auth_log << req.request_line
+ },
+ :BindAddress => '127.0.0.1',
+ :Port => 0})
+ _, proxy_port, _, proxy_host = proxy.listeners[0].addr
+ proxy_url = "http://#{proxy_host}:#{proxy_port}/"
+ begin
+ proxy_thread = proxy.start
+ srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } )
+ open("#{url}/proxy", :proxy=>proxy_url) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("proxy", f.read)
+ }
+ assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear
+ open("#{url}/proxy", :proxy=>URI(proxy_url)) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("proxy", f.read)
+ }
+ assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear
+ open("#{url}/proxy", :proxy=>nil) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("proxy", f.read)
+ }
+ assert_equal("", proxy_auth_log); proxy_auth_log.clear
+ assert_raise(ArgumentError) {
+ open("#{url}/proxy", :proxy=>:invalid) {}
+ }
+ assert_equal("", proxy_auth_log); proxy_auth_log.clear
+ with_env("http_proxy"=>proxy_url) {
+ # should not use proxy for 127.0.0.0/8.
+ open("#{url}/proxy") {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("proxy", f.read)
+ }
+ }
+ assert_equal("", proxy_auth_log); proxy_auth_log.clear
+ ensure
+ proxy.shutdown
+ proxy_thread.join
+ end
+ assert_equal("", proxy_log.string)
+ }
+ end
+
+ def test_proxy_http_basic_authentication_failure
+ with_http {|srv, dr, url|
+ proxy_log = StringIO.new('')
+ proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN)
+ proxy_auth_log = ''
+ proxy = WEBrick::HTTPProxyServer.new({
+ :ServerType => Thread,
+ :Logger => proxy_logger,
+ :AccessLog => [[NullLog, ""]],
+ :ProxyAuthProc => lambda {|req, res|
+ proxy_auth_log << req.request_line
+ if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}"
+ raise WEBrick::HTTPStatus::ProxyAuthenticationRequired
+ end
+ },
+ :BindAddress => '127.0.0.1',
+ :Port => 0})
+ _, proxy_port, _, proxy_host = proxy.listeners[0].addr
+ proxy_url = "http://#{proxy_host}:#{proxy_port}/"
+ begin
+ th = proxy.start
+ srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } )
+ exc = assert_raise(OpenURI::HTTPError) { open("#{url}/proxy", :proxy=>proxy_url) {} }
+ assert_equal("407", exc.io.status[0])
+ assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear
+ ensure
+ proxy.shutdown
+ th.join
+ end
+ assert_match(/ERROR WEBrick::HTTPStatus::ProxyAuthenticationRequired/, proxy_log.string)
+ }
+ end
+
+ def test_proxy_http_basic_authentication_success
+ with_http {|srv, dr, url|
+ proxy_log = StringIO.new('')
+ proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN)
+ proxy_auth_log = ''
+ proxy = WEBrick::HTTPProxyServer.new({
+ :ServerType => Thread,
+ :Logger => proxy_logger,
+ :AccessLog => [[NullLog, ""]],
+ :ProxyAuthProc => lambda {|req, res|
+ proxy_auth_log << req.request_line
+ if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}"
+ raise WEBrick::HTTPStatus::ProxyAuthenticationRequired
+ end
+ },
+ :BindAddress => '127.0.0.1',
+ :Port => 0})
+ _, proxy_port, _, proxy_host = proxy.listeners[0].addr
+ proxy_url = "http://#{proxy_host}:#{proxy_port}/"
+ begin
+ th = proxy.start
+ srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } )
+ open("#{url}/proxy",
+ :proxy_http_basic_authentication=>[proxy_url, "user", "pass"]) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("proxy", f.read)
+ }
+ assert_match(/#{Regexp.quote url}/, proxy_auth_log); proxy_auth_log.clear
+ assert_raise(ArgumentError) {
+ open("#{url}/proxy",
+ :proxy_http_basic_authentication=>[true, "user", "pass"]) {}
+ }
+ assert_equal("", proxy_auth_log); proxy_auth_log.clear
+ ensure
+ proxy.shutdown
+ th.join
+ end
+ assert_equal("", proxy_log.string)
+ }
+ end
+
+ def test_redirect
+ with_http {|srv, dr, url|
+ srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" }
+ srv.mount_proc("/r2/") {|req, res| res.body = "r2" }
+ srv.mount_proc("/to-file/") {|req, res| res.status = 301; res["location"] = "file:///foo" }
+ open("#{url}/r1/") {|f|
+ assert_equal("#{url}/r2", f.base_uri.to_s)
+ assert_equal("r2", f.read)
+ }
+ assert_raise(OpenURI::HTTPRedirect) { open("#{url}/r1/", :redirect=>false) {} }
+ assert_raise(RuntimeError) { open("#{url}/to-file/") {} }
+ }
+ end
+
+ def test_redirect_loop
+ with_http {|srv, dr, url|
+ srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" }
+ srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r1"; res.body = "r2" }
+ assert_raise(RuntimeError) { open("#{url}/r1/") {} }
+ }
+ end
+
+ def test_redirect_relative
+ TCPServer.open("127.0.0.1", 0) {|serv|
+ port = serv.addr[1]
+ th = Thread.new {
+ sock = serv.accept
+ begin
+ req = sock.gets("\r\n\r\n")
+ assert_match(%r{\AGET /foo/bar }, req)
+ sock.print "HTTP/1.0 302 Found\r\n"
+ sock.print "Location: ../baz\r\n\r\n"
+ ensure
+ sock.close
+ end
+ sock = serv.accept
+ begin
+ req = sock.gets("\r\n\r\n")
+ assert_match(%r{\AGET /baz }, req)
+ sock.print "HTTP/1.0 200 OK\r\n"
+ sock.print "Content-Length: 4\r\n\r\n"
+ sock.print "ab\r\n"
+ ensure
+ sock.close
+ end
+ }
+ begin
+ content = URI("http://127.0.0.1:#{port}/foo/bar").read
+ assert_equal("ab\r\n", content)
+ ensure
+ Thread.kill(th)
+ th.join
+ end
+ }
+ end
+
+ def test_redirect_invalid
+ TCPServer.open("127.0.0.1", 0) {|serv|
+ port = serv.addr[1]
+ th = Thread.new {
+ sock = serv.accept
+ begin
+ req = sock.gets("\r\n\r\n")
+ assert_match(%r{\AGET /foo/bar }, req)
+ sock.print "HTTP/1.0 302 Found\r\n"
+ sock.print "Location: ::\r\n\r\n"
+ ensure
+ sock.close
+ end
+ }
+ begin
+ assert_raise(OpenURI::HTTPError) {
+ URI("http://127.0.0.1:#{port}/foo/bar").read
+ }
+ ensure
+ Thread.kill(th)
+ th.join
+ end
+ }
+ end
+
+ def setup_redirect_auth(srv, url)
+ srv.mount_proc("/r1/") {|req, res|
+ res.status = 301
+ res["location"] = "#{url}/r2"
+ }
+ srv.mount_proc("/r2/") {|req, res|
+ if req["Authorization"] != "Basic #{['user:pass'].pack('m').chomp}"
+ raise WEBrick::HTTPStatus::Unauthorized
+ end
+ res.body = "r2"
+ }
+ end
+
+ def test_redirect_auth_success
+ with_http {|srv, dr, url|
+ setup_redirect_auth(srv, url)
+ open("#{url}/r2/", :http_basic_authentication=>['user', 'pass']) {|f|
+ assert_equal("r2", f.read)
+ }
+ }
+ end
+
+ def test_redirect_auth_failure_r2
+ log_tester = lambda {|server_log|
+ assert_equal(1, server_log.length)
+ assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, server_log[0])
+ }
+ with_http(log_tester) {|srv, dr, url, server_thread, server_log|
+ setup_redirect_auth(srv, url)
+ exc = assert_raise(OpenURI::HTTPError) { open("#{url}/r2/") {} }
+ assert_equal("401", exc.io.status[0])
+ }
+ end
+
+ def test_redirect_auth_failure_r1
+ log_tester = lambda {|server_log|
+ assert_equal(1, server_log.length)
+ assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, server_log[0])
+ }
+ with_http(log_tester) {|srv, dr, url, server_thread, server_log|
+ setup_redirect_auth(srv, url)
+ exc = assert_raise(OpenURI::HTTPError) { open("#{url}/r1/", :http_basic_authentication=>['user', 'pass']) {} }
+ assert_equal("401", exc.io.status[0])
+ }
+ end
+
+ def test_userinfo
+ assert_raise(ArgumentError) { open("http://user:pass@127.0.0.1/") {} }
+ end
+
+ def test_progress
+ with_http {|srv, dr, url|
+ content = "a" * 100000
+ srv.mount_proc("/data/") {|req, res| res.body = content }
+ length = []
+ progress = []
+ open("#{url}/data/",
+ :content_length_proc => lambda {|n| length << n },
+ :progress_proc => lambda {|n| progress << n }
+ ) {|f|
+ assert_equal(1, length.length)
+ assert_equal(content.length, length[0])
+ assert(progress.length>1,"maybe test is wrong")
+ assert(progress.sort == progress,"monotone increasing expected but was\n#{progress.inspect}")
+ assert_equal(content.length, progress[-1])
+ assert_equal(content, f.read)
+ }
+ }
+ end
+
+ def test_progress_chunked
+ with_http {|srv, dr, url|
+ content = "a" * 100000
+ srv.mount_proc("/data/") {|req, res| res.body = content; res.chunked = true }
+ length = []
+ progress = []
+ open("#{url}/data/",
+ :content_length_proc => lambda {|n| length << n },
+ :progress_proc => lambda {|n| progress << n }
+ ) {|f|
+ assert_equal(1, length.length)
+ assert_equal(nil, length[0])
+ assert(progress.length>1,"maybe test is worng")
+ assert(progress.sort == progress,"monotone increasing expected but was\n#{progress.inspect}")
+ assert_equal(content.length, progress[-1])
+ assert_equal(content, f.read)
+ }
+ }
+ end
+
+ def test_uri_read
+ with_http {|srv, dr, url|
+ srv.mount_proc("/uriread", lambda { |req, res| res.body = "uriread" } )
+ data = URI("#{url}/uriread").read
+ assert_equal("200", data.status[0])
+ assert_equal("uriread", data)
+ }
+ end
+
+ def test_encoding
+ with_http {|srv, dr, url|
+ content_u8 = "\u3042"
+ content_ej = "\xa2\xa4".force_encoding("euc-jp")
+ srv.mount_proc("/u8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset=utf-8' }
+ srv.mount_proc("/ej/") {|req, res| res.body = content_ej; res['content-type'] = 'TEXT/PLAIN; charset=EUC-JP' }
+ srv.mount_proc("/nc/") {|req, res| res.body = "aa"; res['content-type'] = 'Text/Plain' }
+ open("#{url}/u8/") {|f|
+ assert_equal(content_u8, f.read)
+ assert_equal("text/plain", f.content_type)
+ assert_equal("utf-8", f.charset)
+ }
+ open("#{url}/ej/") {|f|
+ assert_equal(content_ej, f.read)
+ assert_equal("text/plain", f.content_type)
+ assert_equal("euc-jp", f.charset)
+ }
+ open("#{url}/nc/") {|f|
+ assert_equal("aa", f.read)
+ assert_equal("text/plain", f.content_type)
+ assert_equal("iso-8859-1", f.charset)
+ assert_equal("unknown", f.charset { "unknown" })
+ }
+ }
+ end
+
+ def test_quoted_attvalue
+ with_http {|srv, dr, url|
+ content_u8 = "\u3042"
+ srv.mount_proc("/qu8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset="utf\-8"' }
+ open("#{url}/qu8/") {|f|
+ assert_equal(content_u8, f.read)
+ assert_equal("text/plain", f.content_type)
+ assert_equal("utf-8", f.charset)
+ }
+ }
+ end
+
+ def test_last_modified
+ with_http {|srv, dr, url|
+ srv.mount_proc("/data/") {|req, res| res.body = "foo"; res['last-modified'] = 'Fri, 07 Aug 2009 06:05:04 GMT' }
+ open("#{url}/data/") {|f|
+ assert_equal("foo", f.read)
+ assert_equal(Time.utc(2009,8,7,6,5,4), f.last_modified)
+ }
+ }
+ end
+
+ def test_content_encoding
+ with_http {|srv, dr, url|
+ content = "abc" * 10000
+ Zlib::GzipWriter.wrap(StringIO.new(content_gz="".force_encoding("ascii-8bit"))) {|z| z.write content }
+ srv.mount_proc("/data/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip' }
+ srv.mount_proc("/data2/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip'; res.chunked = true }
+ srv.mount_proc("/noce/") {|req, res| res.body = content_gz }
+ open("#{url}/data/") {|f|
+ assert_equal [], f.content_encoding
+ assert_equal(content, f.read)
+ }
+ open("#{url}/data2/") {|f|
+ assert_equal [], f.content_encoding
+ assert_equal(content, f.read)
+ }
+ open("#{url}/noce/") {|f|
+ assert_equal [], f.content_encoding
+ assert_equal(content_gz, f.read.force_encoding("ascii-8bit"))
+ }
+ }
+ end if defined?(Zlib::GzipWriter)
+
+ def test_multiple_cookies
+ with_http {|srv, dr, url|
+ srv.mount_proc("/mcookie/") {|req, res|
+ res.cookies << "name1=value1; blabla"
+ res.cookies << "name2=value2; blabla"
+ res.body = "foo"
+ }
+ open("#{url}/mcookie/") {|f|
+ assert_equal("foo", f.read)
+ assert_equal(["name1=value1; blabla", "name2=value2; blabla"],
+ f.metas['set-cookie'].sort)
+ }
+ }
+ end
+
+ # 192.0.2.0/24 is TEST-NET. [RFC3330]
+
+ def test_ftp_invalid_request
+ assert_raise(ArgumentError) { URI("ftp://127.0.0.1/").read }
+ assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db").read }
+ assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab").read }
+ assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db/f").read }
+ assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab/f").read }
+ assert_nothing_raised(URI::InvalidComponentError) { URI("ftp://127.0.0.1/d/f;type=x") }
+ end
+
+ def test_ftp
+ TCPServer.open("127.0.0.1", 0) {|serv|
+ _, port, _, host = serv.addr
+ th = Thread.new {
+ s = serv.accept
+ begin
+ s.print "220 Test FTP Server\r\n"
+ assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n"
+ assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n"
+ assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n"
+ assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n"
+ assert_equal("PASV\r\n", s.gets)
+ TCPServer.open("127.0.0.1", 0) {|data_serv|
+ _, data_serv_port, _, _ = data_serv.addr
+ hi = data_serv_port >> 8
+ lo = data_serv_port & 0xff
+ s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n"
+ assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n"
+ data_sock = data_serv.accept
+ begin
+ data_sock << "content"
+ ensure
+ data_sock.close
+ end
+ s.print "226 transfer complete\r\n"
+ assert_nil(s.gets)
+ }
+ ensure
+ s.close if s
+ end
+ }
+ begin
+ content = URI("ftp://#{host}:#{port}/foo/bar").read
+ assert_equal("content", content)
+ ensure
+ Thread.kill(th)
+ th.join
+ end
+ }
+ end
+
+ def test_ftp_active
+ TCPServer.open("127.0.0.1", 0) {|serv|
+ _, port, _, host = serv.addr
+ th = Thread.new {
+ s = serv.accept
+ begin
+ content = "content"
+ s.print "220 Test FTP Server\r\n"
+ assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n"
+ assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n"
+ assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n"
+ assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n"
+ assert(m = /\APORT 127,0,0,1,(\d+),(\d+)\r\n\z/.match(s.gets))
+ active_port = m[1].to_i << 8 | m[2].to_i
+ TCPSocket.open("127.0.0.1", active_port) {|data_sock|
+ s.print "200 data connection opened\r\n"
+ assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n"
+ begin
+ data_sock << content
+ ensure
+ data_sock.close
+ end
+ s.print "226 transfer complete\r\n"
+ assert_nil(s.gets)
+ }
+ ensure
+ s.close if s
+ end
+ }
+ begin
+ content = URI("ftp://#{host}:#{port}/foo/bar").read(:ftp_active_mode=>true)
+ assert_equal("content", content)
+ ensure
+ Thread.kill(th)
+ th.join
+ end
+ }
+ end
+
+ def test_ftp_ascii
+ TCPServer.open("127.0.0.1", 0) {|serv|
+ _, port, _, host = serv.addr
+ th = Thread.new {
+ s = serv.accept
+ begin
+ content = "content"
+ s.print "220 Test FTP Server\r\n"
+ assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n"
+ assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n"
+ assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n"
+ assert_equal("CWD /foo\r\n", s.gets); s.print "250 CWD successful\r\n"
+ assert_equal("TYPE A\r\n", s.gets); s.print "200 type set to A\r\n"
+ assert_equal("SIZE bar\r\n", s.gets); s.print "213 #{content.bytesize}\r\n"
+ assert_equal("PASV\r\n", s.gets)
+ TCPServer.open("127.0.0.1", 0) {|data_serv|
+ _, data_serv_port, _, _ = data_serv.addr
+ hi = data_serv_port >> 8
+ lo = data_serv_port & 0xff
+ s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n"
+ assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n"
+ data_sock = data_serv.accept
+ begin
+ data_sock << content
+ ensure
+ data_sock.close
+ end
+ s.print "226 transfer complete\r\n"
+ assert_nil(s.gets)
+ }
+ ensure
+ s.close if s
+ end
+ }
+ begin
+ length = []
+ progress = []
+ content = URI("ftp://#{host}:#{port}/%2Ffoo/b%61r;type=a").read(
+ :content_length_proc => lambda {|n| length << n },
+ :progress_proc => lambda {|n| progress << n })
+ assert_equal("content", content)
+ assert_equal([7], length)
+ assert_equal(7, progress.inject(&:+))
+ ensure
+ Thread.kill(th)
+ th.join
+ end
+ }
+ end
+
+ def test_ftp_over_http_proxy
+ TCPServer.open("127.0.0.1", 0) {|proxy_serv|
+ proxy_port = proxy_serv.addr[1]
+ th = Thread.new {
+ proxy_sock = proxy_serv.accept
+ begin
+ req = proxy_sock.gets("\r\n\r\n")
+ assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req)
+ proxy_sock.print "HTTP/1.0 200 OK\r\n"
+ proxy_sock.print "Content-Length: 4\r\n\r\n"
+ proxy_sock.print "ab\r\n"
+ ensure
+ proxy_sock.close
+ end
+ }
+ begin
+ with_env('ftp_proxy'=>"http://127.0.0.1:#{proxy_port}") {
+ content = URI("ftp://192.0.2.1/foo/bar").read
+ assert_equal("ab\r\n", content)
+ }
+ ensure
+ Thread.kill(th)
+ th.join
+ end
+ }
+ end
+
+ def test_ftp_over_http_proxy_auth
+ TCPServer.open("127.0.0.1", 0) {|proxy_serv|
+ proxy_port = proxy_serv.addr[1]
+ th = Thread.new {
+ proxy_sock = proxy_serv.accept
+ begin
+ req = proxy_sock.gets("\r\n\r\n")
+ assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req)
+ assert_match(%r{Proxy-Authorization: Basic #{['proxy-user:proxy-password'].pack('m').chomp}\r\n}, req)
+ proxy_sock.print "HTTP/1.0 200 OK\r\n"
+ proxy_sock.print "Content-Length: 4\r\n\r\n"
+ proxy_sock.print "ab\r\n"
+ ensure
+ proxy_sock.close
+ end
+ }
+ begin
+ content = URI("ftp://192.0.2.1/foo/bar").read(
+ :proxy_http_basic_authentication => ["http://127.0.0.1:#{proxy_port}", "proxy-user", "proxy-password"])
+ assert_equal("ab\r\n", content)
+ ensure
+ Thread.kill(th)
+ th.join
+ end
+ }
+ end
+
+end
+
diff --git a/jni/ruby/test/open-uri/test_ssl.rb b/jni/ruby/test/open-uri/test_ssl.rb
new file mode 100644
index 0000000..fa40f6d
--- /dev/null
+++ b/jni/ruby/test/open-uri/test_ssl.rb
@@ -0,0 +1,390 @@
+require 'test/unit'
+require 'open-uri'
+require 'stringio'
+require 'webrick'
+begin
+ require 'openssl'
+ require 'webrick/https'
+ require_relative '../openssl/utils'
+rescue LoadError
+end
+require 'webrick/httpproxy'
+
+class TestOpenURISSL < Test::Unit::TestCase
+end
+
+class TestOpenURISSL
+ NullLog = Object.new
+ def NullLog.<<(arg)
+ end
+
+ def with_https(log_tester=lambda {|log| assert_equal([], log) })
+ log = []
+ logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN)
+ Dir.mktmpdir {|dr|
+ srv = WEBrick::HTTPServer.new({
+ :DocumentRoot => dr,
+ :ServerType => Thread,
+ :Logger => logger,
+ :AccessLog => [[NullLog, ""]],
+ :SSLEnable => true,
+ :SSLCertificate => OpenSSL::X509::Certificate.new(SERVER_CERT),
+ :SSLPrivateKey => OpenSSL::PKey::RSA.new(SERVER_KEY),
+ :SSLTmpDhCallback => proc { OpenSSL::TestUtils::TEST_KEY_DH1024 },
+ :BindAddress => '127.0.0.1',
+ :Port => 0})
+ _, port, _, host = srv.listeners[0].addr
+ threads = []
+ server_thread = srv.start
+ threads << Thread.new {
+ server_thread.join
+ if log_tester
+ log_tester.call(log)
+ end
+ }
+ threads << Thread.new {
+ begin
+ yield srv, dr, "https://#{host}:#{port}", server_thread, log, threads
+ ensure
+ srv.shutdown
+ end
+ }
+ assert_join_threads(threads)
+ }
+ end
+
+ def setup
+ @proxies = %w[http_proxy HTTP_PROXY https_proxy HTTPS_PROXY ftp_proxy FTP_PROXY no_proxy]
+ @old_proxies = @proxies.map {|k| ENV[k] }
+ @proxies.each {|k| ENV[k] = nil }
+ end
+
+ def teardown
+ @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] }
+ end
+
+ def setup_validation(srv, dr)
+ cacert_filename = "#{dr}/cacert.pem"
+ open(cacert_filename, "w") {|f| f << CA_CERT }
+ srv.mount_proc("/data", lambda { |req, res| res.body = "ddd" } )
+ cacert_filename
+ end
+
+ def test_validation_success
+ with_https {|srv, dr, url|
+ cacert_filename = setup_validation(srv, dr)
+ open("#{url}/data", :ssl_ca_cert => cacert_filename) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("ddd", f.read)
+ }
+ }
+ end
+
+ def test_validation_noverify
+ with_https {|srv, dr, url|
+ setup_validation(srv, dr)
+ open("#{url}/data", :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("ddd", f.read)
+ }
+ }
+ end
+
+ def test_validation_failure
+ unless /mswin|mingw/ =~ RUBY_PLATFORM
+ # on Windows, Errno::ECONNRESET will be raised, and it'll be eaten by
+ # WEBrick
+ log_tester = lambda {|server_log|
+ assert_equal(1, server_log.length)
+ assert_match(/ERROR OpenSSL::SSL::SSLError:/, server_log[0])
+ }
+ end
+ with_https(log_tester) {|srv, dr, url, server_thread, server_log|
+ setup_validation(srv, dr)
+ assert_raise(OpenSSL::SSL::SSLError) { open("#{url}/data") {} }
+ }
+ end
+
+ def with_https_proxy(proxy_log_tester=lambda {|proxy_log, proxy_access_log| assert_equal([], proxy_log) })
+ proxy_log = []
+ proxy_logger = WEBrick::Log.new(proxy_log, WEBrick::BasicLog::WARN)
+ with_https {|srv, dr, url, server_thread, server_log, threads|
+ srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } )
+ cacert_filename = "#{dr}/cacert.pem"
+ open(cacert_filename, "w") {|f| f << CA_CERT }
+ cacert_directory = "#{dr}/certs"
+ Dir.mkdir cacert_directory
+ hashed_name = "%08x.0" % OpenSSL::X509::Certificate.new(CA_CERT).subject.hash
+ open("#{cacert_directory}/#{hashed_name}", "w") {|f| f << CA_CERT }
+ proxy = WEBrick::HTTPProxyServer.new({
+ :ServerType => Thread,
+ :Logger => proxy_logger,
+ :AccessLog => [[proxy_access_log=[], WEBrick::AccessLog::COMMON_LOG_FORMAT]],
+ :BindAddress => '127.0.0.1',
+ :Port => 0})
+ _, proxy_port, _, proxy_host = proxy.listeners[0].addr
+ proxy_thread = proxy.start
+ threads << Thread.new {
+ proxy_thread.join
+ if proxy_log_tester
+ proxy_log_tester.call(proxy_log, proxy_access_log)
+ end
+ }
+ begin
+ yield srv, dr, url, cacert_filename, cacert_directory, proxy_host, proxy_port
+ ensure
+ proxy.shutdown
+ end
+ }
+ end
+
+ def test_proxy_cacert_file
+ url = nil
+ proxy_log_tester = lambda {|proxy_log, proxy_access_log|
+ assert_equal(1, proxy_access_log.length)
+ assert_match(%r[CONNECT #{url.sub(%r{\Ahttps://}, '')} ], proxy_access_log[0])
+ assert_equal([], proxy_log)
+ }
+ with_https_proxy(proxy_log_tester) {|srv, dr, url_, cacert_filename, cacert_directory, proxy_host, proxy_port|
+ url = url_
+ open("#{url}/proxy", :proxy=>"http://#{proxy_host}:#{proxy_port}/", :ssl_ca_cert => cacert_filename) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("proxy", f.read)
+ }
+ }
+ end
+
+ def test_proxy_cacert_dir
+ url = nil
+ proxy_log_tester = lambda {|proxy_log, proxy_access_log|
+ assert_equal(1, proxy_access_log.length)
+ assert_match(%r[CONNECT #{url.sub(%r{\Ahttps://}, '')} ], proxy_access_log[0])
+ assert_equal([], proxy_log)
+ }
+ with_https_proxy(proxy_log_tester) {|srv, dr, url_, cacert_filename, cacert_directory, proxy_host, proxy_port|
+ url = url_
+ open("#{url}/proxy", :proxy=>"http://#{proxy_host}:#{proxy_port}/", :ssl_ca_cert => cacert_directory) {|f|
+ assert_equal("200", f.status[0])
+ assert_equal("proxy", f.read)
+ }
+ }
+ end
+
+end if defined?(OpenSSL::TestUtils)
+
+if defined?(OpenSSL::TestUtils)
+# mkdir demoCA demoCA/private demoCA/newcerts
+# touch demoCA/index.txt
+# echo 00 > demoCA/serial
+# openssl req -new -keyout demoCA/private/cakey.pem -out demoCA/careq.pem
+# openssl ca -out demoCA/cacert.pem -startdate 090101000000Z -enddate 491231235959Z -batch -keyfile demoCA/private/cakey.pem -selfsign -infiles demoCA/careq.pem
+
+# cp /etc/ssl/openssl.cnf openssl-server.cnf # Debian
+# vi openssl-server.cnf # enable "nsCertType = server"
+# mkdir server
+# openssl genrsa -des3 -out server/server.key 1024
+# openssl rsa -in server/server.key -out server/servernopass.key
+# openssl req -new -days 365 -key server/servernopass.key -out server/csr.pem
+# openssl ca -config openssl-server.cnf -startdate 090101000000Z -enddate 491231235959Z -in server/csr.pem -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem -out server/cert.pem
+
+# demoCA/cacert.pem => TestOpenURISSL::CA_CERT
+# server/cert.pem => TestOpenURISSL::SERVER_CERT
+# server/servernopass.key => TestOpenURISSL::SERVER_KEY
+
+TestOpenURISSL::CA_CERT = <<'End'
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 0 (0x0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
+ Validity
+ Not Before: Jan 1 00:00:00 2009 GMT
+ Not After : Dec 31 23:59:59 2049 GMT
+ Subject: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:9f:58:19:39:bc:ea:0c:b8:c3:5d:12:a7:d8:20:
+ 6c:53:ac:91:34:c8:b4:db:3f:56:f6:75:b6:6c:23:
+ 80:23:6a:5f:b3:f6:9a:3e:00:b4:16:19:1c:9c:2c:
+ 8d:e8:53:d5:0b:f1:52:3f:7b:60:93:86:ae:89:ab:
+ 20:82:9a:b6:72:14:3c:4d:a9:0b:6c:34:79:9e:d3:
+ 14:82:6d:c9:3b:90:d9:5e:68:6f:8c:b5:d8:09:f4:
+ 6f:3b:22:9f:5e:81:9c:37:df:cf:90:36:65:57:dc:
+ ad:31:ca:8b:48:92:a7:3c:1e:42:e9:1c:4e:1e:cb:
+ 36:c1:44:4e:ab:9a:b2:73:6d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F
+ X509v3 Authority Key Identifier:
+ keyid:24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 13:eb:db:ca:cd:90:f2:09:9e:d9:72:70:5e:42:5d:11:84:ce:
+ 00:1d:c4:2f:41:d2:3e:16:e5:d4:97:1f:43:a9:a7:9c:fa:60:
+ c4:35:96:f2:f6:0d:13:6d:0f:36:dd:59:03:08:ee:2e:a6:df:
+ 9e:d8:6d:ca:72:8f:02:c2:2b:53:7b:12:7f:55:81:6c:9e:7d:
+ e7:40:7e:f8:f5:75:0d:4b:a0:8d:ee:a4:d9:e8:5f:06:c9:86:
+ 66:71:70:6c:41:81:6a:dd:a4:4f:a3:c1:ac:70:d4:78:1b:23:
+ 30:2f:a5:ef:98:ee:d4:62:80:fd:bf:d4:7a:9b:8e:2d:18:e5:
+ 00:46
+-----BEGIN CERTIFICATE-----
+MIICfzCCAeigAwIBAgIBADANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJKUDEO
+MAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYDVQQDEwxSdWJ5
+IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBHMQswCQYD
+VQQGEwJKUDEOMAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYD
+VQQDEwxSdWJ5IFRlc3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ9Y
+GTm86gy4w10Sp9ggbFOskTTItNs/VvZ1tmwjgCNqX7P2mj4AtBYZHJwsjehT1Qvx
+Uj97YJOGromrIIKatnIUPE2pC2w0eZ7TFIJtyTuQ2V5ob4y12An0bzsin16BnDff
+z5A2ZVfcrTHKi0iSpzweQukcTh7LNsFETquasnNtAgMBAAGjezB5MAkGA1UdEwQC
+MAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRl
+MB0GA1UdDgQWBBQkbwOj7gZRdbK6/Do4Wb/th83ofzAfBgNVHSMEGDAWgBQkbwOj
+7gZRdbK6/Do4Wb/th83ofzANBgkqhkiG9w0BAQUFAAOBgQAT69vKzZDyCZ7ZcnBe
+Ql0RhM4AHcQvQdI+FuXUlx9Dqaec+mDENZby9g0TbQ823VkDCO4upt+e2G3Kco8C
+witTexJ/VYFsnn3nQH749XUNS6CN7qTZ6F8GyYZmcXBsQYFq3aRPo8GscNR4GyMw
+L6XvmO7UYoD9v9R6m44tGOUARg==
+-----END CERTIFICATE-----
+End
+
+TestOpenURISSL::SERVER_CERT = <<'End'
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, ST=Tokyo, O=RubyTest, CN=Ruby Test CA
+ Validity
+ Not Before: Jan 1 00:00:00 2009 GMT
+ Not After : Dec 31 23:59:59 2049 GMT
+ Subject: C=JP, ST=Tokyo, O=RubyTest, CN=127.0.0.1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:bb:bd:74:69:53:58:50:24:79:f2:eb:db:8b:97:
+ e4:69:a4:dd:48:0c:40:35:62:42:b3:35:8c:96:2a:
+ 62:76:98:b5:2a:e0:f8:78:33:b6:ff:f8:55:bf:44:
+ 69:21:d7:b5:0e:bd:8a:dd:31:1b:88:d5:b4:5e:7a:
+ 82:e0:ba:99:6c:04:76:e9:ff:e6:f8:f5:06:8e:7e:
+ a4:db:db:eb:43:44:12:a7:ca:ca:2b:aa:5f:83:10:
+ e2:9e:35:55:e8:e8:af:be:c8:7d:bb:c2:d4:aa:c1:
+ 1c:57:0b:c0:0c:3a:1d:6e:23:a9:03:26:7c:ea:8c:
+ f0:86:61:ce:f1:ff:42:c7:23
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Cert Type:
+ SSL Server
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 7F:17:5A:58:88:96:E1:1F:44:EA:FF:AD:C6:2E:90:E2:95:32:DD:F0
+ X509v3 Authority Key Identifier:
+ keyid:24:6F:03:A3:EE:06:51:75:B2:BA:FC:3A:38:59:BF:ED:87:CD:E8:7F
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 9a:34:99:ea:76:a2:ed:f0:f7:a7:75:3b:81:fb:75:57:93:c1:
+ 27:b6:1e:7a:38:67:95:be:58:42:9a:0a:dd:2b:23:fb:85:42:
+ 80:34:bf:b9:0e:9c:5e:5a:dc:2d:25:8c:68:02:a2:c7:7f:c0:
+ eb:f3:e0:61:e2:05:e5:7e:c1:e0:33:1c:76:65:23:2c:25:08:
+ f6:5a:11:b9:d4:f7:e3:80:bb:b0:ce:76:1a:56:22:af:e2:4a:
+ e1:7e:a4:60:f3:fd:9c:53:46:51:57:32:6b:05:53:80:5c:a5:
+ 61:93:87:ae:06:a8:a2:ba:4d:a1:b7:1b:0f:8f:82:0a:e8:b3:
+ ea:63
+-----BEGIN CERTIFICATE-----
+MIICkTCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJKUDEO
+MAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRUwEwYDVQQDEwxSdWJ5
+IFRlc3QgQ0EwHhcNMDkwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjBEMQswCQYD
+VQQGEwJKUDEOMAwGA1UECBMFVG9reW8xETAPBgNVBAoTCFJ1YnlUZXN0MRIwEAYD
+VQQDEwkxMjcuMC4wLjEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALu9dGlT
+WFAkefLr24uX5Gmk3UgMQDViQrM1jJYqYnaYtSrg+Hgztv/4Vb9EaSHXtQ69it0x
+G4jVtF56guC6mWwEdun/5vj1Bo5+pNvb60NEEqfKyiuqX4MQ4p41Vejor77IfbvC
+1KrBHFcLwAw6HW4jqQMmfOqM8IZhzvH/QscjAgMBAAGjgY8wgYwwCQYDVR0TBAIw
+ADARBglghkgBhvhCAQEEBAMCBkAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu
+ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBR/F1pYiJbhH0Tq/63GLpDilTLd
+8DAfBgNVHSMEGDAWgBQkbwOj7gZRdbK6/Do4Wb/th83ofzANBgkqhkiG9w0BAQUF
+AAOBgQCaNJnqdqLt8PendTuB+3VXk8Enth56OGeVvlhCmgrdKyP7hUKANL+5Dpxe
+WtwtJYxoAqLHf8Dr8+Bh4gXlfsHgMxx2ZSMsJQj2WhG51PfjgLuwznYaViKv4krh
+fqRg8/2cU0ZRVzJrBVOAXKVhk4euBqiiuk2htxsPj4IK6LPqYw==
+-----END CERTIFICATE-----
+End
+
+TestOpenURISSL::SERVER_KEY = <<'End'
+Private-Key: (1024 bit)
+modulus:
+ 00:bb:bd:74:69:53:58:50:24:79:f2:eb:db:8b:97:
+ e4:69:a4:dd:48:0c:40:35:62:42:b3:35:8c:96:2a:
+ 62:76:98:b5:2a:e0:f8:78:33:b6:ff:f8:55:bf:44:
+ 69:21:d7:b5:0e:bd:8a:dd:31:1b:88:d5:b4:5e:7a:
+ 82:e0:ba:99:6c:04:76:e9:ff:e6:f8:f5:06:8e:7e:
+ a4:db:db:eb:43:44:12:a7:ca:ca:2b:aa:5f:83:10:
+ e2:9e:35:55:e8:e8:af:be:c8:7d:bb:c2:d4:aa:c1:
+ 1c:57:0b:c0:0c:3a:1d:6e:23:a9:03:26:7c:ea:8c:
+ f0:86:61:ce:f1:ff:42:c7:23
+publicExponent: 65537 (0x10001)
+privateExponent:
+ 00:af:3a:ec:17:0a:f5:d9:07:d2:d3:4c:15:c5:3b:
+ 66:b4:bc:6e:d5:ba:a9:8b:aa:45:3b:63:f5:ee:8b:
+ 6d:0f:e9:04:e0:1a:cf:8f:d2:25:32:d1:a5:a7:3a:
+ c1:2e:17:5a:25:82:00:c4:e7:fb:1d:42:ea:71:6c:
+ c4:0f:e1:db:23:ff:1e:d6:c8:d6:60:ca:2d:06:fc:
+ 54:3c:03:d4:09:96:bb:38:7a:22:a1:61:2c:f7:d0:
+ d0:90:6c:9f:61:ba:61:30:5a:aa:64:ad:43:3a:53:
+ 38:e8:ba:cc:8c:51:3e:68:3e:3a:6a:0f:5d:5d:e0:
+ d6:df:f2:54:93:d3:14:22:a1
+prime1:
+ 00:e8:ec:11:fe:e6:2b:23:21:29:d5:40:a6:11:ec:
+ 4c:ae:4d:08:2a:71:18:ac:d1:3e:40:2f:12:41:59:
+ 12:09:e2:f7:c2:d7:6b:0a:96:0a:06:e3:90:6a:4e:
+ b2:eb:25:b7:09:68:e9:13:ab:d0:5a:29:7a:e4:72:
+ 1a:ee:46:a0:8b
+prime2:
+ 00:ce:57:5e:31:e9:c9:a8:5b:1f:55:af:67:e2:49:
+ 2a:af:90:b6:02:c0:32:2f:ca:ae:1e:de:47:81:73:
+ a8:f8:37:53:70:93:24:62:77:d4:b8:80:30:9f:65:
+ 26:20:46:ae:5a:65:6e:6d:af:68:4c:8d:e8:3c:f3:
+ d1:d1:d9:6e:c9
+exponent1:
+ 03:f1:02:b8:f2:82:26:5d:08:4d:30:83:de:e7:c5:
+ c0:69:53:4b:0c:90:e3:53:c3:1e:e8:ed:01:28:15:
+ b3:0f:21:2c:2d:e3:04:d1:d7:27:98:b0:37:ec:4f:
+ 00:c5:a9:9c:42:27:37:8a:ff:c2:96:d3:1a:8c:87:
+ c2:22:75:d3
+exponent2:
+ 6f:17:32:ab:84:c7:01:51:2d:e9:9f:ea:3a:36:52:
+ 38:fb:9c:42:96:df:6e:43:9c:c3:19:c1:3d:bc:db:
+ 77:e7:b1:90:a6:67:ac:6b:ff:a6:e5:bd:47:d3:d9:
+ 56:ff:36:d7:8c:4c:8b:d9:28:3a:2f:1c:9d:d4:57:
+ 5e:b7:c5:a1
+coefficient:
+ 45:50:47:66:56:e9:21:d9:40:0e:af:3f:f2:05:77:
+ ab:e7:08:40:97:88:2a:51:b3:7e:86:b0:b2:03:2e:
+ 6d:36:3f:46:42:97:7d:5a:a2:93:6c:05:c2:8b:8b:
+ 2d:af:d5:7d:75:e9:70:f0:2d:21:e3:b9:cf:4d:9a:
+ c4:97:e2:79
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC7vXRpU1hQJHny69uLl+RppN1IDEA1YkKzNYyWKmJ2mLUq4Ph4
+M7b/+FW/RGkh17UOvYrdMRuI1bReeoLguplsBHbp/+b49QaOfqTb2+tDRBKnysor
+ql+DEOKeNVXo6K++yH27wtSqwRxXC8AMOh1uI6kDJnzqjPCGYc7x/0LHIwIDAQAB
+AoGBAK867BcK9dkH0tNMFcU7ZrS8btW6qYuqRTtj9e6LbQ/pBOAaz4/SJTLRpac6
+wS4XWiWCAMTn+x1C6nFsxA/h2yP/HtbI1mDKLQb8VDwD1AmWuzh6IqFhLPfQ0JBs
+n2G6YTBaqmStQzpTOOi6zIxRPmg+OmoPXV3g1t/yVJPTFCKhAkEA6OwR/uYrIyEp
+1UCmEexMrk0IKnEYrNE+QC8SQVkSCeL3wtdrCpYKBuOQak6y6yW3CWjpE6vQWil6
+5HIa7kagiwJBAM5XXjHpyahbH1WvZ+JJKq+QtgLAMi/Krh7eR4FzqPg3U3CTJGJ3
+1LiAMJ9lJiBGrlplbm2vaEyN6Dzz0dHZbskCQAPxArjygiZdCE0wg97nxcBpU0sM
+kONTwx7o7QEoFbMPISwt4wTR1yeYsDfsTwDFqZxCJzeK/8KW0xqMh8IiddMCQG8X
+MquExwFRLemf6jo2Ujj7nEKW325DnMMZwT2823fnsZCmZ6xr/6blvUfT2Vb/NteM
+TIvZKDovHJ3UV163xaECQEVQR2ZW6SHZQA6vP/IFd6vnCECXiCpRs36GsLIDLm02
+P0ZCl31aopNsBcKLiy2v1X116XDwLSHjuc9NmsSX4nk=
+-----END RSA PRIVATE KEY-----
+End
+
+end
diff --git a/jni/ruby/test/openssl/test_asn1.rb b/jni/ruby/test/openssl/test_asn1.rb
new file mode 100644
index 0000000..9fb5a55
--- /dev/null
+++ b/jni/ruby/test/openssl/test_asn1.rb
@@ -0,0 +1,609 @@
+require_relative 'utils'
+
+class OpenSSL::TestASN1 < Test::Unit::TestCase
+ def test_decode
+ subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ now = Time.at(Time.now.to_i) # suppress usec
+ s = 0xdeadbeafdeadbeafdeadbeafdeadbeaf
+ exts = [
+ ["basicConstraints","CA:TRUE,pathlen:1",true],
+ ["keyUsage","keyCertSign, cRLSign",true],
+ ["subjectKeyIdentifier","hash",false],
+ ]
+ dgst = OpenSSL::Digest::SHA1.new
+ cert = OpenSSL::TestUtils.issue_cert(
+ subj, key, s, now, now+3600, exts, nil, nil, dgst)
+
+
+ asn1 = OpenSSL::ASN1.decode(cert)
+ assert_equal(OpenSSL::ASN1::Sequence, asn1.class)
+ assert_equal(3, asn1.value.size)
+ tbs_cert, sig_alg, sig_val = *asn1.value
+
+ assert_equal(OpenSSL::ASN1::Sequence, tbs_cert.class)
+ assert_equal(8, tbs_cert.value.size)
+
+ version = tbs_cert.value[0]
+ assert_equal(:CONTEXT_SPECIFIC, version.tag_class)
+ assert_equal(0, version.tag)
+ assert_equal(1, version.value.size)
+ assert_equal(OpenSSL::ASN1::Integer, version.value[0].class)
+ assert_equal(2, version.value[0].value)
+
+ serial = tbs_cert.value[1]
+ assert_equal(OpenSSL::ASN1::Integer, serial.class)
+ assert_equal(0xdeadbeafdeadbeafdeadbeafdeadbeaf, serial.value)
+
+ sig = tbs_cert.value[2]
+ assert_equal(OpenSSL::ASN1::Sequence, sig.class)
+ assert_equal(2, sig.value.size)
+ assert_equal(OpenSSL::ASN1::ObjectId, sig.value[0].class)
+ assert_equal("1.2.840.113549.1.1.5", sig.value[0].oid)
+ assert_equal(OpenSSL::ASN1::Null, sig.value[1].class)
+
+ dn = tbs_cert.value[3] # issuer
+ assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash)
+ assert_equal(OpenSSL::ASN1::Sequence, dn.class)
+ assert_equal(3, dn.value.size)
+ assert_equal(OpenSSL::ASN1::Set, dn.value[0].class)
+ assert_equal(OpenSSL::ASN1::Set, dn.value[1].class)
+ assert_equal(OpenSSL::ASN1::Set, dn.value[2].class)
+ assert_equal(1, dn.value[0].value.size)
+ assert_equal(1, dn.value[1].value.size)
+ assert_equal(1, dn.value[2].value.size)
+ assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class)
+ assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class)
+ assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class)
+ assert_equal(2, dn.value[0].value[0].value.size)
+ assert_equal(2, dn.value[1].value[0].value.size)
+ assert_equal(2, dn.value[2].value[0].value.size)
+ oid, value = *dn.value[0].value[0].value
+ assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+ assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+ assert_equal(OpenSSL::ASN1::IA5String, value.class)
+ assert_equal("org", value.value)
+ oid, value = *dn.value[1].value[0].value
+ assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+ assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+ assert_equal(OpenSSL::ASN1::IA5String, value.class)
+ assert_equal("ruby-lang", value.value)
+ oid, value = *dn.value[2].value[0].value
+ assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+ assert_equal("2.5.4.3", oid.oid)
+ assert_equal(OpenSSL::ASN1::UTF8String, value.class)
+ assert_equal("TestCA", value.value)
+
+ validity = tbs_cert.value[4]
+ assert_equal(OpenSSL::ASN1::Sequence, validity.class)
+ assert_equal(2, validity.value.size)
+ assert_equal(OpenSSL::ASN1::UTCTime, validity.value[0].class)
+ assert_equal(now, validity.value[0].value)
+ assert_equal(OpenSSL::ASN1::UTCTime, validity.value[1].class)
+ assert_equal(now+3600, validity.value[1].value)
+
+ dn = tbs_cert.value[5] # subject
+ assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash)
+ assert_equal(OpenSSL::ASN1::Sequence, dn.class)
+ assert_equal(3, dn.value.size)
+ assert_equal(OpenSSL::ASN1::Set, dn.value[0].class)
+ assert_equal(OpenSSL::ASN1::Set, dn.value[1].class)
+ assert_equal(OpenSSL::ASN1::Set, dn.value[2].class)
+ assert_equal(1, dn.value[0].value.size)
+ assert_equal(1, dn.value[1].value.size)
+ assert_equal(1, dn.value[2].value.size)
+ assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class)
+ assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class)
+ assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class)
+ assert_equal(2, dn.value[0].value[0].value.size)
+ assert_equal(2, dn.value[1].value[0].value.size)
+ assert_equal(2, dn.value[2].value[0].value.size)
+ oid, value = *dn.value[0].value[0].value
+ assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+ assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+ assert_equal(OpenSSL::ASN1::IA5String, value.class)
+ assert_equal("org", value.value)
+ oid, value = *dn.value[1].value[0].value
+ assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+ assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+ assert_equal(OpenSSL::ASN1::IA5String, value.class)
+ assert_equal("ruby-lang", value.value)
+ oid, value = *dn.value[2].value[0].value
+ assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+ assert_equal("2.5.4.3", oid.oid)
+ assert_equal(OpenSSL::ASN1::UTF8String, value.class)
+ assert_equal("TestCA", value.value)
+
+ pkey = tbs_cert.value[6]
+ assert_equal(OpenSSL::ASN1::Sequence, pkey.class)
+ assert_equal(2, pkey.value.size)
+ assert_equal(OpenSSL::ASN1::Sequence, pkey.value[0].class)
+ assert_equal(2, pkey.value[0].value.size)
+ assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class)
+ assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid)
+ assert_equal(OpenSSL::ASN1::BitString, pkey.value[1].class)
+ assert_equal(0, pkey.value[1].unused_bits)
+ spkey = OpenSSL::ASN1.decode(pkey.value[1].value)
+ assert_equal(OpenSSL::ASN1::Sequence, spkey.class)
+ assert_equal(2, spkey.value.size)
+ assert_equal(OpenSSL::ASN1::Integer, spkey.value[0].class)
+ assert_equal(143085709396403084580358323862163416700436550432664688288860593156058579474547937626086626045206357324274536445865308750491138538454154232826011964045825759324933943290377903384882276841880081931690695505836279972214003660451338124170055999155993192881685495391496854691199517389593073052473319331505702779271, spkey.value[0].value)
+ assert_equal(OpenSSL::ASN1::Integer, spkey.value[1].class)
+ assert_equal(65537, spkey.value[1].value)
+
+ extensions = tbs_cert.value[7]
+ assert_equal(:CONTEXT_SPECIFIC, extensions.tag_class)
+ assert_equal(3, extensions.tag)
+ assert_equal(1, extensions.value.size)
+ assert_equal(OpenSSL::ASN1::Sequence, extensions.value[0].class)
+ assert_equal(3, extensions.value[0].value.size)
+
+ ext = extensions.value[0].value[0] # basicConstraints
+ assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+ assert_equal(3, ext.value.size)
+ assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+ assert_equal("2.5.29.19", ext.value[0].oid)
+ assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class)
+ assert_equal(true, ext.value[1].value)
+ assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class)
+ extv = OpenSSL::ASN1.decode(ext.value[2].value)
+ assert_equal(OpenSSL::ASN1::Sequence, extv.class)
+ assert_equal(2, extv.value.size)
+ assert_equal(OpenSSL::ASN1::Boolean, extv.value[0].class)
+ assert_equal(true, extv.value[0].value)
+ assert_equal(OpenSSL::ASN1::Integer, extv.value[1].class)
+ assert_equal(1, extv.value[1].value)
+
+ ext = extensions.value[0].value[1] # keyUsage
+ assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+ assert_equal(3, ext.value.size)
+ assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+ assert_equal("2.5.29.15", ext.value[0].oid)
+ assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class)
+ assert_equal(true, ext.value[1].value)
+ assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class)
+ extv = OpenSSL::ASN1.decode(ext.value[2].value)
+ assert_equal(OpenSSL::ASN1::BitString, extv.class)
+ str = "\000"; str[0] = 0b00000110.chr
+ assert_equal(str, extv.value)
+
+ ext = extensions.value[0].value[2] # subjetKeyIdentifier
+ assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+ assert_equal(2, ext.value.size)
+ assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+ assert_equal("2.5.29.14", ext.value[0].oid)
+ assert_equal(OpenSSL::ASN1::OctetString, ext.value[1].class)
+ extv = OpenSSL::ASN1.decode(ext.value[1].value)
+ assert_equal(OpenSSL::ASN1::OctetString, extv.class)
+ sha1 = OpenSSL::Digest::SHA1.new
+ sha1.update(pkey.value[1].value)
+ assert_equal(sha1.digest, extv.value)
+
+ assert_equal(OpenSSL::ASN1::Sequence, sig_alg.class)
+ assert_equal(2, sig_alg.value.size)
+ assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class)
+ assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid)
+ assert_equal(OpenSSL::ASN1::Null, pkey.value[0].value[1].class)
+
+ assert_equal(OpenSSL::ASN1::BitString, sig_val.class)
+ cululated_sig = key.sign(OpenSSL::Digest::SHA1.new, tbs_cert.to_der)
+ assert_equal(cululated_sig, sig_val.value)
+ end
+
+ def test_encode_boolean
+ encode_decode_test(OpenSSL::ASN1::Boolean, [true, false])
+ end
+
+ def test_encode_integer
+ encode_decode_test(OpenSSL::ASN1::Integer, [72, -127, -128, 128, -1, 0, 1, -(2**12345), 2**12345])
+ end
+
+ def test_encode_nil
+ m = OpenSSL::ASN1
+ [
+ m::Boolean, m::Integer, m::BitString, m::OctetString,
+ m::ObjectId, m::Enumerated, m::UTF8String, m::UTCTime,
+ m::GeneralizedTime, m::Sequence, m::Set
+ ].each do |klass|
+ #Primitives raise TypeError, Constructives NoMethodError
+ assert_raise(TypeError, NoMethodError) { klass.send(:new, nil).to_der }
+ end
+ end
+
+ def encode_decode_test(type, values)
+ values.each do |v|
+ assert_equal(v, OpenSSL::ASN1.decode(type.new(v).to_der).value)
+ end
+ end
+
+ def test_decode_pem #should fail gracefully (cf. [ruby-dev:44542])
+ pem = <<-_EOS_
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdugAwIBAgIBATANBgkqhkiG9w0BAQUFADA9MRMwEQYKCZImiZPyLGQB
+GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
+Fw0xMTA5MjUxMzQ4MjZaFw0xMTA5MjUxNDQ4MjZaMD0xEzARBgoJkiaJk/IsZAEZ
+FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMMAkNBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuV9ht9J7k4NBs38jOXvvTKY9
+gW8nLICSno5EETR1cuF7i4pNs9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enen
+fzq/t/e/1IRW0wkJUJUFQign4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWm
+qbjs07JbuS4QQGGXLc+Su96DkYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v6
+8JkRFIhdGlb6JL8fllf/A/blNwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX
+9KZYcU00mOX+fdxOSnGqS/8JDRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wID
+AQABMA0GCSqGSIb3DQEBBQUAA4IBAQAiAtrIr1pLX4GYN5klviWKb8HC9ICYuAFI
+NfE3FwqzErEVXotuMe3yPVyB3Bv6rjYY/x5EtS5+WPTbHlvHZTkfcsnTpizcn4mW
+dJ6dDRaFCHt1YKKjUxqBt9lvvrc3nReYZN/P+s1mrDhWzGf8iPZgf8sFUHgnaK7W
+CXRVXmPFgCDRNpDDVQ0MQkr509yYfTH+dujNzqTCwSvkyZFyQ7Oe8Yj0VR6kquG3
+rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm
+/93PnPG1IvPjYNd5VlV+sXSnaxQn974HRCsMv7jA8BD6IgSaX6WK
+-----END CERTIFICATE-----
+ _EOS_
+ assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode(pem) }
+ assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode_all(pem) }
+ end
+
+ def test_primitive_cannot_set_infinite_length
+ begin
+ prim = OpenSSL::ASN1::Integer.new(50)
+ assert_equal(false, prim.infinite_length)
+ prim.infinite_length = true
+ flunk('Could set infinite length on primitive value')
+ rescue NoMethodError
+ #ok
+ end
+ end
+
+ def test_decode_all
+ expected = %w{ 02 01 01 02 01 02 02 01 03 }
+ raw = [expected.join('')].pack('H*')
+ ary = OpenSSL::ASN1.decode_all(raw)
+ assert_equal(3, ary.size)
+ ary.each_with_index do |asn1, i|
+ assert_universal(OpenSSL::ASN1::INTEGER, asn1)
+ assert_equal(i + 1, asn1.value)
+ end
+ end
+
+ def test_decode_utctime
+ expected = Time.at 1374535380
+ assert_equal expected, OpenSSL::ASN1.decode("\x17\v1307222323Z").value
+
+ expected += 17
+ assert_equal expected, OpenSSL::ASN1.decode("\x17\r130722232317Z").value
+ end
+
+ def test_create_inf_length_primitive
+ expected = %w{ 24 80 04 01 61 00 00 }
+ raw = [expected.join('')].pack('H*')
+ val = OpenSSL::ASN1::OctetString.new('a')
+ cons = OpenSSL::ASN1::Constructive.new([val,
+ OpenSSL::ASN1::EndOfContent.new],
+ OpenSSL::ASN1::OCTET_STRING,
+ nil,
+ :UNIVERSAL)
+ cons.infinite_length = true
+ assert_equal(nil, cons.tagging)
+ assert_equal(raw, cons.to_der)
+ asn1 = OpenSSL::ASN1.decode(raw)
+ assert(asn1.infinite_length)
+ assert_equal(raw, asn1.to_der)
+ end
+
+ def test_cons_without_inf_length_forbidden
+ assert_raise(OpenSSL::ASN1::ASN1Error) do
+ val = OpenSSL::ASN1::OctetString.new('a')
+ cons = OpenSSL::ASN1::Constructive.new([val],
+ OpenSSL::ASN1::OCTET_STRING,
+ nil,
+ :UNIVERSAL)
+ cons.to_der
+ end
+ end
+
+ def test_cons_without_array_forbidden
+ assert_raise(OpenSSL::ASN1::ASN1Error) do
+ val = OpenSSL::ASN1::OctetString.new('a')
+ cons = OpenSSL::ASN1::Constructive.new(val,
+ OpenSSL::ASN1::OCTET_STRING,
+ nil,
+ :UNIVERSAL)
+ cons.infinite_length = true
+ cons.to_der
+ end
+ end
+
+ def test_parse_empty_sequence
+ expected = %w{ A0 07 30 02 30 00 02 01 00 }
+ raw = [expected.join('')].pack('H*')
+ asn1 = OpenSSL::ASN1.decode(raw)
+ assert_equal(raw, asn1.to_der)
+ assert_equal(2, asn1.value.size)
+ seq = asn1.value[0]
+ assert_equal(1, seq.value.size)
+ inner_seq = seq.value[0]
+ assert_equal(0, inner_seq.value.size)
+ end
+
+ def test_parse_tagged_0_infinite
+ expected = %w{ 30 80 02 01 01 80 01 02 00 00 }
+ raw = [expected.join('')].pack('H*')
+ asn1 = OpenSSL::ASN1.decode(raw)
+ assert_equal(3, asn1.value.size)
+ int = asn1.value[0]
+ assert_universal(OpenSSL::ASN1::INTEGER, int)
+ tagged = asn1.value[1]
+ assert_equal(0, tagged.tag)
+ assert_universal(OpenSSL::ASN1::EOC, asn1.value[2])
+ assert_equal(raw, asn1.to_der)
+ end
+
+ def test_seq_infinite_length
+ begin
+ content = [ OpenSSL::ASN1::Null.new(nil),
+ OpenSSL::ASN1::EndOfContent.new ]
+ cons = OpenSSL::ASN1::Sequence.new(content)
+ cons.infinite_length = true
+ expected = %w{ 30 80 05 00 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, cons.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_set_infinite_length
+ begin
+ content = [ OpenSSL::ASN1::Null.new(nil),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ cons = OpenSSL::ASN1::Set.new(content)
+ cons.infinite_length = true
+ expected = %w{ 31 80 05 00 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, cons.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_octet_string_infinite_length
+ begin
+ octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ cons = OpenSSL::ASN1::Constructive.new(
+ octets,
+ OpenSSL::ASN1::OCTET_STRING,
+ nil,
+ :UNIVERSAL)
+ cons.infinite_length = true
+ expected = %w{ 24 80 04 03 61 61 61 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, cons.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_prim_explicit_tagging
+ begin
+ oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT)
+ expected = %w{ A0 03 04 01 61 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, oct_str.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_prim_explicit_tagging_tag_class
+ begin
+ oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT)
+ oct_str2 = OpenSSL::ASN1::OctetString.new(
+ "a",
+ 0,
+ :EXPLICIT,
+ :CONTEXT_SPECIFIC)
+ assert_equal(oct_str.to_der, oct_str2.to_der)
+ end
+ end
+
+ def test_prim_implicit_tagging
+ begin
+ int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT)
+ expected = %w{ 80 01 01 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, int.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_prim_implicit_tagging_tag_class
+ begin
+ int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT)
+ int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC);
+ assert_equal(int.to_der, int2.to_der)
+ end
+ end
+
+ def test_cons_explicit_tagging
+ begin
+ content = [ OpenSSL::ASN1::PrintableString.new('abc') ]
+ seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT)
+ expected = %w{ A2 07 30 05 13 03 61 62 63 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, seq.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_cons_explicit_tagging_inf_length
+ begin
+ content = [ OpenSSL::ASN1::PrintableString.new('abc') ,
+ OpenSSL::ASN1::EndOfContent.new() ]
+ seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT)
+ seq.infinite_length = true
+ expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, seq.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_cons_implicit_tagging
+ begin
+ content = [ OpenSSL::ASN1::Null.new(nil) ]
+ seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT)
+ expected = %w{ A1 02 05 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, seq.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_cons_implicit_tagging_inf_length
+ begin
+ content = [ OpenSSL::ASN1::Null.new(nil),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT)
+ seq.infinite_length = true
+ expected = %w{ A1 80 05 00 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, seq.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_octet_string_infinite_length_explicit_tagging
+ begin
+ octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ cons = OpenSSL::ASN1::Constructive.new(
+ octets,
+ 1,
+ :EXPLICIT)
+ cons.infinite_length = true
+ expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, cons.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_octet_string_infinite_length_implicit_tagging
+ begin
+ octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ cons = OpenSSL::ASN1::Constructive.new(
+ octets,
+ 0,
+ :IMPLICIT)
+ cons.infinite_length = true
+ expected = %w{ A0 80 04 03 61 61 61 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, cons.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_recursive_octet_string_infinite_length
+ begin
+ octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ container1 = OpenSSL::ASN1::Constructive.new(
+ octets_sub1,
+ OpenSSL::ASN1::OCTET_STRING,
+ nil,
+ :UNIVERSAL)
+ container1.infinite_length = true
+ container2 = OpenSSL::ASN1::Constructive.new(
+ octets_sub2,
+ OpenSSL::ASN1::OCTET_STRING,
+ nil,
+ :UNIVERSAL)
+ container2.infinite_length = true
+ octets3 = OpenSSL::ASN1::OctetString.new("\x03")
+
+ octets = [ container1, container2, octets3,
+ OpenSSL::ASN1::EndOfContent.new() ]
+ cons = OpenSSL::ASN1::Constructive.new(
+ octets,
+ OpenSSL::ASN1::OCTET_STRING,
+ nil,
+ :UNIVERSAL)
+ cons.infinite_length = true
+ expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, cons.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_bit_string_infinite_length
+ begin
+ content = [ OpenSSL::ASN1::BitString.new("\x01"),
+ OpenSSL::ASN1::EndOfContent.new() ]
+ cons = OpenSSL::ASN1::Constructive.new(
+ content,
+ OpenSSL::ASN1::BIT_STRING,
+ nil,
+ :UNIVERSAL)
+ cons.infinite_length = true
+ expected = %w{ 23 80 03 02 00 01 00 00 }
+ raw = [expected.join('')].pack('H*')
+ assert_equal(raw, cons.to_der)
+ assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+ end
+ end
+
+ def test_primitive_inf_length
+ assert_raises(OpenSSL::ASN1::ASN1Error) do
+ spec = %w{ 02 80 02 01 01 00 00 }
+ raw = [spec.join('')].pack('H*')
+ OpenSSL::ASN1.decode(raw)
+ OpenSSL::ASN1.decode_all(raw)
+ end
+ end
+
+ def test_recursive_octet_string_parse
+ test = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }
+ raw = [test.join('')].pack('H*')
+ asn1 = OpenSSL::ASN1.decode(raw)
+ assert_equal(OpenSSL::ASN1::Constructive, asn1.class)
+ assert_universal(OpenSSL::ASN1::OCTET_STRING, asn1)
+ assert_equal(true, asn1.infinite_length)
+ assert_equal(4, asn1.value.size)
+ nested1 = asn1.value[0]
+ assert_equal(OpenSSL::ASN1::Constructive, nested1.class)
+ assert_universal(OpenSSL::ASN1::OCTET_STRING, nested1)
+ assert_equal(true, nested1.infinite_length)
+ assert_equal(2, nested1.value.size)
+ oct1 = nested1.value[0]
+ assert_universal(OpenSSL::ASN1::OCTET_STRING, oct1)
+ assert_equal(false, oct1.infinite_length)
+ assert_universal(OpenSSL::ASN1::EOC, nested1.value[1])
+ assert_equal(false, nested1.value[1].infinite_length)
+ nested2 = asn1.value[1]
+ assert_equal(OpenSSL::ASN1::Constructive, nested2.class)
+ assert_universal(OpenSSL::ASN1::OCTET_STRING, nested2)
+ assert_equal(true, nested2.infinite_length)
+ assert_equal(2, nested2.value.size)
+ oct2 = nested2.value[0]
+ assert_universal(OpenSSL::ASN1::OCTET_STRING, oct2)
+ assert_equal(false, oct2.infinite_length)
+ assert_universal(OpenSSL::ASN1::EOC, nested2.value[1])
+ assert_equal(false, nested2.value[1].infinite_length)
+ oct3 = asn1.value[2]
+ assert_universal(OpenSSL::ASN1::OCTET_STRING, oct3)
+ assert_equal(false, oct3.infinite_length)
+ assert_universal(OpenSSL::ASN1::EOC, asn1.value[3])
+ assert_equal(false, asn1.value[3].infinite_length)
+ end
+
+ private
+
+ def assert_universal(tag, asn1)
+ assert_equal(tag, asn1.tag)
+ if asn1.respond_to?(:tagging)
+ assert_nil(asn1.tagging)
+ end
+ assert_equal(:UNIVERSAL, asn1.tag_class)
+ end
+
+end if defined?(OpenSSL::TestUtils)
+
diff --git a/jni/ruby/test/openssl/test_bn.rb b/jni/ruby/test/openssl/test_bn.rb
new file mode 100644
index 0000000..667cb29
--- /dev/null
+++ b/jni/ruby/test/openssl/test_bn.rb
@@ -0,0 +1,52 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestBN < Test::Unit::TestCase
+ def test_new_str
+ e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable
+ e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
+ assert_equal(e1, OpenSSL::BN.new("999"))
+ assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s))
+ assert_equal(e1, OpenSSL::BN.new("999", 10))
+ assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s, 10))
+ assert_equal(e1, OpenSSL::BN.new("\x03\xE7", 2))
+ assert_equal(e2, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2))
+ assert_equal(e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0))
+ assert_equal(e2, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0))
+ end
+
+ def test_new_bn
+ e1 = OpenSSL::BN.new(999.to_s(16), 16)
+ e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
+ assert_equal(e1, OpenSSL::BN.new(e1))
+ assert_equal(e2, OpenSSL::BN.new(e2))
+ end
+
+ def test_new_integer
+ assert_equal(999.to_bn, OpenSSL::BN.new(999))
+ assert_equal((2 ** 107 - 1).to_bn, OpenSSL::BN.new(2 ** 107 - 1))
+ assert_equal(-999.to_bn, OpenSSL::BN.new(-999))
+ assert_equal((-(2 ** 107 - 1)).to_bn, OpenSSL::BN.new(-(2 ** 107 - 1)))
+ end
+
+ def test_to_bn
+ e1 = OpenSSL::BN.new(999.to_s(16), 16)
+ e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
+ assert_equal(e1, 999.to_bn)
+ assert_equal(e2, (2**107-1).to_bn)
+ end
+
+ def test_prime_p
+ assert_equal(true, OpenSSL::BN.new((2 ** 107 - 1).to_s(16), 16).prime?)
+ assert_equal(true, OpenSSL::BN.new((2 ** 127 - 1).to_s(16), 16).prime?(1))
+ end
+
+ def test_cmp_nil
+ bn = OpenSSL::BN.new('1')
+ assert_equal(false, bn == nil)
+ assert_equal(true, bn != nil)
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_buffering.rb b/jni/ruby/test/openssl/test_buffering.rb
new file mode 100644
index 0000000..c62dd4d
--- /dev/null
+++ b/jni/ruby/test/openssl/test_buffering.rb
@@ -0,0 +1,87 @@
+require_relative 'utils'
+require 'stringio'
+
+class OpenSSL::TestBuffering < Test::Unit::TestCase
+
+ class IO
+ include OpenSSL::Buffering
+
+ attr_accessor :sync
+
+ def initialize
+ @io = ""
+ def @io.sync
+ true
+ end
+
+ super
+
+ @sync = false
+ end
+
+ def string
+ @io
+ end
+
+ def sysread(size)
+ str = @io.slice!(0, size)
+ raise EOFError if str.empty?
+ str
+ end
+
+ def syswrite(str)
+ @io << str
+ str.size
+ end
+ end
+
+ def setup
+ @io = IO.new
+ end
+
+ def test_flush
+ @io.write 'a'
+
+ refute @io.sync
+ assert_empty @io.string
+
+ assert_equal @io, @io.flush
+
+ refute @io.sync
+ assert_equal 'a', @io.string
+ end
+
+ def test_flush_error
+ @io.write 'a'
+
+ refute @io.sync
+ assert_empty @io.string
+
+ def @io.syswrite *a
+ raise SystemCallError, 'fail'
+ end
+
+ assert_raises SystemCallError do
+ @io.flush
+ end
+
+ refute @io.sync, 'sync must not change'
+ end
+
+ def test_getc
+ @io.syswrite('abc')
+ assert_equal(?a, @io.getc)
+ assert_equal(?b, @io.getc)
+ assert_equal(?c, @io.getc)
+ end
+
+ def test_each_byte
+ @io.syswrite('abc')
+ res = []
+ @io.each_byte do |c|
+ res << c
+ end
+ assert_equal([97, 98, 99], res)
+ end
+
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/openssl/test_cipher.rb b/jni/ruby/test/openssl/test_cipher.rb
new file mode 100644
index 0000000..6f92c38
--- /dev/null
+++ b/jni/ruby/test/openssl/test_cipher.rb
@@ -0,0 +1,260 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestCipher < Test::Unit::TestCase
+
+ class << self
+
+ def has_cipher?(name)
+ ciphers = OpenSSL::Cipher.ciphers
+ # redefine method so we can use the cached ciphers value from the closure
+ # and need not recompute the list each time
+ define_singleton_method :has_cipher? do |name|
+ ciphers.include?(name)
+ end
+ has_cipher?(name)
+ end
+
+ def has_ciphers?(list)
+ list.all? { |name| has_cipher?(name) }
+ end
+
+ end
+
+ def setup
+ @c1 = OpenSSL::Cipher::Cipher.new("DES-EDE3-CBC")
+ @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC")
+ @key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ @iv = "\0\0\0\0\0\0\0\0"
+ @hexkey = "0000000000000000000000000000000000000000000000"
+ @hexiv = "0000000000000000"
+ @data = "DATA"
+ end
+
+ def teardown
+ @c1 = @c2 = nil
+ end
+
+ def test_crypt
+ @c1.encrypt.pkcs5_keyivgen(@key, @iv)
+ @c2.encrypt.pkcs5_keyivgen(@key, @iv)
+ s1 = @c1.update(@data) + @c1.final
+ s2 = @c2.update(@data) + @c2.final
+ assert_equal(s1, s2, "encrypt")
+
+ @c1.decrypt.pkcs5_keyivgen(@key, @iv)
+ @c2.decrypt.pkcs5_keyivgen(@key, @iv)
+ assert_equal(@data, @c1.update(s1)+@c1.final, "decrypt")
+ assert_equal(@data, @c2.update(s2)+@c2.final, "decrypt")
+ end
+
+ def test_info
+ assert_equal("DES-EDE3-CBC", @c1.name, "name")
+ assert_equal("DES-EDE3-CBC", @c2.name, "name")
+ assert_kind_of(Fixnum, @c1.key_len, "key_len")
+ assert_kind_of(Fixnum, @c1.iv_len, "iv_len")
+ end
+
+ def test_dup
+ assert_equal(@c1.name, @c1.dup.name, "dup")
+ assert_equal(@c1.name, @c1.clone.name, "clone")
+ @c1.encrypt
+ @c1.key = @key
+ @c1.iv = @iv
+ tmpc = @c1.dup
+ s1 = @c1.update(@data) + @c1.final
+ s2 = tmpc.update(@data) + tmpc.final
+ assert_equal(s1, s2, "encrypt dup")
+ end
+
+ def test_reset
+ @c1.encrypt
+ @c1.key = @key
+ @c1.iv = @iv
+ s1 = @c1.update(@data) + @c1.final
+ @c1.reset
+ s2 = @c1.update(@data) + @c1.final
+ assert_equal(s1, s2, "encrypt reset")
+ end
+
+ def test_empty_data
+ @c1.encrypt
+ assert_raise(ArgumentError){ @c1.update("") }
+ end
+
+ def test_initialize
+ assert_raise(RuntimeError) {@c1.__send__(:initialize, "DES-EDE3-CBC")}
+ assert_raise(RuntimeError) {OpenSSL::Cipher.allocate.final}
+ end
+
+ def test_ctr_if_exists
+ begin
+ cipher = OpenSSL::Cipher.new('aes-128-ctr')
+ cipher.encrypt
+ cipher.pkcs5_keyivgen('password')
+ c = cipher.update('hello,world') + cipher.final
+ cipher.decrypt
+ cipher.pkcs5_keyivgen('password')
+ assert_equal('hello,world', cipher.update(c) + cipher.final)
+ end
+ end if has_cipher?('aes-128-ctr')
+
+ if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00907000
+ def test_ciphers
+ OpenSSL::Cipher.ciphers.each{|name|
+ next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name
+ begin
+ assert_kind_of(OpenSSL::Cipher::Cipher, OpenSSL::Cipher::Cipher.new(name))
+ rescue OpenSSL::Cipher::CipherError => e
+ next if /wrap/ =~ name and e.message == 'wrap mode not allowed'
+ raise
+ end
+ }
+ end
+
+ def test_AES
+ pt = File.read(__FILE__)
+ %w(ECB CBC CFB OFB).each{|mode|
+ c1 = OpenSSL::Cipher::AES256.new(mode)
+ c1.encrypt
+ c1.pkcs5_keyivgen("passwd")
+ ct = c1.update(pt) + c1.final
+
+ c2 = OpenSSL::Cipher::AES256.new(mode)
+ c2.decrypt
+ c2.pkcs5_keyivgen("passwd")
+ assert_equal(pt, c2.update(ct) + c2.final)
+ }
+ end
+
+ def test_AES_crush
+ 500.times do
+ assert_nothing_raised("[Bug #2768]") do
+ # it caused OpenSSL SEGV by uninitialized key
+ OpenSSL::Cipher::AES128.new("ECB").update "." * 17
+ end
+ end
+ end
+ end
+
+ if has_ciphers?(['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])
+
+ def test_authenticated
+ cipher = OpenSSL::Cipher.new('aes-128-gcm')
+ assert(cipher.authenticated?)
+ cipher = OpenSSL::Cipher.new('aes-128-cbc')
+ refute(cipher.authenticated?)
+ end
+
+ def test_aes_gcm
+ ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo|
+ pt = "You should all use Authenticated Encryption!"
+ cipher, key, iv = new_encryptor(algo)
+
+ cipher.auth_data = "aad"
+ ct = cipher.update(pt) + cipher.final
+ tag = cipher.auth_tag
+ assert_equal(16, tag.size)
+
+ decipher = new_decryptor(algo, key, iv)
+ decipher.auth_tag = tag
+ decipher.auth_data = "aad"
+
+ assert_equal(pt, decipher.update(ct) + decipher.final)
+ end
+ end
+
+ def test_aes_gcm_short_tag
+ ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo|
+ pt = "You should all use Authenticated Encryption!"
+ cipher, key, iv = new_encryptor(algo)
+
+ cipher.auth_data = "aad"
+ ct = cipher.update(pt) + cipher.final
+ tag = cipher.auth_tag(8)
+ assert_equal(8, tag.size)
+
+ decipher = new_decryptor(algo, key, iv)
+ decipher.auth_tag = tag
+ decipher.auth_data = "aad"
+
+ assert_equal(pt, decipher.update(ct) + decipher.final)
+ end
+ end
+
+ def test_aes_gcm_wrong_tag
+ pt = "You should all use Authenticated Encryption!"
+ cipher, key, iv = new_encryptor('aes-128-gcm')
+
+ cipher.auth_data = "aad"
+ ct = cipher.update(pt) + cipher.final
+ tag = cipher.auth_tag
+
+ decipher = new_decryptor('aes-128-gcm', key, iv)
+ tag.setbyte(-1, (tag.getbyte(-1) + 1) & 0xff)
+ decipher.auth_tag = tag
+ decipher.auth_data = "aad"
+
+ assert_raise OpenSSL::Cipher::CipherError do
+ decipher.update(ct) + decipher.final
+ end
+ end
+
+ def test_aes_gcm_wrong_auth_data
+ pt = "You should all use Authenticated Encryption!"
+ cipher, key, iv = new_encryptor('aes-128-gcm')
+
+ cipher.auth_data = "aad"
+ ct = cipher.update(pt) + cipher.final
+ tag = cipher.auth_tag
+
+ decipher = new_decryptor('aes-128-gcm', key, iv)
+ decipher.auth_tag = tag
+ decipher.auth_data = "daa"
+
+ assert_raise OpenSSL::Cipher::CipherError do
+ decipher.update(ct) + decipher.final
+ end
+ end
+
+ def test_aes_gcm_wrong_ciphertext
+ pt = "You should all use Authenticated Encryption!"
+ cipher, key, iv = new_encryptor('aes-128-gcm')
+
+ cipher.auth_data = "aad"
+ ct = cipher.update(pt) + cipher.final
+ tag = cipher.auth_tag
+
+ decipher = new_decryptor('aes-128-gcm', key, iv)
+ decipher.auth_tag = tag
+ decipher.auth_data = "aad"
+
+ assert_raise OpenSSL::Cipher::CipherError do
+ decipher.update(ct[0..-2] << ct[-1].succ) + decipher.final
+ end
+ end
+
+ end
+
+ private
+
+ def new_encryptor(algo)
+ cipher = OpenSSL::Cipher.new(algo)
+ cipher.encrypt
+ key = cipher.random_key
+ iv = cipher.random_iv
+ [cipher, key, iv]
+ end
+
+ def new_decryptor(algo, key, iv)
+ OpenSSL::Cipher.new(algo).tap do |cipher|
+ cipher.decrypt
+ cipher.key = key
+ cipher.iv = iv
+ end
+ end
+
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_config.rb b/jni/ruby/test/openssl/test_config.rb
new file mode 100644
index 0000000..62f9fab
--- /dev/null
+++ b/jni/ruby/test/openssl/test_config.rb
@@ -0,0 +1,297 @@
+require_relative 'utils'
+
+class OpenSSL::TestConfig < Test::Unit::TestCase
+ def setup
+ file = Tempfile.open("openssl.cnf")
+ file << <<__EOD__
+HOME = .
+[ ca ]
+default_ca = CA_default
+[ CA_default ]
+dir = ./demoCA
+certs = ./certs
+__EOD__
+ file.close
+ @tmpfile = file
+ @it = OpenSSL::Config.new(file.path)
+ end
+
+ def teardown
+ @tmpfile.close!
+ end
+
+ def test_constants
+ assert(defined?(OpenSSL::Config::DEFAULT_CONFIG_FILE))
+ config_file = OpenSSL::Config::DEFAULT_CONFIG_FILE
+ skip "DEFAULT_CONFIG_FILE may return a wrong path on your platforms. [Bug #6830]" unless File.readable?(config_file)
+ assert_nothing_raised do
+ OpenSSL::Config.load(config_file)
+ end
+ end
+
+ def test_s_parse
+ c = OpenSSL::Config.parse('')
+ assert_equal("[ default ]\n\n", c.to_s)
+ c = OpenSSL::Config.parse(@it.to_s)
+ assert_equal(['CA_default', 'ca', 'default'], c.sections.sort)
+ end
+
+ def test_s_parse_format
+ c = OpenSSL::Config.parse(<<__EOC__)
+ baz =qx\t # "baz = qx"
+
+foo::bar = baz # shortcut section::key format
+ default::bar = baz # ditto
+a=\t \t # "a = ": trailing spaces are ignored
+ =b # " = b": empty key
+ =c # " = c": empty key (override the above line)
+ d= # "c = ": trailing comment is ignored
+
+sq = 'foo''b\\'ar'
+ dq ="foo""''\\""
+ dq2 = foo""bar
+esc=a\\r\\n\\b\\tb
+foo\\bar = foo\\b\\\\ar
+foo\\bar::foo\\bar = baz
+[default1 default2]\t\t # space is allowed in section name
+ fo =b ar # space allowed in value
+[emptysection]
+ [doller ]
+foo=bar
+bar = $(foo)
+baz = 123$(default::bar)456${foo}798
+qux = ${baz}
+quxx = $qux.$qux
+__EOC__
+ assert_equal(['default', 'default1 default2', 'doller', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort)
+ assert_equal(['', 'a', 'bar', 'baz', 'd', 'dq', 'dq2', 'esc', 'foo\\bar', 'sq'], c['default'].keys.sort)
+ assert_equal('c', c['default'][''])
+ assert_equal('', c['default']['a'])
+ assert_equal('qx', c['default']['baz'])
+ assert_equal('', c['default']['d'])
+ assert_equal('baz', c['default']['bar'])
+ assert_equal("foob'ar", c['default']['sq'])
+ assert_equal("foo''\"", c['default']['dq'])
+ assert_equal("foobar", c['default']['dq2'])
+ assert_equal("a\r\n\b\tb", c['default']['esc'])
+ assert_equal("foo\b\\ar", c['default']['foo\\bar'])
+ assert_equal('baz', c['foo']['bar'])
+ assert_equal('baz', c['foo\\bar']['foo\\bar'])
+ assert_equal('b ar', c['default1 default2']['fo'])
+
+ # dolloer
+ assert_equal('bar', c['doller']['foo'])
+ assert_equal('bar', c['doller']['bar'])
+ assert_equal('123baz456bar798', c['doller']['baz'])
+ assert_equal('123baz456bar798', c['doller']['qux'])
+ assert_equal('123baz456bar798.123baz456bar798', c['doller']['quxx'])
+
+ excn = assert_raise(OpenSSL::ConfigError) do
+ OpenSSL::Config.parse("foo = $bar")
+ end
+ assert_equal("error in line 1: variable has no value", excn.message)
+
+ excn = assert_raise(OpenSSL::ConfigError) do
+ OpenSSL::Config.parse("foo = $(bar")
+ end
+ assert_equal("error in line 1: no close brace", excn.message)
+
+ excn = assert_raise(OpenSSL::ConfigError) do
+ OpenSSL::Config.parse("f o =b ar # no space in key")
+ end
+ assert_equal("error in line 1: missing equal sign", excn.message)
+
+ excn = assert_raise(OpenSSL::ConfigError) do
+ OpenSSL::Config.parse(<<__EOC__)
+# comment 1 # comments
+
+#
+ # comment 2
+\t#comment 3
+ [second ]\t
+[third # section not terminated
+__EOC__
+ end
+ assert_equal("error in line 7: missing close square bracket", excn.message)
+ end
+
+ def test_s_load
+ # alias of new
+ c = OpenSSL::Config.load
+ assert_equal("", c.to_s)
+ assert_equal([], c.sections)
+ #
+ Tempfile.create("openssl.cnf") {|file|
+ file.close
+ c = OpenSSL::Config.load(file.path)
+ assert_equal("[ default ]\n\n", c.to_s)
+ assert_equal(['default'], c.sections)
+ }
+ end
+
+ def test_initialize
+ c = OpenSSL::Config.new
+ assert_equal("", c.to_s)
+ assert_equal([], c.sections)
+ end
+
+ def test_initialize_with_empty_file
+ Tempfile.create("openssl.cnf") {|file|
+ file.close
+ c = OpenSSL::Config.new(file.path)
+ assert_equal("[ default ]\n\n", c.to_s)
+ assert_equal(['default'], c.sections)
+ }
+ end
+
+ def test_initialize_with_example_file
+ assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort)
+ end
+
+ def test_get_value
+ assert_equal('CA_default', @it.get_value('ca', 'default_ca'))
+ assert_equal(nil, @it.get_value('ca', 'no such key'))
+ assert_equal(nil, @it.get_value('no such section', 'no such key'))
+ assert_equal('.', @it.get_value('', 'HOME'))
+ assert_raise(TypeError) do
+ @it.get_value(nil, 'HOME') # not allowed unlike Config#value
+ end
+ # fallback to 'default' ugly...
+ assert_equal('.', @it.get_value('unknown', 'HOME'))
+ end
+
+ def test_get_value_ENV
+ key = ENV.keys.first
+ assert_not_nil(key) # make sure we have at least one ENV var.
+ assert_equal(ENV[key], @it.get_value('ENV', key))
+ end
+
+ def test_value
+ # supress deprecation warnings
+ OpenSSL::TestUtils.silent do
+ assert_equal('CA_default', @it.value('ca', 'default_ca'))
+ assert_equal(nil, @it.value('ca', 'no such key'))
+ assert_equal(nil, @it.value('no such section', 'no such key'))
+ assert_equal('.', @it.value('', 'HOME'))
+ assert_equal('.', @it.value(nil, 'HOME'))
+ assert_equal('.', @it.value('HOME'))
+ # fallback to 'default' ugly...
+ assert_equal('.', @it.value('unknown', 'HOME'))
+ end
+ end
+
+ def test_value_ENV
+ OpenSSL::TestUtils.silent do
+ key = ENV.keys.first
+ assert_not_nil(key) # make sure we have at least one ENV var.
+ assert_equal(ENV[key], @it.value('ENV', key))
+ end
+ end
+
+ def test_aref
+ assert_equal({'HOME' => '.'}, @it['default'])
+ assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it['CA_default'])
+ assert_equal({}, @it['no_such_section'])
+ assert_equal({}, @it[''])
+ end
+
+ def test_section
+ OpenSSL::TestUtils.silent do
+ assert_equal({'HOME' => '.'}, @it.section('default'))
+ assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it.section('CA_default'))
+ assert_equal({}, @it.section('no_such_section'))
+ assert_equal({}, @it.section(''))
+ end
+ end
+
+ def test_sections
+ assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort)
+ @it['new_section'] = {'foo' => 'bar'}
+ assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort)
+ @it['new_section'] = {}
+ assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort)
+ end
+
+ def test_add_value
+ c = OpenSSL::Config.new
+ assert_equal("", c.to_s)
+ # add key
+ c.add_value('default', 'foo', 'bar')
+ assert_equal("[ default ]\nfoo=bar\n\n", c.to_s)
+ # add another key
+ c.add_value('default', 'baz', 'qux')
+ assert_equal('bar', c['default']['foo'])
+ assert_equal('qux', c['default']['baz'])
+ # update the value
+ c.add_value('default', 'baz', 'quxxx')
+ assert_equal('bar', c['default']['foo'])
+ assert_equal('quxxx', c['default']['baz'])
+ # add section and key
+ c.add_value('section', 'foo', 'bar')
+ assert_equal('bar', c['default']['foo'])
+ assert_equal('quxxx', c['default']['baz'])
+ assert_equal('bar', c['section']['foo'])
+ end
+
+ def test_aset
+ @it['foo'] = {'bar' => 'baz'}
+ assert_equal({'bar' => 'baz'}, @it['foo'])
+ @it['foo'] = {'bar' => 'qux', 'baz' => 'quxx'}
+ assert_equal({'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
+
+ # OpenSSL::Config is add only for now.
+ @it['foo'] = {'foo' => 'foo'}
+ assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
+ # you cannot override or remove any section and key.
+ @it['foo'] = {}
+ assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
+ end
+
+ def test_each
+ # each returns [section, key, value] array.
+ ary = @it.map { |e| e }.sort { |a, b| a[0] <=> b[0] }
+ assert_equal(4, ary.size)
+ assert_equal('CA_default', ary[0][0])
+ assert_equal('CA_default', ary[1][0])
+ assert_equal(["ca", "default_ca", "CA_default"], ary[2])
+ assert_equal(["default", "HOME", "."], ary[3])
+ end
+
+ def test_to_s
+ c = OpenSSL::Config.parse("[empty]\n")
+ assert_equal("[ default ]\n\n[ empty ]\n\n", c.to_s)
+ end
+
+ def test_inspect
+ assert_match(/#<OpenSSL::Config sections=\[.*\]>/, @it.inspect)
+ end
+
+ def test_freeze
+ c = OpenSSL::Config.new
+ c['foo'] = [['key', 'value']]
+ c.freeze
+
+ bug = '[ruby-core:18377]'
+ # RuntimeError for 1.9, TypeError for 1.8
+ e = assert_raise(TypeError, bug) do
+ c['foo'] = [['key', 'wrong']]
+ end
+ assert_match(/can't modify/, e.message, bug)
+ end
+
+ def test_dup
+ assert(!@it.sections.empty?)
+ c = @it.dup
+ assert_equal(@it.sections.sort, c.sections.sort)
+ @it['newsection'] = {'a' => 'b'}
+ assert_not_equal(@it.sections.sort, c.sections.sort)
+ end
+
+ def test_clone
+ assert(!@it.sections.empty?)
+ c = @it.clone
+ assert_equal(@it.sections.sort, c.sections.sort)
+ @it['newsection'] = {'a' => 'b'}
+ assert_not_equal(@it.sections.sort, c.sections.sort)
+ end
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/openssl/test_digest.rb b/jni/ruby/test/openssl/test_digest.rb
new file mode 100644
index 0000000..a23b2ef
--- /dev/null
+++ b/jni/ruby/test/openssl/test_digest.rb
@@ -0,0 +1,126 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestDigest < Test::Unit::TestCase
+ def setup
+ @d1 = OpenSSL::Digest.new("MD5")
+ @d2 = OpenSSL::Digest::MD5.new
+ @md = Digest::MD5.new
+ @data = "DATA"
+ end
+
+ def teardown
+ @d1 = @d2 = @md = nil
+ end
+
+ def test_digest
+ assert_equal(@md.digest, @d1.digest)
+ assert_equal(@md.hexdigest, @d1.hexdigest)
+ @d1 << @data
+ @d2 << @data
+ @md << @data
+ assert_equal(@md.digest, @d1.digest)
+ assert_equal(@md.hexdigest, @d1.hexdigest)
+ assert_equal(@d1.digest, @d2.digest)
+ assert_equal(@d1.hexdigest, @d2.hexdigest)
+ assert_equal(@md.digest, OpenSSL::Digest::MD5.digest(@data))
+ assert_equal(@md.hexdigest, OpenSSL::Digest::MD5.hexdigest(@data))
+ end
+
+ def test_eql
+ assert(@d1 == @d2, "==")
+ d = @d1.clone
+ assert(d == @d1, "clone")
+ end
+
+ def test_info
+ assert_equal("MD5", @d1.name, "name")
+ assert_equal("MD5", @d2.name, "name")
+ assert_equal(16, @d1.size, "size")
+ end
+
+ def test_dup
+ @d1.update(@data)
+ assert_equal(@d1.name, @d1.dup.name, "dup")
+ assert_equal(@d1.name, @d1.clone.name, "clone")
+ assert_equal(@d1.digest, @d1.clone.digest, "clone .digest")
+ end
+
+ def test_reset
+ @d1.update(@data)
+ dig1 = @d1.digest
+ @d1.reset
+ @d1.update(@data)
+ dig2 = @d1.digest
+ assert_equal(dig1, dig2, "reset")
+ end
+
+ def test_digest_constants
+ algs = %w(DSS1 MD4 MD5 RIPEMD160 SHA SHA1)
+ if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
+ algs += %w(SHA224 SHA256 SHA384 SHA512)
+ end
+ algs.each do |alg|
+ assert_not_nil(OpenSSL::Digest.new(alg))
+ klass = OpenSSL::Digest.const_get(alg)
+ assert_not_nil(klass.new)
+ end
+ end
+
+ def test_digest_by_oid_and_name
+ check_digest(OpenSSL::ASN1::ObjectId.new("MD5"))
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA1"))
+ end
+
+ if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
+ def encode16(str)
+ str.unpack("H*").first
+ end
+
+ def test_098_features
+ sha224_a = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"
+ sha256_a = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
+ sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"
+ sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"
+
+ assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a"))
+ assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a"))
+ assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a"))
+ assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a"))
+
+ assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a")))
+ assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a")))
+ assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a")))
+ assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a")))
+ end
+
+ def test_digest_by_oid_and_name_sha2
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA224"))
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA256"))
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA384"))
+ check_digest(OpenSSL::ASN1::ObjectId.new("SHA512"))
+ end
+ end
+
+ def test_openssl_digest
+ assert_equal OpenSSL::Digest::MD5, OpenSSL::Digest("MD5")
+
+ assert_raises NameError do
+ OpenSSL::Digest("no such digest")
+ end
+ end
+
+ private
+
+ def check_digest(oid)
+ d = OpenSSL::Digest.new(oid.sn)
+ assert_not_nil(d)
+ d = OpenSSL::Digest.new(oid.ln)
+ assert_not_nil(d)
+ d = OpenSSL::Digest.new(oid.oid)
+ assert_not_nil(d)
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_engine.rb b/jni/ruby/test/openssl/test_engine.rb
new file mode 100644
index 0000000..a7264d0
--- /dev/null
+++ b/jni/ruby/test/openssl/test_engine.rb
@@ -0,0 +1,75 @@
+require_relative 'utils'
+
+class OpenSSL::TestEngine < Test::Unit::TestCase
+
+ def teardown
+ OpenSSL::Engine.cleanup # [ruby-core:40669]
+ assert_equal(0, OpenSSL::Engine.engines.size)
+ end
+
+ def test_engines_free # [ruby-dev:44173]
+ OpenSSL::Engine.load("openssl")
+ OpenSSL::Engine.engines
+ OpenSSL::Engine.engines
+ end
+
+ def test_openssl_engine_builtin
+ engine = OpenSSL::Engine.load("openssl")
+ assert_equal(true, engine)
+ assert_equal(1, OpenSSL::Engine.engines.size)
+ end
+
+ def test_openssl_engine_by_id_string
+ engine = get_engine
+ assert_not_nil(engine)
+ assert_equal(1, OpenSSL::Engine.engines.size)
+ end
+
+ def test_openssl_engine_id_name_inspect
+ engine = get_engine
+ assert_equal("openssl", engine.id)
+ assert_not_nil(engine.name)
+ assert_not_nil(engine.inspect)
+ end
+
+ def test_openssl_engine_digest_sha1
+ engine = get_engine
+ digest = engine.digest("SHA1")
+ assert_not_nil(digest)
+ data = "test"
+ assert_equal(OpenSSL::Digest::SHA1.digest(data), digest.digest(data))
+ end
+
+ def test_openssl_engine_cipher_rc4
+ engine = get_engine
+ algo = "RC4" #AES is not supported by openssl Engine (<=1.0.0e)
+ data = "a" * 1000
+ key = OpenSSL::Random.random_bytes(16)
+ # suppress message from openssl Engine's RC4 cipher [ruby-core:41026]
+ err_back = $stderr.dup
+ $stderr.reopen(IO::NULL)
+ encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) }
+ decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) }
+ assert_equal(data, decrypted)
+ ensure
+ if err_back
+ $stderr.reopen(err_back)
+ err_back.close
+ end
+ end
+
+ private
+
+ def get_engine
+ OpenSSL::Engine.by_id("openssl")
+ end
+
+ def crypt_data(data, key, mode)
+ cipher = yield
+ cipher.send mode
+ cipher.key = key
+ cipher.update(data) + cipher.final
+ end
+
+end if defined?(OpenSSL::TestUtils)
+
diff --git a/jni/ruby/test/openssl/test_fips.rb b/jni/ruby/test/openssl/test_fips.rb
new file mode 100644
index 0000000..6e4ac6d
--- /dev/null
+++ b/jni/ruby/test/openssl/test_fips.rb
@@ -0,0 +1,14 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestFIPS < Test::Unit::TestCase
+
+ def test_fips_mode_is_reentrant
+ OpenSSL.fips_mode = false
+ OpenSSL.fips_mode = false
+ end
+
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_hmac.rb b/jni/ruby/test/openssl/test_hmac.rb
new file mode 100644
index 0000000..f709ebd
--- /dev/null
+++ b/jni/ruby/test/openssl/test_hmac.rb
@@ -0,0 +1,41 @@
+# coding: UTF-8
+
+require_relative 'utils'
+
+class OpenSSL::TestHMAC < Test::Unit::TestCase
+ def setup
+ @digest = OpenSSL::Digest::MD5
+ @key = "KEY"
+ @data = "DATA"
+ @h1 = OpenSSL::HMAC.new(@key, @digest.new)
+ @h2 = OpenSSL::HMAC.new(@key, "MD5")
+ end
+
+ def teardown
+ end
+
+ def test_hmac
+ @h1.update(@data)
+ @h2.update(@data)
+ assert_equal(@h1.digest, @h2.digest)
+
+ assert_equal(OpenSSL::HMAC.digest(@digest.new, @key, @data), @h1.digest, "digest")
+ assert_equal(OpenSSL::HMAC.hexdigest(@digest.new, @key, @data), @h1.hexdigest, "hexdigest")
+
+ assert_equal(OpenSSL::HMAC.digest("MD5", @key, @data), @h2.digest, "digest")
+ assert_equal(OpenSSL::HMAC.hexdigest("MD5", @key, @data), @h2.hexdigest, "hexdigest")
+ end
+
+ def test_dup
+ @h1.update(@data)
+ h = @h1.dup
+ assert_equal(@h1.digest, h.digest, "dup digest")
+ end
+
+ def test_binary_update
+ data = "Lücíllé: Bût... yøü sáîd hé wås âlrîght.\nDr. Físhmån: Yés. Hé's løst hîs léft hånd, sø hé's gøîng tø bé åll rîght"
+ hmac = OpenSSL::HMAC.new("qShkcwN92rsM9nHfdnP4ugcVU2iI7iM/trovs01ZWok", "SHA256")
+ result = hmac.update(data).hexdigest
+ assert_equal "a13984b929a07912e4e21c5720876a8e150d6f67f854437206e7f86547248396", result
+ end
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/openssl/test_ns_spki.rb b/jni/ruby/test/openssl/test_ns_spki.rb
new file mode 100644
index 0000000..ab07bfb
--- /dev/null
+++ b/jni/ruby/test/openssl/test_ns_spki.rb
@@ -0,0 +1,51 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestNSSPI < Test::Unit::TestCase
+ def setup
+ # This request data is adopt from the specification of
+ # "Netscape Extensions for User Key Generation".
+ # -- http://wp.netscape.com/eng/security/comm4-keygen.html
+ @b64 = "MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue+PtwBRE6XfV"
+ @b64 << "WtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID"
+ @b64 << "AQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n/S"
+ @b64 << "r/7iJNroWlSzSMtTiQTEB+ADWHGj9u1xrUrOilq/o2cuQxIfZcNZkYAkWP4DubqW"
+ @b64 << "i0//rgBvmco="
+ end
+
+ def test_build_data
+ key1 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ key2 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ spki = OpenSSL::Netscape::SPKI.new
+ spki.challenge = "RandomString"
+ spki.public_key = key1.public_key
+ spki.sign(key1, OpenSSL::Digest::SHA1.new)
+ assert(spki.verify(spki.public_key))
+ assert(spki.verify(key1.public_key))
+ assert(!spki.verify(key2.public_key))
+
+ der = spki.to_der
+ spki = OpenSSL::Netscape::SPKI.new(der)
+ assert_equal("RandomString", spki.challenge)
+ assert_equal(key1.public_key.to_der, spki.public_key.to_der)
+ assert(spki.verify(spki.public_key))
+ assert_not_nil(spki.to_text)
+ end
+
+ def test_decode_data
+ spki = OpenSSL::Netscape::SPKI.new(@b64)
+ assert_equal(@b64, spki.to_pem)
+ assert_equal(@b64.unpack("m").first, spki.to_der)
+ assert_equal("MozillaIsMyFriend", spki.challenge)
+ assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
+
+ spki = OpenSSL::Netscape::SPKI.new(@b64.unpack("m").first)
+ assert_equal(@b64, spki.to_pem)
+ assert_equal(@b64.unpack("m").first, spki.to_der)
+ assert_equal("MozillaIsMyFriend", spki.challenge)
+ assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_ocsp.rb b/jni/ruby/test/openssl/test_ocsp.rb
new file mode 100644
index 0000000..af727d8
--- /dev/null
+++ b/jni/ruby/test/openssl/test_ocsp.rb
@@ -0,0 +1,47 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestOCSP < Test::Unit::TestCase
+ def setup
+ ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
+ ca_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ ca_serial = 0xabcabcabcabc
+
+ subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert")
+ @key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ serial = 0xabcabcabcabd
+
+ now = Time.at(Time.now.to_i) # suppress usec
+ dgst = OpenSSL::Digest::SHA1.new
+
+ @ca_cert = OpenSSL::TestUtils.issue_cert(
+ ca_subj, ca_key, ca_serial, now, now+3600, [], nil, nil, dgst)
+ @cert = OpenSSL::TestUtils.issue_cert(
+ subj, @key, serial, now, now+3600, [], @ca_cert, nil, dgst)
+ end
+
+ def test_new_certificate_id
+ cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert)
+ assert_kind_of OpenSSL::OCSP::CertificateId, cid
+ assert_equal @cert.serial, cid.serial
+ end
+
+ def test_new_certificate_id_with_digest
+ cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new)
+ assert_kind_of OpenSSL::OCSP::CertificateId, cid
+ assert_equal @cert.serial, cid.serial
+ end if defined?(OpenSSL::Digest::SHA256)
+
+ def test_new_ocsp_request
+ request = OpenSSL::OCSP::Request.new
+ cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+ request.add_certid(cid)
+ request.sign(@cert, @key, [@cert])
+ assert_kind_of OpenSSL::OCSP::Request, request
+ # in current implementation not same instance of certificate id, but should contain same data
+ assert_equal cid.serial, request.certid.first.serial
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pair.rb b/jni/ruby/test/openssl/test_pair.rb
new file mode 100644
index 0000000..3aca5f4
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pair.rb
@@ -0,0 +1,372 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+require 'socket'
+require_relative '../ruby/ut_eof'
+
+module OpenSSL::SSLPairM
+ def server
+ host = "127.0.0.1"
+ port = 0
+ ctx = OpenSSL::SSL::SSLContext.new()
+ ctx.ciphers = "ADH"
+ ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
+ tcps = create_tcp_server(host, port)
+ ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
+ return ssls
+ end
+
+ def client(port)
+ host = "127.0.0.1"
+ ctx = OpenSSL::SSL::SSLContext.new()
+ ctx.ciphers = "ADH"
+ s = create_tcp_client(host, port)
+ ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
+ ssl.connect
+ ssl.sync_close = true
+ ssl
+ end
+
+ def ssl_pair
+ ssls = server
+ th = Thread.new {
+ ns = ssls.accept
+ ssls.close
+ ns
+ }
+ port = ssls.to_io.local_address.ip_port
+ c = client(port)
+ s = th.value
+ if block_given?
+ begin
+ yield c, s
+ ensure
+ c.close unless c.closed?
+ s.close unless s.closed?
+ end
+ else
+ return c, s
+ end
+ ensure
+ if th && th.alive?
+ th.kill
+ th.join
+ end
+ end
+end
+
+module OpenSSL::SSLPair
+ include OpenSSL::SSLPairM
+
+ def create_tcp_server(host, port)
+ TCPServer.new(host, port)
+ end
+
+ def create_tcp_client(host, port)
+ TCPSocket.new(host, port)
+ end
+end
+
+module OpenSSL::SSLPairLowlevelSocket
+ include OpenSSL::SSLPairM
+
+ def create_tcp_server(host, port)
+ Addrinfo.tcp(host, port).listen
+ end
+
+ def create_tcp_client(host, port)
+ Addrinfo.tcp(host, port).connect
+ end
+end
+
+module OpenSSL::TestEOF1M
+ def open_file(content)
+ s1, s2 = ssl_pair
+ th = Thread.new { s2 << content; s2.close }
+ yield s1
+ ensure
+ th.join
+ s1.close
+ end
+end
+
+module OpenSSL::TestEOF2M
+ def open_file(content)
+ s1, s2 = ssl_pair
+ th = Thread.new { s1 << content; s1.close }
+ yield s2
+ ensure
+ th.join
+ s2.close
+ end
+end
+
+module OpenSSL::TestPairM
+ def test_getc
+ ssl_pair {|s1, s2|
+ s1 << "a"
+ assert_equal(?a, s2.getc)
+ }
+ end
+
+ def test_readpartial
+ ssl_pair {|s1, s2|
+ s2.write "a\nbcd"
+ assert_equal("a\n", s1.gets)
+ result = ""
+ result << s1.readpartial(10) until result.length == 3
+ assert_equal("bcd", result)
+ s2.write "efg"
+ result = ""
+ result << s1.readpartial(10) until result.length == 3
+ assert_equal("efg", result)
+ s2.close
+ assert_raise(EOFError) { s1.readpartial(10) }
+ assert_raise(EOFError) { s1.readpartial(10) }
+ assert_equal("", s1.readpartial(0))
+ }
+ end
+
+ def test_readall
+ ssl_pair {|s1, s2|
+ s2.close
+ assert_equal("", s1.read)
+ }
+ end
+
+ def test_readline
+ ssl_pair {|s1, s2|
+ s2.close
+ assert_raise(EOFError) { s1.readline }
+ }
+ end
+
+ def test_puts_meta
+ ssl_pair {|s1, s2|
+ begin
+ old = $/
+ $/ = '*'
+ s1.puts 'a'
+ ensure
+ $/ = old
+ end
+ s1.close
+ assert_equal("a\n", s2.read)
+ }
+ end
+
+ def test_puts_empty
+ ssl_pair {|s1, s2|
+ s1.puts
+ s1.close
+ assert_equal("\n", s2.read)
+ }
+ end
+
+ def test_read_nonblock
+ ssl_pair {|s1, s2|
+ err = nil
+ assert_raise(OpenSSL::SSL::SSLErrorWaitReadable) {
+ begin
+ s2.read_nonblock(10)
+ ensure
+ err = $!
+ end
+ }
+ assert_kind_of(IO::WaitReadable, err)
+ s1.write "abc\ndef\n"
+ IO.select([s2])
+ assert_equal("ab", s2.read_nonblock(2))
+ assert_equal("c\n", s2.gets)
+ ret = nil
+ assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10) }
+ assert_equal("def\n", ret)
+ s1.close
+ sleep 0.1
+ assert_raise(EOFError) { s2.read_nonblock(10) }
+ }
+ end
+
+ def test_read_nonblock_no_exception
+ ssl_pair {|s1, s2|
+ assert_equal :wait_readable, s2.read_nonblock(10, exception: false)
+ s1.write "abc\ndef\n"
+ IO.select([s2])
+ assert_equal("ab", s2.read_nonblock(2, exception: false))
+ assert_equal("c\n", s2.gets)
+ ret = nil
+ assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10, exception: false) }
+ assert_equal("def\n", ret)
+ s1.close
+ sleep 0.1
+ assert_equal(nil, s2.read_nonblock(10, exception: false))
+ }
+ end
+
+ def write_nonblock(socket, meth, str)
+ ret = socket.send(meth, str)
+ ret.is_a?(Symbol) ? 0 : ret
+ end
+
+ def write_nonblock_no_ex(socket, str)
+ ret = socket.write_nonblock str, exception: false
+ ret.is_a?(Symbol) ? 0 : ret
+ end
+
+ def test_write_nonblock
+ ssl_pair {|s1, s2|
+ n = 0
+ begin
+ n += write_nonblock s1, :write_nonblock, "a" * 100000
+ n += write_nonblock s1, :write_nonblock, "b" * 100000
+ n += write_nonblock s1, :write_nonblock, "c" * 100000
+ n += write_nonblock s1, :write_nonblock, "d" * 100000
+ n += write_nonblock s1, :write_nonblock, "e" * 100000
+ n += write_nonblock s1, :write_nonblock, "f" * 100000
+ rescue IO::WaitWritable
+ end
+ s1.close
+ assert_equal(n, s2.read.length)
+ }
+ end
+
+ def test_write_nonblock_no_exceptions
+ ssl_pair {|s1, s2|
+ n = 0
+ begin
+ n += write_nonblock_no_ex s1, "a" * 100000
+ n += write_nonblock_no_ex s1, "b" * 100000
+ n += write_nonblock_no_ex s1, "c" * 100000
+ n += write_nonblock_no_ex s1, "d" * 100000
+ n += write_nonblock_no_ex s1, "e" * 100000
+ n += write_nonblock_no_ex s1, "f" * 100000
+ rescue OpenSSL::SSL::SSLError => e
+ # on some platforms (maybe depend on OpenSSL version), writing to
+ # SSLSocket after SSL_ERROR_WANT_WRITE causes this error.
+ raise e if n == 0
+ end
+ s1.close
+ assert_equal(n, s2.read.length)
+ }
+ end
+
+ def test_write_nonblock_with_buffered_data
+ ssl_pair {|s1, s2|
+ s1.write "foo"
+ s1.write_nonblock("bar")
+ s1.write "baz"
+ s1.close
+ assert_equal("foobarbaz", s2.read)
+ }
+ end
+
+ def test_write_nonblock_with_buffered_data_no_exceptions
+ ssl_pair {|s1, s2|
+ s1.write "foo"
+ s1.write_nonblock("bar", exception: false)
+ s1.write "baz"
+ s1.close
+ assert_equal("foobarbaz", s2.read)
+ }
+ end
+
+ def tcp_pair
+ host = "127.0.0.1"
+ serv = TCPServer.new(host, 0)
+ port = serv.connect_address.ip_port
+ sock1 = TCPSocket.new(host, port)
+ sock2 = serv.accept
+ serv.close
+ [sock1, sock2]
+ ensure
+ serv.close if serv && !serv.closed?
+ end
+
+ def test_connect_accept_nonblock
+ ctx = OpenSSL::SSL::SSLContext.new()
+ ctx.ciphers = "ADH"
+ ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
+
+ sock1, sock2 = tcp_pair
+
+ th = Thread.new {
+ s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx)
+ s2.sync_close = true
+ begin
+ sleep 0.2
+ s2.accept_nonblock
+ rescue IO::WaitReadable
+ IO.select([s2])
+ retry
+ rescue IO::WaitWritable
+ IO.select(nil, [s2])
+ retry
+ end
+ s2
+ }
+
+ sleep 0.1
+ ctx = OpenSSL::SSL::SSLContext.new()
+ ctx.ciphers = "ADH"
+ s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx)
+ begin
+ sleep 0.2
+ s1.connect_nonblock
+ rescue IO::WaitReadable
+ IO.select([s1])
+ retry
+ rescue IO::WaitWritable
+ IO.select(nil, [s1])
+ retry
+ end
+ s1.sync_close = true
+
+ s2 = th.value
+
+ s1.print "a\ndef"
+ assert_equal("a\n", s2.gets)
+ ensure
+ th.join
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ sock1.close if sock1 && !sock1.closed?
+ sock2.close if sock2 && !sock2.closed?
+ end
+end
+
+class OpenSSL::TestEOF1 < Test::Unit::TestCase
+ include TestEOF
+ include OpenSSL::SSLPair
+ include OpenSSL::TestEOF1M
+end
+
+class OpenSSL::TestEOF1LowlevelSocket < Test::Unit::TestCase
+ include TestEOF
+ include OpenSSL::SSLPairLowlevelSocket
+ include OpenSSL::TestEOF1M
+end
+
+class OpenSSL::TestEOF2 < Test::Unit::TestCase
+ include TestEOF
+ include OpenSSL::SSLPair
+ include OpenSSL::TestEOF2M
+end
+
+class OpenSSL::TestEOF2LowlevelSocket < Test::Unit::TestCase
+ include TestEOF
+ include OpenSSL::SSLPairLowlevelSocket
+ include OpenSSL::TestEOF2M
+end
+
+class OpenSSL::TestPair < Test::Unit::TestCase
+ include OpenSSL::SSLPair
+ include OpenSSL::TestPairM
+end
+
+class OpenSSL::TestPairLowlevelSocket < Test::Unit::TestCase
+ include OpenSSL::SSLPairLowlevelSocket
+ include OpenSSL::TestPairM
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_partial_record_read.rb b/jni/ruby/test/openssl/test_partial_record_read.rb
new file mode 100644
index 0000000..1899a30
--- /dev/null
+++ b/jni/ruby/test/openssl/test_partial_record_read.rb
@@ -0,0 +1,34 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+ class OpenSSL::TestPartialRecordRead < OpenSSL::SSLTestCase
+ def test_partial_tls_record_read_nonblock
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :server_proc =>
+ Proc.new do |server_ctx, server_ssl|
+ begin
+ server_ssl.io.write("\x01") # the beginning of a TLS record
+ sleep 6 # do not finish prematurely before the read by the client is attempted
+ ensure
+ server_ssl.close
+ end
+ end
+ ) do |server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.sync_close = true
+ begin
+ ssl.connect
+ sleep 3 # wait is required for the (incomplete) TLS record to arrive at the client socket
+
+ # Should raise a IO::WaitReadable since a full TLS record is not available for reading.
+ assert_raise(IO::WaitReadable) { ssl.read_nonblock(1) }
+ ensure
+ ssl.close
+ end
+ end
+ end
+
+ end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkcs12.rb b/jni/ruby/test/openssl/test_pkcs12.rb
new file mode 100644
index 0000000..4e37904
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkcs12.rb
@@ -0,0 +1,209 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+module OpenSSL
+ class TestPKCS12 < Test::Unit::TestCase
+ include OpenSSL::TestUtils
+
+ def setup
+ ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+
+ now = Time.now
+ ca_exts = [
+ ["basicConstraints","CA:TRUE",true],
+ ["keyUsage","keyCertSign, cRLSign",true],
+ ["subjectKeyIdentifier","hash",false],
+ ["authorityKeyIdentifier","keyid:always",false],
+ ]
+
+ @cacert = issue_cert(ca, TEST_KEY_RSA2048, 1, now, now+3600, ca_exts,
+ nil, nil, OpenSSL::Digest::SHA1.new)
+
+ inter_ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Intermediate CA")
+ inter_ca_key = OpenSSL::PKey.read <<-_EOS_
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDp7hIG0SFMG/VWv1dBUWziAPrNmkMXJgTCAoB7jffzRtyyN04K
+oq/89HAszTMStZoMigQURfokzKsjpUp8OYCAEsBtt9d5zPndWMz/gHN73GrXk3LT
+ZsxEn7Xv5Da+Y9F/Hx2QZUHarV5cdZixq2NbzWGwrToogOQMh2pxN3Z/0wIDAQAB
+AoGBAJysUyx3olpsGzv3OMRJeahASbmsSKTXVLZvoIefxOINosBFpCIhZccAG6UV
+5c/xCvS89xBw8aD15uUfziw3AuT8QPEtHCgfSjeT7aWzBfYswEgOW4XPuWr7EeI9
+iNHGD6z+hCN/IQr7FiEBgTp6A+i/hffcSdR83fHWKyb4M7TRAkEA+y4BNd668HmC
+G5MPRx25n6LixuBxrNp1umfjEI6UZgEFVpYOg4agNuimN6NqM253kcTR94QNTUs5
+Kj3EhG1YWwJBAO5rUjiOyCNVX2WUQrOMYK/c1lU7fvrkdygXkvIGkhsPoNRzLPeA
+HGJszKtrKD8bNihWpWNIyqKRHfKVD7yXT+kCQGCAhVCIGTRoypcDghwljHqLnysf
+ci0h5ZdPcIqc7ODfxYhFsJ/Rql5ONgYsT5Ig/+lOQAkjf+TRYM4c2xKx2/8CQBvG
+jv6dy70qDgIUgqzONtlmHeYyFzn9cdBO5sShdVYHvRHjFSMEXsosqK9zvW2UqvuK
+FJx7d3f29gkzynCLJDkCQGQZlEZJC4vWmWJGRKJ24P6MyQn3VsPfErSKOg4lvyM3
+Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
+-----END RSA PRIVATE KEY-----
+ _EOS_
+
+ @inter_cacert = issue_cert(inter_ca, inter_ca_key, 2, now, now+3600, ca_exts,
+ @cacert, TEST_KEY_RSA2048, OpenSSL::Digest::SHA1.new)
+
+ exts = [
+ ["keyUsage","digitalSignature",true],
+ ["subjectKeyIdentifier","hash",false],
+ ]
+ ee = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Ruby PKCS12 Test Certificate")
+ @mycert = issue_cert(ee, TEST_KEY_RSA1024, 3, now, now+3600, exts,
+ @inter_cacert, inter_ca_key, OpenSSL::Digest::SHA1.new)
+ end
+
+ def test_create
+ pkcs12 = OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert
+ )
+ assert_equal @mycert, pkcs12.certificate
+ assert_equal TEST_KEY_RSA1024, pkcs12.key
+ assert_nil pkcs12.ca_certs
+ end
+
+ def test_create_no_pass
+ pkcs12 = OpenSSL::PKCS12.create(
+ nil,
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert
+ )
+ assert_equal @mycert, pkcs12.certificate
+ assert_equal TEST_KEY_RSA1024, pkcs12.key
+ assert_nil pkcs12.ca_certs
+
+ decoded = OpenSSL::PKCS12.new(pkcs12.to_der)
+ assert_cert @mycert, decoded.certificate
+ end
+
+ def test_create_with_chain
+ chain = [@inter_cacert, @cacert]
+
+ pkcs12 = OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert,
+ chain
+ )
+ assert_equal chain, pkcs12.ca_certs
+ end
+
+ def test_create_with_chain_decode
+ chain = [@cacert, @inter_cacert]
+
+ passwd = "omg"
+
+ pkcs12 = OpenSSL::PKCS12.create(
+ passwd,
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert,
+ chain
+ )
+
+ decoded = OpenSSL::PKCS12.new(pkcs12.to_der, passwd)
+ assert_equal chain.size, decoded.ca_certs.size
+ assert_include_cert @cacert, decoded.ca_certs
+ assert_include_cert @inter_cacert, decoded.ca_certs
+ assert_cert @mycert, decoded.certificate
+ assert_equal TEST_KEY_RSA1024.to_der, decoded.key.to_der
+ end
+
+ def test_create_with_bad_nid
+ assert_raises(ArgumentError) do
+ OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert,
+ [],
+ "foo"
+ )
+ end
+ end
+
+ def test_create_with_itr
+ OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert,
+ [],
+ nil,
+ nil,
+ 2048
+ )
+
+ assert_raises(TypeError) do
+ OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert,
+ [],
+ nil,
+ nil,
+ "omg"
+ )
+ end
+ end
+
+ def test_create_with_mac_itr
+ OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert,
+ [],
+ nil,
+ nil,
+ nil,
+ 2048
+ )
+
+ assert_raises(TypeError) do
+ OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ TEST_KEY_RSA1024,
+ @mycert,
+ [],
+ nil,
+ nil,
+ nil,
+ "omg"
+ )
+ end
+ end
+
+ private
+ def assert_cert expected, actual
+ [
+ :subject,
+ :issuer,
+ :serial,
+ :not_before,
+ :not_after,
+ ].each do |attribute|
+ assert_equal expected.send(attribute), actual.send(attribute)
+ end
+ assert_equal expected.to_der, actual.to_der
+ end
+
+ def assert_include_cert cert, ary
+ der = cert.to_der
+ ary.each do |candidate|
+ if candidate.to_der == der
+ return true
+ end
+ end
+ false
+ end
+
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkcs5.rb b/jni/ruby/test/openssl/test_pkcs5.rb
new file mode 100644
index 0000000..5e85dde
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkcs5.rb
@@ -0,0 +1,97 @@
+require_relative 'utils'
+
+class OpenSSL::TestPKCS5 < Test::Unit::TestCase
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20
+ p ="password"
+ s = "salt"
+ c = 1
+ dk_len = 20
+ raw = %w{ 0c 60 c8 0f 96 1f 0e 71
+ f3 a9 b5 24 af 60 12 06
+ 2f e0 37 a6 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+ assert_equal(expected, value)
+ end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20
+ p ="password"
+ s = "salt"
+ c = 2
+ dk_len = 20
+ raw = %w{ ea 6c 01 4d c7 2d 6f 8c
+ cd 1e d9 2a ce 1d 41 f0
+ d8 de 89 57 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+ assert_equal(expected, value)
+ end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20
+ p ="password"
+ s = "salt"
+ c = 4096
+ dk_len = 20
+ raw = %w{ 4b 00 79 01 b7 65 48 9a
+ be ad 49 d9 26 f7 21 d0
+ 65 a4 29 c1 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+ assert_equal(expected, value)
+ end
+
+# takes too long!
+# def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20
+# p ="password"
+# s = "salt"
+# c = 16777216
+# dk_len = 20
+# raw = %w{ ee fe 3d 61 cd 4d a4 e4
+# e9 94 5b 3d 6b a2 15 8c
+# 26 34 e9 84 }
+# expected = [raw.join('')].pack('H*')
+# value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+# assert_equal(expected, value)
+# end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25
+ p ="passwordPASSWORDpassword"
+ s = "saltSALTsaltSALTsaltSALTsaltSALTsalt"
+ c = 4096
+ dk_len = 25
+
+ raw = %w{ 3d 2e ec 4f e4 1c 84 9b
+ 80 c8 d8 36 62 c0 e4 4a
+ 8b 29 1a 96 4c f2 f0 70
+ 38 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+ assert_equal(expected, value)
+ end
+
+ def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16
+ p ="pass\0word"
+ s = "sa\0lt"
+ c = 4096
+ dk_len = 16
+ raw = %w{ 56 fa 6a a7 55 48 09 9d
+ cc 37 d7 f0 34 25 e0 c3 }
+ expected = [raw.join('')].pack('H*')
+ value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+ assert_equal(expected, value)
+ end
+
+ def test_pbkdf2_hmac_sha256_c_20000_len_32
+ #unfortunately no official test vectors available yet for SHA-2
+ p ="password"
+ s = OpenSSL::Random.random_bytes(16)
+ c = 20000
+ dk_len = 32
+ digest = OpenSSL::Digest::SHA256.new
+ value1 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest)
+ value2 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest)
+ assert_equal(value1, value2)
+ end if OpenSSL::PKCS5.respond_to?(:pbkdf2_hmac)
+
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/openssl/test_pkcs7.rb b/jni/ruby/test/openssl/test_pkcs7.rb
new file mode 100644
index 0000000..47bd4f3
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkcs7.rb
@@ -0,0 +1,297 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestPKCS7 < Test::Unit::TestCase
+ def setup
+ @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+ ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+ ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+
+ now = Time.now
+ ca_exts = [
+ ["basicConstraints","CA:TRUE",true],
+ ["keyUsage","keyCertSign, cRLSign",true],
+ ["subjectKeyIdentifier","hash",false],
+ ["authorityKeyIdentifier","keyid:always",false],
+ ]
+ @ca_cert = issue_cert(ca, @rsa2048, 1, now, now+3600, ca_exts,
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ ee_exts = [
+ ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+ ["authorityKeyIdentifier","keyid:always",false],
+ ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+ ]
+ @ee1_cert = issue_cert(ee1, @rsa1024, 2, now, now+1800, ee_exts,
+ @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ @ee2_cert = issue_cert(ee2, @rsa1024, 3, now, now+1800, ee_exts,
+ @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ end
+
+ def issue_cert(*args)
+ OpenSSL::TestUtils.issue_cert(*args)
+ end
+
+ def test_signed
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ ca_certs = [@ca_cert]
+
+ data = "aaaaa\r\nbbbbb\r\nccccc\r\n"
+ tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs)
+ p7 = OpenSSL::PKCS7.new(tmp.to_der)
+ certs = p7.certificates
+ signers = p7.signers
+ assert(p7.verify([], store))
+ assert_equal(data, p7.data)
+ assert_equal(2, certs.size)
+ assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+ assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+ assert_equal(1, signers.size)
+ assert_equal(@ee1_cert.serial, signers[0].serial)
+ assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+
+ # Normaly OpenSSL tries to translate the supplied content into canonical
+ # MIME format (e.g. a newline character is converted into CR+LF).
+ # If the content is a binary, PKCS7::BINARY flag should be used.
+
+ data = "aaaaa\nbbbbb\nccccc\n"
+ flag = OpenSSL::PKCS7::BINARY
+ tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag)
+ p7 = OpenSSL::PKCS7.new(tmp.to_der)
+ certs = p7.certificates
+ signers = p7.signers
+ assert(p7.verify([], store))
+ assert_equal(data, p7.data)
+ assert_equal(2, certs.size)
+ assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+ assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+ assert_equal(1, signers.size)
+ assert_equal(@ee1_cert.serial, signers[0].serial)
+ assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+
+ # A signed-data which have multiple signatures can be created
+ # through the following steps.
+ # 1. create two signed-data
+ # 2. copy signerInfo and certificate from one to another
+
+ tmp1 = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, [], flag)
+ tmp2 = OpenSSL::PKCS7.sign(@ee2_cert, @rsa1024, data, [], flag)
+ tmp1.add_signer(tmp2.signers[0])
+ tmp1.add_certificate(@ee2_cert)
+
+ p7 = OpenSSL::PKCS7.new(tmp1.to_der)
+ certs = p7.certificates
+ signers = p7.signers
+ assert(p7.verify([], store))
+ assert_equal(data, p7.data)
+ assert_equal(2, certs.size)
+ assert_equal(2, signers.size)
+ assert_equal(@ee1_cert.serial, signers[0].serial)
+ assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+ assert_equal(@ee2_cert.serial, signers[1].serial)
+ assert_equal(@ee2_cert.issuer.to_s, signers[1].issuer.to_s)
+ end
+
+ def test_detached_sign
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ ca_certs = [@ca_cert]
+
+ data = "aaaaa\nbbbbb\nccccc\n"
+ flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED
+ tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag)
+ p7 = OpenSSL::PKCS7.new(tmp.to_der)
+ assert_nothing_raised do
+ OpenSSL::ASN1.decode(p7)
+ end
+
+ certs = p7.certificates
+ signers = p7.signers
+ assert(!p7.verify([], store))
+ assert(p7.verify([], store, data))
+ assert_equal(data, p7.data)
+ assert_equal(2, certs.size)
+ assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+ assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+ assert_equal(1, signers.size)
+ assert_equal(@ee1_cert.serial, signers[0].serial)
+ assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+ end
+
+ def test_enveloped
+ if OpenSSL::OPENSSL_VERSION_NUMBER <= 0x0090704f
+ # PKCS7_encrypt() of OpenSSL-0.9.7d goes to SEGV.
+ # http://www.mail-archive.com/openssl-dev@openssl.org/msg17376.html
+ return
+ end
+
+ certs = [@ee1_cert, @ee2_cert]
+ cipher = OpenSSL::Cipher::AES.new("128-CBC")
+ data = "aaaaa\nbbbbb\nccccc\n"
+
+ tmp = OpenSSL::PKCS7.encrypt(certs, data, cipher, OpenSSL::PKCS7::BINARY)
+ p7 = OpenSSL::PKCS7.new(tmp.to_der)
+ recip = p7.recipients
+ assert_equal(:enveloped, p7.type)
+ assert_equal(2, recip.size)
+
+ assert_equal(@ca_cert.subject.to_s, recip[0].issuer.to_s)
+ assert_equal(2, recip[0].serial)
+ assert_equal(data, p7.decrypt(@rsa1024, @ee1_cert))
+
+ assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s)
+ assert_equal(3, recip[1].serial)
+ assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert))
+ end
+
+ def test_graceful_parsing_failure #[ruby-core:43250]
+ contents = File.read(__FILE__)
+ assert_raise(ArgumentError) { OpenSSL::PKCS7.new(contents) }
+ end
+
+ def test_set_type_signed
+ p7 = OpenSSL::PKCS7.new
+ p7.type = "signed"
+ assert_equal(:signed, p7.type)
+ end
+
+ def test_set_type_data
+ p7 = OpenSSL::PKCS7.new
+ p7.type = "data"
+ assert_equal(:data, p7.type)
+ end
+
+ def test_set_type_signed_and_enveloped
+ p7 = OpenSSL::PKCS7.new
+ p7.type = "signedAndEnveloped"
+ assert_equal(:signedAndEnveloped, p7.type)
+ end
+
+ def test_set_type_enveloped
+ p7 = OpenSSL::PKCS7.new
+ p7.type = "enveloped"
+ assert_equal(:enveloped, p7.type)
+ end
+
+ def test_set_type_encrypted
+ p7 = OpenSSL::PKCS7.new
+ p7.type = "encrypted"
+ assert_equal(:encrypted, p7.type)
+ end
+
+ def test_degenerate_pkcs7
+ ca_cert_pem = <<END
+-----BEGIN CERTIFICATE-----
+MIID4DCCAsigAwIBAgIJAL1oVI72wmQwMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
+BAYTAkFVMQ4wDAYDVQQIEwVTdGF0ZTENMAsGA1UEBxMEQ2l0eTEQMA4GA1UEChMH
+RXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBDQTAeFw0xMjEwMTgwOTE2NTBaFw0y
+MjEwMTYwOTE2NTBaMFMxCzAJBgNVBAYTAkFVMQ4wDAYDVQQIEwVTdGF0ZTENMAsG
+A1UEBxMEQ2l0eTEQMA4GA1UEChMHRXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBD
+QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTSPNxOkd5NN19XO0fJ
+tGVlWN4DWuvVL9WbWnXJXX9rU6X8sSOL9RrRA64eEZf2UBFjz9fMHZj/OGcxZpus
+4YtzfSrMU6xfvsIHeqX+mT60ms2RfX4UXab50MQArBin3JVKHGnOi25uyAOylVFU
+TuzzQJvKyB67vjuRPMlVAgVAZAP07ru9gW0ajt/ODxvUfvXxp5SFF68mVP2ipMBr
+4fujUwQC6cVHmnuL6p87VFoo9uk87TSQVDOQGL8MK4moMFtEW9oUTU22CgnxnCsS
+sCCELYhy9BdaTWQH26LzMfhnwSuIRHZyprW4WZtU0akrYXNiCj8o92rZmQWXJDbl
+qNECAwEAAaOBtjCBszAdBgNVHQ4EFgQUNtVw4jvkZZbkdQbkYi2/F4QN79owgYMG
+A1UdIwR8MHqAFDbVcOI75GWW5HUG5GItvxeEDe/aoVekVTBTMQswCQYDVQQGEwJB
+VTEOMAwGA1UECBMFU3RhdGUxDTALBgNVBAcTBENpdHkxEDAOBgNVBAoTB0V4YW1w
+bGUxEzARBgNVBAMTCkV4YW1wbGUgQ0GCCQC9aFSO9sJkMDAMBgNVHRMEBTADAQH/
+MA0GCSqGSIb3DQEBBQUAA4IBAQBvJIsY9bIqliZ3WD1KoN4cvAQeRAPsoLXQkkHg
+P6Nrcw9rJ5JvoHfYbo5aNlwbnkbt/B2xlVEXUYpJoBZFXafgxG2gJleioIgnaDS4
+FPPwZf1C5ZrOgUBfxTGjHex4ghSAoNGOd35jQzin5NGKOvZclPjZ2vQ++LP3aA2l
+9Fn2qASS46IzMGJlC75mlTOTQwDM16UunMAK26lNG9J6q02o4d/oU2a7x0fD80yF
+64kNA1wDAwaVCYiUH541qKp+b4iDqer8nf8HqzYDFlpje18xYZMEd1hj8dVOharM
+pISJ+D52hV/BGEYF8r5k3hpC5d76gSP2oCcaY0XvLBf97qik
+-----END CERTIFICATE-----
+END
+ p7 = OpenSSL::PKCS7.new
+ p7.type = "signed"
+ ca_cert = OpenSSL::X509::Certificate.new(ca_cert_pem)
+ p7.add_certificate ca_cert
+ p7.add_data ""
+
+ assert_nothing_raised do
+ p7.to_pem
+ end
+ end
+
+ def test_split_content
+ pki_message_pem = <<END
+-----BEGIN PKCS7-----
+MIIHSwYJKoZIhvcNAQcCoIIHPDCCBzgCAQExCzAJBgUrDgMCGgUAMIIDiAYJKoZI
+hvcNAQcBoIIDeQSCA3UwgAYJKoZIhvcNAQcDoIAwgAIBADGCARAwggEMAgEAMHUw
+cDEQMA4GA1UECgwHZXhhbXBsZTEXMBUGA1UEAwwOVEFSTUFDIFJPT1QgQ0ExIjAg
+BgkqhkiG9w0BCQEWE3NvbWVvbmVAZXhhbXBsZS5vcmcxCzAJBgNVBAYTAlVTMRIw
+EAYDVQQHDAlUb3duIEhhbGwCAWYwDQYJKoZIhvcNAQEBBQAEgYBspXXse8ZhG1FE
+E3PVAulbvrdR52FWPkpeLvSjgEkYzTiUi0CC3poUL1Ku5mOlavWAJgoJpFICDbvc
+N4ZNDCwOhnzoI9fMGmm1gvPQy15BdhhZRo9lP7Ga/Hg2APKT0/0yhPsmJ+w+u1e7
+OoJEVeEZ27x3+u745bGEcu8of5th6TCABgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcE
+CBNs2U5mMsd/oIAEggIQU6cur8QBz02/4eMpHdlU9IkyrRMiaMZ/ky9zecOAjnvY
+d2jZqS7RhczpaNJaSli3GmDsKrF+XqE9J58s9ScGqUigzapusTsxIoRUPr7Ztb0a
+pg8VWDipAsuw7GfEkgx868sV93uC4v6Isfjbhd+JRTFp/wR1kTi7YgSXhES+RLUW
+gQbDIDgEQYxJ5U951AJtnSpjs9za2ZkTdd8RSEizJK0bQ1vqLoApwAVgZqluATqQ
+AHSDCxhweVYw6+y90B9xOrqPC0eU7Wzryq2+Raq5ND2Wlf5/N11RQ3EQdKq/l5Te
+ijp9PdWPlkUhWVoDlOFkysjk+BE+7AkzgYvz9UvBjmZsMsWqf+KsZ4S8/30ndLzu
+iucsu6eOnFLLX8DKZxV6nYffZOPzZZL8hFBcE7PPgSdBEkazMrEBXq1j5mN7exbJ
+NOA5uGWyJNBMOCe+1JbxG9UeoqvCCTHESxEeDu7xR3NnSOD47n7cXwHr81YzK2zQ
+5oWpP3C8jzI7tUjLd1S0Z3Psd17oaCn+JOfUtuB0nc3wfPF/WPo0xZQodWxp2/Cl
+EltR6qr1zf5C7GwmLzBZ6bHFAIT60/JzV0/56Pn8ztsRFtI4cwaBfTfvnwi8/sD9
+/LYOMY+/b6UDCUSR7RTN7XfrtAqDEzSdzdJkOWm1jvM8gkLmxpZdvxG3ZvDYnEQE
+5Nq+un5nAny1wf3rWierBAjE5ntiAmgs5AAAAAAAAAAAAACgggHqMIIB5jCCAU+g
+AwIBAgIBATANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQwQUM5RjAyNi1EQ0VB
+LTRDMTItOTEyNy1DMEZEN0QyQThCNUEwHhcNMTIxMDE5MDk0NTQ3WhcNMTMxMDE5
+MDk0NTQ3WjAvMS0wKwYDVQQDEyQwQUM5RjAyNi1EQ0VBLTRDMTItOTEyNy1DMEZE
+N0QyQThCNUEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALTsTNyGIsKvyw56
+WI3Gll/RmjsupkrdEtPbx7OjS9MEgyhOAf9+u6CV0LJGHpy7HUeROykF6xpbSdCm
+Mr6kNObl5N0ljOb8OmV4atKjmGg1rWawDLyDQ9Dtuby+dzfHtzAzP+J/3ZoOtSqq
+AHVTnCclU1pm/uHN0HZ5nL5iLJTvAgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIFoDAN
+BgkqhkiG9w0BAQUFAAOBgQA8K+BouEV04HRTdMZd3akjTQOm6aEGW4nIRnYIf8ZV
+mvUpLirVlX/unKtJinhGisFGpuYLMpemx17cnGkBeLCQRvHQjC+ho7l8/LOGheMS
+nvu0XHhvmJtRbm8MKHhogwZqHFDnXonvjyqhnhEtK5F2Fimcce3MoF2QtEe0UWv/
+8DGCAaowggGmAgEBMDQwLzEtMCsGA1UEAxMkMEFDOUYwMjYtRENFQS00QzEyLTkx
+MjctQzBGRDdEMkE4QjVBAgEBMAkGBSsOAwIaBQCggc0wEgYKYIZIAYb4RQEJAjEE
+EwIxOTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0x
+MjEwMTkwOTQ1NDdaMCAGCmCGSAGG+EUBCQUxEgQQ2EFUJdQNwQDxclIQ8qNyYzAj
+BgkqhkiG9w0BCQQxFgQUy8GFXPpAwRJUT3rdvNC9Pn+4eoswOAYKYIZIAYb4RQEJ
+BzEqEygwRkU3QzJEQTVEMDc2NzFFOTcxNDlCNUE3MDRCMERDNkM4MDYwRDJBMA0G
+CSqGSIb3DQEBAQUABIGAWUNdzvU2iiQOtihBwF0h48Nnw/2qX8uRjg6CVTOMcGji
+BxjUMifEbT//KJwljshl4y3yBLqeVYLOd04k6aKSdjgdZnrnUPI6p5tL5PfJkTAE
+L6qflZ9YCU5erE4T5U98hCQBMh4nOYxgaTjnZzhpkKQuEiKq/755cjzTzlI/eok=
+-----END PKCS7-----
+END
+ pki_message_content_pem = <<END
+-----BEGIN PKCS7-----
+MIIDawYJKoZIhvcNAQcDoIIDXDCCA1gCAQAxggEQMIIBDAIBADB1MHAxEDAOBgNV
+BAoMB2V4YW1wbGUxFzAVBgNVBAMMDlRBUk1BQyBST09UIENBMSIwIAYJKoZIhvcN
+AQkBFhNzb21lb25lQGV4YW1wbGUub3JnMQswCQYDVQQGEwJVUzESMBAGA1UEBwwJ
+VG93biBIYWxsAgFmMA0GCSqGSIb3DQEBAQUABIGAbKV17HvGYRtRRBNz1QLpW763
+UedhVj5KXi70o4BJGM04lItAgt6aFC9SruZjpWr1gCYKCaRSAg273DeGTQwsDoZ8
+6CPXzBpptYLz0MteQXYYWUaPZT+xmvx4NgDyk9P9MoT7JifsPrtXuzqCRFXhGdu8
+d/ru+OWxhHLvKH+bYekwggI9BgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECBNs2U5m
+Msd/gIICGFOnLq/EAc9Nv+HjKR3ZVPSJMq0TImjGf5Mvc3nDgI572Hdo2aku0YXM
+6WjSWkpYtxpg7Cqxfl6hPSefLPUnBqlIoM2qbrE7MSKEVD6+2bW9GqYPFVg4qQLL
+sOxnxJIMfOvLFfd7guL+iLH424XfiUUxaf8EdZE4u2IEl4REvkS1FoEGwyA4BEGM
+SeVPedQCbZ0qY7Pc2tmZE3XfEUhIsyStG0Nb6i6AKcAFYGapbgE6kAB0gwsYcHlW
+MOvsvdAfcTq6jwtHlO1s68qtvkWquTQ9lpX+fzddUUNxEHSqv5eU3oo6fT3Vj5ZF
+IVlaA5ThZMrI5PgRPuwJM4GL8/VLwY5mbDLFqn/irGeEvP99J3S87ornLLunjpxS
+y1/AymcVep2H32Tj82WS/IRQXBOzz4EnQRJGszKxAV6tY+Zje3sWyTTgObhlsiTQ
+TDgnvtSW8RvVHqKrwgkxxEsRHg7u8UdzZ0jg+O5+3F8B6/NWMyts0OaFqT9wvI8y
+O7VIy3dUtGdz7Hde6Ggp/iTn1LbgdJ3N8Hzxf1j6NMWUKHVsadvwpRJbUeqq9c3+
+QuxsJi8wWemxxQCE+tPyc1dP+ej5/M7bERbSOHMGgX03758IvP7A/fy2DjGPv2+l
+AwlEke0Uze1367QKgxM0nc3SZDlptY7zPIJC5saWXb8Rt2bw2JxEBOTavrp+ZwJ8
+tcH961onq8Tme2ICaCzk
+-----END PKCS7-----
+END
+ pki_msg = OpenSSL::PKCS7.new(pki_message_pem)
+ store = OpenSSL::X509::Store.new
+ pki_msg.verify(nil, store, nil, OpenSSL::PKCS7::NOVERIFY)
+ p7enc = OpenSSL::PKCS7.new(pki_msg.data)
+ assert_equal(pki_message_content_pem, p7enc.to_pem)
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkey_dh.rb b/jni/ruby/test/openssl/test_pkey_dh.rb
new file mode 100644
index 0000000..67dd3e7
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkey_dh.rb
@@ -0,0 +1,82 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestPKeyDH < Test::Unit::TestCase
+
+ NEW_KEYLEN = 256
+
+ def test_new
+ dh = OpenSSL::PKey::DH.new(NEW_KEYLEN)
+ assert_key(dh)
+ end
+
+ def test_new_break
+ assert_nil(OpenSSL::PKey::DH.new(NEW_KEYLEN) { break })
+ assert_raises(RuntimeError) do
+ OpenSSL::PKey::DH.new(NEW_KEYLEN) { raise }
+ end
+ end
+
+ def test_to_der
+ dh = OpenSSL::TestUtils::TEST_KEY_DH1024
+ der = dh.to_der
+ dh2 = OpenSSL::PKey::DH.new(der)
+ assert_equal_params(dh, dh2)
+ assert_no_key(dh2)
+ end
+
+ def test_to_pem
+ dh = OpenSSL::TestUtils::TEST_KEY_DH1024
+ pem = dh.to_pem
+ dh2 = OpenSSL::PKey::DH.new(pem)
+ assert_equal_params(dh, dh2)
+ assert_no_key(dh2)
+ end
+
+ def test_public_key
+ dh = OpenSSL::TestUtils::TEST_KEY_DH1024
+ public_key = dh.public_key
+ assert_no_key(public_key) #implies public_key.public? is false!
+ assert_equal(dh.to_der, public_key.to_der)
+ assert_equal(dh.to_pem, public_key.to_pem)
+ end
+
+ def test_generate_key
+ dh = OpenSSL::TestUtils::TEST_KEY_DH512_PUB.public_key # creates a copy
+ assert_no_key(dh)
+ dh.generate_key!
+ assert_key(dh)
+ end
+
+ def test_key_exchange
+ dh = OpenSSL::TestUtils::TEST_KEY_DH512_PUB
+ dh2 = dh.public_key
+ dh.generate_key!
+ dh2.generate_key!
+ assert_equal(dh.compute_key(dh2.pub_key), dh2.compute_key(dh.pub_key))
+ end
+
+ private
+
+ def assert_equal_params(dh1, dh2)
+ assert_equal(dh1.g, dh2.g)
+ assert_equal(dh1.p, dh2.p)
+ end
+
+ def assert_no_key(dh)
+ assert_equal(false, dh.public?)
+ assert_equal(false, dh.private?)
+ assert_equal(nil, dh.pub_key)
+ assert_equal(nil, dh.priv_key)
+ end
+
+ def assert_key(dh)
+ assert(dh.public?)
+ assert(dh.private?)
+ assert(dh.pub_key)
+ assert(dh.priv_key)
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkey_dsa.rb b/jni/ruby/test/openssl/test_pkey_dsa.rb
new file mode 100644
index 0000000..e4ea1b5
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkey_dsa.rb
@@ -0,0 +1,240 @@
+require_relative 'utils'
+require 'base64'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestPKeyDSA < Test::Unit::TestCase
+ def test_private
+ key = OpenSSL::PKey::DSA.new(256)
+ assert(key.private?)
+ key2 = OpenSSL::PKey::DSA.new(key.to_der)
+ assert(key2.private?)
+ key3 = key.public_key
+ assert(!key3.private?)
+ key4 = OpenSSL::PKey::DSA.new(key3.to_der)
+ assert(!key4.private?)
+ end
+
+ def test_new
+ key = OpenSSL::PKey::DSA.new 256
+ pem = key.public_key.to_pem
+ OpenSSL::PKey::DSA.new pem
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_new_break
+ assert_nil(OpenSSL::PKey::DSA.new(512) { break })
+ assert_raise(RuntimeError) do
+ OpenSSL::PKey::DSA.new(512) { raise }
+ end
+ end
+
+ def test_sys_sign_verify
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ data = 'Sign me!'
+ digest = OpenSSL::Digest::SHA1.digest(data)
+ sig = key.syssign(digest)
+ assert(key.sysverify(digest, sig))
+ end
+
+ def test_sign_verify
+ check_sign_verify(OpenSSL::Digest::DSS1.new)
+ end
+
+if (OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000)
+ def test_sign_verify_sha1
+ check_sign_verify(OpenSSL::Digest::SHA1.new)
+ end
+
+ def test_sign_verify_sha256
+ check_sign_verify(OpenSSL::Digest::SHA256.new)
+ end
+end
+
+ def test_digest_state_irrelevant_verify
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ digest1 = OpenSSL::Digest::DSS1.new
+ digest2 = OpenSSL::Digest::DSS1.new
+ data = 'Sign me!'
+ sig = key.sign(digest1, data)
+ digest1.reset
+ digest1 << 'Change state of digest1'
+ assert(key.verify(digest1, sig, data))
+ assert(key.verify(digest2, sig, data))
+ end
+
+ def test_read_DSA_PUBKEY
+ p = 7188211954100152441468596248707152960171255279130004340103875772401008316444412091945435731597638374542374929457672178957081124632837356913990200866056699
+ q = 957032439192465935099784319494405376402293318491
+ g = 122928973717064636255205666162891733518376475981809749897454444301389338825906076467196186192907631719698166056821519884939865041993585844526937010746285
+ y = 1235756183583465414789073313502727057075641172514181938731172021825149551960029708596057102104063395063907739571546165975727369183495540798749742124846271
+ algo = OpenSSL::ASN1::ObjectId.new('DSA')
+ params = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(p),
+ OpenSSL::ASN1::Integer.new(q),
+ OpenSSL::ASN1::Integer.new(g)])
+ algo_id = OpenSSL::ASN1::Sequence.new ([algo, params])
+ pub_key = OpenSSL::ASN1::Integer.new(y)
+ seq = OpenSSL::ASN1::Sequence.new([algo_id, OpenSSL::ASN1::BitString.new(pub_key.to_der)])
+ key = OpenSSL::PKey::DSA.new(seq.to_der)
+ assert(key.public?)
+ assert(!key.private?)
+ assert_equal(p, key.p)
+ assert_equal(q, key.q)
+ assert_equal(g, key.g)
+ assert_equal(y, key.pub_key)
+ assert_equal(nil, key.priv_key)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_DSAPublicKey_pem
+ p = 12260055936871293565827712385212529106400444521449663325576634579961635627321079536132296996623400607469624537382977152381984332395192110731059176842635699
+ q = 979494906553787301107832405790107343409973851677
+ g = 3731695366899846297271147240305742456317979984190506040697507048095553842519347835107669437969086119948785140453492839427038591924536131566350847469993845
+ y = 10505239074982761504240823422422813362721498896040719759460296306305851824586095328615844661273887569281276387605297130014564808567159023649684010036304695
+ pem = <<-EOF
+-----BEGIN DSA PUBLIC KEY-----
+MIHfAkEAyJSJ+g+P/knVcgDwwTzC7Pwg/pWs2EMd/r+lYlXhNfzg0biuXRul8VR4
+VUC/phySExY0PdcqItkR/xYAYNMbNwJBAOoV57X0FxKO/PrNa/MkoWzkCKV/hzhE
+p0zbFdsicw+hIjJ7S6Sd/FlDlo89HQZ2FuvWJ6wGLM1j00r39+F2qbMCFQCrkhIX
+SG+is37hz1IaBeEudjB2HQJAR0AloavBvtsng8obsjLb7EKnB+pSeHr/BdIQ3VH7
+fWLOqqkzFeRrYMDzUpl36XktY6Yq8EJYlW9pCMmBVNy/dQ==
+-----END DSA PUBLIC KEY-----
+ EOF
+ key = OpenSSL::PKey::DSA.new(pem)
+ assert(key.public?)
+ assert(!key.private?)
+ assert_equal(p, key.p)
+ assert_equal(q, key.q)
+ assert_equal(g, key.g)
+ assert_equal(y, key.pub_key)
+ assert_equal(nil, key.priv_key)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_DSA_PUBKEY_pem
+ p = 12260055936871293565827712385212529106400444521449663325576634579961635627321079536132296996623400607469624537382977152381984332395192110731059176842635699
+ q = 979494906553787301107832405790107343409973851677
+ g = 3731695366899846297271147240305742456317979984190506040697507048095553842519347835107669437969086119948785140453492839427038591924536131566350847469993845
+ y = 10505239074982761504240823422422813362721498896040719759460296306305851824586095328615844661273887569281276387605297130014564808567159023649684010036304695
+ pem = <<-EOF
+-----BEGIN PUBLIC KEY-----
+MIHxMIGoBgcqhkjOOAQBMIGcAkEA6hXntfQXEo78+s1r8yShbOQIpX+HOESnTNsV
+2yJzD6EiMntLpJ38WUOWjz0dBnYW69YnrAYszWPTSvf34XapswIVAKuSEhdIb6Kz
+fuHPUhoF4S52MHYdAkBHQCWhq8G+2yeDyhuyMtvsQqcH6lJ4ev8F0hDdUft9Ys6q
+qTMV5GtgwPNSmXfpeS1jpirwQliVb2kIyYFU3L91A0QAAkEAyJSJ+g+P/knVcgDw
+wTzC7Pwg/pWs2EMd/r+lYlXhNfzg0biuXRul8VR4VUC/phySExY0PdcqItkR/xYA
+YNMbNw==
+-----END PUBLIC KEY-----
+ EOF
+ key = OpenSSL::PKey::DSA.new(pem)
+ assert(key.public?)
+ assert(!key.private?)
+ assert_equal(p, key.p)
+ assert_equal(q, key.q)
+ assert_equal(g, key.g)
+ assert_equal(y, key.pub_key)
+ assert_equal(nil, key.priv_key)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_export_format_is_DSA_PUBKEY_pem
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ pem = key.public_key.to_pem
+ pem.gsub!(/^-+(\w|\s)+-+$/, "") # eliminate --------BEGIN...-------
+ asn1 = OpenSSL::ASN1.decode(Base64.decode64(pem))
+ assert_equal(OpenSSL::ASN1::SEQUENCE, asn1.tag)
+ assert_equal(2, asn1.value.size)
+ seq = asn1.value
+ assert_equal(OpenSSL::ASN1::SEQUENCE, seq[0].tag)
+ assert_equal(2, seq[0].value.size)
+ algo_id = seq[0].value
+ assert_equal(OpenSSL::ASN1::OBJECT, algo_id[0].tag)
+ assert_equal('DSA', algo_id[0].value)
+ assert_equal(OpenSSL::ASN1::SEQUENCE, algo_id[1].tag)
+ assert_equal(3, algo_id[1].value.size)
+ params = algo_id[1].value
+ assert_equal(OpenSSL::ASN1::INTEGER, params[0].tag)
+ assert_equal(key.p, params[0].value)
+ assert_equal(OpenSSL::ASN1::INTEGER, params[1].tag)
+ assert_equal(key.q, params[1].value)
+ assert_equal(OpenSSL::ASN1::INTEGER, params[2].tag)
+ assert_equal(key.g, params[2].value)
+ assert_equal(OpenSSL::ASN1::BIT_STRING, seq[1].tag)
+ assert_equal(0, seq[1].unused_bits)
+ pub_key = OpenSSL::ASN1.decode(seq[1].value)
+ assert_equal(OpenSSL::ASN1::INTEGER, pub_key.tag)
+ assert_equal(key.pub_key, pub_key.value)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_private_key_der
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ der = key.to_der
+ key2 = OpenSSL::PKey.read(der)
+ assert(key2.private?)
+ assert_equal(der, key2.to_der)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_private_key_pem
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ pem = key.to_pem
+ key2 = OpenSSL::PKey.read(pem)
+ assert(key2.private?)
+ assert_equal(pem, key2.to_pem)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_public_key_der
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256.public_key
+ der = key.to_der
+ key2 = OpenSSL::PKey.read(der)
+ assert(!key2.private?)
+ assert_equal(der, key2.to_der)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_public_key_pem
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256.public_key
+ pem = key.to_pem
+ key2 = OpenSSL::PKey.read(pem)
+ assert(!key2.private?)
+ assert_equal(pem, key2.to_pem)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_private_key_pem_pw
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ pem = key.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret')
+ #callback form for password
+ key2 = OpenSSL::PKey.read(pem) do
+ 'secret'
+ end
+ assert(key2.private?)
+ # pass password directly
+ key2 = OpenSSL::PKey.read(pem, 'secret')
+ assert(key2.private?)
+ #omit pem equality check, will be different due to cipher iv
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_export_password_length
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ assert_raise(OpenSSL::OpenSSLError) do
+ key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec')
+ end
+ pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr')
+ assert(pem)
+ end
+
+ private
+
+ def check_sign_verify(digest)
+ key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ data = 'Sign me!'
+ sig = key.sign(digest, data)
+ assert(key.verify(digest, sig, data))
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkey_ec.rb b/jni/ruby/test/openssl/test_pkey_ec.rb
new file mode 100644
index 0000000..1693ace
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkey_ec.rb
@@ -0,0 +1,211 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::PKey::EC)
+
+class OpenSSL::TestEC < Test::Unit::TestCase
+ def setup
+ @data1 = 'foo'
+ @data2 = 'bar' * 1000 # data too long for DSA sig
+
+ @groups = []
+ @keys = []
+
+ OpenSSL::PKey::EC.builtin_curves.each do |curve, comment|
+ next if curve.start_with?("Oakley") # Oakley curves are not suitable for ECDSA
+ group = OpenSSL::PKey::EC::Group.new(curve)
+
+ key = OpenSSL::PKey::EC.new(group)
+ key.generate_key
+
+ @groups << group
+ @keys << key
+ end
+ end
+
+ def compare_keys(k1, k2)
+ assert_equal(k1.to_pem, k2.to_pem)
+ end
+
+ def test_builtin_curves
+ assert(!OpenSSL::PKey::EC.builtin_curves.empty?)
+ end
+
+ def test_curve_names
+ @groups.each_with_index do |group, idx|
+ key = @keys[idx]
+ assert_equal(group.curve_name, key.group.curve_name)
+ end
+ end
+
+ def test_check_key
+ for key in @keys
+ assert_equal(key.check_key, true)
+ assert_equal(key.private_key?, true)
+ assert_equal(key.public_key?, true)
+ end
+ end
+
+ def test_group_encoding
+ for group in @groups
+ for meth in [:to_der, :to_pem]
+ txt = group.send(meth)
+ gr = OpenSSL::PKey::EC::Group.new(txt)
+
+ assert_equal(txt, gr.send(meth))
+
+ assert_equal(group.generator.to_bn, gr.generator.to_bn)
+ assert_equal(group.cofactor, gr.cofactor)
+ assert_equal(group.order, gr.order)
+ assert_equal(group.seed, gr.seed)
+ assert_equal(group.degree, gr.degree)
+ end
+ end
+ end
+
+ def test_key_encoding
+ for key in @keys
+ group = key.group
+
+ for meth in [:to_der, :to_pem]
+ txt = key.send(meth)
+ assert_equal(txt, OpenSSL::PKey::EC.new(txt).send(meth))
+ end
+
+ bn = key.public_key.to_bn
+ assert_equal(bn, OpenSSL::PKey::EC::Point.new(group, bn).to_bn)
+ end
+ end
+
+ def test_set_keys
+ for key in @keys
+ k = OpenSSL::PKey::EC.new
+ k.group = key.group
+ k.private_key = key.private_key
+ k.public_key = key.public_key
+
+ compare_keys(key, k)
+ end
+ end
+
+ def test_dsa_sign_verify
+ for key in @keys
+ sig = key.dsa_sign_asn1(@data1)
+ assert(key.dsa_verify_asn1(@data1, sig))
+ end
+ end
+
+ def test_dsa_sign_asn1_FIPS186_3
+ for key in @keys
+ size = key.group.order.num_bits / 8 + 1
+ dgst = (1..size).to_a.pack('C*')
+ begin
+ sig = key.dsa_sign_asn1(dgst)
+ # dgst is auto-truncated according to FIPS186-3 after openssl-0.9.8m
+ assert(key.dsa_verify_asn1(dgst + "garbage", sig))
+ rescue OpenSSL::PKey::ECError => e
+ # just an exception for longer dgst before openssl-0.9.8m
+ assert_equal('ECDSA_sign: data too large for key size', e.message)
+ # no need to do following tests
+ return
+ end
+ end
+ end
+
+ def test_dh_compute_key
+ for key in @keys
+ k = OpenSSL::PKey::EC.new(key.group)
+ k.generate_key
+
+ puba = key.public_key
+ pubb = k.public_key
+ a = key.dh_compute_key(pubb)
+ b = k.dh_compute_key(puba)
+ assert_equal(a, b)
+ end
+ end
+
+ def test_read_private_key_der
+ ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+ der = ec.to_der
+ ec2 = OpenSSL::PKey.read(der)
+ assert(ec2.private_key?)
+ assert_equal(der, ec2.to_der)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_private_key_pem
+ ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+ pem = ec.to_pem
+ ec2 = OpenSSL::PKey.read(pem)
+ assert(ec2.private_key?)
+ assert_equal(pem, ec2.to_pem)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_public_key_der
+ ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+ ec2 = OpenSSL::PKey::EC.new(ec.group)
+ ec2.public_key = ec.public_key
+ der = ec2.to_der
+ ec3 = OpenSSL::PKey.read(der)
+ assert(!ec3.private_key?)
+ assert_equal(der, ec3.to_der)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_public_key_pem
+ ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+ ec2 = OpenSSL::PKey::EC.new(ec.group)
+ ec2.public_key = ec.public_key
+ pem = ec2.to_pem
+ ec3 = OpenSSL::PKey.read(pem)
+ assert(!ec3.private_key?)
+ assert_equal(pem, ec3.to_pem)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_private_key_pem_pw
+ ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+ pem = ec.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret')
+ #callback form for password
+ ec2 = OpenSSL::PKey.read(pem) do
+ 'secret'
+ end
+ assert(ec2.private_key?)
+ # pass password directly
+ ec2 = OpenSSL::PKey.read(pem, 'secret')
+ assert(ec2.private_key?)
+ #omit pem equality check, will be different due to cipher iv
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_export_password_length
+ key = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+ assert_raise(OpenSSL::OpenSSLError) do
+ key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec')
+ end
+ pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr')
+ assert(pem)
+ end
+
+ def test_ec_point_mul
+ ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+ p1 = ec.public_key
+ bn1 = OpenSSL::BN.new('10')
+ bn2 = OpenSSL::BN.new('20')
+
+ p2 = p1.mul(bn1)
+ assert(p1.group == p2.group)
+ p2 = p1.mul(bn1, bn2)
+ assert(p1.group == p2.group)
+ p2 = p1.mul([bn1, bn2], [p1])
+ assert(p1.group == p2.group)
+ p2 = p1.mul([bn1, bn2], [p1], bn2)
+ assert(p1.group == p2.group)
+ end
+
+# test Group: asn1_flag, point_conversion
+
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkey_rsa.rb b/jni/ruby/test/openssl/test_pkey_rsa.rb
new file mode 100644
index 0000000..ea042c2
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkey_rsa.rb
@@ -0,0 +1,313 @@
+require_relative 'utils'
+require 'base64'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestPKeyRSA < Test::Unit::TestCase
+ def test_padding
+ key = OpenSSL::PKey::RSA.new(512, 3)
+
+ # Need right size for raw mode
+ plain0 = "x" * (512/8)
+ cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING)
+ plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING)
+ assert_equal(plain0, plain1)
+
+ # Need smaller size for pkcs1 mode
+ plain0 = "x" * (512/8 - 11)
+ cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING)
+ plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING)
+ assert_equal(plain0, plain1)
+
+ cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default
+ plain1 = key.public_decrypt(cipherdef)
+ assert_equal(plain0, plain1)
+ assert_equal(cipher1, cipherdef)
+
+ # Failure cases
+ assert_raise(ArgumentError){ key.private_encrypt() }
+ assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) }
+ assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) }
+ end
+
+ def test_private
+ key = OpenSSL::PKey::RSA.new(512, 3)
+ assert(key.private?)
+ key2 = OpenSSL::PKey::RSA.new(key.to_der)
+ assert(key2.private?)
+ key3 = key.public_key
+ assert(!key3.private?)
+ key4 = OpenSSL::PKey::RSA.new(key3.to_der)
+ assert(!key4.private?)
+ end
+
+ def test_new
+ key = OpenSSL::PKey::RSA.new 512
+ pem = key.public_key.to_pem
+ OpenSSL::PKey::RSA.new pem
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_new_exponent_default
+ assert_equal(65537, OpenSSL::PKey::RSA.new(512).e)
+ end
+
+ def test_new_with_exponent
+ 1.upto(30) do |idx|
+ e = (2 ** idx) + 1
+ key = OpenSSL::PKey::RSA.new(512, e)
+ assert_equal(e, key.e)
+ end
+ end
+
+ def test_new_break
+ assert_nil(OpenSSL::PKey::RSA.new(1024) { break })
+ assert_raise(RuntimeError) do
+ OpenSSL::PKey::RSA.new(1024) { raise }
+ end
+ end
+
+ def test_sign_verify
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ digest = OpenSSL::Digest::SHA1.new
+ data = 'Sign me!'
+ sig = key.sign(digest, data)
+ assert(key.verify(digest, sig, data))
+ end
+
+ def test_sign_verify_memory_leak
+ bug9743 = '[ruby-core:62038] [Bug #9743]'
+ assert_no_memory_leak(%w[-ropenssl], <<-PREP, <<-CODE, bug9743, rss: true, timeout: 30)
+ data = 'Sign me!'
+ digest = OpenSSL::Digest::SHA512.new
+ pkey = OpenSSL::PKey::RSA.new(2048)
+ signature = pkey.sign(digest, data)
+ pub_key = pkey.public_key
+ PREP
+ 20_000.times {
+ pub_key.verify(digest, signature, data)
+ }
+ CODE
+
+ assert_no_memory_leak(%w[-ropenssl], <<-PREP, <<-CODE, bug9743, rss: true, timeout: 30)
+ data = 'Sign me!'
+ digest = OpenSSL::Digest::SHA512.new
+ pkey = OpenSSL::PKey::RSA.new(2048)
+ signature = pkey.sign(digest, data)
+ pub_key = pkey.public_key
+ PREP
+ 20_000.times {
+ begin
+ pub_key.verify(digest, signature, 1)
+ rescue TypeError
+ end
+ }
+ CODE
+ end
+
+ def test_digest_state_irrelevant_sign
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ digest1 = OpenSSL::Digest::SHA1.new
+ digest2 = OpenSSL::Digest::SHA1.new
+ data = 'Sign me!'
+ digest1 << 'Change state of digest1'
+ sig1 = key.sign(digest1, data)
+ sig2 = key.sign(digest2, data)
+ assert_equal(sig1, sig2)
+ end
+
+ def test_digest_state_irrelevant_verify
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ digest1 = OpenSSL::Digest::SHA1.new
+ digest2 = OpenSSL::Digest::SHA1.new
+ data = 'Sign me!'
+ sig = key.sign(digest1, data)
+ digest1.reset
+ digest1 << 'Change state of digest1'
+ assert(key.verify(digest1, sig, data))
+ assert(key.verify(digest2, sig, data))
+ end
+
+ def test_read_RSAPublicKey
+ modulus = 10664264882656732240315063514678024569492171560814833397008094754351396057398262071307709191731289492697968568138092052265293364132872019762410446076526351
+ exponent = 65537
+ seq = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)])
+ key = OpenSSL::PKey::RSA.new(seq.to_der)
+ assert(key.public?)
+ assert(!key.private?)
+ assert_equal(modulus, key.n)
+ assert_equal(exponent, key.e)
+ assert_equal(nil, key.d)
+ assert_equal(nil, key.p)
+ assert_equal(nil, key.q)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_RSA_PUBKEY
+ modulus = 10664264882656732240315063514678024569492171560814833397008094754351396057398262071307709191731289492697968568138092052265293364132872019762410446076526351
+ exponent = 65537
+ algo = OpenSSL::ASN1::ObjectId.new('rsaEncryption')
+ null_params = OpenSSL::ASN1::Null.new(nil)
+ algo_id = OpenSSL::ASN1::Sequence.new ([algo, null_params])
+ pub_key = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)])
+ seq = OpenSSL::ASN1::Sequence.new([algo_id, OpenSSL::ASN1::BitString.new(pub_key.to_der)])
+ key = OpenSSL::PKey::RSA.new(seq.to_der)
+ assert(key.public?)
+ assert(!key.private?)
+ assert_equal(modulus, key.n)
+ assert_equal(exponent, key.e)
+ assert_equal(nil, key.d)
+ assert_equal(nil, key.p)
+ assert_equal(nil, key.q)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_RSAPublicKey_pem
+ modulus = 9416340886363418692990906464787534854462163316648195510702927337693641649864839352187127240942127674615733815606532506566068276485089353644309497938966061
+ exponent = 65537
+ pem = <<-EOF
+-----BEGIN RSA PUBLIC KEY-----
+MEgCQQCzyh2RIZK62E2PbTWqUljD+K23XR9AGBKNtXjal6WD2yRGcLqzPJLNCa60
+AudJR1JobbIbDJrQu6AXnWh5k/YtAgMBAAE=
+-----END RSA PUBLIC KEY-----
+ EOF
+ key = OpenSSL::PKey::RSA.new(pem)
+ assert(key.public?)
+ assert(!key.private?)
+ assert_equal(modulus, key.n)
+ assert_equal(exponent, key.e)
+ assert_equal(nil, key.d)
+ assert_equal(nil, key.p)
+ assert_equal(nil, key.q)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_RSA_PUBKEY_pem
+ modulus = 9416340886363418692990906464787534854462163316648195510702927337693641649864839352187127240942127674615733815606532506566068276485089353644309497938966061
+ exponent = 65537
+ pem = <<-EOF
+-----BEGIN PUBLIC KEY-----
+MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALPKHZEhkrrYTY9tNapSWMP4rbdd
+H0AYEo21eNqXpYPbJEZwurM8ks0JrrQC50lHUmhtshsMmtC7oBedaHmT9i0C
+AwEAAQ==
+-----END PUBLIC KEY-----
+ EOF
+ key = OpenSSL::PKey::RSA.new(pem)
+ assert(key.public?)
+ assert(!key.private?)
+ assert_equal(modulus, key.n)
+ assert_equal(exponent, key.e)
+ assert_equal(nil, key.d)
+ assert_equal(nil, key.p)
+ assert_equal(nil, key.q)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_export_format_is_RSA_PUBKEY
+ key = OpenSSL::PKey::RSA.new(512)
+ asn1 = OpenSSL::ASN1.decode(key.public_key.to_der)
+ check_PUBKEY(asn1, key)
+ end
+
+ def test_export_format_is_RSA_PUBKEY_pem
+ key = OpenSSL::PKey::RSA.new(512)
+ pem = key.public_key.to_pem
+ pem.gsub!(/^-+(\w|\s)+-+$/, "") # eliminate --------BEGIN...-------
+ asn1 = OpenSSL::ASN1.decode(Base64.decode64(pem))
+ check_PUBKEY(asn1, key)
+ end
+
+ def test_read_private_key_der
+ der = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_der
+ key = OpenSSL::PKey.read(der)
+ assert(key.private?)
+ assert_equal(der, key.to_der)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_private_key_pem
+ pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem
+ key = OpenSSL::PKey.read(pem)
+ assert(key.private?)
+ assert_equal(pem, key.to_pem)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_public_key_der
+ der = OpenSSL::TestUtils::TEST_KEY_RSA1024.public_key.to_der
+ key = OpenSSL::PKey.read(der)
+ assert(!key.private?)
+ assert_equal(der, key.to_der)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_public_key_pem
+ pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.public_key.to_pem
+ key = OpenSSL::PKey.read(pem)
+ assert(!key.private?)
+ assert_equal(pem, key.to_pem)
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_private_key_pem_pw
+ pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret')
+ #callback form for password
+ key = OpenSSL::PKey.read(pem) do
+ 'secret'
+ end
+ assert(key.private?)
+ # pass password directly
+ key = OpenSSL::PKey.read(pem, 'secret')
+ assert(key.private?)
+ #omit pem equality check, will be different due to cipher iv
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_read_private_key_pem_pw_exception
+ pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret')
+ # it raises an ArgumentError from PEM reading. The exception raised inside are ignored for now.
+ assert_raise(ArgumentError) do
+ OpenSSL::PKey.read(pem) do
+ raise RuntimeError
+ end
+ end
+ assert_equal([], OpenSSL.errors)
+ end
+
+ def test_export_password_length
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ assert_raise(OpenSSL::OpenSSLError) do
+ key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec')
+ end
+ pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr')
+ assert(pem)
+ end
+
+ private
+
+ def check_PUBKEY(asn1, key)
+ assert_equal(OpenSSL::ASN1::SEQUENCE, asn1.tag)
+ assert_equal(2, asn1.value.size)
+ seq = asn1.value
+ assert_equal(OpenSSL::ASN1::SEQUENCE, seq[0].tag)
+ assert_equal(2, seq[0].value.size)
+ algo_id = seq[0].value
+ assert_equal(OpenSSL::ASN1::OBJECT, algo_id[0].tag)
+ assert_equal('rsaEncryption', algo_id[0].value)
+ assert_equal(OpenSSL::ASN1::NULL, algo_id[1].tag)
+ assert_equal(nil, algo_id[1].value)
+ assert_equal(OpenSSL::ASN1::BIT_STRING, seq[1].tag)
+ assert_equal(0, seq[1].unused_bits)
+ pub_key = OpenSSL::ASN1.decode(seq[1].value)
+ assert_equal(OpenSSL::ASN1::SEQUENCE, pub_key.tag)
+ assert_equal(2, pub_key.value.size)
+ assert_equal(OpenSSL::ASN1::INTEGER, pub_key.value[0].tag)
+ assert_equal(key.n, pub_key.value[0].value)
+ assert_equal(OpenSSL::ASN1::INTEGER, pub_key.value[1].tag)
+ assert_equal(key.e, pub_key.value[1].value)
+ assert_equal([], OpenSSL.errors)
+ end
+
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_ssl.rb b/jni/ruby/test/openssl/test_ssl.rb
new file mode 100644
index 0000000..8aa18e5
--- /dev/null
+++ b/jni/ruby/test/openssl/test_ssl.rb
@@ -0,0 +1,933 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestSSL < OpenSSL::SSLTestCase
+
+ def test_ctx_setup
+ ctx = OpenSSL::SSL::SSLContext.new
+ assert_equal(ctx.setup, true)
+ assert_equal(ctx.setup, nil)
+ end
+
+ def test_ctx_setup_no_compression
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_COMPRESSION
+ assert_equal(ctx.setup, true)
+ assert_equal(ctx.setup, nil)
+ assert_equal(OpenSSL::SSL::OP_NO_COMPRESSION,
+ ctx.options & OpenSSL::SSL::OP_NO_COMPRESSION)
+ end if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
+
+ def test_ctx_setup_with_extra_chain_cert
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.extra_chain_cert = [@ca_cert, @cli_cert]
+ assert_equal(ctx.setup, true)
+ assert_equal(ctx.setup, nil)
+ end
+
+ def test_not_started_session
+ skip "non socket argument of SSLSocket.new is not supported on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM
+ open(__FILE__) do |f|
+ assert_nil OpenSSL::SSL::SSLSocket.new(f).cert
+ end
+ end
+
+ def test_ssl_gets
+ start_server(OpenSSL::SSL::VERIFY_NONE, true) { |server, port|
+ server_connect(port) { |ssl|
+ ssl.write "abc\n"
+ IO.select [ssl]
+
+ line = ssl.gets
+
+ assert_equal "abc\n", line
+ assert_equal Encoding::BINARY, line.encoding
+ }
+ }
+ end
+
+ def test_ssl_read_nonblock
+ start_server(OpenSSL::SSL::VERIFY_NONE, true) { |server, port|
+ server_connect(port) { |ssl|
+ assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) }
+ ssl.write("abc\n")
+ IO.select [ssl]
+ assert_equal('a', ssl.read_nonblock(1))
+ assert_equal("bc\n", ssl.read_nonblock(100))
+ assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) }
+ }
+ }
+ end
+
+ def test_connect_and_close
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ assert(ssl.connect)
+ ssl.close
+ assert(!sock.closed?)
+ sock.close
+
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.sync_close = true # !!
+ assert(ssl.connect)
+ ssl.close
+ assert(sock.closed?)
+ }
+ end
+
+ def test_read_and_write
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ server_connect(port) { |ssl|
+ # syswrite and sysread
+ ITERATIONS.times{|i|
+ str = "x" * 100 + "\n"
+ ssl.syswrite(str)
+ newstr = ''
+ newstr << ssl.sysread(str.size - newstr.size) until newstr.size == str.size
+ assert_equal(str, newstr)
+
+ str = "x" * i * 100 + "\n"
+ buf = ""
+ ssl.syswrite(str)
+ assert_equal(buf.object_id, ssl.sysread(str.size, buf).object_id)
+ newstr = buf
+ newstr << ssl.sysread(str.size - newstr.size) until newstr.size == str.size
+ assert_equal(str, newstr)
+ }
+
+ # puts and gets
+ ITERATIONS.times{
+ str = "x" * 100 + "\n"
+ ssl.puts(str)
+ assert_equal(str, ssl.gets)
+
+ str = "x" * 100
+ ssl.puts(str)
+ assert_equal(str, ssl.gets("\n", 100))
+ assert_equal("\n", ssl.gets)
+ }
+
+ # read and write
+ ITERATIONS.times{|i|
+ str = "x" * 100 + "\n"
+ ssl.write(str)
+ assert_equal(str, ssl.read(str.size))
+
+ str = "x" * i * 100 + "\n"
+ buf = ""
+ ssl.write(str)
+ assert_equal(buf.object_id, ssl.read(str.size, buf).object_id)
+ assert_equal(str, buf)
+ }
+ }
+ }
+ end
+
+ def test_client_auth_failure
+ vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+ start_server(vflag, true, :ignore_listener_error => true){|server, port|
+ assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET){
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.sync_close = true
+ begin
+ ssl.connect
+ ensure
+ ssl.close
+ end
+ }
+ }
+ end
+
+ def test_client_auth_success
+ vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+ start_server(vflag, true){|server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.key = @cli_key
+ ctx.cert = @cli_cert
+
+ server_connect(port, ctx) { |ssl|
+ ssl.puts("foo")
+ assert_equal("foo\n", ssl.gets)
+ }
+
+ called = nil
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.client_cert_cb = Proc.new{ |sslconn|
+ called = true
+ [@cli_cert, @cli_key]
+ }
+
+ server_connect(port, ctx) { |ssl|
+ assert(called)
+ ssl.puts("foo")
+ assert_equal("foo\n", ssl.gets)
+ }
+ }
+ end
+
+ def test_client_ca
+ ctx_proc = Proc.new do |ctx|
+ ctx.client_ca = [@ca_cert]
+ end
+
+ vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+ start_server(vflag, true, :ctx_proc => ctx_proc){|server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ client_ca_from_server = nil
+ ctx.client_cert_cb = Proc.new do |sslconn|
+ client_ca_from_server = sslconn.client_ca
+ [@cli_cert, @cli_key]
+ end
+ server_connect(port, ctx) { |ssl| assert_equal([@ca], client_ca_from_server) }
+ }
+ end
+
+ def test_read_nonblock_without_session
+ OpenSSL::TestUtils.silent do
+ start_server(OpenSSL::SSL::VERIFY_NONE, false){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.sync_close = true
+
+ assert_equal :wait_readable, ssl.read_nonblock(100, exception: false)
+ ssl.write("abc\n")
+ IO.select [ssl]
+ assert_equal('a', ssl.read_nonblock(1))
+ assert_equal("bc\n", ssl.read_nonblock(100))
+ assert_equal :wait_readable, ssl.read_nonblock(100, exception: false)
+ ssl.close
+ }
+ end
+ end
+
+ def test_starttls
+ OpenSSL::TestUtils.silent do
+ start_server(OpenSSL::SSL::VERIFY_NONE, false){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.sync_close = true
+ str = "x" * 1000 + "\n"
+
+ ITERATIONS.times{
+ ssl.puts(str)
+ assert_equal(str, ssl.gets)
+ }
+ starttls(ssl)
+
+ ITERATIONS.times{
+ ssl.puts(str)
+ assert_equal(str, ssl.gets)
+ }
+
+ ssl.close
+ }
+ end
+ end
+
+ def test_parallel
+ GC.start
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ ssls = []
+ 10.times{
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.connect
+ ssl.sync_close = true
+ ssls << ssl
+ }
+ str = "x" * 1000 + "\n"
+ ITERATIONS.times{
+ ssls.each{|ssl|
+ ssl.puts(str)
+ assert_equal(str, ssl.gets)
+ }
+ }
+ ssls.each{|ssl| ssl.close }
+ }
+ end
+
+ def test_verify_result
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ begin
+ assert_raise(OpenSSL::SSL::SSLError){ ssl.connect }
+ assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result)
+ ensure
+ ssl.close
+ end
+ }
+
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params(
+ :verify_callback => Proc.new do |preverify_ok, store_ctx|
+ store_ctx.error = OpenSSL::X509::V_OK
+ true
+ end
+ )
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ begin
+ ssl.connect
+ assert_equal(OpenSSL::X509::V_OK, ssl.verify_result)
+ ensure
+ ssl.close
+ end
+ }
+
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params(
+ :verify_callback => Proc.new do |preverify_ok, store_ctx|
+ store_ctx.error = OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION
+ false
+ end
+ )
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ begin
+ assert_raise(OpenSSL::SSL::SSLError){ ssl.connect }
+ assert_equal(OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION, ssl.verify_result)
+ ensure
+ ssl.close
+ end
+ }
+ end
+
+ def test_exception_in_verify_callback_is_ignored
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params(
+ :verify_callback => Proc.new do |preverify_ok, store_ctx|
+ store_ctx.error = OpenSSL::X509::V_OK
+ raise RuntimeError
+ end
+ )
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ begin
+ OpenSSL::TestUtils.silent do
+ # SSLError, not RuntimeError
+ assert_raise(OpenSSL::SSL::SSLError) { ssl.connect }
+ end
+ assert_equal(OpenSSL::X509::V_ERR_CERT_REJECTED, ssl.verify_result)
+ ensure
+ ssl.close
+ end
+ }
+ end
+
+ def test_sslctx_set_params
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params
+ assert_equal(OpenSSL::SSL::VERIFY_PEER, ctx.verify_mode)
+ assert_equal(OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options], ctx.options)
+ ciphers = ctx.ciphers
+ ciphers_versions = ciphers.collect{|_, v, _, _| v }
+ ciphers_names = ciphers.collect{|v, _, _, _| v }
+ assert(ciphers_names.all?{|v| /ADH/ !~ v })
+ assert(ciphers_versions.all?{|v| /SSLv2/ !~ v })
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ begin
+ assert_raise(OpenSSL::SSL::SSLError){ ssl.connect }
+ assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result)
+ ensure
+ ssl.close
+ end
+ }
+ end
+
+ def test_post_connect_check_with_anon_ciphers
+ sslerr = OpenSSL::SSL::SSLError
+
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, {use_anon_cipher: true}){|server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ciphers = "aNULL"
+ server_connect(port, ctx) { |ssl|
+ msg = "Peer verification enabled, but no certificate received. Anonymous cipher suite " \
+ "ADH-AES256-GCM-SHA384 was negotiated. Anonymous suites must be disabled to use peer verification."
+ assert_raise_with_message(sslerr,msg){ssl.post_connection_check("localhost.localdomain")}
+ }
+ }
+ end
+
+ def test_post_connection_check
+ sslerr = OpenSSL::SSL::SSLError
+
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ server_connect(port) { |ssl|
+ assert_raise(sslerr){ssl.post_connection_check("localhost.localdomain")}
+ assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")}
+ assert(ssl.post_connection_check("localhost"))
+ assert_raise(sslerr){ssl.post_connection_check("foo.example.com")}
+
+ cert = ssl.peer_cert
+ assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain"))
+ assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1"))
+ assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost"))
+ assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com"))
+ }
+ }
+
+ now = Time.now
+ exts = [
+ ["keyUsage","keyEncipherment,digitalSignature",true],
+ ["subjectAltName","DNS:localhost.localdomain",false],
+ ["subjectAltName","IP:127.0.0.1",false],
+ ]
+ @svr_cert = issue_cert(@svr, @svr_key, 4, now, now+1800, exts,
+ @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ server_connect(port) { |ssl|
+ assert(ssl.post_connection_check("localhost.localdomain"))
+ assert(ssl.post_connection_check("127.0.0.1"))
+ assert_raise(sslerr){ssl.post_connection_check("localhost")}
+ assert_raise(sslerr){ssl.post_connection_check("foo.example.com")}
+
+ cert = ssl.peer_cert
+ assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain"))
+ assert(OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1"))
+ assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost"))
+ assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com"))
+ }
+ }
+
+ now = Time.now
+ exts = [
+ ["keyUsage","keyEncipherment,digitalSignature",true],
+ ["subjectAltName","DNS:*.localdomain",false],
+ ]
+ @svr_cert = issue_cert(@svr, @svr_key, 5, now, now+1800, exts,
+ @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ server_connect(port) { |ssl|
+ assert(ssl.post_connection_check("localhost.localdomain"))
+ assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")}
+ assert_raise(sslerr){ssl.post_connection_check("localhost")}
+ assert_raise(sslerr){ssl.post_connection_check("foo.example.com")}
+ cert = ssl.peer_cert
+ assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain"))
+ assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1"))
+ assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost"))
+ assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com"))
+ }
+ }
+ end
+
+ def test_verify_certificate_identity
+ [true, false].each do |criticality|
+ cert = create_null_byte_SAN_certificate(criticality)
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, 'www.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, "www.example.com\0.evil.com"))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.255'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.1'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '13::17'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '13:0:0:0:0:0:0:17'))
+ end
+ end
+
+ def test_verify_hostname
+ assert_equal(true, OpenSSL::SSL.verify_hostname("www.example.com", "*.example.com"))
+ assert_equal(false, OpenSSL::SSL.verify_hostname("www.subdomain.example.com", "*.example.com"))
+ end
+
+ def test_verify_wildcard
+ assert_equal(false, OpenSSL::SSL.verify_wildcard("foo", "x*"))
+ assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "foo"))
+ assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "f*"))
+ assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "*"))
+ assert_equal(false, OpenSSL::SSL.verify_wildcard("abc*bcd", "abcd"))
+ assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "x*"))
+ assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "*--qdk4b9b"))
+ assert_equal(true, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b"))
+ end
+
+ # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27
+ def test_post_connection_check_wildcard_san
+ # case-insensitive ASCII comparison
+ # RFC 6125, section 6.4.1
+ #
+ # "..matching of the reference identifier against the presented identifier
+ # is performed by comparing the set of domain name labels using a
+ # case-insensitive ASCII comparison, as clarified by [DNS-CASE] (e.g.,
+ # "WWW.Example.Com" would be lower-cased to "www.example.com" for
+ # comparison purposes)
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*.example.com'), 'www.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*.Example.COM'), 'www.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*.example.com'), 'WWW.Example.COM'))
+ # 1. The client SHOULD NOT attempt to match a presented identifier in
+ # which the wildcard character comprises a label other than the
+ # left-most label (e.g., do not match bar.*.example.net).
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:www.*.com'), 'www.example.com'))
+ # 2. If the wildcard character is the only character of the left-most
+ # label in the presented identifier, the client SHOULD NOT compare
+ # against anything but the left-most label of the reference
+ # identifier (e.g., *.example.com would match foo.example.com but
+ # not bar.foo.example.com or example.com).
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*.example.com'), 'foo.example.com'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*.example.com'), 'bar.foo.example.com'))
+ # 3. The client MAY match a presented identifier in which the wildcard
+ # character is not the only character of the label (e.g.,
+ # baz*.example.net and *baz.example.net and b*z.example.net would
+ # be taken to match baz1.example.net and foobaz.example.net and
+ # buzz.example.net, respectively). ...
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:b*z.example.com'), 'buzz.example.com'))
+ # Section 6.4.3 of RFC6125 states that client should NOT match identifier
+ # where wildcard is other than left-most label.
+ #
+ # Also implicitly mentions the wildcard character only in singular form,
+ # and discourages matching against more than one wildcard.
+ #
+ # See RFC 6125, section 7.2, subitem 2.
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*b*.example.com'), 'abc.example.com'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*b*.example.com'), 'ab.example.com'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:*b*.example.com'), 'bc.example.com'))
+ # ... However, the client SHOULD NOT
+ # attempt to match a presented identifier where the wildcard
+ # character is embedded within an A-label or U-label [IDNA-DEFS] of
+ # an internationalized domain name [IDNA-PROTO].
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:xn*.example.com'), 'xn1ca.example.com'))
+ # part of A-label
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:xn--*.example.com'), 'xn--1ca.example.com'))
+ # part of U-label
+ # dNSName in RFC5280 is an IA5String so U-label should NOT be allowed
+ # regardless of wildcard.
+ #
+ # See Section 7.2 of RFC 5280:
+ # IA5String is limited to the set of ASCII characters.
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_san('DNS:á*.example.com'), 'á1.example.com'))
+ end
+
+ def test_post_connection_check_wildcard_cn
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*.example.com'), 'www.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*.Example.COM'), 'www.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*.example.com'), 'WWW.Example.COM'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('www.*.com'), 'www.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*.example.com'), 'foo.example.com'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*.example.com'), 'bar.foo.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('baz*.example.com'), 'baz1.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*baz.example.com'), 'foobaz.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('b*z.example.com'), 'buzz.example.com'))
+ # Section 6.4.3 of RFC6125 states that client should NOT match identifier
+ # where wildcard is other than left-most label.
+ #
+ # Also implicitly mentions the wildcard character only in singular form,
+ # and discourages matching against more than one wildcard.
+ #
+ # See RFC 6125, section 7.2, subitem 2.
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*b*.example.com'), 'abc.example.com'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*b*.example.com'), 'ab.example.com'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('*b*.example.com'), 'bc.example.com'))
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('xn*.example.com'), 'xn1ca.example.com'))
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('xn--*.example.com'), 'xn--1ca.example.com'))
+ # part of U-label
+ # Subject in RFC5280 states case-insensitive ASCII comparison.
+ #
+ # See Section 7.2 of RFC 5280:
+ # IA5String is limited to the set of ASCII characters.
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+ create_cert_with_name('á*.example.com'), 'á1.example.com'))
+ end
+
+ def create_cert_with_san(san)
+ ef = OpenSSL::X509::ExtensionFactory.new
+ cert = OpenSSL::X509::Certificate.new
+ cert.subject = OpenSSL::X509::Name.parse("/DC=some/DC=site/CN=Some Site")
+ ext = ef.create_ext('subjectAltName', san)
+ cert.add_extension(ext)
+ cert
+ end
+
+ def create_cert_with_name(name)
+ cert = OpenSSL::X509::Certificate.new
+ cert.subject = OpenSSL::X509::Name.new([['DC', 'some'], ['DC', 'site'], ['CN', name]])
+ cert
+ end
+
+
+ # Create NULL byte SAN certificate
+ def create_null_byte_SAN_certificate(critical = false)
+ ef = OpenSSL::X509::ExtensionFactory.new
+ cert = OpenSSL::X509::Certificate.new
+ cert.subject = OpenSSL::X509::Name.parse "/DC=some/DC=site/CN=Some Site"
+ ext = ef.create_ext('subjectAltName', 'DNS:placeholder,IP:192.168.7.1,IP:13::17', critical)
+ ext_asn1 = OpenSSL::ASN1.decode(ext.to_der)
+ san_list_der = ext_asn1.value.reduce(nil) { |memo,val| val.tag == 4 ? val.value : memo }
+ san_list_asn1 = OpenSSL::ASN1.decode(san_list_der)
+ san_list_asn1.value[0].value = "www.example.com\0.evil.com"
+ pos = critical ? 2 : 1
+ ext_asn1.value[pos].value = san_list_asn1.to_der
+ real_ext = OpenSSL::X509::Extension.new ext_asn1
+ cert.add_extension(real_ext)
+ cert
+ end
+
+ def test_tlsext_hostname
+ return unless OpenSSL::SSL::SSLSocket.instance_methods.include?(:hostname)
+
+ ctx_proc = Proc.new do |ctx, ssl|
+ foo_ctx = ctx.dup
+
+ ctx.servername_cb = Proc.new do |ssl2, hostname|
+ case hostname
+ when 'foo.example.com'
+ foo_ctx
+ when 'bar.example.com'
+ nil
+ else
+ raise "unknown hostname #{hostname.inspect}"
+ end
+ end
+ end
+
+ server_proc = Proc.new do |ctx, ssl|
+ readwrite_loop(ctx, ssl)
+ end
+
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
+ 2.times do |i|
+ ctx = OpenSSL::SSL::SSLContext.new
+ if defined?(OpenSSL::SSL::OP_NO_TICKET)
+ # disable RFC4507 support
+ ctx.options = OpenSSL::SSL::OP_NO_TICKET
+ end
+ server_connect(port, ctx) { |ssl|
+ ssl.hostname = (i & 1 == 0) ? 'foo.example.com' : 'bar.example.com'
+ str = "x" * 100 + "\n"
+ ssl.puts(str)
+ assert_equal(str, ssl.gets)
+ }
+ end
+ end
+ end
+
+ def test_multibyte_read_write
+ #German a umlaut
+ auml = [%w{ C3 A4 }.join('')].pack('H*')
+ auml.force_encoding(Encoding::UTF_8)
+
+ [10, 1000, 100000].each {|i|
+ str = nil
+ num_written = nil
+ server_proc = Proc.new {|ctx, ssl|
+ cmp = ssl.read
+ raw_size = cmp.size
+ cmp.force_encoding(Encoding::UTF_8)
+ assert_equal(str, cmp)
+ assert_equal(num_written, raw_size)
+ ssl.close
+ }
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :server_proc => server_proc){|server, port|
+ server_connect(port) { |ssl|
+ str = auml * i
+ num_written = ssl.write(str)
+ }
+ }
+ }
+ end
+
+ def test_unset_OP_ALL
+ ctx_proc = Proc.new { |ctx|
+ # If OP_DONT_INSERT_EMPTY_FRAGMENTS is not defined, this test is
+ # redundant because the default options already are equal to OP_ALL.
+ # But it also degrades gracefully, so keep it
+ ctx.options = OpenSSL::SSL::OP_ALL
+ }
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc){|server, port|
+ server_connect(port) { |ssl|
+ ssl.puts('hello')
+ assert_equal("hello\n", ssl.gets)
+ }
+ }
+ end
+
+ # different OpenSSL versions react differently when facing a SSL/TLS version
+ # that has been marked as forbidden, therefore either of these may be raised
+ HANDSHAKE_ERRORS = [OpenSSL::SSL::SSLError, Errno::ECONNRESET]
+
+if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1
+
+ def test_forbid_ssl_v3_for_client
+ ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :SSLv3
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end
+
+ def test_forbid_ssl_v3_from_server
+ start_server_version(:SSLv3) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end
+
+end
+
+if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_1
+
+ def test_tls_v1_1
+ start_server_version(:TLSv1_1) { |server, port|
+ server_connect(port) { |ssl| assert_equal("TLSv1.1", ssl.ssl_version) }
+ }
+ end
+
+ def test_forbid_tls_v1_for_client
+ ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :TLSv1
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end
+
+ def test_forbid_tls_v1_from_server
+ start_server_version(:TLSv1) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end
+
+end
+
+if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_2
+
+ def test_tls_v1_2
+ start_server_version(:TLSv1_2) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :TLSv1_2_client
+ server_connect(port, ctx) { |ssl| assert_equal("TLSv1.2", ssl.ssl_version) }
+ }
+ end if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000
+
+ def test_forbid_tls_v1_1_for_client
+ ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :TLSv1_1
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
+
+ def test_forbid_tls_v1_1_from_server
+ start_server_version(:TLSv1_1) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
+
+ def test_forbid_tls_v1_2_for_client
+ ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :TLSv1_2
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2)
+
+ def test_forbid_tls_v1_2_from_server
+ start_server_version(:TLSv1_2) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2)
+
+end
+
+ def test_renegotiation_cb
+ num_handshakes = 0
+ renegotiation_cb = Proc.new { |ssl| num_handshakes += 1 }
+ ctx_proc = Proc.new { |ctx| ctx.renegotiation_cb = renegotiation_cb }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ server_connect(port) { |ssl|
+ assert_equal(1, num_handshakes)
+ }
+ }
+ end
+
+if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000
+
+ def test_npn_protocol_selection_ary
+ advertised = ["http/1.1", "spdy/2"]
+ ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ selector = lambda { |which|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.npn_select_cb = -> (protocols) { protocols.send(which) }
+ server_connect(port, ctx) { |ssl|
+ assert_equal(advertised.send(which), ssl.npn_protocol)
+ }
+ }
+ selector.call(:first)
+ selector.call(:last)
+ }
+ end
+
+ def test_npn_protocol_selection_enum
+ advertised = Object.new
+ def advertised.each
+ yield "http/1.1"
+ yield "spdy/2"
+ end
+ ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ selector = lambda { |selected, which|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.npn_select_cb = -> (protocols) { protocols.to_a.send(which) }
+ server_connect(port, ctx) { |ssl|
+ assert_equal(selected, ssl.npn_protocol)
+ }
+ }
+ selector.call("http/1.1", :first)
+ selector.call("spdy/2", :last)
+ }
+ end
+
+ def test_npn_protocol_selection_cancel
+ ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.npn_select_cb = -> (protocols) { raise RuntimeError.new }
+ assert_raise(RuntimeError) { server_connect(port, ctx) }
+ }
+ end
+
+ def test_npn_advertised_protocol_too_long
+ ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["a" * 256] }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.npn_select_cb = -> (protocols) { protocols.first }
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end
+
+ def test_npn_selected_protocol_too_long
+ ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
+ start_server_version(:SSLv23, ctx_proc) { |server, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.npn_select_cb = -> (protocols) { "a" * 256 }
+ assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+ }
+ end
+
+end
+
+ def test_invalid_shutdown_by_gc
+ assert_nothing_raised {
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ 10.times {
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ GC.start
+ ssl.connect
+ sock.close
+ }
+ }
+ }
+ end
+
+ def test_close_after_socket_close
+ start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.sync_close = true
+ ssl.connect
+ sock.close
+ assert_nothing_raised do
+ ssl.close
+ end
+ }
+ end
+
+ def test_sync_close_without_connect
+ Socket.open(:INET, :STREAM) {|s|
+ ssl = OpenSSL::SSL::SSLSocket.new(s)
+ ssl.sync_close = true
+ ssl.close
+ assert(s.closed?)
+ }
+ end
+
+ private
+
+ def start_server_version(version, ctx_proc=nil, server_proc=nil, &blk)
+ ctx_wrap = Proc.new { |ctx|
+ ctx.ssl_version = version
+ ctx_proc.call(ctx) if ctx_proc
+ }
+ start_server(
+ OpenSSL::SSL::VERIFY_NONE,
+ true,
+ :ctx_proc => ctx_wrap,
+ :server_proc => server_proc,
+ :ignore_listener_error => true,
+ &blk
+ )
+ end
+
+ def server_connect(port, ctx=nil)
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = ctx ? OpenSSL::SSL::SSLSocket.new(sock, ctx) : OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.sync_close = true
+ ssl.connect
+ yield ssl if block_given?
+ ensure
+ if ssl
+ ssl.close
+ elsif sock
+ sock.close
+ end
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_ssl_session.rb b/jni/ruby/test/openssl/test_ssl_session.rb
new file mode 100644
index 0000000..d4c2220
--- /dev/null
+++ b/jni/ruby/test/openssl/test_ssl_session.rb
@@ -0,0 +1,380 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase
+ def test_session_equals
+ session = OpenSSL::SSL::Session.new <<-SESSION
+-----BEGIN SSL SESSION PARAMETERS-----
+MIIDFgIBAQICAwEEAgA5BCCY3pW6iTkPoD5SENuztz/gZjhvey6XnHbsxd22k0Ol
+dgQw8uaN3hCRnlhoIKPWInCFzrp/tQsDRFs9jDjc9pwpy/oKHmJdQQMQA1g8FYnO
+gpdVoQYCBE52ikKiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B
+AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi
+eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA5MTkwMDE4MTBaFw0xMTA5MTkwMDQ4
+MTBaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
+LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB
+7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/
+GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw
+DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQARC7GP7InX1t7VEXz2
+I8RI57S0/HSJL4fDIYP3zFpitHX1PZeo+7XuzMilvPjjBo/ky9Jzo8TYiY+N+JEz
+mY/A/zPA4ZsJ7KYj6/FEdIc/vRlS0CvsbClbNjw1jl/PoB2FLr2b3uuBcZEsyZeP
+yq154ijq37Ajf8K5Mi5FgshoP41BPtRPj+VVf61rv1IcEnNWdDCS6DR4XsaNC+zt
+G6AqCqkytIXWRuDw6n6vYLF3A/tn2sldLo7/scY0PMDNbo63O/LTxkDHmPhSkD68
+8m9SsMeTR+RCiDEZWFPVcAH/8mDfi+5k8uN3qS+gOU/PPrmHGgl5ykiSFgqs4v61
+tddwpBAEDjcwMzA5NTYzMTU1MzAwpQMCARM=
+-----END SSL SESSION PARAMETERS-----
+ SESSION
+
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true) { |_, port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+ ctx.session_id_context = self.object_id.to_s
+
+ sock = TCPSocket.new '127.0.0.1', port
+ begin
+ ssl = OpenSSL::SSL::SSLSocket.new sock, ctx
+ ssl.session = session
+
+ assert_equal session, ssl.session
+ ensure
+ sock.close
+ end
+ }
+ end
+
+ def test_session
+ timeout(5) do
+ start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ ssl.connect
+ session = ssl.session
+ assert(session == OpenSSL::SSL::Session.new(session.to_pem))
+ assert(session == OpenSSL::SSL::Session.new(ssl))
+ assert_equal(300, session.timeout)
+ session.timeout = 5
+ assert_equal(5, session.timeout)
+ assert_not_nil(session.time)
+ # SSL_SESSION_time keeps long value so we can't keep nsec fragment.
+ session.time = t1 = Time.now.to_i
+ assert_equal(Time.at(t1), session.time)
+ if session.respond_to?(:id)
+ assert_not_nil(session.id)
+ end
+ pem = session.to_pem
+ assert_match(/\A-----BEGIN SSL SESSION PARAMETERS-----/, pem)
+ assert_match(/-----END SSL SESSION PARAMETERS-----\Z/, pem)
+ pem.gsub!(/-----(BEGIN|END) SSL SESSION PARAMETERS-----/, '').gsub!(/[\r\n]+/m, '')
+ assert_equal(session.to_der, pem.unpack('m*')[0])
+ assert_not_nil(session.to_text)
+ ssl.close
+ end
+ end
+ end
+
+ DUMMY_SESSION = <<__EOS__
+-----BEGIN SSL SESSION PARAMETERS-----
+MIIDzQIBAQICAwEEAgA5BCAF219w9ZEV8dNA60cpEGOI34hJtIFbf3bkfzSgMyad
+MQQwyGLbkCxE4OiMLdKKem+pyh8V7ifoP7tCxhdmwoDlJxI1v6nVCjai+FGYuncy
+NNSWoQYCBE4DDWuiAwIBCqOCAo4wggKKMIIBcqADAgECAgECMA0GCSqGSIb3DQEB
+BQUAMD0xEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
+LWxhbmcxCzAJBgNVBAMMAkNBMB4XDTExMDYyMzA5NTQ1MVoXDTExMDYyMzEwMjQ1
+MVowRDETMBEGCgmSJomT8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1Ynkt
+bGFuZzESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7CxaKPERYHs
+k4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/Q3geLv8Z
+D9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQABoxIwEDAO
+BgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggEBACj5WhoZ/ODVeHpwgq1d
+8fW/13ICRYHYpv6dzlWihyqclGxbKMlMnaVCPz+4JaVtMz3QB748KJQgL3Llg3R1
+ek+f+n1MBCMfFFsQXJ2gtLB84zD6UCz8aaCWN5/czJCd7xMz7fRLy3TOIW5boXAU
+zIa8EODk+477K1uznHm286ab0Clv+9d304hwmBZgkzLg6+31Of6d6s0E0rwLGiS2
+sOWYg34Y3r4j8BS9Ak4jzpoLY6cJ0QAKCOJCgmjGr4XHpyXMLbicp3ga1uSbwtVO
+gF/gTfpLhJC+y0EQ5x3Ftl88Cq7ZJuLBDMo/TLIfReJMQu/HlrTT7+LwtneSWGmr
+KkSkAgQApQMCAROqgcMEgcAuDkAVfj6QAJMz9yqTzW5wPFyty7CxUEcwKjUqj5UP
+/Yvky1EkRuM/eQfN7ucY+MUvMqv+R8ZSkHPsnjkBN5ChvZXjrUSZKFVjR4eFVz2V
+jismLEJvIFhQh6pqTroRrOjMfTaM5Lwoytr2FTGobN9rnjIRsXeFQW1HLFbXn7Dh
+8uaQkMwIVVSGRB8T7t6z6WIdWruOjCZ6G5ASI5XoqAHwGezhLodZuvJEfsVyCF9y
+j+RBGfCFrrQbBdnkFI/ztgM=
+-----END SSL SESSION PARAMETERS-----
+__EOS__
+
+ DUMMY_SESSION_NO_EXT = <<-__EOS__
+-----BEGIN SSL SESSION PARAMETERS-----
+MIIDCAIBAQICAwAEAgA5BCDyAW7rcpzMjDSosH+Tv6sukymeqgq3xQVVMez628A+
+lAQw9TrKzrIqlHEh6ltuQaqv/Aq83AmaAlogYktZgXAjOGnhX7ifJDNLMuCfQq53
+hPAaoQYCBE4iDeeiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B
+AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi
+eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA3MTYyMjE3MTFaFw0xMTA3MTYyMjQ3
+MTFaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
+LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB
+7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/
+GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw
+DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQA3TRzABRG3kz8jEEYr
+tDQqXgsxwTsLhTT5d1yF0D8uFw+y15hJAJnh6GJHjqhWBrF4zNoTApFo+4iIL6g3
+q9C3mUsxIVAHx41DwZBh/FI7J4FqlAoGOguu7892CNVY3ZZjc3AXMTdKjcNoWPzz
+FCdj5fNT24JMMe+ZdGZK97ChahJsdn/6B3j6ze9NK9mfYEbiJhejGTPLOFVHJCGR
+KYYZ3ZcKhLDr9ql4d7cCo1gBtemrmFQGPui7GttNEqmXqUKvV8mYoa8farf5i7T4
+L6a/gp2cVZTaDIS1HjbJsA/Ag7AajZqiN6LfqShNUVsrMZ+5CoV8EkBDTZPJ9MSr
+a3EqpAIEAKUDAgET
+-----END SSL SESSION PARAMETERS-----
+__EOS__
+
+
+ def test_session_time
+ sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT)
+ sess.time = (now = Time.now)
+ assert_equal(now.to_i, sess.time.to_i)
+ sess.time = 1
+ assert_equal(1, sess.time.to_i)
+ sess.time = 1.2345
+ assert_equal(1, sess.time.to_i)
+ # Can OpenSSL handle t>2038y correctly? Version?
+ sess.time = 2**31 - 1
+ assert_equal(2**31 - 1, sess.time.to_i)
+ end
+
+ def test_session_timeout
+ sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT)
+ assert_raise(TypeError) do
+ sess.timeout = Time.now
+ end
+ sess.timeout = 1
+ assert_equal(1, sess.timeout.to_i)
+ sess.timeout = 1.2345
+ assert_equal(1, sess.timeout.to_i)
+ sess.timeout = 2**31 - 1
+ assert_equal(2**31 - 1, sess.timeout.to_i)
+ end
+
+ def test_session_exts_read
+ assert(OpenSSL::SSL::Session.new(DUMMY_SESSION))
+ end if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x009080bf
+
+ def test_client_session
+ last_session = nil
+ start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
+ 2.times do
+ sock = TCPSocket.new("127.0.0.1", port)
+ # Debian's openssl 0.9.8g-13 failed at assert(ssl.session_reused?),
+ # when use default SSLContext. [ruby-dev:36167]
+ ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ ssl.session = last_session if last_session
+ ssl.connect
+
+ session = ssl.session
+ if last_session
+ assert(ssl.session_reused?)
+
+ if session.respond_to?(:id)
+ assert_equal(session.id, last_session.id)
+ end
+ assert_equal(session.to_pem, last_session.to_pem)
+ assert_equal(session.to_der, last_session.to_der)
+ # Older version of OpenSSL may not be consistent. Look up which versions later.
+ assert_equal(session.to_text, last_session.to_text)
+ else
+ assert(!ssl.session_reused?)
+ end
+ last_session = session
+
+ str = "x" * 100 + "\n"
+ ssl.puts(str)
+ assert_equal(str, ssl.gets)
+
+ ssl.close
+ end
+ end
+ end
+
+ def test_server_session
+ connections = 0
+ saved_session = nil
+
+ ctx_proc = Proc.new do |ctx, ssl|
+# add test for session callbacks here
+ end
+
+ server_proc = Proc.new do |ctx, ssl|
+ session = ssl.session
+ stats = ctx.session_cache_stats
+
+ case connections
+ when 0
+ assert_equal(stats[:cache_num], 1)
+ assert_equal(stats[:cache_hits], 0)
+ assert_equal(stats[:cache_misses], 0)
+ assert(!ssl.session_reused?)
+ when 1
+ assert_equal(stats[:cache_num], 1)
+ assert_equal(stats[:cache_hits], 1)
+ assert_equal(stats[:cache_misses], 0)
+ assert(ssl.session_reused?)
+ ctx.session_remove(session)
+ saved_session = session
+ when 2
+ assert_equal(stats[:cache_num], 1)
+ assert_equal(stats[:cache_hits], 1)
+ assert_equal(stats[:cache_misses], 1)
+ assert(!ssl.session_reused?)
+ ctx.session_add(saved_session)
+ when 3
+ assert_equal(stats[:cache_num], 2)
+ assert_equal(stats[:cache_hits], 2)
+ assert_equal(stats[:cache_misses], 1)
+ assert(ssl.session_reused?)
+ ctx.flush_sessions(Time.now + 5000)
+ when 4
+ assert_equal(stats[:cache_num], 1)
+ assert_equal(stats[:cache_hits], 2)
+ assert_equal(stats[:cache_misses], 2)
+ assert(!ssl.session_reused?)
+ ctx.session_add(saved_session)
+ end
+ connections += 1
+
+ readwrite_loop(ctx, ssl)
+ end
+
+ first_session = nil
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
+ 10.times do |i|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new
+ if defined?(OpenSSL::SSL::OP_NO_TICKET)
+ # disable RFC4507 support
+ ctx.options = OpenSSL::SSL::OP_NO_TICKET
+ end
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ ssl.session = first_session if first_session
+ ssl.connect
+
+ session = ssl.session
+ if first_session
+ case i
+ when 1; assert(ssl.session_reused?)
+ when 2; assert(!ssl.session_reused?)
+ when 3; assert(ssl.session_reused?)
+ when 4; assert(!ssl.session_reused?)
+ when 5..10; assert(ssl.session_reused?)
+ end
+ end
+ first_session ||= session
+
+ str = "x" * 100 + "\n"
+ ssl.puts(str)
+ assert_equal(str, ssl.gets)
+
+ ssl.close
+ end
+ end
+ end
+
+ def test_ctx_client_session_cb
+ called = {}
+ ctx = OpenSSL::SSL::SSLContext.new("SSLv3")
+ ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+
+ ctx.session_new_cb = lambda { |ary|
+ sock, sess = ary
+ called[:new] = [sock, sess]
+ }
+
+ ctx.session_remove_cb = lambda { |ary|
+ ctx, sess = ary
+ called[:remove] = [ctx, sess]
+ # any resulting value is OK (ignored)
+ }
+
+ start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ begin
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ ssl.connect
+ assert_equal(1, ctx.session_cache_stats[:cache_num])
+ assert_equal(1, ctx.session_cache_stats[:connect_good])
+ assert_equal([ssl, ssl.session], called[:new])
+ assert(ctx.session_remove(ssl.session))
+ assert(!ctx.session_remove(ssl.session))
+ assert_equal([ctx, ssl.session], called[:remove])
+ ssl.close
+ ensure
+ sock.close if !sock.closed?
+ end
+ end
+ end
+
+ def test_ctx_server_session_cb
+ called = {}
+
+ ctx_proc = Proc.new { |ctx, ssl|
+ ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_SERVER
+ last_server_session = nil
+
+ # get_cb is called whenever a client proposed to resume a session but
+ # the session could not be found in the internal session cache.
+ ctx.session_get_cb = lambda { |ary|
+ sess, data = ary
+ if last_server_session
+ called[:get2] = [sess, data]
+ last_server_session
+ else
+ called[:get1] = [sess, data]
+ last_server_session = sess
+ nil
+ end
+ }
+
+ ctx.session_new_cb = lambda { |ary|
+ sock, sess = ary
+ called[:new] = [sock, sess]
+ # SSL server doesn't cache sessions so get_cb is called next time.
+ ctx.session_remove(sess)
+ }
+
+ ctx.session_remove_cb = lambda { |ary|
+ ctx, sess = ary
+ called[:remove] = [ctx, sess]
+ }
+ }
+
+ server_proc = Proc.new { |c, ssl|
+ ssl.session
+ c.session_cache_stats
+ readwrite_loop(c, ssl)
+ }
+ start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
+ last_client_session = nil
+ 3.times do
+ sock = TCPSocket.new("127.0.0.1", port)
+ begin
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, OpenSSL::SSL::SSLContext.new("SSLv3"))
+ ssl.sync_close = true
+ ssl.session = last_client_session if last_client_session
+ ssl.connect
+ last_client_session = ssl.session
+ ssl.close
+ timeout(5) do
+ Thread.pass until called.key?(:new)
+ assert(called.delete(:new))
+ Thread.pass until called.key?(:remove)
+ assert(called.delete(:remove))
+ end
+ ensure
+ sock.close if !sock.closed?
+ end
+ end
+ end
+ assert(called[:get1])
+ assert(called[:get2])
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509cert.rb b/jni/ruby/test/openssl/test_x509cert.rb
new file mode 100644
index 0000000..783677a
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509cert.rb
@@ -0,0 +1,226 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Certificate < Test::Unit::TestCase
+ def setup
+ @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256
+ @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512
+ @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+ @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+ @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+ end
+
+ def teardown
+ end
+
+ def issue_cert(*args)
+ OpenSSL::TestUtils.issue_cert(*args)
+ end
+
+ def test_serial
+ [1, 2**32, 2**100].each{|s|
+ cert = issue_cert(@ca, @rsa2048, s, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ assert_equal(s, cert.serial)
+ cert = OpenSSL::X509::Certificate.new(cert.to_der)
+ assert_equal(s, cert.serial)
+ }
+ end
+
+ def test_public_key
+ exts = [
+ ["basicConstraints","CA:TRUE",true],
+ ["subjectKeyIdentifier","hash",false],
+ ["authorityKeyIdentifier","keyid:always",false],
+ ]
+
+ sha1 = OpenSSL::Digest::SHA1.new
+ dsa_digest = OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new
+
+ [
+ [@rsa1024, sha1], [@rsa2048, sha1], [@dsa256, dsa_digest], [@dsa512, dsa_digest]
+ ].each{|pk, digest|
+ cert = issue_cert(@ca, pk, 1, Time.now, Time.now+3600, exts,
+ nil, nil, digest)
+ assert_equal(cert.extensions.sort_by(&:to_s)[2].value,
+ OpenSSL::TestUtils.get_subject_key_id(cert))
+ cert = OpenSSL::X509::Certificate.new(cert.to_der)
+ assert_equal(cert.extensions.sort_by(&:to_s)[2].value,
+ OpenSSL::TestUtils.get_subject_key_id(cert))
+ }
+ end
+
+ def test_validity
+ now = Time.now until now && now.usec != 0
+ cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ assert_not_equal(now, cert.not_before)
+ assert_not_equal(now+3600, cert.not_after)
+
+ now = Time.at(now.to_i)
+ cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ assert_equal(now.getutc, cert.not_before)
+ assert_equal((now+3600).getutc, cert.not_after)
+
+ now = Time.at(0)
+ cert = issue_cert(@ca, @rsa2048, 1, now, now, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ assert_equal(now.getutc, cert.not_before)
+ assert_equal(now.getutc, cert.not_after)
+
+ now = Time.at(0x7fffffff)
+ cert = issue_cert(@ca, @rsa2048, 1, now, now, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ assert_equal(now.getutc, cert.not_before)
+ assert_equal(now.getutc, cert.not_after)
+ end
+
+ def test_extension
+ ca_exts = [
+ ["basicConstraints","CA:TRUE",true],
+ ["keyUsage","keyCertSign, cRLSign",true],
+ ["subjectKeyIdentifier","hash",false],
+ ["authorityKeyIdentifier","keyid:always",false],
+ ]
+ ca_cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, ca_exts,
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ ca_cert.extensions.each_with_index{|ext, i|
+ assert_equal(ca_exts[i].first, ext.oid)
+ assert_equal(ca_exts[i].last, ext.critical?)
+ }
+
+ ee1_exts = [
+ ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+ ["subjectKeyIdentifier","hash",false],
+ ["authorityKeyIdentifier","keyid:always",false],
+ ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+ ["subjectAltName","email:ee1@ruby-lang.org",false],
+ ]
+ ee1_cert = issue_cert(@ee1, @rsa1024, 2, Time.now, Time.now+1800, ee1_exts,
+ ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ assert_equal(ca_cert.subject.to_der, ee1_cert.issuer.to_der)
+ ee1_cert.extensions.each_with_index{|ext, i|
+ assert_equal(ee1_exts[i].first, ext.oid)
+ assert_equal(ee1_exts[i].last, ext.critical?)
+ }
+
+ ee2_exts = [
+ ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+ ["subjectKeyIdentifier","hash",false],
+ ["authorityKeyIdentifier","issuer:always",false],
+ ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+ ["subjectAltName","email:ee2@ruby-lang.org",false],
+ ]
+ ee2_cert = issue_cert(@ee2, @rsa1024, 3, Time.now, Time.now+1800, ee2_exts,
+ ca_cert, @rsa2048, OpenSSL::Digest::MD5.new)
+ assert_equal(ca_cert.subject.to_der, ee2_cert.issuer.to_der)
+ ee2_cert.extensions.each_with_index{|ext, i|
+ assert_equal(ee2_exts[i].first, ext.oid)
+ assert_equal(ee2_exts[i].last, ext.critical?)
+ }
+
+ end
+
+ def test_sign_and_verify_rsa_sha1
+ cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ assert_equal(false, cert.verify(@rsa1024))
+ assert_equal(true, cert.verify(@rsa2048))
+ assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
+ assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) })
+ cert.serial = 2
+ assert_equal(false, cert.verify(@rsa2048))
+ end
+
+ def test_sign_and_verify_rsa_md5
+ cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::MD5.new)
+ assert_equal(false, cert.verify(@rsa1024))
+ assert_equal(true, cert.verify(@rsa2048))
+
+ assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
+ assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) })
+ cert.subject = @ee1
+ assert_equal(false, cert.verify(@rsa2048))
+ rescue OpenSSL::X509::CertificateError # RHEL7 disables MD5
+ end
+
+ def test_sign_and_verify_dsa
+ cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ assert_equal(false, certificate_error_returns_false { cert.verify(@rsa1024) })
+ assert_equal(false, certificate_error_returns_false { cert.verify(@rsa2048) })
+ assert_equal(false, cert.verify(@dsa256))
+ assert_equal(true, cert.verify(@dsa512))
+ cert.not_after = Time.now
+ assert_equal(false, cert.verify(@dsa512))
+ end
+
+ def test_sign_and_verify_rsa_dss1
+ cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::DSS1.new)
+ assert_equal(false, cert.verify(@rsa1024))
+ assert_equal(true, cert.verify(@rsa2048))
+ assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
+ assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) })
+ cert.subject = @ee1
+ assert_equal(false, cert.verify(@rsa2048))
+ rescue OpenSSL::X509::CertificateError
+ end
+
+ def test_sign_and_verify_dsa_md5
+ assert_raise(OpenSSL::X509::CertificateError){
+ issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::MD5.new)
+ }
+ end
+
+ def test_dsig_algorithm_mismatch
+ assert_raise(OpenSSL::X509::CertificateError) do
+ issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::DSS1.new)
+ end if OpenSSL::OPENSSL_VERSION_NUMBER < 0x10001000 # [ruby-core:42949]
+
+ assert_raise(OpenSSL::X509::CertificateError) do
+ issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::MD5.new)
+ end
+ end
+
+ def test_dsa_with_sha2
+ begin
+ cert = issue_cert(@ca, @dsa256, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA256.new)
+ assert_equal("dsa_with_SHA256", cert.signature_algorithm)
+ rescue OpenSSL::X509::CertificateError
+ # dsa_with_sha2 not supported. skip following test.
+ return
+ end
+ # TODO: need more tests for dsa + sha2
+
+ # SHA1 is allowed from OpenSSL 1.0.0 (0.9.8 requires DSS1)
+ cert = issue_cert(@ca, @dsa256, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ assert_equal("dsaWithSHA1", cert.signature_algorithm)
+ end if defined?(OpenSSL::Digest::SHA256)
+
+ def test_check_private_key
+ cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ assert_equal(true, cert.check_private_key(@rsa2048))
+ end
+
+ private
+
+ def certificate_error_returns_false
+ yield
+ rescue OpenSSL::X509::CertificateError
+ false
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509crl.rb b/jni/ruby/test/openssl/test_x509crl.rb
new file mode 100644
index 0000000..9dc1b1c
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509crl.rb
@@ -0,0 +1,220 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509CRL < Test::Unit::TestCase
+ def setup
+ @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256
+ @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512
+ @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+ @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+ @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+ end
+
+ def teardown
+ end
+
+ def issue_crl(*args)
+ OpenSSL::TestUtils.issue_crl(*args)
+ end
+
+ def issue_cert(*args)
+ OpenSSL::TestUtils.issue_cert(*args)
+ end
+
+ def test_basic
+ now = Time.at(Time.now.to_i)
+
+ cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ crl = issue_crl([], 1, now, now+1600, [],
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ assert_equal(1, crl.version)
+ assert_equal(cert.issuer.to_der, crl.issuer.to_der)
+ assert_equal(now, crl.last_update)
+ assert_equal(now+1600, crl.next_update)
+
+ crl = OpenSSL::X509::CRL.new(crl.to_der)
+ assert_equal(1, crl.version)
+ assert_equal(cert.issuer.to_der, crl.issuer.to_der)
+ assert_equal(now, crl.last_update)
+ assert_equal(now+1600, crl.next_update)
+ end
+
+ def test_revoked
+
+ # CRLReason ::= ENUMERATED {
+ # unspecified (0),
+ # keyCompromise (1),
+ # cACompromise (2),
+ # affiliationChanged (3),
+ # superseded (4),
+ # cessationOfOperation (5),
+ # certificateHold (6),
+ # removeFromCRL (8),
+ # privilegeWithdrawn (9),
+ # aACompromise (10) }
+
+ now = Time.at(Time.now.to_i)
+ revoke_info = [
+ [1, Time.at(0), 1],
+ [2, Time.at(0x7fffffff), 2],
+ [3, now, 3],
+ [4, now, 4],
+ [5, now, 5],
+ ]
+ cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ revoked = crl.revoked
+ assert_equal(5, revoked.size)
+ assert_equal(1, revoked[0].serial)
+ assert_equal(2, revoked[1].serial)
+ assert_equal(3, revoked[2].serial)
+ assert_equal(4, revoked[3].serial)
+ assert_equal(5, revoked[4].serial)
+
+ assert_equal(Time.at(0), revoked[0].time)
+ assert_equal(Time.at(0x7fffffff), revoked[1].time)
+ assert_equal(now, revoked[2].time)
+ assert_equal(now, revoked[3].time)
+ assert_equal(now, revoked[4].time)
+
+ assert_equal("CRLReason", revoked[0].extensions[0].oid)
+ assert_equal("CRLReason", revoked[1].extensions[0].oid)
+ assert_equal("CRLReason", revoked[2].extensions[0].oid)
+ assert_equal("CRLReason", revoked[3].extensions[0].oid)
+ assert_equal("CRLReason", revoked[4].extensions[0].oid)
+
+ assert_equal("Key Compromise", revoked[0].extensions[0].value)
+ assert_equal("CA Compromise", revoked[1].extensions[0].value)
+ assert_equal("Affiliation Changed", revoked[2].extensions[0].value)
+ assert_equal("Superseded", revoked[3].extensions[0].value)
+ assert_equal("Cessation Of Operation", revoked[4].extensions[0].value)
+
+ assert_equal(false, revoked[0].extensions[0].critical?)
+ assert_equal(false, revoked[1].extensions[0].critical?)
+ assert_equal(false, revoked[2].extensions[0].critical?)
+ assert_equal(false, revoked[3].extensions[0].critical?)
+ assert_equal(false, revoked[4].extensions[0].critical?)
+
+ assert_equal("Key Compromise", revoked[0].extensions[0].value)
+ assert_equal("CA Compromise", revoked[1].extensions[0].value)
+ assert_equal("Affiliation Changed", revoked[2].extensions[0].value)
+ assert_equal("Superseded", revoked[3].extensions[0].value)
+ assert_equal("Cessation Of Operation", revoked[4].extensions[0].value)
+
+ revoke_info = (1..1000).collect{|i| [i, now, 0] }
+ crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ revoked = crl.revoked
+ assert_equal(1000, revoked.size)
+ assert_equal(1, revoked[0].serial)
+ assert_equal(1000, revoked[999].serial)
+ end
+
+ def test_extension
+ cert_exts = [
+ ["basicConstraints", "CA:TRUE", true],
+ ["subjectKeyIdentifier", "hash", false],
+ ["authorityKeyIdentifier", "keyid:always", false],
+ ["subjectAltName", "email:xyzzy@ruby-lang.org", false],
+ ["keyUsage", "cRLSign, keyCertSign", true],
+ ]
+ crl_exts = [
+ ["authorityKeyIdentifier", "keyid:always", false],
+ ["issuerAltName", "issuer:copy", false],
+ ]
+
+ cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, cert_exts,
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ crl = issue_crl([], 1, Time.now, Time.now+1600, crl_exts,
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ exts = crl.extensions
+ assert_equal(3, exts.size)
+ assert_equal("1", exts[0].value)
+ assert_equal("crlNumber", exts[0].oid)
+ assert_equal(false, exts[0].critical?)
+
+ assert_equal("authorityKeyIdentifier", exts[1].oid)
+ keyid = OpenSSL::TestUtils.get_subject_key_id(cert)
+ assert_match(/^keyid:#{keyid}/, exts[1].value)
+ assert_equal(false, exts[1].critical?)
+
+ assert_equal("issuerAltName", exts[2].oid)
+ assert_equal("email:xyzzy@ruby-lang.org", exts[2].value)
+ assert_equal(false, exts[2].critical?)
+
+ crl = OpenSSL::X509::CRL.new(crl.to_der)
+ exts = crl.extensions
+ assert_equal(3, exts.size)
+ assert_equal("1", exts[0].value)
+ assert_equal("crlNumber", exts[0].oid)
+ assert_equal(false, exts[0].critical?)
+
+ assert_equal("authorityKeyIdentifier", exts[1].oid)
+ keyid = OpenSSL::TestUtils.get_subject_key_id(cert)
+ assert_match(/^keyid:#{keyid}/, exts[1].value)
+ assert_equal(false, exts[1].critical?)
+
+ assert_equal("issuerAltName", exts[2].oid)
+ assert_equal("email:xyzzy@ruby-lang.org", exts[2].value)
+ assert_equal(false, exts[2].critical?)
+ end
+
+ def test_crlnumber
+ cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ assert_match(1.to_s, crl.extensions[0].value)
+ assert_match(/X509v3 CRL Number:\s+#{1}/m, crl.to_text)
+
+ crl = issue_crl([], 2**32, Time.now, Time.now+1600, [],
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ assert_match((2**32).to_s, crl.extensions[0].value)
+ assert_match(/X509v3 CRL Number:\s+#{2**32}/m, crl.to_text)
+
+ crl = issue_crl([], 2**100, Time.now, Time.now+1600, [],
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ assert_match(/X509v3 CRL Number:\s+#{2**100}/m, crl.to_text)
+ assert_match((2**100).to_s, crl.extensions[0].value)
+ end
+
+ def test_sign_and_verify
+ cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+ cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ assert_equal(false, crl.verify(@rsa1024))
+ assert_equal(true, crl.verify(@rsa2048))
+ assert_equal(false, crl_error_returns_false { crl.verify(@dsa256) })
+ assert_equal(false, crl_error_returns_false { crl.verify(@dsa512) })
+ crl.version = 0
+ assert_equal(false, crl.verify(@rsa2048))
+
+ cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+ nil, nil, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+ cert, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ assert_equal(false, crl_error_returns_false { crl.verify(@rsa1024) })
+ assert_equal(false, crl_error_returns_false { crl.verify(@rsa2048) })
+ assert_equal(false, crl.verify(@dsa256))
+ assert_equal(true, crl.verify(@dsa512))
+ crl.version = 0
+ assert_equal(false, crl.verify(@dsa512))
+ end
+
+ private
+
+ def crl_error_returns_false
+ yield
+ rescue OpenSSL::X509::CRLError
+ false
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509ext.rb b/jni/ruby/test/openssl/test_x509ext.rb
new file mode 100644
index 0000000..29e9f1d
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509ext.rb
@@ -0,0 +1,69 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Extension < Test::Unit::TestCase
+ def setup
+ @basic_constraints_value = OpenSSL::ASN1::Sequence([
+ OpenSSL::ASN1::Boolean(true), # CA
+ OpenSSL::ASN1::Integer(2) # pathlen
+ ])
+ @basic_constraints = OpenSSL::ASN1::Sequence([
+ OpenSSL::ASN1::ObjectId("basicConstraints"),
+ OpenSSL::ASN1::Boolean(true),
+ OpenSSL::ASN1::OctetString(@basic_constraints_value.to_der),
+ ])
+ end
+
+ def teardown
+ end
+
+ def test_new
+ ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der)
+ assert_equal("basicConstraints", ext.oid)
+ assert_equal(true, ext.critical?)
+ assert_equal("CA:TRUE, pathlen:2", ext.value)
+
+ ext = OpenSSL::X509::Extension.new("2.5.29.19",
+ @basic_constraints_value.to_der, true)
+ assert_equal(@basic_constraints.to_der, ext.to_der)
+ end
+
+ def test_create_by_factory
+ ef = OpenSSL::X509::ExtensionFactory.new
+
+ bc = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2")
+ assert_equal(@basic_constraints.to_der, bc.to_der)
+
+ bc = ef.create_extension("basicConstraints", "CA:TRUE, pathlen:2", true)
+ assert_equal(@basic_constraints.to_der, bc.to_der)
+
+ begin
+ ef.config = OpenSSL::Config.parse(<<-_end_of_cnf_)
+ [crlDistPts]
+ URI.1 = http://www.example.com/crl
+ URI.2 = ldap://ldap.example.com/cn=ca?certificateRevocationList;binary
+ _end_of_cnf_
+ rescue NotImplementedError
+ return
+ end
+
+ cdp = ef.create_extension("crlDistributionPoints", "@crlDistPts")
+ assert_equal(false, cdp.critical?)
+ assert_equal("crlDistributionPoints", cdp.oid)
+ assert_match(%{URI:http://www\.example\.com/crl}, cdp.value)
+ assert_match(
+ %r{URI:ldap://ldap\.example\.com/cn=ca\?certificateRevocationList;binary},
+ cdp.value)
+
+ cdp = ef.create_extension("crlDistributionPoints", "critical, @crlDistPts")
+ assert_equal(true, cdp.critical?)
+ assert_equal("crlDistributionPoints", cdp.oid)
+ assert_match(%{URI:http://www.example.com/crl}, cdp.value)
+ assert_match(
+ %r{URI:ldap://ldap.example.com/cn=ca\?certificateRevocationList;binary},
+ cdp.value)
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509name.rb b/jni/ruby/test/openssl/test_x509name.rb
new file mode 100644
index 0000000..a92af53
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509name.rb
@@ -0,0 +1,367 @@
+# coding: US-ASCII
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Name < Test::Unit::TestCase
+ OpenSSL::ASN1::ObjectId.register(
+ "1.2.840.113549.1.9.1", "emailAddress", "emailAddress")
+ OpenSSL::ASN1::ObjectId.register(
+ "2.5.4.5", "serialNumber", "serialNumber")
+
+ def setup
+ @obj_type_tmpl = Hash.new(OpenSSL::ASN1::PRINTABLESTRING)
+ @obj_type_tmpl.update(OpenSSL::X509::Name::OBJECT_TYPE_TEMPLATE)
+ end
+
+ def teardown
+ end
+
+ def test_s_new
+ dn = [ ["C", "JP"], ["O", "example"], ["CN", "www.example.jp"] ]
+ name = OpenSSL::X509::Name.new(dn)
+ ary = name.to_a
+ assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+ assert_equal("C", ary[0][0])
+ assert_equal("O", ary[1][0])
+ assert_equal("CN", ary[2][0])
+ assert_equal("JP", ary[0][1])
+ assert_equal("example", ary[1][1])
+ assert_equal("www.example.jp", ary[2][1])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+ dn = [
+ ["countryName", "JP"],
+ ["organizationName", "example"],
+ ["commonName", "www.example.jp"]
+ ]
+ name = OpenSSL::X509::Name.new(dn)
+ ary = name.to_a
+ assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+ assert_equal("C", ary[0][0])
+ assert_equal("O", ary[1][0])
+ assert_equal("CN", ary[2][0])
+ assert_equal("JP", ary[0][1])
+ assert_equal("example", ary[1][1])
+ assert_equal("www.example.jp", ary[2][1])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+ name = OpenSSL::X509::Name.new(dn, @obj_type_tmpl)
+ ary = name.to_a
+ assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+
+ dn = [
+ ["countryName", "JP", OpenSSL::ASN1::PRINTABLESTRING],
+ ["organizationName", "example", OpenSSL::ASN1::PRINTABLESTRING],
+ ["commonName", "www.example.jp", OpenSSL::ASN1::PRINTABLESTRING]
+ ]
+ name = OpenSSL::X509::Name.new(dn)
+ ary = name.to_a
+ assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+
+ dn = [
+ ["DC", "org"],
+ ["DC", "ruby-lang"],
+ ["CN", "GOTOU Yuuzou"],
+ ["emailAddress", "gotoyuzo@ruby-lang.org"],
+ ["serialNumber", "123"],
+ ]
+ name = OpenSSL::X509::Name.new(dn)
+ ary = name.to_a
+ assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123", name.to_s)
+ assert_equal("DC", ary[0][0])
+ assert_equal("DC", ary[1][0])
+ assert_equal("CN", ary[2][0])
+ assert_equal("emailAddress", ary[3][0])
+ assert_equal("serialNumber", ary[4][0])
+ assert_equal("org", ary[0][1])
+ assert_equal("ruby-lang", ary[1][1])
+ assert_equal("GOTOU Yuuzou", ary[2][1])
+ assert_equal("gotoyuzo@ruby-lang.org", ary[3][1])
+ assert_equal("123", ary[4][1])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2])
+
+ name_from_der = OpenSSL::X509::Name.new(name.to_der)
+ assert_equal(name_from_der.to_s, name.to_s)
+ assert_equal(name_from_der.to_a, name.to_a)
+ assert_equal(name_from_der.to_der, name.to_der)
+ end
+
+ def test_unrecognized_oid
+ dn = [ ["1.2.3.4.5.6.7.8.9.7.5.3.1", "Unknown OID 1"],
+ ["1.1.2.3.5.8.13.21.34", "Unknown OID 2"],
+ ["C", "US"],
+ ["postalCode", "60602"],
+ ["ST", "Illinois"],
+ ["L", "Chicago"],
+ #["street", "123 Fake St"],
+ ["O", "Some Company LLC"],
+ ["CN", "mydomain.com"] ]
+
+ name = OpenSSL::X509::Name.new(dn)
+ ary = name.to_a
+ #assert_equal("/1.2.3.4.5.6.7.8.9.7.5.3.1=Unknown OID 1/1.1.2.3.5.8.13.21.34=Unknown OID 2/C=US/postalCode=60602/ST=Illinois/L=Chicago/street=123 Fake St/O=Some Company LLC/CN=mydomain.com", name.to_s)
+ assert_equal("/1.2.3.4.5.6.7.8.9.7.5.3.1=Unknown OID 1/1.1.2.3.5.8.13.21.34=Unknown OID 2/C=US/postalCode=60602/ST=Illinois/L=Chicago/O=Some Company LLC/CN=mydomain.com", name.to_s)
+ assert_equal("1.2.3.4.5.6.7.8.9.7.5.3.1", ary[0][0])
+ assert_equal("1.1.2.3.5.8.13.21.34", ary[1][0])
+ assert_equal("C", ary[2][0])
+ assert_equal("postalCode", ary[3][0])
+ assert_equal("ST", ary[4][0])
+ assert_equal("L", ary[5][0])
+ #assert_equal("street", ary[6][0])
+ assert_equal("O", ary[6][0])
+ assert_equal("CN", ary[7][0])
+ assert_equal("Unknown OID 1", ary[0][1])
+ assert_equal("Unknown OID 2", ary[1][1])
+ assert_equal("US", ary[2][1])
+ assert_equal("60602", ary[3][1])
+ assert_equal("Illinois", ary[4][1])
+ assert_equal("Chicago", ary[5][1])
+ #assert_equal("123 Fake St", ary[6][1])
+ assert_equal("Some Company LLC", ary[6][1])
+ assert_equal("mydomain.com", ary[7][1])
+ end
+
+ def test_unrecognized_oid_parse_encode_equality
+ dn = [ ["1.2.3.4.5.6.7.8.9.7.5.3.2", "Unknown OID1"],
+ ["1.1.2.3.5.8.13.21.35", "Unknown OID2"],
+ ["C", "US"],
+ ["postalCode", "60602"],
+ ["ST", "Illinois"],
+ ["L", "Chicago"],
+ #["street", "123 Fake St"],
+ ["O", "Some Company LLC"],
+ ["CN", "mydomain.com"] ]
+
+ name1 = OpenSSL::X509::Name.new(dn)
+ name2 = OpenSSL::X509::Name.parse(name1.to_s)
+ assert_equal(name1.to_s, name2.to_s)
+ assert_equal(name1.to_a, name2.to_a)
+ end
+
+ def test_s_parse
+ dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
+ name = OpenSSL::X509::Name.parse(dn)
+ assert_equal(dn, name.to_s)
+ ary = name.to_a
+ assert_equal("DC", ary[0][0])
+ assert_equal("DC", ary[1][0])
+ assert_equal("CN", ary[2][0])
+ assert_equal("org", ary[0][1])
+ assert_equal("ruby-lang", ary[1][1])
+ assert_equal("www.ruby-lang.org", ary[2][1])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+ dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org"
+ name = OpenSSL::X509::Name.parse(dn2)
+ ary = name.to_a
+ assert_equal(dn, name.to_s)
+ assert_equal("org", ary[0][1])
+ assert_equal("ruby-lang", ary[1][1])
+ assert_equal("www.ruby-lang.org", ary[2][1])
+
+ name = OpenSSL::X509::Name.parse(dn2, @obj_type_tmpl)
+ ary = name.to_a
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+ end
+
+ def test_s_parse_rfc2253
+ scanner = OpenSSL::X509::Name::RFC2253DN.method(:scan)
+
+ assert_equal([["C", "JP"]], scanner.call("C=JP"))
+ assert_equal([
+ ["DC", "org"],
+ ["DC", "ruby-lang"],
+ ["CN", "GOTOU Yuuzou"],
+ ["emailAddress", "gotoyuzo@ruby-lang.org"],
+ ],
+ scanner.call(
+ "emailAddress=gotoyuzo@ruby-lang.org,CN=GOTOU Yuuzou,"+
+ "DC=ruby-lang,DC=org")
+ )
+
+ u8 = OpenSSL::ASN1::UTF8STRING
+ assert_equal([
+ ["DC", "org"],
+ ["DC", "ruby-lang"],
+ ["O", ",=+<>#;"],
+ ["O", ",=+<>#;"],
+ ["OU", ""],
+ ["OU", ""],
+ ["L", "aaa=\"bbb, ccc\""],
+ ["L", "aaa=\"bbb, ccc\""],
+ ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+ ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+ ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+ ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265", u8],
+ ["2.5.4.3", "GOTOU, Yuuzou"],
+ ["2.5.4.3", "GOTOU, Yuuzou"],
+ ["2.5.4.3", "GOTOU, Yuuzou"],
+ ["2.5.4.3", "GOTOU, Yuuzou"],
+ ["CN", "GOTOU \"gotoyuzo\" Yuuzou"],
+ ["CN", "GOTOU \"gotoyuzo\" Yuuzou"],
+ ["1.2.840.113549.1.9.1", "gotoyuzo@ruby-lang.org"],
+ ["emailAddress", "gotoyuzo@ruby-lang.org"],
+ ],
+ scanner.call(
+ "emailAddress=gotoyuzo@ruby-lang.org," +
+ "1.2.840.113549.1.9.1=gotoyuzo@ruby-lang.org," +
+ 'CN=GOTOU \"gotoyuzo\" Yuuzou,' +
+ 'CN="GOTOU \"gotoyuzo\" Yuuzou",' +
+ '2.5.4.3=GOTOU\,\20Yuuzou,' +
+ '2.5.4.3=GOTOU\, Yuuzou,' +
+ '2.5.4.3="GOTOU, Yuuzou",' +
+ '2.5.4.3="GOTOU\, Yuuzou",' +
+ "CN=#0C0CE5BE8CE897A4E8A395E894B5," +
+ 'CN=\E5\BE\8C\E8\97\A4\E8\A3\95\E8\94\B5,' +
+ "CN=\"\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5\"," +
+ "CN=\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5," +
+ 'L=aaa\=\"bbb\, ccc\",' +
+ 'L="aaa=\"bbb, ccc\"",' +
+ 'OU=,' +
+ 'OU="",' +
+ 'O=\,\=\+\<\>\#\;,' +
+ 'O=",=+<>#;",' +
+ "DC=ruby-lang," +
+ "DC=org")
+ )
+
+ [
+ "DC=org+DC=jp",
+ "DC=org,DC=ruby-lang+DC=rubyist,DC=www"
+ ].each{|dn|
+ ex = scanner.call(dn) rescue $!
+ dn_r = Regexp.escape(dn)
+ assert_match(/^multi-valued RDN is not supported: #{dn_r}/, ex.message)
+ }
+
+ [
+ ["DC=org,DC=exapmle,CN", "CN"],
+ ["DC=org,DC=example,", ""],
+ ["DC=org,DC=exapmle,CN=www.example.org;", "CN=www.example.org;"],
+ ["DC=org,DC=exapmle,CN=#www.example.org", "CN=#www.example.org"],
+ ["DC=org,DC=exapmle,CN=#777777.example.org", "CN=#777777.example.org"],
+ ["DC=org,DC=exapmle,CN=\"www.example\".org", "CN=\"www.example\".org"],
+ ["DC=org,DC=exapmle,CN=www.\"example.org\"", "CN=www.\"example.org\""],
+ ["DC=org,DC=exapmle,CN=www.\"example\".org", "CN=www.\"example\".org"],
+ ].each{|dn, msg|
+ ex = scanner.call(dn) rescue $!
+ assert_match(/^malformed RDN: .*=>#{Regexp.escape(msg)}/, ex.message)
+ }
+
+ dn = "CN=www.ruby-lang.org,DC=ruby-lang,DC=org"
+ name = OpenSSL::X509::Name.parse_rfc2253(dn)
+ assert_equal(dn, name.to_s(OpenSSL::X509::Name::RFC2253))
+ ary = name.to_a
+ assert_equal("DC", ary[0][0])
+ assert_equal("DC", ary[1][0])
+ assert_equal("CN", ary[2][0])
+ assert_equal("org", ary[0][1])
+ assert_equal("ruby-lang", ary[1][1])
+ assert_equal("www.ruby-lang.org", ary[2][1])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+ end
+
+ def test_add_entry
+ dn = [
+ ["DC", "org"],
+ ["DC", "ruby-lang"],
+ ["CN", "GOTOU Yuuzou"],
+ ["emailAddress", "gotoyuzo@ruby-lang.org"],
+ ["serialNumber", "123"],
+ ]
+ name = OpenSSL::X509::Name.new
+ dn.each{|attr| name.add_entry(*attr) }
+ ary = name.to_a
+ assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123", name.to_s)
+ assert_equal("DC", ary[0][0])
+ assert_equal("DC", ary[1][0])
+ assert_equal("CN", ary[2][0])
+ assert_equal("emailAddress", ary[3][0])
+ assert_equal("serialNumber", ary[4][0])
+ assert_equal("org", ary[0][1])
+ assert_equal("ruby-lang", ary[1][1])
+ assert_equal("GOTOU Yuuzou", ary[2][1])
+ assert_equal("gotoyuzo@ruby-lang.org", ary[3][1])
+ assert_equal("123", ary[4][1])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+ assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+ assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2])
+ assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2])
+ end
+
+ def test_add_entry_street
+ return if OpenSSL::OPENSSL_VERSION_NUMBER < 0x009080df # 0.9.8m
+ # openssl/crypto/objects/obj_mac.h 1.83
+ dn = [
+ ["DC", "org"],
+ ["DC", "ruby-lang"],
+ ["CN", "GOTOU Yuuzou"],
+ ["emailAddress", "gotoyuzo@ruby-lang.org"],
+ ["serialNumber", "123"],
+ ["street", "Namiki"],
+ ]
+ name = OpenSSL::X509::Name.new
+ dn.each{|attr| name.add_entry(*attr) }
+ ary = name.to_a
+ assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123/street=Namiki", name.to_s)
+ assert_equal("Namiki", ary[5][1])
+ end
+
+ def test_equals2
+ n1 = OpenSSL::X509::Name.parse 'CN=a'
+ n2 = OpenSSL::X509::Name.parse 'CN=a'
+
+ assert_equal n1, n2
+ end
+
+ def test_spaceship
+ n1 = OpenSSL::X509::Name.parse 'CN=a'
+ n2 = OpenSSL::X509::Name.parse 'CN=b'
+
+ assert_equal(-1, n1 <=> n2)
+ end
+
+ def name_hash(name)
+ # OpenSSL 1.0.0 uses SHA1 for canonical encoding (not just a der) of
+ # X509Name for X509_NAME_hash.
+ name.respond_to?(:hash_old) ? name.hash_old : name.hash
+ end
+
+ def test_hash
+ dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
+ name = OpenSSL::X509::Name.parse(dn)
+ d = Digest::MD5.digest(name.to_der)
+ expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24
+ assert_equal(expected, name_hash(name))
+ #
+ dn = "/DC=org/DC=ruby-lang/CN=baz.ruby-lang.org"
+ name = OpenSSL::X509::Name.parse(dn)
+ d = Digest::MD5.digest(name.to_der)
+ expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24
+ assert_equal(expected, name_hash(name))
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509req.rb b/jni/ruby/test/openssl/test_x509req.rb
new file mode 100644
index 0000000..27040cb
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509req.rb
@@ -0,0 +1,158 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Request < Test::Unit::TestCase
+ def setup
+ @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256
+ @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512
+ @dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou")
+ end
+
+ def issue_csr(ver, dn, key, digest)
+ req = OpenSSL::X509::Request.new
+ req.version = ver
+ req.subject = dn
+ req.public_key = key.public_key
+ req.sign(key, digest)
+ req
+ end
+
+ def test_public_key
+ req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+ assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
+ req = OpenSSL::X509::Request.new(req.to_der)
+ assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
+
+ req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
+ req = OpenSSL::X509::Request.new(req.to_der)
+ assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
+ end
+
+ def test_version
+ req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+ assert_equal(0, req.version)
+ req = OpenSSL::X509::Request.new(req.to_der)
+ assert_equal(0, req.version)
+
+ req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+ assert_equal(1, req.version)
+ req = OpenSSL::X509::Request.new(req.to_der)
+ assert_equal(1, req.version)
+ end
+
+ def test_subject
+ req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+ assert_equal(@dn.to_der, req.subject.to_der)
+ req = OpenSSL::X509::Request.new(req.to_der)
+ assert_equal(@dn.to_der, req.subject.to_der)
+ end
+
+ def create_ext_req(exts)
+ ef = OpenSSL::X509::ExtensionFactory.new
+ exts = exts.collect{|e| ef.create_extension(*e) }
+ return OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)])
+ end
+
+ def get_ext_req(ext_req_value)
+ set = OpenSSL::ASN1.decode(ext_req_value)
+ seq = set.value[0]
+ seq.value.collect{|asn1ext|
+ OpenSSL::X509::Extension.new(asn1ext).to_a
+ }
+ end
+
+ def test_attr
+ exts = [
+ ["keyUsage", "Digital Signature, Key Encipherment", true],
+ ["subjectAltName", "email:gotoyuzo@ruby-lang.org", false],
+ ]
+ attrval = create_ext_req(exts)
+ attrs = [
+ OpenSSL::X509::Attribute.new("extReq", attrval),
+ OpenSSL::X509::Attribute.new("msExtReq", attrval),
+ ]
+
+ req0 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+ attrs.each{|attr| req0.add_attribute(attr) }
+ req1 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+ req1.attributes = attrs
+ assert_equal(req0.to_der, req1.to_der)
+
+ attrs = req0.attributes
+ assert_equal(2, attrs.size)
+ assert_equal("extReq", attrs[0].oid)
+ assert_equal("msExtReq", attrs[1].oid)
+ assert_equal(exts, get_ext_req(attrs[0].value))
+ assert_equal(exts, get_ext_req(attrs[1].value))
+
+ req = OpenSSL::X509::Request.new(req0.to_der)
+ attrs = req.attributes
+ assert_equal(2, attrs.size)
+ assert_equal("extReq", attrs[0].oid)
+ assert_equal("msExtReq", attrs[1].oid)
+ assert_equal(exts, get_ext_req(attrs[0].value))
+ assert_equal(exts, get_ext_req(attrs[1].value))
+ end
+
+ def test_sign_and_verify_rsa_sha1
+ req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+ assert_equal(true, req.verify(@rsa1024))
+ assert_equal(false, req.verify(@rsa2048))
+ assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
+ assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
+ req.version = 1
+ assert_equal(false, req.verify(@rsa1024))
+ end
+
+ def test_sign_and_verify_rsa_md5
+ req = issue_csr(0, @dn, @rsa2048, OpenSSL::Digest::MD5.new)
+ assert_equal(false, req.verify(@rsa1024))
+ assert_equal(true, req.verify(@rsa2048))
+ assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
+ assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
+ req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBar")
+ assert_equal(false, req.verify(@rsa2048))
+ rescue OpenSSL::X509::RequestError # RHEL7 disables MD5
+ end
+
+ def test_sign_and_verify_dsa
+ req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+ assert_equal(false, request_error_returns_false { req.verify(@rsa1024) })
+ assert_equal(false, request_error_returns_false { req.verify(@rsa2048) })
+ assert_equal(false, req.verify(@dsa256))
+ assert_equal(true, req.verify(@dsa512))
+ req.public_key = @rsa1024.public_key
+ assert_equal(false, req.verify(@dsa512))
+ end
+
+ def test_sign_and_verify_rsa_dss1
+ req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::DSS1.new)
+ assert_equal(true, req.verify(@rsa1024))
+ assert_equal(false, req.verify(@rsa2048))
+ assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
+ assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
+ req.version = 1
+ assert_equal(false, req.verify(@rsa1024))
+ rescue OpenSSL::X509::RequestError
+ skip
+ end
+
+ def test_sign_and_verify_dsa_md5
+ assert_raise(OpenSSL::X509::RequestError){
+ issue_csr(0, @dn, @dsa512, OpenSSL::Digest::MD5.new) }
+ end
+
+ private
+
+ def request_error_returns_false
+ yield
+ rescue OpenSSL::X509::RequestError
+ false
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509store.rb b/jni/ruby/test/openssl/test_x509store.rb
new file mode 100644
index 0000000..f3e144f
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509store.rb
@@ -0,0 +1,231 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Store < Test::Unit::TestCase
+ def setup
+ @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ @dsa256 = OpenSSL::TestUtils::TEST_KEY_DSA256
+ @dsa512 = OpenSSL::TestUtils::TEST_KEY_DSA512
+ @ca1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA1")
+ @ca2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA2")
+ @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+ @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+ end
+
+ def teardown
+ end
+
+ def test_nosegv_on_cleanup
+ cert = OpenSSL::X509::Certificate.new
+ store = OpenSSL::X509::Store.new
+ ctx = OpenSSL::X509::StoreContext.new(store, cert, [])
+ EnvUtil.suppress_warning do
+ ctx.cleanup
+ end
+ ctx.verify
+ end
+
+ def issue_cert(*args)
+ OpenSSL::TestUtils.issue_cert(*args)
+ end
+
+ def issue_crl(*args)
+ OpenSSL::TestUtils.issue_crl(*args)
+ end
+
+ def test_verify
+ now = Time.at(Time.now.to_i)
+ ca_exts = [
+ ["basicConstraints","CA:TRUE",true],
+ ["keyUsage","cRLSign,keyCertSign",true],
+ ]
+ ee_exts = [
+ ["keyUsage","keyEncipherment,digitalSignature",true],
+ ]
+ ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, ca_exts,
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ ca2_cert = issue_cert(@ca2, @rsa1024, 2, now, now+1800, ca_exts,
+ ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ ee1_cert = issue_cert(@ee1, @dsa256, 10, now, now+1800, ee_exts,
+ ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+ ee2_cert = issue_cert(@ee2, @dsa512, 20, now, now+1800, ee_exts,
+ ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+ ee3_cert = issue_cert(@ee2, @dsa512, 30, now-100, now-1, ee_exts,
+ ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+ ee4_cert = issue_cert(@ee2, @dsa512, 40, now+1000, now+2000, ee_exts,
+ ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+
+ revoke_info = []
+ crl1 = issue_crl(revoke_info, 1, now, now+1800, [],
+ ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ revoke_info = [ [2, now, 1], ]
+ crl1_2 = issue_crl(revoke_info, 2, now, now+1800, [],
+ ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ revoke_info = [ [20, now, 1], ]
+ crl2 = issue_crl(revoke_info, 1, now, now+1800, [],
+ ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+ revoke_info = []
+ crl2_2 = issue_crl(revoke_info, 2, now-100, now-1, [],
+ ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+
+ assert_equal(true, ca1_cert.verify(ca1_cert.public_key)) # self signed
+ assert_equal(true, ca2_cert.verify(ca1_cert.public_key)) # issued by ca1
+ assert_equal(true, ee1_cert.verify(ca2_cert.public_key)) # issued by ca2
+ assert_equal(true, ee2_cert.verify(ca2_cert.public_key)) # issued by ca2
+ assert_equal(true, ee3_cert.verify(ca2_cert.public_key)) # issued by ca2
+ assert_equal(true, crl1.verify(ca1_cert.public_key)) # issued by ca1
+ assert_equal(true, crl1_2.verify(ca1_cert.public_key)) # issued by ca1
+ assert_equal(true, crl2.verify(ca2_cert.public_key)) # issued by ca2
+ assert_equal(true, crl2_2.verify(ca2_cert.public_key)) # issued by ca2
+
+ store = OpenSSL::X509::Store.new
+ assert_equal(false, store.verify(ca1_cert))
+ assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+ assert_equal(false, store.verify(ca2_cert))
+ assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+ store.add_cert(ca1_cert)
+ assert_equal(true, store.verify(ca2_cert))
+ assert_equal(OpenSSL::X509::V_OK, store.error)
+ assert_equal("ok", store.error_string)
+ chain = store.chain
+ assert_equal(2, chain.size)
+ assert_equal(@ca2.to_der, chain[0].subject.to_der)
+ assert_equal(@ca1.to_der, chain[1].subject.to_der)
+
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ assert_equal(false, store.verify(ca2_cert))
+ assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+ store.purpose = OpenSSL::X509::PURPOSE_CRL_SIGN
+ assert_equal(true, store.verify(ca2_cert))
+ assert_equal(OpenSSL::X509::V_OK, store.error)
+
+ store.add_cert(ca2_cert)
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ assert_equal(true, store.verify(ee1_cert))
+ assert_equal(true, store.verify(ee2_cert))
+ assert_equal(OpenSSL::X509::V_OK, store.error)
+ assert_equal("ok", store.error_string)
+ chain = store.chain
+ assert_equal(3, chain.size)
+ assert_equal(@ee2.to_der, chain[0].subject.to_der)
+ assert_equal(@ca2.to_der, chain[1].subject.to_der)
+ assert_equal(@ca1.to_der, chain[2].subject.to_der)
+ assert_equal(false, store.verify(ee3_cert))
+ assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+ assert_match(/expire/i, store.error_string)
+ assert_equal(false, store.verify(ee4_cert))
+ assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+ assert_match(/not yet valid/i, store.error_string)
+
+ store = OpenSSL::X509::Store.new
+ store.add_cert(ca1_cert)
+ store.add_cert(ca2_cert)
+ store.time = now + 1500
+ assert_equal(true, store.verify(ca1_cert))
+ assert_equal(true, store.verify(ca2_cert))
+ assert_equal(true, store.verify(ee4_cert))
+ store.time = now + 1900
+ assert_equal(true, store.verify(ca1_cert))
+ assert_equal(false, store.verify(ca2_cert))
+ assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+ assert_equal(false, store.verify(ee4_cert))
+ assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+ store.time = now + 4000
+ assert_equal(false, store.verify(ee1_cert))
+ assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+ assert_equal(false, store.verify(ee4_cert))
+ assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+
+ # the underlying X509 struct caches the result of the last
+ # verification for signature and not-before. so the following code
+ # rebuilds new objects to avoid site effect.
+ store.time = Time.now - 4000
+ assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ca2_cert)))
+ assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+ assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ee1_cert)))
+ assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+
+ return unless defined?(OpenSSL::X509::V_FLAG_CRL_CHECK)
+
+ store = OpenSSL::X509::Store.new
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
+ store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
+ store.add_cert(ca1_cert)
+ store.add_crl(crl1) # revoke no cert
+ store.add_crl(crl2) # revoke ee2_cert
+ assert_equal(true, store.verify(ca1_cert))
+ assert_equal(true, store.verify(ca2_cert))
+ assert_equal(true, store.verify(ee1_cert, [ca2_cert]))
+ assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+ store = OpenSSL::X509::Store.new
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
+ store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
+ store.add_cert(ca1_cert)
+ store.add_crl(crl1_2) # revoke ca2_cert
+ store.add_crl(crl2) # revoke ee2_cert
+ assert_equal(true, store.verify(ca1_cert))
+ assert_equal(false, store.verify(ca2_cert))
+ assert_equal(true, store.verify(ee1_cert, [ca2_cert]),
+ "This test is expected to be success with OpenSSL 0.9.7c or later.")
+ assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+ store.flags =
+ OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+ assert_equal(true, store.verify(ca1_cert))
+ assert_equal(false, store.verify(ca2_cert))
+ assert_equal(false, store.verify(ee1_cert, [ca2_cert]))
+ assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+ store = OpenSSL::X509::Store.new
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
+ store.flags =
+ OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+ store.add_cert(ca1_cert)
+ store.add_cert(ca2_cert)
+ store.add_crl(crl1)
+ store.add_crl(crl2_2) # issued by ca2 but expired.
+ assert_equal(true, store.verify(ca1_cert))
+ assert_equal(true, store.verify(ca2_cert))
+ assert_equal(false, store.verify(ee1_cert))
+ assert_equal(OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED, store.error)
+ assert_equal(false, store.verify(ee2_cert))
+ end
+
+ def test_set_errors
+ now = Time.now
+ ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, [],
+ nil, nil, OpenSSL::Digest::SHA1.new)
+ store = OpenSSL::X509::Store.new
+ store.add_cert(ca1_cert)
+ assert_raise(OpenSSL::X509::StoreError){
+ store.add_cert(ca1_cert) # add same certificate twice
+ }
+
+ revoke_info = []
+ crl1 = issue_crl(revoke_info, 1, now, now+1800, [],
+ ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ revoke_info = [ [2, now, 1], ]
+ crl2 = issue_crl(revoke_info, 2, now+1800, now+3600, [],
+ ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+ store.add_crl(crl1)
+ if /0\.9\.8.*-rhel/ =~ OpenSSL::OPENSSL_VERSION
+ # RedHat is distributing a patched version of OpenSSL that allows
+ # multiple CRL for a key (multi-crl.patch)
+ assert_nothing_raised do
+ store.add_crl(crl2) # add CRL issued by same CA twice.
+ end
+ else
+ assert_raise(OpenSSL::X509::StoreError){
+ store.add_crl(crl2) # add CRL issued by same CA twice.
+ }
+ end
+ end
+end
+
+end
diff --git a/jni/ruby/test/openssl/utils.rb b/jni/ruby/test/openssl/utils.rb
new file mode 100644
index 0000000..bd936be
--- /dev/null
+++ b/jni/ruby/test/openssl/utils.rb
@@ -0,0 +1,333 @@
+begin
+ require "openssl"
+
+ # Disable FIPS mode for tests for installations
+ # where FIPS mode would be enabled by default.
+ # Has no effect on all other installations.
+ OpenSSL.fips_mode=false
+rescue LoadError
+end
+require "test/unit"
+require "digest/md5"
+require 'tempfile'
+require "rbconfig"
+require "socket"
+
+module OpenSSL::TestUtils
+ TEST_KEY_RSA1024 = OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
+aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
+Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
+AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
+maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
+gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
+74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
+JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
+sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
+8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
+wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
+qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
+dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
+-----END RSA PRIVATE KEY-----
+ _end_of_pem_
+
+ TEST_KEY_RSA2048 = OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
+s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
+4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
+kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
+NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
+DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
+I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
+PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
+seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
+Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
+VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
+wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
+0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
+XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
+aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
+h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
+Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
+IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
+v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
+U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
+vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
+Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
+9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
+gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
+4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
+-----END RSA PRIVATE KEY-----
+ _end_of_pem_
+
+ TEST_KEY_DSA256 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
+-----BEGIN DSA PRIVATE KEY-----
+MIH3AgEAAkEAhk2libbY2a8y2Pt21+YPYGZeW6wzaW2yfj5oiClXro9XMR7XWLkE
+9B7XxLNFCS2gmCCdMsMW1HulaHtLFQmB2wIVAM43JZrcgpu6ajZ01VkLc93gu/Ed
+AkAOhujZrrKV5CzBKutKLb0GVyVWmdC7InoNSMZEeGU72rT96IjM59YzoqmD0pGM
+3I1o4cGqg1D1DfM1rQlnN1eSAkBq6xXfEDwJ1mLNxF6q8Zm/ugFYWR5xcX/3wFiT
+b4+EjHP/DbNh9Vm5wcfnDBJ1zKvrMEf2xqngYdrV/3CiGJeKAhRvL57QvJZcQGvn
+ISNX5cMzFHRW3Q==
+-----END DSA PRIVATE KEY-----
+ _end_of_pem_
+
+ TEST_KEY_DSA512 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
+-----BEGIN DSA PRIVATE KEY-----
+MIH4AgEAAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgTYiEEHaOYhkIxv0Ok
+RZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB4DZGH7UyarcaGy6D
+AkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqoji3/lHdKoVdTQNuR
+S/m6DlCwhjRjiQ/lBRgCLCcaAkEAjN891JBjzpMj4bWgsACmMggFf57DS0Ti+5++
+Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxXoXi9OAIUBG98h4tilg6S
+55jreJD3Se3slps=
+-----END DSA PRIVATE KEY-----
+ _end_of_pem_
+
+if defined?(OpenSSL::PKey::EC)
+
+ TEST_KEY_EC_P256V1 = OpenSSL::PKey::EC.new <<-_end_of_pem_
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49
+AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt
+CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg==
+-----END EC PRIVATE KEY-----
+ _end_of_pem_
+
+end
+
+ TEST_KEY_DH512_PUB = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MEYCQQDmWXGPqk76sKw/edIOdhAQD4XzjJ+AR/PTk2qzaGs+u4oND2yU5D2NN4wr
+aPgwHyJBiK1/ebK3tYcrSKrOoRyrAgEC
+-----END DH PARAMETERS-----
+ _end_of_pem_
+
+ TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
+pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
+AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
+-----END DH PARAMETERS-----
+ _end_of_pem_
+
+ TEST_KEY_DH1024.priv_key = OpenSSL::BN.new("48561834C67E65FFD2A9B47F41E5E78FDC95C387428FDB1E4B0188B64D1643C3A8D3455B945B7E8C4D166010C7C2CE23BFB9BEF43D0348FE7FA5284B0225E7FE1537546D114E3D8A4411B9B9351AB451E1A358F50ED61B1F00DA29336EEBBD649980AC86D76AF8BBB065298C2052672EEF3EF13AB47A15275FC2836F3AC74CEA", 16)
+
+ DSA_SIGNATURE_DIGEST = OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000 ?
+ OpenSSL::Digest::SHA1 :
+ OpenSSL::Digest::DSS1
+
+ module_function
+
+ def issue_cert(dn, key, serial, not_before, not_after, extensions,
+ issuer, issuer_key, digest)
+ cert = OpenSSL::X509::Certificate.new
+ issuer = cert unless issuer
+ issuer_key = key unless issuer_key
+ cert.version = 2
+ cert.serial = serial
+ cert.subject = dn
+ cert.issuer = issuer.subject
+ cert.public_key = key.public_key
+ cert.not_before = not_before
+ cert.not_after = not_after
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = cert
+ ef.issuer_certificate = issuer
+ extensions.each{|oid, value, critical|
+ cert.add_extension(ef.create_extension(oid, value, critical))
+ }
+ cert.sign(issuer_key, digest)
+ cert
+ end
+
+ def issue_crl(revoke_info, serial, lastup, nextup, extensions,
+ issuer, issuer_key, digest)
+ crl = OpenSSL::X509::CRL.new
+ crl.issuer = issuer.subject
+ crl.version = 1
+ crl.last_update = lastup
+ crl.next_update = nextup
+ revoke_info.each{|rserial, time, reason_code|
+ revoked = OpenSSL::X509::Revoked.new
+ revoked.serial = rserial
+ revoked.time = time
+ enum = OpenSSL::ASN1::Enumerated(reason_code)
+ ext = OpenSSL::X509::Extension.new("CRLReason", enum)
+ revoked.add_extension(ext)
+ crl.add_revoked(revoked)
+ }
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.issuer_certificate = issuer
+ ef.crl = crl
+ crlnum = OpenSSL::ASN1::Integer(serial)
+ crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum))
+ extensions.each{|oid, value, critical|
+ crl.add_extension(ef.create_extension(oid, value, critical))
+ }
+ crl.sign(issuer_key, digest)
+ crl
+ end
+
+ def get_subject_key_id(cert)
+ asn1_cert = OpenSSL::ASN1.decode(cert)
+ tbscert = asn1_cert.value[0]
+ pkinfo = tbscert.value[6]
+ publickey = pkinfo.value[1]
+ pkvalue = publickey.value
+ OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase
+ end
+
+ def silent
+ begin
+ back, $VERBOSE = $VERBOSE, nil
+ yield
+ ensure
+ $VERBOSE = back
+ end
+ end
+
+ class OpenSSL::SSLTestCase < Test::Unit::TestCase
+ RUBY = EnvUtil.rubybin
+ ITERATIONS = ($0 == __FILE__) ? 100 : 10
+
+ def setup
+ @ca_key = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ @svr_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ @cli_key = OpenSSL::TestUtils::TEST_KEY_DSA256
+ @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+ @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+ @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+ now = Time.at(Time.now.to_i)
+ ca_exts = [
+ ["basicConstraints","CA:TRUE",true],
+ ["keyUsage","cRLSign,keyCertSign",true],
+ ]
+ ee_exts = [
+ ["keyUsage","keyEncipherment,digitalSignature",true],
+ ]
+ @ca_cert = issue_cert(@ca, @ca_key, 1, now, now+3600, ca_exts, nil, nil, OpenSSL::Digest::SHA1.new)
+ @svr_cert = issue_cert(@svr, @svr_key, 2, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+ @cli_cert = issue_cert(@cli, @cli_key, 3, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+ @server = nil
+ end
+
+ def teardown
+ end
+
+ def issue_cert(*arg)
+ OpenSSL::TestUtils.issue_cert(*arg)
+ end
+
+ def issue_crl(*arg)
+ OpenSSL::TestUtils.issue_crl(*arg)
+ end
+
+ def readwrite_loop(ctx, ssl)
+ while line = ssl.gets
+ if line =~ /^STARTTLS$/
+ ssl.accept
+ next
+ end
+ ssl.write(line)
+ end
+ rescue OpenSSL::SSL::SSLError
+ rescue IOError
+ ensure
+ ssl.close rescue nil
+ end
+
+ def server_loop(ctx, ssls, stop_pipe_r, ignore_listener_error, server_proc, threads)
+ loop do
+ ssl = nil
+ begin
+ readable, = IO.select([ssls, stop_pipe_r])
+ if readable.include? stop_pipe_r
+ return
+ end
+ ssl = ssls.accept
+ rescue OpenSSL::SSL::SSLError
+ if ignore_listener_error
+ retry
+ else
+ raise
+ end
+ end
+
+ th = Thread.start do
+ server_proc.call(ctx, ssl)
+ end
+ threads << th
+ end
+ rescue Errno::EBADF, IOError, Errno::EINVAL, Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
+ if !ignore_listener_error
+ raise
+ end
+ end
+
+ def start_server(verify_mode, start_immediately, args = {}, &block)
+ IO.pipe {|stop_pipe_r, stop_pipe_w|
+ ctx_proc = args[:ctx_proc]
+ server_proc = args[:server_proc]
+ ignore_listener_error = args.fetch(:ignore_listener_error, false)
+ use_anon_cipher = args.fetch(:use_anon_cipher, false)
+ server_proc ||= method(:readwrite_loop)
+
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ciphers = "ADH-AES256-GCM-SHA384" if use_anon_cipher
+ ctx.cert_store = store
+ #ctx.extra_chain_cert = [ ca_cert ]
+ ctx.cert = @svr_cert
+ ctx.key = @svr_key
+ ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
+ ctx.verify_mode = verify_mode
+ ctx_proc.call(ctx) if ctx_proc
+
+ Socket.do_not_reverse_lookup = true
+ tcps = nil
+ tcps = TCPServer.new("127.0.0.1", 0)
+ port = tcps.connect_address.ip_port
+
+ ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
+ ssls.start_immediately = start_immediately
+
+ threads = []
+ begin
+ server = Thread.new do
+ begin
+ server_loop(ctx, ssls, stop_pipe_r, ignore_listener_error, server_proc, threads)
+ ensure
+ tcps.close
+ end
+ end
+ threads.unshift server
+
+ $stderr.printf("SSL server started: pid=%d port=%d\n", $$, port) if $DEBUG
+
+ client = Thread.new do
+ begin
+ block.call(server, port.to_i)
+ ensure
+ stop_pipe_w.close
+ end
+ end
+ threads.unshift client
+ ensure
+ assert_join_threads(threads)
+ end
+ }
+ end
+
+ def starttls(ssl)
+ ssl.puts("STARTTLS")
+ sleep 1 # When this line is eliminated, process on Cygwin blocks
+ # forever at ssl.connect. But I don't know why it does.
+ ssl.connect
+ end
+ end
+
+end if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) and
+ /\AOpenSSL +0\./ !~ OpenSSL::OPENSSL_LIBRARY_VERSION
diff --git a/jni/ruby/test/optparse/test_acceptable.rb b/jni/ruby/test/optparse/test_acceptable.rb
new file mode 100644
index 0000000..6ec619e
--- /dev/null
+++ b/jni/ruby/test/optparse/test_acceptable.rb
@@ -0,0 +1,195 @@
+require_relative 'test_optparse'
+
+class TestOptionParser::Acceptable < TestOptionParser
+
+ def setup
+ super
+ @opt.def_option("--integer VAL", Integer) { |v| @integer = v }
+ @opt.def_option("--float VAL", Float) { |v| @float = v }
+ @opt.def_option("--numeric VAL", Numeric) { |v| @numeric = v }
+
+ @opt.def_option("--decimal-integer VAL",
+ OptionParser::DecimalInteger) { |i| @decimal_integer = i }
+ @opt.def_option("--octal-integer VAL",
+ OptionParser::OctalInteger) { |i| @octal_integer = i }
+ @opt.def_option("--decimal-numeric VAL",
+ OptionParser::DecimalNumeric) { |i| @decimal_numeric = i }
+ end
+
+ def test_integer
+ assert_equal(%w"", no_error {@opt.parse!(%w"--integer 0")})
+ assert_equal(0, @integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--integer 0b10")})
+ assert_equal(2, @integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--integer 077")})
+ assert_equal(63, @integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--integer 10")})
+ assert_equal(10, @integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--integer 0x3")})
+ assert_equal(3, @integer)
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--integer 0b")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--integer 09")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--integer 0x")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--integer 1234xyz")
+ end
+ end
+
+ def test_float
+ assert_equal(%w"", no_error {@opt.parse!(%w"--float 0")})
+ assert_in_epsilon(0.0, @float)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--float 0.0")})
+ assert_in_epsilon(0.0, @float)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--float 1.2")})
+ assert_in_epsilon(1.2, @float)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--float 1E2")})
+ assert_in_epsilon(100, @float)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--float 1E-2")})
+ assert_in_epsilon(0.01, @float)
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--float 0e")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--float 1.234xyz")
+ end
+ end
+
+ def test_numeric
+ assert_equal(%w"", no_error {@opt.parse!(%w"--numeric 0")})
+ assert_equal(0, @numeric)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--numeric 0/1")})
+ assert_equal(0, @numeric)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--numeric 1/2")})
+ assert_equal(Rational(1, 2), @numeric)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--numeric 1.2/2.3")})
+ assert_equal(Rational(12, 23), @numeric)
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--numeric 1/")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--numeric 12/34xyz")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--numeric 12x/34yz")
+ end
+ end
+
+ def test_decimal_integer
+ assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-integer 0")})
+ assert_equal(0, @decimal_integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-integer 10")})
+ assert_equal(10, @decimal_integer)
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--decimal-integer 0b1")
+ end
+
+ e = assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--decimal-integer 09")
+ end
+
+ assert_equal("invalid argument: --decimal-integer 09", e.message)
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--decimal-integer x")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--decimal-integer 1234xyz")
+ end
+ end
+
+ def test_octal_integer
+ assert_equal(%w"", no_error {@opt.parse!(%w"--octal-integer 0")})
+ assert_equal(0, @octal_integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--octal-integer 6")})
+ assert_equal(6, @octal_integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--octal-integer 07")})
+ assert_equal(7, @octal_integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--octal-integer 10")})
+ assert_equal(8, @octal_integer)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--octal-integer 011")})
+ assert_equal(9, @octal_integer)
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--octal-integer 09")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--octal-integer 0b1")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--octal-integer x")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--octal-integer 01234xyz")
+ end
+ end
+
+ def test_decimal_numeric
+ assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-numeric 0")})
+ assert_equal(0, @decimal_numeric)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-numeric 01")})
+ assert_equal(1, @decimal_numeric)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-numeric 1.2")})
+ assert_in_delta(1.2, @decimal_numeric)
+
+ assert_equal(%w"", no_error {@opt.parse!(%w"--decimal-numeric 1E2")})
+ assert_in_delta(100.0, @decimal_numeric)
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--decimal-numeric 0b1")
+ end
+
+ e = assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--decimal-numeric 09")
+ end
+
+ assert_equal("invalid argument: --decimal-numeric 09", e.message)
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--decimal-integer 1234xyz")
+ end
+
+ assert_raises(OptionParser::InvalidArgument) do
+ @opt.parse!(%w"--decimal-integer 12.34xyz")
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/optparse/test_autoconf.rb b/jni/ruby/test/optparse/test_autoconf.rb
new file mode 100644
index 0000000..cb9c938
--- /dev/null
+++ b/jni/ruby/test/optparse/test_autoconf.rb
@@ -0,0 +1,63 @@
+require 'test/unit'
+require 'optparse/ac'
+
+class TestOptionParser < Test::Unit::TestCase; end
+
+class TestOptionParser::AutoConf < Test::Unit::TestCase
+ def setup
+ @opt = OptionParser::AC.new
+ @foo = @bar = self.class
+ @opt.ac_arg_enable("foo", "foo option") {|x| @foo = x}
+ @opt.ac_arg_disable("bar", "bar option") {|x| @bar = x}
+ @opt.ac_arg_with("zot", "zot option") {|x| @zot = x}
+ end
+
+ class DummyOutput < String
+ alias write <<
+ end
+ def no_error(*args)
+ $stderr, stderr = DummyOutput.new, $stderr
+ assert_nothing_raised(*args) {return yield}
+ ensure
+ stderr, $stderr = $stderr, stderr
+ $!.backtrace.delete_if {|e| /\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}/o =~ e} if $!
+ assert_empty(stderr)
+ end
+
+ def test_enable
+ @opt.parse!(%w"--enable-foo")
+ assert_equal(true, @foo)
+ @opt.parse!(%w"--enable-bar")
+ assert_equal(true, @bar)
+ end
+
+ def test_disable
+ @opt.parse!(%w"--disable-foo")
+ assert_equal(false, @foo)
+ @opt.parse!(%w"--disable-bar")
+ assert_equal(false, @bar)
+ end
+
+ def test_with
+ @opt.parse!(%w"--with-zot=foobar")
+ assert_equal("foobar", @zot)
+ @opt.parse!(%w"--without-zot")
+ assert_nil(@zot)
+ end
+
+ def test_without
+ @opt.parse!(%w"--without-zot")
+ assert_nil(@zot)
+ assert_raise(OptionParser::NeedlessArgument) {@opt.parse!(%w"--without-zot=foobar")}
+ end
+
+ def test_help
+ help = @opt.help
+ assert_match(/--enable-foo/, help)
+ assert_match(/--disable-bar/, help)
+ assert_match(/--with-zot/, help)
+ assert_not_match(/--disable-foo/, help)
+ assert_not_match(/--enable-bar/, help)
+ assert_not_match(/--without/, help)
+ end
+end
diff --git a/jni/ruby/test/optparse/test_bash_completion.rb b/jni/ruby/test/optparse/test_bash_completion.rb
new file mode 100644
index 0000000..baeb6d9
--- /dev/null
+++ b/jni/ruby/test/optparse/test_bash_completion.rb
@@ -0,0 +1,42 @@
+require 'test/unit'
+require 'optparse'
+
+class TestOptionParser < Test::Unit::TestCase
+end
+class TestOptionParser::BashCompletion < Test::Unit::TestCase
+ def setup
+ @opt = OptionParser.new
+ @opt.define("-z", "zzz") {}
+ @opt.define("--foo") {}
+ @opt.define("--bar=BAR") {}
+ @opt.define("--for=TYPE", [:hello, :help, :zot]) {}
+ end
+
+ def test_empty
+ assert_equal([], @opt.candidate(""))
+ end
+
+ def test_one_hyphen
+ assert_equal(%w[-z --foo --bar= --for=], @opt.candidate("-"))
+ end
+
+ def test_two_hyphen
+ assert_equal(%w[--foo --bar= --for=], @opt.candidate("--"))
+ end
+
+ def test_long_f
+ assert_equal(%w[--foo --for=], @opt.candidate("--f"))
+ end
+
+ def test_long_for_option
+ assert_equal(%w[--for=], @opt.candidate("--for"))
+ end
+
+ def test_long_for_option_args
+ assert_equal(%w[hello help zot], @opt.candidate("--for="))
+ end
+
+ def test_long_for_option_complete
+ assert_equal(%w[hello help], @opt.candidate("--for=h"))
+ end
+end
diff --git a/jni/ruby/test/optparse/test_getopts.rb b/jni/ruby/test/optparse/test_getopts.rb
new file mode 100644
index 0000000..ae22f68
--- /dev/null
+++ b/jni/ruby/test/optparse/test_getopts.rb
@@ -0,0 +1,34 @@
+require 'test/unit'
+require 'optparse'
+
+class TestOptionParser < Test::Unit::TestCase
+end
+class TestOptionParser::Getopts < Test::Unit::TestCase
+ def setup
+ @opt = OptionParser.new
+ end
+
+ def test_short_noarg
+ o = @opt.getopts(%w[-a], "ab")
+ assert_equal(true, o['a'])
+ assert_equal(false, o['b'])
+ end
+
+ def test_short_arg
+ o = @opt.getopts(%w[-a1], "a:b:")
+ assert_equal("1", o['a'])
+ assert_equal(nil, o['b'])
+ end
+
+ def test_long_noarg
+ o = @opt.getopts(%w[--foo], "", "foo", "bar")
+ assert_equal(true, o['foo'])
+ assert_equal(false, o['bar'])
+ end
+
+ def test_long_arg
+ o = @opt.getopts(%w[--bar ZOT], "", "foo:FOO", "bar:BAR")
+ assert_equal("FOO", o['foo'])
+ assert_equal("ZOT", o['bar'])
+ end
+end
diff --git a/jni/ruby/test/optparse/test_noarg.rb b/jni/ruby/test/optparse/test_noarg.rb
new file mode 100644
index 0000000..3e6ed42
--- /dev/null
+++ b/jni/ruby/test/optparse/test_noarg.rb
@@ -0,0 +1,57 @@
+require_relative 'test_optparse'
+
+module TestOptionParser::NoArg
+ class Def1 < TestOptionParser
+ include NoArg
+ def setup
+ super
+ @opt.def_option("-x") {|x| @flag = x}
+ @opt.def_option("--option") {|x| @flag = x}
+ end
+ end
+ class Def2 < TestOptionParser
+ include NoArg
+ def setup
+ super
+ @opt.def_option("-x", "--option") {|x| @flag = x}
+ end
+ end
+
+ def test_short
+ assert_raise(OptionParser::InvalidOption) {@opt.parse!(%w"-xq")}
+ assert_equal(%w"", no_error {@opt.parse!(%w"-x")})
+ assert_equal(true, @flag)
+ @flag = nil
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"-x foo")})
+ assert_equal(true, @flag)
+ end
+
+ def test_abbrev
+ assert_raise(OptionParser::InvalidOption) {@opt.parse!(%w"-oq")}
+ assert_equal(%w"", no_error {@opt.parse!(%w"-o")})
+ assert_equal(true, @flag)
+ @flag = nil
+ assert_raise(OptionParser::InvalidOption) {@opt.parse!(%w"-O")}
+ assert_nil(@flag)
+ @flag = nil
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"-o foo")})
+ assert_equal(true, @flag)
+ end
+
+ def test_long
+ assert_raise(OptionParser::NeedlessArgument) {@opt.parse!(%w"--option=x")}
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt")})
+ assert_equal(true, @flag)
+ @flag = nil
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"--opt foo")})
+ assert_equal(true, @flag)
+ end
+
+ def test_ambiguous
+ @opt.def_option("--open") {|x|}
+ assert_raise(OptionParser::AmbiguousOption) {@opt.parse!(%w"--op")}
+ assert_raise(OptionParser::AmbiguousOption) {@opt.parse!(%w"-o")}
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt")})
+ assert_equal(true, @flag)
+ end
+end
diff --git a/jni/ruby/test/optparse/test_optarg.rb b/jni/ruby/test/optparse/test_optarg.rb
new file mode 100644
index 0000000..3114b80
--- /dev/null
+++ b/jni/ruby/test/optparse/test_optarg.rb
@@ -0,0 +1,46 @@
+require_relative 'test_optparse'
+
+class TestOptionParser::OptArg < TestOptionParser
+ def setup
+ super
+ @opt.def_option("-x[VAL]") {|x| @flag = x}
+ @opt.def_option("--option[=VAL]") {|x| @flag = x}
+ @opt.def_option("--regexp[=REGEXP]", Regexp) {|x| @reopt = x}
+ @reopt = nil
+ end
+
+ def test_short
+ assert_equal(%w"", no_error {@opt.parse!(%w"-x")})
+ assert_equal(nil, @flag)
+ @flag = false
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"-x foo")})
+ assert_equal(nil, @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-xfoo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-x=")})
+ assert_equal("=", @flag)
+ end
+
+ def test_abbrev
+ assert_equal(%w"", no_error {@opt.parse!(%w"-o")})
+ assert_equal(nil, @flag)
+ @flag = false
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"-o foo")})
+ assert_equal(nil, @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-ofoo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-o=")})
+ assert_equal("=", @flag)
+ end
+
+ def test_long
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt")})
+ assert_equal(nil, @flag)
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"--opt= foo")})
+ assert_equal("", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt=foo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"--opt foo")})
+ assert_equal(nil, @flag)
+ end
+end
diff --git a/jni/ruby/test/optparse/test_optparse.rb b/jni/ruby/test/optparse/test_optparse.rb
new file mode 100644
index 0000000..e85a2ef
--- /dev/null
+++ b/jni/ruby/test/optparse/test_optparse.rb
@@ -0,0 +1,66 @@
+require 'test/unit'
+require 'optparse'
+
+class TestOptionParser < Test::Unit::TestCase
+ def setup
+ @opt = OptionParser.new
+ @flag = self.class # cannot set by option
+ end
+
+ class DummyOutput < String
+ alias write <<
+ end
+ def assert_no_error(*args)
+ $stderr, stderr = DummyOutput.new, $stderr
+ assert_nothing_raised(*args) {return yield}
+ ensure
+ stderr, $stderr = $stderr, stderr
+ $!.backtrace.delete_if {|e| /\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}/o =~ e} if $!
+ assert_empty(stderr)
+ end
+ alias no_error assert_no_error
+
+ def test_permute
+ assert_equal(%w"", no_error {@opt.permute!(%w"")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo bar", no_error {@opt.permute!(%w"foo bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"- foo bar", no_error {@opt.permute!(%w"- foo bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo bar", no_error {@opt.permute!(%w"-- foo bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo - bar", no_error {@opt.permute!(%w"foo - bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo bar", no_error {@opt.permute!(%w"foo -- bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo --help bar", no_error {@opt.permute!(%w"foo -- --help bar")})
+ assert_equal(self.class, @flag)
+ end
+
+ def test_order
+ assert_equal(%w"", no_error {@opt.order!(%w"")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo bar", no_error {@opt.order!(%w"foo bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"- foo bar", no_error {@opt.order!(%w"- foo bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo bar", no_error {@opt.permute!(%w"-- foo bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo - bar", no_error {@opt.order!(%w"foo - bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo -- bar", no_error {@opt.order!(%w"foo -- bar")})
+ assert_equal(self.class, @flag)
+ assert_equal(%w"foo -- --help bar", no_error {@opt.order!(%w"foo -- --help bar")})
+ assert_equal(self.class, @flag)
+ end
+
+ def test_regexp
+ return unless defined?(@reopt)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--regexp=/foo/")})
+ assert_equal(/foo/, @reopt)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--regexp=/foo/i")})
+ assert_equal(/foo/i, @reopt)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--regexp=/foo/n")})
+ assert_equal(/foo/n, @reopt)
+ end
+end
diff --git a/jni/ruby/test/optparse/test_placearg.rb b/jni/ruby/test/optparse/test_placearg.rb
new file mode 100644
index 0000000..0bbd1a0
--- /dev/null
+++ b/jni/ruby/test/optparse/test_placearg.rb
@@ -0,0 +1,56 @@
+require_relative 'test_optparse'
+
+class TestOptionParser::PlaceArg < TestOptionParser
+ def setup
+ super
+ @opt.def_option("-x [VAL]") {|x| @flag = x}
+ @opt.def_option("--option [VAL]") {|x| @flag = x}
+ @opt.def_option("-T [level]", /^[0-4]$/, Integer) {|x| @topt = x}
+ @topt = nil
+ @opt.def_option("-n") {}
+ @opt.def_option("--regexp [REGEXP]", Regexp) {|x| @reopt = x}
+ @reopt = nil
+ end
+
+ def test_short
+ assert_equal(%w"", no_error {@opt.parse!(%w"-x -n")})
+ assert_equal(nil, @flag)
+ @flag = false
+ assert_equal(%w"", no_error {@opt.parse!(%w"-x foo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-xbar")})
+ assert_equal("bar", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-x=")})
+ assert_equal("=", @flag)
+ end
+
+ def test_abbrev
+ assert_equal(%w"", no_error {@opt.parse!(%w"-o -n")})
+ assert_equal(nil, @flag)
+ @flag = false
+ assert_equal(%w"", no_error {@opt.parse!(%w"-o foo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-obar")})
+ assert_equal("bar", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-o=")})
+ assert_equal("=", @flag)
+ end
+
+ def test_long
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt -n")})
+ assert_equal(nil, @flag)
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"--opt= foo")})
+ assert_equal("", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt=foo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt bar")})
+ assert_equal("bar", @flag)
+ end
+
+ def test_conv
+ assert_equal(%w"te.rb", no_error('[ruby-dev:38333]') {@opt.parse!(%w"-T te.rb")})
+ assert_nil(@topt)
+ assert_equal(%w"te.rb", no_error('[ruby-dev:38333]') {@opt.parse!(%w"-T1 te.rb")})
+ assert_equal(1, @topt)
+ end
+end
diff --git a/jni/ruby/test/optparse/test_reqarg.rb b/jni/ruby/test/optparse/test_reqarg.rb
new file mode 100644
index 0000000..397da4a
--- /dev/null
+++ b/jni/ruby/test/optparse/test_reqarg.rb
@@ -0,0 +1,77 @@
+require_relative 'test_optparse'
+
+module TestOptionParser::ReqArg
+ class Def1 < TestOptionParser
+ include ReqArg
+ def setup
+ super
+ @opt.def_option("-xVAL") {|x| @flag = x}
+ @opt.def_option("--option=VAL") {|x| @flag = x}
+ @opt.def_option("--regexp=REGEXP", Regexp) {|x| @reopt = x}
+ @reopt = nil
+ end
+ end
+ class Def2 < TestOptionParser
+ include ReqArg
+ def setup
+ super
+ @opt.def_option("-x", "--option=VAL") {|x| @flag = x}
+ end
+ end
+ class Def3 < TestOptionParser
+ include ReqArg
+ def setup
+ super
+ @opt.def_option("--option=VAL", "-x") {|x| @flag = x}
+ end
+ end
+ class Def4 < TestOptionParser
+ include ReqArg
+ def setup
+ super
+ @opt.def_option("-xVAL", "--option=VAL") {|x| @flag = x}
+ end
+ end
+
+ def test_short
+ assert_raise(OptionParser::MissingArgument) {@opt.parse!(%w"-x")}
+ assert_equal(%w"", no_error {@opt.parse!(%w"-x foo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-xbar")})
+ assert_equal("bar", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-x=")})
+ assert_equal("=", @flag)
+ end
+
+ def test_abbrev
+ assert_raise(OptionParser::MissingArgument) {@opt.parse!(%w"-o")}
+ assert_equal(%w"", no_error {@opt.parse!(%w"-o foo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-obar")})
+ assert_equal("bar", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"-o=")})
+ assert_equal("=", @flag)
+ end
+
+ def test_long
+ assert_raise(OptionParser::MissingArgument) {@opt.parse!(%w"--opt")}
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt foo")})
+ assert_equal("foo", @flag)
+ assert_equal(%w"foo", no_error {@opt.parse!(%w"--opt= foo")})
+ assert_equal("", @flag)
+ assert_equal(%w"", no_error {@opt.parse!(%w"--opt=foo")})
+ assert_equal("foo", @flag)
+ end
+
+ class TestOptionParser::WithPattern < TestOptionParser
+ def test_pattern
+ pat = num = nil
+ @opt.def_option("--pattern=VAL", /(\w+)(?:\s*:\s*(\w+))?/) {|x, y, z| pat = [x, y, z]}
+ @opt.def_option("-T NUM", /\A[1-4]\z/) {|n| num = n}
+ no_error {@opt.parse!(%w"--pattern=key:val")}
+ assert_equal(%w"key:val key val", pat, '[ruby-list:45645]')
+ no_error {@opt.parse!(%w"-T 4")}
+ assert_equal("4", num, '[ruby-dev:37514]')
+ end
+ end
+end
diff --git a/jni/ruby/test/optparse/test_summary.rb b/jni/ruby/test/optparse/test_summary.rb
new file mode 100644
index 0000000..54fd194
--- /dev/null
+++ b/jni/ruby/test/optparse/test_summary.rb
@@ -0,0 +1,46 @@
+require_relative 'test_optparse'
+
+class TestOptionParser::SummaryTest < TestOptionParser
+ def test_short_clash
+ r = nil
+ o = OptionParser.new do |opts|
+ opts.on("-f", "--first-option", "description 1", "description 2"){r = "first-option"}
+ opts.on("-t", "--test-option"){r = "test-option"}
+ opts.on("-t", "--another-test-option"){r = "another-test-option"}
+ opts.separator "this is\nseparator"
+ opts.on("-l", "--last-option"){r = "last-option"}
+ end
+ s = o.summarize
+ o.parse("-t")
+ assert_match(/--#{r}/, s.grep(/^\s*-t,/)[0])
+ assert_match(/first-option/, s[0])
+ assert_match(/description 1/, s[0])
+ assert_match(/description 2/, s[1])
+ assert_match(/last-option/, s[-1])
+ end
+
+ def test_banner
+ o = OptionParser.new("foo bar")
+ assert_equal("foo bar", o.banner)
+ end
+
+ def test_banner_from_progname
+ o = OptionParser.new
+ o.program_name = "foobar"
+ assert_equal("Usage: foobar [options]\n", o.help)
+ end
+
+ def test_summary
+ o = OptionParser.new("foo\nbar")
+ assert_equal("foo\nbar\n", o.to_s)
+ assert_equal(["foo\n", "bar"], o.to_a)
+ end
+
+ def test_summary_containing_space
+ # test for r35467. OptionParser#to_a shouldn't split str by spaces.
+ bug6348 = '[ruby-dev:45568]'
+ o = OptionParser.new("foo bar")
+ assert_equal("foo bar\n", o.to_s, bug6348)
+ assert_equal(["foo bar"], o.to_a, bug6348)
+ end
+end
diff --git a/jni/ruby/test/optparse/test_zsh_completion.rb b/jni/ruby/test/optparse/test_zsh_completion.rb
new file mode 100644
index 0000000..7e5ba71
--- /dev/null
+++ b/jni/ruby/test/optparse/test_zsh_completion.rb
@@ -0,0 +1,22 @@
+require 'test/unit'
+require 'optparse'
+
+class TestOptionParser < Test::Unit::TestCase
+end
+class TestOptionParser::BashCompletion < Test::Unit::TestCase
+ def setup
+ @opt = OptionParser.new
+ @opt.define("-z", "zzz") {}
+ @opt.define("--foo") {}
+ @opt.define("--bar=BAR") {}
+ @opt.define("--for=TYPE", [:hello, :help, :zot]) {}
+ end
+
+ def test_compsys
+ compsys = @opt.compsys("", "zshcompsys")
+ assert_match(/\"-z\[zzz\]\"/, compsys)
+ assert_match(/\"--foo\[\]\"/, compsys)
+ assert_match(/\"--bar\[\]\"/, compsys)
+ assert_match(/\"--for\[\]\"/, compsys)
+ end
+end
diff --git a/jni/ruby/test/ostruct/test_ostruct.rb b/jni/ruby/test/ostruct/test_ostruct.rb
new file mode 100644
index 0000000..14bc2b2
--- /dev/null
+++ b/jni/ruby/test/ostruct/test_ostruct.rb
@@ -0,0 +1,138 @@
+require 'test/unit'
+require 'ostruct'
+
+class TC_OpenStruct < Test::Unit::TestCase
+ def test_initialize
+ h = {name: "John Smith", age: 70, pension: 300}
+ assert_equal h, OpenStruct.new(h).to_h
+ assert_equal h, OpenStruct.new(OpenStruct.new(h)).to_h
+ assert_equal h, OpenStruct.new(Struct.new(*h.keys).new(*h.values)).to_h
+ end
+
+ def test_equality
+ o1 = OpenStruct.new
+ o2 = OpenStruct.new
+ assert_equal(o1, o2)
+
+ o1.a = 'a'
+ assert_not_equal(o1, o2)
+
+ o2.a = 'a'
+ assert_equal(o1, o2)
+
+ o1.a = 'b'
+ assert_not_equal(o1, o2)
+
+ o2 = Object.new
+ o2.instance_eval{@table = {:a => 'b'}}
+ assert_not_equal(o1, o2)
+ end
+
+ def test_inspect
+ foo = OpenStruct.new
+ assert_equal("#<OpenStruct>", foo.inspect)
+ foo.bar = 1
+ foo.baz = 2
+ assert_equal("#<OpenStruct bar=1, baz=2>", foo.inspect)
+
+ foo = OpenStruct.new
+ foo.bar = OpenStruct.new
+ assert_equal('#<OpenStruct bar=#<OpenStruct>>', foo.inspect)
+ foo.bar.foo = foo
+ assert_equal('#<OpenStruct bar=#<OpenStruct foo=#<OpenStruct ...>>>', foo.inspect)
+ end
+
+ def test_frozen
+ o = OpenStruct.new
+ o.a = 'a'
+ o.freeze
+ assert_raise(RuntimeError) {o.b = 'b'}
+ assert_not_respond_to(o, :b)
+ assert_raise(RuntimeError) {o.a = 'z'}
+ assert_equal('a', o.a)
+ o = OpenStruct.new :a => 42
+ def o.frozen?; nil end
+ o.freeze
+ assert_raise(RuntimeError, '[ruby-core:22559]') {o.a = 1764}
+ end
+
+ def test_delete_field
+ bug = '[ruby-core:33010]'
+ o = OpenStruct.new
+ assert_not_respond_to(o, :a)
+ assert_not_respond_to(o, :a=)
+ o.a = 'a'
+ assert_respond_to(o, :a)
+ assert_respond_to(o, :a=)
+ a = o.delete_field :a
+ assert_not_respond_to(o, :a, bug)
+ assert_not_respond_to(o, :a=, bug)
+ assert_equal(a, 'a')
+ s = Object.new
+ def s.to_sym
+ :foo
+ end
+ o[s] = true
+ assert_respond_to(o, :foo)
+ assert_respond_to(o, :foo=)
+ o.delete_field s
+ assert_not_respond_to(o, :foo)
+ assert_not_respond_to(o, :foo=)
+ end
+
+ def test_setter
+ os = OpenStruct.new
+ os[:foo] = :bar
+ assert_equal :bar, os.foo
+ os['foo'] = :baz
+ assert_equal :baz, os.foo
+ end
+
+ def test_getter
+ os = OpenStruct.new
+ os.foo = :bar
+ assert_equal :bar, os[:foo]
+ assert_equal :bar, os['foo']
+ end
+
+ def test_to_h
+ h = {name: "John Smith", age: 70, pension: 300}
+ os = OpenStruct.new(h)
+ to_h = os.to_h
+ assert_equal(h, to_h)
+
+ to_h[:age] = 71
+ assert_equal(70, os.age)
+ assert_equal(70, h[:age])
+
+ assert_equal(h, OpenStruct.new("name" => "John Smith", "age" => 70, pension: 300).to_h)
+ end
+
+ def test_each_pair
+ h = {name: "John Smith", age: 70, pension: 300}
+ os = OpenStruct.new(h)
+ assert_equal '#<Enumerator: #<OpenStruct name="John Smith", age=70, pension=300>:each_pair>', os.each_pair.inspect
+ assert_equal [[:name, "John Smith"], [:age, 70], [:pension, 300]], os.each_pair.to_a
+ assert_equal 3, os.each_pair.size
+ end
+
+ def test_eql_and_hash
+ os1 = OpenStruct.new age: 70
+ os2 = OpenStruct.new age: 70.0
+ assert_equal os1, os2
+ assert_equal false, os1.eql?(os2)
+ assert_not_equal os1.hash, os2.hash
+ assert_equal true, os1.eql?(os1.dup)
+ assert_equal os1.hash, os1.dup.hash
+ end
+
+ def test_method_missing
+ os = OpenStruct.new
+ e = assert_raise(NoMethodError) { os.foo true }
+ assert_equal :foo, e.name
+ assert_equal [true], e.args
+ assert_match(/#{__callee__}/, e.backtrace[0])
+ e = assert_raise(ArgumentError) { os.send :foo=, true, true }
+ assert_match(/#{__callee__}/, e.backtrace[0])
+ end
+end
diff --git a/jni/ruby/test/pathname/test_pathname.rb b/jni/ruby/test/pathname/test_pathname.rb
new file mode 100644
index 0000000..046f3f0
--- /dev/null
+++ b/jni/ruby/test/pathname/test_pathname.rb
@@ -0,0 +1,1389 @@
+require 'test/unit'
+require 'pathname'
+
+require 'fileutils'
+require 'tmpdir'
+require 'enumerator'
+
+
+class TestPathname < Test::Unit::TestCase
+ def self.define_assertion(name, linenum, &block)
+ name = "test_#{name}_#{linenum}"
+ define_method(name, &block)
+ end
+
+ def self.get_linenum
+ if /:(\d+):/ =~ caller[1]
+ $1.to_i
+ else
+ nil
+ end
+ end
+
+ def self.defassert(name, result, *args)
+ define_assertion(name, get_linenum) {
+ mesg = "#{name}(#{args.map {|a| a.inspect }.join(', ')})"
+ assert_nothing_raised(mesg) {
+ assert_equal(result, self.send(name, *args), mesg)
+ }
+ }
+ end
+
+ def self.defassert_raise(name, exc, *args)
+ define_assertion(name, get_linenum) {
+ message = "#{name}(#{args.map {|a| a.inspect }.join(', ')})"
+ assert_raise(exc, message) { self.send(name, *args) }
+ }
+ end
+
+ DOSISH = File::ALT_SEPARATOR != nil
+ DOSISH_DRIVE_LETTER = File.dirname("A:") == "A:."
+ DOSISH_UNC = File.dirname("//") == "//"
+
+ def cleanpath_aggressive(path)
+ Pathname.new(path).cleanpath.to_s
+ end
+
+ defassert(:cleanpath_aggressive, '/', '/')
+ defassert(:cleanpath_aggressive, '.', '')
+ defassert(:cleanpath_aggressive, '.', '.')
+ defassert(:cleanpath_aggressive, '..', '..')
+ defassert(:cleanpath_aggressive, 'a', 'a')
+ defassert(:cleanpath_aggressive, '/', '/.')
+ defassert(:cleanpath_aggressive, '/', '/..')
+ defassert(:cleanpath_aggressive, '/a', '/a')
+ defassert(:cleanpath_aggressive, '.', './')
+ defassert(:cleanpath_aggressive, '..', '../')
+ defassert(:cleanpath_aggressive, 'a', 'a/')
+ defassert(:cleanpath_aggressive, 'a/b', 'a//b')
+ defassert(:cleanpath_aggressive, 'a', 'a/.')
+ defassert(:cleanpath_aggressive, 'a', 'a/./')
+ defassert(:cleanpath_aggressive, '.', 'a/..')
+ defassert(:cleanpath_aggressive, '.', 'a/../')
+ defassert(:cleanpath_aggressive, '/a', '/a/.')
+ defassert(:cleanpath_aggressive, '..', './..')
+ defassert(:cleanpath_aggressive, '..', '../.')
+ defassert(:cleanpath_aggressive, '..', './../')
+ defassert(:cleanpath_aggressive, '..', '.././')
+ defassert(:cleanpath_aggressive, '/', '/./..')
+ defassert(:cleanpath_aggressive, '/', '/../.')
+ defassert(:cleanpath_aggressive, '/', '/./../')
+ defassert(:cleanpath_aggressive, '/', '/.././')
+ defassert(:cleanpath_aggressive, 'a/b/c', 'a/b/c')
+ defassert(:cleanpath_aggressive, 'b/c', './b/c')
+ defassert(:cleanpath_aggressive, 'a/c', 'a/./c')
+ defassert(:cleanpath_aggressive, 'a/b', 'a/b/.')
+ defassert(:cleanpath_aggressive, '.', 'a/../.')
+ defassert(:cleanpath_aggressive, '/a', '/../.././../a')
+ defassert(:cleanpath_aggressive, '../../d', 'a/b/../../../../c/../d')
+
+ if DOSISH_UNC
+ defassert(:cleanpath_aggressive, '//a/b/c', '//a/b/c/')
+ else
+ defassert(:cleanpath_aggressive, '/', '///')
+ defassert(:cleanpath_aggressive, '/a', '///a')
+ defassert(:cleanpath_aggressive, '/', '///..')
+ defassert(:cleanpath_aggressive, '/', '///.')
+ defassert(:cleanpath_aggressive, '/', '///a/../..')
+ end
+
+ if DOSISH
+ defassert(:cleanpath_aggressive, 'c:/foo/bar', 'c:\\foo\\bar')
+ end
+
+ def cleanpath_conservative(path)
+ Pathname.new(path).cleanpath(true).to_s
+ end
+
+ defassert(:cleanpath_conservative, '/', '/')
+ defassert(:cleanpath_conservative, '.', '')
+ defassert(:cleanpath_conservative, '.', '.')
+ defassert(:cleanpath_conservative, '..', '..')
+ defassert(:cleanpath_conservative, 'a', 'a')
+ defassert(:cleanpath_conservative, '/', '/.')
+ defassert(:cleanpath_conservative, '/', '/..')
+ defassert(:cleanpath_conservative, '/a', '/a')
+ defassert(:cleanpath_conservative, '.', './')
+ defassert(:cleanpath_conservative, '..', '../')
+ defassert(:cleanpath_conservative, 'a/', 'a/')
+ defassert(:cleanpath_conservative, 'a/b', 'a//b')
+ defassert(:cleanpath_conservative, 'a/.', 'a/.')
+ defassert(:cleanpath_conservative, 'a/.', 'a/./')
+ defassert(:cleanpath_conservative, 'a/..', 'a/../')
+ defassert(:cleanpath_conservative, '/a/.', '/a/.')
+ defassert(:cleanpath_conservative, '..', './..')
+ defassert(:cleanpath_conservative, '..', '../.')
+ defassert(:cleanpath_conservative, '..', './../')
+ defassert(:cleanpath_conservative, '..', '.././')
+ defassert(:cleanpath_conservative, '/', '/./..')
+ defassert(:cleanpath_conservative, '/', '/../.')
+ defassert(:cleanpath_conservative, '/', '/./../')
+ defassert(:cleanpath_conservative, '/', '/.././')
+ defassert(:cleanpath_conservative, 'a/b/c', 'a/b/c')
+ defassert(:cleanpath_conservative, 'b/c', './b/c')
+ defassert(:cleanpath_conservative, 'a/c', 'a/./c')
+ defassert(:cleanpath_conservative, 'a/b/.', 'a/b/.')
+ defassert(:cleanpath_conservative, 'a/..', 'a/../.')
+ defassert(:cleanpath_conservative, '/a', '/../.././../a')
+ defassert(:cleanpath_conservative, 'a/b/../../../../c/../d', 'a/b/../../../../c/../d')
+
+ if DOSISH
+ defassert(:cleanpath_conservative, 'c:/foo/bar', 'c:\\foo\\bar')
+ end
+
+ if DOSISH_UNC
+ defassert(:cleanpath_conservative, '//', '//')
+ else
+ defassert(:cleanpath_conservative, '/', '//')
+ end
+
+ # has_trailing_separator?(path) -> bool
+ def has_trailing_separator?(path)
+ Pathname.allocate.__send__(:has_trailing_separator?, path)
+ end
+
+ defassert(:has_trailing_separator?, false, "/")
+ defassert(:has_trailing_separator?, false, "///")
+ defassert(:has_trailing_separator?, false, "a")
+ defassert(:has_trailing_separator?, true, "a/")
+
+ def add_trailing_separator(path)
+ Pathname.allocate.__send__(:add_trailing_separator, path)
+ end
+
+ def del_trailing_separator(path)
+ Pathname.allocate.__send__(:del_trailing_separator, path)
+ end
+
+ defassert(:del_trailing_separator, "/", "/")
+ defassert(:del_trailing_separator, "/a", "/a")
+ defassert(:del_trailing_separator, "/a", "/a/")
+ defassert(:del_trailing_separator, "/a", "/a//")
+ defassert(:del_trailing_separator, ".", ".")
+ defassert(:del_trailing_separator, ".", "./")
+ defassert(:del_trailing_separator, ".", ".//")
+
+ if DOSISH_DRIVE_LETTER
+ defassert(:del_trailing_separator, "A:", "A:")
+ defassert(:del_trailing_separator, "A:/", "A:/")
+ defassert(:del_trailing_separator, "A:/", "A://")
+ defassert(:del_trailing_separator, "A:.", "A:.")
+ defassert(:del_trailing_separator, "A:.", "A:./")
+ defassert(:del_trailing_separator, "A:.", "A:.//")
+ end
+
+ if DOSISH_UNC
+ defassert(:del_trailing_separator, "//", "//")
+ defassert(:del_trailing_separator, "//a", "//a")
+ defassert(:del_trailing_separator, "//a", "//a/")
+ defassert(:del_trailing_separator, "//a", "//a//")
+ defassert(:del_trailing_separator, "//a/b", "//a/b")
+ defassert(:del_trailing_separator, "//a/b", "//a/b/")
+ defassert(:del_trailing_separator, "//a/b", "//a/b//")
+ defassert(:del_trailing_separator, "//a/b/c", "//a/b/c")
+ defassert(:del_trailing_separator, "//a/b/c", "//a/b/c/")
+ defassert(:del_trailing_separator, "//a/b/c", "//a/b/c//")
+ else
+ defassert(:del_trailing_separator, "/", "///")
+ defassert(:del_trailing_separator, "///a", "///a/")
+ end
+
+ if DOSISH
+ defassert(:del_trailing_separator, "a", "a\\")
+ defassert(:del_trailing_separator, "\225\\".force_encoding("cp932"), "\225\\\\".force_encoding("cp932"))
+ defassert(:del_trailing_separator, "\225".force_encoding("cp437"), "\225\\\\".force_encoding("cp437"))
+ end
+
+ def test_plus
+ assert_kind_of(Pathname, Pathname("a") + Pathname("b"))
+ end
+
+ def plus(path1, path2) # -> path
+ (Pathname.new(path1) + Pathname.new(path2)).to_s
+ end
+
+ defassert(:plus, '/', '/', '/')
+ defassert(:plus, 'a/b', 'a', 'b')
+ defassert(:plus, 'a', 'a', '.')
+ defassert(:plus, 'b', '.', 'b')
+ defassert(:plus, '.', '.', '.')
+ defassert(:plus, '/b', 'a', '/b')
+
+ defassert(:plus, '/', '/', '..')
+ defassert(:plus, '.', 'a', '..')
+ defassert(:plus, 'a', 'a/b', '..')
+ defassert(:plus, '../..', '..', '..')
+ defassert(:plus, '/c', '/', '../c')
+ defassert(:plus, 'c', 'a', '../c')
+ defassert(:plus, 'a/c', 'a/b', '../c')
+ defassert(:plus, '../../c', '..', '../c')
+
+ defassert(:plus, 'a//b/d//e', 'a//b/c', '../d//e')
+
+ def test_slash
+ assert_kind_of(Pathname, Pathname("a") / Pathname("b"))
+ end
+
+ def test_parent
+ assert_equal(Pathname("."), Pathname("a").parent)
+ end
+
+ def parent(path) # -> path
+ Pathname.new(path).parent.to_s
+ end
+
+ defassert(:parent, '/', '/')
+ defassert(:parent, '/', '/a')
+ defassert(:parent, '/a', '/a/b')
+ defassert(:parent, '/a/b', '/a/b/c')
+ defassert(:parent, '.', 'a')
+ defassert(:parent, 'a', 'a/b')
+ defassert(:parent, 'a/b', 'a/b/c')
+ defassert(:parent, '..', '.')
+ defassert(:parent, '../..', '..')
+
+ def test_join
+ r = Pathname("a").join(Pathname("b"), Pathname("c"))
+ assert_equal(Pathname("a/b/c"), r)
+ r = Pathname("/a").join(Pathname("b"), Pathname("c"))
+ assert_equal(Pathname("/a/b/c"), r)
+ r = Pathname("/a").join(Pathname("/b"), Pathname("c"))
+ assert_equal(Pathname("/b/c"), r)
+ r = Pathname("/a").join(Pathname("/b"), Pathname("/c"))
+ assert_equal(Pathname("/c"), r)
+ r = Pathname("/a").join("/b", "/c")
+ assert_equal(Pathname("/c"), r)
+ r = Pathname("/foo/var").join()
+ assert_equal(Pathname("/foo/var"), r)
+ end
+
+ def test_absolute
+ assert_equal(true, Pathname("/").absolute?)
+ assert_equal(false, Pathname("a").absolute?)
+ end
+
+ def relative?(path)
+ Pathname.new(path).relative?
+ end
+
+ defassert(:relative?, false, '/')
+ defassert(:relative?, false, '/a')
+ defassert(:relative?, false, '/..')
+ defassert(:relative?, true, 'a')
+ defassert(:relative?, true, 'a/b')
+
+ if DOSISH_DRIVE_LETTER
+ defassert(:relative?, false, 'A:')
+ defassert(:relative?, false, 'A:/')
+ defassert(:relative?, false, 'A:/a')
+ end
+
+ if File.dirname('//') == '//'
+ defassert(:relative?, false, '//')
+ defassert(:relative?, false, '//a')
+ defassert(:relative?, false, '//a/')
+ defassert(:relative?, false, '//a/b')
+ defassert(:relative?, false, '//a/b/')
+ defassert(:relative?, false, '//a/b/c')
+ end
+
+ def relative_path_from(dest_directory, base_directory)
+ Pathname.new(dest_directory).relative_path_from(Pathname.new(base_directory)).to_s
+ end
+
+ defassert(:relative_path_from, "../a", "a", "b")
+ defassert(:relative_path_from, "../a", "a", "b/")
+ defassert(:relative_path_from, "../a", "a/", "b")
+ defassert(:relative_path_from, "../a", "a/", "b/")
+ defassert(:relative_path_from, "../a", "/a", "/b")
+ defassert(:relative_path_from, "../a", "/a", "/b/")
+ defassert(:relative_path_from, "../a", "/a/", "/b")
+ defassert(:relative_path_from, "../a", "/a/", "/b/")
+
+ defassert(:relative_path_from, "../b", "a/b", "a/c")
+ defassert(:relative_path_from, "../a", "../a", "../b")
+
+ defassert(:relative_path_from, "a", "a", ".")
+ defassert(:relative_path_from, "..", ".", "a")
+
+ defassert(:relative_path_from, ".", ".", ".")
+ defassert(:relative_path_from, ".", "..", "..")
+ defassert(:relative_path_from, "..", "..", ".")
+
+ defassert(:relative_path_from, "c/d", "/a/b/c/d", "/a/b")
+ defassert(:relative_path_from, "../..", "/a/b", "/a/b/c/d")
+ defassert(:relative_path_from, "../../../../e", "/e", "/a/b/c/d")
+ defassert(:relative_path_from, "../b/c", "a/b/c", "a/d")
+
+ defassert(:relative_path_from, "../a", "/../a", "/b")
+ defassert(:relative_path_from, "../../a", "../a", "b")
+ defassert(:relative_path_from, ".", "/a/../../b", "/b")
+ defassert(:relative_path_from, "..", "a/..", "a")
+ defassert(:relative_path_from, ".", "a/../b", "b")
+
+ defassert(:relative_path_from, "a", "a", "b/..")
+ defassert(:relative_path_from, "b/c", "b/c", "b/..")
+
+ defassert_raise(:relative_path_from, ArgumentError, "/", ".")
+ defassert_raise(:relative_path_from, ArgumentError, ".", "/")
+ defassert_raise(:relative_path_from, ArgumentError, "a", "..")
+ defassert_raise(:relative_path_from, ArgumentError, ".", "..")
+
+ def with_tmpchdir(base=nil)
+ Dir.mktmpdir(base) {|d|
+ d = Pathname.new(d).realpath.to_s
+ Dir.chdir(d) {
+ yield d
+ }
+ }
+ end
+
+ def has_symlink?
+ begin
+ File.symlink(nil, nil)
+ rescue NotImplementedError
+ return false
+ rescue TypeError
+ end
+ return true
+ end
+
+ def realpath(path, basedir=nil)
+ Pathname.new(path).realpath(basedir).to_s
+ end
+
+ def test_realpath
+ return if !has_symlink?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist") }
+ File.symlink("not-exist-target", "#{dir}/not-exist")
+ assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist") }
+
+ File.symlink("loop", "#{dir}/loop")
+ assert_raise(Errno::ELOOP) { realpath("#{dir}/loop") }
+ assert_raise(Errno::ELOOP) { realpath("#{dir}/loop", dir) }
+
+ File.symlink("../#{File.basename(dir)}/./not-exist-target", "#{dir}/not-exist2")
+ assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist2") }
+
+ File.open("#{dir}/exist-target", "w") {}
+ File.symlink("../#{File.basename(dir)}/./exist-target", "#{dir}/exist2")
+ assert_nothing_raised { realpath("#{dir}/exist2") }
+
+ File.symlink("loop-relative", "loop-relative")
+ assert_raise(Errno::ELOOP) { realpath("#{dir}/loop-relative") }
+
+ Dir.mkdir("exist")
+ assert_equal("#{dir}/exist", realpath("exist"))
+ assert_raise(Errno::ELOOP) { realpath("../loop", "#{dir}/exist") }
+
+ File.symlink("loop1/loop1", "loop1")
+ assert_raise(Errno::ELOOP) { realpath("#{dir}/loop1") }
+
+ File.symlink("loop2", "loop3")
+ File.symlink("loop3", "loop2")
+ assert_raise(Errno::ELOOP) { realpath("#{dir}/loop2") }
+
+ Dir.mkdir("b")
+
+ File.symlink("b", "c")
+ assert_equal("#{dir}/b", realpath("c"))
+ assert_equal("#{dir}/b", realpath("c/../c"))
+ assert_equal("#{dir}/b", realpath("c/../c/../c/."))
+
+ File.symlink("..", "b/d")
+ assert_equal("#{dir}/b", realpath("c/d/c/d/c"))
+
+ File.symlink("#{dir}/b", "e")
+ assert_equal("#{dir}/b", realpath("e"))
+
+ Dir.mkdir("f")
+ Dir.mkdir("f/g")
+ File.symlink("f/g", "h")
+ assert_equal("#{dir}/f/g", realpath("h"))
+ File.chmod(0000, "f")
+ assert_raise(Errno::EACCES) { realpath("h") }
+ File.chmod(0755, "f")
+ }
+ end
+
+ def realdirpath(path)
+ Pathname.new(path).realdirpath.to_s
+ end
+
+ def test_realdirpath
+ return if !has_symlink?
+ Dir.mktmpdir('rubytest-pathname') {|dir|
+ rdir = realpath(dir)
+ assert_equal("#{rdir}/not-exist", realdirpath("#{dir}/not-exist"))
+ assert_raise(Errno::ENOENT) { realdirpath("#{dir}/not-exist/not-exist-child") }
+ File.symlink("not-exist-target", "#{dir}/not-exist")
+ assert_equal("#{rdir}/not-exist-target", realdirpath("#{dir}/not-exist"))
+ File.symlink("../#{File.basename(dir)}/./not-exist-target", "#{dir}/not-exist2")
+ assert_equal("#{rdir}/not-exist-target", realdirpath("#{dir}/not-exist2"))
+ File.open("#{dir}/exist-target", "w") {}
+ File.symlink("../#{File.basename(dir)}/./exist-target", "#{dir}/exist")
+ assert_equal("#{rdir}/exist-target", realdirpath("#{dir}/exist"))
+ File.symlink("loop", "#{dir}/loop")
+ assert_raise(Errno::ELOOP) { realdirpath("#{dir}/loop") }
+ }
+ end
+
+ def descend(path)
+ Pathname.new(path).enum_for(:descend).map {|v| v.to_s }
+ end
+
+ defassert(:descend, %w[/ /a /a/b /a/b/c], "/a/b/c")
+ defassert(:descend, %w[a a/b a/b/c], "a/b/c")
+ defassert(:descend, %w[. ./a ./a/b ./a/b/c], "./a/b/c")
+ defassert(:descend, %w[a/], "a/")
+
+ def ascend(path)
+ Pathname.new(path).enum_for(:ascend).map {|v| v.to_s }
+ end
+
+ defassert(:ascend, %w[/a/b/c /a/b /a /], "/a/b/c")
+ defassert(:ascend, %w[a/b/c a/b a], "a/b/c")
+ defassert(:ascend, %w[./a/b/c ./a/b ./a .], "./a/b/c")
+ defassert(:ascend, %w[a/], "a/")
+
+ def test_initialize
+ p1 = Pathname.new('a')
+ assert_equal('a', p1.to_s)
+ p2 = Pathname.new(p1)
+ assert_equal(p1, p2)
+ end
+
+ def test_initialize_nul
+ assert_raise(ArgumentError) { Pathname.new("a\0") }
+ end
+
+ class AnotherStringLike # :nodoc:
+ def initialize(s) @s = s end
+ def to_str() @s end
+ def ==(other) @s == other end
+ end
+
+ def test_equality
+ obj = Pathname.new("a")
+ str = "a"
+ sym = :a
+ ano = AnotherStringLike.new("a")
+ assert_equal(false, obj == str)
+ assert_equal(false, str == obj)
+ assert_equal(false, obj == ano)
+ assert_equal(false, ano == obj)
+ assert_equal(false, obj == sym)
+ assert_equal(false, sym == obj)
+
+ obj2 = Pathname.new("a")
+ assert_equal(true, obj == obj2)
+ assert_equal(true, obj === obj2)
+ assert_equal(true, obj.eql?(obj2))
+ end
+
+ def test_hashkey
+ h = {}
+ h[Pathname.new("a")] = 1
+ h[Pathname.new("a")] = 2
+ assert_equal(1, h.size)
+ end
+
+ def assert_pathname_cmp(e, s1, s2)
+ p1 = Pathname.new(s1)
+ p2 = Pathname.new(s2)
+ r = p1 <=> p2
+ assert(e == r,
+ "#{p1.inspect} <=> #{p2.inspect}: <#{e}> expected but was <#{r}>")
+ end
+ def test_comparison
+ assert_pathname_cmp( 0, "a", "a")
+ assert_pathname_cmp( 1, "b", "a")
+ assert_pathname_cmp(-1, "a", "b")
+ ss = %w(
+ a
+ a/
+ a/b
+ a.
+ a0
+ )
+ s1 = ss.shift
+ ss.each {|s2|
+ assert_pathname_cmp(-1, s1, s2)
+ s1 = s2
+ }
+ end
+
+ def test_comparison_string
+ assert_equal(nil, Pathname.new("a") <=> "a")
+ assert_equal(nil, "a" <=> Pathname.new("a"))
+ end
+
+ def pathsub(path, pat, repl) Pathname.new(path).sub(pat, repl).to_s end
+ defassert(:pathsub, "a.o", "a.c", /\.c\z/, ".o")
+
+ def pathsubext(path, repl) Pathname.new(path).sub_ext(repl).to_s end
+ defassert(:pathsubext, 'a.o', 'a.c', '.o')
+ defassert(:pathsubext, 'a.o', 'a.c++', '.o')
+ defassert(:pathsubext, 'a.png', 'a.gif', '.png')
+ defassert(:pathsubext, 'ruby.tar.bz2', 'ruby.tar.gz', '.bz2')
+ defassert(:pathsubext, 'd/a.o', 'd/a.c', '.o')
+ defassert(:pathsubext, 'foo', 'foo.exe', '')
+ defassert(:pathsubext, 'lex.yy.o', 'lex.yy.c', '.o')
+ defassert(:pathsubext, 'fooaa.o', 'fooaa', '.o')
+ defassert(:pathsubext, 'd.e/aa.o', 'd.e/aa', '.o')
+ defassert(:pathsubext, 'long_enough.bug-3664', 'long_enough.not_to_be_embeded[ruby-core:31640]', '.bug-3664')
+
+ def test_sub_matchdata
+ result = Pathname("abc.gif").sub(/\..*/) {
+ assert_not_nil($~)
+ assert_equal(".gif", $~[0])
+ ".png"
+ }
+ assert_equal("abc.png", result.to_s)
+ end
+
+ def root?(path)
+ Pathname.new(path).root?
+ end
+
+ defassert(:root?, true, "/")
+ defassert(:root?, true, "//")
+ defassert(:root?, true, "///")
+ defassert(:root?, false, "")
+ defassert(:root?, false, "a")
+
+ def test_mountpoint?
+ r = Pathname("/").mountpoint?
+ assert_include([true, false], r)
+ end
+
+ def test_mountpoint_enoent
+ r = Pathname("/nonexistent").mountpoint?
+ assert_equal false, r
+ end
+
+ def test_destructive_update
+ path = Pathname.new("a")
+ path.to_s.replace "b"
+ assert_equal(Pathname.new("a"), path)
+ end
+
+ def test_null_character
+ assert_raise(ArgumentError) { Pathname.new("\0") }
+ end
+
+ def test_taint
+ obj = Pathname.new("a"); assert_same(obj, obj.taint)
+ obj = Pathname.new("a"); assert_same(obj, obj.untaint)
+
+ assert_equal(false, Pathname.new("a" ) .tainted?)
+ assert_equal(false, Pathname.new("a" ) .to_s.tainted?)
+ assert_equal(true, Pathname.new("a" ).taint .tainted?)
+ assert_equal(true, Pathname.new("a" ).taint.to_s.tainted?)
+ assert_equal(true, Pathname.new("a".taint) .tainted?)
+ assert_equal(true, Pathname.new("a".taint) .to_s.tainted?)
+ assert_equal(true, Pathname.new("a".taint).taint .tainted?)
+ assert_equal(true, Pathname.new("a".taint).taint.to_s.tainted?)
+
+ str = "a"
+ path = Pathname.new(str)
+ str.taint
+ assert_equal(false, path .tainted?)
+ assert_equal(false, path.to_s.tainted?)
+ end
+
+ def test_untaint
+ obj = Pathname.new("a"); assert_same(obj, obj.untaint)
+
+ assert_equal(false, Pathname.new("a").taint.untaint .tainted?)
+ assert_equal(false, Pathname.new("a").taint.untaint.to_s.tainted?)
+
+ str = "a".taint
+ path = Pathname.new(str)
+ str.untaint
+ assert_equal(true, path .tainted?)
+ assert_equal(true, path.to_s.tainted?)
+ end
+
+ def test_freeze
+ obj = Pathname.new("a"); assert_same(obj, obj.freeze)
+
+ assert_equal(false, Pathname.new("a" ) .frozen?)
+ assert_equal(false, Pathname.new("a".freeze) .frozen?)
+ assert_equal(true, Pathname.new("a" ).freeze .frozen?)
+ assert_equal(true, Pathname.new("a".freeze).freeze .frozen?)
+ assert_equal(false, Pathname.new("a" ) .to_s.frozen?)
+ assert_equal(false, Pathname.new("a".freeze) .to_s.frozen?)
+ assert_equal(false, Pathname.new("a" ).freeze.to_s.frozen?)
+ assert_equal(false, Pathname.new("a".freeze).freeze.to_s.frozen?)
+ end
+
+ def test_freeze_and_taint
+ obj = Pathname.new("a")
+ obj.freeze
+ assert_equal(false, obj.tainted?)
+ assert_raise(RuntimeError) { obj.taint }
+
+ obj = Pathname.new("a")
+ obj.taint
+ assert_equal(true, obj.tainted?)
+ obj.freeze
+ assert_equal(true, obj.tainted?)
+ assert_nothing_raised { obj.taint }
+ end
+
+ def test_to_s
+ str = "a"
+ obj = Pathname.new(str)
+ assert_equal(str, obj.to_s)
+ assert_not_same(str, obj.to_s)
+ assert_not_same(obj.to_s, obj.to_s)
+ end
+
+ def test_kernel_open
+ count = 0
+ result = Kernel.open(Pathname.new(__FILE__)) {|f|
+ assert(File.identical?(__FILE__, f))
+ count += 1
+ 2
+ }
+ assert_equal(1, count)
+ assert_equal(2, result)
+ end
+
+ def test_each_filename
+ result = []
+ Pathname.new("/usr/bin/ruby").each_filename {|f| result << f }
+ assert_equal(%w[usr bin ruby], result)
+ assert_equal(%w[usr bin ruby], Pathname.new("/usr/bin/ruby").each_filename.to_a)
+ end
+
+ def test_kernel_pathname
+ assert_equal(Pathname.new("a"), Pathname("a"))
+ end
+
+ def test_children
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {}
+ open("b", "w") {}
+ Dir.mkdir("d")
+ open("d/x", "w") {}
+ open("d/y", "w") {}
+ assert_equal([Pathname("a"), Pathname("b"), Pathname("d")], Pathname(".").children.sort)
+ assert_equal([Pathname("d/x"), Pathname("d/y")], Pathname("d").children.sort)
+ assert_equal([Pathname("x"), Pathname("y")], Pathname("d").children(false).sort)
+ }
+ end
+
+ def test_each_child
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {}
+ open("b", "w") {}
+ Dir.mkdir("d")
+ open("d/x", "w") {}
+ open("d/y", "w") {}
+ a = []; Pathname(".").each_child {|v| a << v }; a.sort!
+ assert_equal([Pathname("a"), Pathname("b"), Pathname("d")], a)
+ a = []; Pathname("d").each_child {|v| a << v }; a.sort!
+ assert_equal([Pathname("d/x"), Pathname("d/y")], a)
+ a = []; Pathname("d").each_child(false) {|v| a << v }; a.sort!
+ assert_equal([Pathname("x"), Pathname("y")], a)
+ }
+ end
+
+ def test_each_line
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.puts 1, 2 }
+ a = []
+ Pathname("a").each_line {|line| a << line }
+ assert_equal(["1\n", "2\n"], a)
+
+ a = []
+ Pathname("a").each_line("2") {|line| a << line }
+ assert_equal(["1\n2", "\n"], a)
+
+ a = []
+ Pathname("a").each_line(1) {|line| a << line }
+ assert_equal(["1", "\n", "2", "\n"], a)
+
+ a = []
+ Pathname("a").each_line("2", 1) {|line| a << line }
+ assert_equal(["1", "\n", "2", "\n"], a)
+
+ a = []
+ enum = Pathname("a").each_line
+ enum.each {|line| a << line }
+ assert_equal(["1\n", "2\n"], a)
+ }
+ end
+
+ def test_readlines
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.puts 1, 2 }
+ a = Pathname("a").readlines
+ assert_equal(["1\n", "2\n"], a)
+ }
+ end
+
+ def test_read
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.puts 1, 2 }
+ assert_equal("1\n2\n", Pathname("a").read)
+ }
+ end
+
+ def test_binread
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ str = Pathname("a").binread
+ assert_equal("abc", str)
+ assert_equal(Encoding::ASCII_8BIT, str.encoding)
+ }
+ end
+
+ def test_write
+ with_tmpchdir('rubytest-pathname') {|dir|
+ path = Pathname("a")
+ path.write "abc"
+ assert_equal("abc", path.read)
+ }
+ end
+
+ def test_binwrite
+ with_tmpchdir('rubytest-pathname') {|dir|
+ path = Pathname("a")
+ path.binwrite "abc\x80"
+ assert_equal("abc\x80".b, path.binread)
+ }
+ end
+
+ def test_sysopen
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ fd = Pathname("a").sysopen
+ io = IO.new(fd)
+ begin
+ assert_equal("abc", io.read)
+ ensure
+ io.close
+ end
+ }
+ end
+
+ def test_atime
+ assert_kind_of(Time, Pathname(__FILE__).atime)
+ end
+
+ def test_birthtime
+ assert_kind_of(Time, Pathname(__FILE__).birthtime)
+ rescue NotImplementedError
+ assert_raise(NotImplementedError) do
+ File.birthtime(__FILE__)
+ end
+ end
+
+ def test_ctime
+ assert_kind_of(Time, Pathname(__FILE__).ctime)
+ end
+
+ def test_mtime
+ assert_kind_of(Time, Pathname(__FILE__).mtime)
+ end
+
+ def test_chmod
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ path = Pathname("a")
+ old = path.stat.mode
+ path.chmod(0444)
+ assert_equal(0444, path.stat.mode & 0777)
+ path.chmod(old)
+ }
+ end
+
+ def test_lchmod
+ return if !has_symlink?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ File.symlink("a", "l")
+ path = Pathname("l")
+ old = path.lstat.mode
+ begin
+ path.lchmod(0444)
+ rescue NotImplementedError
+ next
+ end
+ assert_equal(0444, path.lstat.mode & 0777)
+ path.chmod(old)
+ }
+ end
+
+ def test_chown
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ path = Pathname("a")
+ old_uid = path.stat.uid
+ old_gid = path.stat.gid
+ begin
+ path.chown(0, 0)
+ rescue Errno::EPERM
+ next
+ end
+ assert_equal(0, path.stat.uid)
+ assert_equal(0, path.stat.gid)
+ path.chown(old_uid, old_gid)
+ }
+ end
+
+ def test_lchown
+ return if !has_symlink?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ File.symlink("a", "l")
+ path = Pathname("l")
+ old_uid = path.stat.uid
+ old_gid = path.stat.gid
+ begin
+ path.lchown(0, 0)
+ rescue Errno::EPERM
+ next
+ end
+ assert_equal(0, path.stat.uid)
+ assert_equal(0, path.stat.gid)
+ path.lchown(old_uid, old_gid)
+ }
+ end
+
+ def test_fnmatch
+ path = Pathname("a")
+ assert_equal(true, path.fnmatch("*"))
+ assert_equal(false, path.fnmatch("*.*"))
+ assert_equal(false, Pathname(".foo").fnmatch("*"))
+ assert_equal(true, Pathname(".foo").fnmatch("*", File::FNM_DOTMATCH))
+ end
+
+ def test_fnmatch?
+ path = Pathname("a")
+ assert_equal(true, path.fnmatch?("*"))
+ assert_equal(false, path.fnmatch?("*.*"))
+ end
+
+ def test_ftype
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal("file", Pathname("f").ftype)
+ Dir.mkdir("d")
+ assert_equal("directory", Pathname("d").ftype)
+ }
+ end
+
+ def test_make_link
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ Pathname("l").make_link(Pathname("a"))
+ assert_equal("abc", Pathname("l").read)
+ }
+ end
+
+ def test_open
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ path = Pathname("a")
+
+ path.open {|f|
+ assert_equal("abc", f.read)
+ }
+
+ path.open("r") {|f|
+ assert_equal("abc", f.read)
+ }
+
+ Pathname("b").open("w", 0444) {|f| f.write "def" }
+ assert_equal(0444, File.stat("b").mode & 0777)
+ assert_equal("def", File.read("b"))
+
+ Pathname("c").open("w", 0444, {}) {|f| f.write "ghi" }
+ assert_equal(0444, File.stat("c").mode & 0777)
+ assert_equal("ghi", File.read("c"))
+
+ g = path.open
+ assert_equal("abc", g.read)
+ g.close
+ }
+ end
+
+ def test_readlink
+ return if !has_symlink?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ File.symlink("a", "l")
+ assert_equal(Pathname("a"), Pathname("l").readlink)
+ }
+ end
+
+ def test_rename
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ Pathname("a").rename(Pathname("b"))
+ assert_equal("abc", File.read("b"))
+ }
+ end
+
+ def test_stat
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ s = Pathname("a").stat
+ assert_equal(3, s.size)
+ }
+ end
+
+ def test_lstat
+ return if !has_symlink?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ File.symlink("a", "l")
+ s = Pathname("l").lstat
+ assert_equal(true, s.symlink?)
+ s = Pathname("l").stat
+ assert_equal(false, s.symlink?)
+ assert_equal(3, s.size)
+ s = Pathname("a").lstat
+ assert_equal(false, s.symlink?)
+ assert_equal(3, s.size)
+ }
+ end
+
+ def test_make_symlink
+ return if !has_symlink?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ Pathname("l").make_symlink(Pathname("a"))
+ s = Pathname("l").lstat
+ assert_equal(true, s.symlink?)
+ }
+ end
+
+ def test_truncate
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ Pathname("a").truncate(2)
+ assert_equal("ab", File.read("a"))
+ }
+ end
+
+ def test_utime
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {|f| f.write "abc" }
+ atime = Time.utc(2000)
+ mtime = Time.utc(1999)
+ Pathname("a").utime(atime, mtime)
+ s = File.stat("a")
+ assert_equal(atime, s.atime)
+ assert_equal(mtime, s.mtime)
+ }
+ end
+
+ def test_basename
+ assert_equal(Pathname("basename"), Pathname("dirname/basename").basename)
+ assert_equal(Pathname("bar"), Pathname("foo/bar.x").basename(".x"))
+ end
+
+ def test_dirname
+ assert_equal(Pathname("dirname"), Pathname("dirname/basename").dirname)
+ end
+
+ def test_extname
+ assert_equal(".ext", Pathname("basename.ext").extname)
+ end
+
+ def test_expand_path
+ drv = DOSISH_DRIVE_LETTER ? Dir.pwd.sub(%r(/.*), '') : ""
+ assert_equal(Pathname(drv + "/a"), Pathname("/a").expand_path)
+ assert_equal(Pathname(drv + "/a"), Pathname("a").expand_path("/"))
+ assert_equal(Pathname(drv + "/a"), Pathname("a").expand_path(Pathname("/")))
+ assert_equal(Pathname(drv + "/b"), Pathname("/b").expand_path(Pathname("/a")))
+ assert_equal(Pathname(drv + "/a/b"), Pathname("b").expand_path(Pathname("/a")))
+ end
+
+ def test_split
+ assert_equal([Pathname("dirname"), Pathname("basename")], Pathname("dirname/basename").split)
+ end
+
+ def test_blockdev?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").blockdev?)
+ }
+ end
+
+ def test_chardev?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").chardev?)
+ }
+ end
+
+ def test_executable?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").executable?)
+ }
+ end
+
+ def test_executable_real?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").executable_real?)
+ }
+ end
+
+ def test_exist?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(true, Pathname("f").exist?)
+ }
+ end
+
+ def test_grpowned?
+ skip "Unix file owner test" if DOSISH
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ File.chown(-1, Process.gid, "f")
+ assert_equal(true, Pathname("f").grpowned?)
+ }
+ end
+
+ def test_directory?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").directory?)
+ Dir.mkdir("d")
+ assert_equal(true, Pathname("d").directory?)
+ }
+ end
+
+ def test_file?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(true, Pathname("f").file?)
+ Dir.mkdir("d")
+ assert_equal(false, Pathname("d").file?)
+ }
+ end
+
+ def test_pipe?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").pipe?)
+ }
+ end
+
+ def test_socket?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").socket?)
+ }
+ end
+
+ def test_owned?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(true, Pathname("f").owned?)
+ }
+ end
+
+ def test_readable?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(true, Pathname("f").readable?)
+ }
+ end
+
+ def test_world_readable?
+ skip "Unix file mode bit test" if DOSISH
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ File.chmod(0400, "f")
+ assert_equal(nil, Pathname("f").world_readable?)
+ File.chmod(0444, "f")
+ assert_equal(0444, Pathname("f").world_readable?)
+ }
+ end
+
+ def test_readable_real?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(true, Pathname("f").readable_real?)
+ }
+ end
+
+ def test_setuid?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").setuid?)
+ }
+ end
+
+ def test_setgid?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").setgid?)
+ }
+ end
+
+ def test_size
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(3, Pathname("f").size)
+ open("z", "w") {|f| }
+ assert_equal(0, Pathname("z").size)
+ assert_raise(Errno::ENOENT) { Pathname("not-exist").size }
+ }
+ end
+
+ def test_size?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(3, Pathname("f").size?)
+ open("z", "w") {|f| }
+ assert_equal(nil, Pathname("z").size?)
+ assert_equal(nil, Pathname("not-exist").size?)
+ }
+ end
+
+ def test_sticky?
+ skip "Unix file mode bit test" if DOSISH
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").sticky?)
+ }
+ end
+
+ def test_symlink?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").symlink?)
+ }
+ end
+
+ def test_writable?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(true, Pathname("f").writable?)
+ }
+ end
+
+ def test_world_writable?
+ skip "Unix file mode bit test" if DOSISH
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ File.chmod(0600, "f")
+ assert_equal(nil, Pathname("f").world_writable?)
+ File.chmod(0666, "f")
+ assert_equal(0666, Pathname("f").world_writable?)
+ }
+ end
+
+ def test_writable_real?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(true, Pathname("f").writable?)
+ }
+ end
+
+ def test_zero?
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ assert_equal(false, Pathname("f").zero?)
+ open("z", "w") {|f| }
+ assert_equal(true, Pathname("z").zero?)
+ assert_equal(false, Pathname("not-exist").zero?)
+ }
+ end
+
+ def test_s_glob
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ Dir.mkdir("d")
+ assert_equal([Pathname("d"), Pathname("f")], Pathname.glob("*").sort)
+ a = []
+ Pathname.glob("*") {|path| a << path }
+ a.sort!
+ assert_equal([Pathname("d"), Pathname("f")], a)
+ }
+ end
+
+ def test_s_getwd
+ wd = Pathname.getwd
+ assert_kind_of(Pathname, wd)
+ end
+
+ def test_s_pwd
+ wd = Pathname.pwd
+ assert_kind_of(Pathname, wd)
+ end
+
+ def test_entries
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {}
+ open("b", "w") {}
+ assert_equal([Pathname("."), Pathname(".."), Pathname("a"), Pathname("b")], Pathname(".").entries.sort)
+ }
+ end
+
+ def test_each_entry
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {}
+ open("b", "w") {}
+ a = []
+ Pathname(".").each_entry {|v| a << v }
+ assert_equal([Pathname("."), Pathname(".."), Pathname("a"), Pathname("b")], a.sort)
+ }
+ end
+
+ def test_mkdir
+ with_tmpchdir('rubytest-pathname') {|dir|
+ Pathname("d").mkdir
+ assert(File.directory?("d"))
+ Pathname("e").mkdir(0770)
+ assert(File.directory?("e"))
+ }
+ end
+
+ def test_rmdir
+ with_tmpchdir('rubytest-pathname') {|dir|
+ Pathname("d").mkdir
+ assert(File.directory?("d"))
+ Pathname("d").rmdir
+ assert(!File.exist?("d"))
+ }
+ end
+
+ def test_opendir
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {}
+ open("b", "w") {}
+ a = []
+ Pathname(".").opendir {|d|
+ d.each {|e| a << e }
+ }
+ assert_equal([".", "..", "a", "b"], a.sort)
+ }
+ end
+
+ def test_find
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("a", "w") {}
+ open("b", "w") {}
+ Dir.mkdir("d")
+ open("d/x", "w") {}
+ open("d/y", "w") {}
+ a = []; Pathname(".").find {|v| a << v }; a.sort!
+ assert_equal([Pathname("."), Pathname("a"), Pathname("b"), Pathname("d"), Pathname("d/x"), Pathname("d/y")], a)
+ a = []; Pathname("d").find {|v| a << v }; a.sort!
+ assert_equal([Pathname("d"), Pathname("d/x"), Pathname("d/y")], a)
+ a = Pathname(".").find.sort
+ assert_equal([Pathname("."), Pathname("a"), Pathname("b"), Pathname("d"), Pathname("d/x"), Pathname("d/y")], a)
+ a = Pathname("d").find.sort
+ assert_equal([Pathname("d"), Pathname("d/x"), Pathname("d/y")], a)
+
+ begin
+ File.unlink("d/y")
+ File.chmod(0600, "d")
+ a = []; Pathname(".").find(ignore_error: true) {|v| a << v }; a.sort!
+ assert_equal([Pathname("."), Pathname("a"), Pathname("b"), Pathname("d"), Pathname("d/x")], a)
+ a = []; Pathname("d").find(ignore_error: true) {|v| a << v }; a.sort!
+ assert_equal([Pathname("d"), Pathname("d/x")], a)
+
+ skip "no meaning test on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
+ a = [];
+ assert_raise_with_message(Errno::EACCES, %r{d/x}) do
+ Pathname(".").find(ignore_error: false) {|v| a << v }
+ end
+ a.sort!
+ assert_equal([Pathname("."), Pathname("a"), Pathname("b"), Pathname("d"), Pathname("d/x")], a)
+ a = [];
+ assert_raise_with_message(Errno::EACCES, %r{d/x}) do
+ Pathname("d").find(ignore_error: false) {|v| a << v }
+ end
+ a.sort!
+ assert_equal([Pathname("d"), Pathname("d/x")], a)
+ ensure
+ File.chmod(0700, "d")
+ end
+ }
+ end
+
+ def test_mkpath
+ with_tmpchdir('rubytest-pathname') {|dir|
+ Pathname("a/b/c/d").mkpath
+ assert(File.directory?("a/b/c/d"))
+ }
+ end
+
+ def test_rmtree
+ with_tmpchdir('rubytest-pathname') {|dir|
+ Pathname("a/b/c/d").mkpath
+ assert(File.exist?("a/b/c/d"))
+ Pathname("a").rmtree
+ assert(!File.exist?("a"))
+ }
+ end
+
+ def test_unlink
+ with_tmpchdir('rubytest-pathname') {|dir|
+ open("f", "w") {|f| f.write "abc" }
+ Pathname("f").unlink
+ assert(!File.exist?("f"))
+ Dir.mkdir("d")
+ Pathname("d").unlink
+ assert(!File.exist?("d"))
+ }
+ end
+
+ def test_matchop
+ assert_raise(NoMethodError) { Pathname("a") =~ /a/ }
+ end
+
+ def test_file_basename
+ assert_equal("bar", File.basename(Pathname.new("foo/bar")))
+ end
+
+ def test_file_dirname
+ assert_equal("foo", File.dirname(Pathname.new("foo/bar")))
+ end
+
+ def test_file_split
+ assert_equal(["foo", "bar"], File.split(Pathname.new("foo/bar")))
+ end
+
+ def test_file_extname
+ assert_equal(".baz", File.extname(Pathname.new("bar.baz")))
+ end
+
+ def test_file_fnmatch
+ assert(File.fnmatch("*.*", Pathname.new("bar.baz")))
+ end
+
+ def test_file_join
+ assert_equal("foo/bar", File.join(Pathname.new("foo"), Pathname.new("bar")))
+ lambda {
+ $SAFE = 1
+ assert_equal("foo/bar", File.join(Pathname.new("foo"), Pathname.new("bar").taint))
+ }.call
+ end
+
+ def test_relative_path_from_casefold
+ assert_separately([], <<-'end;') # do
+ module File::Constants
+ remove_const :FNM_SYSCASE
+ FNM_SYSCASE = FNM_CASEFOLD
+ end
+ require 'pathname'
+ foo = Pathname.new("fo\u{f6}")
+ bar = Pathname.new("b\u{e4}r".encode("ISO-8859-1"))
+ assert_instance_of(Pathname, foo.relative_path_from(bar))
+ end;
+ end
+end
diff --git a/jni/ruby/test/psych/handlers/test_recorder.rb b/jni/ruby/test/psych/handlers/test_recorder.rb
new file mode 100644
index 0000000..96b8eac
--- /dev/null
+++ b/jni/ruby/test/psych/handlers/test_recorder.rb
@@ -0,0 +1,25 @@
+require 'psych/helper'
+require 'psych/handlers/recorder'
+
+module Psych
+ module Handlers
+ class TestRecorder < TestCase
+ def test_replay
+ yaml = "--- foo\n...\n"
+ output = StringIO.new
+
+ recorder = Psych::Handlers::Recorder.new
+ parser = Psych::Parser.new recorder
+ parser.parse yaml
+
+ assert_equal 5, recorder.events.length
+
+ emitter = Psych::Emitter.new output
+ recorder.events.each do |m, args|
+ emitter.send m, *args
+ end
+ assert_equal yaml, output.string
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/helper.rb b/jni/ruby/test/psych/helper.rb
new file mode 100644
index 0000000..11b2216
--- /dev/null
+++ b/jni/ruby/test/psych/helper.rb
@@ -0,0 +1,114 @@
+require 'minitest/autorun'
+require 'stringio'
+require 'tempfile'
+require 'date'
+require 'psych'
+
+module Psych
+ class TestCase < MiniTest::Unit::TestCase
+ def self.suppress_warning
+ verbose, $VERBOSE = $VERBOSE, nil
+ yield
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def with_default_external(enc)
+ verbose, $VERBOSE = $VERBOSE, nil
+ origenc, Encoding.default_external = Encoding.default_external, enc
+ $VERBOSE = verbose
+ yield
+ ensure
+ verbose, $VERBOSE = $VERBOSE, nil
+ Encoding.default_external = origenc
+ $VERBOSE = verbose
+ end
+
+ def with_default_internal(enc)
+ verbose, $VERBOSE = $VERBOSE, nil
+ origenc, Encoding.default_internal = Encoding.default_internal, enc
+ $VERBOSE = verbose
+ yield
+ ensure
+ verbose, $VERBOSE = $VERBOSE, nil
+ Encoding.default_internal = origenc
+ $VERBOSE = verbose
+ end
+
+ #
+ # Convert between Psych and the object to verify correct parsing and
+ # emitting
+ #
+ def assert_to_yaml( obj, yaml )
+ assert_equal( obj, Psych::load( yaml ) )
+ assert_equal( obj, Psych::parse( yaml ).transform )
+ assert_equal( obj, Psych::load( obj.psych_to_yaml ) )
+ assert_equal( obj, Psych::parse( obj.psych_to_yaml ).transform )
+ assert_equal( obj, Psych::load(
+ obj.psych_to_yaml(
+ :UseVersion => true, :UseHeader => true, :SortKeys => true
+ )
+ ))
+ end
+
+ #
+ # Test parser only
+ #
+ def assert_parse_only( obj, yaml )
+ assert_equal( obj, Psych::load( yaml ) )
+ assert_equal( obj, Psych::parse( yaml ).transform )
+ end
+
+ def assert_cycle( obj )
+ v = Visitors::YAMLTree.create
+ v << obj
+ assert_equal(obj, Psych.load(v.tree.yaml))
+ assert_equal( obj, Psych::load(Psych.dump(obj)))
+ assert_equal( obj, Psych::load( obj.psych_to_yaml ) )
+ end
+
+ #
+ # Make a time with the time zone
+ #
+ def mktime( year, mon, day, hour, min, sec, usec, zone = "Z" )
+ usec = Rational(usec.to_s) * 1000000
+ val = Time::utc( year.to_i, mon.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i, usec )
+ if zone != "Z"
+ hour = zone[0,3].to_i * 3600
+ min = zone[3,2].to_i * 60
+ ofs = (hour + min)
+ val = Time.at( val.tv_sec - ofs, val.tv_nsec / 1000.0 )
+ end
+ return val
+ end
+ end
+end
+
+# backport so that tests will run on 1.9 and 2.0.0
+unless Tempfile.respond_to? :create
+ def Tempfile.create(basename, *rest)
+ tmpfile = nil
+ Dir::Tmpname.create(basename, *rest) do |tmpname, n, opts|
+ mode = File::RDWR|File::CREAT|File::EXCL
+ perm = 0600
+ if opts
+ mode |= opts.delete(:mode) || 0
+ opts[:perm] = perm
+ perm = nil
+ else
+ opts = perm
+ end
+ tmpfile = File.open(tmpname, mode, opts)
+ end
+ if block_given?
+ begin
+ yield tmpfile
+ ensure
+ tmpfile.close if !tmpfile.closed?
+ File.unlink tmpfile
+ end
+ else
+ tmpfile
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/json/test_stream.rb b/jni/ruby/test/psych/json/test_stream.rb
new file mode 100644
index 0000000..b0c33e6
--- /dev/null
+++ b/jni/ruby/test/psych/json/test_stream.rb
@@ -0,0 +1,109 @@
+require 'psych/helper'
+
+module Psych
+ module JSON
+ class TestStream < TestCase
+ def setup
+ @io = StringIO.new
+ @stream = Psych::JSON::Stream.new(@io)
+ @stream.start
+ end
+
+ def test_explicit_documents
+ @io = StringIO.new
+ @stream = Psych::JSON::Stream.new(@io)
+ @stream.start
+
+ @stream.push({ 'foo' => 'bar' })
+
+ assert !@stream.finished?, 'stream not finished'
+ @stream.finish
+ assert @stream.finished?, 'stream finished'
+
+ assert_match(/^---/, @io.string)
+ assert_match(/\.\.\.$/, @io.string)
+ end
+
+ def test_null
+ @stream.push(nil)
+ assert_match(/^--- null/, @io.string)
+ end
+
+ def test_string
+ @stream.push "foo"
+ assert_match(/(["])foo\1/, @io.string)
+ end
+
+ def test_symbol
+ @stream.push :foo
+ assert_match(/(["])foo\1/, @io.string)
+ end
+
+ def test_int
+ @stream.push 10
+ assert_match(/^--- 10/, @io.string)
+ end
+
+ def test_float
+ @stream.push 1.2
+ assert_match(/^--- 1.2/, @io.string)
+ end
+
+ def test_hash
+ hash = { 'one' => 'two' }
+ @stream.push hash
+
+ json = @io.string
+ assert_match(/}$/, json)
+ assert_match(/^--- \{/, json)
+ assert_match(/["]one['"]/, json)
+ assert_match(/["]two['"]/, json)
+ end
+
+ def test_list_to_json
+ list = %w{ one two }
+ @stream.push list
+
+ json = @io.string
+ assert_match(/\]$/, json)
+ assert_match(/^--- \[/, json)
+ assert_match(/["]one["]/, json)
+ assert_match(/["]two["]/, json)
+ end
+
+ class Foo; end
+
+ def test_json_dump_exclude_tag
+ @stream << Foo.new
+ json = @io.string
+ refute_match('Foo', json)
+ end
+
+ class Bar
+ def encode_with coder
+ coder.represent_seq 'omg', %w{ a b c }
+ end
+ end
+
+ def test_json_list_dump_exclude_tag
+ @stream << Bar.new
+ json = @io.string
+ refute_match('omg', json)
+ end
+
+ def test_time
+ time = Time.utc(2010, 10, 10)
+ @stream.push({'a' => time })
+ json = @io.string
+ assert_match "{\"a\": \"2010-10-10 00:00:00.000000000 Z\"}\n", json
+ end
+
+ def test_datetime
+ time = Time.new(2010, 10, 10).to_datetime
+ @stream.push({'a' => time })
+ json = @io.string
+ assert_match "{\"a\": \"#{time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")}\"}\n", json
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/nodes/test_enumerable.rb b/jni/ruby/test/psych/nodes/test_enumerable.rb
new file mode 100644
index 0000000..19cf94b
--- /dev/null
+++ b/jni/ruby/test/psych/nodes/test_enumerable.rb
@@ -0,0 +1,43 @@
+require 'psych/helper'
+
+module Psych
+ module Nodes
+ class TestEnumerable < TestCase
+ def test_includes_enumerable
+ yaml = '--- hello'
+ assert_equal 3, Psych.parse_stream(yaml).to_a.length
+ end
+
+ def test_returns_enumerator
+ yaml = '--- hello'
+ assert_equal 3, Psych.parse_stream(yaml).each.map { |x| x }.length
+ end
+
+ def test_scalar
+ assert_equal 3, calls('--- hello').length
+ end
+
+ def test_sequence
+ assert_equal 4, calls("---\n- hello").length
+ end
+
+ def test_mapping
+ assert_equal 5, calls("---\nhello: world").length
+ end
+
+ def test_alias
+ assert_equal 5, calls("--- &yay\n- foo\n- *yay\n").length
+ end
+
+ private
+
+ def calls yaml
+ calls = []
+ Psych.parse_stream(yaml).each do |node|
+ calls << node
+ end
+ calls
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_alias_and_anchor.rb b/jni/ruby/test/psych/test_alias_and_anchor.rb
new file mode 100644
index 0000000..9e2c240
--- /dev/null
+++ b/jni/ruby/test/psych/test_alias_and_anchor.rb
@@ -0,0 +1,96 @@
+require_relative 'helper'
+
+class ObjectWithInstanceVariables
+ attr_accessor :var1, :var2
+end
+
+class SubStringWithInstanceVariables < String
+ attr_accessor :var1
+end
+
+module Psych
+ class TestAliasAndAnchor < TestCase
+ def test_mri_compatibility
+ yaml = <<EOYAML
+---
+- &id001 !ruby/object {}
+
+- *id001
+- *id001
+EOYAML
+ result = Psych.load yaml
+ result.each {|el| assert_same(result[0], el) }
+ end
+
+ def test_mri_compatibility_object_with_ivars
+ yaml = <<EOYAML
+---
+- &id001 !ruby/object:ObjectWithInstanceVariables
+ var1: test1
+ var2: test2
+- *id001
+- *id001
+EOYAML
+
+ result = Psych.load yaml
+ result.each do |el|
+ assert_same(result[0], el)
+ assert_equal('test1', el.var1)
+ assert_equal('test2', el.var2)
+ end
+ end
+
+ def test_mri_compatibility_substring_with_ivars
+ yaml = <<EOYAML
+---
+- &id001 !str:SubStringWithInstanceVariables
+ str: test
+ "@var1": test
+- *id001
+- *id001
+EOYAML
+ result = Psych.load yaml
+ result.each do |el|
+ assert_same(result[0], el)
+ assert_equal('test', el.var1)
+ end
+ end
+
+ def test_anchor_alias_round_trip
+ o = Object.new
+ original = [o,o,o]
+
+ yaml = Psych.dump original
+ result = Psych.load yaml
+ result.each {|el| assert_same(result[0], el) }
+ end
+
+ def test_anchor_alias_round_trip_object_with_ivars
+ o = ObjectWithInstanceVariables.new
+ o.var1 = 'test1'
+ o.var2 = 'test2'
+ original = [o,o,o]
+
+ yaml = Psych.dump original
+ result = Psych.load yaml
+ result.each do |el|
+ assert_same(result[0], el)
+ assert_equal('test1', el.var1)
+ assert_equal('test2', el.var2)
+ end
+ end
+
+ def test_anchor_alias_round_trip_substring_with_ivars
+ o = SubStringWithInstanceVariables.new
+ o.var1 = 'test'
+ original = [o,o,o]
+
+ yaml = Psych.dump original
+ result = Psych.load yaml
+ result.each do |el|
+ assert_same(result[0], el)
+ assert_equal('test', el.var1)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_array.rb b/jni/ruby/test/psych/test_array.rb
new file mode 100644
index 0000000..960ffd7
--- /dev/null
+++ b/jni/ruby/test/psych/test_array.rb
@@ -0,0 +1,57 @@
+require_relative 'helper'
+
+module Psych
+ class TestArray < TestCase
+ class X < Array
+ end
+
+ class Y < Array
+ attr_accessor :val
+ end
+
+ def setup
+ super
+ @list = [{ :a => 'b' }, 'foo']
+ end
+
+ def test_another_subclass_with_attributes
+ y = Y.new.tap {|y| y.val = 1}
+ y << "foo" << "bar"
+ y = Psych.load Psych.dump y
+
+ assert_equal %w{foo bar}, y
+ assert_equal Y, y.class
+ assert_equal 1, y.val
+ end
+
+ def test_subclass
+ yaml = Psych.dump X.new
+ assert_match X.name, yaml
+
+ list = X.new
+ list << 1
+ assert_equal X, list.class
+ assert_equal 1, list.first
+ end
+
+ def test_subclass_with_attributes
+ y = Psych.load Psych.dump Y.new.tap {|y| y.val = 1}
+ assert_equal Y, y.class
+ assert_equal 1, y.val
+ end
+
+ def test_backwards_with_syck
+ x = Psych.load "--- !seq:#{X.name} []\n\n"
+ assert_equal X, x.class
+ end
+
+ def test_self_referential
+ @list << @list
+ assert_cycle(@list)
+ end
+
+ def test_cycle
+ assert_cycle(@list)
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_boolean.rb b/jni/ruby/test/psych/test_boolean.rb
new file mode 100644
index 0000000..b656f4f
--- /dev/null
+++ b/jni/ruby/test/psych/test_boolean.rb
@@ -0,0 +1,36 @@
+require_relative 'helper'
+
+module Psych
+ ###
+ # Test booleans from YAML spec:
+ # http://yaml.org/type/bool.html
+ class TestBoolean < TestCase
+ %w{ yes Yes YES true True TRUE on On ON }.each do |truth|
+ define_method(:"test_#{truth}") do
+ assert_equal true, Psych.load("--- #{truth}")
+ end
+ end
+
+ %w{ no No NO false False FALSE off Off OFF }.each do |truth|
+ define_method(:"test_#{truth}") do
+ assert_equal false, Psych.load("--- #{truth}")
+ end
+ end
+
+ ###
+ # YAML spec says "y" and "Y" may be used as true, but Syck treats them
+ # as literal strings
+ def test_y
+ assert_equal "y", Psych.load("--- y")
+ assert_equal "Y", Psych.load("--- Y")
+ end
+
+ ###
+ # YAML spec says "n" and "N" may be used as false, but Syck treats them
+ # as literal strings
+ def test_n
+ assert_equal "n", Psych.load("--- n")
+ assert_equal "N", Psych.load("--- N")
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_class.rb b/jni/ruby/test/psych/test_class.rb
new file mode 100644
index 0000000..c7f964c
--- /dev/null
+++ b/jni/ruby/test/psych/test_class.rb
@@ -0,0 +1,36 @@
+require_relative 'helper'
+
+module Psych
+ class TestClass < TestCase
+ module Foo
+ end
+
+ def test_cycle_anonymous_class
+ assert_raises(::TypeError) do
+ assert_cycle(Class.new)
+ end
+ end
+
+ def test_cycle_anonymous_module
+ assert_raises(::TypeError) do
+ assert_cycle(Module.new)
+ end
+ end
+
+ def test_cycle
+ assert_cycle(TestClass)
+ end
+
+ def test_dump
+ Psych.dump TestClass
+ end
+
+ def test_cycle_module
+ assert_cycle(Foo)
+ end
+
+ def test_dump_module
+ Psych.dump Foo
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_coder.rb b/jni/ruby/test/psych/test_coder.rb
new file mode 100644
index 0000000..7571e89
--- /dev/null
+++ b/jni/ruby/test/psych/test_coder.rb
@@ -0,0 +1,184 @@
+require_relative 'helper'
+
+module Psych
+ class TestCoder < TestCase
+ class InitApi
+ attr_accessor :implicit
+ attr_accessor :style
+ attr_accessor :tag
+ attr_accessor :a, :b, :c
+
+ def initialize
+ @a = 1
+ @b = 2
+ @c = 3
+ end
+
+ def init_with coder
+ @a = coder['aa']
+ @b = coder['bb']
+ @implicit = coder.implicit
+ @tag = coder.tag
+ @style = coder.style
+ end
+
+ def encode_with coder
+ coder['aa'] = @a
+ coder['bb'] = @b
+ end
+ end
+
+ class TaggingCoder < InitApi
+ def encode_with coder
+ super
+ coder.tag = coder.tag.sub(/!/, '!hello')
+ coder.implicit = false
+ coder.style = Psych::Nodes::Mapping::FLOW
+ end
+ end
+
+ class ScalarCoder
+ def encode_with coder
+ coder.scalar = "foo"
+ end
+ end
+
+ class Represent
+ yaml_tag 'foo'
+ def encode_with coder
+ coder.represent_scalar 'foo', 'bar'
+ end
+ end
+
+ class RepresentWithInit
+ yaml_tag name
+ attr_accessor :str
+
+ def init_with coder
+ @str = coder.scalar
+ end
+
+ def encode_with coder
+ coder.represent_scalar self.class.name, 'bar'
+ end
+ end
+
+ class RepresentWithSeq
+ yaml_tag name
+ attr_accessor :seq
+
+ def init_with coder
+ @seq = coder.seq
+ end
+
+ def encode_with coder
+ coder.represent_seq self.class.name, %w{ foo bar }
+ end
+ end
+
+ class RepresentWithMap
+ yaml_tag name
+ attr_accessor :map
+
+ def init_with coder
+ @map = coder.map
+ end
+
+ def encode_with coder
+ coder.represent_map self.class.name, { "string" => 'a', :symbol => 'b' }
+ end
+ end
+
+ class RepresentWithObject
+ def encode_with coder
+ coder.represent_object self.class.name, 20
+ end
+ end
+
+ def test_represent_with_object
+ thing = Psych.load(Psych.dump(RepresentWithObject.new))
+ assert_equal 20, thing
+ end
+
+ def test_json_dump_exclude_tag
+ refute_match('TestCoder::InitApi', Psych.to_json(InitApi.new))
+ end
+
+ def test_map_takes_block
+ coder = Psych::Coder.new 'foo'
+ tag = coder.tag
+ style = coder.style
+ coder.map { |map| map.add 'foo', 'bar' }
+ assert_equal 'bar', coder['foo']
+ assert_equal tag, coder.tag
+ assert_equal style, coder.style
+ end
+
+ def test_map_with_tag
+ coder = Psych::Coder.new 'foo'
+ coder.map('hello') { |map| map.add 'foo', 'bar' }
+ assert_equal 'bar', coder['foo']
+ assert_equal 'hello', coder.tag
+ end
+
+ def test_map_with_tag_and_style
+ coder = Psych::Coder.new 'foo'
+ coder.map('hello', 'world') { |map| map.add 'foo', 'bar' }
+ assert_equal 'bar', coder['foo']
+ assert_equal 'hello', coder.tag
+ assert_equal 'world', coder.style
+ end
+
+ def test_represent_map
+ thing = Psych.load(Psych.dump(RepresentWithMap.new))
+ assert_equal({ "string" => 'a', :symbol => 'b' }, thing.map)
+ end
+
+ def test_represent_sequence
+ thing = Psych.load(Psych.dump(RepresentWithSeq.new))
+ assert_equal %w{ foo bar }, thing.seq
+ end
+
+ def test_represent_with_init
+ thing = Psych.load(Psych.dump(RepresentWithInit.new))
+ assert_equal 'bar', thing.str
+ end
+
+ def test_represent!
+ assert_match(/foo/, Psych.dump(Represent.new))
+ assert_instance_of(Represent, Psych.load(Psych.dump(Represent.new)))
+ end
+
+ def test_scalar_coder
+ foo = Psych.load(Psych.dump(ScalarCoder.new))
+ assert_equal 'foo', foo
+ end
+
+ def test_load_dumped_tagging
+ foo = InitApi.new
+ bar = Psych.load(Psych.dump(foo))
+ assert_equal false, bar.implicit
+ assert_equal "!ruby/object:Psych::TestCoder::InitApi", bar.tag
+ assert_equal Psych::Nodes::Mapping::BLOCK, bar.style
+ end
+
+ def test_dump_with_tag
+ foo = TaggingCoder.new
+ assert_match(/hello/, Psych.dump(foo))
+ assert_match(/\{aa/, Psych.dump(foo))
+ end
+
+ def test_dump_encode_with
+ foo = InitApi.new
+ assert_match(/aa/, Psych.dump(foo))
+ end
+
+ def test_dump_init_with
+ foo = InitApi.new
+ bar = Psych.load(Psych.dump(foo))
+ assert_equal foo.a, bar.a
+ assert_equal foo.b, bar.b
+ assert_nil bar.c
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_date_time.rb b/jni/ruby/test/psych/test_date_time.rb
new file mode 100644
index 0000000..72150ad
--- /dev/null
+++ b/jni/ruby/test/psych/test_date_time.rb
@@ -0,0 +1,38 @@
+require_relative 'helper'
+require 'date'
+
+module Psych
+ class TestDateTime < TestCase
+ def test_negative_year
+ time = Time.utc -1, 12, 16
+ assert_cycle time
+ end
+
+ def test_new_datetime
+ assert_cycle DateTime.new
+ end
+
+ def test_invalid_date
+ assert_cycle "2013-10-31T10:40:07-000000000000033"
+ end
+
+ def test_string_tag
+ dt = DateTime.now
+ yaml = Psych.dump dt
+ assert_match(/DateTime/, yaml)
+ end
+
+ def test_round_trip
+ dt = DateTime.now
+ assert_cycle dt
+ end
+
+ def test_alias_with_time
+ t = Time.now
+ h = {:a => t, :b => t}
+ yaml = Psych.dump h
+ assert_match('&', yaml)
+ assert_match('*', yaml)
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_deprecated.rb b/jni/ruby/test/psych/test_deprecated.rb
new file mode 100644
index 0000000..fd2d329
--- /dev/null
+++ b/jni/ruby/test/psych/test_deprecated.rb
@@ -0,0 +1,214 @@
+require_relative 'helper'
+
+module Psych
+ class TestDeprecated < TestCase
+ def teardown
+ $VERBOSE = @orig_verbose
+ Psych.domain_types.clear
+ end
+
+ class QuickEmitter
+ attr_reader :name
+ attr_reader :value
+
+ def initialize
+ @name = 'hello!!'
+ @value = 'Friday!'
+ end
+
+ def to_yaml opts = {}
+ Psych.quick_emit object_id, opts do |out|
+ out.map taguri, to_yaml_style do |map|
+ map.add 'name', @name
+ map.add 'value', nil
+ end
+ end
+ end
+ end
+
+ def setup
+ @qe = QuickEmitter.new
+ @orig_verbose, $VERBOSE = $VERBOSE, false
+ end
+
+ def test_quick_emit
+ qe2 = Psych.load @qe.to_yaml
+ assert_equal @qe.name, qe2.name
+ assert_instance_of QuickEmitter, qe2
+ assert_nil qe2.value
+ end
+
+ def test_recursive_quick_emit
+ hash = { :qe => @qe }
+ hash2 = Psych.load Psych.dump hash
+ qe = hash2[:qe]
+
+ assert_equal @qe.name, qe.name
+ assert_instance_of QuickEmitter, qe
+ assert_nil qe.value
+ end
+
+ class QuickEmitterEncodeWith
+ attr_reader :name
+ attr_reader :value
+
+ def initialize
+ @name = 'hello!!'
+ @value = 'Friday!'
+ end
+
+ def encode_with coder
+ coder.map do |map|
+ map.add 'name', @name
+ map.add 'value', nil
+ end
+ end
+
+ def to_yaml opts = {}
+ raise
+ end
+ end
+
+ ###
+ # An object that defines both to_yaml and encode_with should only call
+ # encode_with.
+ def test_recursive_quick_emit_encode_with
+ qeew = QuickEmitterEncodeWith.new
+ hash = { :qe => qeew }
+ hash2 = Psych.load Psych.dump hash
+ qe = hash2[:qe]
+
+ assert_equal qeew.name, qe.name
+ assert_instance_of QuickEmitterEncodeWith, qe
+ assert_nil qe.value
+ end
+
+ class YamlInit
+ attr_reader :name
+ attr_reader :value
+
+ def initialize
+ @name = 'hello!!'
+ @value = 'Friday!'
+ end
+
+ def yaml_initialize tag, vals
+ vals.each { |ivar, val| instance_variable_set "@#{ivar}", 'TGIF!' }
+ end
+ end
+
+ def test_yaml_initialize
+ hash = { :yi => YamlInit.new }
+ hash2 = Psych.load Psych.dump hash
+ yi = hash2[:yi]
+
+ assert_equal 'TGIF!', yi.name
+ assert_equal 'TGIF!', yi.value
+ assert_instance_of YamlInit, yi
+ end
+
+ class YamlInitAndInitWith
+ attr_reader :name
+ attr_reader :value
+
+ def initialize
+ @name = 'shaners'
+ @value = 'Friday!'
+ end
+
+ def init_with coder
+ coder.map.each { |ivar, val| instance_variable_set "@#{ivar}", 'TGIF!' }
+ end
+
+ def yaml_initialize tag, vals
+ raise
+ end
+ end
+
+ ###
+ # An object that implements both yaml_initialize and init_with should not
+ # receive the yaml_initialize call.
+ def test_yaml_initialize_and_init_with
+ hash = { :yi => YamlInitAndInitWith.new }
+ hash2 = Psych.load Psych.dump hash
+ yi = hash2[:yi]
+
+ assert_equal 'TGIF!', yi.name
+ assert_equal 'TGIF!', yi.value
+ assert_instance_of YamlInitAndInitWith, yi
+ end
+
+ def test_coder_scalar
+ coder = Psych::Coder.new 'foo'
+ coder.scalar('tag', 'some string', :plain)
+ assert_equal 'tag', coder.tag
+ assert_equal 'some string', coder.scalar
+ assert_equal :scalar, coder.type
+ end
+
+ class YamlAs
+ TestCase.suppress_warning do
+ psych_yaml_as 'helloworld' # this should be yaml_as but to avoid syck
+ end
+ end
+
+ def test_yaml_as
+ assert_match(/helloworld/, Psych.dump(YamlAs.new))
+ end
+
+ def test_ruby_type
+ types = []
+ appender = lambda { |*args| types << args }
+
+ Psych.add_ruby_type('foo', &appender)
+ Psych.load <<-eoyml
+- !ruby.yaml.org,2002/foo bar
+ eoyml
+
+ assert_equal [["tag:ruby.yaml.org,2002:foo", "bar"]], types
+ end
+
+ def test_detect_implicit
+ assert_equal '', Psych.detect_implicit(nil)
+ assert_equal '', Psych.detect_implicit(Object.new)
+ assert_equal '', Psych.detect_implicit(1.2)
+ assert_equal 'null', Psych.detect_implicit('')
+ assert_equal 'string', Psych.detect_implicit('foo')
+ end
+
+ def test_private_type
+ types = []
+ Psych.add_private_type('foo') { |*args| types << args }
+ Psych.load <<-eoyml
+- !x-private:foo bar
+ eoyml
+
+ assert_equal [["x-private:foo", "bar"]], types
+ end
+
+ def test_tagurize
+ assert_nil Psych.tagurize nil
+ assert_equal Psych, Psych.tagurize(Psych)
+ assert_equal 'tag:yaml.org,2002:foo', Psych.tagurize('foo')
+ end
+
+ def test_read_type_class
+ things = Psych.read_type_class 'tag:yaml.org,2002:int:Psych::TestDeprecated::QuickEmitter', Object
+ assert_equal 'int', things.first
+ assert_equal Psych::TestDeprecated::QuickEmitter, things.last
+ end
+
+ def test_read_type_class_no_class
+ things = Psych.read_type_class 'tag:yaml.org,2002:int', Object
+ assert_equal 'int', things.first
+ assert_equal Object, things.last
+ end
+
+ def test_object_maker
+ thing = Psych.object_maker(Object, { 'a' => 'b', 'c' => 'd' })
+ assert_instance_of(Object, thing)
+ assert_equal 'b', thing.instance_variable_get(:@a)
+ assert_equal 'd', thing.instance_variable_get(:@c)
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_document.rb b/jni/ruby/test/psych/test_document.rb
new file mode 100644
index 0000000..bd77d60
--- /dev/null
+++ b/jni/ruby/test/psych/test_document.rb
@@ -0,0 +1,46 @@
+require_relative 'helper'
+
+module Psych
+ class TestDocument < TestCase
+ def setup
+ super
+ @stream = Psych.parse_stream(<<-eoyml)
+%YAML 1.1
+%TAG ! tag:tenderlovemaking.com,2009:
+--- !fun
+ eoyml
+ @doc = @stream.children.first
+ end
+
+ def test_parse_tag
+ assert_equal([['!', 'tag:tenderlovemaking.com,2009:']],
+ @doc.tag_directives)
+ end
+
+ def test_emit_tag
+ assert_match('%TAG ! tag:tenderlovemaking.com,2009:', @stream.yaml)
+ end
+
+ def test_emit_multitag
+ @doc.tag_directives << ['!!', 'foo.com,2009:']
+ yaml = @stream.yaml
+ assert_match('%TAG ! tag:tenderlovemaking.com,2009:', yaml)
+ assert_match('%TAG !! foo.com,2009:', yaml)
+ end
+
+ def test_emit_bad_tag
+ assert_raises(RuntimeError) do
+ @doc.tag_directives = [['!']]
+ @stream.yaml
+ end
+ end
+
+ def test_parse_version
+ assert_equal([1,1], @doc.version)
+ end
+
+ def test_emit_version
+ assert_match('%YAML 1.1', @stream.yaml)
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_emitter.rb b/jni/ruby/test/psych/test_emitter.rb
new file mode 100644
index 0000000..1c96c12
--- /dev/null
+++ b/jni/ruby/test/psych/test_emitter.rb
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+
+require_relative 'helper'
+
+module Psych
+ class TestEmitter < TestCase
+ def setup
+ super
+ @out = StringIO.new('')
+ @emitter = Psych::Emitter.new @out
+ end
+
+ def test_line_width
+ @emitter.line_width = 10
+ assert_equal 10, @emitter.line_width
+ end
+
+ def test_set_canonical
+ @emitter.canonical = true
+ assert_equal true, @emitter.canonical
+
+ @emitter.canonical = false
+ assert_equal false, @emitter.canonical
+ end
+
+ def test_indentation_set
+ assert_equal 2, @emitter.indentation
+ @emitter.indentation = 5
+ assert_equal 5, @emitter.indentation
+ end
+
+ def test_emit_utf_8
+ @emitter.start_stream Psych::Nodes::Stream::UTF8
+ @emitter.start_document [], [], false
+ @emitter.scalar '日本語', nil, nil, false, true, 1
+ @emitter.end_document true
+ @emitter.end_stream
+ assert_match('日本語', @out.string)
+ end
+
+ def test_start_stream_arg_error
+ assert_raises(TypeError) do
+ @emitter.start_stream 'asdfasdf'
+ end
+ end
+
+ def test_start_doc_arg_error
+ @emitter.start_stream Psych::Nodes::Stream::UTF8
+
+ [
+ [nil, [], false],
+ [[nil, nil], [], false],
+ [[], 'foo', false],
+ [[], ['foo'], false],
+ [[], [nil,nil], false],
+ ].each do |args|
+ assert_raises(TypeError) do
+ @emitter.start_document(*args)
+ end
+ end
+ end
+
+ def test_scalar_arg_error
+ @emitter.start_stream Psych::Nodes::Stream::UTF8
+ @emitter.start_document [], [], false
+
+ [
+ [:foo, nil, nil, false, true, 1],
+ ['foo', Object.new, nil, false, true, 1],
+ ['foo', nil, Object.new, false, true, 1],
+ ['foo', nil, nil, false, true, :foo],
+ [nil, nil, nil, false, true, 1],
+ ].each do |args|
+ assert_raises(TypeError) do
+ @emitter.scalar(*args)
+ end
+ end
+ end
+
+ def test_start_sequence_arg_error
+ @emitter.start_stream Psych::Nodes::Stream::UTF8
+ @emitter.start_document [], [], false
+
+ assert_raises(TypeError) do
+ @emitter.start_sequence(nil, Object.new, true, 1)
+ end
+
+ assert_raises(TypeError) do
+ @emitter.start_sequence(nil, nil, true, :foo)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_encoding.rb b/jni/ruby/test/psych/test_encoding.rb
new file mode 100644
index 0000000..517cae2
--- /dev/null
+++ b/jni/ruby/test/psych/test_encoding.rb
@@ -0,0 +1,259 @@
+# -*- coding: utf-8 -*-
+
+require_relative 'helper'
+
+module Psych
+ class TestEncoding < TestCase
+ class EncodingCatcher < Handler
+ attr_reader :strings
+ def initialize
+ @strings = []
+ end
+
+ (Handler.instance_methods(true) -
+ Object.instance_methods).each do |m|
+ class_eval %{
+ def #{m} *args
+ @strings += args.flatten.find_all { |a|
+ String === a
+ }
+ end
+ }
+ end
+ end
+
+ def setup
+ super
+ @buffer = StringIO.new
+ @handler = EncodingCatcher.new
+ @parser = Psych::Parser.new @handler
+ @utf8 = Encoding.find('UTF-8')
+ @emitter = Psych::Emitter.new @buffer
+ end
+
+ def test_dump_load_encoding_object
+ assert_cycle Encoding::US_ASCII
+ assert_cycle Encoding::UTF_8
+ end
+
+ def test_transcode_shiftjis
+ str = "こんにちは!"
+ loaded = Psych.load("--- こんにちは!".encode('SHIFT_JIS'))
+ assert_equal str, loaded
+ end
+
+ def test_transcode_utf16le
+ str = "こんにちは!"
+ loaded = Psych.load("--- こんにちは!".encode('UTF-16LE'))
+ assert_equal str, loaded
+ end
+
+ def test_transcode_utf16be
+ str = "こんにちは!"
+ loaded = Psych.load("--- こんにちは!".encode('UTF-16BE'))
+ assert_equal str, loaded
+ end
+
+ def test_io_shiftjis
+ Tempfile.create(['shiftjis', 'yml'], :encoding => 'SHIFT_JIS') {|t|
+ t.write '--- こんにちは!'
+ t.close
+
+ # If the external encoding isn't utf8, utf16le, or utf16be, we cannot
+ # process the file.
+ File.open(t.path, 'r', :encoding => 'SHIFT_JIS') do |f|
+ assert_raises Psych::SyntaxError do
+ Psych.load(f)
+ end
+ end
+ }
+ end
+
+ def test_io_utf16le
+ Tempfile.create(['utf16le', 'yml']) {|t|
+ t.binmode
+ t.write '--- こんにちは!'.encode('UTF-16LE')
+ t.close
+
+ File.open(t.path, 'rb', :encoding => 'UTF-16LE') do |f|
+ assert_equal "こんにちは!", Psych.load(f)
+ end
+ }
+ end
+
+ def test_io_utf16be
+ Tempfile.create(['utf16be', 'yml']) {|t|
+ t.binmode
+ t.write '--- こんにちは!'.encode('UTF-16BE')
+ t.close
+
+ File.open(t.path, 'rb', :encoding => 'UTF-16BE') do |f|
+ assert_equal "こんにちは!", Psych.load(f)
+ end
+ }
+ end
+
+ def test_io_utf8
+ Tempfile.create(['utf8', 'yml']) {|t|
+ t.binmode
+ t.write '--- こんにちは!'.encode('UTF-8')
+ t.close
+
+ File.open(t.path, 'rb', :encoding => 'UTF-8') do |f|
+ assert_equal "こんにちは!", Psych.load(f)
+ end
+ }
+ end
+
+ def test_emit_alias
+ @emitter.start_stream Psych::Parser::UTF8
+ @emitter.start_document [], [], true
+ e = assert_raises(RuntimeError) do
+ @emitter.alias 'ドラえもん'.encode('EUC-JP')
+ end
+ assert_match(/alias value/, e.message)
+ end
+
+ def test_to_yaml_is_valid
+ with_default_external(Encoding::US_ASCII) do
+ with_default_internal(nil) do
+ s = "こんにちは!"
+ # If no encoding is specified, use UTF-8
+ assert_equal Encoding::UTF_8, Psych.dump(s).encoding
+ assert_equal s, Psych.load(Psych.dump(s))
+ end
+ end
+ end
+
+ def test_start_mapping
+ foo = 'foo'
+ bar = 'バー'
+
+ @emitter.start_stream Psych::Parser::UTF8
+ @emitter.start_document [], [], true
+ @emitter.start_mapping(
+ foo.encode('Shift_JIS'),
+ bar.encode('UTF-16LE'),
+ false, Nodes::Sequence::ANY)
+ @emitter.end_mapping
+ @emitter.end_document false
+ @emitter.end_stream
+
+ @parser.parse @buffer.string
+ assert_encodings @utf8, @handler.strings
+ assert_equal [foo, bar], @handler.strings
+ end
+
+ def test_start_sequence
+ foo = 'foo'
+ bar = 'バー'
+
+ @emitter.start_stream Psych::Parser::UTF8
+ @emitter.start_document [], [], true
+ @emitter.start_sequence(
+ foo.encode('Shift_JIS'),
+ bar.encode('UTF-16LE'),
+ false, Nodes::Sequence::ANY)
+ @emitter.end_sequence
+ @emitter.end_document false
+ @emitter.end_stream
+
+ @parser.parse @buffer.string
+ assert_encodings @utf8, @handler.strings
+ assert_equal [foo, bar], @handler.strings
+ end
+
+ def test_doc_tag_encoding
+ key = '鍵'
+ @emitter.start_stream Psych::Parser::UTF8
+ @emitter.start_document(
+ [1, 1],
+ [['!'.encode('EUC-JP'), key.encode('EUC-JP')]],
+ true
+ )
+ @emitter.scalar 'foo', nil, nil, true, false, Nodes::Scalar::ANY
+ @emitter.end_document false
+ @emitter.end_stream
+
+ @parser.parse @buffer.string
+ assert_encodings @utf8, @handler.strings
+ assert_equal key, @handler.strings[1]
+ end
+
+ def test_emitter_encoding
+ str = "壁に耳あり、障子に目あり"
+ thing = Psych.load Psych.dump str.encode('EUC-JP')
+ assert_equal str, thing
+ end
+
+ def test_default_internal
+ with_default_internal(Encoding::EUC_JP) do
+ str = "壁に耳あり、障子に目あり"
+ assert_equal @utf8, str.encoding
+
+ @parser.parse str
+ assert_encodings Encoding::EUC_JP, @handler.strings
+ assert_equal str, @handler.strings.first.encode('UTF-8')
+ end
+ end
+
+ def test_scalar
+ @parser.parse("--- a")
+ assert_encodings @utf8, @handler.strings
+ end
+
+ def test_alias
+ @parser.parse(<<-eoyml)
+%YAML 1.1
+---
+!!seq [
+ !!str "Without properties",
+ &A !!str "Anchored",
+ !!str "Tagged",
+ *A,
+ !!str "",
+]
+ eoyml
+ assert_encodings @utf8, @handler.strings
+ end
+
+ def test_list_anchor
+ list = %w{ a b }
+ list << list
+ @parser.parse(Psych.dump(list))
+ assert_encodings @utf8, @handler.strings
+ end
+
+ def test_map_anchor
+ h = {}
+ h['a'] = h
+ @parser.parse(Psych.dump(h))
+ assert_encodings @utf8, @handler.strings
+ end
+
+ def test_map_tag
+ @parser.parse(<<-eoyml)
+%YAML 1.1
+---
+!!map { a : b }
+ eoyml
+ assert_encodings @utf8, @handler.strings
+ end
+
+ def test_doc_tag
+ @parser.parse(<<-eoyml)
+%YAML 1.1
+%TAG ! tag:tenderlovemaking.com,2009:
+--- !fun
+ eoyml
+ assert_encodings @utf8, @handler.strings
+ end
+
+ private
+ def assert_encodings encoding, strings
+ strings.each do |str|
+ assert_equal encoding, str.encoding, str
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_exception.rb b/jni/ruby/test/psych/test_exception.rb
new file mode 100644
index 0000000..30dfb24
--- /dev/null
+++ b/jni/ruby/test/psych/test_exception.rb
@@ -0,0 +1,157 @@
+require_relative 'helper'
+
+module Psych
+ class TestException < TestCase
+ class Wups < Exception
+ attr_reader :foo, :bar
+ def initialize *args
+ super
+ @foo = 1
+ @bar = 2
+ end
+ end
+
+ def setup
+ super
+ @wups = Wups.new
+ end
+
+ def test_naming_exception
+ err = String.xxx rescue $!
+ new_err = Psych.load(Psych.dump(err))
+ assert_equal err.message, new_err.message
+ end
+
+ def test_load_takes_file
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.load '--- `'
+ end
+ assert_nil ex.file
+
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.load '--- `', 'meow'
+ end
+ assert_equal 'meow', ex.file
+ end
+
+ def test_psych_parse_stream_takes_file
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.parse_stream '--- `'
+ end
+ assert_nil ex.file
+ assert_match '(<unknown>)', ex.message
+
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.parse_stream '--- `', 'omg!'
+ end
+ assert_equal 'omg!', ex.file
+ assert_match 'omg!', ex.message
+ end
+
+ def test_load_stream_takes_file
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.load_stream '--- `'
+ end
+ assert_nil ex.file
+ assert_match '(<unknown>)', ex.message
+
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.load_stream '--- `', 'omg!'
+ end
+ assert_equal 'omg!', ex.file
+ end
+
+ def test_parse_file_exception
+ Tempfile.create(['parsefile', 'yml']) {|t|
+ t.binmode
+ t.write '--- `'
+ t.close
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.parse_file t.path
+ end
+ assert_equal t.path, ex.file
+ }
+ end
+
+ def test_load_file_exception
+ Tempfile.create(['loadfile', 'yml']) {|t|
+ t.binmode
+ t.write '--- `'
+ t.close
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.load_file t.path
+ end
+ assert_equal t.path, ex.file
+ }
+ end
+
+ def test_psych_parse_takes_file
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.parse '--- `'
+ end
+ assert_match '(<unknown>)', ex.message
+ assert_nil ex.file
+
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.parse '--- `', 'omg!'
+ end
+ assert_match 'omg!', ex.message
+ end
+
+ def test_attributes
+ e = assert_raises(Psych::SyntaxError) {
+ Psych.load '--- `foo'
+ }
+
+ assert_nil e.file
+ assert_equal 1, e.line
+ assert_equal 5, e.column
+ # FIXME: offset isn't being set correctly by libyaml
+ # assert_equal 5, e.offset
+
+ assert e.problem
+ assert e.context
+ end
+
+ def test_convert
+ w = Psych.load(Psych.dump(@wups))
+ assert_equal @wups, w
+ assert_equal 1, w.foo
+ assert_equal 2, w.bar
+ end
+
+ def test_to_yaml_properties
+ class << @wups
+ def to_yaml_properties
+ [:@foo]
+ end
+ end
+
+ w = Psych.load(Psych.dump(@wups))
+ assert_equal @wups, w
+ assert_equal 1, w.foo
+ assert_nil w.bar
+ end
+
+ def test_psych_syntax_error
+ Tempfile.create(['parsefile', 'yml']) do |t|
+ t.binmode
+ t.write '--- `'
+ t.close
+
+ begin
+ Psych.parse_file t.path
+ rescue StandardError
+ assert true # count assertion
+ ensure
+ return unless $!
+
+ ancestors = $!.class.ancestors.inspect
+
+ flunk "Psych::SyntaxError not rescued by StandardError: #{ancestors}"
+ end
+ end
+ end
+
+ end
+end
diff --git a/jni/ruby/test/psych/test_hash.rb b/jni/ruby/test/psych/test_hash.rb
new file mode 100644
index 0000000..264a471
--- /dev/null
+++ b/jni/ruby/test/psych/test_hash.rb
@@ -0,0 +1,49 @@
+require_relative 'helper'
+
+module Psych
+ class TestHash < TestCase
+ class X < Hash
+ end
+
+ def setup
+ super
+ @hash = { :a => 'b' }
+ end
+
+ def test_load_with_class_syck_compatibility
+ hash = Psych.load "--- !ruby/object:Hash\n:user_id: 7\n:username: Lucas\n"
+ assert_equal({ user_id: 7, username: 'Lucas'}, hash)
+ end
+
+ def test_empty_subclass
+ assert_match "!ruby/hash:#{X}", Psych.dump(X.new)
+ x = Psych.load Psych.dump X.new
+ assert_equal X, x.class
+ end
+
+ def test_map
+ x = Psych.load "--- !map:#{X} { }\n"
+ assert_equal X, x.class
+ end
+
+ def test_self_referential
+ @hash['self'] = @hash
+ assert_cycle(@hash)
+ end
+
+ def test_cycles
+ assert_cycle(@hash)
+ end
+
+ def test_ref_append
+ hash = Psych.load(<<-eoyml)
+---
+foo: &foo
+ hello: world
+bar:
+ <<: *foo
+eoyml
+ assert_equal({"foo"=>{"hello"=>"world"}, "bar"=>{"hello"=>"world"}}, hash)
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_json_tree.rb b/jni/ruby/test/psych/test_json_tree.rb
new file mode 100644
index 0000000..a23fc1a
--- /dev/null
+++ b/jni/ruby/test/psych/test_json_tree.rb
@@ -0,0 +1,65 @@
+require_relative 'helper'
+
+module Psych
+ class TestJSONTree < TestCase
+ def test_string
+ assert_match(/"foo"/, Psych.to_json("foo"))
+ end
+
+ def test_symbol
+ assert_match(/"foo"/, Psych.to_json(:foo))
+ end
+
+ def test_nil
+ assert_match(/^null/, Psych.to_json(nil))
+ end
+
+ def test_int
+ assert_match(/^10/, Psych.to_json(10))
+ end
+
+ def test_float
+ assert_match(/^1.2/, Psych.to_json(1.2))
+ end
+
+ def test_hash
+ hash = { 'one' => 'two' }
+ json = Psych.to_json(hash)
+ assert_match(/}$/, json)
+ assert_match(/^\{/, json)
+ assert_match(/['"]one['"]/, json)
+ assert_match(/['"]two['"]/, json)
+ end
+
+ class Bar
+ def encode_with coder
+ coder.represent_seq 'omg', %w{ a b c }
+ end
+ end
+
+ def test_json_list_dump_exclude_tag
+ json = Psych.to_json Bar.new
+ refute_match('omg', json)
+ end
+
+ def test_list_to_json
+ list = %w{ one two }
+ json = Psych.to_json(list)
+ assert_match(/\]$/, json)
+ assert_match(/^\[/, json)
+ assert_match(/"one"/, json)
+ assert_match(/"two"/, json)
+ end
+
+ def test_time
+ time = Time.utc(2010, 10, 10)
+ assert_equal "{\"a\": \"2010-10-10 00:00:00.000000000 Z\"}\n",
+Psych.to_json({'a' => time })
+ end
+
+ def test_datetime
+ time = Time.new(2010, 10, 10).to_datetime
+ assert_equal "{\"a\": \"#{time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")}\"}\n", Psych.to_json({'a' => time })
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_marshalable.rb b/jni/ruby/test/psych/test_marshalable.rb
new file mode 100644
index 0000000..7df74ee
--- /dev/null
+++ b/jni/ruby/test/psych/test_marshalable.rb
@@ -0,0 +1,54 @@
+require_relative 'helper'
+require 'delegate'
+
+module Psych
+ class TestMarshalable < TestCase
+ def test_objects_defining_marshal_dump_and_marshal_load_can_be_dumped
+ sd = SimpleDelegator.new(1)
+ loaded = Psych.load(Psych.dump(sd))
+
+ assert_instance_of(SimpleDelegator, loaded)
+ assert_equal(sd, loaded)
+ end
+
+ class PsychCustomMarshalable < BasicObject
+ attr_reader :foo
+
+ def initialize(foo)
+ @foo = foo
+ end
+
+ def marshal_dump
+ [foo]
+ end
+
+ def mashal_load(data)
+ @foo = data[0]
+ end
+
+ def init_with(coder)
+ @foo = coder['foo']
+ end
+
+ def encode_with(coder)
+ coder['foo'] = 2
+ end
+
+ def respond_to?(method)
+ [:marshal_dump, :marshal_load, :init_with, :encode_with].include?(method)
+ end
+
+ def class
+ PsychCustomMarshalable
+ end
+ end
+
+ def test_init_with_takes_priority_over_marshal_methods
+ obj = PsychCustomMarshalable.new(1)
+ loaded = Psych.load(Psych.dump(obj))
+
+ assert(PsychCustomMarshalable === loaded)
+ assert_equal(2, loaded.foo)
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_merge_keys.rb b/jni/ruby/test/psych/test_merge_keys.rb
new file mode 100644
index 0000000..1620a6a
--- /dev/null
+++ b/jni/ruby/test/psych/test_merge_keys.rb
@@ -0,0 +1,180 @@
+require_relative 'helper'
+
+module Psych
+ class TestMergeKeys < TestCase
+ class Product
+ attr_reader :bar
+ end
+
+ def test_merge_key_with_bare_hash
+ doc = Psych.load <<-eodoc
+map:
+ <<:
+ hello: world
+ eodoc
+ hash = { "map" => { "hello" => "world" } }
+ assert_equal hash, doc
+ end
+
+ def test_roundtrip_with_chevron_key
+ h = {}
+ v = { 'a' => h, '<<' => h }
+ assert_cycle v
+ end
+
+ def test_explicit_string
+ doc = Psych.load <<-eoyml
+a: &me { hello: world }
+b: { !!str '<<': *me }
+eoyml
+ expected = {
+ "a" => { "hello" => "world" },
+ "b" => {
+ "<<" => { "hello" => "world" }
+ }
+ }
+ assert_equal expected, doc
+ end
+
+ def test_mergekey_with_object
+ s = <<-eoyml
+foo: &foo
+ bar: 10
+product:
+ !ruby/object:#{Product.name}
+ <<: *foo
+ eoyml
+ hash = Psych.load s
+ assert_equal({"bar" => 10}, hash["foo"])
+ product = hash["product"]
+ assert_equal 10, product.bar
+ end
+
+ def test_merge_nil
+ yaml = <<-eoyml
+defaults: &defaults
+development:
+ <<: *defaults
+ eoyml
+ assert_equal({'<<' => nil }, Psych.load(yaml)['development'])
+ end
+
+ def test_merge_array
+ yaml = <<-eoyml
+foo: &hello
+- 1
+baz:
+ <<: *hello
+ eoyml
+ assert_equal({'<<' => [1]}, Psych.load(yaml)['baz'])
+ end
+
+ def test_merge_is_not_partial
+ yaml = <<-eoyml
+default: &default
+ hello: world
+foo: &hello
+- 1
+baz:
+ <<: [*hello, *default]
+ eoyml
+ doc = Psych.load yaml
+ refute doc['baz'].key? 'hello'
+ assert_equal({'<<' => [[1], {"hello"=>"world"}]}, Psych.load(yaml)['baz'])
+ end
+
+ def test_merge_seq_nil
+ yaml = <<-eoyml
+foo: &hello
+baz:
+ <<: [*hello]
+ eoyml
+ assert_equal({'<<' => [nil]}, Psych.load(yaml)['baz'])
+ end
+
+ def test_bad_seq_merge
+ yaml = <<-eoyml
+defaults: &defaults [1, 2, 3]
+development:
+ <<: *defaults
+ eoyml
+ assert_equal({'<<' => [1,2,3]}, Psych.load(yaml)['development'])
+ end
+
+ def test_missing_merge_key
+ yaml = <<-eoyml
+bar:
+ << : *foo
+ eoyml
+ exp = assert_raises(Psych::BadAlias) { Psych.load yaml }
+ assert_match 'foo', exp.message
+ end
+
+ # [ruby-core:34679]
+ def test_merge_key
+ yaml = <<-eoyml
+foo: &foo
+ hello: world
+bar:
+ << : *foo
+ baz: boo
+ eoyml
+
+ hash = {
+ "foo" => { "hello" => "world"},
+ "bar" => { "hello" => "world", "baz" => "boo" } }
+ assert_equal hash, Psych.load(yaml)
+ end
+
+ def test_multiple_maps
+ yaml = <<-eoyaml
+---
+- &CENTER { x: 1, y: 2 }
+- &LEFT { x: 0, y: 2 }
+- &BIG { r: 10 }
+- &SMALL { r: 1 }
+
+# All the following maps are equal:
+
+- # Merge multiple maps
+ << : [ *CENTER, *BIG ]
+ label: center/big
+ eoyaml
+
+ hash = {
+ 'x' => 1,
+ 'y' => 2,
+ 'r' => 10,
+ 'label' => 'center/big'
+ }
+
+ assert_equal hash, Psych.load(yaml)[4]
+ end
+
+ def test_override
+ yaml = <<-eoyaml
+---
+- &CENTER { x: 1, y: 2 }
+- &LEFT { x: 0, y: 2 }
+- &BIG { r: 10 }
+- &SMALL { r: 1 }
+
+# All the following maps are equal:
+
+- # Override
+ << : [ *BIG, *LEFT, *SMALL ]
+ x: 1
+ label: center/big
+ eoyaml
+
+ hash = {
+ 'x' => 1,
+ 'y' => 2,
+ 'r' => 10,
+ 'label' => 'center/big'
+ }
+
+ assert_equal hash, Psych.load(yaml)[4]
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_nil.rb b/jni/ruby/test/psych/test_nil.rb
new file mode 100644
index 0000000..3dbf562
--- /dev/null
+++ b/jni/ruby/test/psych/test_nil.rb
@@ -0,0 +1,18 @@
+require_relative 'helper'
+
+module Psych
+ class TestNil < TestCase
+ def test_nil
+ yml = Psych.dump nil
+ assert_match(/--- \n(?:\.\.\.\n)?/, yml)
+ assert_nil Psych.load(yml)
+ end
+
+ def test_array_nil
+ yml = Psych.dump [nil]
+ assert_equal "---\n- \n", yml
+ assert_equal [nil], Psych.load(yml)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/psych/test_null.rb b/jni/ruby/test/psych/test_null.rb
new file mode 100644
index 0000000..1725550
--- /dev/null
+++ b/jni/ruby/test/psych/test_null.rb
@@ -0,0 +1,19 @@
+require_relative 'helper'
+
+module Psych
+ ###
+ # Test null from YAML spec:
+ # http://yaml.org/type/null.html
+ class TestNull < TestCase
+ def test_null_list
+ assert_equal [nil] * 5, Psych.load(<<-eoyml)
+---
+- ~
+- null
+-
+- Null
+- NULL
+ eoyml
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_numeric.rb b/jni/ruby/test/psych/test_numeric.rb
new file mode 100644
index 0000000..5378b4a
--- /dev/null
+++ b/jni/ruby/test/psych/test_numeric.rb
@@ -0,0 +1,45 @@
+require_relative 'helper'
+require 'bigdecimal'
+
+module Psych
+ ###
+ # Test numerics from YAML spec:
+ # http://yaml.org/type/float.html
+ # http://yaml.org/type/int.html
+ class TestNumeric < TestCase
+ def setup
+ @old_debug = $DEBUG
+ $DEBUG = true
+ end
+
+ def teardown
+ $DEBUG = @old_debug
+ end
+
+ def test_load_float_with_dot
+ assert_equal 1.0, Psych.load('--- 1.')
+ end
+
+ def test_non_float_with_0
+ str = Psych.load('--- 090')
+ assert_equal '090', str
+ end
+
+ def test_big_decimal_tag
+ decimal = BigDecimal("12.34")
+ assert_match "!ruby/object:BigDecimal", Psych.dump(decimal)
+ end
+
+ def test_big_decimal_round_trip
+ decimal = BigDecimal("12.34")
+ assert_cycle decimal
+ end
+
+ def test_does_not_attempt_numeric
+ str = Psych.load('--- 4 roses')
+ assert_equal '4 roses', str
+ str = Psych.load('--- 1.1.1')
+ assert_equal '1.1.1', str
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_object.rb b/jni/ruby/test/psych/test_object.rb
new file mode 100644
index 0000000..5e3ce82
--- /dev/null
+++ b/jni/ruby/test/psych/test_object.rb
@@ -0,0 +1,44 @@
+require_relative 'helper'
+
+module Psych
+ class Tagged
+ yaml_tag '!foo'
+
+ attr_accessor :baz
+
+ def initialize
+ @baz = 'bar'
+ end
+ end
+
+ class Foo
+ attr_accessor :parent
+
+ def initialize parent
+ @parent = parent
+ end
+ end
+
+ class TestObject < TestCase
+ def test_dump_with_tag
+ tag = Tagged.new
+ assert_match('foo', Psych.dump(tag))
+ end
+
+ def test_tag_round_trip
+ tag = Tagged.new
+ tag2 = Psych.load(Psych.dump(tag))
+ assert_equal tag.baz, tag2.baz
+ assert_instance_of(Tagged, tag2)
+ end
+
+ def test_cyclic_references
+ foo = Foo.new(nil)
+ foo.parent = foo
+ loaded = Psych.load Psych.dump foo
+
+ assert_instance_of(Foo, loaded)
+ assert_equal loaded, loaded.parent
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_object_references.rb b/jni/ruby/test/psych/test_object_references.rb
new file mode 100644
index 0000000..273b466
--- /dev/null
+++ b/jni/ruby/test/psych/test_object_references.rb
@@ -0,0 +1,71 @@
+require_relative 'helper'
+
+module Psych
+ class TestObjectReferences < TestCase
+ def test_range_has_references
+ assert_reference_trip 1..2
+ end
+
+ def test_module_has_references
+ assert_reference_trip Psych
+ end
+
+ def test_class_has_references
+ assert_reference_trip TestObjectReferences
+ end
+
+ def test_rational_has_references
+ assert_reference_trip Rational('1.2')
+ end
+
+ def test_complex_has_references
+ assert_reference_trip Complex(1, 2)
+ end
+
+ def test_datetime_has_references
+ assert_reference_trip DateTime.now
+ end
+
+ def test_struct_has_references
+ assert_reference_trip Struct.new(:foo).new(1)
+ end
+
+ def assert_reference_trip obj
+ yml = Psych.dump([obj, obj])
+ assert_match(/\*-?\d+/, yml)
+ data = Psych.load yml
+ assert_equal data.first.object_id, data.last.object_id
+ end
+
+ def test_float_references
+ data = Psych.load <<-eoyml
+---\s
+- &name 1.2
+- *name
+ eoyml
+ assert_equal data.first, data.last
+ assert_equal data.first.object_id, data.last.object_id
+ end
+
+ def test_binary_references
+ data = Psych.load <<-eoyml
+---
+- &name !binary |-
+ aGVsbG8gd29ybGQh
+- *name
+ eoyml
+ assert_equal data.first, data.last
+ assert_equal data.first.object_id, data.last.object_id
+ end
+
+ def test_regexp_references
+ data = Psych.load <<-eoyml
+---\s
+- &name !ruby/regexp /pattern/i
+- *name
+ eoyml
+ assert_equal data.first, data.last
+ assert_equal data.first.object_id, data.last.object_id
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_omap.rb b/jni/ruby/test/psych/test_omap.rb
new file mode 100644
index 0000000..36edc26
--- /dev/null
+++ b/jni/ruby/test/psych/test_omap.rb
@@ -0,0 +1,75 @@
+require_relative 'helper'
+
+module Psych
+ class TestOmap < TestCase
+ def test_parse_as_map
+ o = Psych.load "--- !!omap\na: 1\nb: 2"
+ assert_kind_of Psych::Omap, o
+ assert_equal 1, o['a']
+ assert_equal 2, o['b']
+ end
+
+ def test_self_referential
+ map = Psych::Omap.new
+ map['foo'] = 'bar'
+ map['self'] = map
+ assert_equal(map, Psych.load(Psych.dump(map)))
+ end
+
+ def test_keys
+ map = Psych::Omap.new
+ map['foo'] = 'bar'
+ assert_equal 'bar', map['foo']
+ end
+
+ def test_order
+ map = Psych::Omap.new
+ map['a'] = 'b'
+ map['b'] = 'c'
+ assert_equal [%w{a b}, %w{b c}], map.to_a
+ end
+
+ def test_square
+ list = [["a", "b"], ["b", "c"]]
+ map = Psych::Omap[*list.flatten]
+ assert_equal list, map.to_a
+ assert_equal 'b', map['a']
+ assert_equal 'c', map['b']
+ end
+
+ def test_dump
+ map = Psych::Omap['a', 'b', 'c', 'd']
+ yaml = Psych.dump(map)
+ assert_match('!omap', yaml)
+ assert_match('- a: b', yaml)
+ assert_match('- c: d', yaml)
+ end
+
+ def test_round_trip
+ list = [["a", "b"], ["b", "c"]]
+ map = Psych::Omap[*list.flatten]
+ assert_cycle(map)
+ end
+
+ def test_load
+ list = [["a", "b"], ["c", "d"]]
+ map = Psych.load(<<-eoyml)
+--- !omap
+- a: b
+- c: d
+ eoyml
+ assert_equal list, map.to_a
+ end
+
+ # NOTE: This test will not work with Syck
+ def test_load_shorthand
+ list = [["a", "b"], ["c", "d"]]
+ map = Psych.load(<<-eoyml)
+--- !!omap
+- a: b
+- c: d
+ eoyml
+ assert_equal list, map.to_a
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_parser.rb b/jni/ruby/test/psych/test_parser.rb
new file mode 100644
index 0000000..0abe0dd
--- /dev/null
+++ b/jni/ruby/test/psych/test_parser.rb
@@ -0,0 +1,339 @@
+# coding: utf-8
+
+require_relative 'helper'
+
+module Psych
+ class TestParser < TestCase
+ class EventCatcher < Handler
+ attr_accessor :parser
+ attr_reader :calls, :marks
+ def initialize
+ @parser = nil
+ @calls = []
+ @marks = []
+ end
+
+ (Handler.instance_methods(true) -
+ Object.instance_methods).each do |m|
+ class_eval %{
+ def #{m} *args
+ super
+ @marks << @parser.mark if @parser
+ @calls << [:#{m}, args]
+ end
+ }
+ end
+ end
+
+ def setup
+ super
+ @handler = EventCatcher.new
+ @parser = Psych::Parser.new @handler
+ @handler.parser = @parser
+ end
+
+ def test_ast_roundtrip
+ parser = Psych.parser
+ parser.parse('null')
+ ast = parser.handler.root
+ assert_match(/^null/, ast.yaml)
+ end
+
+ def test_exception_memory_leak
+ yaml = <<-eoyaml
+%YAML 1.1
+%TAG ! tag:tenderlovemaking.com,2009:
+--- &ponies
+- first element
+- *ponies
+- foo: bar
+...
+ eoyaml
+
+ [:start_stream, :start_document, :end_document, :alias, :scalar,
+ :start_sequence, :end_sequence, :start_mapping, :end_mapping,
+ :end_stream].each do |method|
+
+ klass = Class.new(Psych::Handler) do
+ define_method(method) do |*args|
+ raise
+ end
+ end
+
+ parser = Psych::Parser.new klass.new
+ 2.times {
+ assert_raises(RuntimeError, method.to_s) do
+ parser.parse yaml
+ end
+ }
+ end
+ end
+
+ def test_multiparse
+ 3.times do
+ @parser.parse '--- foo'
+ end
+ end
+
+ def test_filename
+ ex = assert_raises(Psych::SyntaxError) do
+ @parser.parse '--- `', 'omg!'
+ end
+ assert_match 'omg!', ex.message
+ end
+
+ def test_line_numbers
+ assert_equal 0, @parser.mark.line
+ @parser.parse "---\n- hello\n- world"
+ line_calls = @handler.marks.map(&:line).zip(@handler.calls.map(&:first))
+ assert_equal [[0, :start_stream],
+ [0, :start_document],
+ [1, :start_sequence],
+ [2, :scalar],
+ [3, :scalar],
+ [3, :end_sequence],
+ [3, :end_document],
+ [3, :end_stream]], line_calls
+
+ assert_equal 3, @parser.mark.line
+ end
+
+ def test_column_numbers
+ assert_equal 0, @parser.mark.column
+ @parser.parse "---\n- hello\n- world"
+ col_calls = @handler.marks.map(&:column).zip(@handler.calls.map(&:first))
+ assert_equal [[0, :start_stream],
+ [3, :start_document],
+ [1, :start_sequence],
+ [0, :scalar],
+ [0, :scalar],
+ [0, :end_sequence],
+ [0, :end_document],
+ [0, :end_stream]], col_calls
+
+ assert_equal 0, @parser.mark.column
+ end
+
+ def test_index_numbers
+ assert_equal 0, @parser.mark.index
+ @parser.parse "---\n- hello\n- world"
+ idx_calls = @handler.marks.map(&:index).zip(@handler.calls.map(&:first))
+ assert_equal [[0, :start_stream],
+ [3, :start_document],
+ [5, :start_sequence],
+ [12, :scalar],
+ [19, :scalar],
+ [19, :end_sequence],
+ [19, :end_document],
+ [19, :end_stream]], idx_calls
+
+ assert_equal 19, @parser.mark.index
+ end
+
+ def test_bom
+ tadpole = 'おたまじゃくし'
+
+ # BOM + text
+ yml = "\uFEFF#{tadpole}".encode('UTF-16LE')
+ @parser.parse yml
+ assert_equal tadpole, @parser.handler.calls[2][1].first
+ end
+
+ def test_external_encoding
+ tadpole = 'おたまじゃくし'
+
+ @parser.external_encoding = Psych::Parser::UTF16LE
+ @parser.parse tadpole.encode 'UTF-16LE'
+ assert_equal tadpole, @parser.handler.calls[2][1].first
+ end
+
+ def test_bogus_io
+ o = Object.new
+ def o.external_encoding; nil end
+ def o.read len; self end
+
+ assert_raises(TypeError) do
+ @parser.parse o
+ end
+ end
+
+ def test_parse_io
+ @parser.parse StringIO.new("--- a")
+ assert_called :start_stream
+ assert_called :scalar
+ assert_called :end_stream
+ end
+
+ def test_syntax_error
+ assert_raises(Psych::SyntaxError) do
+ @parser.parse("---\n\"foo\"\n\"bar\"\n")
+ end
+ end
+
+ def test_syntax_error_twice
+ assert_raises(Psych::SyntaxError) do
+ @parser.parse("---\n\"foo\"\n\"bar\"\n")
+ end
+
+ assert_raises(Psych::SyntaxError) do
+ @parser.parse("---\n\"foo\"\n\"bar\"\n")
+ end
+ end
+
+ def test_syntax_error_has_path_for_string
+ e = assert_raises(Psych::SyntaxError) do
+ @parser.parse("---\n\"foo\"\n\"bar\"\n")
+ end
+ assert_match '(<unknown>):', e.message
+ end
+
+ def test_syntax_error_has_path_for_io
+ io = StringIO.new "---\n\"foo\"\n\"bar\"\n"
+ def io.path; "hello!"; end
+
+ e = assert_raises(Psych::SyntaxError) do
+ @parser.parse(io)
+ end
+ assert_match "(#{io.path}):", e.message
+ end
+
+ def test_mapping_end
+ @parser.parse("---\n!!map { key: value }")
+ assert_called :end_mapping
+ end
+
+ def test_mapping_tag
+ @parser.parse("---\n!!map { key: value }")
+ assert_called :start_mapping, ["tag:yaml.org,2002:map", false, Nodes::Mapping::FLOW]
+ end
+
+ def test_mapping_anchor
+ @parser.parse("---\n&A { key: value }")
+ assert_called :start_mapping, ['A', true, Nodes::Mapping::FLOW]
+ end
+
+ def test_mapping_block
+ @parser.parse("---\n key: value")
+ assert_called :start_mapping, [true, Nodes::Mapping::BLOCK]
+ end
+
+ def test_mapping_start
+ @parser.parse("---\n{ key: value }")
+ assert_called :start_mapping
+ assert_called :start_mapping, [true, Nodes::Mapping::FLOW]
+ end
+
+ def test_sequence_end
+ @parser.parse("---\n&A [1, 2]")
+ assert_called :end_sequence
+ end
+
+ def test_sequence_start_anchor
+ @parser.parse("---\n&A [1, 2]")
+ assert_called :start_sequence, ["A", true, Nodes::Sequence::FLOW]
+ end
+
+ def test_sequence_start_tag
+ @parser.parse("---\n!!seq [1, 2]")
+ assert_called :start_sequence, ["tag:yaml.org,2002:seq", false, Nodes::Sequence::FLOW]
+ end
+
+ def test_sequence_start_flow
+ @parser.parse("---\n[1, 2]")
+ assert_called :start_sequence, [true, Nodes::Sequence::FLOW]
+ end
+
+ def test_sequence_start_block
+ @parser.parse("---\n - 1\n - 2")
+ assert_called :start_sequence, [true, Nodes::Sequence::BLOCK]
+ end
+
+ def test_literal_scalar
+ @parser.parse(<<-eoyml)
+%YAML 1.1
+---
+"literal\n\
+ \ttext\n"
+ eoyml
+ assert_called :scalar, ['literal text ', false, true, Nodes::Scalar::DOUBLE_QUOTED]
+ end
+
+ def test_scalar
+ @parser.parse("--- foo\n")
+ assert_called :scalar, ['foo', true, false, Nodes::Scalar::PLAIN]
+ end
+
+ def test_scalar_with_tag
+ @parser.parse("---\n!!str foo\n")
+ assert_called :scalar, ['foo', 'tag:yaml.org,2002:str', false, false, Nodes::Scalar::PLAIN]
+ end
+
+ def test_scalar_with_anchor
+ @parser.parse("---\n&A foo\n")
+ assert_called :scalar, ['foo', 'A', true, false, Nodes::Scalar::PLAIN]
+ end
+
+ def test_scalar_plain_implicit
+ @parser.parse("---\n&A foo\n")
+ assert_called :scalar, ['foo', 'A', true, false, Nodes::Scalar::PLAIN]
+ end
+
+ def test_alias
+ @parser.parse(<<-eoyml)
+%YAML 1.1
+---
+!!seq [
+ !!str "Without properties",
+ &A !!str "Anchored",
+ !!str "Tagged",
+ *A,
+ !!str "",
+]
+ eoyml
+ assert_called :alias, ['A']
+ end
+
+ def test_end_stream
+ @parser.parse("--- foo\n")
+ assert_called :end_stream
+ end
+
+ def test_start_stream
+ @parser.parse("--- foo\n")
+ assert_called :start_stream
+ end
+
+ def test_end_document_implicit
+ @parser.parse("\"foo\"\n")
+ assert_called :end_document, [true]
+ end
+
+ def test_end_document_explicit
+ @parser.parse("\"foo\"\n...")
+ assert_called :end_document, [false]
+ end
+
+ def test_start_document_version
+ @parser.parse("%YAML 1.1\n---\n\"foo\"\n")
+ assert_called :start_document, [[1,1], [], false]
+ end
+
+ def test_start_document_tag
+ @parser.parse("%TAG !yaml! tag:yaml.org,2002\n---\n!yaml!str \"foo\"\n")
+ assert_called :start_document, [[], [['!yaml!', 'tag:yaml.org,2002']], false]
+ end
+
+ def assert_called call, with = nil, parser = @parser
+ if with
+ call = parser.handler.calls.find { |x|
+ x.first == call && x.last.compact == with
+ }
+ assert(call,
+ "#{[call,with].inspect} not in #{parser.handler.calls.inspect}"
+ )
+ else
+ assert parser.handler.calls.any? { |x| x.first == call }
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_psych.rb b/jni/ruby/test/psych/test_psych.rb
new file mode 100644
index 0000000..8054bd6
--- /dev/null
+++ b/jni/ruby/test/psych/test_psych.rb
@@ -0,0 +1,168 @@
+require_relative 'helper'
+
+require 'stringio'
+require 'tempfile'
+
+class TestPsych < Psych::TestCase
+ def teardown
+ Psych.domain_types.clear
+ end
+
+ def test_line_width
+ yml = Psych.dump('123456 7', { :line_width => 5 })
+ assert_match(/^\s*7/, yml)
+ end
+
+ def test_indent
+ yml = Psych.dump({:a => {'b' => 'c'}}, {:indentation => 5})
+ assert_match(/^[ ]{5}b/, yml)
+ end
+
+ def test_canonical
+ yml = Psych.dump({:a => {'b' => 'c'}}, {:canonical => true})
+ assert_match(/\? "b/, yml)
+ end
+
+ def test_header
+ yml = Psych.dump({:a => {'b' => 'c'}}, {:header => true})
+ assert_match(/YAML/, yml)
+ end
+
+ def test_version_array
+ yml = Psych.dump({:a => {'b' => 'c'}}, {:version => [1,1]})
+ assert_match(/1.1/, yml)
+ end
+
+ def test_version_string
+ yml = Psych.dump({:a => {'b' => 'c'}}, {:version => '1.1'})
+ assert_match(/1.1/, yml)
+ end
+
+ def test_version_bool
+ yml = Psych.dump({:a => {'b' => 'c'}}, {:version => true})
+ assert_match(/1.1/, yml)
+ end
+
+ def test_load_argument_error
+ assert_raises(TypeError) do
+ Psych.load nil
+ end
+ end
+
+ def test_non_existing_class_on_deserialize
+ e = assert_raises(ArgumentError) do
+ Psych.load("--- !ruby/object:NonExistent\nfoo: 1")
+ end
+ assert_equal 'undefined class/module NonExistent', e.message
+ end
+
+ def test_dump_stream
+ things = [22, "foo \n", {}]
+ stream = Psych.dump_stream(*things)
+ assert_equal things, Psych.load_stream(stream)
+ end
+
+ def test_dump_file
+ hash = {'hello' => 'TGIF!'}
+ Tempfile.create('fun.yml') do |io|
+ assert_equal io, Psych.dump(hash, io)
+ io.rewind
+ assert_equal Psych.dump(hash), io.read
+ end
+ end
+
+ def test_dump_io
+ hash = {'hello' => 'TGIF!'}
+ stringio = StringIO.new ''
+ assert_equal stringio, Psych.dump(hash, stringio)
+ assert_equal Psych.dump(hash), stringio.string
+ end
+
+ def test_simple
+ assert_equal 'foo', Psych.load("--- foo\n")
+ end
+
+ def test_libyaml_version
+ assert Psych.libyaml_version
+ assert_equal Psych.libyaml_version.join('.'), Psych::LIBYAML_VERSION
+ end
+
+ def test_load_documents
+ docs = Psych.load_documents("--- foo\n...\n--- bar\n...")
+ assert_equal %w{ foo bar }, docs
+ end
+
+ def test_parse_stream
+ docs = Psych.parse_stream("--- foo\n...\n--- bar\n...")
+ assert_equal %w{ foo bar }, docs.children.map { |x| x.transform }
+ end
+
+ def test_add_builtin_type
+ got = nil
+ Psych.add_builtin_type 'omap' do |type, val|
+ got = val
+ end
+ Psych.load('--- !!omap hello')
+ assert_equal 'hello', got
+ ensure
+ Psych.remove_type 'omap'
+ end
+
+ def test_domain_types
+ got = nil
+ Psych.add_domain_type 'foo.bar,2002', 'foo' do |type, val|
+ got = val
+ end
+
+ Psych.load('--- !foo.bar,2002/foo hello')
+ assert_equal 'hello', got
+
+ Psych.load("--- !foo.bar,2002/foo\n- hello\n- world")
+ assert_equal %w{ hello world }, got
+
+ Psych.load("--- !foo.bar,2002/foo\nhello: world")
+ assert_equal({ 'hello' => 'world' }, got)
+ end
+
+ def test_load_file
+ Tempfile.create(['yikes', 'yml']) {|t|
+ t.binmode
+ t.write('--- hello world')
+ t.close
+ assert_equal 'hello world', Psych.load_file(t.path)
+ }
+ end
+
+ def test_parse_file
+ Tempfile.create(['yikes', 'yml']) {|t|
+ t.binmode
+ t.write('--- hello world')
+ t.close
+ assert_equal 'hello world', Psych.parse_file(t.path).transform
+ }
+ end
+
+ def test_degenerate_strings
+ assert_equal false, Psych.load(' ')
+ assert_equal false, Psych.parse(' ')
+ assert_equal false, Psych.load('')
+ assert_equal false, Psych.parse('')
+ end
+
+ def test_callbacks
+ types = []
+ appender = lambda { |*args| types << args }
+
+ Psych.add_builtin_type('foo', &appender)
+ Psych.add_domain_type('example.com,2002', 'foo', &appender)
+ Psych.load <<-eoyml
+- !tag:yaml.org,2002:foo bar
+- !tag:example.com,2002:foo bar
+ eoyml
+
+ assert_equal [
+ ["tag:yaml.org,2002:foo", "bar"],
+ ["tag:example.com,2002:foo", "bar"]
+ ], types
+ end
+end
diff --git a/jni/ruby/test/psych/test_safe_load.rb b/jni/ruby/test/psych/test_safe_load.rb
new file mode 100644
index 0000000..dd299c0
--- /dev/null
+++ b/jni/ruby/test/psych/test_safe_load.rb
@@ -0,0 +1,97 @@
+require 'psych/helper'
+
+module Psych
+ class TestSafeLoad < TestCase
+ class Foo; end
+
+ [1, 2.2, {}, [], "foo"].each do |obj|
+ define_method(:"test_basic_#{obj.class}") do
+ assert_safe_cycle obj
+ end
+ end
+
+ def test_no_recursion
+ x = []
+ x << x
+ assert_raises(Psych::BadAlias) do
+ Psych.safe_load Psych.dump(x)
+ end
+ end
+
+ def test_explicit_recursion
+ x = []
+ x << x
+ assert_equal(x, Psych.safe_load(Psych.dump(x), [], [], true))
+ end
+
+ def test_symbol_whitelist
+ yml = Psych.dump :foo
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load yml
+ end
+ assert_equal(:foo, Psych.safe_load(yml, [Symbol], [:foo]))
+ end
+
+ def test_symbol
+ assert_raises(Psych::DisallowedClass) do
+ assert_safe_cycle :foo
+ end
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load '--- !ruby/symbol foo', []
+ end
+ assert_safe_cycle :foo, [Symbol]
+ assert_safe_cycle :foo, %w{ Symbol }
+ assert_equal :foo, Psych.safe_load('--- !ruby/symbol foo', [Symbol])
+ end
+
+ def test_foo
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load '--- !ruby/object:Foo {}', [Foo]
+ end
+ assert_raises(Psych::DisallowedClass) do
+ assert_safe_cycle Foo.new
+ end
+ assert_kind_of(Foo, Psych.safe_load(Psych.dump(Foo.new), [Foo]))
+ end
+
+ X = Struct.new(:x)
+ def test_struct_depends_on_sym
+ assert_safe_cycle(X.new, [X, Symbol])
+ assert_raises(Psych::DisallowedClass) do
+ cycle X.new, [X]
+ end
+ end
+
+ def test_anon_struct
+ assert Psych.safe_load(<<-eoyml, [Struct, Symbol])
+--- !ruby/struct
+ foo: bar
+ eoyml
+
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load(<<-eoyml, [Struct])
+--- !ruby/struct
+ foo: bar
+ eoyml
+ end
+
+ assert_raises(Psych::DisallowedClass) do
+ Psych.safe_load(<<-eoyml, [Symbol])
+--- !ruby/struct
+ foo: bar
+ eoyml
+ end
+ end
+
+ private
+
+ def cycle object, whitelist = []
+ Psych.safe_load(Psych.dump(object), whitelist)
+ end
+
+ def assert_safe_cycle object, whitelist = []
+ other = cycle object, whitelist
+ assert_equal object, other
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_scalar.rb b/jni/ruby/test/psych/test_scalar.rb
new file mode 100644
index 0000000..e6b7697
--- /dev/null
+++ b/jni/ruby/test/psych/test_scalar.rb
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+
+require_relative 'helper'
+
+module Psych
+ class TestScalar < TestCase
+ def test_utf_8
+ assert_equal "日本語", Psych.load("--- 日本語")
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_scalar_scanner.rb b/jni/ruby/test/psych/test_scalar_scanner.rb
new file mode 100644
index 0000000..e8e423c
--- /dev/null
+++ b/jni/ruby/test/psych/test_scalar_scanner.rb
@@ -0,0 +1,106 @@
+require_relative 'helper'
+require 'date'
+
+module Psych
+ class TestScalarScanner < TestCase
+ attr_reader :ss
+
+ def setup
+ super
+ @ss = Psych::ScalarScanner.new ClassLoader.new
+ end
+
+ def test_scan_time
+ { '2001-12-15T02:59:43.1Z' => Time.utc(2001, 12, 15, 02, 59, 43, 100000),
+ '2001-12-14t21:59:43.10-05:00' => Time.utc(2001, 12, 15, 02, 59, 43, 100000),
+ '2001-12-14 21:59:43.10 -5' => Time.utc(2001, 12, 15, 02, 59, 43, 100000),
+ '2001-12-15 2:59:43.10' => Time.utc(2001, 12, 15, 02, 59, 43, 100000),
+ '2011-02-24 11:17:06 -0800' => Time.utc(2011, 02, 24, 19, 17, 06)
+ }.each do |time_str, time|
+ assert_equal time, @ss.tokenize(time_str)
+ end
+ end
+
+ def test_scan_bad_time
+ [ '2001-12-15T02:59:73.1Z',
+ '2001-12-14t90:59:43.10-05:00',
+ '2001-92-14 21:59:43.10 -5',
+ '2001-12-15 92:59:43.10',
+ '2011-02-24 81:17:06 -0800',
+ ].each do |time_str|
+ assert_equal time_str, @ss.tokenize(time_str)
+ end
+ end
+
+ def test_scan_bad_dates
+ x = '2000-15-01'
+ assert_equal x, @ss.tokenize(x)
+
+ x = '2000-10-51'
+ assert_equal x, @ss.tokenize(x)
+
+ x = '2000-10-32'
+ assert_equal x, @ss.tokenize(x)
+ end
+
+ def test_scan_good_edge_date
+ x = '2000-1-31'
+ assert_equal Date.strptime(x, '%Y-%m-%d'), @ss.tokenize(x)
+ end
+
+ def test_scan_bad_edge_date
+ x = '2000-11-31'
+ assert_equal x, @ss.tokenize(x)
+ end
+
+ def test_scan_date
+ date = '1980-12-16'
+ token = @ss.tokenize date
+ assert_equal 1980, token.year
+ assert_equal 12, token.month
+ assert_equal 16, token.day
+ end
+
+ def test_scan_inf
+ assert_equal(1 / 0.0, ss.tokenize('.inf'))
+ end
+
+ def test_scan_minus_inf
+ assert_equal(-1 / 0.0, ss.tokenize('-.inf'))
+ end
+
+ def test_scan_nan
+ assert ss.tokenize('.nan').nan?
+ end
+
+ def test_scan_null
+ assert_equal nil, ss.tokenize('null')
+ assert_equal nil, ss.tokenize('~')
+ assert_equal nil, ss.tokenize('')
+ end
+
+ def test_scan_symbol
+ assert_equal :foo, ss.tokenize(':foo')
+ end
+
+ def test_scan_sexagesimal_float
+ assert_equal 685230.15, ss.tokenize('190:20:30.15')
+ end
+
+ def test_scan_sexagesimal_int
+ assert_equal 685230, ss.tokenize('190:20:30')
+ end
+
+ def test_scan_float
+ assert_equal 1.2, ss.tokenize('1.2')
+ end
+
+ def test_scan_true
+ assert_equal true, ss.tokenize('true')
+ end
+
+ def test_scan_strings_starting_with_underscores
+ assert_equal "_100", ss.tokenize('_100')
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_serialize_subclasses.rb b/jni/ruby/test/psych/test_serialize_subclasses.rb
new file mode 100644
index 0000000..f597b7a
--- /dev/null
+++ b/jni/ruby/test/psych/test_serialize_subclasses.rb
@@ -0,0 +1,38 @@
+require_relative 'helper'
+
+module Psych
+ class TestSerializeSubclasses < TestCase
+ class SomeObject
+ def initialize one, two
+ @one = one
+ @two = two
+ end
+
+ def == other
+ @one == other.instance_eval { @one } &&
+ @two == other.instance_eval { @two }
+ end
+ end
+
+ def test_some_object
+ so = SomeObject.new('foo', [1,2,3])
+ assert_equal so, Psych.load(Psych.dump(so))
+ end
+
+ class StructSubclass < Struct.new(:foo)
+ def initialize foo, bar
+ super(foo)
+ @bar = bar
+ end
+
+ def == other
+ super(other) && @bar == other.instance_eval{ @bar }
+ end
+ end
+
+ def test_struct_subclass
+ so = StructSubclass.new('foo', [1,2,3])
+ assert_equal so, Psych.load(Psych.dump(so))
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_set.rb b/jni/ruby/test/psych/test_set.rb
new file mode 100644
index 0000000..921fe22
--- /dev/null
+++ b/jni/ruby/test/psych/test_set.rb
@@ -0,0 +1,49 @@
+require_relative 'helper'
+
+module Psych
+ class TestSet < TestCase
+ def setup
+ super
+ @set = Psych::Set.new
+ @set['foo'] = 'bar'
+ @set['bar'] = 'baz'
+ end
+
+ def test_dump
+ assert_match(/!set/, Psych.dump(@set))
+ end
+
+ def test_roundtrip
+ assert_cycle(@set)
+ end
+
+ ###
+ # FIXME: Syck should also support !!set as shorthand
+ def test_load_from_yaml
+ loaded = Psych.load(<<-eoyml)
+--- !set
+foo: bar
+bar: baz
+ eoyml
+ assert_equal(@set, loaded)
+ end
+
+ def test_loaded_class
+ assert_instance_of(Psych::Set, Psych.load(Psych.dump(@set)))
+ end
+
+ def test_set_shorthand
+ loaded = Psych.load(<<-eoyml)
+--- !!set
+foo: bar
+bar: baz
+ eoyml
+ assert_instance_of(Psych::Set, loaded)
+ end
+
+ def test_set_self_reference
+ @set['self'] = @set
+ assert_cycle(@set)
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_stream.rb b/jni/ruby/test/psych/test_stream.rb
new file mode 100644
index 0000000..7e41178
--- /dev/null
+++ b/jni/ruby/test/psych/test_stream.rb
@@ -0,0 +1,93 @@
+require_relative 'helper'
+
+module Psych
+ class TestStream < TestCase
+ def test_parse_partial
+ rb = Psych.parse("--- foo\n...\n--- `").to_ruby
+ assert_equal 'foo', rb
+ end
+
+ def test_load_partial
+ rb = Psych.load("--- foo\n...\n--- `")
+ assert_equal 'foo', rb
+ end
+
+ def test_parse_stream_yields_documents
+ list = []
+ Psych.parse_stream("--- foo\n...\n--- bar") do |doc|
+ list << doc.to_ruby
+ end
+ assert_equal %w{ foo bar }, list
+ end
+
+ def test_parse_stream_break
+ list = []
+ Psych.parse_stream("--- foo\n...\n--- `") do |doc|
+ list << doc.to_ruby
+ break
+ end
+ assert_equal %w{ foo }, list
+ end
+
+ def test_load_stream_yields_documents
+ list = []
+ Psych.load_stream("--- foo\n...\n--- bar") do |ruby|
+ list << ruby
+ end
+ assert_equal %w{ foo bar }, list
+ end
+
+ def test_load_stream_break
+ list = []
+ Psych.load_stream("--- foo\n...\n--- `") do |ruby|
+ list << ruby
+ break
+ end
+ assert_equal %w{ foo }, list
+ end
+
+ def test_explicit_documents
+ io = StringIO.new
+ stream = Psych::Stream.new(io)
+ stream.start
+ stream.push({ 'foo' => 'bar' })
+
+ assert !stream.finished?, 'stream not finished'
+ stream.finish
+ assert stream.finished?, 'stream finished'
+
+ assert_match(/^---/, io.string)
+ assert_match(/\.\.\.$/, io.string)
+ end
+
+ def test_start_takes_block
+ io = StringIO.new
+ stream = Psych::Stream.new(io)
+ stream.start do |emitter|
+ emitter.push({ 'foo' => 'bar' })
+ end
+
+ assert stream.finished?, 'stream finished'
+ assert_match(/^---/, io.string)
+ assert_match(/\.\.\.$/, io.string)
+ end
+
+ def test_no_backreferences
+ io = StringIO.new
+ stream = Psych::Stream.new(io)
+ stream.start do |emitter|
+ x = { 'foo' => 'bar' }
+ emitter.push x
+ emitter.push x
+ end
+
+ assert stream.finished?, 'stream finished'
+ assert_match(/^---/, io.string)
+ assert_match(/\.\.\.$/, io.string)
+ assert_equal 2, io.string.scan('---').length
+ assert_equal 2, io.string.scan('...').length
+ assert_equal 2, io.string.scan('foo').length
+ assert_equal 2, io.string.scan('bar').length
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_string.rb b/jni/ruby/test/psych/test_string.rb
new file mode 100644
index 0000000..1ce16fd
--- /dev/null
+++ b/jni/ruby/test/psych/test_string.rb
@@ -0,0 +1,166 @@
+require_relative 'helper'
+
+module Psych
+ class TestString < TestCase
+ class X < String
+ end
+
+ class Y < String
+ attr_accessor :val
+ end
+
+ class Z < String
+ def initialize
+ force_encoding Encoding::US_ASCII
+ end
+ end
+
+ def test_string_with_newline
+ assert_equal "1\n2", Psych.load("--- ! '1\n\n 2'\n")
+ end
+
+ def test_no_doublequotes_with_special_characters
+ assert_equal 2, Psych.dump(%Q{<%= ENV["PATH"] %>}).count('"')
+ end
+
+ def test_doublequotes_when_there_is_a_single
+ yaml = Psych.dump "@123'abc"
+ assert_match(/---\s*"/, yaml)
+ end
+
+ def test_cycle_x
+ str = X.new 'abc'
+ assert_cycle str
+ end
+
+ def test_dash_dot
+ assert_cycle '-.'
+ assert_cycle '+.'
+ end
+
+ def test_string_subclass_with_anchor
+ y = Psych.load <<-eoyml
+---
+body:
+ string: &70121654388580 !ruby/string
+ str: ! 'foo'
+ x:
+ body: *70121654388580
+ eoyml
+ assert_equal({"body"=>{"string"=>"foo", "x"=>{"body"=>"foo"}}}, y)
+ end
+
+ def test_self_referential_string
+ y = Psych.load <<-eoyml
+---
+string: &70121654388580 !ruby/string
+ str: ! 'foo'
+ body: *70121654388580
+ eoyml
+
+ assert_equal({"string"=>"foo"}, y)
+ value = y['string']
+ assert_equal value, value.instance_variable_get(:@body)
+ end
+
+ def test_another_subclass_with_attributes
+ y = Psych.load Psych.dump Y.new("foo").tap {|y| y.val = 1}
+ assert_equal "foo", y
+ assert_equal Y, y.class
+ assert_equal 1, y.val
+ end
+
+ def test_backwards_with_syck
+ x = Psych.load "--- !str:#{X.name} foo\n\n"
+ assert_equal X, x.class
+ assert_equal 'foo', x
+ end
+
+ def test_empty_subclass
+ assert_match "!ruby/string:#{X}", Psych.dump(X.new)
+ x = Psych.load Psych.dump X.new
+ assert_equal X, x.class
+ end
+
+ def test_empty_character_subclass
+ assert_match "!ruby/string:#{Z}", Psych.dump(Z.new)
+ x = Psych.load Psych.dump Z.new
+ assert_equal Z, x.class
+ end
+
+ def test_subclass_with_attributes
+ y = Psych.load Psych.dump Y.new.tap {|y| y.val = 1}
+ assert_equal Y, y.class
+ assert_equal 1, y.val
+ end
+
+ def test_string_with_base_60
+ yaml = Psych.dump '01:03:05'
+ assert_match "'01:03:05'", yaml
+ assert_equal '01:03:05', Psych.load(yaml)
+ end
+
+ def test_nonascii_string_as_binary
+ string = "hello \x80 world!"
+ string.force_encoding 'ascii-8bit'
+ yml = Psych.dump string
+ assert_match(/binary/, yml)
+ assert_equal string, Psych.load(yml)
+ end
+
+ def test_binary_string_null
+ string = "\x00"
+ yml = Psych.dump string
+ assert_match(/binary/, yml)
+ assert_equal string, Psych.load(yml)
+ end
+
+ def test_binary_string
+ string = binary_string
+ yml = Psych.dump string
+ assert_match(/binary/, yml)
+ assert_equal string, Psych.load(yml)
+ end
+
+ def test_non_binary_string
+ string = binary_string(0.29)
+ yml = Psych.dump string
+ refute_match(/binary/, yml)
+ assert_equal string, Psych.load(yml)
+ end
+
+ def test_ascii_only_8bit_string
+ string = "abc".encode(Encoding::ASCII_8BIT)
+ yml = Psych.dump string
+ refute_match(/binary/, yml)
+ assert_equal string, Psych.load(yml)
+ end
+
+ def test_string_with_ivars
+ food = "is delicious"
+ ivar = "on rock and roll"
+ food.instance_variable_set(:@we_built_this_city, ivar)
+
+ Psych.load Psych.dump food
+ assert_equal ivar, food.instance_variable_get(:@we_built_this_city)
+ end
+
+ def test_binary
+ string = [0, 123,22, 44, 9, 32, 34, 39].pack('C*')
+ assert_cycle string
+ end
+
+ def test_float_confusion
+ assert_cycle '1.'
+ end
+
+ def binary_string percentage = 0.31, length = 100
+ string = ''
+ (percentage * length).to_i.times do |i|
+ string << "\b"
+ end
+ string << 'a' * (length - string.length)
+ string
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_struct.rb b/jni/ruby/test/psych/test_struct.rb
new file mode 100644
index 0000000..8c7f251
--- /dev/null
+++ b/jni/ruby/test/psych/test_struct.rb
@@ -0,0 +1,49 @@
+require_relative 'helper'
+
+class PsychStructWithIvar < Struct.new(:foo)
+ attr_reader :bar
+ def initialize *args
+ super
+ @bar = 'hello'
+ end
+end
+
+module Psych
+ class TestStruct < TestCase
+ class StructSubclass < Struct.new(:foo)
+ def initialize foo, bar
+ super(foo)
+ @bar = bar
+ end
+ end
+
+ def test_self_referential_struct
+ ss = StructSubclass.new(nil, 'foo')
+ ss.foo = ss
+
+ loaded = Psych.load(Psych.dump(ss))
+ assert_instance_of(StructSubclass, loaded.foo)
+
+ assert_equal(ss, loaded)
+ end
+
+ def test_roundtrip
+ thing = PsychStructWithIvar.new('bar')
+ struct = Psych.load(Psych.dump(thing))
+
+ assert_equal 'hello', struct.bar
+ assert_equal 'bar', struct.foo
+ end
+
+ def test_load
+ obj = Psych.load(<<-eoyml)
+--- !ruby/struct:PsychStructWithIvar
+:foo: bar
+:@bar: hello
+ eoyml
+
+ assert_equal 'hello', obj.bar
+ assert_equal 'bar', obj.foo
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_symbol.rb b/jni/ruby/test/psych/test_symbol.rb
new file mode 100644
index 0000000..558a672
--- /dev/null
+++ b/jni/ruby/test/psych/test_symbol.rb
@@ -0,0 +1,25 @@
+require_relative 'helper'
+
+module Psych
+ class TestSymbol < TestCase
+ def test_cycle_empty
+ assert_cycle :''
+ end
+
+ def test_cycle_colon
+ assert_cycle :':'
+ end
+
+ def test_cycle
+ assert_cycle :a
+ end
+
+ def test_stringy
+ assert_cycle :"1"
+ end
+
+ def test_load_quoted
+ assert_equal :"1", Psych.load("--- :'1'\n")
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_tainted.rb b/jni/ruby/test/psych/test_tainted.rb
new file mode 100644
index 0000000..37fc5b2
--- /dev/null
+++ b/jni/ruby/test/psych/test_tainted.rb
@@ -0,0 +1,130 @@
+require_relative 'helper'
+
+module Psych
+ class TestStringTainted < TestCase
+ class Tainted < Handler
+ attr_reader :tc
+
+ def initialize tc
+ @tc = tc
+ end
+
+ def start_document version, tags, implicit
+ tags.flatten.each do |tag|
+ assert_taintedness tag
+ end
+ end
+
+ def alias name
+ assert_taintedness name
+ end
+
+ def scalar value, anchor, tag, plain, quoted, style
+ assert_taintedness value
+ assert_taintedness tag if tag
+ assert_taintedness anchor if anchor
+ end
+
+ def start_sequence anchor, tag, implicit, style
+ assert_taintedness tag if tag
+ assert_taintedness anchor if anchor
+ end
+
+ def start_mapping anchor, tag, implicit, style
+ assert_taintedness tag if tag
+ assert_taintedness anchor if anchor
+ end
+
+ def assert_taintedness thing, message = "'#{thing}' should be tainted"
+ tc.assert thing.tainted?, message
+ end
+ end
+
+ class Untainted < Tainted
+ def assert_taintedness thing, message = "'#{thing}' should not be tainted"
+ tc.assert !thing.tainted?, message
+ end
+ end
+
+
+ def setup
+ handler = Tainted.new self
+ @parser = Psych::Parser.new handler
+ end
+
+ def test_tags_are_tainted
+ assert_taintedness "%TAG !yaml! tag:yaml.org,2002:\n---\n!yaml!str \"foo\""
+ end
+
+ def test_alias
+ assert_taintedness "--- &ponies\n- foo\n- *ponies"
+ end
+
+ def test_scalar
+ assert_taintedness "--- ponies"
+ end
+
+ def test_anchor
+ assert_taintedness "--- &hi ponies"
+ end
+
+ def test_scalar_tag
+ assert_taintedness "--- !str ponies"
+ end
+
+ def test_seq_start_tag
+ assert_taintedness "--- !!seq [ a ]"
+ end
+
+ def test_seq_start_anchor
+ assert_taintedness "--- &zomg [ a ]"
+ end
+
+ def test_seq_mapping_tag
+ assert_taintedness "--- !!map { a: b }"
+ end
+
+ def test_seq_mapping_anchor
+ assert_taintedness "--- &himom { a: b }"
+ end
+
+ def assert_taintedness string
+ @parser.parse string.taint
+ end
+ end
+
+ class TestStringUntainted < TestStringTainted
+ def setup
+ handler = Untainted.new self
+ @parser = Psych::Parser.new handler
+ end
+
+ def assert_taintedness string
+ @parser.parse string
+ end
+ end
+
+ class TestStringIOUntainted < TestStringTainted
+ def setup
+ handler = Untainted.new self
+ @parser = Psych::Parser.new handler
+ end
+
+ def assert_taintedness string
+ @parser.parse StringIO.new(string)
+ end
+ end
+
+ class TestIOTainted < TestStringTainted
+ def assert_taintedness string
+ Tempfile.create(['something', 'yml']) {|t|
+ t.binmode
+ t.write string
+ t.close
+ File.open(t.path, 'r:bom|utf-8') { |f|
+ @parser.parse f
+ }
+ }
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_to_yaml_properties.rb b/jni/ruby/test/psych/test_to_yaml_properties.rb
new file mode 100644
index 0000000..5b4860c
--- /dev/null
+++ b/jni/ruby/test/psych/test_to_yaml_properties.rb
@@ -0,0 +1,63 @@
+require_relative 'helper'
+
+module Psych
+ class TestToYamlProperties < MiniTest::Unit::TestCase
+ class Foo
+ attr_accessor :a, :b, :c
+ def initialize
+ @a = 1
+ @b = 2
+ @c = 3
+ end
+
+ def to_yaml_properties
+ [:@a, :@b]
+ end
+ end
+
+ def test_object_dump_yaml_properties
+ foo = Psych.load(Psych.dump(Foo.new))
+ assert_equal 1, foo.a
+ assert_equal 2, foo.b
+ assert_nil foo.c
+ end
+
+ class Bar < Struct.new(:foo, :bar)
+ attr_reader :baz
+ def initialize *args
+ super
+ @baz = 'hello'
+ end
+
+ def to_yaml_properties
+ []
+ end
+ end
+
+ def test_struct_dump_yaml_properties
+ bar = Psych.load(Psych.dump(Bar.new('a', 'b')))
+ assert_equal 'a', bar.foo
+ assert_equal 'b', bar.bar
+ assert_nil bar.baz
+ end
+
+ def test_string_dump
+ string = "okonomiyaki"
+ class << string
+ def to_yaml_properties
+ [:@tastes]
+ end
+ end
+
+ string.instance_variable_set(:@tastes, 'delicious')
+ v = Psych.load Psych.dump string
+ assert_equal 'delicious', v.instance_variable_get(:@tastes)
+ end
+
+ def test_string_load_syck
+ str = Psych.load("--- !str \nstr: okonomiyaki\n:@tastes: delicious\n")
+ assert_equal 'okonomiyaki', str
+ assert_equal 'delicious', str.instance_variable_get(:@tastes)
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_tree_builder.rb b/jni/ruby/test/psych/test_tree_builder.rb
new file mode 100644
index 0000000..7ad3ddd
--- /dev/null
+++ b/jni/ruby/test/psych/test_tree_builder.rb
@@ -0,0 +1,79 @@
+require_relative 'helper'
+
+module Psych
+ class TestTreeBuilder < TestCase
+ def setup
+ super
+ @parser = Psych::Parser.new TreeBuilder.new
+ @parser.parse(<<-eoyml)
+%YAML 1.1
+---
+- foo
+- {
+ bar : &A !!str baz,
+ boo : *A
+}
+- *A
+ eoyml
+ @tree = @parser.handler.root
+ end
+
+ def test_stream
+ assert_instance_of Nodes::Stream, @tree
+ end
+
+ def test_documents
+ assert_equal 1, @tree.children.length
+ assert_instance_of Nodes::Document, @tree.children.first
+ doc = @tree.children.first
+
+ assert_equal [1,1], doc.version
+ assert_equal [], doc.tag_directives
+ assert_equal false, doc.implicit
+ end
+
+ def test_sequence
+ doc = @tree.children.first
+ assert_equal 1, doc.children.length
+
+ seq = doc.children.first
+ assert_instance_of Nodes::Sequence, seq
+ assert_nil seq.anchor
+ assert_nil seq.tag
+ assert_equal true, seq.implicit
+ assert_equal Nodes::Sequence::BLOCK, seq.style
+ end
+
+ def test_scalar
+ doc = @tree.children.first
+ seq = doc.children.first
+
+ assert_equal 3, seq.children.length
+ scalar = seq.children.first
+ assert_instance_of Nodes::Scalar, scalar
+ assert_equal 'foo', scalar.value
+ assert_nil scalar.anchor
+ assert_nil scalar.tag
+ assert_equal true, scalar.plain
+ assert_equal false, scalar.quoted
+ assert_equal Nodes::Scalar::PLAIN, scalar.style
+ end
+
+ def test_mapping
+ doc = @tree.children.first
+ seq = doc.children.first
+ map = seq.children[1]
+
+ assert_instance_of Nodes::Mapping, map
+ end
+
+ def test_alias
+ doc = @tree.children.first
+ seq = doc.children.first
+ assert_equal 3, seq.children.length
+ al = seq.children[2]
+ assert_instance_of Nodes::Alias, al
+ assert_equal 'A', al.anchor
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/test_yaml.rb b/jni/ruby/test/psych/test_yaml.rb
new file mode 100644
index 0000000..cd3e8ee
--- /dev/null
+++ b/jni/ruby/test/psych/test_yaml.rb
@@ -0,0 +1,1288 @@
+# -*- coding: us-ascii; mode: ruby; ruby-indent-level: 4; tab-width: 4 -*-
+# vim:sw=4:ts=4
+# $Id$
+#
+require_relative 'helper'
+require 'ostruct'
+
+# [ruby-core:01946]
+module Psych_Tests
+ StructTest = Struct::new( :c )
+end
+
+class Psych_Unit_Tests < Psych::TestCase
+ def teardown
+ Psych.domain_types.clear
+ end
+
+ def test_y_method
+ assert_raises(NoMethodError) do
+ OpenStruct.new.y 1
+ end
+ end
+
+ def test_syck_compat
+ time = Time.utc(2010, 10, 10)
+ yaml = Psych.dump time
+ assert_match "2010-10-10 00:00:00.000000000 Z", yaml
+ end
+
+ # [ruby-core:34969]
+ def test_regexp_with_n
+ assert_cycle(Regexp.new('',0,'n'))
+ end
+ #
+ # Tests modified from 00basic.t in Psych.pm
+ #
+ def test_basic_map
+ # Simple map
+ assert_parse_only(
+ { 'one' => 'foo', 'three' => 'baz', 'two' => 'bar' }, <<EOY
+one: foo
+two: bar
+three: baz
+EOY
+ )
+ end
+
+ def test_basic_strings
+ # Common string types
+ assert_cycle("x")
+ assert_cycle(":x")
+ assert_cycle(":")
+ assert_parse_only(
+ { 1 => 'simple string', 2 => 42, 3 => '1 Single Quoted String',
+ 4 => 'Psych\'s Double "Quoted" String', 5 => "A block\n with several\n lines.\n",
+ 6 => "A \"chomped\" block", 7 => "A folded\n string\n", 8 => ": started string" },
+ <<EOY
+1: simple string
+2: 42
+3: '1 Single Quoted String'
+4: "Psych's Double \\\"Quoted\\\" String"
+5: |
+ A block
+ with several
+ lines.
+6: |-
+ A "chomped" block
+7: >
+ A
+ folded
+ string
+8: ": started string"
+EOY
+ )
+ end
+
+ #
+ # Test the specification examples
+ # - Many examples have been changes because of whitespace problems that
+ # caused the two to be inequivalent, or keys to be sorted wrong
+ #
+
+ def test_spec_simple_implicit_sequence
+ # Simple implicit sequence
+ assert_to_yaml(
+ [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ], <<EOY
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
+EOY
+ )
+ end
+
+ def test_spec_simple_implicit_map
+ # Simple implicit map
+ assert_to_yaml(
+ { 'hr' => 65, 'avg' => 0.278, 'rbi' => 147 }, <<EOY
+avg: 0.278
+hr: 65
+rbi: 147
+EOY
+ )
+ end
+
+ def test_spec_simple_map_with_nested_sequences
+ # Simple mapping with nested sequences
+ assert_to_yaml(
+ { 'american' =>
+ [ 'Boston Red Sox', 'Detroit Tigers', 'New York Yankees' ],
+ 'national' =>
+ [ 'New York Mets', 'Chicago Cubs', 'Atlanta Braves' ] }, <<EOY
+american:
+ - Boston Red Sox
+ - Detroit Tigers
+ - New York Yankees
+national:
+ - New York Mets
+ - Chicago Cubs
+ - Atlanta Braves
+EOY
+ )
+ end
+
+ def test_spec_simple_sequence_with_nested_map
+ # Simple sequence with nested map
+ assert_to_yaml(
+ [
+ {'name' => 'Mark McGwire', 'hr' => 65, 'avg' => 0.278},
+ {'name' => 'Sammy Sosa', 'hr' => 63, 'avg' => 0.288}
+ ], <<EOY
+-
+ avg: 0.278
+ hr: 65
+ name: Mark McGwire
+-
+ avg: 0.288
+ hr: 63
+ name: Sammy Sosa
+EOY
+ )
+ end
+
+ def test_spec_sequence_of_sequences
+ # Simple sequence with inline sequences
+ assert_parse_only(
+ [
+ [ 'name', 'hr', 'avg' ],
+ [ 'Mark McGwire', 65, 0.278 ],
+ [ 'Sammy Sosa', 63, 0.288 ]
+ ], <<EOY
+- [ name , hr , avg ]
+- [ Mark McGwire , 65 , 0.278 ]
+- [ Sammy Sosa , 63 , 0.288 ]
+EOY
+ )
+ end
+
+ def test_spec_mapping_of_mappings
+ # Simple map with inline maps
+ assert_parse_only(
+ { 'Mark McGwire' =>
+ { 'hr' => 65, 'avg' => 0.278 },
+ 'Sammy Sosa' =>
+ { 'hr' => 63, 'avg' => 0.288 }
+ }, <<EOY
+Mark McGwire: {hr: 65, avg: 0.278}
+Sammy Sosa: {hr: 63,
+ avg: 0.288}
+EOY
+ )
+ end
+
+ def test_ambiguous_comments
+ # [ruby-talk:88012]
+ assert_to_yaml( "Call the method #dave", <<EOY )
+--- "Call the method #dave"
+EOY
+ end
+
+ def test_spec_nested_comments
+ # Map and sequences with comments
+ assert_parse_only(
+ { 'hr' => [ 'Mark McGwire', 'Sammy Sosa' ],
+ 'rbi' => [ 'Sammy Sosa', 'Ken Griffey' ] }, <<EOY
+hr: # 1998 hr ranking
+ - Mark McGwire
+ - Sammy Sosa
+rbi:
+ # 1998 rbi ranking
+ - Sammy Sosa
+ - Ken Griffey
+EOY
+ )
+ end
+
+ def test_spec_anchors_and_aliases
+ # Anchors and aliases
+ assert_parse_only(
+ { 'hr' =>
+ [ 'Mark McGwire', 'Sammy Sosa' ],
+ 'rbi' =>
+ [ 'Sammy Sosa', 'Ken Griffey' ] }, <<EOY
+hr:
+ - Mark McGwire
+ # Name "Sammy Sosa" scalar SS
+ - &SS Sammy Sosa
+rbi:
+ # So it can be referenced later.
+ - *SS
+ - Ken Griffey
+EOY
+ )
+
+ assert_to_yaml(
+ [{"arrival"=>"EDI", "departure"=>"LAX", "fareref"=>"DOGMA", "currency"=>"GBP"}, {"arrival"=>"MEL", "departure"=>"SYD", "fareref"=>"MADF", "currency"=>"AUD"}, {"arrival"=>"MCO", "departure"=>"JFK", "fareref"=>"DFSF", "currency"=>"USD"}], <<EOY
+ -
+ &F fareref: DOGMA
+ &C currency: GBP
+ &D departure: LAX
+ &A arrival: EDI
+ - { *F: MADF, *C: AUD, *D: SYD, *A: MEL }
+ - { *F: DFSF, *C: USD, *D: JFK, *A: MCO }
+EOY
+ )
+
+ assert_to_yaml(
+ {"ALIASES"=>["fareref", "currency", "departure", "arrival"], "FARES"=>[{"arrival"=>"EDI", "departure"=>"LAX", "fareref"=>"DOGMA", "currency"=>"GBP"}, {"arrival"=>"MEL", "departure"=>"SYD", "fareref"=>"MADF", "currency"=>"AUD"}, {"arrival"=>"MCO", "departure"=>"JFK", "fareref"=>"DFSF", "currency"=>"USD"}]}, <<EOY
+---
+ALIASES: [&f fareref, &c currency, &d departure, &a arrival]
+FARES:
+- *f: DOGMA
+ *c: GBP
+ *d: LAX
+ *a: EDI
+
+- *f: MADF
+ *c: AUD
+ *d: SYD
+ *a: MEL
+
+- *f: DFSF
+ *c: USD
+ *d: JFK
+ *a: MCO
+
+EOY
+ )
+
+ end
+
+ def test_spec_mapping_between_sequences
+ # Complex key #1
+ assert_parse_only(
+ { [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ],
+ [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ] }, <<EOY
+? # PLAY SCHEDULE
+ - Detroit Tigers
+ - Chicago Cubs
+:
+ - 2001-07-23
+
+? [ New York Yankees,
+ Atlanta Braves ]
+: [ 2001-07-02, 2001-08-12,
+ 2001-08-14 ]
+EOY
+ )
+
+ # Complex key #2
+ assert_parse_only(
+ { [ 'New York Yankees', 'Atlanta Braves' ] =>
+ [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ),
+ Date.new( 2001, 8, 14 ) ],
+ [ 'Detroit Tigers', 'Chicago Cubs' ] =>
+ [ Date.new( 2001, 7, 23 ) ]
+ }, <<EOY
+?
+ - New York Yankees
+ - Atlanta Braves
+:
+ - 2001-07-02
+ - 2001-08-12
+ - 2001-08-14
+?
+ - Detroit Tigers
+ - Chicago Cubs
+:
+ - 2001-07-23
+EOY
+ )
+ end
+
+ def test_spec_sequence_key_shortcut
+ # Shortcut sequence map
+ assert_parse_only(
+ { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ),
+ 'bill-to' => 'Chris Dumars', 'product' =>
+ [ { 'item' => 'Super Hoop', 'quantity' => 1 },
+ { 'item' => 'Basketball', 'quantity' => 4 },
+ { 'item' => 'Big Shoes', 'quantity' => 1 } ] }, <<EOY
+invoice: 34843
+date : 2001-01-23
+bill-to: Chris Dumars
+product:
+ - item : Super Hoop
+ quantity: 1
+ - item : Basketball
+ quantity: 4
+ - item : Big Shoes
+ quantity: 1
+EOY
+ )
+ end
+
+ def test_spec_sequence_in_sequence_shortcut
+ # Seq-in-seq
+ assert_parse_only( [ [ [ 'one', 'two', 'three' ] ] ], <<EOY )
+- - - one
+ - two
+ - three
+EOY
+ end
+
+ def test_spec_sequence_shortcuts
+ # Sequence shortcuts combined
+ assert_parse_only(
+[
+ [
+ [ [ 'one' ] ],
+ [ 'two', 'three' ],
+ { 'four' => nil },
+ [ { 'five' => [ 'six' ] } ],
+ [ 'seven' ]
+ ],
+ [ 'eight', 'nine' ]
+], <<EOY )
+- - - - one
+ - - two
+ - three
+ - four:
+ - - five:
+ - six
+ - - seven
+- - eight
+ - nine
+EOY
+ end
+
+ def test_spec_single_literal
+ # Literal scalar block
+ assert_parse_only( [ "\\/|\\/|\n/ | |_\n" ], <<EOY )
+- |
+ \\/|\\/|
+ / | |_
+EOY
+ end
+
+ def test_spec_single_folded
+ # Folded scalar block
+ assert_parse_only(
+ [ "Mark McGwire's year was crippled by a knee injury.\n" ], <<EOY
+- >
+ Mark McGwire\'s
+ year was crippled
+ by a knee injury.
+EOY
+ )
+ end
+
+ def test_spec_preserve_indent
+ # Preserve indented spaces
+ assert_parse_only(
+ "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n", <<EOY
+--- >
+ Sammy Sosa completed another
+ fine season with great stats.
+
+ 63 Home Runs
+ 0.288 Batting Average
+
+ What a year!
+EOY
+ )
+ end
+
+ def test_spec_indentation_determines_scope
+ assert_parse_only(
+ { 'name' => 'Mark McGwire', 'accomplishment' => "Mark set a major league home run record in 1998.\n",
+ 'stats' => "65 Home Runs\n0.278 Batting Average\n" }, <<EOY
+name: Mark McGwire
+accomplishment: >
+ Mark set a major league
+ home run record in 1998.
+stats: |
+ 65 Home Runs
+ 0.278 Batting Average
+EOY
+ )
+ end
+
+ def test_spec_multiline_scalars
+ # Multiline flow scalars
+ assert_parse_only(
+ { 'plain' => 'This unquoted scalar spans many lines.',
+ 'quoted' => "So does this quoted scalar.\n" }, <<EOY
+plain: This unquoted
+ scalar spans
+ many lines.
+quoted: "\\
+ So does this quoted
+ scalar.\\n"
+EOY
+ )
+ end
+
+ def test_spec_type_int
+ assert_parse_only(
+ { 'canonical' => 12345, 'decimal' => 12345, 'octal' => '014'.oct, 'hexadecimal' => '0xC'.hex }, <<EOY
+canonical: 12345
+decimal: +12,345
+octal: 014
+hexadecimal: 0xC
+EOY
+ )
+ assert_parse_only(
+ { 'canonical' => 685230, 'decimal' => 685230, 'octal' => 02472256, 'hexadecimal' => 0x0A74AE, 'sexagesimal' => 685230 }, <<EOY)
+canonical: 685230
+decimal: +685,230
+octal: 02472256
+hexadecimal: 0x0A,74,AE
+sexagesimal: 190:20:30
+EOY
+ end
+
+ def test_spec_type_float
+ assert_parse_only(
+ { 'canonical' => 1230.15, 'exponential' => 1230.15, 'fixed' => 1230.15,
+ 'negative infinity' => -1.0/0.0 }, <<EOY)
+canonical: 1.23015e+3
+exponential: 12.3015e+02
+fixed: 1,230.15
+negative infinity: -.inf
+EOY
+ nan = Psych::load( <<EOY )
+not a number: .NaN
+EOY
+ assert( nan['not a number'].nan? )
+ end
+
+ def test_spec_type_misc
+ assert_parse_only(
+ { nil => nil, true => true, false => false, 'string' => '12345' }, <<EOY
+null: ~
+true: yes
+false: no
+string: '12345'
+EOY
+ )
+ end
+
+ def test_spec_complex_invoice
+ # Complex invoice type
+ id001 = { 'given' => 'Chris', 'family' => 'Dumars', 'address' =>
+ { 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak',
+ 'state' => 'MI', 'postal' => 48046 } }
+ assert_parse_only(
+ { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ),
+ 'bill-to' => id001, 'ship-to' => id001, 'product' =>
+ [ { 'sku' => 'BL394D', 'quantity' => 4,
+ 'description' => 'Basketball', 'price' => 450.00 },
+ { 'sku' => 'BL4438H', 'quantity' => 1,
+ 'description' => 'Super Hoop', 'price' => 2392.00 } ],
+ 'tax' => 251.42, 'total' => 4443.52,
+ 'comments' => "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.\n" }, <<EOY
+invoice: 34843
+date : 2001-01-23
+bill-to: &id001
+ given : Chris
+ family : !str Dumars
+ address:
+ lines: |
+ 458 Walkman Dr.
+ Suite #292
+ city : Royal Oak
+ state : MI
+ postal : 48046
+ship-to: *id001
+product:
+ - !map
+ sku : BL394D
+ quantity : 4
+ description : Basketball
+ price : 450.00
+ - sku : BL4438H
+ quantity : 1
+ description : Super Hoop
+ price : 2392.00
+tax : 251.42
+total: 4443.52
+comments: >
+ Late afternoon is best.
+ Backup contact is Nancy
+ Billsmer @ 338-4338.
+EOY
+ )
+ end
+
+ def test_spec_log_file
+ doc_ct = 0
+ Psych::load_documents( <<EOY
+---
+Time: 2001-11-23 15:01:42 -05:00
+User: ed
+Warning: >
+ This is an error message
+ for the log file
+---
+Time: 2001-11-23 15:02:31 -05:00
+User: ed
+Warning: >
+ A slightly different error
+ message.
+---
+Date: 2001-11-23 15:03:17 -05:00
+User: ed
+Fatal: >
+ Unknown variable "bar"
+Stack:
+ - file: TopClass.py
+ line: 23
+ code: |
+ x = MoreObject("345\\n")
+ - file: MoreClass.py
+ line: 58
+ code: |-
+ foo = bar
+EOY
+ ) { |doc|
+ case doc_ct
+ when 0
+ assert_equal( doc, { 'Time' => mktime( 2001, 11, 23, 15, 01, 42, 00, "-05:00" ),
+ 'User' => 'ed', 'Warning' => "This is an error message for the log file\n" } )
+ when 1
+ assert_equal( doc, { 'Time' => mktime( 2001, 11, 23, 15, 02, 31, 00, "-05:00" ),
+ 'User' => 'ed', 'Warning' => "A slightly different error message.\n" } )
+ when 2
+ assert_equal( doc, { 'Date' => mktime( 2001, 11, 23, 15, 03, 17, 00, "-05:00" ),
+ 'User' => 'ed', 'Fatal' => "Unknown variable \"bar\"\n",
+ 'Stack' => [
+ { 'file' => 'TopClass.py', 'line' => 23, 'code' => "x = MoreObject(\"345\\n\")\n" },
+ { 'file' => 'MoreClass.py', 'line' => 58, 'code' => "foo = bar" } ] } )
+ end
+ doc_ct += 1
+ }
+ assert_equal( doc_ct, 3 )
+ end
+
+ def test_spec_root_fold
+ y = Psych::load( <<EOY
+---
+This Psych stream contains a single text value.
+The next stream is a log file - a sequence of
+log entries. Adding an entry to the log is a
+simple matter of appending it at the end.
+EOY
+ )
+ assert_equal( y, "This Psych stream contains a single text value. The next stream is a log file - a sequence of log entries. Adding an entry to the log is a simple matter of appending it at the end." )
+ end
+
+ def test_spec_root_mapping
+ y = Psych::load( <<EOY
+# This stream is an example of a top-level mapping.
+invoice : 34843
+date : 2001-01-23
+total : 4443.52
+EOY
+ )
+ assert_equal( y, { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ), 'total' => 4443.52 } )
+ end
+
+ def test_spec_oneline_docs
+ doc_ct = 0
+ Psych::load_documents( <<EOY
+# The following is a sequence of three documents.
+# The first contains an empty mapping, the second
+# an empty sequence, and the last an empty string.
+--- {}
+--- [ ]
+--- ''
+EOY
+ ) { |doc|
+ case doc_ct
+ when 0
+ assert_equal( doc, {} )
+ when 1
+ assert_equal( doc, [] )
+ when 2
+ assert_equal( doc, '' )
+ end
+ doc_ct += 1
+ }
+ assert_equal( doc_ct, 3 )
+ end
+
+ def test_spec_domain_prefix
+ customer_proc = proc { |type, val|
+ if Hash === val
+ _, _, type = type.split( ':', 3 )
+ val['type'] = "domain #{type}"
+ val
+ else
+ raise ArgumentError, "Not a Hash in domain.tld,2002/invoice: " + val.inspect
+ end
+ }
+ Psych.add_domain_type( "domain.tld,2002", 'invoice', &customer_proc )
+ Psych.add_domain_type( "domain.tld,2002", 'customer', &customer_proc )
+ assert_parse_only( { "invoice"=> { "customers"=> [ { "given"=>"Chris", "type"=>"domain customer", "family"=>"Dumars" } ], "type"=>"domain invoice" } }, <<EOY
+# 'http://domain.tld,2002/invoice' is some type family.
+invoice: !domain.tld,2002/invoice
+ # 'seq' is shorthand for 'http://yaml.org/seq'.
+ # This does not effect '^customer' below
+ # because it is does not specify a prefix.
+ customers: !seq
+ # '^customer' is shorthand for the full
+ # notation 'http://domain.tld,2002/customer'.
+ - !customer
+ given : Chris
+ family : Dumars
+EOY
+ )
+ end
+
+ def test_spec_throwaway
+ assert_parse_only(
+ {"this"=>"contains three lines of text.\nThe third one starts with a\n# character. This isn't a comment.\n"}, <<EOY
+### These are four throwaway comment ###
+
+### lines (the second line is empty). ###
+this: | # Comments may trail lines.
+ contains three lines of text.
+ The third one starts with a
+ # character. This isn't a comment.
+
+# These are three throwaway comment
+# lines (the first line is empty).
+EOY
+ )
+ end
+
+ def test_spec_force_implicit
+ # Force implicit
+ assert_parse_only(
+ { 'integer' => 12, 'also int' => 12, 'string' => '12' }, <<EOY
+integer: 12
+also int: ! "12"
+string: !str 12
+EOY
+ )
+ end
+
+ ###
+ # Commenting out this test. This line:
+ #
+ # - !domain.tld,2002/type\\x30 value
+ #
+ # Is invalid according to the YAML spec:
+ #
+ # http://yaml.org/spec/1.1/#id896876
+ #
+# def test_spec_url_escaping
+# Psych.add_domain_type( "domain.tld,2002", "type0" ) { |type, val|
+# "ONE: #{val}"
+# }
+# Psych.add_domain_type( "domain.tld,2002", "type%30" ) { |type, val|
+# "TWO: #{val}"
+# }
+# assert_parse_only(
+# { 'same' => [ 'ONE: value', 'ONE: value' ], 'different' => [ 'TWO: value' ] }, <<EOY
+#same:
+# - !domain.tld,2002/type\\x30 value
+# - !domain.tld,2002/type0 value
+#different: # As far as the Psych parser is concerned
+# - !domain.tld,2002/type%30 value
+#EOY
+# )
+# end
+
+ def test_spec_override_anchor
+ # Override anchor
+ a001 = "The alias node below is a repeated use of this value.\n"
+ assert_parse_only(
+ { 'anchor' => 'This scalar has an anchor.', 'override' => a001, 'alias' => a001 }, <<EOY
+anchor : &A001 This scalar has an anchor.
+override : &A001 >
+ The alias node below is a
+ repeated use of this value.
+alias : *A001
+EOY
+ )
+ end
+
+ def test_spec_explicit_families
+ Psych.add_domain_type( "somewhere.com,2002", 'type' ) { |type, val|
+ "SOMEWHERE: #{val}"
+ }
+ assert_parse_only(
+ { 'not-date' => '2002-04-28', 'picture' => "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236i^\020' \202\n\001\000;", 'hmm' => "SOMEWHERE: family above is short for\nhttp://somewhere.com/type\n" }, <<EOY
+not-date: !str 2002-04-28
+picture: !binary |
+ R0lGODlhDAAMAIQAAP//9/X
+ 17unp5WZmZgAAAOfn515eXv
+ Pz7Y6OjuDg4J+fn5OTk6enp
+ 56enmleECcgggoBADs=
+
+hmm: !somewhere.com,2002/type |
+ family above is short for
+ http://somewhere.com/type
+EOY
+ )
+ end
+
+ def test_spec_application_family
+ # Testing the clarkevans.com graphs
+ Psych.add_domain_type( "clarkevans.com,2002", 'graph/shape' ) { |type, val|
+ if Array === val
+ val << "Shape Container"
+ val
+ else
+ raise ArgumentError, "Invalid graph of type #{val.class}: " + val.inspect
+ end
+ }
+ one_shape_proc = Proc.new { |type, val|
+ if Hash === val
+ type = type.split( /:/ )
+ val['TYPE'] = "Shape: #{type[2]}"
+ val
+ else
+ raise ArgumentError, "Invalid graph of type #{val.class}: " + val.inspect
+ end
+ }
+ Psych.add_domain_type( "clarkevans.com,2002", 'graph/circle', &one_shape_proc )
+ Psych.add_domain_type( "clarkevans.com,2002", 'graph/line', &one_shape_proc )
+ Psych.add_domain_type( "clarkevans.com,2002", 'graph/text', &one_shape_proc )
+ # MODIFIED to remove invalid Psych
+ assert_parse_only(
+ [[{"radius"=>7, "center"=>{"x"=>73, "y"=>129}, "TYPE"=>"Shape: graph/circle"}, {"finish"=>{"x"=>89, "y"=>102}, "TYPE"=>"Shape: graph/line", "start"=>{"x"=>73, "y"=>129}}, {"TYPE"=>"Shape: graph/text", "value"=>"Pretty vector drawing.", "start"=>{"x"=>73, "y"=>129}, "color"=>16772795}, "Shape Container"]], <<EOY
+- !clarkevans.com,2002/graph/shape
+ - !/graph/circle
+ center: &ORIGIN {x: 73, y: 129}
+ radius: 7
+ - !/graph/line # !clarkevans.com,2002/graph/line
+ start: *ORIGIN
+ finish: { x: 89, y: 102 }
+ - !/graph/text
+ start: *ORIGIN
+ color: 0xFFEEBB
+ value: Pretty vector drawing.
+EOY
+ )
+ end
+
+ def test_spec_float_explicit
+ assert_parse_only(
+ [ 10.0, 10.0, 10.0, 10.0 ], <<EOY
+# All entries in the sequence
+# have the same type and value.
+- 10.0
+- !float 10
+- !yaml.org,2002/float '10'
+- !yaml.org,2002/float "\\
+ 1\\
+ 0"
+EOY
+ )
+ end
+
+ def test_spec_builtin_seq
+ # Assortment of sequences
+ assert_parse_only(
+ { 'empty' => [], 'in-line' => [ 'one', 'two', 'three', 'four', 'five' ],
+ 'nested' => [ 'First item in top sequence', [ 'Subordinate sequence entry' ],
+ "A multi-line sequence entry\n", 'Sixth item in top sequence' ] }, <<EOY
+empty: []
+in-line: [ one, two, three # May span lines,
+ , four, # indentation is
+ five ] # mostly ignored.
+nested:
+ - First item in top sequence
+ -
+ - Subordinate sequence entry
+ - >
+ A multi-line
+ sequence entry
+ - Sixth item in top sequence
+EOY
+ )
+ end
+
+ def test_spec_builtin_map
+ # Assortment of mappings
+ assert_parse_only(
+ { 'empty' => {}, 'in-line' => { 'one' => 1, 'two' => 2 },
+ 'spanning' => { 'one' => 1, 'two' => 2 },
+ 'nested' => { 'first' => 'First entry', 'second' =>
+ { 'key' => 'Subordinate mapping' }, 'third' =>
+ [ 'Subordinate sequence', {}, 'Previous mapping is empty.',
+ { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' },
+ 'The previous entry is equal to the following one.',
+ { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' } ],
+ 12.0 => 'This key is a float.', "?\n" => 'This key had to be protected.',
+ "\a" => 'This key had to be escaped.',
+ "This is a multi-line folded key\n" => "Whose value is also multi-line.\n",
+ [ 'This key', 'is a sequence' ] => [ 'With a sequence value.' ] } }, <<EOY
+
+empty: {}
+in-line: { one: 1, two: 2 }
+spanning: { one: 1,
+ two: 2 }
+nested:
+ first : First entry
+ second:
+ key: Subordinate mapping
+ third:
+ - Subordinate sequence
+ - { }
+ - Previous mapping is empty.
+ - A key: value pair in a sequence.
+ A second: key:value pair.
+ - The previous entry is equal to the following one.
+ -
+ A key: value pair in a sequence.
+ A second: key:value pair.
+ !float 12 : This key is a float.
+ ? >
+ ?
+ : This key had to be protected.
+ "\\a" : This key had to be escaped.
+ ? >
+ This is a
+ multi-line
+ folded key
+ : >
+ Whose value is
+ also multi-line.
+ ?
+ - This key
+ - is a sequence
+ :
+ - With a sequence value.
+# The following parses correctly,
+# but Ruby 1.6.* fails the comparison!
+# ?
+# This: key
+# is a: mapping
+# :
+# with a: mapping value.
+EOY
+ )
+ end
+
+ def test_spec_builtin_literal_blocks
+ # Assortment of literal scalar blocks
+ assert_parse_only(
+ {"both are equal to"=>" This has no newline.", "is equal to"=>"The \\ ' \" characters may be\nfreely used. Leading white\n space is significant.\n\nLine breaks are significant.\nThus this value contains one\nempty line and ends with a\nsingle line break, but does\nnot start with one.\n", "also written as"=>" This has no newline.", "indented and chomped"=>" This has no newline.", "empty"=>"", "literal"=>"The \\ ' \" characters may be\nfreely used. Leading white\n space is significant.\n\nLine breaks are significant.\nThus this value contains one\nempty line and ends with a\nsingle line break, but does\nnot start with one.\n"}, <<EOY
+empty: |
+
+literal: |
+ The \\\ ' " characters may be
+ freely used. Leading white
+ space is significant.
+
+ Line breaks are significant.
+ Thus this value contains one
+ empty line and ends with a
+ single line break, but does
+ not start with one.
+
+is equal to: "The \\\\ ' \\" characters may \\
+ be\\nfreely used. Leading white\\n space \\
+ is significant.\\n\\nLine breaks are \\
+ significant.\\nThus this value contains \\
+ one\\nempty line and ends with a\\nsingle \\
+ line break, but does\\nnot start with one.\\n"
+
+# Comments may follow a nested
+# scalar value. They must be
+# less indented.
+
+# Modifiers may be combined in any order.
+indented and chomped: |2-
+ This has no newline.
+
+also written as: |-2
+ This has no newline.
+
+both are equal to: " This has no newline."
+EOY
+ )
+
+ str1 = "This has one newline.\n"
+ str2 = "This has no newline."
+ str3 = "This has two newlines.\n\n"
+ assert_parse_only(
+ { 'clipped' => str1, 'same as "clipped" above' => str1,
+ 'stripped' => str2, 'same as "stripped" above' => str2,
+ 'kept' => str3, 'same as "kept" above' => str3 }, <<EOY
+clipped: |
+ This has one newline.
+
+same as "clipped" above: "This has one newline.\\n"
+
+stripped: |-
+ This has no newline.
+
+same as "stripped" above: "This has no newline."
+
+kept: |+
+ This has two newlines.
+
+same as "kept" above: "This has two newlines.\\n\\n"
+
+EOY
+ )
+ end
+
+ def test_spec_span_single_quote
+ assert_parse_only( {"third"=>"a single quote ' must be escaped.", "second"=>"! : \\ etc. can be used freely.", "is same as"=>"this contains six spaces\nand one line break", "empty"=>"", "span"=>"this contains six spaces\nand one line break"}, <<EOY
+empty: ''
+second: '! : \\ etc. can be used freely.'
+third: 'a single quote '' must be escaped.'
+span: 'this contains
+ six spaces
+
+ and one
+ line break'
+is same as: "this contains six spaces\\nand one line break"
+EOY
+ )
+ end
+
+ def test_spec_span_double_quote
+ assert_parse_only( {"is equal to"=>"this contains four spaces", "third"=>"a \" or a \\ must be escaped.", "second"=>"! : etc. can be used freely.", "empty"=>"", "fourth"=>"this value ends with an LF.\n", "span"=>"this contains four spaces"}, <<EOY
+empty: ""
+second: "! : etc. can be used freely."
+third: "a \\\" or a \\\\ must be escaped."
+fourth: "this value ends with an LF.\\n"
+span: "this contains
+ four \\
+ spaces"
+is equal to: "this contains four spaces"
+EOY
+ )
+ end
+
+ def test_spec_builtin_time
+ # Time
+ assert_parse_only(
+ { "space separated" => mktime( 2001, 12, 14, 21, 59, 43, ".10", "-05:00" ),
+ "canonical" => mktime( 2001, 12, 15, 2, 59, 43, ".10" ),
+ "date (noon UTC)" => Date.new( 2002, 12, 14),
+ "valid iso8601" => mktime( 2001, 12, 14, 21, 59, 43, ".10", "-05:00" ) }, <<EOY
+canonical: 2001-12-15T02:59:43.1Z
+valid iso8601: 2001-12-14t21:59:43.10-05:00
+space separated: 2001-12-14 21:59:43.10 -05:00
+date (noon UTC): 2002-12-14
+EOY
+ )
+ end
+
+ def test_spec_builtin_binary
+ arrow_gif = "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236iiiccc\243\243\243\204\204\204\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371!\376\016Made with GIMP\000,\000\000\000\000\f\000\f\000\000\005, \216\2010\236\343@\024\350i\020\304\321\212\010\034\317\200M$z\357\3770\205p\270\2601f\r\e\316\001\303\001\036\020' \202\n\001\000;"
+ assert_parse_only(
+ { 'canonical' => arrow_gif, 'base64' => arrow_gif,
+ 'description' => "The binary value above is a tiny arrow encoded as a gif image.\n" }, <<EOY
+canonical: !binary "\\
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOf\\
+ n515eXvPz7Y6OjuDg4J+fn5OTk6enp56enmlpaW\\
+ NjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++\\
+ f/++f/++f/++f/++f/++f/++f/++SH+Dk1hZGUg\\
+ d2l0aCBHSU1QACwAAAAADAAMAAAFLCAgjoEwnuN\\
+ AFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84Bww\\
+ EeECcgggoBADs="
+base64: !binary |
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOf
+ n515eXvPz7Y6OjuDg4J+fn5OTk6enp56enmlpaW
+ NjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++
+ f/++f/++f/++f/++f/++f/++f/++SH+Dk1hZGUg
+ d2l0aCBHSU1QACwAAAAADAAMAAAFLCAgjoEwnuN
+ AFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84Bww
+ EeECcgggoBADs=
+description: >
+ The binary value above is a tiny arrow
+ encoded as a gif image.
+EOY
+ )
+ end
+ def test_ruby_regexp
+ # Test Ruby regular expressions
+ assert_to_yaml(
+ { 'simple' => /a.b/, 'complex' => %r'\A"((?:[^"]|\")+)"',
+ 'case-insensitive' => /George McFly/i }, <<EOY
+case-insensitive: !ruby/regexp "/George McFly/i"
+complex: !ruby/regexp "/\\\\A\\"((?:[^\\"]|\\\\\\")+)\\"/"
+simple: !ruby/regexp "/a.b/"
+EOY
+ )
+ end
+
+ #
+ # Test of Ranges
+ #
+ def test_ranges
+
+ # Simple numeric
+ assert_to_yaml( 1..3, <<EOY )
+--- !ruby/range 1..3
+EOY
+
+ # Simple alphabetic
+ assert_to_yaml( 'a'..'z', <<EOY )
+--- !ruby/range a..z
+EOY
+
+ # Float
+ assert_to_yaml( 10.5...30.3, <<EOY )
+--- !ruby/range 10.5...30.3
+EOY
+
+ end
+
+ def test_ruby_struct
+ # Ruby structures
+ book_struct = Struct::new( "MyBookStruct", :author, :title, :year, :isbn )
+ assert_to_yaml(
+ [ book_struct.new( "Yukihiro Matsumoto", "Ruby in a Nutshell", 2002, "0-596-00214-9" ),
+ book_struct.new( [ 'Dave Thomas', 'Andy Hunt' ], "The Pickaxe", 2002,
+ book_struct.new( "This should be the ISBN", "but I have another struct here", 2002, "None" )
+ ) ], <<EOY
+- !ruby/struct:MyBookStruct
+ author: Yukihiro Matsumoto
+ title: Ruby in a Nutshell
+ year: 2002
+ isbn: 0-596-00214-9
+- !ruby/struct:MyBookStruct
+ author:
+ - Dave Thomas
+ - Andy Hunt
+ title: The Pickaxe
+ year: 2002
+ isbn: !ruby/struct:MyBookStruct
+ author: This should be the ISBN
+ title: but I have another struct here
+ year: 2002
+ isbn: None
+EOY
+ )
+
+ assert_to_yaml( Psych_Tests::StructTest.new( 123 ), <<EOY )
+--- !ruby/struct:Psych_Tests::StructTest
+c: 123
+EOY
+
+ end
+
+ def test_ruby_rational
+ assert_to_yaml( Rational(1, 2), <<EOY )
+--- !ruby/object:Rational
+numerator: 1
+denominator: 2
+EOY
+
+ # Read Psych dumped by the ruby 1.8.3.
+ assert_to_yaml( Rational(1, 2), "!ruby/object:Rational 1/2\n" )
+ assert_raises( ArgumentError ) { Psych.load("!ruby/object:Rational INVALID/RATIONAL\n") }
+ end
+
+ def test_ruby_complex
+ assert_to_yaml( Complex(3, 4), <<EOY )
+--- !ruby/object:Complex
+image: 4
+real: 3
+EOY
+
+ # Read Psych dumped by the ruby 1.8.3.
+ assert_to_yaml( Complex(3, 4), "!ruby/object:Complex 3+4i\n" )
+ assert_raises( ArgumentError ) { Psych.load("!ruby/object:Complex INVALID+COMPLEXi\n") }
+ end
+
+ def test_emitting_indicators
+ assert_to_yaml( "Hi, from Object 1. You passed: please, pretty please", <<EOY
+--- "Hi, from Object 1. You passed: please, pretty please"
+EOY
+ )
+ end
+
+ ##
+ ## Test the Psych::Stream class -- INACTIVE at the moment
+ ##
+ #def test_document
+ # y = Psych::Stream.new( :Indent => 2, :UseVersion => 0 )
+ # y.add(
+ # { 'hi' => 'hello', 'map' =>
+ # { 'good' => 'two' },
+ # 'time' => Time.now,
+ # 'try' => /^po(.*)$/,
+ # 'bye' => 'goodbye'
+ # }
+ # )
+ # y.add( { 'po' => 'nil', 'oper' => 90 } )
+ # y.add( { 'hi' => 'wow!', 'bye' => 'wow!' } )
+ # y.add( { [ 'Red Socks', 'Boston' ] => [ 'One', 'Two', 'Three' ] } )
+ # y.add( [ true, false, false ] )
+ #end
+
+ #
+ # Test YPath choices parsing
+ #
+ #def test_ypath_parsing
+ # assert_path_segments( "/*/((one|three)/name|place)|//place",
+ # [ ["*", "one", "name"],
+ # ["*", "three", "name"],
+ # ["*", "place"],
+ # ["/", "place"] ]
+ # )
+ #end
+
+ #
+ # Tests from Tanaka Akira on [ruby-core]
+ #
+ def test_akira
+
+ # Commas in plain scalars [ruby-core:1066]
+ assert_to_yaml(
+ {"A"=>"A,","B"=>"B"}, <<EOY
+A: "A,"
+B: B
+EOY
+ )
+
+ # Double-quoted keys [ruby-core:1069]
+ assert_to_yaml(
+ {"1"=>2, "2"=>3}, <<EOY
+'1': 2
+"2": 3
+EOY
+ )
+
+ # Anchored mapping [ruby-core:1071]
+ assert_to_yaml(
+ [{"a"=>"b"}] * 2, <<EOY
+- &id001
+ a: b
+- *id001
+EOY
+ )
+
+ # Stress test [ruby-core:1071]
+ # a = []; 1000.times { a << {"a"=>"b", "c"=>"d"} }
+ # Psych::load( a.to_yaml )
+
+ end
+
+ #
+ # Test Time.now cycle
+ #
+ def test_time_now_cycle
+ #
+ # From Minero Aoki [ruby-core:2305]
+ #
+ #require 'yaml'
+ t = Time.now
+ t = Time.at(t.tv_sec, t.tv_usec)
+ 5.times do
+ assert_cycle(t)
+ end
+ end
+
+ #
+ # Test Range cycle
+ #
+ def test_range_cycle
+ #
+ # From Minero Aoki [ruby-core:02306]
+ #
+ assert_cycle("a".."z")
+
+ #
+ # From Nobu Nakada [ruby-core:02311]
+ #
+ assert_cycle(0..1)
+ assert_cycle(1.0e20 .. 2.0e20)
+ assert_cycle("0".."1")
+ assert_cycle(".."..."...")
+ assert_cycle(".rb"..".pl")
+ assert_cycle(".rb"...".pl")
+ assert_cycle('"'...".")
+ assert_cycle("'"...".")
+ end
+
+ #
+ # Circular references
+ #
+ def test_circular_references
+ a = []; a[0] = a; a[1] = a
+ inspect_str = "[[...], [...]]"
+ assert_equal( inspect_str, Psych::load(Psych.dump(a)).inspect )
+ end
+
+ #
+ # Test Symbol cycle
+ #
+ def test_symbol_cycle
+ #
+ # From Aaron Schrab [ruby-Bugs:2535]
+ #
+ assert_cycle(:"^foo")
+ end
+
+ #
+ # Test Numeric cycle
+ #
+ class NumericTest < Numeric
+ def initialize(value)
+ @value = value
+ end
+ def ==(other)
+ @value == other.instance_eval{ @value }
+ end
+ end
+ def test_numeric_cycle
+ assert_cycle(1) # Fixnum
+ assert_cycle(111111111111111111111111111111111) # Bignum
+ assert_cycle(NumericTest.new(3)) # Subclass of Numeric
+ end
+
+ #
+ # Test empty map/seq in map cycle
+ #
+ def test_empty_map_key
+ #
+ # empty seq as key
+ #
+ assert_cycle({[]=>""})
+
+ #
+ # empty map as key
+ #
+ assert_cycle({{}=>""})
+ end
+
+ #
+ # contributed by riley lynch [ruby-Bugs-8548]
+ #
+ def test_object_id_collision
+ omap = Psych::Omap.new
+ 1000.times { |i| omap["key_#{i}"] = { "value" => i } }
+ raise "id collision in ordered map" if Psych.dump(omap) =~ /id\d+/
+ end
+
+ def test_date_out_of_range
+ Psych::load('1900-01-01T00:00:00+00:00')
+ end
+
+ def test_normal_exit
+ Psych.load("2000-01-01 00:00:00.#{"0"*1000} +00:00\n")
+ # '[ruby-core:13735]'
+ end
+
+ def test_multiline_string_uses_literal_style
+ yaml = Psych.dump("multi\nline\nstring")
+ assert_match("|", yaml)
+ end
+
+ def test_string_starting_with_non_word_character_uses_double_quotes_without_exclamation_mark
+ yaml = Psych.dump("@123'abc")
+ refute_match("!", yaml)
+ end
+
+ def test_string_dump_with_colon
+ yaml = Psych.dump 'x: foo'
+ refute_match '!', yaml
+ end
+
+ def test_string_dump_starting_with_star
+ yaml = Psych.dump '*foo'
+ refute_match '!', yaml
+ end
+end
diff --git a/jni/ruby/test/psych/test_yamldbm.rb b/jni/ruby/test/psych/test_yamldbm.rb
new file mode 100644
index 0000000..7853658
--- /dev/null
+++ b/jni/ruby/test/psych/test_yamldbm.rb
@@ -0,0 +1,193 @@
+require_relative 'helper'
+require 'tmpdir'
+
+begin
+ require 'yaml/dbm'
+rescue LoadError
+end
+
+module Psych
+ ::Psych::DBM = ::YAML::DBM unless defined?(::Psych::DBM)
+
+ class YAMLDBMTest < TestCase
+ def setup
+ @dir = Dir.mktmpdir("rubytest-file")
+ File.chown(-1, Process.gid, @dir)
+ @yamldbm_file = make_tmp_filename("yamldbm")
+ @yamldbm = YAML::DBM.new(@yamldbm_file)
+ end
+
+ def teardown
+ @yamldbm.clear
+ @yamldbm.close
+ FileUtils.remove_entry_secure @dir
+ end
+
+ def make_tmp_filename(prefix)
+ @dir + "/" + prefix + File.basename(__FILE__) + ".#{$$}.test"
+ end
+
+ def test_store
+ @yamldbm.store('a','b')
+ @yamldbm.store('c','d')
+ assert_equal 'b', @yamldbm['a']
+ assert_equal 'd', @yamldbm['c']
+ assert_nil @yamldbm['e']
+ end
+
+ def test_store_using_carret
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal 'b', @yamldbm['a']
+ assert_equal 'd', @yamldbm['c']
+ assert_nil @yamldbm['e']
+ end
+
+ def test_to_a
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal([['a','b'],['c','d']], @yamldbm.to_a.sort)
+ end
+
+ def test_to_hash
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal({'a'=>'b','c'=>'d'}, @yamldbm.to_hash)
+ end
+
+ def test_has_value?
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal true, @yamldbm.has_value?('b')
+ assert_equal true, @yamldbm.has_value?('d')
+ assert_equal false, @yamldbm.has_value?('f')
+ end
+
+ # Note:
+ # YAML::DBM#index makes warning from internal of ::DBM#index.
+ # It says 'DBM#index is deprecated; use DBM#key', but DBM#key
+ # behaves not same as DBM#index.
+ #
+ # def test_index
+ # @yamldbm['a'] = 'b'
+ # @yamldbm['c'] = 'd'
+ # assert_equal 'a', @yamldbm.index('b')
+ # assert_equal 'c', @yamldbm.index('d')
+ # assert_nil @yamldbm.index('f')
+ # end
+
+ def test_key
+ skip 'only on ruby 2.0.0' if RUBY_VERSION < '2.0.0'
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal 'a', @yamldbm.key('b')
+ assert_equal 'c', @yamldbm.key('d')
+ assert_nil @yamldbm.key('f')
+ end
+
+ def test_fetch
+ assert_equal('bar', @yamldbm['foo']='bar')
+ assert_equal('bar', @yamldbm.fetch('foo'))
+ assert_nil @yamldbm.fetch('bar')
+ assert_equal('baz', @yamldbm.fetch('bar', 'baz'))
+ assert_equal('foobar', @yamldbm.fetch('bar') {|key| 'foo' + key })
+ end
+
+ def test_shift
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal([['a','b'], ['c','d']],
+ [@yamldbm.shift, @yamldbm.shift].sort)
+ assert_nil @yamldbm.shift
+ end
+
+ def test_invert
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal({'b'=>'a','d'=>'c'}, @yamldbm.invert)
+ end
+
+ def test_update
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ @yamldbm.update({'c'=>'d','e'=>'f'})
+ assert_equal 'b', @yamldbm['a']
+ assert_equal 'd', @yamldbm['c']
+ assert_equal 'f', @yamldbm['e']
+ end
+
+ def test_replace
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ @yamldbm.replace({'c'=>'d','e'=>'f'})
+ assert_nil @yamldbm['a']
+ assert_equal 'd', @yamldbm['c']
+ assert_equal 'f', @yamldbm['e']
+ end
+
+ def test_delete
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal 'b', @yamldbm.delete('a')
+ assert_nil @yamldbm['a']
+ assert_equal 'd', @yamldbm['c']
+ assert_nil @yamldbm.delete('e')
+ end
+
+ def test_delete_if
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ @yamldbm['e'] = 'f'
+
+ @yamldbm.delete_if {|k,v| k == 'a'}
+ assert_nil @yamldbm['a']
+ assert_equal 'd', @yamldbm['c']
+ assert_equal 'f', @yamldbm['e']
+
+ @yamldbm.delete_if {|k,v| v == 'd'}
+ assert_nil @yamldbm['c']
+ assert_equal 'f', @yamldbm['e']
+
+ @yamldbm.delete_if {|k,v| false }
+ assert_equal 'f', @yamldbm['e']
+ end
+
+ def test_reject
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ @yamldbm['e'] = 'f'
+ assert_equal({'c'=>'d','e'=>'f'}, @yamldbm.reject {|k,v| k == 'a'})
+ assert_equal({'a'=>'b','e'=>'f'}, @yamldbm.reject {|k,v| v == 'd'})
+ assert_equal({'a'=>'b','c'=>'d','e'=>'f'}, @yamldbm.reject {false})
+ end
+
+ def test_values
+ assert_equal [], @yamldbm.values
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal ['b','d'], @yamldbm.values.sort
+ end
+
+ def test_values_at
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ assert_equal ['b','d'], @yamldbm.values_at('a','c')
+ end
+
+ def test_selsct
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ @yamldbm['e'] = 'f'
+ assert_equal(['b','d'], @yamldbm.select('a','c'))
+ end
+
+ def test_selsct_with_block
+ @yamldbm['a'] = 'b'
+ @yamldbm['c'] = 'd'
+ @yamldbm['e'] = 'f'
+ assert_equal([['a','b']], @yamldbm.select {|k,v| k == 'a'})
+ assert_equal([['c','d']], @yamldbm.select {|k,v| v == 'd'})
+ assert_equal([], @yamldbm.select {false})
+ end
+ end
+end if defined?(YAML::DBM) && defined?(Psych)
diff --git a/jni/ruby/test/psych/test_yamlstore.rb b/jni/ruby/test/psych/test_yamlstore.rb
new file mode 100644
index 0000000..94f1330
--- /dev/null
+++ b/jni/ruby/test/psych/test_yamlstore.rb
@@ -0,0 +1,85 @@
+require_relative 'helper'
+require 'yaml/store'
+require 'tmpdir'
+
+module Psych
+ Psych::Store = YAML::Store unless defined?(Psych::Store)
+
+ class YAMLStoreTest < TestCase
+ def setup
+ @dir = Dir.mktmpdir("rubytest-file")
+ File.chown(-1, Process.gid, @dir)
+ @yamlstore_file = make_tmp_filename("yamlstore")
+ @yamlstore = YAML::Store.new(@yamlstore_file)
+ end
+
+ def teardown
+ FileUtils.remove_entry_secure @dir
+ end
+
+ def make_tmp_filename(prefix)
+ @dir + "/" + prefix + File.basename(__FILE__) + ".#{$$}.test"
+ end
+
+ def test_opening_new_file_in_readonly_mode_should_result_in_empty_values
+ @yamlstore.transaction(true) do
+ assert_nil @yamlstore[:foo]
+ assert_nil @yamlstore[:bar]
+ end
+ end
+
+ def test_opening_new_file_in_readwrite_mode_should_result_in_empty_values
+ @yamlstore.transaction do
+ assert_nil @yamlstore[:foo]
+ assert_nil @yamlstore[:bar]
+ end
+ end
+
+ def test_data_should_be_loaded_correctly_when_in_readonly_mode
+ @yamlstore.transaction do
+ @yamlstore[:foo] = "bar"
+ end
+ @yamlstore.transaction(true) do
+ assert_equal "bar", @yamlstore[:foo]
+ end
+ end
+
+ def test_data_should_be_loaded_correctly_when_in_readwrite_mode
+ @yamlstore.transaction do
+ @yamlstore[:foo] = "bar"
+ end
+ @yamlstore.transaction do
+ assert_equal "bar", @yamlstore[:foo]
+ end
+ end
+
+ def test_changes_after_commit_are_discarded
+ @yamlstore.transaction do
+ @yamlstore[:foo] = "bar"
+ @yamlstore.commit
+ @yamlstore[:foo] = "baz"
+ end
+ @yamlstore.transaction(true) do
+ assert_equal "bar", @yamlstore[:foo]
+ end
+ end
+
+ def test_changes_are_not_written_on_abort
+ @yamlstore.transaction do
+ @yamlstore[:foo] = "bar"
+ @yamlstore.abort
+ end
+ @yamlstore.transaction(true) do
+ assert_nil @yamlstore[:foo]
+ end
+ end
+
+ def test_writing_inside_readonly_transaction_raises_error
+ assert_raises(PStore::Error) do
+ @yamlstore.transaction(true) do
+ @yamlstore[:foo] = "bar"
+ end
+ end
+ end
+ end
+end if defined?(Psych)
diff --git a/jni/ruby/test/psych/visitors/test_depth_first.rb b/jni/ruby/test/psych/visitors/test_depth_first.rb
new file mode 100644
index 0000000..837c8e8
--- /dev/null
+++ b/jni/ruby/test/psych/visitors/test_depth_first.rb
@@ -0,0 +1,49 @@
+require 'psych/helper'
+
+module Psych
+ module Visitors
+ class TestDepthFirst < TestCase
+ class Collector < Struct.new(:calls)
+ def initialize(calls = [])
+ super
+ end
+
+ def call obj
+ calls << obj
+ end
+ end
+
+ def test_scalar
+ collector = Collector.new
+ visitor = Visitors::DepthFirst.new collector
+ visitor.accept Psych.parse_stream '--- hello'
+
+ assert_equal 3, collector.calls.length
+ end
+
+ def test_sequence
+ collector = Collector.new
+ visitor = Visitors::DepthFirst.new collector
+ visitor.accept Psych.parse_stream "---\n- hello"
+
+ assert_equal 4, collector.calls.length
+ end
+
+ def test_mapping
+ collector = Collector.new
+ visitor = Visitors::DepthFirst.new collector
+ visitor.accept Psych.parse_stream "---\nhello: world"
+
+ assert_equal 5, collector.calls.length
+ end
+
+ def test_alias
+ collector = Collector.new
+ visitor = Visitors::DepthFirst.new collector
+ visitor.accept Psych.parse_stream "--- &yay\n- foo\n- *yay\n"
+
+ assert_equal 5, collector.calls.length
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/visitors/test_emitter.rb b/jni/ruby/test/psych/visitors/test_emitter.rb
new file mode 100644
index 0000000..780c953
--- /dev/null
+++ b/jni/ruby/test/psych/visitors/test_emitter.rb
@@ -0,0 +1,144 @@
+require 'psych/helper'
+
+module Psych
+ module Visitors
+ class TestEmitter < TestCase
+ def setup
+ super
+ @io = StringIO.new
+ @visitor = Visitors::Emitter.new @io
+ end
+
+ def test_options
+ io = StringIO.new
+ visitor = Visitors::Emitter.new io, :indentation => 3
+
+ s = Nodes::Stream.new
+ doc = Nodes::Document.new
+ mapping = Nodes::Mapping.new
+ m2 = Nodes::Mapping.new
+ m2.children << Nodes::Scalar.new('a')
+ m2.children << Nodes::Scalar.new('b')
+
+ mapping.children << Nodes::Scalar.new('key')
+ mapping.children << m2
+ doc.children << mapping
+ s.children << doc
+
+ visitor.accept s
+ assert_match(/^[ ]{3}a/, io.string)
+ end
+
+ def test_stream
+ s = Nodes::Stream.new
+ @visitor.accept s
+ assert_equal '', @io.string
+ end
+
+ def test_document
+ s = Nodes::Stream.new
+ doc = Nodes::Document.new [1,1]
+ scalar = Nodes::Scalar.new 'hello world'
+
+ doc.children << scalar
+ s.children << doc
+
+ @visitor.accept s
+
+ assert_match(/1.1/, @io.string)
+ assert_equal @io.string, s.yaml
+ end
+
+ def test_document_implicit_end
+ s = Nodes::Stream.new
+ doc = Nodes::Document.new
+ mapping = Nodes::Mapping.new
+ mapping.children << Nodes::Scalar.new('key')
+ mapping.children << Nodes::Scalar.new('value')
+ doc.children << mapping
+ s.children << doc
+
+ @visitor.accept s
+
+ assert_match(/key: value/, @io.string)
+ assert_equal @io.string, s.yaml
+ assert(/\.\.\./ !~ s.yaml)
+ end
+
+ def test_scalar
+ s = Nodes::Stream.new
+ doc = Nodes::Document.new
+ scalar = Nodes::Scalar.new 'hello world'
+
+ doc.children << scalar
+ s.children << doc
+
+ @visitor.accept s
+
+ assert_match(/hello/, @io.string)
+ assert_equal @io.string, s.yaml
+ end
+
+ def test_scalar_with_tag
+ s = Nodes::Stream.new
+ doc = Nodes::Document.new
+ scalar = Nodes::Scalar.new 'hello world', nil, '!str', false, false, 5
+
+ doc.children << scalar
+ s.children << doc
+
+ @visitor.accept s
+
+ assert_match(/str/, @io.string)
+ assert_match(/hello/, @io.string)
+ assert_equal @io.string, s.yaml
+ end
+
+ def test_sequence
+ s = Nodes::Stream.new
+ doc = Nodes::Document.new
+ scalar = Nodes::Scalar.new 'hello world'
+ seq = Nodes::Sequence.new
+
+ seq.children << scalar
+ doc.children << seq
+ s.children << doc
+
+ @visitor.accept s
+
+ assert_match(/- hello/, @io.string)
+ assert_equal @io.string, s.yaml
+ end
+
+ def test_mapping
+ s = Nodes::Stream.new
+ doc = Nodes::Document.new
+ mapping = Nodes::Mapping.new
+ mapping.children << Nodes::Scalar.new('key')
+ mapping.children << Nodes::Scalar.new('value')
+ doc.children << mapping
+ s.children << doc
+
+ @visitor.accept s
+
+ assert_match(/key: value/, @io.string)
+ assert_equal @io.string, s.yaml
+ end
+
+ def test_alias
+ s = Nodes::Stream.new
+ doc = Nodes::Document.new
+ mapping = Nodes::Mapping.new
+ mapping.children << Nodes::Scalar.new('key', 'A')
+ mapping.children << Nodes::Alias.new('A')
+ doc.children << mapping
+ s.children << doc
+
+ @visitor.accept s
+
+ assert_match(/&A key: \*A/, @io.string)
+ assert_equal @io.string, s.yaml
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/visitors/test_to_ruby.rb b/jni/ruby/test/psych/visitors/test_to_ruby.rb
new file mode 100644
index 0000000..c13d980
--- /dev/null
+++ b/jni/ruby/test/psych/visitors/test_to_ruby.rb
@@ -0,0 +1,326 @@
+# coding: US-ASCII
+require 'psych/helper'
+
+module Psych
+ module Visitors
+ class TestToRuby < TestCase
+ def setup
+ super
+ @visitor = ToRuby.create
+ end
+
+ def test_object
+ mapping = Nodes::Mapping.new nil, "!ruby/object"
+ mapping.children << Nodes::Scalar.new('foo')
+ mapping.children << Nodes::Scalar.new('bar')
+
+ o = mapping.to_ruby
+ assert_equal 'bar', o.instance_variable_get(:@foo)
+ end
+
+ def test_tz_00_00_loads_without_error
+ assert Psych.load('1900-01-01T00:00:00+00:00')
+ end
+
+ def test_legacy_struct
+ foo = Struct.new('AWESOME', :bar)
+ assert_equal foo.new('baz'), Psych.load(<<-eoyml)
+!ruby/struct:AWESOME
+ bar: baz
+ eoyml
+ end
+
+ def test_binary
+ gif = "GIF89a\f\x00\f\x00\x84\x00\x00\xFF\xFF\xF7\xF5\xF5\xEE\xE9\xE9\xE5fff\x00\x00\x00\xE7\xE7\xE7^^^\xF3\xF3\xED\x8E\x8E\x8E\xE0\xE0\xE0\x9F\x9F\x9F\x93\x93\x93\xA7\xA7\xA7\x9E\x9E\x9Eiiiccc\xA3\xA3\xA3\x84\x84\x84\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9!\xFE\x0EMade with GIMP\x00,\x00\x00\x00\x00\f\x00\f\x00\x00\x05, \x8E\x810\x9E\xE3@\x14\xE8i\x10\xC4\xD1\x8A\b\x1C\xCF\x80M$z\xEF\xFF0\x85p\xB8\xB01f\r\e\xCE\x01\xC3\x01\x1E\x10' \x82\n\x01\x00;"
+
+ hash = Psych.load(<<-'eoyaml')
+canonical: !!binary "\
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\
+ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\
+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\
+ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs="
+generic: !binary |
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
+ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
+ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
+description:
+ The binary value above is a tiny arrow encoded as a gif image.
+ eoyaml
+ assert_equal gif, hash['canonical']
+ assert_equal gif, hash['generic']
+ end
+
+ A = Struct.new(:foo)
+
+ def test_struct
+ s = A.new('bar')
+
+ mapping = Nodes::Mapping.new nil, "!ruby/struct:#{s.class}"
+ mapping.children << Nodes::Scalar.new('foo')
+ mapping.children << Nodes::Scalar.new('bar')
+
+ ruby = mapping.to_ruby
+
+ assert_equal s.class, ruby.class
+ assert_equal s.foo, ruby.foo
+ assert_equal s, ruby
+ end
+
+ def test_anon_struct_legacy
+ s = Struct.new(:foo).new('bar')
+
+ mapping = Nodes::Mapping.new nil, '!ruby/struct:'
+ mapping.children << Nodes::Scalar.new('foo')
+ mapping.children << Nodes::Scalar.new('bar')
+
+ assert_equal s.foo, mapping.to_ruby.foo
+ end
+
+ def test_anon_struct
+ s = Struct.new(:foo).new('bar')
+
+ mapping = Nodes::Mapping.new nil, '!ruby/struct'
+ mapping.children << Nodes::Scalar.new('foo')
+ mapping.children << Nodes::Scalar.new('bar')
+
+ assert_equal s.foo, mapping.to_ruby.foo
+ end
+
+ def test_exception
+ exc = ::Exception.new 'hello'
+
+ mapping = Nodes::Mapping.new nil, '!ruby/exception'
+ mapping.children << Nodes::Scalar.new('message')
+ mapping.children << Nodes::Scalar.new('hello')
+
+ ruby = mapping.to_ruby
+
+ assert_equal exc.class, ruby.class
+ assert_equal exc.message, ruby.message
+ end
+
+ def test_regexp
+ node = Nodes::Scalar.new('/foo/', nil, '!ruby/regexp')
+ assert_equal(/foo/, node.to_ruby)
+
+ node = Nodes::Scalar.new('/foo/m', nil, '!ruby/regexp')
+ assert_equal(/foo/m, node.to_ruby)
+
+ node = Nodes::Scalar.new('/foo/ix', nil, '!ruby/regexp')
+ assert_equal(/foo/ix, node.to_ruby)
+ end
+
+ def test_time
+ now = Time.now
+ zone = now.strftime('%z')
+ zone = " #{zone[0,3]}:#{zone[3,5]}"
+
+ formatted = now.strftime("%Y-%m-%d %H:%M:%S.%9N") + zone
+
+ assert_equal now, Nodes::Scalar.new(formatted).to_ruby
+ end
+
+ def test_time_utc
+ now = Time.now.utc
+ formatted = now.strftime("%Y-%m-%d %H:%M:%S") +
+ ".%09dZ" % [now.nsec]
+
+ assert_equal now, Nodes::Scalar.new(formatted).to_ruby
+ end
+
+ def test_time_utc_no_z
+ now = Time.now.utc
+ formatted = now.strftime("%Y-%m-%d %H:%M:%S") +
+ ".%09d" % [now.nsec]
+
+ assert_equal now, Nodes::Scalar.new(formatted).to_ruby
+ end
+
+ def test_date
+ d = '1980-12-16'
+ actual = Date.strptime(d, '%Y-%m-%d')
+
+ date = Nodes::Scalar.new(d, nil, 'tag:yaml.org,2002:timestamp', false)
+
+ assert_equal actual, date.to_ruby
+ end
+
+ def test_rational
+ mapping = Nodes::Mapping.new nil, '!ruby/object:Rational'
+ mapping.children << Nodes::Scalar.new('denominator')
+ mapping.children << Nodes::Scalar.new('2')
+ mapping.children << Nodes::Scalar.new('numerator')
+ mapping.children << Nodes::Scalar.new('1')
+
+ assert_equal Rational(1,2), mapping.to_ruby
+ end
+
+ def test_complex
+ mapping = Nodes::Mapping.new nil, '!ruby/object:Complex'
+ mapping.children << Nodes::Scalar.new('image')
+ mapping.children << Nodes::Scalar.new('2')
+ mapping.children << Nodes::Scalar.new('real')
+ mapping.children << Nodes::Scalar.new('1')
+
+ assert_equal Complex(1,2), mapping.to_ruby
+ end
+
+ if RUBY_VERSION >= '1.9'
+ def test_complex_string
+ node = Nodes::Scalar.new '3+4i', nil, "!ruby/object:Complex"
+ assert_equal Complex(3, 4), node.to_ruby
+ end
+
+ def test_rational_string
+ node = Nodes::Scalar.new '1/2', nil, "!ruby/object:Rational"
+ assert_equal Rational(1, 2), node.to_ruby
+ end
+ end
+
+ def test_range_string
+ node = Nodes::Scalar.new '1..2', nil, "!ruby/range"
+ assert_equal 1..2, node.to_ruby
+ end
+
+ def test_range_string_triple
+ node = Nodes::Scalar.new '1...3', nil, "!ruby/range"
+ assert_equal 1...3, node.to_ruby
+ end
+
+ def test_integer
+ i = Nodes::Scalar.new('1', nil, 'tag:yaml.org,2002:int')
+ assert_equal 1, i.to_ruby
+
+ assert_equal 1, Nodes::Scalar.new('1').to_ruby
+
+ i = Nodes::Scalar.new('-1', nil, 'tag:yaml.org,2002:int')
+ assert_equal(-1, i.to_ruby)
+
+ assert_equal(-1, Nodes::Scalar.new('-1').to_ruby)
+ assert_equal 1, Nodes::Scalar.new('+1').to_ruby
+ end
+
+ def test_int_ignore
+ ['1,000', '1_000'].each do |num|
+ i = Nodes::Scalar.new(num, nil, 'tag:yaml.org,2002:int')
+ assert_equal 1000, i.to_ruby
+
+ assert_equal 1000, Nodes::Scalar.new(num).to_ruby
+ end
+ end
+
+ def test_float_ignore
+ ['1,000.3', '1_000.3'].each do |num|
+ i = Nodes::Scalar.new(num, nil, 'tag:yaml.org,2002:float')
+ assert_equal 1000.3, i.to_ruby
+
+ i = Nodes::Scalar.new(num, nil, '!float')
+ assert_equal 1000.3, i.to_ruby
+
+ assert_equal 1000.3, Nodes::Scalar.new(num).to_ruby
+ end
+ end
+
+ # http://yaml.org/type/bool.html
+ def test_boolean_true
+ %w{ yes Yes YES true True TRUE on On ON }.each do |t|
+ i = Nodes::Scalar.new(t, nil, 'tag:yaml.org,2002:bool')
+ assert_equal true, i.to_ruby
+ assert_equal true, Nodes::Scalar.new(t).to_ruby
+ end
+ end
+
+ # http://yaml.org/type/bool.html
+ def test_boolean_false
+ %w{ no No NO false False FALSE off Off OFF }.each do |t|
+ i = Nodes::Scalar.new(t, nil, 'tag:yaml.org,2002:bool')
+ assert_equal false, i.to_ruby
+ assert_equal false, Nodes::Scalar.new(t).to_ruby
+ end
+ end
+
+ def test_float
+ i = Nodes::Scalar.new('12', nil, 'tag:yaml.org,2002:float')
+ assert_equal 12.0, i.to_ruby
+
+ i = Nodes::Scalar.new('1.2', nil, 'tag:yaml.org,2002:float')
+ assert_equal 1.2, i.to_ruby
+
+ i = Nodes::Scalar.new('1.2')
+ assert_equal 1.2, i.to_ruby
+
+ assert_equal 1, Nodes::Scalar.new('.Inf').to_ruby.infinite?
+ assert_equal 1, Nodes::Scalar.new('.inf').to_ruby.infinite?
+ assert_equal 1, Nodes::Scalar.new('.Inf', nil, 'tag:yaml.org,2002:float').to_ruby.infinite?
+
+ assert_equal(-1, Nodes::Scalar.new('-.inf').to_ruby.infinite?)
+ assert_equal(-1, Nodes::Scalar.new('-.Inf').to_ruby.infinite?)
+ assert_equal(-1, Nodes::Scalar.new('-.Inf', nil, 'tag:yaml.org,2002:float').to_ruby.infinite?)
+
+ assert Nodes::Scalar.new('.NaN').to_ruby.nan?
+ assert Nodes::Scalar.new('.NaN', nil, 'tag:yaml.org,2002:float').to_ruby.nan?
+ end
+
+ def test_exp_float
+ exp = 1.2e+30
+
+ i = Nodes::Scalar.new(exp.to_s, nil, 'tag:yaml.org,2002:float')
+ assert_equal exp, i.to_ruby
+
+ assert_equal exp, Nodes::Scalar.new(exp.to_s).to_ruby
+ end
+
+ def test_scalar
+ scalar = Nodes::Scalar.new('foo')
+ assert_equal 'foo', @visitor.accept(scalar)
+ assert_equal 'foo', scalar.to_ruby
+ end
+
+ def test_sequence
+ seq = Nodes::Sequence.new
+ seq.children << Nodes::Scalar.new('foo')
+ seq.children << Nodes::Scalar.new('bar')
+
+ assert_equal %w{ foo bar }, seq.to_ruby
+ end
+
+ def test_mapping
+ mapping = Nodes::Mapping.new
+ mapping.children << Nodes::Scalar.new('foo')
+ mapping.children << Nodes::Scalar.new('bar')
+ assert_equal({'foo' => 'bar'}, mapping.to_ruby)
+ end
+
+ def test_document
+ doc = Nodes::Document.new
+ doc.children << Nodes::Scalar.new('foo')
+ assert_equal 'foo', doc.to_ruby
+ end
+
+ def test_stream
+ a = Nodes::Document.new
+ a.children << Nodes::Scalar.new('foo')
+
+ b = Nodes::Document.new
+ b.children << Nodes::Scalar.new('bar')
+
+ stream = Nodes::Stream.new
+ stream.children << a
+ stream.children << b
+
+ assert_equal %w{ foo bar }, stream.to_ruby
+ end
+
+ def test_alias
+ seq = Nodes::Sequence.new
+ seq.children << Nodes::Scalar.new('foo', 'A')
+ seq.children << Nodes::Alias.new('A')
+
+ list = seq.to_ruby
+ assert_equal %w{ foo foo }, list
+ assert_equal list[0].object_id, list[1].object_id
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/psych/visitors/test_yaml_tree.rb b/jni/ruby/test/psych/visitors/test_yaml_tree.rb
new file mode 100644
index 0000000..40702bc
--- /dev/null
+++ b/jni/ruby/test/psych/visitors/test_yaml_tree.rb
@@ -0,0 +1,173 @@
+require 'psych/helper'
+
+module Psych
+ module Visitors
+ class TestYAMLTree < TestCase
+ def setup
+ super
+ @v = Visitors::YAMLTree.create
+ end
+
+ def test_tree_can_be_called_twice
+ @v.start
+ @v << Object.new
+ t = @v.tree
+ assert_equal t, @v.tree
+ end
+
+ def test_yaml_tree_can_take_an_emitter
+ io = StringIO.new
+ e = Psych::Emitter.new io
+ v = Visitors::YAMLTree.create({}, e)
+ v.start
+ v << "hello world"
+ v.finish
+
+ assert_match "hello world", io.string
+ end
+
+ def test_binary_formatting
+ gif = "GIF89a\f\x00\f\x00\x84\x00\x00\xFF\xFF\xF7\xF5\xF5\xEE\xE9\xE9\xE5fff\x00\x00\x00\xE7\xE7\xE7^^^\xF3\xF3\xED\x8E\x8E\x8E\xE0\xE0\xE0\x9F\x9F\x9F\x93\x93\x93\xA7\xA7\xA7\x9E\x9E\x9Eiiiccc\xA3\xA3\xA3\x84\x84\x84\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9\xFF\xFE\xF9!\xFE\x0EMade with GIMP\x00,\x00\x00\x00\x00\f\x00\f\x00\x00\x05, \x8E\x810\x9E\xE3@\x14\xE8i\x10\xC4\xD1\x8A\b\x1C\xCF\x80M$z\xEF\xFF0\x85p\xB8\xB01f\r\e\xCE\x01\xC3\x01\x1E\x10' \x82\n\x01\x00;"
+ @v << gif
+ scalar = @v.tree.children.first.children.first
+ assert_equal Psych::Nodes::Scalar::LITERAL, scalar.style
+ end
+
+ def test_object_has_no_class
+ yaml = Psych.dump(Object.new)
+ assert(Psych.dump(Object.new) !~ /Object/, yaml)
+ end
+
+ def test_struct_const
+ foo = Struct.new("Foo", :bar)
+ assert_cycle foo.new('bar')
+ Struct.instance_eval { remove_const(:Foo) }
+ end
+
+ A = Struct.new(:foo)
+
+ def test_struct
+ assert_cycle A.new('bar')
+ end
+
+ def test_struct_anon
+ s = Struct.new(:foo).new('bar')
+ obj = Psych.load(Psych.dump(s))
+ assert_equal s.foo, obj.foo
+ end
+
+ def test_override_method
+ s = Struct.new(:method).new('override')
+ obj = Psych.load(Psych.dump(s))
+ assert_equal s.method, obj.method
+ end
+
+ def test_exception
+ ex = Exception.new 'foo'
+ loaded = Psych.load(Psych.dump(ex))
+
+ assert_equal ex.message, loaded.message
+ assert_equal ex.class, loaded.class
+ end
+
+ def test_regexp
+ assert_cycle(/foo/)
+ assert_cycle(/foo/i)
+ assert_cycle(/foo/mx)
+ end
+
+ def test_time
+ t = Time.now
+ assert_equal t, Psych.load(Psych.dump(t))
+ end
+
+ def test_date
+ date = Date.strptime('2002-12-14', '%Y-%m-%d')
+ assert_cycle date
+ end
+
+ def test_rational
+ assert_cycle Rational(1,2)
+ end
+
+ def test_complex
+ assert_cycle Complex(1,2)
+ end
+
+ def test_scalar
+ assert_cycle 'foo'
+ assert_cycle ':foo'
+ assert_cycle ''
+ assert_cycle ':'
+ end
+
+ def test_boolean
+ assert_cycle true
+ assert_cycle 'true'
+ assert_cycle false
+ assert_cycle 'false'
+ end
+
+ def test_range_inclusive
+ assert_cycle 1..2
+ end
+
+ def test_range_exclusive
+ assert_cycle 1...2
+ end
+
+ def test_anon_class
+ assert_raises(TypeError) do
+ @v.accept Class.new
+ end
+
+ assert_raises(TypeError) do
+ Psych.dump(Class.new)
+ end
+ end
+
+ def test_hash
+ assert_cycle('a' => 'b')
+ end
+
+ def test_list
+ assert_cycle(%w{ a b })
+ assert_cycle([1, 2.2])
+ end
+
+ def test_symbol
+ assert_cycle :foo
+ end
+
+ def test_int
+ assert_cycle 1
+ assert_cycle(-1)
+ assert_cycle '1'
+ assert_cycle '-1'
+ end
+
+ def test_float
+ assert_cycle 1.2
+ assert_cycle '1.2'
+
+ assert Psych.load(Psych.dump(0.0 / 0.0)).nan?
+ assert_equal 1, Psych.load(Psych.dump(1 / 0.0)).infinite?
+ assert_equal(-1, Psych.load(Psych.dump(-1 / 0.0)).infinite?)
+ end
+
+ # http://yaml.org/type/null.html
+ def test_nil
+ assert_cycle nil
+ assert_equal nil, Psych.load('null')
+ assert_equal nil, Psych.load('Null')
+ assert_equal nil, Psych.load('NULL')
+ assert_equal nil, Psych.load('~')
+ assert_equal({'foo' => nil}, Psych.load('foo: '))
+
+ assert_cycle 'null'
+ assert_cycle 'nUll'
+ assert_cycle '~'
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rake/file_creation.rb b/jni/ruby/test/rake/file_creation.rb
new file mode 100644
index 0000000..facc57a
--- /dev/null
+++ b/jni/ruby/test/rake/file_creation.rb
@@ -0,0 +1,34 @@
+module FileCreation
+ OLDFILE = "old"
+ NEWFILE = "new"
+
+ def create_timed_files(oldfile, *newfiles)
+ return if (File.exist?(oldfile) &&
+ newfiles.all? { |newfile|
+ File.exist?(newfile) && File.stat(newfile).mtime > File.stat(oldfile).mtime
+ })
+ now = Time.now
+
+ create_file(oldfile, now - 60)
+
+ newfiles.each do |newfile|
+ create_file(newfile, now)
+ end
+ end
+
+ def create_dir(dirname)
+ FileUtils.mkdir_p(dirname) unless File.exist?(dirname)
+ File.stat(dirname).mtime
+ end
+
+ def create_file(name, file_time=nil)
+ create_dir(File.dirname(name))
+ FileUtils.touch(name) unless File.exist?(name)
+ File.utime(file_time, file_time, name) unless file_time.nil?
+ File.stat(name).mtime
+ end
+
+ def delete_file(name)
+ File.delete(name) rescue nil
+ end
+end
diff --git a/jni/ruby/test/rake/helper.rb b/jni/ruby/test/rake/helper.rb
new file mode 100644
index 0000000..ac1205a
--- /dev/null
+++ b/jni/ruby/test/rake/helper.rb
@@ -0,0 +1,129 @@
+require 'rubygems'
+$:.unshift File.expand_path('../../lib', __FILE__)
+
+begin
+ gem 'minitest', '~> 5'
+rescue Gem::LoadError
+end
+
+require 'minitest/autorun'
+require 'rake'
+require 'tmpdir'
+require File.expand_path('../file_creation', __FILE__)
+
+
+begin
+ require_relative 'support/ruby_runner'
+ require_relative 'support/rakefile_definitions'
+rescue NoMethodError, LoadError
+ # ruby 1.8
+ require 'test/support/ruby_runner'
+ require 'test/support/rakefile_definitions'
+end
+
+class Rake::TestCase < Minitest::Test
+ include FileCreation
+
+ include Rake::DSL
+
+ class TaskManager
+ include Rake::TaskManager
+ end
+
+ RUBY = defined?(EnvUtil) ? EnvUtil.rubybin : Gem.ruby
+
+ def setup
+ ARGV.clear
+
+ test_dir = File.basename File.dirname File.expand_path __FILE__
+
+ @rake_root =
+ if test_dir == 'test'
+ # rake repository
+ File.expand_path '../../', __FILE__
+ else
+ # ruby repository
+ File.expand_path '../../../', __FILE__
+ end
+
+ @verbose = ENV['VERBOSE']
+
+ @rake_exec = File.join @rake_root, 'bin', 'rake'
+ @rake_lib = File.join @rake_root, 'lib'
+ @ruby_options = ["-I#{@rake_lib}", "-I."]
+
+ @orig_pwd = Dir.pwd
+ @orig_appdata = ENV['APPDATA']
+ @orig_home = ENV['HOME']
+ @orig_homedrive = ENV['HOMEDRIVE']
+ @orig_homepath = ENV['HOMEPATH']
+ @orig_rake_columns = ENV['RAKE_COLUMNS']
+ @orig_rake_system = ENV['RAKE_SYSTEM']
+ @orig_rakeopt = ENV['RAKEOPT']
+ @orig_userprofile = ENV['USERPROFILE']
+ ENV.delete 'RAKE_COLUMNS'
+ ENV.delete 'RAKE_SYSTEM'
+ ENV.delete 'RAKEOPT'
+
+ tmpdir = Dir.chdir Dir.tmpdir do Dir.pwd end
+ @tempdir = File.join tmpdir, "test_rake_#{$$}"
+
+ FileUtils.mkdir_p @tempdir
+
+ Dir.chdir @tempdir
+
+ Rake.application = Rake::Application.new
+ Rake::TaskManager.record_task_metadata = true
+ RakeFileUtils.verbose_flag = false
+ end
+
+ def teardown
+ Dir.chdir @orig_pwd
+ FileUtils.rm_rf @tempdir
+
+ if @orig_appdata
+ ENV['APPDATA'] = @orig_appdata
+ else
+ ENV.delete 'APPDATA'
+ end
+
+ ENV['HOME'] = @orig_home
+ ENV['HOMEDRIVE'] = @orig_homedrive
+ ENV['HOMEPATH'] = @orig_homepath
+ ENV['RAKE_COLUMNS'] = @orig_rake_columns
+ ENV['RAKE_SYSTEM'] = @orig_rake_system
+ ENV['RAKEOPT'] = @orig_rakeopt
+ ENV['USERPROFILE'] = @orig_userprofile
+ end
+
+ def ignore_deprecations
+ Rake.application.options.ignore_deprecate = true
+ yield
+ ensure
+ Rake.application.options.ignore_deprecate = false
+ end
+
+ def rake_system_dir
+ @system_dir = 'system'
+
+ FileUtils.mkdir_p @system_dir
+
+ open File.join(@system_dir, 'sys1.rake'), 'w' do |io|
+ io << <<-SYS
+task "sys1" do
+ puts "SYS1"
+end
+ SYS
+ end
+
+ ENV['RAKE_SYSTEM'] = @system_dir
+ end
+
+ def rakefile(contents)
+ open 'Rakefile', 'w' do |io|
+ io << contents
+ end
+ end
+
+ include RakefileDefinitions
+end
diff --git a/jni/ruby/test/rake/support/rakefile_definitions.rb b/jni/ruby/test/rake/support/rakefile_definitions.rb
new file mode 100644
index 0000000..a637c7e
--- /dev/null
+++ b/jni/ruby/test/rake/support/rakefile_definitions.rb
@@ -0,0 +1,478 @@
+module RakefileDefinitions
+ include FileUtils
+
+ def rakefile_access
+ rakefile <<-ACCESS
+TOP_LEVEL_CONSTANT = 0
+
+def a_top_level_function
+end
+
+task :default => [:work, :obj, :const]
+
+task :work do
+ begin
+ a_top_level_function
+ puts "GOOD:M Top level methods can be called in tasks"
+ rescue NameError => ex
+ puts "BAD:M Top level methods can not be called in tasks"
+ end
+end
+
+# TODO: remove `disabled_' when DeprecatedObjectDSL removed
+task :obj
+task :disabled_obj do
+ begin
+ Object.new.instance_eval { task :xyzzy }
+ puts "BAD:D Rake DSL are polluting objects"
+ rescue StandardError => ex
+ puts "GOOD:D Rake DSL are not polluting objects"
+ end
+end
+
+task :const do
+ begin
+ TOP_LEVEL_CONSTANT
+ puts "GOOD:C Top level constants are available in tasks"
+ rescue StandardError => ex
+ puts "BAD:C Top level constants are NOT available in tasks"
+ end
+end
+ ACCESS
+ end
+
+ def rakefile_test_task
+ rakefile <<-RAKEFILE
+ require "rake/testtask"
+
+ Rake::TestTask.new(:unit) do |t|
+ t.description = "custom test task description"
+ end
+ RAKEFILE
+ end
+
+ def rakefile_chains
+ rakefile <<-DEFAULT
+task :default => "play.app"
+
+file "play.scpt" => "base" do |t|
+ cp t.prerequisites.first, t.name
+end
+
+rule ".app" => ".scpt" do |t|
+ cp t.source, t.name
+end
+
+file 'base' do
+ touch 'base'
+end
+ DEFAULT
+ end
+
+ def rakefile_comments
+ rakefile <<-COMMENTS
+# comment for t1
+task :t1 do
+end
+
+# no comment or task because there's a blank line
+
+task :t2 do
+end
+
+desc "override comment for t3"
+# this is not the description
+multitask :t3 do
+end
+
+# this is not the description
+desc "override comment for t4"
+file :t4 do
+end
+ COMMENTS
+ end
+
+ def rakefile_default
+ rakefile <<-DEFAULT
+if ENV['TESTTOPSCOPE']
+ puts "TOPSCOPE"
+end
+
+task :default do
+ puts "DEFAULT"
+end
+
+task :other => [:default] do
+ puts "OTHER"
+end
+
+task :task_scope do
+ if ENV['TESTTASKSCOPE']
+ puts "TASKSCOPE"
+ end
+end
+ DEFAULT
+ end
+
+ def rakefile_dryrun
+ rakefile <<-DRYRUN
+task :default => ["temp_main"]
+
+file "temp_main" => [:all_apps] do touch "temp_main" end
+
+task :all_apps => [:one, :two]
+task :one => ["temp_one"]
+task :two => ["temp_two"]
+
+file "temp_one" do |t|
+ touch "temp_one"
+end
+file "temp_two" do |t|
+ touch "temp_two"
+end
+
+task :clean do
+ ["temp_one", "temp_two", "temp_main"].each do |file|
+ rm_f file
+ end
+end
+ DRYRUN
+
+ FileUtils.touch 'temp_main'
+ FileUtils.touch 'temp_two'
+ end
+
+ def rakefile_extra
+ rakefile 'task :default'
+
+ FileUtils.mkdir_p 'rakelib'
+
+ open File.join('rakelib', 'extra.rake'), 'w' do |io|
+ io << <<-EXTRA_RAKE
+# Added for testing
+
+namespace :extra do
+ desc "An Extra Task"
+ task :extra do
+ puts "Read all about it"
+ end
+end
+ EXTRA_RAKE
+ end
+ end
+
+ def rakefile_file_creation
+ rakefile <<-'FILE_CREATION'
+N = 2
+
+task :default => :run
+
+BUILD_DIR = 'build'
+task :clean do
+ rm_rf 'build'
+ rm_rf 'src'
+end
+
+task :run
+
+TARGET_DIR = 'build/copies'
+
+FileList['src/*'].each do |src|
+ directory TARGET_DIR
+ target = File.join TARGET_DIR, File.basename(src)
+ file target => [src, TARGET_DIR] do
+ cp src, target
+ end
+ task :run => target
+end
+
+task :prep => :clean do
+ mkdir_p 'src'
+ N.times do |n|
+ touch "src/foo#{n}"
+ end
+end
+ FILE_CREATION
+ end
+
+ def rakefile_imports
+ rakefile <<-IMPORTS
+require 'rake/loaders/makefile'
+
+task :default
+
+task :other do
+ puts "OTHER"
+end
+
+file "dynamic_deps" do |t|
+ open(t.name, "w") do |f| f.puts "puts 'DYNAMIC'" end
+end
+
+import "dynamic_deps"
+import "static_deps"
+import "static_deps"
+import "deps.mf"
+puts "FIRST"
+ IMPORTS
+
+ open 'deps.mf', 'w' do |io|
+ io << <<-DEPS
+default: other
+ DEPS
+ end
+
+ open "static_deps", "w" do |f|
+ f.puts 'puts "STATIC"'
+ end
+ end
+
+ def rakefile_regenerate_imports
+ rakefile <<-REGENERATE_IMPORTS
+task :default
+
+task :regenerate do
+ open("deps", "w") do |f|
+ f << <<-CONTENT
+file "deps" => :regenerate
+puts "REGENERATED"
+ CONTENT
+ end
+end
+
+import "deps"
+ REGENERATE_IMPORTS
+
+ open "deps", "w" do |f|
+ f << <<-CONTENT
+file "deps" => :regenerate
+puts "INITIAL"
+ CONTENT
+ end
+ end
+
+ def rakefile_multidesc
+ rakefile <<-MULTIDESC
+task :b
+
+desc "A"
+task :a
+
+desc "B"
+task :b
+
+desc "A2"
+task :a
+
+task :c
+
+desc "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+task :d
+ MULTIDESC
+ end
+
+ def rakefile_namespace
+ rakefile <<-NAMESPACE
+desc "copy"
+task :copy do
+ puts "COPY"
+end
+
+namespace "nest" do
+ desc "nest copy"
+ task :copy do
+ puts "NEST COPY"
+ end
+ task :xx => :copy
+end
+
+anon_ns = namespace do
+ desc "anonymous copy task"
+ task :copy do
+ puts "ANON COPY"
+ end
+end
+
+desc "Top level task to run the anonymous version of copy"
+task :anon => anon_ns[:copy]
+
+namespace "very" do
+ namespace "nested" do
+ task "run" => "rake:copy"
+ end
+end
+
+namespace "a" do
+ desc "Run task in the 'a' namespace"
+ task "run" do
+ puts "IN A"
+ end
+end
+
+namespace "b" do
+ desc "Run task in the 'b' namespace"
+ task "run" => "a:run" do
+ puts "IN B"
+ end
+end
+
+namespace "file1" do
+ file "xyz.rb" do
+ puts "XYZ1"
+ end
+end
+
+namespace "file2" do
+ file "xyz.rb" do
+ puts "XYZ2"
+ end
+end
+
+namespace "scopedep" do
+ task :prepare do
+ touch "scopedep.rb"
+ puts "PREPARE"
+ end
+ file "scopedep.rb" => [:prepare] do
+ puts "SCOPEDEP"
+ end
+end
+ NAMESPACE
+ end
+
+ def rakefile_nosearch
+ FileUtils.touch 'dummy'
+ end
+
+ def rakefile_rakelib
+ FileUtils.mkdir_p 'rakelib'
+
+ Dir.chdir 'rakelib' do
+ open 'test1.rb', 'w' do |io|
+ io << <<-TEST1
+task :default do
+ puts "TEST1"
+end
+ TEST1
+ end
+
+ open 'test2.rake', 'w' do |io|
+ io << <<-TEST1
+task :default do
+ puts "TEST2"
+end
+ TEST1
+ end
+ end
+ end
+
+ def rakefile_rbext
+ open 'rakefile.rb', 'w' do |io|
+ io << 'task :default do puts "OK" end'
+ end
+ end
+
+ def rakefile_unittest
+ rakefile '# Empty Rakefile for Unit Test'
+
+ readme = File.join 'subdir', 'README'
+ FileUtils.mkdir_p File.dirname readme
+
+ FileUtils.touch readme
+ end
+
+ def rakefile_verbose
+ rakefile <<-VERBOSE
+task :standalone_verbose_true do
+ verbose true
+ sh "#{RUBY} -e '0'"
+end
+
+task :standalone_verbose_false do
+ verbose false
+ sh "#{RUBY} -e '0'"
+end
+
+task :inline_verbose_default do
+ sh "#{RUBY} -e '0'"
+end
+
+task :inline_verbose_false do
+ sh "#{RUBY} -e '0'", :verbose => false
+end
+
+task :inline_verbose_true do
+ sh "#{RUBY} -e '0'", :verbose => true
+end
+
+task :block_verbose_true do
+ verbose(true) do
+ sh "#{RUBY} -e '0'"
+ end
+end
+
+task :block_verbose_false do
+ verbose(false) do
+ sh "#{RUBY} -e '0'"
+ end
+end
+ VERBOSE
+ end
+
+ def rakefile_test_signal
+ rakefile <<-TEST_SIGNAL
+require 'rake/testtask'
+
+Rake::TestTask.new(:a) do |t|
+ t.test_files = ['a_test.rb']
+end
+
+Rake::TestTask.new(:b) do |t|
+ t.test_files = ['b_test.rb']
+end
+
+task :test do
+ Rake::Task[:a].invoke
+ Rake::Task[:b].invoke
+end
+
+task :default => :test
+ TEST_SIGNAL
+ open 'a_test.rb', 'w' do |io|
+ io << 'puts "ATEST"' << "\n"
+ io << '$stdout.flush' << "\n"
+ io << 'Process.kill("TERM", $$)' << "\n"
+ end
+ open 'b_test.rb', 'w' do |io|
+ io << 'puts "BTEST"' << "\n"
+ io << '$stdout.flush' << "\n"
+ end
+ end
+
+ def rakefile_failing_test_task
+ rakefile <<-TEST_TASK
+require 'rake/testtask'
+
+task :default => :test
+Rake::TestTask.new(:test) do |t|
+ t.test_files = ['a_test.rb']
+end
+ TEST_TASK
+ open 'a_test.rb', 'w' do |io|
+ io << "require 'minitest/autorun'\n"
+ io << "class ExitTaskTest < Minitest::Test\n"
+ io << " def test_exit\n"
+ io << " assert false, 'this should fail'\n"
+ io << " end\n"
+ io << "end\n"
+ end
+ end
+
+ def rakefile_stand_alone_filelist
+ open 'stand_alone_filelist.rb', 'w' do |io|
+ io << "require 'rake/file_list'\n"
+ io << "FL = Rake::FileList['*.rb']\n"
+ io << "puts FL\n"
+ end
+ end
+end
diff --git a/jni/ruby/test/rake/support/ruby_runner.rb b/jni/ruby/test/rake/support/ruby_runner.rb
new file mode 100644
index 0000000..d51dd24
--- /dev/null
+++ b/jni/ruby/test/rake/support/ruby_runner.rb
@@ -0,0 +1,34 @@
+module RubyRunner
+ include FileUtils
+
+ # Run a shell Ruby command with command line options (using the
+ # default test options). Output is captured in @out and @err
+ def ruby(*option_list)
+ run_ruby(@ruby_options + option_list)
+ end
+
+ # Run a command line rake with the give rake options. Default
+ # command line ruby options are included. Output is captured in
+ # @out and @err
+ def rake(*rake_options)
+ run_ruby @ruby_options + [@rake_exec] + rake_options
+ end
+
+ # Low level ruby command runner ...
+ def run_ruby(option_list)
+ puts "COMMAND: [#{RUBY} #{option_list.join ' '}]" if @verbose
+
+ Open3.popen3(RUBY, *option_list) {|inn, out, err, wait|
+ inn.close
+
+ @exit = wait ? wait.value : $?
+ @out = out.read
+ @err = err.read
+ }
+
+ puts "OUTPUT: [#{@out}]" if @verbose
+ puts "ERROR: [#{@err}]" if @verbose
+ puts "EXIT: [#{@exit.inspect}]" if @verbose
+ puts "PWD: [#{Dir.pwd}]" if @verbose
+ end
+end
diff --git a/jni/ruby/test/rake/test_private_reader.rb b/jni/ruby/test/rake/test_private_reader.rb
new file mode 100644
index 0000000..f86d424
--- /dev/null
+++ b/jni/ruby/test/rake/test_private_reader.rb
@@ -0,0 +1,42 @@
+require File.expand_path('../helper', __FILE__)
+require 'rake/private_reader'
+
+class TestPrivateAttrs < Rake::TestCase
+
+ class Sample
+ include Rake::PrivateReader
+
+ private_reader :reader, :a
+
+ def initialize
+ @reader = :RVALUE
+ end
+
+ def get_reader
+ reader
+ end
+
+ end
+
+ def setup
+ super
+ @sample = Sample.new
+ end
+
+ def test_private_reader_is_private
+ assert_private do @sample.reader end
+ assert_private do @sample.a end
+ end
+
+ def test_private_reader_returns_data
+ assert_equal :RVALUE, @sample.get_reader
+ end
+
+ private
+
+ def assert_private
+ ex = assert_raises(NoMethodError) do yield end
+ assert_match(/private/, ex.message)
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake.rb b/jni/ruby/test/rake/test_rake.rb
new file mode 100644
index 0000000..b2a3928
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake.rb
@@ -0,0 +1,40 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRake < Rake::TestCase
+ def test_each_dir_parent
+ assert_equal ['a'], alldirs('a')
+ assert_equal ['a/b', 'a'], alldirs('a/b')
+ assert_equal ['/a/b', '/a', '/'], alldirs('/a/b')
+ if File.dirname("c:/foo") == "c:"
+ # Under Unix
+ assert_equal ['c:/a/b', 'c:/a', 'c:'], alldirs('c:/a/b')
+ assert_equal ['c:a/b', 'c:a'], alldirs('c:a/b')
+ else
+ # Under Windows
+ assert_equal ['c:/a/b', 'c:/a', 'c:/'], alldirs('c:/a/b')
+ assert_equal ['c:a/b', 'c:a'], alldirs('c:a/b')
+ end
+ end
+
+ def alldirs(fn)
+ result = []
+ Rake.each_dir_parent(fn) { |d| result << d }
+ result
+ end
+
+ def test_can_override_application
+ old_app = Rake.application
+ fake_app = Object.new
+ Rake.application = fake_app
+
+ assert_equal fake_app, Rake.application
+
+ ensure
+ Rake.application = old_app
+ end
+
+ def test_original_dir_reports_current_dir
+ assert_equal @tempdir, Rake.original_dir
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_application.rb b/jni/ruby/test/rake/test_rake_application.rb
new file mode 100644
index 0000000..c010889
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_application.rb
@@ -0,0 +1,643 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeApplication < Rake::TestCase
+
+ def setup
+ super
+
+ @app = Rake.application
+ @app.options.rakelib = []
+ end
+
+ def setup_command_line(*options)
+ ARGV.clear
+ options.each do |option|
+ ARGV << option
+ end
+ end
+
+ def test_display_exception_details
+ obj = Object.new
+ obj.instance_eval("def #{__method__}; raise 'test'; end", "ruby")
+ begin
+ obj.__send__(__method__)
+ rescue => ex
+ end
+
+ out, err = capture_io do
+ @app.display_error_message ex
+ end
+
+ assert_empty out
+
+ assert_match 'rake aborted!', err
+ assert_match __method__.to_s, err
+ end
+
+ def test_display_exception_details_cause
+ skip 'Exception#cause not implemented' unless
+ Exception.method_defined? :cause
+
+ begin
+ raise 'cause a'
+ rescue
+ begin
+ raise 'cause b'
+ rescue => ex
+ end
+ end
+
+ out, err = capture_io do
+ @app.display_error_message ex
+ end
+
+ assert_empty out
+
+ assert_match 'cause a', err
+ assert_match 'cause b', err
+ end
+
+ def test_display_exception_details_cause_loop
+ skip 'Exception#cause not implemented' unless
+ Exception.method_defined? :cause
+
+ begin
+ begin
+ raise 'cause a'
+ rescue => a
+ begin
+ raise 'cause b'
+ rescue
+ raise a
+ end
+ end
+ rescue => ex
+ end
+
+ out, err = capture_io do
+ @app.display_error_message ex
+ end
+
+ assert_empty out
+
+ assert_match 'cause a', err
+ assert_match 'cause b', err
+ end
+
+ def test_display_tasks
+ @app.options.show_tasks = :tasks
+ @app.options.show_task_pattern = //
+ @app.last_description = "COMMENT"
+ @app.define_task(Rake::Task, "t")
+ out, = capture_io do @app.instance_eval { display_tasks_and_comments } end
+ assert_match(/^rake t/, out)
+ assert_match(/# COMMENT/, out)
+ end
+
+ def test_display_tasks_with_long_comments
+ @app.terminal_columns = 80
+ @app.options.show_tasks = :tasks
+ @app.options.show_task_pattern = //
+ numbers = "1234567890" * 8
+ @app.last_description = numbers
+ @app.define_task(Rake::Task, "t")
+
+ out, = capture_io do @app.instance_eval { display_tasks_and_comments } end
+
+ assert_match(/^rake t/, out)
+ assert_match(/# #{numbers[0, 65]}\.\.\./, out)
+ end
+
+ def test_display_tasks_with_task_name_wider_than_tty_display
+ @app.terminal_columns = 80
+ @app.options.show_tasks = :tasks
+ @app.options.show_task_pattern = //
+ task_name = "task name" * 80
+ @app.last_description = "something short"
+ @app.define_task(Rake::Task, task_name)
+
+ out, = capture_io do @app.instance_eval { display_tasks_and_comments } end
+
+ # Ensure the entire task name is output and we end up showing no description
+ assert_match(/rake #{task_name} # .../, out)
+ end
+
+ def test_display_tasks_with_very_long_task_name_to_a_non_tty_shows_name_and_comment
+ @app.options.show_tasks = :tasks
+ @app.options.show_task_pattern = //
+ @app.tty_output = false
+ description = "something short"
+ task_name = "task name" * 80
+ @app.last_description = "something short"
+ @app.define_task(Rake::Task, task_name)
+
+ out, = capture_io do @app.instance_eval { display_tasks_and_comments } end
+
+ # Ensure the entire task name is output and we end up showing no description
+ assert_match(/rake #{task_name} # #{description}/, out)
+ end
+
+ def test_display_tasks_with_long_comments_to_a_non_tty_shows_entire_comment
+ @app.options.show_tasks = :tasks
+ @app.options.show_task_pattern = //
+ @app.tty_output = false
+ @app.last_description = "1234567890" * 8
+ @app.define_task(Rake::Task, "t")
+ out, = capture_io do @app.instance_eval { display_tasks_and_comments } end
+ assert_match(/^rake t/, out)
+ assert_match(/# #{@app.last_description}/, out)
+ end
+
+ def test_truncating_comments_to_a_non_tty
+ @app.terminal_columns = 80
+ @app.options.show_tasks = :tasks
+ @app.options.show_task_pattern = //
+ @app.tty_output = false
+ numbers = "1234567890" * 8
+ @app.last_description = numbers
+ @app.define_task(Rake::Task, "t")
+
+ out, = capture_io do @app.instance_eval { display_tasks_and_comments } end
+
+ assert_match(/^rake t/, out)
+ assert_match(/# #{numbers[0, 65]}\.\.\./, out)
+ end
+
+ def test_describe_tasks
+ @app.options.show_tasks = :describe
+ @app.options.show_task_pattern = //
+ @app.last_description = "COMMENT"
+ @app.define_task(Rake::Task, "t")
+ out, = capture_io do @app.instance_eval { display_tasks_and_comments } end
+ assert_match(/^rake t$/, out)
+ assert_match(/^ {4}COMMENT$/, out)
+ end
+
+ def test_show_lines
+ @app.options.show_tasks = :lines
+ @app.options.show_task_pattern = //
+ @app.last_description = "COMMENT"
+ @app.define_task(Rake::Task, "t")
+ @app['t'].locations << "HERE:1"
+ out, = capture_io do @app.instance_eval { display_tasks_and_comments } end
+ assert_match(/^rake t +[^:]+:\d+ *$/, out)
+ end
+
+ def test_finding_rakefile
+ rakefile_default
+
+ assert_match(/Rakefile/i, @app.instance_eval { have_rakefile })
+ end
+
+ def test_not_finding_rakefile
+ @app.instance_eval { @rakefiles = ['NEVER_FOUND'] }
+ assert(! @app.instance_eval do have_rakefile end)
+ assert_nil @app.rakefile
+ end
+
+ def test_load_rakefile
+ rakefile_unittest
+
+ @app.instance_eval do
+ handle_options
+ options.silent = true
+ load_rakefile
+ end
+
+ assert_equal "rakefile", @app.rakefile.downcase
+ assert_equal @tempdir, Dir.pwd
+ end
+
+ def test_load_rakefile_doesnt_print_rakefile_directory_from_same_dir
+ rakefile_unittest
+
+ _, err = capture_io do
+ @app.instance_eval do
+ # pretend we started from the unittest dir
+ @original_dir = File.expand_path(".")
+ raw_load_rakefile
+ end
+ end
+
+ assert_empty err
+ end
+
+ def test_load_rakefile_from_subdir
+ rakefile_unittest
+ Dir.chdir 'subdir'
+
+ @app.instance_eval do
+ handle_options
+ options.silent = true
+ load_rakefile
+ end
+
+ assert_equal "rakefile", @app.rakefile.downcase
+ assert_equal @tempdir, Dir.pwd
+ end
+
+ def test_load_rakefile_prints_rakefile_directory_from_subdir
+ rakefile_unittest
+ Dir.chdir 'subdir'
+
+ app = Rake::Application.new
+ app.options.rakelib = []
+
+ _, err = capture_io do
+ app.instance_eval do
+ raw_load_rakefile
+ end
+ end
+
+ assert_equal "(in #{@tempdir}\)\n", err
+ end
+
+ def test_load_rakefile_doesnt_print_rakefile_directory_from_subdir_if_silent
+ rakefile_unittest
+ Dir.chdir 'subdir'
+
+ _, err = capture_io do
+ @app.instance_eval do
+ handle_options
+ options.silent = true
+ raw_load_rakefile
+ end
+ end
+
+ assert_empty err
+ end
+
+ def test_load_rakefile_not_found
+ ARGV.clear
+ Dir.chdir @tempdir
+ ENV['RAKE_SYSTEM'] = 'not_exist'
+
+ @app.instance_eval do
+ handle_options
+ options.silent = true
+ end
+
+
+ ex = assert_raises(RuntimeError) do
+ @app.instance_eval do
+ raw_load_rakefile
+ end
+ end
+
+ assert_match(/no rakefile found/i, ex.message)
+ end
+
+ def test_load_from_system_rakefile
+ rake_system_dir
+
+ @app.instance_eval do
+ handle_options
+ options.silent = true
+ options.load_system = true
+ options.rakelib = []
+ load_rakefile
+ end
+
+ assert_equal @system_dir, @app.system_dir
+ assert_nil @app.rakefile
+ rescue SystemExit
+ flunk 'failed to load rakefile'
+ end
+
+ def test_load_from_calculated_system_rakefile
+ rakefile_default
+ def @app.standard_system_dir
+ "__STD_SYS_DIR__"
+ end
+
+ ENV['RAKE_SYSTEM'] = nil
+
+ @app.instance_eval do
+ handle_options
+ options.silent = true
+ options.load_system = true
+ options.rakelib = []
+ load_rakefile
+ end
+
+ assert_equal "__STD_SYS_DIR__", @app.system_dir
+ rescue SystemExit
+ flunk 'failed to find system rakefile'
+ end
+
+ def test_terminal_columns
+ old_rake_columns = ENV['RAKE_COLUMNS']
+
+ ENV['RAKE_COLUMNS'] = '42'
+
+ app = Rake::Application.new
+
+ assert_equal 42, app.terminal_columns
+ ensure
+ if old_rake_columns
+ ENV['RAKE_COLUMNS'].delete
+ else
+ ENV['RAKE_COLUMNS'] = old_rake_columns
+ end
+ end
+
+ def test_windows
+ assert ! (@app.windows? && @app.unix?)
+ end
+
+ def test_loading_imports
+ loader = util_loader
+
+ @app.instance_eval do
+ add_loader("dummy", loader)
+ add_import("x.dummy")
+ load_imports
+ end
+
+ # HACK no assertions
+ end
+
+ def test_building_imported_files_on_demand
+ loader = util_loader
+
+ @app.instance_eval do
+ intern(Rake::Task, "x.dummy").enhance do loader.make_dummy end
+ add_loader("dummy", loader)
+ add_import("x.dummy")
+ load_imports
+ end
+
+ # HACK no assertions
+ end
+
+ def test_handle_options_should_not_strip_options_from_argv
+ assert !@app.options.trace
+
+ valid_option = '--trace'
+ setup_command_line(valid_option)
+
+ @app.handle_options
+
+ assert ARGV.include?(valid_option)
+ assert @app.options.trace
+ end
+
+ def test_handle_options_trace_default_is_stderr
+ setup_command_line("--trace")
+
+ @app.handle_options
+
+ assert_equal STDERR, @app.options.trace_output
+ assert @app.options.trace
+ end
+
+ def test_handle_options_trace_overrides_to_stdout
+ setup_command_line("--trace=stdout")
+
+ @app.handle_options
+
+ assert_equal STDOUT, @app.options.trace_output
+ assert @app.options.trace
+ end
+
+ def test_handle_options_trace_does_not_eat_following_task_names
+ assert !@app.options.trace
+
+ setup_command_line("--trace", "sometask")
+
+ @app.handle_options
+ assert ARGV.include?("sometask")
+ assert @app.options.trace
+ end
+
+ def test_good_run
+ ran = false
+
+ ARGV << '--rakelib=""'
+
+ @app.options.silent = true
+
+ @app.instance_eval do
+ intern(Rake::Task, "default").enhance { ran = true }
+ end
+
+ rakefile_default
+
+ out, err = capture_io do
+ @app.run
+ end
+
+ assert ran
+ assert_empty err
+ assert_equal "DEFAULT\n", out
+ end
+
+ def test_display_task_run
+ ran = false
+ setup_command_line('-f', '-s', '--tasks', '--rakelib=""')
+ @app.last_description = "COMMENT"
+ @app.define_task(Rake::Task, "default")
+ out, = capture_io { @app.run }
+ assert @app.options.show_tasks
+ assert ! ran
+ assert_match(/rake default/, out)
+ assert_match(/# COMMENT/, out)
+ end
+
+ def test_display_prereqs
+ ran = false
+ setup_command_line('-f', '-s', '--prereqs', '--rakelib=""')
+ @app.last_description = "COMMENT"
+ t = @app.define_task(Rake::Task, "default")
+ t.enhance([:a, :b])
+ @app.define_task(Rake::Task, "a")
+ @app.define_task(Rake::Task, "b")
+ out, = capture_io { @app.run }
+ assert @app.options.show_prereqs
+ assert ! ran
+ assert_match(/rake a$/, out)
+ assert_match(/rake b$/, out)
+ assert_match(/rake default\n( *(a|b)\n){2}/m, out)
+ end
+
+ def test_bad_run
+ @app.intern(Rake::Task, "default").enhance { fail }
+ setup_command_line('-f', '-s', '--rakelib=""')
+ _, err = capture_io {
+ assert_raises(SystemExit){ @app.run }
+ }
+ assert_match(/see full trace/i, err)
+ ensure
+ ARGV.clear
+ end
+
+ def test_bad_run_with_trace
+ @app.intern(Rake::Task, "default").enhance { fail }
+ setup_command_line('-f', '-s', '-t')
+ _, err = capture_io {
+ assert_raises(SystemExit) { @app.run }
+ }
+ refute_match(/see full trace/i, err)
+ ensure
+ ARGV.clear
+ end
+
+ def test_bad_run_with_backtrace
+ @app.intern(Rake::Task, "default").enhance { fail }
+ setup_command_line('-f', '-s', '--backtrace')
+ _, err = capture_io {
+ assert_raises(SystemExit) {
+ @app.run
+ }
+ }
+ refute_match(/see full trace/, err)
+ ensure
+ ARGV.clear
+ end
+
+ CustomError = Class.new(RuntimeError)
+
+ def test_bad_run_includes_exception_name
+ @app.intern(Rake::Task, "default").enhance {
+ raise CustomError, "intentional"
+ }
+ setup_command_line('-f', '-s')
+ _, err = capture_io {
+ assert_raises(SystemExit) {
+ @app.run
+ }
+ }
+ assert_match(/CustomError: intentional/, err)
+ end
+
+ def test_rake_error_excludes_exception_name
+ @app.intern(Rake::Task, "default").enhance {
+ fail "intentional"
+ }
+ setup_command_line('-f', '-s')
+ _, err = capture_io {
+ assert_raises(SystemExit) {
+ @app.run
+ }
+ }
+ refute_match(/RuntimeError/, err)
+ assert_match(/intentional/, err)
+ end
+
+ def cause_supported?
+ ex = StandardError.new
+ ex.respond_to?(:cause)
+ end
+
+ def test_printing_original_exception_cause
+ custom_error = Class.new(StandardError)
+ @app.intern(Rake::Task, "default").enhance {
+ begin
+ raise custom_error, "Original Error"
+ rescue custom_error
+ raise custom_error, "Secondary Error"
+ end
+ }
+ setup_command_line('-f', '-s')
+ _ ,err = capture_io {
+ assert_raises(SystemExit) {
+ @app.run
+ }
+ }
+ if cause_supported?
+ assert_match(/Original Error/, err)
+ end
+ assert_match(/Secondary Error/, err)
+ ensure
+ ARGV.clear
+ end
+
+ def test_run_with_bad_options
+ @app.intern(Rake::Task, "default").enhance { fail }
+ setup_command_line('-f', '-s', '--xyzzy')
+ assert_raises(SystemExit) {
+ capture_io { @app.run }
+ }
+ ensure
+ ARGV.clear
+ end
+
+ def test_standard_exception_handling_invalid_option
+ out, err = capture_io do
+ e = assert_raises SystemExit do
+ @app.standard_exception_handling do
+ raise OptionParser::InvalidOption, 'blah'
+ end
+ end
+
+ assert_equal 1, e.status
+ end
+
+ assert_empty out
+ assert_equal "invalid option: blah\n", err
+ end
+
+ def test_standard_exception_handling_other
+ out, err = capture_io do
+ e = assert_raises SystemExit do
+ @app.standard_exception_handling do
+ raise 'blah'
+ end
+ end
+
+ assert_equal 1, e.status
+ end
+
+ assert_empty out
+ assert_match "rake aborted!\n", err
+ assert_match "blah\n", err
+ end
+
+ def test_standard_exception_handling_system_exit
+ out, err = capture_io do
+ e = assert_raises SystemExit do
+ @app.standard_exception_handling do
+ exit 0
+ end
+ end
+
+ assert_equal 0, e.status
+ end
+
+ assert_empty out
+ assert_empty err
+ end
+
+ def test_standard_exception_handling_system_exit_nonzero
+ out, err = capture_io do
+ e = assert_raises SystemExit do
+ @app.standard_exception_handling do
+ exit 5
+ end
+ end
+
+ assert_equal 5, e.status
+ end
+
+ assert_empty out
+ assert_empty err
+ end
+
+ def util_loader
+ loader = Object.new
+
+ loader.instance_variable_set :@load_called, false
+ def loader.load arg
+ raise ArgumentError, arg unless arg == 'x.dummy'
+ @load_called = true
+ end
+
+ loader.instance_variable_set :@make_dummy_called, false
+ def loader.make_dummy
+ @make_dummy_called = true
+ end
+
+ loader
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_application_options.rb b/jni/ruby/test/rake/test_rake_application_options.rb
new file mode 100644
index 0000000..37adfac
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_application_options.rb
@@ -0,0 +1,466 @@
+require File.expand_path('../helper', __FILE__)
+
+TESTING_REQUIRE = []
+
+class TestRakeApplicationOptions < Rake::TestCase
+
+ def setup
+ super
+
+ clear_argv
+ Rake::FileUtilsExt.verbose_flag = false
+ Rake::FileUtilsExt.nowrite_flag = false
+ TESTING_REQUIRE.clear
+ end
+
+ def teardown
+ clear_argv
+ Rake::FileUtilsExt.verbose_flag = false
+ Rake::FileUtilsExt.nowrite_flag = false
+
+ super
+ end
+
+ def clear_argv
+ ARGV.pop until ARGV.empty?
+ end
+
+ def test_default_options
+ opts = command_line
+ assert_nil opts.backtrace
+ assert_nil opts.dryrun
+ assert_nil opts.ignore_system
+ assert_nil opts.load_system
+ assert_nil opts.always_multitask
+ assert_nil opts.nosearch
+ assert_equal ['rakelib'], opts.rakelib
+ assert_nil opts.show_prereqs
+ assert_nil opts.show_task_pattern
+ assert_nil opts.show_tasks
+ assert_nil opts.silent
+ assert_nil opts.trace
+ assert_nil opts.thread_pool_size
+ assert_equal ['rakelib'], opts.rakelib
+ assert ! Rake::FileUtilsExt.verbose_flag
+ assert ! Rake::FileUtilsExt.nowrite_flag
+ end
+
+ def test_dry_run
+ flags('--dry-run', '-n') do |opts|
+ assert opts.dryrun
+ assert opts.trace
+ assert Rake::FileUtilsExt.verbose_flag
+ assert Rake::FileUtilsExt.nowrite_flag
+ end
+ end
+
+ def test_describe
+ flags('--describe') do |opts|
+ assert_equal :describe, opts.show_tasks
+ assert_equal(//.to_s, opts.show_task_pattern.to_s)
+ end
+ end
+
+ def test_describe_with_pattern
+ flags('--describe=X') do |opts|
+ assert_equal :describe, opts.show_tasks
+ assert_equal(/X/.to_s, opts.show_task_pattern.to_s)
+ end
+ end
+
+ def test_execute
+ $xyzzy = 0
+ flags('--execute=$xyzzy=1', '-e $xyzzy=1') do |opts|
+ assert_equal 1, $xyzzy
+ assert_equal :exit, @exit
+ $xyzzy = 0
+ end
+ end
+
+ def test_execute_and_continue
+ $xyzzy = 0
+ flags('--execute-continue=$xyzzy=1', '-E $xyzzy=1') do |opts|
+ assert_equal 1, $xyzzy
+ refute_equal :exit, @exit
+ $xyzzy = 0
+ end
+ end
+
+ def test_execute_and_print
+ $xyzzy = 0
+ out, = capture_io do
+ flags('--execute-print=$xyzzy="pugh"', '-p $xyzzy="pugh"') do |opts|
+ assert_equal 'pugh', $xyzzy
+ assert_equal :exit, @exit
+ $xyzzy = 0
+ end
+ end
+
+ assert_match(/^pugh$/, out)
+ end
+
+ def test_help
+ out, = capture_io do
+ flags '--help', '-H', '-h'
+ end
+
+ assert_match(/\Arake/, out)
+ assert_match(/\boptions\b/, out)
+ assert_match(/\btargets\b/, out)
+ assert_equal :exit, @exit
+ end
+
+ def test_jobs
+ flags([]) do |opts|
+ assert_nil opts.thread_pool_size
+ end
+ flags(['--jobs', '0'], ['-j', '0']) do |opts|
+ assert_equal 0, opts.thread_pool_size
+ end
+ flags(['--jobs', '1'], ['-j', '1']) do |opts|
+ assert_equal 0, opts.thread_pool_size
+ end
+ flags(['--jobs', '4'], ['-j', '4']) do |opts|
+ assert_equal 3, opts.thread_pool_size
+ end
+ flags(['--jobs', 'asdas'], ['-j', 'asdas']) do |opts|
+ assert_equal Rake.suggested_thread_count-1, opts.thread_pool_size
+ end
+ flags('--jobs', '-j') do |opts|
+ assert opts.thread_pool_size > 1_000_000, "thread pool size should be huge (was #{opts.thread_pool_size})"
+ end
+ end
+
+ def test_libdir
+ flags(['--libdir', 'xx'], ['-I', 'xx'], ['-Ixx']) do |opts|
+ $:.include?('xx')
+ end
+ ensure
+ $:.delete('xx')
+ end
+
+ def test_multitask
+ flags('--multitask', '-m') do |opts|
+ assert_equal opts.always_multitask, true
+ end
+ end
+
+ def test_rakefile
+ flags(['--rakefile', 'RF'], ['--rakefile=RF'], ['-f', 'RF'], ['-fRF']) do |opts|
+ assert_equal ['RF'], @app.instance_eval { @rakefiles }
+ end
+ end
+
+ def test_rakelib
+ dirs = %w(A B C).join(File::PATH_SEPARATOR)
+ flags(
+ ['--rakelibdir', dirs],
+ ["--rakelibdir=#{dirs}"],
+ ['-R', dirs],
+ ["-R#{dirs}"]) do |opts|
+ assert_equal ['A', 'B', 'C'], opts.rakelib
+ end
+ end
+
+ def test_require
+ $LOAD_PATH.unshift @tempdir
+
+ open 'reqfile.rb', 'w' do |io| io << 'TESTING_REQUIRE << 1' end
+ open 'reqfile2.rb', 'w' do |io| io << 'TESTING_REQUIRE << 2' end
+ open 'reqfile3.rake', 'w' do |io| io << 'TESTING_REQUIRE << 3' end
+
+ flags(['--require', 'reqfile'], '-rreqfile2', '-rreqfile3')
+
+ assert_includes TESTING_REQUIRE, 1
+ assert_includes TESTING_REQUIRE, 2
+ assert_includes TESTING_REQUIRE, 3
+
+ assert_equal 3, TESTING_REQUIRE.size
+ ensure
+ $LOAD_PATH.delete @tempdir
+ end
+
+ def test_missing_require
+ ex = assert_raises(LoadError) do
+ flags(['--require', 'test/missing']) do |opts|
+ end
+ end
+ assert_match(/such file/, ex.message)
+ assert_match(/test\/missing/, ex.message)
+ end
+
+ def test_prereqs
+ flags('--prereqs', '-P') do |opts|
+ assert opts.show_prereqs
+ end
+ end
+
+ def test_quiet
+ Rake::FileUtilsExt.verbose_flag = true
+ flags('--quiet', '-q') do |opts|
+ assert ! Rake::FileUtilsExt.verbose_flag, "verbose flag shoud be false"
+ assert ! opts.silent, "should not be silent"
+ end
+ end
+
+ def test_no_search
+ flags('--nosearch', '--no-search', '-N') do |opts|
+ assert opts.nosearch
+ end
+ end
+
+ def test_silent
+ Rake::FileUtilsExt.verbose_flag = true
+ flags('--silent', '-s') do |opts|
+ assert ! Rake::FileUtilsExt.verbose_flag, "verbose flag should be false"
+ assert opts.silent, "should be silent"
+ end
+ end
+
+ def test_system
+ flags('--system', '-g') do |opts|
+ assert opts.load_system
+ end
+ end
+
+ def test_no_system
+ flags('--no-system', '-G') do |opts|
+ assert opts.ignore_system
+ end
+ end
+
+ def test_trace
+ flags('--trace', '-t') do |opts|
+ assert opts.trace, "should enable trace option"
+ assert opts.backtrace, "should enabled backtrace option"
+ assert_equal $stderr, opts.trace_output
+ assert Rake::FileUtilsExt.verbose_flag
+ assert ! Rake::FileUtilsExt.nowrite_flag
+ end
+ end
+
+ def test_trace_with_stdout
+ flags('--trace=stdout', '-tstdout') do |opts|
+ assert opts.trace, "should enable trace option"
+ assert opts.backtrace, "should enabled backtrace option"
+ assert_equal $stdout, opts.trace_output
+ assert Rake::FileUtilsExt.verbose_flag
+ assert ! Rake::FileUtilsExt.nowrite_flag
+ end
+ end
+
+ def test_trace_with_stderr
+ flags('--trace=stderr', '-tstderr') do |opts|
+ assert opts.trace, "should enable trace option"
+ assert opts.backtrace, "should enabled backtrace option"
+ assert_equal $stderr, opts.trace_output
+ assert Rake::FileUtilsExt.verbose_flag
+ assert ! Rake::FileUtilsExt.nowrite_flag
+ end
+ end
+
+ def test_trace_with_error
+ ex = assert_raises(Rake::CommandLineOptionError) do
+ flags('--trace=xyzzy') do |opts| end
+ end
+ assert_match(/un(known|recognized).*\btrace\b.*xyzzy/i, ex.message)
+ end
+
+ def test_trace_with_following_task_name
+ flags(['--trace', 'taskname'], ['-t', 'taskname']) do |opts|
+ assert opts.trace, "should enable trace option"
+ assert opts.backtrace, "should enabled backtrace option"
+ assert_equal $stderr, opts.trace_output
+ assert Rake::FileUtilsExt.verbose_flag
+ assert_equal ['taskname'], @app.top_level_tasks
+ end
+ end
+
+ def test_backtrace
+ flags('--backtrace') do |opts|
+ assert opts.backtrace, "should enable backtrace option"
+ assert_equal $stderr, opts.trace_output
+ assert ! opts.trace, "should not enable trace option"
+ end
+ end
+
+ def test_backtrace_with_stdout
+ flags('--backtrace=stdout') do |opts|
+ assert opts.backtrace, "should enable backtrace option"
+ assert_equal $stdout, opts.trace_output
+ assert ! opts.trace, "should not enable trace option"
+ end
+ end
+
+ def test_backtrace_with_stderr
+ flags('--backtrace=stderr') do |opts|
+ assert opts.backtrace, "should enable backtrace option"
+ assert_equal $stderr, opts.trace_output
+ assert ! opts.trace, "should not enable trace option"
+ end
+ end
+
+ def test_backtrace_with_error
+ ex = assert_raises(Rake::CommandLineOptionError) do
+ flags('--backtrace=xyzzy') do |opts| end
+ end
+ assert_match(/un(known|recognized).*\bbacktrace\b.*xyzzy/i, ex.message)
+ end
+
+ def test_backtrace_with_following_task_name
+ flags(['--backtrace', 'taskname']) do |opts|
+ assert ! opts.trace, "should enable trace option"
+ assert opts.backtrace, "should enabled backtrace option"
+ assert_equal $stderr, opts.trace_output
+ assert_equal ['taskname'], @app.top_level_tasks
+ end
+ end
+
+ def test_trace_rules
+ flags('--rules') do |opts|
+ assert opts.trace_rules
+ end
+ end
+
+ def test_tasks
+ flags('--tasks', '-T') do |opts|
+ assert_equal :tasks, opts.show_tasks
+ assert_equal(//.to_s, opts.show_task_pattern.to_s)
+ assert_equal nil, opts.show_all_tasks
+ end
+ flags(['--tasks', 'xyz'], ['-Txyz']) do |opts|
+ assert_equal :tasks, opts.show_tasks
+ assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s)
+ assert_equal nil, opts.show_all_tasks
+ end
+ flags(['--tasks', 'xyz', '--comments']) do |opts|
+ assert_equal :tasks, opts.show_tasks
+ assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s)
+ assert_equal false, opts.show_all_tasks
+ end
+ end
+
+ def test_where
+ flags('--where', '-W') do |opts|
+ assert_equal :lines, opts.show_tasks
+ assert_equal(//.to_s, opts.show_task_pattern.to_s)
+ assert_equal true, opts.show_all_tasks
+ end
+ flags(['--where', 'xyz'], ['-Wxyz']) do |opts|
+ assert_equal :lines, opts.show_tasks
+ assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s)
+ assert_equal true, opts.show_all_tasks
+ end
+ flags(['--where', 'xyz', '--comments'], ['-Wxyz', '--comments']) do |opts|
+ assert_equal :lines, opts.show_tasks
+ assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s)
+ assert_equal false, opts.show_all_tasks
+ end
+ end
+
+ def test_no_deprecated_messages
+ flags('--no-deprecation-warnings', '-X') do |opts|
+ assert opts.ignore_deprecate
+ end
+ end
+
+ def test_verbose
+ capture_io do
+ flags('--verbose', '-v') do |opts|
+ assert Rake::FileUtilsExt.verbose_flag, "verbose should be true"
+ assert ! opts.silent, "opts should not be silent"
+ end
+ end
+ end
+
+ def test_version
+ out, _ = capture_io do
+ flags '--version', '-V'
+ end
+
+ assert_match(/\bversion\b/, out)
+ assert_match(/\b#{RAKEVERSION}\b/, out)
+ assert_equal :exit, @exit
+ end
+
+ def test_bad_option
+ _, err = capture_io do
+ ex = assert_raises(OptionParser::InvalidOption) do
+ flags('--bad-option')
+ end
+
+ if ex.message =~ /^While/ # Ruby 1.9 error message
+ assert_match(/while parsing/i, ex.message)
+ else # Ruby 1.8 error message
+ assert_match(/(invalid|unrecognized) option/i, ex.message)
+ assert_match(/--bad-option/, ex.message)
+ end
+ end
+
+ assert_equal '', err
+ end
+
+ def test_task_collection
+ command_line("a", "b")
+ assert_equal ["a", "b"], @tasks.sort
+ end
+
+ def test_default_task_collection
+ command_line()
+ assert_equal ["default"], @tasks
+ end
+
+ def test_environment_definition
+ ENV.delete('TESTKEY')
+ command_line("TESTKEY=12")
+ assert_equal '12', ENV['TESTKEY']
+ end
+
+ def test_multiline_environment_definition
+ ENV.delete('TESTKEY')
+ command_line("TESTKEY=a\nb\n")
+ assert_equal "a\nb\n", ENV['TESTKEY']
+ end
+
+ def test_environment_and_tasks_together
+ ENV.delete('TESTKEY')
+ command_line("a", "b", "TESTKEY=12")
+ assert_equal ["a", "b"], @tasks.sort
+ assert_equal '12', ENV['TESTKEY']
+ end
+
+ def test_rake_explicit_task_library
+ Rake.add_rakelib 'app/task', 'other'
+
+ libs = Rake.application.options.rakelib
+
+ assert libs.include?("app/task")
+ assert libs.include?("other")
+ end
+
+ private
+
+ def flags(*sets)
+ sets.each do |set|
+ ARGV.clear
+
+ @exit = catch(:system_exit) { command_line(*set) }
+
+ yield(@app.options) if block_given?
+ end
+ end
+
+ def command_line(*options)
+ options.each do |opt| ARGV << opt end
+ @app = Rake::Application.new
+ def @app.exit(*args)
+ throw :system_exit, :exit
+ end
+ @app.instance_eval do
+ args = handle_options
+ collect_command_line_tasks(args)
+ end
+ @tasks = @app.top_level_tasks
+ @app.options
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_backtrace.rb b/jni/ruby/test/rake/test_rake_backtrace.rb
new file mode 100644
index 0000000..78eaa8d
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_backtrace.rb
@@ -0,0 +1,119 @@
+require File.expand_path('../helper', __FILE__)
+require 'open3'
+
+class TestBacktraceSuppression < Rake::TestCase
+ def test_bin_rake_suppressed
+ paths = ["something/bin/rake:12"]
+
+ actual = Rake::Backtrace.collapse(paths)
+
+ assert_equal [], actual
+ end
+
+ def test_system_dir_suppressed
+ path = RbConfig::CONFIG['rubylibprefix']
+ skip if path.nil?
+ path = File.expand_path path
+
+ paths = [path + ":12"]
+
+ actual = Rake::Backtrace.collapse(paths)
+
+ assert_equal [], actual
+ end
+
+ def test_near_system_dir_isnt_suppressed
+ path = RbConfig::CONFIG['rubylibprefix']
+ skip if path.nil?
+ path = File.expand_path path
+
+ paths = [" " + path + ":12"]
+
+ actual = Rake::Backtrace.collapse(paths)
+
+ assert_equal paths, actual
+ end
+end
+
+class TestRakeBacktrace < Rake::TestCase
+ include RubyRunner
+
+ def setup
+ super
+
+ skip 'tmpdir is suppressed in backtrace' if
+ Rake::Backtrace::SUPPRESS_PATTERN =~ Dir.pwd
+ end
+
+ def invoke(*args)
+ rake(*args)
+ @err
+ end
+
+ def test_single_collapse
+ rakefile %q{
+ task :foo do
+ raise "foooo!"
+ end
+ }
+
+ lines = invoke("foo").split("\n")
+
+ assert_equal "rake aborted!", lines[0]
+ assert_equal "foooo!", lines[1]
+ assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:3!i, lines
+ assert_something_matches %r!\ATasks:!, lines
+ end
+
+ def test_multi_collapse
+ rakefile %q{
+ task :foo do
+ Rake.application.invoke_task(:bar)
+ end
+ task :bar do
+ raise "barrr!"
+ end
+ }
+
+ lines = invoke("foo").split("\n")
+
+ assert_equal "rake aborted!", lines[0]
+ assert_equal "barrr!", lines[1]
+ assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:6!i, lines
+ assert_something_matches %r!\A#{Regexp.quote Dir.pwd}/Rakefile:3!i, lines
+ assert_something_matches %r!\ATasks:!, lines
+ end
+
+ def test_suppress_option
+ rakefile %q{
+ task :baz do
+ raise "bazzz!"
+ end
+ }
+
+ lines = invoke("baz").split("\n")
+ assert_equal "rake aborted!", lines[0]
+ assert_equal "bazzz!", lines[1]
+ assert_something_matches %r!Rakefile!i, lines
+
+ lines = invoke("--suppress-backtrace", ".ak.file", "baz").split("\n")
+ assert_equal "rake aborted!", lines[0]
+ assert_equal "bazzz!", lines[1]
+ refute_match %r!Rakefile!i, lines[2]
+ end
+
+ private
+
+ # Assert that the pattern matches at least one line in +lines+.
+ def assert_something_matches(pattern, lines)
+ lines.each do |ln|
+ if pattern =~ ln
+ assert_match pattern, ln
+ return
+ end
+ end
+ flunk "expected #{pattern.inspect} to match something in:\n" +
+ "#{lines.join("\n ")}"
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_clean.rb b/jni/ruby/test/rake/test_rake_clean.rb
new file mode 100644
index 0000000..0bce7bc
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_clean.rb
@@ -0,0 +1,61 @@
+require File.expand_path('../helper', __FILE__)
+require 'rake/clean'
+
+class TestRakeClean < Rake::TestCase
+ def test_clean
+ load 'rake/clean.rb', true
+
+ assert Rake::Task['clean'], "Should define clean"
+ assert Rake::Task['clobber'], "Should define clobber"
+ assert Rake::Task['clobber'].prerequisites.include?("clean"),
+ "Clobber should require clean"
+ end
+
+ def test_cleanup
+ file_name = create_undeletable_file
+
+ out, _ = capture_io do
+ Rake::Cleaner.cleanup(file_name, :verbose => false)
+ end
+ assert_match(/failed to remove/i, out)
+
+ ensure
+ remove_undeletable_file
+ end
+
+ def test_cleanup_ignores_missing_files
+ file_name = File.join(@tempdir, "missing_directory", "no_such_file")
+
+ out, _ = capture_io do
+ Rake::Cleaner.cleanup(file_name, :verbose => false)
+ end
+ refute_match(/failed to remove/i, out)
+ end
+
+ private
+
+ def create_undeletable_file
+ dir_name = File.join(@tempdir, "deletedir")
+ file_name = File.join(dir_name, "deleteme")
+ FileUtils.mkdir(dir_name)
+ FileUtils.touch(file_name)
+ FileUtils.chmod(0, file_name)
+ FileUtils.chmod(0, dir_name)
+ begin
+ FileUtils.chmod(0644, file_name)
+ rescue
+ file_name
+ else
+ skip "Permission to delete files is different on thie system"
+ end
+ end
+
+ def remove_undeletable_file
+ dir_name = File.join(@tempdir, "deletedir")
+ file_name = File.join(dir_name, "deleteme")
+ FileUtils.chmod(0777, dir_name)
+ FileUtils.chmod(0777, file_name)
+ Rake::Cleaner.cleanup(file_name, :verbose => false)
+ Rake::Cleaner.cleanup(dir_name, :verbose => false)
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_cpu_counter.rb b/jni/ruby/test/rake/test_rake_cpu_counter.rb
new file mode 100644
index 0000000..87d0601
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_cpu_counter.rb
@@ -0,0 +1,68 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeCpuCounter < Rake::TestCase
+
+ def setup
+ super
+
+ @cpu_counter = Rake::CpuCounter.new
+ end
+
+ def test_count
+ num = @cpu_counter.count
+ skip 'cannot count CPU' if num == nil
+ assert_kind_of Numeric, num
+ assert_operator num, :>=, 1
+ end
+
+ def test_count_with_default_nil
+ def @cpu_counter.count; nil; end
+ assert_equal(8, @cpu_counter.count_with_default(8))
+ assert_equal(4, @cpu_counter.count_with_default)
+ end
+
+ def test_count_with_default_raise
+ def @cpu_counter.count; raise; end
+ assert_equal(8, @cpu_counter.count_with_default(8))
+ assert_equal(4, @cpu_counter.count_with_default)
+ end
+
+ class TestClassMethod < Rake::TestCase
+ def setup
+ super
+
+ @klass = Class.new(Rake::CpuCounter)
+ end
+
+ def test_count
+ @klass.class_eval do
+ def count; 8; end
+ end
+ assert_equal(8, @klass.count)
+ end
+
+ def test_count_nil
+ counted = false
+ @klass.class_eval do
+ define_method(:count) do
+ counted = true
+ nil
+ end
+ end
+ assert_equal(4, @klass.count)
+ assert_equal(true, counted)
+ end
+
+ def test_count_raise
+ counted = false
+ @klass.class_eval do
+ define_method(:count) do
+ counted = true
+ raise
+ end
+ end
+ assert_equal(4, @klass.count)
+ assert_equal(true, counted)
+ end
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_definitions.rb b/jni/ruby/test/rake/test_rake_definitions.rb
new file mode 100644
index 0000000..ee474cb
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_definitions.rb
@@ -0,0 +1,84 @@
+require File.expand_path('../helper', __FILE__)
+require 'fileutils'
+
+class TestRakeDefinitions < Rake::TestCase
+ include Rake
+
+ EXISTINGFILE = "existing"
+
+ def setup
+ super
+
+ Task.clear
+ end
+
+ def test_task
+ done = false
+ task :one => [:two] do done = true end
+ task :two
+ task :three => [:one, :two]
+ check_tasks(:one, :two, :three)
+ assert done, "Should be done"
+ end
+
+ def test_file_task
+ done = false
+ file "one" => "two" do done = true end
+ file "two"
+ file "three" => ["one", "two"]
+ check_tasks("one", "two", "three")
+ assert done, "Should be done"
+ end
+
+ def check_tasks(n1, n2, n3)
+ t = Task[n1]
+ assert Task === t, "Should be a Task"
+ assert_equal n1.to_s, t.name
+ assert_equal [n2.to_s], t.prerequisites.map { |n| n.to_s }
+ t.invoke
+ t2 = Task[n2]
+ assert_equal FileList[], t2.prerequisites
+ t3 = Task[n3]
+ assert_equal [n1.to_s, n2.to_s], t3.prerequisites.map { |n| n.to_s }
+ end
+
+ def test_incremental_definitions
+ runs = []
+ task :t1 => [:t2] do runs << "A"; 4321 end
+ task :t1 => [:t3] do runs << "B"; 1234 end
+ task :t1 => [:t3]
+ task :t2
+ task :t3
+ Task[:t1].invoke
+ assert_equal ["A", "B"], runs
+ assert_equal ["t2", "t3"], Task[:t1].prerequisites
+ end
+
+ def test_missing_dependencies
+ task :x => ["missing"]
+ assert_raises(RuntimeError) { Task[:x].invoke }
+ end
+
+ def test_falsey_dependencies
+ task :x => nil
+ assert_equal [], Task[:x].prerequisites
+ end
+
+ def test_implicit_file_dependencies
+ runs = []
+ create_existing_file
+ task :y => [EXISTINGFILE] do |t| runs << t.name end
+ Task[:y].invoke
+ assert_equal runs, ['y']
+ end
+
+ private # ----------------------------------------------------------
+
+ def create_existing_file
+ Dir.mkdir File.dirname(EXISTINGFILE) unless
+ File.exist?(File.dirname(EXISTINGFILE))
+ open(EXISTINGFILE, "w") do |f| f.puts "HI" end unless
+ File.exist?(EXISTINGFILE)
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_directory_task.rb b/jni/ruby/test/rake/test_rake_directory_task.rb
new file mode 100644
index 0000000..0014d1c
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_directory_task.rb
@@ -0,0 +1,76 @@
+require File.expand_path('../helper', __FILE__)
+require 'fileutils'
+require 'pathname'
+
+class TestRakeDirectoryTask < Rake::TestCase
+ include Rake
+
+ def test_directory
+ desc "DESC"
+
+ directory "a/b/c"
+
+ assert_equal FileCreationTask, Task["a"].class
+ assert_equal FileCreationTask, Task["a/b"].class
+ assert_equal FileCreationTask, Task["a/b/c"].class
+
+ assert_nil Task["a"].comment
+ assert_nil Task["a/b"].comment
+ assert_equal "DESC", Task["a/b/c"].comment
+
+ verbose(false) {
+ Task['a/b'].invoke
+ }
+
+ assert File.exist?("a/b")
+ refute File.exist?("a/b/c")
+ end
+
+ def test_directory_colon
+ directory "a:b"
+
+ assert_equal FileCreationTask, Task['a:b'].class
+ end unless Rake::Win32.windows?
+
+ if Rake::Win32.windows?
+ def test_directory_win32
+ desc "WIN32 DESC"
+ directory 'c:/a/b/c'
+ assert_equal FileTask, Task['c:'].class
+ assert_equal FileCreationTask, Task['c:/a'].class
+ assert_equal FileCreationTask, Task['c:/a/b'].class
+ assert_equal FileCreationTask, Task['c:/a/b/c'].class
+ assert_nil Task['c:/'].comment
+ assert_equal "WIN32 DESC", Task['c:/a/b/c'].comment
+ assert_nil Task['c:/a/b'].comment
+ end
+ end
+
+ def test_can_use_blocks
+ runlist = []
+
+ t1 = directory("a/b/c" => :t2) { |t| runlist << t.name }
+ task(:t2) { |t| runlist << t.name }
+
+ verbose(false) {
+ t1.invoke
+ }
+
+ assert_equal Task["a/b/c"], t1
+ assert_equal FileCreationTask, Task["a/b/c"].class
+ assert_equal ["t2", "a/b/c"], runlist
+ assert File.directory?("a/b/c")
+ end
+
+ def test_can_use_pathname
+ directory Pathname.new "a/b/c"
+
+ assert_equal FileCreationTask, Task["a/b/c"].class
+
+ verbose(false) {
+ Task['a/b/c'].invoke
+ }
+
+ assert File.directory?("a/b/c")
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_dsl.rb b/jni/ruby/test/rake/test_rake_dsl.rb
new file mode 100644
index 0000000..ad52f76
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_dsl.rb
@@ -0,0 +1,40 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeDsl < Rake::TestCase
+
+ def setup
+ super
+ Rake::Task.clear
+ end
+
+ def test_namespace_command
+ namespace "n" do
+ task "t"
+ end
+ refute_nil Rake::Task["n:t"]
+ end
+
+ def test_namespace_command_with_bad_name
+ ex = assert_raises(ArgumentError) do
+ namespace 1 do end
+ end
+ assert_match(/string/i, ex.message)
+ assert_match(/symbol/i, ex.message)
+ end
+
+ def test_namespace_command_with_a_string_like_object
+ name = Object.new
+ def name.to_str
+ "bob"
+ end
+ namespace name do
+ task "t"
+ end
+ refute_nil Rake::Task["bob:t"]
+ end
+
+ def test_no_commands_constant
+ assert ! defined?(Commands), "should not define Commands"
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_early_time.rb b/jni/ruby/test/rake/test_rake_early_time.rb
new file mode 100644
index 0000000..18c4dad
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_early_time.rb
@@ -0,0 +1,31 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeEarlyTime < Rake::TestCase
+ def test_create
+ early = Rake::EarlyTime.instance
+ assert early <= Time.now
+ assert early < Time.now
+ assert early != Time.now
+ assert Time.now > early
+ assert Time.now >= early
+ assert Time.now != early
+ end
+
+ def test_equality
+ early = Rake::EarlyTime.instance
+ assert_equal early, early, "two early times should be equal"
+ end
+
+ def test_original_time_compare_is_not_messed_up
+ t1 = Time.mktime(1970, 1, 1, 0, 0, 0)
+ t2 = Time.now
+ assert t1 < t2
+ assert t2 > t1
+ assert t1 == t1
+ assert t2 == t2
+ end
+
+ def test_to_s
+ assert_equal "<EARLY TIME>", Rake::EARLY.to_s
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_extension.rb b/jni/ruby/test/rake/test_rake_extension.rb
new file mode 100644
index 0000000..18d55f1
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_extension.rb
@@ -0,0 +1,59 @@
+require File.expand_path('../helper', __FILE__)
+require 'stringio'
+
+class TestRakeExtension < Rake::TestCase
+
+ module Redirect
+ def error_redirect
+ old_err = $stderr
+ result = StringIO.new
+ $stderr = result
+ yield
+ result
+ ensure
+ $stderr = old_err
+ end
+ end
+
+ class Sample
+ extend Redirect
+
+ def duplicate_method
+ :original
+ end
+
+ OK_ERRS = error_redirect do
+ rake_extension("a") do
+ def ok_method
+ end
+ end
+ end
+
+
+ DUP_ERRS = error_redirect do
+ rake_extension("duplicate_method") do
+ def duplicate_method
+ :override
+ end
+ end
+ end
+ end
+
+ def test_methods_actually_exist
+ sample = Sample.new
+ sample.ok_method
+ sample.duplicate_method
+ end
+
+ def test_no_warning_when_defining_ok_method
+ assert_equal "", Sample::OK_ERRS.string
+ end
+
+ def test_extension_complains_when_a_method_that_is_present
+ assert_match(/warning:/i, Sample::DUP_ERRS.string)
+ assert_match(/already exists/i, Sample::DUP_ERRS.string)
+ assert_match(/duplicate_method/i, Sample::DUP_ERRS.string)
+ assert_equal :original, Sample.new.duplicate_method
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_file_creation_task.rb b/jni/ruby/test/rake/test_rake_file_creation_task.rb
new file mode 100644
index 0000000..d8dcd96
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_file_creation_task.rb
@@ -0,0 +1,56 @@
+require File.expand_path('../helper', __FILE__)
+require 'fileutils'
+
+######################################################################
+class TestRakeFileCreationTask < Rake::TestCase
+ include Rake
+ include Rake::DSL
+
+ DUMMY_DIR = 'dummy_dir'
+
+ def setup
+ super
+
+ Task.clear
+ end
+
+ def test_file_needed
+ create_dir DUMMY_DIR
+ fc_task = Task[DUMMY_DIR]
+ assert_equal DUMMY_DIR, fc_task.name
+ FileUtils.rm_rf fc_task.name
+ assert fc_task.needed?, "file should be needed"
+ FileUtils.mkdir fc_task.name
+ assert_equal nil, fc_task.prerequisites.map { |n| Task[n].timestamp }.max
+ assert ! fc_task.needed?, "file should not be needed"
+ end
+
+ def test_directory
+ directory DUMMY_DIR
+ fc_task = Task[DUMMY_DIR]
+ assert_equal DUMMY_DIR, fc_task.name
+ assert FileCreationTask === fc_task
+ end
+
+ def test_no_retriggers_on_filecreate_task
+ create_timed_files(OLDFILE, NEWFILE)
+ t1 = Rake.application.intern(FileCreationTask, OLDFILE).enhance([NEWFILE])
+ t2 = Rake.application.intern(FileCreationTask, NEWFILE)
+ assert ! t2.needed?, "Should not need to build new file"
+ assert ! t1.needed?, "Should not need to rebuild old file because of new"
+ end
+
+ def test_no_retriggers_on_file_task
+ create_timed_files(OLDFILE, NEWFILE)
+ t1 = Rake.application.intern(FileCreationTask, OLDFILE).enhance([NEWFILE])
+ t2 = Rake.application.intern(FileCreationTask, NEWFILE)
+ assert ! t2.needed?, "Should not need to build new file"
+ assert ! t1.needed?, "Should not need to rebuild old file because of new"
+ end
+
+ def test_very_early_timestamp
+ t1 = Rake.application.intern(FileCreationTask, OLDFILE)
+ assert t1.timestamp < Time.now
+ assert t1.timestamp < Time.now - 1_000_000
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_file_list.rb b/jni/ruby/test/rake/test_rake_file_list.rb
new file mode 100644
index 0000000..c1b4c92
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_file_list.rb
@@ -0,0 +1,655 @@
+require File.expand_path('../helper', __FILE__)
+require 'pathname'
+
+class TestRakeFileList < Rake::TestCase
+ FileList = Rake::FileList
+
+ def setup
+ super
+
+ FileUtils.mkdir "CVS" rescue nil
+ FileUtils.mkdir ".svn" rescue nil
+ @cdir = "cfiles"
+ FileUtils.mkdir @cdir rescue nil
+ FileUtils.touch ".dummy"
+ FileUtils.touch "x.bak"
+ FileUtils.touch "x~"
+ FileUtils.touch "core"
+ FileUtils.touch "x.c"
+ FileUtils.touch "xyz.c"
+ FileUtils.touch "abc.c"
+ FileUtils.touch "abc.h"
+ FileUtils.touch "abc.x"
+ FileUtils.touch "existing"
+
+ open 'xyzzy.txt', 'w' do |io|
+ io.puts 'x'
+ io.puts 'XYZZY'
+ end
+
+ end
+
+ def test_delegating_methods_do_not_include_to_a_or_to_ary
+ assert ! FileList::DELEGATING_METHODS.include?("to_a"), "should not include to_a"
+ assert ! FileList::DELEGATING_METHODS.include?(:to_a), "should not include to_a"
+ assert ! FileList::DELEGATING_METHODS.include?("to_ary"), "should not include to_ary"
+ assert ! FileList::DELEGATING_METHODS.include?(:to_ary), "should not include to_ary"
+ end
+
+ def test_create
+ fl = FileList.new
+ assert_equal 0, fl.size
+ end
+
+ def test_create_with_args
+ fl = FileList.new("*.c", "x")
+ assert_equal ["abc.c", "x.c", "xyz.c", "x"].sort,
+ fl.sort
+ end
+
+ def test_create_with_pathname
+ fl = FileList.new(Pathname.new("*.c"))
+ assert_equal ["abc.c", "x.c", "xyz.c"].sort,
+ fl.sort
+ end
+
+ def test_create_with_block
+ fl = FileList.new { |f| f.include("x") }
+ assert_equal ["x"], fl.resolve
+ end
+
+ def test_create_with_brackets
+ fl = FileList["*.c", "x"]
+ assert_equal ["abc.c", "x.c", "xyz.c", "x"].sort,
+ fl.sort
+ end
+
+ def test_create_with_brackets_and_filelist
+ fl = FileList[FileList["*.c", "x"]]
+ assert_equal ["abc.c", "x.c", "xyz.c", "x"].sort,
+ fl.sort
+ end
+
+ def test_include_with_another_array
+ fl = FileList.new.include(["x", "y", "z"])
+ assert_equal ["x", "y", "z"].sort, fl.sort
+ end
+
+ def test_include_with_another_filelist
+ fl = FileList.new.include(FileList["*.c", "x"])
+ assert_equal ["abc.c", "x.c", "xyz.c", "x"].sort,
+ fl.sort
+ end
+
+ def test_include_with_pathname
+ fl = FileList.new.include(Pathname.new("*.c"))
+ assert_equal ["abc.c", "x.c", "xyz.c"].sort,
+ fl.sort
+ end
+
+ def test_append
+ fl = FileList.new
+ fl << "a.rb" << "b.rb"
+ assert_equal ['a.rb', 'b.rb'], fl
+ end
+
+ def test_append_pathname
+ fl = FileList.new
+ fl << Pathname.new("a.rb")
+ assert_equal ['a.rb'], fl
+ end
+
+ def test_add_many
+ fl = FileList.new
+ fl.include %w(a d c)
+ fl.include('x', 'y')
+ assert_equal ['a', 'd', 'c', 'x', 'y'], fl
+ assert_equal ['a', 'd', 'c', 'x', 'y'], fl.resolve
+ end
+
+ def test_add_return
+ f = FileList.new
+ g = f << "x"
+ assert_equal f.object_id, g.object_id
+ h = f.include("y")
+ assert_equal f.object_id, h.object_id
+ end
+
+ def test_match
+ fl = FileList.new
+ fl.include '*.c'
+
+ assert_equal %w[abc.c x.c xyz.c], fl.sort
+ end
+
+ def test_add_matching
+ fl = FileList.new
+ fl << "a.java"
+ fl.include '*.c'
+
+ assert_equal %w[a.java abc.c x.c xyz.c], fl.sort
+ end
+
+ def test_multiple_patterns
+ fl = FileList.new
+ fl.include('*.z', '*foo*')
+
+ assert_equal [], fl
+
+ fl.include('*.c', '*xist*')
+ assert_equal %w[x.c xyz.c abc.c existing].sort, fl.sort
+ end
+
+ def test_square_bracket_pattern
+ fl = FileList.new
+ fl.include("abc.[ch]")
+ assert fl.size == 2
+ assert fl.include?("abc.c")
+ assert fl.include?("abc.h")
+ end
+
+ def test_curly_bracket_pattern
+ fl = FileList.new
+ fl.include("abc.{c,h}")
+ assert fl.size == 2
+ assert fl.include?("abc.c")
+ assert fl.include?("abc.h")
+ end
+
+ def test_reject
+ fl = FileList.new
+ fl.include %w(x.c abc.c xyz.c existing)
+ fl.reject! { |fn| fn =~ /^x/ }
+ assert_equal %w[abc.c existing], fl
+ end
+
+ def test_exclude
+ fl = FileList['x.c', 'abc.c', 'xyz.c', 'existing']
+ fl.each { |fn| touch fn, :verbose => false }
+
+ x = fl.exclude(%r{^x.+\.})
+
+ assert_equal FileList, x.class
+ assert_equal %w(x.c abc.c existing), fl
+ assert_equal fl.object_id, x.object_id
+
+ fl.exclude('*.c')
+
+ assert_equal ['existing'], fl
+
+ fl.exclude('existing')
+
+ assert_equal [], fl
+ end
+
+ def test_exclude_pathname
+ fl = FileList['x.c', 'abc.c', 'other']
+ fl.each { |fn| touch fn, :verbose => false }
+
+ fl.exclude(Pathname.new('*.c'))
+
+ assert_equal ['other'], fl
+ end
+
+ def test_excluding_via_block
+ fl = FileList['a.c', 'b.c', 'xyz.c']
+ fl.exclude { |fn| fn.pathmap('%n') == 'xyz' }
+ assert fl.excluded_from_list?("xyz.c"), "Should exclude xyz.c"
+ assert_equal ['a.c', 'b.c'], fl
+ end
+
+ def test_exclude_return_on_create
+ fl = FileList['*'].exclude(/.*\.[hcx]$/)
+ assert_equal %w[cfiles existing xyzzy.txt], fl.sort
+ assert_equal FileList, fl.class
+ end
+
+ def test_exclude_with_string_return_on_create
+ fl = FileList['*'].exclude('abc.c')
+ assert_equal %w[abc.h abc.x cfiles existing x.c xyz.c xyzzy.txt], fl.sort
+ assert_equal FileList, fl.class
+ end
+
+ def test_default_exclude
+ fl = FileList.new
+ fl.clear_exclude
+ fl.include("**/*~", "**/*.bak", "**/core")
+ assert fl.member?("core"), "Should include core"
+ assert fl.member?("x.bak"), "Should include .bak files"
+ end
+
+ def test_unique
+ fl = FileList.new
+ fl << "x.c" << "a.c" << "b.rb" << "a.c"
+ assert_equal ['x.c', 'a.c', 'b.rb', 'a.c'], fl
+ fl.uniq!
+ assert_equal ['x.c', 'a.c', 'b.rb'], fl
+ end
+
+ def test_to_string
+ fl = FileList.new
+ fl << "a.java" << "b.java"
+ assert_equal "a.java b.java", fl.to_s
+ assert_equal "a.java b.java", "#{fl}"
+ end
+
+ def test_to_array
+ fl = FileList['a.java', 'b.java']
+ assert_equal ['a.java', 'b.java'], fl.to_a
+ assert_equal Array, fl.to_a.class
+ assert_equal ['a.java', 'b.java'], fl.to_ary
+ assert_equal Array, fl.to_ary.class
+ end
+
+ def test_to_s_pending
+ fl = FileList['abc.*']
+ result = fl.to_s
+ assert_match(%r{abc\.c}, result)
+ assert_match(%r{abc\.h}, result)
+ assert_match(%r{abc\.x}, result)
+ assert_match(%r{(abc\..\b ?){2}}, result)
+ end
+
+ def test_inspect_pending
+ fl = FileList['abc.*']
+ result = fl.inspect
+ assert_match(%r{"abc\.c"}, result)
+ assert_match(%r{"abc\.h"}, result)
+ assert_match(%r{"abc\.x"}, result)
+ assert_match(%r|^\[("abc\..", ){2}"abc\.."\]$|, result)
+ end
+
+ def test_sub
+ fl = FileList["*.c"]
+ f2 = fl.sub(/\.c$/, ".o")
+ assert_equal FileList, f2.class
+ assert_equal ["abc.o", "x.o", "xyz.o"].sort,
+ f2.sort
+ f3 = fl.gsub(/\.c$/, ".o")
+ assert_equal FileList, f3.class
+ assert_equal ["abc.o", "x.o", "xyz.o"].sort,
+ f3.sort
+ end
+
+ def test_claim_to_be_a_kind_of_array
+ fl = FileList['*.c']
+ assert fl.is_a?(Array)
+ assert fl.kind_of?(Array)
+ end
+
+ def test_claim_to_be_a_kind_of_filelist
+ fl = FileList['*.c']
+ assert fl.is_a?(FileList)
+ assert fl.kind_of?(FileList)
+ end
+
+ def test_claim_to_be_a_filelist_instance
+ fl = FileList['*.c']
+ assert fl.instance_of?(FileList)
+ end
+
+ def test_dont_claim_to_be_an_array_instance
+ fl = FileList['*.c']
+ assert ! fl.instance_of?(Array)
+ end
+
+ def test_sub!
+ f = "x/a.c"
+ fl = FileList[f, "x/b.c"]
+ res = fl.sub!(/\.c$/, ".o")
+ assert_equal ["x/a.o", "x/b.o"].sort, fl.sort
+ assert_equal "x/a.c", f
+ assert_equal fl.object_id, res.object_id
+ end
+
+ def test_sub_with_block
+ fl = FileList["src/org/onestepback/a.java", "src/org/onestepback/b.java"]
+# The block version doesn't work the way I want it to ...
+# f2 = fl.sub(%r{^src/(.*)\.java$}) { |x| "classes/" + $1 + ".class" }
+ f2 = fl.sub(%r{^src/(.*)\.java$}, "classes/\\1.class")
+ assert_equal [
+ "classes/org/onestepback/a.class",
+ "classes/org/onestepback/b.class"
+ ].sort,
+ f2.sort
+ end
+
+ def test_string_ext
+ assert_equal "one.net", "one.two".ext("net")
+ assert_equal "one.net", "one.two".ext(".net")
+ assert_equal "one.net", "one".ext("net")
+ assert_equal "one.net", "one".ext(".net")
+ assert_equal "one.two.net", "one.two.c".ext(".net")
+ assert_equal "one/two.net", "one/two.c".ext(".net")
+ assert_equal "one.x/two.net", "one.x/two.c".ext(".net")
+ assert_equal "one.x/two.net", "one.x/two".ext(".net")
+ assert_equal ".onerc.net", ".onerc.dot".ext("net")
+ assert_equal ".onerc.net", ".onerc".ext("net")
+ assert_equal ".a/.onerc.net", ".a/.onerc".ext("net")
+ assert_equal "one", "one.two".ext('')
+ assert_equal "one", "one.two".ext
+ assert_equal ".one", ".one.two".ext
+ assert_equal ".one", ".one".ext
+ assert_equal ".", ".".ext("c")
+ assert_equal "..", "..".ext("c")
+ # These only need to work in windows
+ if Rake::Win32.windows?
+ assert_equal "one.x\\two.net", "one.x\\two.c".ext(".net")
+ assert_equal "one.x\\two.net", "one.x\\two".ext(".net")
+ end
+ end
+
+ def test_filelist_ext
+ assert_equal FileList['one.c', '.one.c'],
+ FileList['one.net', '.one'].ext('c')
+ end
+
+ def test_gsub
+ fl = FileList["*.c"]
+ f2 = fl.gsub(/a/, "A")
+ assert_equal ["Abc.c", "x.c", "xyz.c"].sort,
+ f2.sort
+ end
+
+ def test_gsub!
+ f = FileList["*.c"]
+ f.gsub!(/a/, "A")
+ assert_equal ["Abc.c", "x.c", "xyz.c"].sort,
+ f.sort
+ end
+
+ def test_egrep_returns_0_if_no_matches
+ files = FileList['test/lib/*_test.rb'].exclude("test/lib/filelist_test.rb")
+ assert_equal 0, files.egrep(/XYZZY/) { }
+ end
+
+ def test_egrep_with_output
+ files = FileList['*.txt']
+
+ out, = capture_io do
+ files.egrep(/XYZZY/)
+ end
+
+ assert_equal "xyzzy.txt:2:XYZZY\n", out
+ end
+
+ def test_egrep_with_block
+ files = FileList['*.txt']
+ found = nil
+
+ files.egrep(/XYZZY/) do |fn, ln, line|
+ found = [fn, ln, line]
+ end
+
+ assert_equal ["xyzzy.txt", 2, "XYZZY\n"], found
+ end
+
+ def test_egrep_with_error
+ files = FileList['*.txt']
+
+ _, err = capture_io do
+ files.egrep(/XYZZY/) do |fn, ln, line |
+ raise "_EGREP_FAILURE_"
+ end
+ end
+
+ assert_equal "Error while processing 'xyzzy.txt': _EGREP_FAILURE_\n", err
+ end
+
+ def test_existing
+ fl = FileList['abc.c', 'notthere.c']
+ assert_equal ["abc.c"], fl.existing
+ assert fl.existing.is_a?(FileList)
+ end
+
+ def test_existing!
+ fl = FileList['abc.c', 'notthere.c']
+ result = fl.existing!
+ assert_equal ["abc.c"], fl
+ assert_equal fl.object_id, result.object_id
+ end
+
+ def test_ignore_special
+ f = FileList['*']
+ assert ! f.include?("CVS"), "Should not contain CVS"
+ assert ! f.include?(".svn"), "Should not contain .svn"
+ assert ! f.include?(".dummy"), "Should not contain dot files"
+ assert ! f.include?("x.bak"), "Should not contain .bak files"
+ assert ! f.include?("x~"), "Should not contain ~ files"
+ assert ! f.include?("core"), "Should not contain core files"
+ end
+
+ def test_clear_ignore_patterns
+ f = FileList['*', '.svn']
+ f.clear_exclude
+ assert f.include?("abc.c")
+ assert f.include?("xyz.c")
+ assert f.include?("CVS")
+ assert f.include?(".svn")
+ assert f.include?("x.bak")
+ assert f.include?("x~")
+ end
+
+ def test_exclude_with_alternate_file_seps
+ fl = FileList.new
+ assert fl.excluded_from_list?("x/CVS/y")
+ assert fl.excluded_from_list?("x\\CVS\\y")
+ assert fl.excluded_from_list?("x/.svn/y")
+ assert fl.excluded_from_list?("x\\.svn\\y")
+ assert fl.excluded_from_list?("x/core")
+ assert fl.excluded_from_list?("x\\core")
+ end
+
+ def test_add_default_exclude_list
+ fl = FileList.new
+ fl.exclude(/~\d+$/)
+ assert fl.excluded_from_list?("x/CVS/y")
+ assert fl.excluded_from_list?("x\\CVS\\y")
+ assert fl.excluded_from_list?("x/.svn/y")
+ assert fl.excluded_from_list?("x\\.svn\\y")
+ assert fl.excluded_from_list?("x/core")
+ assert fl.excluded_from_list?("x\\core")
+ assert fl.excluded_from_list?("x/abc~1")
+ end
+
+ def test_basic_array_functions
+ f = FileList['b', 'c', 'a']
+ assert_equal 'b', f.first
+ assert_equal 'b', f[0]
+ assert_equal 'a', f.last
+ assert_equal 'a', f[2]
+ assert_equal 'a', f[-1]
+ assert_equal ['a', 'b', 'c'], f.sort
+ f.sort!
+ assert_equal ['a', 'b', 'c'], f
+ end
+
+ def test_flatten
+ assert_equal ['a', 'x.c', 'xyz.c', 'abc.c'].sort,
+ ['a', FileList['*.c']].flatten.sort
+ end
+
+ def test_clone_and_dup
+ a = FileList['a', 'b', 'c']
+ c = a.clone
+ d = a.dup
+ a << 'd'
+ assert_equal ['a', 'b', 'c', 'd'], a
+ assert_equal ['a', 'b', 'c'], c
+ assert_equal ['a', 'b', 'c'], d
+ end
+
+ def test_dup_and_clone_replicate_taint
+ a = FileList['a', 'b', 'c']
+ a.taint
+ c = a.clone
+ d = a.dup
+ assert c.tainted?, "Clone should be tainted"
+ assert d.tainted?, "Dup should be tainted"
+ end
+
+ def test_duped_items_will_thaw
+ a = FileList['a', 'b', 'c']
+ a.freeze
+ d = a.dup
+ d << 'more'
+ assert_equal ['a', 'b', 'c', 'more'], d
+ end
+
+ def test_cloned_items_stay_frozen
+ a = FileList['a', 'b', 'c']
+ a.freeze
+ c = a.clone
+ assert_raises(TypeError, RuntimeError) do
+ c << 'more'
+ end
+ end
+
+ def test_array_comparisons
+ fl = FileList['b', 'b']
+ a = ['b', 'a']
+ b = ['b', 'b']
+ c = ['b', 'c']
+ assert_equal(1, fl <=> a)
+ assert_equal(0, fl <=> b)
+ assert_equal(-1, fl <=> c)
+ assert_equal(-1, a <=> fl)
+ assert_equal(0, b <=> fl)
+ assert_equal(1, c <=> fl)
+ end
+
+ def test_array_equality
+ a = FileList['a', 'b']
+ b = ['a', 'b']
+ assert a == b
+ assert b == a
+# assert a.eql?(b)
+# assert b.eql?(a)
+ assert ! a.equal?(b)
+ assert ! b.equal?(a)
+ end
+
+ def test_enumeration_methods
+ a = FileList['a', 'b']
+ b = a.map { |it| it.upcase }
+ assert_equal ['A', 'B'], b
+ assert_equal FileList, b.class
+
+ b = a.map { |it| it.upcase }
+ assert_equal ['A', 'B'], b
+ assert_equal FileList, b.class
+
+ b = a.sort
+ assert_equal ['a', 'b'], b
+ assert_equal FileList, b.class
+
+ b = a.sort_by { |it| it }
+ assert_equal ['a', 'b'], b
+ assert_equal FileList, b.class
+
+ b = a.select { |it| it == 'b' }
+ assert_equal ['b'], b
+ assert_equal FileList, b.class
+
+ b = a.select { |it| it.size == 1 }
+ assert_equal ['a', 'b'], b
+ assert_equal FileList, b.class
+
+ b = a.reject { |it| it == 'b' }
+ assert_equal ['a'], b
+ assert_equal FileList, b.class
+
+ b = a.grep(/./)
+ assert_equal ['a', 'b'], b
+ assert_equal FileList, b.class
+
+ b = a.partition { |it| it == 'b' }
+ assert_equal [['b'], ['a']], b
+ assert_equal Array, b.class
+ assert_equal FileList, b[0].class
+ assert_equal FileList, b[1].class
+
+ b = a.zip(['x', 'y']).to_a
+ assert_equal [['a', 'x'], ['b', 'y']], b
+ assert_equal Array, b.class
+ assert_equal Array, b[0].class
+ assert_equal Array, b[1].class
+ end
+
+ def test_array_operators
+ a = ['a', 'b']
+ b = ['c', 'd']
+ f = FileList['x', 'y']
+ g = FileList['w', 'z']
+
+ r = f + g
+ assert_equal ['x', 'y', 'w', 'z'], r
+ assert_equal FileList, r.class
+
+ r = a + g
+ assert_equal ['a', 'b', 'w', 'z'], r
+ assert_equal Array, r.class
+
+ r = f + b
+ assert_equal ['x', 'y', 'c', 'd'], r
+ assert_equal FileList, r.class
+
+ r = FileList['w', 'x', 'y', 'z'] - f
+ assert_equal ['w', 'z'], r
+ assert_equal FileList, r.class
+
+ r = FileList['w', 'x', 'y', 'z'] & f
+ assert_equal ['x', 'y'], r
+ assert_equal FileList, r.class
+
+ r = f * 2
+ assert_equal ['x', 'y', 'x', 'y'], r
+ assert_equal FileList, r.class
+
+ r = f * ','
+ assert_equal 'x,y', r
+ assert_equal String, r.class
+
+ r = f | ['a', 'x']
+ assert_equal ['a', 'x', 'y'].sort, r.sort
+ assert_equal FileList, r.class
+ end
+
+ def test_other_array_returning_methods
+ f = FileList['a', nil, 'b']
+ r = f.compact
+ assert_equal ['a', 'b'], r
+ assert_equal FileList, r.class
+
+ f = FileList['a', 'b']
+ r = f.concat(['x', 'y'])
+ assert_equal ['a', 'b', 'x', 'y'], r
+ assert_equal FileList, r.class
+
+ f = FileList['a', ['b', 'c'], FileList['d', 'e']]
+ r = f.flatten
+ assert_equal ['a', 'b', 'c', 'd', 'e'], r
+ assert_equal FileList, r.class
+
+ f = FileList['a', 'b', 'a']
+ r = f.uniq
+ assert_equal ['a', 'b'], r
+ assert_equal FileList, r.class
+
+ f = FileList['a', 'b', 'c', 'd']
+ r = f.values_at(1, 3)
+ assert_equal ['b', 'd'], r
+ assert_equal FileList, r.class
+ end
+
+ def test_file_utils_can_use_filelists
+ cfiles = FileList['*.c']
+
+ cp cfiles, @cdir, :verbose => false
+
+ assert File.exist?(File.join(@cdir, 'abc.c'))
+ assert File.exist?(File.join(@cdir, 'xyz.c'))
+ assert File.exist?(File.join(@cdir, 'x.c'))
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_file_list_path_map.rb b/jni/ruby/test/rake/test_rake_file_list_path_map.rb
new file mode 100644
index 0000000..5935dc2
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_file_list_path_map.rb
@@ -0,0 +1,8 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeFileListPathMap < Rake::TestCase
+ def test_file_list_supports_pathmap
+ assert_equal ['a', 'b'], FileList['dir/a.rb', 'dir/b.rb'].pathmap("%n")
+ end
+end
+
diff --git a/jni/ruby/test/rake/test_rake_file_task.rb b/jni/ruby/test/rake/test_rake_file_task.rb
new file mode 100644
index 0000000..a249511
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_file_task.rb
@@ -0,0 +1,197 @@
+require File.expand_path('../helper', __FILE__)
+require 'fileutils'
+require 'pathname'
+
+class TestRakeFileTask < Rake::TestCase
+ include Rake
+
+ def setup
+ super
+
+ Task.clear
+ @runs = Array.new
+ FileUtils.rm_f NEWFILE
+ FileUtils.rm_f OLDFILE
+ end
+
+ def test_file_need
+ name = "dummy"
+ file name
+
+ ftask = Task[name]
+
+ assert_equal name.to_s, ftask.name
+ File.delete(ftask.name) rescue nil
+
+ assert ftask.needed?, "file should be needed"
+ assert_equal Rake::LATE, ftask.timestamp
+
+ open(ftask.name, "w") { |f| f.puts "HI" }
+
+ assert_equal nil, ftask.prerequisites.map { |n| Task[n].timestamp }.max
+ assert ! ftask.needed?, "file should not be needed"
+ ensure
+ File.delete(ftask.name) rescue nil
+ end
+
+ def test_file_times_new_depends_on_old
+ create_timed_files(OLDFILE, NEWFILE)
+
+ t1 = Rake.application.intern(FileTask, NEWFILE).enhance([OLDFILE])
+ t2 = Rake.application.intern(FileTask, OLDFILE)
+ assert ! t2.needed?, "Should not need to build old file"
+ assert ! t1.needed?, "Should not need to rebuild new file because of old"
+ end
+
+ def test_file_times_new_depend_on_regular_task_timestamps
+ load_phony
+
+ name = "dummy"
+ task name
+
+ create_timed_files(NEWFILE)
+
+ t1 = Rake.application.intern(FileTask, NEWFILE).enhance([name])
+
+ assert t1.needed?, "depending on non-file task uses Time.now"
+
+ task(name => :phony)
+
+ assert t1.needed?, "unless the non-file task has a timestamp"
+ end
+
+ def test_file_times_old_depends_on_new
+ create_timed_files(OLDFILE, NEWFILE)
+
+ t1 = Rake.application.intern(FileTask, OLDFILE).enhance([NEWFILE])
+ t2 = Rake.application.intern(FileTask, NEWFILE)
+ assert ! t2.needed?, "Should not need to build new file"
+ preq_stamp = t1.prerequisites.map { |t| Task[t].timestamp }.max
+ assert_equal t2.timestamp, preq_stamp
+ assert t1.timestamp < preq_stamp, "T1 should be older"
+ assert t1.needed?, "Should need to rebuild old file because of new"
+ end
+
+ def test_file_depends_on_task_depend_on_file
+ create_timed_files(OLDFILE, NEWFILE)
+
+ file NEWFILE => [:obj] do |t| @runs << t.name end
+ task :obj => [OLDFILE] do |t| @runs << t.name end
+ file OLDFILE do |t| @runs << t.name end
+
+ Task[:obj].invoke
+ Task[NEWFILE].invoke
+ assert @runs.include?(NEWFILE)
+ end
+
+ def test_existing_file_depends_on_non_existing_file
+ create_file(OLDFILE)
+ delete_file(NEWFILE)
+ file NEWFILE do |t| @runs << t.name end
+ file OLDFILE => NEWFILE do |t| @runs << t.name end
+
+ Task[OLDFILE].invoke
+
+ assert_equal [NEWFILE, OLDFILE], @runs
+ end
+
+ def test_needed_eh_build_all
+ create_file 'a'
+
+ file 'a'
+
+ a_task = Task['a']
+
+ refute a_task.needed?
+
+ Rake.application.options.build_all = true
+
+ assert a_task.needed?
+ ensure
+ delete_file 'a'
+ end
+
+ def test_needed_eh_dependency
+ create_file 'a', Time.now
+ create_file 'b', Time.now - 60
+
+ create_file 'c', Time.now
+ create_file 'd', Time.now - 60
+
+ file 'b' => 'a'
+
+ b_task = Task['b']
+
+ assert b_task.needed?
+
+ file 'c' => 'd'
+
+ c_task = Task['c']
+
+ refute c_task.needed?
+ ensure
+ delete_file 'old'
+ delete_file 'new'
+ end
+
+ def test_needed_eh_exists
+ name = "dummy"
+ file name
+
+ ftask = Task[name]
+
+ assert ftask.needed?
+
+ create_file name
+
+ refute ftask.needed?
+ ensure
+ delete_file name
+ end
+
+ def test_source_is_first_prerequisite
+ t = file :f => ["preqA", "preqB"]
+ assert_equal "preqA", t.source
+ end
+
+ def test_sources_is_all_prerequisites
+ t = file :f => ["preqA", "preqB"]
+ assert_equal ["preqA", "preqB"], t.sources
+ end
+
+ def test_task_can_be_pathname
+ name = "dummy"
+ file Pathname.new name
+
+ ftask = Task[name]
+
+ assert_equal name.to_s, ftask.name
+ end
+
+ def test_prerequisite_can_be_pathname
+ t = file :f => Pathname.new("preq")
+ assert_equal "preq", t.source
+ end
+
+ # I have currently disabled this test. I'm not convinced that
+ # deleting the file target on failure is always the proper thing to
+ # do. I'm willing to hear input on this topic.
+ def ztest_file_deletes_on_failure
+ task :obj
+ file NEWFILE => [:obj] do |t|
+ FileUtils.touch NEWFILE
+ fail "Ooops"
+ end
+ assert Task[NEWFILE]
+ begin
+ Task[NEWFILE].invoke
+ rescue Exception
+ end
+ assert(! File.exist?(NEWFILE), "NEWFILE should be deleted")
+ end
+
+ def load_phony
+ load File.join(@rake_lib, "rake/phony.rb")
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_file_utils.rb b/jni/ruby/test/rake/test_rake_file_utils.rb
new file mode 100644
index 0000000..37d33dc
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_file_utils.rb
@@ -0,0 +1,309 @@
+require File.expand_path('../helper', __FILE__)
+require 'fileutils'
+require 'stringio'
+
+class TestRakeFileUtils < Rake::TestCase
+
+ def teardown
+ FileUtils::LN_SUPPORTED[0] = true
+ RakeFileUtils.verbose_flag = Rake::FileUtilsExt::DEFAULT
+
+ super
+ end
+
+ def test_rm_one_file
+ create_file("a")
+ FileUtils.rm_rf "a"
+ refute File.exist?("a")
+ end
+
+ def test_rm_two_files
+ create_file("a")
+ create_file("b")
+ FileUtils.rm_rf ["a", "b"]
+ refute File.exist?("a")
+ refute File.exist?("b")
+ end
+
+ def test_rm_filelist
+ list = Rake::FileList.new << "a" << "b"
+ list.each { |fn| create_file(fn) }
+ FileUtils.rm_r list
+ refute File.exist?("a")
+ refute File.exist?("b")
+ end
+
+ def test_ln
+ open("a", "w") { |f| f.puts "TEST_LN" }
+
+ Rake::FileUtilsExt.safe_ln("a", "b", :verbose => false)
+
+ assert_equal "TEST_LN\n", File.read('b')
+ end
+
+ class BadLink
+ include Rake::FileUtilsExt
+ attr_reader :cp_args
+
+ def initialize(klass)
+ @failure_class = klass
+ end
+
+ def cp(*args)
+ @cp_args = args
+ end
+
+ def ln(*args)
+ fail @failure_class, "ln not supported"
+ end
+
+ public :safe_ln
+ end
+
+ def test_safe_ln_failover_to_cp_on_standard_error
+ FileUtils::LN_SUPPORTED[0] = true
+ c = BadLink.new(StandardError)
+ c.safe_ln "a", "b"
+ assert_equal ['a', 'b'], c.cp_args
+ c.safe_ln "x", "y"
+ assert_equal ['x', 'y'], c.cp_args
+ end
+
+ def test_safe_ln_failover_to_cp_on_not_implemented_error
+ FileUtils::LN_SUPPORTED[0] = true
+ c = BadLink.new(NotImplementedError)
+ c.safe_ln "a", "b"
+ assert_equal ['a', 'b'], c.cp_args
+ end
+
+ def test_safe_ln_fails_on_script_error
+ FileUtils::LN_SUPPORTED[0] = true
+ c = BadLink.new(ScriptError)
+ assert_raises(ScriptError) do c.safe_ln "a", "b" end
+ end
+
+ def test_verbose
+ verbose true
+ assert_equal true, verbose
+ verbose false
+ assert_equal false, verbose
+ verbose(true) {
+ assert_equal true, verbose
+ }
+ assert_equal false, verbose
+ end
+
+ def test_nowrite
+ nowrite true
+ assert_equal true, nowrite
+ nowrite false
+ assert_equal false, nowrite
+ nowrite(true) {
+ assert_equal true, nowrite
+ }
+ assert_equal false, nowrite
+ end
+
+ def test_file_utils_methods_are_available_at_top_level
+ create_file("a")
+
+ capture_io do
+ rm_rf "a"
+ end
+
+ refute File.exist?("a")
+ end
+
+ def test_fileutils_methods_dont_leak
+ obj = Object.new
+ assert_raises(NoMethodError) { obj.copy } # from FileUtils
+ assert_raises(NoMethodError) { obj.ruby "-v" } # from RubyFileUtils
+ end
+
+ def test_sh
+ shellcommand
+
+ verbose(false) { sh %{#{Rake::TestCase::RUBY} shellcommand.rb} }
+ assert true, "should not fail"
+ end
+
+ def test_sh_with_a_single_string_argument
+ check_expansion
+
+ ENV['RAKE_TEST_SH'] = 'someval'
+ verbose(false) {
+ sh %{#{RUBY} check_expansion.rb #{env_var} someval}
+ }
+ end
+
+ def test_sh_with_multiple_arguments
+ check_no_expansion
+ ENV['RAKE_TEST_SH'] = 'someval'
+
+ verbose(false) {
+ sh RUBY, 'check_no_expansion.rb', env_var, 'someval'
+ }
+ end
+
+ def test_sh_failure
+ shellcommand
+
+ assert_raises(RuntimeError) {
+ verbose(false) { sh %{#{RUBY} shellcommand.rb 1} }
+ }
+ end
+
+ def test_sh_special_handling
+ shellcommand
+
+ count = 0
+ verbose(false) {
+ sh(%{#{RUBY} shellcommand.rb}) do |ok, res|
+ assert(ok)
+ assert_equal 0, res.exitstatus
+ count += 1
+ end
+ sh(%{#{RUBY} shellcommand.rb 1}) do |ok, res|
+ assert(!ok)
+ assert_equal 1, res.exitstatus
+ count += 1
+ end
+ }
+ assert_equal 2, count, "Block count should be 2"
+ end
+
+ def test_sh_noop
+ shellcommand
+
+ verbose(false) { sh %{shellcommand.rb 1}, :noop=>true }
+ assert true, "should not fail"
+ end
+
+ def test_sh_bad_option
+ shellcommand
+
+ ex = assert_raises(ArgumentError) {
+ verbose(false) { sh %{shellcommand.rb}, :bad_option=>true }
+ }
+ assert_match(/bad_option/, ex.message)
+ end
+
+ def test_sh_verbose
+ shellcommand
+
+ _, err = capture_io do
+ verbose(true) {
+ sh %{shellcommand.rb}, :noop=>true
+ }
+ end
+
+ assert_equal "shellcommand.rb\n", err
+ end
+
+ def test_sh_verbose_false
+ shellcommand
+
+ _, err = capture_io do
+ verbose(false) {
+ sh %{shellcommand.rb}, :noop=>true
+ }
+ end
+
+ assert_equal '', err
+ end
+
+ def test_sh_verbose_flag_nil
+ shellcommand
+
+ RakeFileUtils.verbose_flag = nil
+
+ assert_silent do
+ sh %{shellcommand.rb}, :noop=>true
+ end
+ end
+
+ def test_ruby_with_a_single_string_argument
+ check_expansion
+
+ ENV['RAKE_TEST_SH'] = 'someval'
+
+ verbose(false) {
+ replace_ruby {
+ ruby %{check_expansion.rb #{env_var} someval}
+ }
+ }
+ end
+
+ def test_ruby_with_multiple_arguments
+ check_no_expansion
+
+ ENV['RAKE_TEST_SH'] = 'someval'
+ verbose(false) {
+ replace_ruby {
+ ruby 'check_no_expansion.rb', env_var, 'someval'
+ }
+ }
+ end
+
+ def test_split_all
+ assert_equal ['a'], Rake::FileUtilsExt.split_all('a')
+ assert_equal ['..'], Rake::FileUtilsExt.split_all('..')
+ assert_equal ['/'], Rake::FileUtilsExt.split_all('/')
+ assert_equal ['a', 'b'], Rake::FileUtilsExt.split_all('a/b')
+ assert_equal ['/', 'a', 'b'], Rake::FileUtilsExt.split_all('/a/b')
+ assert_equal ['..', 'a', 'b'], Rake::FileUtilsExt.split_all('../a/b')
+ end
+
+ def command(name, text)
+ open name, 'w', 0750 do |io|
+ io << text
+ end
+ end
+
+ def check_no_expansion
+ command 'check_no_expansion.rb', <<-CHECK_EXPANSION
+if ARGV[0] != ARGV[1]
+ exit 0
+else
+ exit 1
+end
+ CHECK_EXPANSION
+ end
+
+ def check_expansion
+ command 'check_expansion.rb', <<-CHECK_EXPANSION
+if ARGV[0] != ARGV[1]
+ exit 1
+else
+ exit 0
+end
+ CHECK_EXPANSION
+ end
+
+ def replace_ruby
+ ruby = FileUtils::RUBY
+ FileUtils.send :remove_const, :RUBY
+ FileUtils.const_set :RUBY, RUBY
+ yield
+ ensure
+ FileUtils.send :remove_const, :RUBY
+ FileUtils.const_set:RUBY, ruby
+ end
+
+ def shellcommand
+ command 'shellcommand.rb', <<-SHELLCOMMAND
+#!/usr/bin/env ruby
+
+exit((ARGV[0] || "0").to_i)
+ SHELLCOMMAND
+ end
+
+ def env_var
+ windows? ? '%RAKE_TEST_SH%' : '$RAKE_TEST_SH'
+ end
+
+ def windows?
+ ! File::ALT_SEPARATOR.nil?
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_ftp_file.rb b/jni/ruby/test/rake/test_rake_ftp_file.rb
new file mode 100644
index 0000000..5749b8a
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_ftp_file.rb
@@ -0,0 +1,74 @@
+require File.expand_path('../helper', __FILE__)
+require 'date'
+require 'time'
+require 'rake/contrib/ftptools'
+
+class FakeDate
+ def self.today
+ Date.new(2003, 10, 3)
+ end
+
+ def self.now
+ Time.local(2003, 10, 3, 12, 00, 00)
+ end
+end
+
+class TestRakeFtpFile < Rake::TestCase
+
+ def setup
+ super
+
+ Rake::FtpFile.class_eval {
+ @date_class = FakeDate
+ @time_class = FakeDate
+ }
+ end
+
+ def test_general
+ file = Rake::FtpFile.new(
+ "here",
+ "-rw-r--r-- 1 a279376 develop 121770 Mar 6 14:50 wiki.pl")
+ assert_equal "wiki.pl", file.name
+ assert_equal "here/wiki.pl", file.path
+ assert_equal "a279376", file.owner
+ assert_equal "develop", file.group
+ assert_equal 0644, file.mode
+ assert_equal 121_770, file.size
+ assert_equal Time.mktime(2003, 3, 6, 14, 50, 0, 0), file.time
+ assert ! file.directory?
+ assert ! file.symlink?
+ end
+
+ def test_far_date
+ file = Rake::FtpFile.new(
+ ".",
+ "drwxr-xr-x 3 a279376 develop 4096 Nov 26 2001 vss")
+ assert_equal Time.mktime(2001, 11, 26, 0, 0, 0, 0), file.time
+ end
+
+ def test_close_date
+ file = Rake::FtpFile.new(
+ ".",
+ "drwxr-xr-x 3 a279376 develop 4096 Nov 26 15:35 vss")
+ assert_equal Time.mktime(2002, 11, 26, 15, 35, 0, 0), file.time
+ end
+
+ def test_directory
+ file = Rake::FtpFile.new(
+ ".",
+ "drwxrwxr-x 9 a279376 develop 4096 Mar 13 14:32 working")
+ assert file.directory?
+ assert !file.symlink?
+ end
+
+ def test_symlink
+ file = Rake::FtpFile.new(
+ ".",
+ "lrwxrwxrwx 1 a279376 develop 64 Mar 26 2002 " +
+ "xtrac -> /home/a279376/working/ics/development/java/" +
+ "com/fmr/fwp/ics/xtrac")
+ assert_equal 'xtrac', file.name
+ assert file.symlink?
+ assert !file.directory?
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_functional.rb b/jni/ruby/test/rake/test_rake_functional.rb
new file mode 100644
index 0000000..bf7ba92
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_functional.rb
@@ -0,0 +1,482 @@
+require File.expand_path('../helper', __FILE__)
+require 'fileutils'
+require 'open3'
+
+class TestRakeFunctional < Rake::TestCase
+ include RubyRunner
+
+ def setup
+ super
+
+ if @verbose
+ puts
+ puts
+ puts '-' * 80
+ puts @__name__
+ puts '-' * 80
+ end
+ end
+
+ def test_rake_default
+ rakefile_default
+
+ rake
+
+ assert_match(/^DEFAULT$/, @out)
+ end
+
+ def test_rake_error_on_bad_task
+ rakefile_default
+
+ rake '-t', 'xyz'
+
+ assert_match(/rake aborted/, @err)
+ end
+
+ def test_env_available_at_top_scope
+ rakefile_default
+
+ rake "TESTTOPSCOPE=1"
+
+ assert_match(/^TOPSCOPE$/, @out)
+ end
+
+ def test_env_available_at_task_scope
+ rakefile_default
+
+ rake 'TESTTASKSCOPE=1', 'task_scope'
+
+ assert_match(/^TASKSCOPE$/, @out)
+ end
+
+ def test_multi_desc
+ ENV['RAKE_COLUMNS'] = '80'
+ rakefile_multidesc
+
+ rake "-T"
+
+ assert_match %r{^rake a *# A / A2 *$}, @out
+ assert_match %r{^rake b *# B *$}, @out
+ refute_match %r{^rake c}, @out
+ assert_match %r{^rake d *# x{65}\.\.\.$}, @out
+ end
+
+ def test_long_description
+ rakefile_multidesc
+
+ rake "--describe"
+
+ assert_match %r{^rake a\n *A\n *A2 *$}m, @out
+ assert_match %r{^rake b\n *B *$}m, @out
+ assert_match %r{^rake d\n *x{80}}m, @out
+ refute_match %r{^rake c\n}m, @out
+ end
+
+ def test_proper_namespace_access
+ rakefile_access
+
+ rake
+
+ refute_match %r{^BAD:}, @out
+ end
+
+ def test_rbext
+ rakefile_rbext
+
+ rake "-N"
+
+ assert_match %r{^OK$}, @out
+ end
+
+ def test_system
+ rake_system_dir
+
+ rake '-g', "sys1"
+
+ assert_match %r{^SYS1}, @out
+ end
+
+ def test_system_excludes_rakelib_files_too
+ rake_system_dir
+
+ rake '-g', "sys1", '-T', 'extra'
+
+ refute_match %r{extra:extra}, @out
+ end
+
+ def test_by_default_rakelib_files_are_included
+ rake_system_dir
+ rakefile_extra
+
+ rake '-T', 'extra', '--trace'
+
+ assert_match %r{extra:extra}, @out
+ end
+
+ def test_implicit_system
+ rake_system_dir
+ Dir.chdir @tempdir
+
+ rake "sys1", "--trace"
+
+ assert_match %r{^SYS1}, @out
+ end
+
+ def test_no_system
+ rake_system_dir
+ rakefile_extra
+
+ rake '-G', "sys1"
+
+ assert_match %r{^Don't know how to build task}, @err # emacs wart: '
+ end
+
+ def test_nosearch_with_rakefile_uses_local_rakefile
+ rakefile_default
+
+ rake "--nosearch"
+
+ assert_match %r{^DEFAULT}, @out
+ end
+
+ def test_nosearch_without_rakefile_finds_system
+ rakefile_nosearch
+ rake_system_dir
+
+ rake "--nosearch", "sys1"
+
+ assert_match %r{^SYS1}, @out
+ end
+
+ def test_nosearch_without_rakefile_and_no_system_fails
+ rakefile_nosearch
+ ENV['RAKE_SYSTEM'] = 'not_exist'
+
+ rake "--nosearch"
+
+ assert_match %r{^No Rakefile found}, @err
+ end
+
+ def test_invalid_command_line_options
+ rakefile_default
+
+ rake "--bad-options"
+
+ assert_match %r{invalid +option}i, @err
+ end
+
+ def test_inline_verbose_default_should_show_command
+ rakefile_verbose
+
+ rake "inline_verbose_default"
+
+ assert_match(/#{Regexp.quote(RUBY)} -e/, @err)
+ end
+
+ def test_inline_verbose_true_should_show_command
+ rakefile_verbose
+
+ rake "inline_verbose_true"
+
+ assert_match(/#{Regexp.quote(RUBY)} -e/, @err)
+ end
+
+ def test_inline_verbose_false_should_not_show_command
+ rakefile_verbose
+
+ rake "inline_verbose_false"
+
+ refute_match(/#{Regexp.quote(RUBY)} -e/, @err)
+ end
+
+ def test_block_verbose_false_should_not_show_command
+ rakefile_verbose
+
+ rake "block_verbose_false"
+
+ refute_match(/#{Regexp.quote(RUBY)} -e/, @err)
+ end
+
+ def test_block_verbose_true_should_show_command
+ rakefile_verbose
+
+ rake "block_verbose_true"
+
+ assert_match(/#{Regexp.quote(RUBY)} -e/, @err)
+ end
+
+ def test_standalone_verbose_true_should_show_command
+ rakefile_verbose
+
+ rake "standalone_verbose_true"
+
+ assert_match(/#{Regexp.quote(RUBY)} -e/, @err)
+ end
+
+ def test_standalone_verbose_false_should_not_show_command
+ rakefile_verbose
+
+ rake "standalone_verbose_false"
+
+ refute_match(/#{Regexp.quote(RUBY)} -e/, @err)
+ end
+
+ def test_dry_run
+ rakefile_default
+
+ rake "-n", "other"
+
+ assert_match %r{Execute \(dry run\) default}, @err
+ assert_match %r{Execute \(dry run\) other}, @err
+ refute_match %r{DEFAULT}, @out
+ refute_match %r{OTHER}, @out
+ end
+
+ # Test for the trace/dry_run bug found by Brian Chandler
+ def test_dry_run_bug
+ rakefile_dryrun
+
+ rake
+
+ FileUtils.rm_f 'temp_one'
+
+ rake "--dry-run"
+
+ refute_match(/No such file/, @out)
+ end
+
+ # Test for the trace/dry_run bug found by Brian Chandler
+ def test_trace_bug
+ rakefile_dryrun
+
+ rake
+
+ FileUtils.rm_f 'temp_one'
+
+ rake "--trace"
+
+ refute_match(/No such file/, @out)
+ end
+
+ def test_imports
+ rakefile_imports
+
+ rake
+
+ assert File.exist?(File.join(@tempdir, 'dynamic_deps')),
+ "'dynamic_deps' file should exist"
+ assert_match(/^FIRST$\s+^DYNAMIC$\s+^STATIC$\s+^OTHER$/, @out)
+ end
+
+ def test_regenerate_imports
+ rakefile_regenerate_imports
+
+ rake
+
+ assert_match(/^INITIAL\s+^REGENERATED$/, @out)
+ end
+
+ def test_rules_chaining_to_file_task
+ rakefile_chains
+
+ rake
+
+ assert File.exist?(File.join(@tempdir, 'play.app')),
+ "'play.app' file should exist"
+ end
+
+ def test_file_creation_task
+ rakefile_file_creation
+
+ rake "prep"
+ rake "run"
+ rake "run"
+
+ assert(@err !~ /^cp src/, "Should not recopy data")
+ end
+
+ def test_dash_f_with_no_arg_foils_rakefile_lookup
+ rakefile_rakelib
+
+ rake '-I', 'rakelib', '-rtest1', '-f'
+
+ assert_match(/^TEST1$/, @out)
+ end
+
+ def test_dot_rake_files_can_be_loaded_with_dash_r
+ rakefile_rakelib
+
+ rake '-I', 'rakelib', '-rtest2', '-f'
+
+ assert_empty @err
+ assert_match(/^TEST2$/, @out)
+ end
+
+ def test_can_invoke_task_in_toplevel_namespace
+ rakefile_namespace
+
+ rake "copy"
+
+ assert_match(/^COPY$/, @out)
+ end
+
+ def test_can_invoke_task_in_nested_namespace
+ rakefile_namespace
+
+ rake "nest:copy"
+
+ assert_match(/^NEST COPY$/, @out)
+ end
+
+ def test_tasks_can_reference_task_in_same_namespace
+ rakefile_namespace
+
+ rake "nest:xx"
+
+ assert_match(/^NEST COPY$/m, @out)
+ end
+
+ def test_tasks_can_reference_task_in_other_namespaces
+ rakefile_namespace
+
+ rake "b:run"
+
+ assert_match(/^IN A\nIN B$/m, @out)
+ end
+
+ def test_anonymous_tasks_can_be_invoked_indirectly
+ rakefile_namespace
+
+ rake "anon"
+
+ assert_match(/^ANON COPY$/m, @out)
+ end
+
+ def test_rake_namespace_refers_to_toplevel
+ rakefile_namespace
+
+ rake "very:nested:run"
+
+ assert_match(/^COPY$/m, @out)
+ end
+
+ def test_file_task_are_not_scoped_by_namespaces
+ rakefile_namespace
+
+ rake "xyz.rb"
+
+ assert_match(/^XYZ1\nXYZ2$/m, @out)
+ end
+
+ def test_file_task_dependencies_scoped_by_namespaces
+ rakefile_namespace
+
+ rake "scopedep.rb"
+
+ assert_match(/^PREPARE\nSCOPEDEP$/m, @out)
+ end
+
+ def test_test_task_descriptions
+ rakefile_test_task
+
+ rake "-T"
+
+ assert_match(/custom test task description/, @out)
+ end
+
+ def test_comment_before_task_acts_like_desc
+ rakefile_comments
+
+ rake "-T"
+
+ refute_match(/comment for t1/, @out)
+ end
+
+ def test_comment_separated_from_task_by_blank_line_is_not_picked_up
+ rakefile_comments
+
+ rake "-T"
+
+ refute_match("t2", @out)
+ end
+
+ def test_comment_after_desc_is_ignored
+ rakefile_comments
+
+ rake "-T"
+
+ assert_match("override comment for t3", @out)
+ end
+
+ def test_comment_before_desc_is_ignored
+ rakefile_comments
+
+ rake "-T"
+
+ assert_match("override comment for t4", @out)
+ end
+
+ def test_correct_number_of_tasks_reported
+ rakefile_comments
+
+ rake "-T"
+
+ assert_equal(2, @out.split(/\n/).grep(/t\d/).size)
+ end
+
+ def test_file_list_is_requirable_separately
+ ruby '-rrake/file_list', '-e', 'puts Rake::FileList["a"].size'
+ assert_equal "1\n", @out
+ end
+
+ def can_detect_signals?
+ system RUBY, '-e', 'Process.kill "TERM", $$'
+ status = $?
+ if @verbose
+ puts " SIG status = #{$?.inspect}"
+ puts " SIG status.respond_to?(:signaled?) = " +
+ "#{$?.respond_to?(:signaled?).inspect}"
+ puts " SIG status.signaled? = #{status.signaled?}" if
+ status.respond_to?(:signaled?)
+ end
+ status.respond_to?(:signaled?) && status.signaled?
+ end
+
+ def test_signal_propagation_in_tests
+ if can_detect_signals?
+ rakefile_test_signal
+ rake
+ assert_match(/ATEST/, @out)
+ refute_match(/BTEST/, @out)
+ else
+ skip "Signal detect seems broken on this system"
+ end
+ end
+
+ def test_failing_test_sets_exit_status
+ skip if uncertain_exit_status?
+ rakefile_failing_test_task
+ rake
+ assert @exit.exitstatus > 0, "should be non-zero"
+ end
+
+ def test_stand_alone_filelist
+ rakefile_stand_alone_filelist
+
+ run_ruby @ruby_options + ["stand_alone_filelist.rb"]
+
+ assert_match(/^stand_alone_filelist\.rb$/, @out)
+ assert_equal 0, @exit.exitstatus unless uncertain_exit_status?
+ end
+
+ private
+
+ # We are unable to accurately verify that Rake returns a proper
+ # error exit status using popen3 in Ruby 1.8.7 and JRuby. This
+ # predicate function can be used to skip tests or assertions as
+ # needed.
+ def uncertain_exit_status?
+ RUBY_VERSION < "1.9" || defined?(JRUBY_VERSION)
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_invocation_chain.rb b/jni/ruby/test/rake/test_rake_invocation_chain.rb
new file mode 100644
index 0000000..0176339
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_invocation_chain.rb
@@ -0,0 +1,64 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeInvocationChain < Rake::TestCase
+ include Rake
+
+ def setup
+ super
+
+ @empty = InvocationChain.empty
+
+ @first_member = "A"
+ @second_member = "B"
+ @one = @empty.append(@first_member)
+ @two = @one.append(@second_member)
+ end
+
+ def test_conj_on_invocation_chains
+ list = InvocationChain.empty.conj("B").conj("A")
+ assert_equal InvocationChain.make("A", "B"), list
+ assert_equal InvocationChain, list.class
+ end
+
+ def test_make_on_invocation_chains
+ assert_equal @empty, InvocationChain.make()
+ assert_equal @one, InvocationChain.make(@first_member)
+ assert_equal @two, InvocationChain.make(@second_member, @first_member)
+ end
+
+ def test_append_with_one_argument
+ chain = @empty.append("A")
+
+ assert_equal 'TOP => A', chain.to_s # HACK
+ end
+
+ def test_append_one_circular
+ ex = assert_raises RuntimeError do
+ @one.append(@first_member)
+ end
+ assert_match(/circular +dependency/i, ex.message)
+ assert_match(/A.*=>.*A/, ex.message)
+ end
+
+ def test_append_two_circular
+ ex = assert_raises RuntimeError do
+ @two.append(@first_member)
+ end
+ assert_match(/A.*=>.*B.*=>.*A/, ex.message)
+ end
+
+ def test_member_eh_one
+ assert @one.member?(@first_member)
+ end
+
+ def test_member_eh_two
+ assert @two.member?(@first_member)
+ assert @two.member?(@second_member)
+ end
+
+ def test_to_s_empty
+ assert_equal "TOP", @empty.to_s
+ assert_equal "TOP => A", @one.to_s
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_late_time.rb b/jni/ruby/test/rake/test_rake_late_time.rb
new file mode 100644
index 0000000..4b910a7
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_late_time.rb
@@ -0,0 +1,18 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeLateTime < Rake::TestCase
+ def test_late_time_comparisons
+ late = Rake::LATE
+ assert_equal late, late
+ assert late >= Time.now
+ assert late > Time.now
+ assert late != Time.now
+ assert Time.now < late
+ assert Time.now <= late
+ assert Time.now != late
+ end
+
+ def test_to_s
+ assert_equal '<LATE TIME>', Rake::LATE.to_s
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_linked_list.rb b/jni/ruby/test/rake/test_rake_linked_list.rb
new file mode 100644
index 0000000..10957fb
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_linked_list.rb
@@ -0,0 +1,84 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestLinkedList < Rake::TestCase
+ include Rake
+
+ def test_empty_list
+ empty = LinkedList::EMPTY
+ assert empty.empty?, "should be empty"
+ end
+
+ def test_list_with_one_item
+ list = LinkedList.make(:one)
+ assert ! list.empty?, "should not be empty"
+ assert_equal :one, list.head
+ assert_equal LinkedList::EMPTY, list.tail
+ end
+
+ def test_make_with_no_arguments
+ empty = LinkedList.make()
+ assert_equal LinkedList::EMPTY, empty
+ end
+
+ def test_make_with_one_argument
+ list = LinkedList.make(:one)
+ assert ! list.empty?
+ assert_equal :one, list.head
+ assert_equal LinkedList::EMPTY, list.tail
+ end
+
+ def test_make_with_two_arguments
+ list = LinkedList.make(:one, :two)
+ assert ! list.empty?
+ assert_equal :one, list.head
+ assert_equal :two, list.tail.head
+ assert_equal LinkedList::EMPTY, list.tail.tail
+ end
+
+ def test_list_with_several_items
+ list = LinkedList.make(:one, :two, :three)
+
+ assert ! list.empty?, "should not be empty"
+ assert_equal :one, list.head
+ assert_equal :two, list.tail.head
+ assert_equal :three, list.tail.tail.head
+ assert_equal LinkedList::EMPTY, list.tail.tail.tail
+ end
+
+ def test_lists_are_structurally_equivalent
+ list = LinkedList.make(1, 2, 3)
+ same = LinkedList.make(1, 2, 3)
+ diff = LinkedList.make(1, 2, 4)
+ short = LinkedList.make(1, 2)
+
+ assert_equal list, same
+ refute_equal list, diff
+ refute_equal list, short
+ refute_equal short, list
+ end
+
+ def test_converstion_to_string
+ list = LinkedList.make(:one, :two, :three)
+ assert_equal "LL(one, two, three)", list.to_s
+ assert_equal "LL()", LinkedList.make().to_s
+ end
+
+ def test_converstion_with_inspect
+ list = LinkedList.make(:one, :two, :three)
+ assert_equal "LL(:one, :two, :three)", list.inspect
+ assert_equal "LL()", LinkedList.make().inspect
+ end
+
+ def test_lists_are_enumerable
+ list = LinkedList.make(1, 2, 3)
+ new_list = list.map { |item| item + 10 }
+ expected = [11, 12, 13]
+ assert_equal expected, new_list
+ end
+
+ def test_conjunction
+ list = LinkedList.make.conj("C").conj("B").conj("A")
+ assert_equal LinkedList.make("A", "B", "C"), list
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_makefile_loader.rb b/jni/ruby/test/rake/test_rake_makefile_loader.rb
new file mode 100644
index 0000000..9e9265a
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_makefile_loader.rb
@@ -0,0 +1,46 @@
+require File.expand_path('../helper', __FILE__)
+require 'rake/loaders/makefile'
+
+class TestRakeMakefileLoader < Rake::TestCase
+ include Rake
+
+ def test_parse
+ Dir.chdir @tempdir
+
+ open 'sample.mf', 'w' do |io|
+ io << <<-'SAMPLE_MF'
+# Comments
+a: a1 a2 a3 a4
+b: b1 b2 b3 \
+ b4 b5 b6\
+# Mid: Comment
+b7
+
+ a : a5 a6 a7
+c: c1
+d: d1 d2 \
+
+e f : e1 f1
+
+g\ 0: g1 g\ 2 g\ 3 g4
+ SAMPLE_MF
+ end
+
+ Task.clear
+ loader = Rake::MakefileLoader.new
+ loader.load 'sample.mf'
+ %w(a b c d).each do |t|
+ assert Task.task_defined?(t), "#{t} should be a defined task"
+ end
+ assert_equal %w(a1 a2 a3 a4 a5 a6 a7).sort, Task['a'].prerequisites.sort
+ assert_equal %w(b1 b2 b3 b4 b5 b6 b7).sort, Task['b'].prerequisites.sort
+ assert_equal %w(c1).sort, Task['c'].prerequisites.sort
+ assert_equal %w(d1 d2).sort, Task['d'].prerequisites.sort
+ assert_equal %w(e1 f1).sort, Task['e'].prerequisites.sort
+ assert_equal %w(e1 f1).sort, Task['f'].prerequisites.sort
+ assert_equal(
+ ["g1", "g 2", "g 3", "g4"].sort,
+ Task['g 0'].prerequisites.sort)
+ assert_equal 7, Task.tasks.size
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_multi_task.rb b/jni/ruby/test/rake/test_rake_multi_task.rb
new file mode 100644
index 0000000..9f8fed6
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_multi_task.rb
@@ -0,0 +1,64 @@
+require File.expand_path('../helper', __FILE__)
+require 'thread'
+
+class TestRakeMultiTask < Rake::TestCase
+ include Rake
+ include Rake::DSL
+
+ def setup
+ super
+
+ Task.clear
+ @runs = Array.new
+ @mutex = Mutex.new
+ end
+
+ def teardown
+ Rake.application.thread_pool.join
+
+ super
+ end
+
+ def add_run(obj)
+ @mutex.synchronize do
+ @runs << obj
+ end
+ end
+
+ def test_running_multitasks
+ task :a do 3.times do |i| add_run("A#{i}"); sleep 0.01; end end
+ task :b do 3.times do |i| add_run("B#{i}"); sleep 0.01; end end
+ multitask :both => [:a, :b]
+ Task[:both].invoke
+ assert_equal 6, @runs.size
+ assert @runs.index("A0") < @runs.index("A1")
+ assert @runs.index("A1") < @runs.index("A2")
+ assert @runs.index("B0") < @runs.index("B1")
+ assert @runs.index("B1") < @runs.index("B2")
+ end
+
+ def test_all_multitasks_wait_on_slow_prerequisites
+ task :slow do 3.times do |i| add_run("S#{i}"); sleep 0.05 end end
+ task :a => [:slow] do 3.times do |i| add_run("A#{i}"); sleep 0.01 end end
+ task :b => [:slow] do 3.times do |i| add_run("B#{i}"); sleep 0.01 end end
+ multitask :both => [:a, :b]
+ Task[:both].invoke
+ assert_equal 9, @runs.size
+ assert @runs.index("S0") < @runs.index("S1")
+ assert @runs.index("S1") < @runs.index("S2")
+ assert @runs.index("S2") < @runs.index("A0")
+ assert @runs.index("S2") < @runs.index("B0")
+ assert @runs.index("A0") < @runs.index("A1")
+ assert @runs.index("A1") < @runs.index("A2")
+ assert @runs.index("B0") < @runs.index("B1")
+ assert @runs.index("B1") < @runs.index("B2")
+ end
+
+ def test_multitasks_with_parameters
+ task :a, [:arg] do |t, args| add_run(args[:arg]) end
+ multitask :b, [:arg] => [:a] do |t, args| add_run(args[:arg] + 'mt') end
+ Task[:b].invoke "b"
+ assert @runs[0] == "b"
+ assert @runs[1] == "bmt"
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_name_space.rb b/jni/ruby/test/rake/test_rake_name_space.rb
new file mode 100644
index 0000000..d35e70e
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_name_space.rb
@@ -0,0 +1,57 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeNameSpace < Rake::TestCase
+
+ class TM
+ include Rake::TaskManager
+ end
+
+ def test_namespace_creation
+ mgr = TM.new
+ ns = Rake::NameSpace.new(mgr, [])
+ refute_nil ns
+ end
+
+ def test_namespace_lookup
+ mgr = TM.new
+ ns = mgr.in_namespace("n") do
+ mgr.define_task(Rake::Task, "t")
+ end
+
+ refute_nil ns["t"]
+ assert_equal mgr["n:t"], ns["t"]
+ end
+
+ def test_namespace_reports_tasks_it_owns
+ mgr = TM.new
+ nns = nil
+ ns = mgr.in_namespace("n") do
+ mgr.define_task(Rake::Task, :x)
+ mgr.define_task(Rake::Task, :y)
+ nns = mgr.in_namespace("nn") do
+ mgr.define_task(Rake::Task, :z)
+ end
+ end
+ mgr.in_namespace("m") do
+ mgr.define_task(Rake::Task, :x)
+ end
+
+ assert_equal ["n:nn:z", "n:x", "n:y"],
+ ns.tasks.map { |tsk| tsk.name }
+ assert_equal ["n:nn:z"], nns.tasks.map { |t| t.name }
+ end
+
+ def test_scope
+ mgr = TM.new
+
+ scope = Rake::LinkedList.new 'b'
+ scope = scope.conj 'a'
+
+ ns = Rake::NameSpace.new mgr, scope
+
+ assert_equal scope, ns.scope
+
+ refute_same scope, ns.scope
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_package_task.rb b/jni/ruby/test/rake/test_rake_package_task.rb
new file mode 100644
index 0000000..87cb57c
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_package_task.rb
@@ -0,0 +1,79 @@
+require File.expand_path('../helper', __FILE__)
+require 'rake/packagetask'
+
+class TestRakePackageTask < Rake::TestCase
+
+ def test_initialize
+ touch 'install.rb'
+ touch 'a.c'
+ touch 'b.c'
+ mkdir 'CVS'
+ touch 'a.rb~'
+
+ pkg = Rake::PackageTask.new("pkgr", "1.2.3") { |p|
+ p.package_files << "install.rb"
+ p.package_files.include '*.c'
+ p.package_files.exclude(/\bCVS\b/)
+ p.package_files.exclude(/~$/)
+ p.package_dir = 'pkg'
+ p.need_tar = true
+ p.need_tar_gz = true
+ p.need_tar_bz2 = true
+ p.need_zip = true
+ }
+
+ assert_equal "pkg", pkg.package_dir
+
+ assert_includes pkg.package_files, 'a.c'
+
+ assert_equal 'pkgr', pkg.name
+ assert_equal '1.2.3', pkg.version
+ assert Rake::Task[:package]
+ assert Rake::Task['pkg/pkgr-1.2.3.tgz']
+ assert Rake::Task['pkg/pkgr-1.2.3.tar.gz']
+ assert Rake::Task['pkg/pkgr-1.2.3.tar.bz2']
+ assert Rake::Task['pkg/pkgr-1.2.3.zip']
+ assert Rake::Task['pkg/pkgr-1.2.3']
+ assert Rake::Task[:clobber_package]
+ assert Rake::Task[:repackage]
+ end
+
+ def test_initialize_no_version
+ e = assert_raises RuntimeError do
+ Rake::PackageTask.new 'pkgr'
+ end
+
+ assert_equal 'Version required (or :noversion)', e.message
+ end
+
+ def test_initialize_noversion
+ pkg = Rake::PackageTask.new 'pkgr', :noversion
+
+ assert_equal 'pkg', pkg.package_dir
+ assert_equal 'pkgr', pkg.name
+ assert_equal nil, pkg.version
+ end
+
+ def test_clone
+ pkg = Rake::PackageTask.new("x", :noversion)
+ p2 = pkg.clone
+ pkg.package_files << "y"
+ p2.package_files << "x"
+ assert_equal ["y"], pkg.package_files
+ assert_equal ["x"], p2.package_files
+ end
+
+ def test_package_name
+ pkg = Rake::PackageTask.new 'a', '1'
+
+ assert_equal 'a-1', pkg.package_name
+ end
+
+ def test_package_name_noversion
+ pkg = Rake::PackageTask.new 'a', :noversion
+
+ assert_equal 'a', pkg.package_name
+ end
+
+end
+
diff --git a/jni/ruby/test/rake/test_rake_path_map.rb b/jni/ruby/test/rake/test_rake_path_map.rb
new file mode 100644
index 0000000..038ba1f
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_path_map.rb
@@ -0,0 +1,168 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakePathMap < Rake::TestCase
+
+ def test_returns_self_with_no_args
+ assert_equal "abc.rb", "abc.rb".pathmap
+ end
+
+ def test_s_returns_file_separator
+ sep = File::ALT_SEPARATOR || File::SEPARATOR
+ assert_equal sep, "abc.rb".pathmap("%s")
+ assert_equal sep, "".pathmap("%s")
+ assert_equal "a#{sep}b", "a/b".pathmap("%d%s%f")
+ end
+
+ def test_f_returns_basename
+ assert_equal "abc.rb", "abc.rb".pathmap("%f")
+ assert_equal "abc.rb", "this/is/a/dir/abc.rb".pathmap("%f")
+ assert_equal "abc.rb", "/this/is/a/dir/abc.rb".pathmap("%f")
+ end
+
+ def test_n_returns_basename_without_extension
+ assert_equal "abc", "abc.rb".pathmap("%n")
+ assert_equal "abc", "abc".pathmap("%n")
+ assert_equal "abc", "this/is/a/dir/abc.rb".pathmap("%n")
+ assert_equal "abc", "/this/is/a/dir/abc.rb".pathmap("%n")
+ assert_equal "abc", "/this/is/a/dir/abc".pathmap("%n")
+ end
+
+ def test_d_returns_dirname
+ assert_equal ".", "abc.rb".pathmap("%d")
+ assert_equal "/", "/abc".pathmap("%d")
+ assert_equal "this/is/a/dir", "this/is/a/dir/abc.rb".pathmap("%d")
+ assert_equal "/this/is/a/dir", "/this/is/a/dir/abc.rb".pathmap("%d")
+ end
+
+ def test_9d_returns_partial_dirname
+ assert_equal "this/is", "this/is/a/dir/abc.rb".pathmap("%2d")
+ assert_equal "this", "this/is/a/dir/abc.rb".pathmap("%1d")
+ assert_equal ".", "this/is/a/dir/abc.rb".pathmap("%0d")
+ assert_equal "dir", "this/is/a/dir/abc.rb".pathmap("%-1d")
+ assert_equal "a/dir", "this/is/a/dir/abc.rb".pathmap("%-2d")
+ assert_equal "this/is/a/dir", "this/is/a/dir/abc.rb".pathmap("%100d")
+ assert_equal "this/is/a/dir", "this/is/a/dir/abc.rb".pathmap("%-100d")
+ end
+
+ def test_x_returns_extension
+ assert_equal "", "abc".pathmap("%x")
+ assert_equal ".rb", "abc.rb".pathmap("%x")
+ assert_equal ".rb", "abc.xyz.rb".pathmap("%x")
+ assert_equal "", ".depends".pathmap("%x")
+ assert_equal "", "dir/.depends".pathmap("%x")
+ end
+
+ def test_x_returns_everything_but_extension
+ assert_equal "abc", "abc".pathmap("%X")
+ assert_equal "abc", "abc.rb".pathmap("%X")
+ assert_equal "abc.xyz", "abc.xyz.rb".pathmap("%X")
+ assert_equal "ab.xyz", "ab.xyz.rb".pathmap("%X")
+ assert_equal "a.xyz", "a.xyz.rb".pathmap("%X")
+ assert_equal "abc", "abc.rb".pathmap("%X")
+ assert_equal "ab", "ab.rb".pathmap("%X")
+ assert_equal "a", "a.rb".pathmap("%X")
+ assert_equal ".depends", ".depends".pathmap("%X")
+ assert_equal "a/dir/.depends", "a/dir/.depends".pathmap("%X")
+ assert_equal "/.depends", "/.depends".pathmap("%X")
+ end
+
+ def test_p_returns_entire_pathname
+ assert_equal "abc.rb", "abc.rb".pathmap("%p")
+ assert_equal "this/is/a/dir/abc.rb", "this/is/a/dir/abc.rb".pathmap("%p")
+ assert_equal "/this/is/a/dir/abc.rb", "/this/is/a/dir/abc.rb".pathmap("%p")
+ end
+
+ def test_dash_returns_empty_string
+ assert_equal "", "abc.rb".pathmap("%-")
+ assert_equal "abc.rb", "abc.rb".pathmap("%X%-%x")
+ end
+
+ def test_percent_percent_returns_percent
+ assert_equal "a%b", "".pathmap("a%%b")
+ end
+
+ def test_undefined_percent_causes_error
+ assert_raises(ArgumentError) {
+ "dir/abc.rb".pathmap("%z")
+ }
+ end
+
+ def test_pattern_returns_substitutions
+ assert_equal "bin/org/osb",
+ "src/org/osb/Xyz.java".pathmap("%{src,bin}d")
+ end
+
+ def test_pattern_can_use_backreferences
+ assert_equal "dir/hi/is", "dir/this/is".pathmap("%{t(hi)s,\\1}p")
+ end
+
+ def test_pattern_with_star_replacement_string_uses_block
+ assert_equal "src/ORG/osb",
+ "src/org/osb/Xyz.java".pathmap("%{/org,*}d") { |d| d.upcase }
+ assert_equal "Xyz.java",
+ "src/org/osb/Xyz.java".pathmap("%{.*,*}f") { |f| f.capitalize }
+ end
+
+ def test_pattern_with_no_replacement_nor_block_substitutes_empty_string
+ assert_equal "bc.rb", "abc.rb".pathmap("%{a}f")
+ end
+
+ def test_pattern_works_with_certain_valid_operators
+ assert_equal "dir/xbc.rb", "dir/abc.rb".pathmap("%{a,x}p")
+ assert_equal "d1r", "dir/abc.rb".pathmap("%{i,1}d")
+ assert_equal "xbc.rb", "dir/abc.rb".pathmap("%{a,x}f")
+ assert_equal ".Rb", "dir/abc.rb".pathmap("%{r,R}x")
+ assert_equal "xbc", "dir/abc.rb".pathmap("%{a,x}n")
+ end
+
+ def test_multiple_patterns
+ assert_equal "this/is/b/directory/abc.rb",
+ "this/is/a/dir/abc.rb".pathmap("%{a,b;dir,\\0ectory}p")
+ end
+
+ def test_partial_directory_selection_works_with_patterns
+ assert_equal "this/is/a/long",
+ "this/is/a/really/long/path/ok.rb".pathmap("%{/really/,/}5d")
+ end
+
+ def test_pattern_with_invalid_operator
+ ex = assert_raises(ArgumentError) do
+ "abc.xyz".pathmap("%{src,bin}z")
+ end
+ assert_match(/unknown.*pathmap.*spec.*z/i, ex.message)
+ end
+
+ def test_works_with_windows_separators
+ if File::ALT_SEPARATOR
+ assert_equal "abc", 'dir\abc.rb'.pathmap("%n")
+ assert_equal 'this\is\a\dir',
+ 'this\is\a\dir\abc.rb'.pathmap("%d")
+ end
+ end
+
+ def test_complex_patterns
+ sep = "".pathmap("%s")
+ assert_equal(
+ "dir/abc.rb",
+ "dir/abc.rb".pathmap("%d/%n%x"))
+ assert_equal(
+ "./abc.rb",
+ "abc.rb".pathmap("%d/%n%x"))
+ assert_equal(
+ "Your file extension is '.rb'",
+ "dir/abc.rb".pathmap("Your file extension is '%x'"))
+ assert_equal(
+ "bin/org/onstepback/proj/A.class",
+ "src/org/onstepback/proj/A.java".pathmap("%{src,bin}d/%n.class"))
+ assert_equal(
+ "src_work/bin/org/onstepback/proj/A.class",
+ "src_work/src/org/onstepback/proj/A.java".
+ pathmap('%{\bsrc\b,bin}X.class'))
+ assert_equal(
+ ".depends.bak",
+ ".depends".pathmap("%X.bak"))
+ assert_equal(
+ "d#{sep}a/b/c#{sep}file.txt",
+ "a/b/c/d/file.txt".pathmap("%-1d%s%3d%s%f"))
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_path_map_explode.rb b/jni/ruby/test/rake/test_rake_path_map_explode.rb
new file mode 100644
index 0000000..a79235e
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_path_map_explode.rb
@@ -0,0 +1,34 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakePathMapExplode < Rake::TestCase
+ def setup
+ super
+
+ String.class_eval { public :pathmap_explode }
+ end
+
+ def teardown
+ String.class_eval { protected :pathmap_explode }
+
+ super
+ end
+
+ def test_explode
+ assert_equal ['a'], 'a'.pathmap_explode
+ assert_equal ['a', 'b'], 'a/b'.pathmap_explode
+ assert_equal ['a', 'b', 'c'], 'a/b/c'.pathmap_explode
+ assert_equal ['/', 'a'], '/a'.pathmap_explode
+ assert_equal ['/', 'a', 'b'], '/a/b'.pathmap_explode
+ assert_equal ['/', 'a', 'b', 'c'], '/a/b/c'.pathmap_explode
+
+ if File::ALT_SEPARATOR
+ assert_equal ['c:.', 'a'], 'c:a'.pathmap_explode
+ assert_equal ['c:.', 'a', 'b'], 'c:a/b'.pathmap_explode
+ assert_equal ['c:.', 'a', 'b', 'c'], 'c:a/b/c'.pathmap_explode
+ assert_equal ['c:/', 'a'], 'c:/a'.pathmap_explode
+ assert_equal ['c:/', 'a', 'b'], 'c:/a/b'.pathmap_explode
+ assert_equal ['c:/', 'a', 'b', 'c'], 'c:/a/b/c'.pathmap_explode
+ end
+ end
+end
+
diff --git a/jni/ruby/test/rake/test_rake_path_map_partial.rb b/jni/ruby/test/rake/test_rake_path_map_partial.rb
new file mode 100644
index 0000000..566e681
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_path_map_partial.rb
@@ -0,0 +1,18 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakePathMapPartial < Rake::TestCase
+ def test_pathmap_partial
+ @path = "1/2/file"
+ def @path.call(n)
+ pathmap_partial(n)
+ end
+ assert_equal("1", @path.call(1))
+ assert_equal("1/2", @path.call(2))
+ assert_equal("1/2", @path.call(3))
+ assert_equal(".", @path.call(0))
+ assert_equal("2", @path.call(-1))
+ assert_equal("1/2", @path.call(-2))
+ assert_equal("1/2", @path.call(-3))
+ end
+end
+
diff --git a/jni/ruby/test/rake/test_rake_pathname_extensions.rb b/jni/ruby/test/rake/test_rake_pathname_extensions.rb
new file mode 100644
index 0000000..7da702d
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_pathname_extensions.rb
@@ -0,0 +1,15 @@
+require File.expand_path('../helper', __FILE__)
+require 'rake/ext/pathname'
+
+class TestRakePathnameExtensions < Rake::TestCase
+ def test_ext_works_on_pathnames
+ pathname = Pathname.new("abc.foo")
+ assert_equal Pathname.new("abc.bar"), pathname.ext("bar")
+ end
+
+ def test_path_map_works_on_pathnames
+ pathname = Pathname.new("this/is/a/dir/abc.rb")
+ assert_equal Pathname.new("abc.rb"), pathname.pathmap("%f")
+ assert_equal Pathname.new("this/is/a/dir"), pathname.pathmap("%d")
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_pseudo_status.rb b/jni/ruby/test/rake/test_rake_pseudo_status.rb
new file mode 100644
index 0000000..51b3fef
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_pseudo_status.rb
@@ -0,0 +1,21 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakePseudoStatus < Rake::TestCase
+ def test_with_zero_exit_status
+ s = Rake::PseudoStatus.new
+ assert_equal 0, s.exitstatus
+ assert_equal 0, s.to_i
+ assert_equal 0, s >> 8
+ refute s.stopped?
+ assert s.exited?
+ end
+
+ def test_with_99_exit_status
+ s = Rake::PseudoStatus.new(99)
+ assert_equal 99, s.exitstatus
+ assert_equal 25344, s.to_i
+ assert_equal 99, s >> 8
+ refute s.stopped?
+ assert s.exited?
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_rake_test_loader.rb b/jni/ruby/test/rake/test_rake_rake_test_loader.rb
new file mode 100644
index 0000000..0485c4c
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_rake_test_loader.rb
@@ -0,0 +1,20 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeRakeTestLoader < Rake::TestCase
+
+ def test_pattern
+ orig_loaded_features = $:.dup
+ FileUtils.touch 'foo.rb'
+ FileUtils.touch 'test_a.rb'
+ FileUtils.touch 'test_b.rb'
+
+ ARGV.replace %w[foo.rb test_*.rb -v]
+
+ load File.join(@rake_lib, 'rake/rake_test_loader.rb')
+
+ assert_equal %w[-v], ARGV
+ ensure
+ $:.replace orig_loaded_features
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_reduce_compat.rb b/jni/ruby/test/rake/test_rake_reduce_compat.rb
new file mode 100644
index 0000000..d295266
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_reduce_compat.rb
@@ -0,0 +1,26 @@
+require File.expand_path('../helper', __FILE__)
+require 'open3'
+
+class TestRakeReduceCompat < Rake::TestCase
+ include RubyRunner
+
+ def invoke_normal(task_name)
+ rake task_name.to_s
+ @out
+ end
+
+ def test_no_deprecated_dsl
+ rakefile %q{
+ task :check_task do
+ Module.new { p defined?(task) }
+ end
+
+ task :check_file do
+ Module.new { p defined?(file) }
+ end
+ }
+
+ assert_equal "nil", invoke_normal(:check_task).chomp
+ assert_equal "nil", invoke_normal(:check_file).chomp
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_require.rb b/jni/ruby/test/rake/test_rake_require.rb
new file mode 100644
index 0000000..d229edb
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_require.rb
@@ -0,0 +1,40 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeRequire < Rake::TestCase
+
+ def test_can_load_rake_library
+ rakefile_rakelib
+ app = Rake::Application.new
+
+ assert app.instance_eval {
+ rake_require("test2", ['rakelib'], [])
+ }
+ end
+
+ def test_wont_reload_rake_library
+ rakefile_rakelib
+ app = Rake::Application.new
+
+ paths = ['rakelib']
+ loaded_files = []
+ app.rake_require("test2", paths, loaded_files)
+
+ assert ! app.instance_eval {
+ rake_require("test2", paths, loaded_files)
+ }
+ end
+
+ def test_throws_error_if_library_not_found
+ rakefile_rakelib
+
+ app = Rake::Application.new
+ ex = assert_raises(LoadError) {
+ assert app.instance_eval {
+ rake_require("testx", ['rakelib'], [])
+ }
+ }
+ assert_match(/(can *not|can't)\s+find/i, ex.message)
+ assert_match(/testx/, ex.message)
+ end
+end
+
diff --git a/jni/ruby/test/rake/test_rake_rules.rb b/jni/ruby/test/rake/test_rake_rules.rb
new file mode 100644
index 0000000..ece75e5
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_rules.rb
@@ -0,0 +1,388 @@
+require File.expand_path('../helper', __FILE__)
+require 'fileutils'
+
+class TestRakeRules < Rake::TestCase
+ include Rake
+
+ SRCFILE = "abc.c"
+ SRCFILE2 = "xyz.c"
+ FTNFILE = "abc.f"
+ OBJFILE = "abc.o"
+ FOOFILE = "foo"
+ DOTFOOFILE = ".foo"
+
+ def setup
+ super
+
+ Task.clear
+ @runs = []
+ end
+
+ def test_multiple_rules1
+ create_file(FTNFILE)
+ delete_file(SRCFILE)
+ delete_file(OBJFILE)
+ rule(/\.o$/ => ['.c']) do @runs << :C end
+ rule(/\.o$/ => ['.f']) do @runs << :F end
+ t = Task[OBJFILE]
+ t.invoke
+ Task[OBJFILE].invoke
+ assert_equal [:F], @runs
+ end
+
+ def test_multiple_rules2
+ create_file(FTNFILE)
+ delete_file(SRCFILE)
+ delete_file(OBJFILE)
+ rule(/\.o$/ => ['.f']) do @runs << :F end
+ rule(/\.o$/ => ['.c']) do @runs << :C end
+ Task[OBJFILE].invoke
+ assert_equal [:F], @runs
+ end
+
+ def test_create_with_source
+ create_file(SRCFILE)
+ rule(/\.o$/ => ['.c']) do |t|
+ @runs << t.name
+ assert_equal OBJFILE, t.name
+ assert_equal SRCFILE, t.source
+ end
+ Task[OBJFILE].invoke
+ assert_equal [OBJFILE], @runs
+ end
+
+ def test_single_dependent
+ create_file(SRCFILE)
+ rule(/\.o$/ => '.c') do |t|
+ @runs << t.name
+ end
+ Task[OBJFILE].invoke
+ assert_equal [OBJFILE], @runs
+ end
+
+ def test_rule_can_be_created_by_string
+ create_file(SRCFILE)
+ rule '.o' => ['.c'] do |t|
+ @runs << t.name
+ end
+ Task[OBJFILE].invoke
+ assert_equal [OBJFILE], @runs
+ end
+
+ def test_rule_prereqs_can_be_created_by_string
+ create_file(SRCFILE)
+ rule '.o' => '.c' do |t|
+ @runs << t.name
+ end
+ Task[OBJFILE].invoke
+ assert_equal [OBJFILE], @runs
+ end
+
+ def test_plain_strings_as_dependents_refer_to_files
+ create_file(SRCFILE)
+ rule '.o' => SRCFILE do |t|
+ @runs << t.name
+ end
+ Task[OBJFILE].invoke
+ assert_equal [OBJFILE], @runs
+ end
+
+ def test_file_names_beginning_with_dot_can_be_tricked_into_referring_to_file
+ verbose(false) do
+ create_file('.foo')
+ rule '.o' => "./.foo" do |t|
+ @runs << t.name
+ end
+ Task[OBJFILE].invoke
+ assert_equal [OBJFILE], @runs
+ end
+ end
+
+ def test_file_names_beginning_with_dot_can_be_wrapped_in_lambda
+ verbose(false) do
+
+ create_file(".foo")
+ rule '.o' => lambda { ".foo" } do |t|
+ @runs << "#{t.name} - #{t.source}"
+ end
+ Task[OBJFILE].invoke
+ assert_equal ["#{OBJFILE} - .foo"], @runs
+ end
+ end
+
+ def test_file_names_containing_percent_can_be_wrapped_in_lambda
+ verbose(false) do
+ create_file("foo%x")
+ rule '.o' => lambda { "foo%x" } do |t|
+ @runs << "#{t.name} - #{t.source}"
+ end
+ Task[OBJFILE].invoke
+ assert_equal ["#{OBJFILE} - foo%x"], @runs
+ end
+ end
+
+ def test_non_extension_rule_name_refers_to_file
+ verbose(false) do
+ create_file("abc.c")
+ rule "abc" => '.c' do |t|
+ @runs << t.name
+ end
+ Task["abc"].invoke
+ assert_equal ["abc"], @runs
+ end
+ end
+
+ def test_pathmap_automatically_applies_to_name
+ verbose(false) do
+ create_file("zzabc.c")
+ rule ".o" => 'zz%{x,a}n.c' do |t|
+ @runs << "#{t.name} - #{t.source}"
+ end
+ Task["xbc.o"].invoke
+ assert_equal ["xbc.o - zzabc.c"], @runs
+ end
+ end
+
+ def test_plain_strings_are_just_filenames
+ verbose(false) do
+ create_file("plainname")
+ rule ".o" => 'plainname' do |t|
+ @runs << "#{t.name} - #{t.source}"
+ end
+ Task["xbc.o"].invoke
+ assert_equal ["xbc.o - plainname"], @runs
+ end
+ end
+
+ def test_rule_runs_when_explicit_task_has_no_actions
+ create_file(SRCFILE)
+ create_file(SRCFILE2)
+ delete_file(OBJFILE)
+ rule '.o' => '.c' do |t|
+ @runs << t.source
+ end
+ file OBJFILE => [SRCFILE2]
+ Task[OBJFILE].invoke
+ assert_equal [SRCFILE], @runs
+ end
+
+ def test_close_matches_on_name_do_not_trigger_rule
+ create_file("x.c")
+ rule '.o' => ['.c'] do |t|
+ @runs << t.name
+ end
+ assert_raises(RuntimeError) { Task['x.obj'].invoke }
+ assert_raises(RuntimeError) { Task['x.xyo'].invoke }
+ end
+
+ def test_rule_rebuilds_obj_when_source_is_newer
+ create_timed_files(OBJFILE, SRCFILE)
+ rule(/\.o$/ => ['.c']) do
+ @runs << :RULE
+ end
+ Task[OBJFILE].invoke
+ assert_equal [:RULE], @runs
+ end
+
+ def test_rule_with_two_sources_runs_if_both_sources_are_present
+ create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
+ rule OBJFILE => [lambda { SRCFILE }, lambda { SRCFILE2 }] do
+ @runs << :RULE
+ end
+ Task[OBJFILE].invoke
+ assert_equal [:RULE], @runs
+ end
+
+ def test_rule_with_two_sources_but_one_missing_does_not_run
+ create_timed_files(OBJFILE, SRCFILE)
+ delete_file(SRCFILE2)
+ rule OBJFILE => [lambda { SRCFILE }, lambda { SRCFILE2 }] do
+ @runs << :RULE
+ end
+ Task[OBJFILE].invoke
+ assert_equal [], @runs
+ end
+
+ def test_rule_with_two_sources_builds_both_sources
+ task 'x.aa'
+ task 'x.bb'
+ rule '.a' => '.aa' do
+ @runs << "A"
+ end
+ rule '.b' => '.bb' do
+ @runs << "B"
+ end
+ rule ".c" => ['.a', '.b'] do
+ @runs << "C"
+ end
+ Task["x.c"].invoke
+ assert_equal ["A", "B", "C"], @runs.sort
+ end
+
+ def test_second_rule_runs_when_first_rule_doesnt
+ create_timed_files(OBJFILE, SRCFILE)
+ delete_file(SRCFILE2)
+ rule OBJFILE => [lambda { SRCFILE }, lambda { SRCFILE2 }] do
+ @runs << :RULE1
+ end
+ rule OBJFILE => [lambda { SRCFILE }] do
+ @runs << :RULE2
+ end
+ Task[OBJFILE].invoke
+ assert_equal [:RULE2], @runs
+ end
+
+ def test_second_rule_doest_run_if_first_triggers
+ create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
+ rule OBJFILE => [lambda { SRCFILE }, lambda { SRCFILE2 }] do
+ @runs << :RULE1
+ end
+ rule OBJFILE => [lambda { SRCFILE }] do
+ @runs << :RULE2
+ end
+ Task[OBJFILE].invoke
+ assert_equal [:RULE1], @runs
+ end
+
+ def test_second_rule_doest_run_if_first_triggers_with_reversed_rules
+ create_timed_files(OBJFILE, SRCFILE, SRCFILE2)
+ rule OBJFILE => [lambda { SRCFILE }] do
+ @runs << :RULE1
+ end
+ rule OBJFILE => [lambda { SRCFILE }, lambda { SRCFILE2 }] do
+ @runs << :RULE2
+ end
+ Task[OBJFILE].invoke
+ assert_equal [:RULE1], @runs
+ end
+
+ def test_rule_with_proc_dependent_will_trigger
+ mkdir_p("src/jw")
+ create_file("src/jw/X.java")
+ rule %r(classes/.*\.class) => [
+ proc { |fn| fn.pathmap("%{classes,src}d/%n.java") }
+ ] do |task|
+ assert_equal task.name, 'classes/jw/X.class'
+ assert_equal task.source, 'src/jw/X.java'
+ @runs << :RULE
+ end
+ Task['classes/jw/X.class'].invoke
+ assert_equal [:RULE], @runs
+ ensure
+ rm_r("src", :verbose=>false) rescue nil
+ end
+
+ def test_proc_returning_lists_are_flattened_into_prereqs
+ ran = false
+ mkdir_p("flatten")
+ create_file("flatten/a.txt")
+ task 'flatten/b.data' do |t|
+ ran = true
+ touch t.name, :verbose => false
+ end
+ rule '.html' =>
+ proc { |fn|
+ [
+ fn.ext("txt"),
+ "flatten/b.data"
+ ]
+ } do |task|
+ end
+ Task['flatten/a.html'].invoke
+ assert ran, "Should have triggered flattened dependency"
+ ensure
+ rm_r("flatten", :verbose=>false) rescue nil
+ end
+
+ def test_recursive_rules_will_work_as_long_as_they_terminate
+ actions = []
+ create_file("abc.xml")
+ rule '.y' => '.xml' do actions << 'y' end
+ rule '.c' => '.y' do actions << 'c'end
+ rule '.o' => '.c' do actions << 'o'end
+ rule '.exe' => '.o' do actions << 'exe'end
+ Task["abc.exe"].invoke
+ assert_equal ['y', 'c', 'o', 'exe'], actions
+ end
+
+ def test_recursive_rules_that_dont_terminate_will_overflow
+ create_file("a.a")
+ prev = 'a'
+ ('b'..'z').each do |letter|
+ rule ".#{letter}" => ".#{prev}" do |t| puts "#{t.name}" end
+ prev = letter
+ end
+ ex = assert_raises(Rake::RuleRecursionOverflowError) {
+ Task["a.z"].invoke
+ }
+ assert_match(/a\.z => a.y/, ex.message)
+ end
+
+ def test_rules_with_bad_dependents_will_fail
+ rule "a" => [1] do |t| puts t.name end
+ assert_raises(RuntimeError) do Task['a'].invoke end
+ end
+
+ def test_string_rule_with_args
+ delete_file(OBJFILE)
+ create_file(SRCFILE)
+ rule '.o', [:a] => SRCFILE do |t, args|
+ assert_equal 'arg', args.a
+ end
+ Task[OBJFILE].invoke('arg')
+ end
+
+ def test_regex_rule_with_args
+ delete_file(OBJFILE)
+ create_file(SRCFILE)
+ rule(/.o$/, [:a] => SRCFILE) do |t, args|
+ assert_equal 'arg', args.a
+ end
+ Task[OBJFILE].invoke('arg')
+ end
+
+ def test_string_rule_with_args_and_lambda_prereq
+ delete_file(OBJFILE)
+ create_file(SRCFILE)
+ rule '.o', [:a] => [lambda{SRCFILE}]do |t, args|
+ assert_equal 'arg', args.a
+ end
+ Task[OBJFILE].invoke('arg')
+ end
+
+ def test_regex_rule_with_args_and_lambda_prereq
+ delete_file(OBJFILE)
+ create_file(SRCFILE)
+ rule(/.o$/, [:a] => [lambda{SRCFILE}]) do |t, args|
+ assert_equal 'arg', args.a
+ end
+ Task[OBJFILE].invoke('arg')
+ end
+
+ def test_rule_with_method_prereq
+ create_file(".foo")
+ obj = Object.new
+ def obj.find_prereq
+ ".foo"
+ end
+ rule '.o' => obj.method(:find_prereq) do |t|
+ @runs << "#{t.name} - #{t.source}"
+ end
+ Task[OBJFILE].invoke
+ assert_equal ["#{OBJFILE} - .foo"], @runs
+ end
+
+ def test_rule_with_one_arg_method_prereq
+ create_file(SRCFILE)
+ obj = Object.new
+ def obj.find_prereq(task_name)
+ task_name.ext(".c")
+ end
+ rule '.o' => obj.method(:find_prereq) do |t|
+ @runs << "#{t.name} - #{t.source}"
+ end
+ Task[OBJFILE].invoke
+ assert_equal ["#{OBJFILE} - abc.c"], @runs
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_scope.rb b/jni/ruby/test/rake/test_rake_scope.rb
new file mode 100644
index 0000000..ef06618
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_scope.rb
@@ -0,0 +1,44 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeScope < Rake::TestCase
+ include Rake
+
+ def test_path_against_empty_scope
+ scope = Scope.make
+ assert_equal scope, Scope::EMPTY
+ assert_equal scope.path, ""
+ end
+
+ def test_path_against_one_element
+ scope = Scope.make(:one)
+ assert_equal "one", scope.path
+ end
+
+ def test_path_against_two_elements
+ scope = Scope.make(:inner, :outer)
+ assert_equal "outer:inner", scope.path
+ end
+
+ def test_path_with_task_name
+ scope = Scope.make(:inner, :outer)
+ assert_equal "outer:inner:task", scope.path_with_task_name("task")
+ end
+
+ def test_path_with_task_name_against_empty_scope
+ scope = Scope.make
+ assert_equal "task", scope.path_with_task_name("task")
+ end
+
+ def test_conj_against_two_elements
+ scope = Scope.make.conj("B").conj("A")
+ assert_equal Scope.make("A", "B"), scope
+ end
+
+ def test_trim
+ scope = Scope.make("A", "B")
+ assert_equal scope, scope.trim(0)
+ assert_equal scope.tail, scope.trim(1)
+ assert_equal scope.tail.tail, scope.trim(2)
+ assert_equal scope.tail.tail, scope.trim(3)
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_task.rb b/jni/ruby/test/rake/test_rake_task.rb
new file mode 100644
index 0000000..d7f14ef
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_task.rb
@@ -0,0 +1,393 @@
+require File.expand_path('../helper', __FILE__)
+require 'fileutils'
+
+class TestRakeTask < Rake::TestCase
+ include Rake
+
+ def setup
+ super
+
+ Task.clear
+ Rake::TaskManager.record_task_metadata = true
+ end
+
+ def teardown
+ Rake::TaskManager.record_task_metadata = false
+ Rake.application.thread_pool.join
+
+ super
+ end
+
+ def test_create
+ arg = nil
+ t = task(:name) { |task| arg = task; 1234 }
+ assert_equal "name", t.name
+ assert_equal [], t.prerequisites
+ assert t.needed?
+ t.execute(0)
+ assert_equal t, arg
+ assert_nil t.source
+ assert_equal [], t.sources
+ assert_equal 1, t.locations.size
+ assert_match(/#{Regexp.quote(__FILE__)}/, t.locations.first)
+ end
+
+ def test_inspect
+ t = task(:foo => [:bar, :baz])
+ assert_equal "<Rake::Task foo => [bar, baz]>", t.inspect
+ end
+
+ def test_invoke
+ runlist = []
+ t1 = task(:t1 => [:t2, :t3]) { |t| runlist << t.name; 3321 }
+ task(:t2) { |t| runlist << t.name }
+ task(:t3) { |t| runlist << t.name }
+ assert_equal ["t2", "t3"], t1.prerequisites
+ t1.invoke
+ assert_equal ["t2", "t3", "t1"], runlist
+ end
+
+ def test_invoke_with_circular_dependencies
+ runlist = []
+ t1 = task(:t1 => [:t2]) { |t| runlist << t.name; 3321 }
+ t2 = task(:t2 => [:t1]) { |t| runlist << t.name }
+ assert_equal ["t2"], t1.prerequisites
+ assert_equal ["t1"], t2.prerequisites
+ ex = assert_raises RuntimeError do
+ t1.invoke
+ end
+ assert_match(/circular dependency/i, ex.message)
+ assert_match(/t1 => t2 => t1/, ex.message)
+ end
+
+ def test_dry_run_prevents_actions
+ Rake.application.options.dryrun = true
+ runlist = []
+ t1 = task(:t1) { |t| runlist << t.name; 3321 }
+ _, err = capture_io { t1.invoke }
+ assert_match(/execute .*t1/i, err)
+ assert_match(/dry run/i, err)
+ refute_match(/invoke/i, err)
+ assert_equal [], runlist
+ ensure
+ Rake.application.options.dryrun = false
+ end
+
+ def test_tasks_can_be_traced
+ Rake.application.options.trace = true
+ t1 = task(:t1)
+ _, err = capture_io {
+ t1.invoke
+ }
+ assert_match(/invoke t1/i, err)
+ assert_match(/execute t1/i, err)
+ ensure
+ Rake.application.options.trace = false
+ end
+
+ def test_no_double_invoke
+ runlist = []
+ t1 = task(:t1 => [:t2, :t3]) { |t| runlist << t.name; 3321 }
+ task(:t2 => [:t3]) { |t| runlist << t.name }
+ task(:t3) { |t| runlist << t.name }
+ t1.invoke
+ assert_equal ["t3", "t2", "t1"], runlist
+ end
+
+ def test_can_double_invoke_with_reenable
+ runlist = []
+ t1 = task(:t1) { |t| runlist << t.name }
+ t1.invoke
+ t1.reenable
+ t1.invoke
+ assert_equal ["t1", "t1"], runlist
+ end
+
+ def test_clear
+ desc "a task"
+ t = task("t" => "a") { }
+ t.clear
+ assert t.prerequisites.empty?, "prerequisites should be empty"
+ assert t.actions.empty?, "actions should be empty"
+ assert_nil t.comment, "comments should be empty"
+ end
+
+ def test_clear_prerequisites
+ t = task("t" => ["a", "b"])
+ assert_equal ['a', 'b'], t.prerequisites
+ t.clear_prerequisites
+ assert_equal [], t.prerequisites
+ end
+
+ def test_clear_actions
+ t = task("t") { }
+ t.clear_actions
+ assert t.actions.empty?, "actions should be empty"
+ end
+
+ def test_clear_comments
+ desc "the original foo"
+ task :foo => [:x] do
+ # Dummy action
+ end
+
+ task(:foo).clear_comments
+
+ desc "a slightly different foo"
+ task :foo
+
+ assert_equal "a slightly different foo", task(:foo).comment
+ assert_equal ["x"], task(:foo).prerequisites
+ assert_equal 1, task(:foo).actions.size
+ end
+
+ def test_find
+ task :tfind
+ assert_equal "tfind", Task[:tfind].name
+ ex = assert_raises(RuntimeError) { Task[:leaves] }
+ assert_equal "Don't know how to build task 'leaves'", ex.message
+ end
+
+ def test_defined
+ assert ! Task.task_defined?(:a)
+ task :a
+ assert Task.task_defined?(:a)
+ end
+
+ def test_multi_invocations
+ runs = []
+ p = proc do |t| runs << t.name end
+ task({ :t1 => [:t2, :t3] }, &p)
+ task({ :t2 => [:t3] }, &p)
+ task(:t3, &p)
+ Task[:t1].invoke
+ assert_equal ["t1", "t2", "t3"], runs.sort
+ end
+
+ def test_task_list
+ task :t2
+ task :t1 => [:t2]
+ assert_equal ["t1", "t2"], Task.tasks.map { |t| t.name }
+ end
+
+ def test_task_gives_name_on_to_s
+ task :abc
+ assert_equal "abc", Task[:abc].to_s
+ end
+
+ def test_symbols_can_be_prerequisites
+ task :a => :b
+ assert_equal ["b"], Task[:a].prerequisites
+ end
+
+ def test_strings_can_be_prerequisites
+ task :a => "b"
+ assert_equal ["b"], Task[:a].prerequisites
+ end
+
+ def test_arrays_can_be_prerequisites
+ task :a => ["b", "c"]
+ assert_equal ["b", "c"], Task[:a].prerequisites
+ end
+
+ def test_filelists_can_be_prerequisites
+ task :a => FileList.new.include("b", "c")
+ assert_equal ["b", "c"], Task[:a].prerequisites
+ end
+
+ def test_prerequiste_tasks_returns_tasks_not_strings
+ a = task :a => ["b", "c"]
+ b = task :b
+ c = task :c
+ assert_equal [b, c], a.prerequisite_tasks
+ end
+
+ def test_prerequiste_tasks_fails_if_prerequisites_are_undefined
+ a = task :a => ["b", "c"]
+ task :b
+ assert_raises(RuntimeError) do
+ a.prerequisite_tasks
+ end
+ end
+
+ def test_prerequiste_tasks_honors_namespaces
+ a = b = nil
+ namespace "X" do
+ a = task :a => ["b", "c"]
+ b = task :b
+ end
+ c = task :c
+
+ assert_equal [b, c], a.prerequisite_tasks
+ end
+
+ def test_all_prerequisite_tasks_includes_all_prerequisites
+ a = task :a => "b"
+ b = task :b => ["c", "d"]
+ c = task :c => "e"
+ d = task :d
+ e = task :e
+
+ assert_equal [b, c, d, e], a.all_prerequisite_tasks.sort_by { |t| t.name }
+ end
+
+ def test_all_prerequisite_tasks_does_not_include_duplicates
+ a = task :a => ["b", "c"]
+ b = task :b => "c"
+ c = task :c
+
+ assert_equal [b, c], a.all_prerequisite_tasks.sort_by { |t| t.name }
+ end
+
+ def test_all_prerequisite_tasks_includes_self_on_cyclic_dependencies
+ a = task :a => "b"
+ b = task :b => "a"
+
+ assert_equal [a, b], a.all_prerequisite_tasks.sort_by { |t| t.name }
+ end
+
+ def test_timestamp_returns_now_if_all_prereqs_have_no_times
+ a = task :a => ["b", "c"]
+ task :b
+ task :c
+
+ assert_in_delta Time.now, a.timestamp, 0.1, 'computer too slow?'
+ end
+
+ def test_timestamp_returns_latest_prereq_timestamp
+ a = task :a => ["b", "c"]
+ b = task :b
+ c = task :c
+
+ now = Time.now
+ def b.timestamp() Time.now + 10 end
+ def c.timestamp() Time.now + 5 end
+
+ assert_in_delta now, a.timestamp, 0.1, 'computer too slow?'
+ end
+
+ def test_always_multitask
+ mx = Mutex.new
+ result = []
+
+ t_a = task(:a) do |t|
+ sleep 0.2
+ mx.synchronize { result << t.name }
+ end
+
+ t_b = task(:b) do |t|
+ mx.synchronize { result << t.name }
+ end
+
+ t_c = task(:c => [:a, :b]) do |t|
+ mx.synchronize { result << t.name }
+ end
+
+ t_c.invoke
+
+ # task should always run in order
+ assert_equal ['a', 'b', 'c'], result
+
+ [t_a, t_b, t_c].each { |t| t.reenable }
+ result.clear
+
+ Rake.application.options.always_multitask = true
+ t_c.invoke
+
+ # with multitask, task 'b' should grab the mutex first
+ assert_equal ['b', 'a', 'c'], result
+ end
+
+ def test_investigation_output
+ t1 = task(:t1 => [:t2, :t3]) { |t| runlist << t.name; 3321 }
+ task(:t2)
+ task(:t3)
+ out = t1.investigation
+ assert_match(/class:\s*Rake::Task/, out)
+ assert_match(/needed:\s*true/, out)
+ assert_match(/pre-requisites:\s*--t[23]/, out)
+ end
+
+ # NOTE: Rail-ties uses comment=.
+ def test_comment_setting
+ t = task(:t, :name, :rev)
+ t.comment = "A Comment"
+ assert_equal "A Comment", t.comment
+ end
+
+ def test_comments_with_sentences
+ desc "Comment 1. Comment 2."
+ t = task(:t, :name, :rev)
+ assert_equal "Comment 1", t.comment
+ end
+
+ def test_comments_with_tabbed_sentences
+ desc "Comment 1.\tComment 2."
+ t = task(:t, :name, :rev)
+ assert_equal "Comment 1", t.comment
+ end
+
+ def test_comments_with_decimal_points
+ desc "Revision 1.2.3."
+ t = task(:t, :name, :rev)
+ assert_equal "Revision 1.2.3", t.comment
+ end
+
+ def test_comments_do_not_set
+ t = task(:t, :name, :rev)
+ assert_equal nil, t.comment
+ end
+
+ def test_comments_is_nil
+ t = task(:t, :name, :rev)
+ t.comment = nil
+ assert_equal nil, t.comment
+ end
+
+ def test_extended_comments
+ desc %{
+ This is a comment.
+
+ And this is the extended comment.
+ name -- Name of task to execute.
+ rev -- Software revision to use.
+ }
+ t = task(:t, :name, :rev)
+ assert_equal "[name,rev]", t.arg_description
+ assert_equal "This is a comment", t.comment
+ assert_match(/^\s*name -- Name/, t.full_comment)
+ assert_match(/^\s*rev -- Software/, t.full_comment)
+ assert_match(/\A\s*This is a comment\.$/, t.full_comment)
+ end
+
+ def test_multiple_comments
+ desc "line one"
+ t = task(:t)
+ desc "line two"
+ task(:t)
+ assert_equal "line one / line two", t.comment
+ end
+
+ def test_duplicate_comments
+ desc "line one"
+ t = task(:t)
+ desc "line one"
+ task(:t)
+ assert_equal "line one", t.comment
+ end
+
+ def test_interspersed_duplicate_comments
+ desc "line one"
+ t = task(:t)
+ desc "line two"
+ task(:t)
+ desc "line one"
+ task(:t)
+ assert_equal "line one / line two", t.comment
+ end
+
+ def test_source_is_first_prerequisite
+ t = task :t => ["preqA", "preqB"]
+ assert_equal "preqA", t.source
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_task_argument_parsing.rb b/jni/ruby/test/rake/test_rake_task_argument_parsing.rb
new file mode 100644
index 0000000..3cb5d9c
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_task_argument_parsing.rb
@@ -0,0 +1,119 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeTaskArgumentParsing < Rake::TestCase
+ def setup
+ super
+
+ @app = Rake::Application.new
+ end
+
+ def test_name_only
+ name, args = @app.parse_task_string("name")
+ assert_equal "name", name
+ assert_equal [], args
+ end
+
+ def test_empty_args
+ name, args = @app.parse_task_string("name[]")
+ assert_equal "name", name
+ assert_equal [], args
+ end
+
+ def test_one_argument
+ name, args = @app.parse_task_string("name[one]")
+ assert_equal "name", name
+ assert_equal ["one"], args
+ end
+
+ def test_two_arguments
+ name, args = @app.parse_task_string("name[one,two]")
+ assert_equal "name", name
+ assert_equal ["one", "two"], args
+ end
+
+ def test_can_handle_spaces_between_args
+ name, args = @app.parse_task_string("name[one, two,\tthree , \tfour]")
+ assert_equal "name", name
+ assert_equal ["one", "two", "three", "four"], args
+ end
+
+ def test_keeps_embedded_spaces
+ name, args = @app.parse_task_string("name[a one ana, two]")
+ assert_equal "name", name
+ assert_equal ["a one ana", "two"], args
+ end
+
+ def test_can_handle_commas_in_args
+ name, args = @app.parse_task_string("name[one, two, three_a\\, three_b, four]")
+ assert_equal "name", name
+ assert_equal ["one", "two", "three_a, three_b", "four"], args
+ end
+
+ def test_treat_blank_arg_as_empty_string
+ name, args = @app.parse_task_string("name[one,]")
+ assert_equal "name", name
+ assert_equal ["one", ""], args
+
+ name, args = @app.parse_task_string("name[one,,two]")
+ assert_equal "name", name
+ assert_equal ["one", "", "two"], args
+ end
+
+ def test_terminal_width_using_env
+ app = Rake::Application.new
+ app.terminal_columns = 1234
+
+ assert_equal 1234, app.terminal_width
+ end
+
+ def test_terminal_width_using_stty
+ def @app.unix?() true end
+ def @app.dynamic_width_stty() 1235 end
+ def @app.dynamic_width_tput() 0 end
+
+ assert_equal 1235, @app.terminal_width
+ end
+
+ def test_terminal_width_using_tput
+ def @app.unix?() true end
+ def @app.dynamic_width_stty() 0 end
+ def @app.dynamic_width_tput() 1236 end
+
+ assert_equal 1236, @app.terminal_width
+ end
+
+ def test_terminal_width_using_hardcoded_80
+ def @app.unix?() false end
+
+ assert_equal 80, @app.terminal_width
+ end
+
+ def test_terminal_width_with_failure
+ def @app.unix?() raise end
+
+ assert_equal 80, @app.terminal_width
+ end
+
+ def test_no_rakeopt
+ ARGV << '--trace'
+ app = Rake::Application.new
+ app.init
+ assert !app.options.silent
+ end
+
+ def test_rakeopt_with_blank_options
+ ARGV << '--trace'
+ app = Rake::Application.new
+ app.init
+ assert !app.options.silent
+ end
+
+ def test_rakeopt_with_silent_options
+ ENV['RAKEOPT'] = '-s'
+ app = Rake::Application.new
+
+ app.init
+
+ assert app.options.silent
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_task_arguments.rb b/jni/ruby/test/rake/test_rake_task_arguments.rb
new file mode 100644
index 0000000..369ecf6
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_task_arguments.rb
@@ -0,0 +1,127 @@
+require File.expand_path('../helper', __FILE__)
+
+######################################################################
+class TestRakeTaskArguments < Rake::TestCase
+ def teardown
+ ENV.delete('rev')
+ ENV.delete('VER')
+
+ super
+ end
+
+ def test_empty_arg_list_is_empty
+ ta = Rake::TaskArguments.new([], [])
+ assert_equal({}, ta.to_hash)
+ end
+
+ def test_multiple_values_in_args
+ ta = Rake::TaskArguments.new([:a, :b, :c], [:one, :two, :three])
+ assert_equal({:a => :one, :b => :two, :c => :three}, ta.to_hash)
+ end
+
+ def test_has_key
+ ta = Rake::TaskArguments.new([:a], [:one])
+ assert(ta.has_key?(:a))
+ refute(ta.has_key?(:b))
+ end
+
+ def test_to_s
+ ta = Rake::TaskArguments.new([:a, :b, :c], [1, 2, 3])
+ assert_equal ta.to_hash.inspect, ta.to_s
+ assert_equal ta.to_hash.inspect, ta.inspect
+ end
+
+ def test_enumerable_behavior
+ ta = Rake::TaskArguments.new([:a, :b, :c], [1, 2, 3])
+ assert_equal [10, 20, 30], ta.map { |k, v| v * 10 }.sort
+ end
+
+ def test_named_args
+ ta = Rake::TaskArguments.new(["aa", "bb"], [1, 2])
+ assert_equal 1, ta.aa
+ assert_equal 1, ta[:aa]
+ assert_equal 1, ta["aa"]
+ assert_equal 2, ta.bb
+ assert_nil ta.cc
+ end
+
+ def test_args_knows_its_names
+ ta = Rake::TaskArguments.new(["aa", "bb"], [1, 2])
+ assert_equal ["aa", "bb"], ta.names
+ end
+
+ def test_extra_names_are_nil
+ ta = Rake::TaskArguments.new(["aa", "bb", "cc"], [1, 2])
+ assert_nil ta.cc
+ end
+
+ def test_args_do_not_reference_env_values
+ ta = Rake::TaskArguments.new(["aa"], [1])
+ ENV['rev'] = "1.2"
+ ENV['VER'] = "2.3"
+ assert_nil ta.rev
+ assert_nil ta.ver
+ end
+
+ def test_creating_new_argument_scopes
+ parent = Rake::TaskArguments.new(['p'], [1])
+ child = parent.new_scope(['c', 'p'])
+ assert_equal({:p=>1}, child.to_hash)
+ assert_equal 1, child.p
+ assert_equal 1, child["p"]
+ assert_equal 1, child[:p]
+ assert_nil child.c
+ end
+
+ def test_child_hides_parent_arg_names
+ parent = Rake::TaskArguments.new(['aa'], [1])
+ child = Rake::TaskArguments.new(['aa'], [2], parent)
+ assert_equal 2, child.aa
+ end
+
+ def test_default_arguments_values_can_be_merged
+ ta = Rake::TaskArguments.new(["aa", "bb"], [nil, "original_val"])
+ ta.with_defaults({ :aa => 'default_val' })
+ assert_equal 'default_val', ta[:aa]
+ assert_equal 'original_val', ta[:bb]
+ end
+
+ def test_default_arguments_that_dont_match_names_are_ignored
+ ta = Rake::TaskArguments.new(["aa", "bb"], [nil, "original_val"])
+ ta.with_defaults({ "cc" => "default_val" })
+ assert_nil ta[:cc]
+ end
+
+ def test_all_and_extra_arguments_without_named_arguments
+ app = Rake::Application.new
+ _, args = app.parse_task_string("task[1,two,more]")
+ ta = Rake::TaskArguments.new([], args)
+ assert_equal [], ta.names
+ assert_equal ['1', 'two', 'more'], ta.to_a
+ assert_equal ['1', 'two', 'more'], ta.extras
+ end
+
+ def test_all_and_extra_arguments_with_named_arguments
+ app = Rake::Application.new
+ _, args = app.parse_task_string("task[1,two,more,still more]")
+ ta = Rake::TaskArguments.new([:first, :second], args)
+ assert_equal [:first, :second], ta.names
+ assert_equal "1", ta[:first]
+ assert_equal "two", ta[:second]
+ assert_equal ['1', 'two', 'more', 'still more'], ta.to_a
+ assert_equal ['more', 'still more'], ta.extras
+ end
+
+ def test_extra_args_with_less_than_named_arguments
+ app = Rake::Application.new
+ _, args = app.parse_task_string("task[1,two]")
+ ta = Rake::TaskArguments.new([:first, :second, :third], args)
+ assert_equal [:first, :second, :third], ta.names
+ assert_equal "1", ta[:first]
+ assert_equal "two", ta[:second]
+ assert_equal nil, ta[:third]
+ assert_equal ['1', 'two'], ta.to_a
+ assert_equal [], ta.extras
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_task_lib.rb b/jni/ruby/test/rake/test_rake_task_lib.rb
new file mode 100644
index 0000000..9f3f7e9
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_task_lib.rb
@@ -0,0 +1,9 @@
+require File.expand_path('../helper', __FILE__)
+require 'rake/tasklib'
+
+class TestRakeTaskLib < Rake::TestCase
+ def test_paste
+ tl = Rake::TaskLib.new
+ assert_equal :ab, tl.paste(:a, :b)
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_task_manager.rb b/jni/ruby/test/rake/test_rake_task_manager.rb
new file mode 100644
index 0000000..c2730b6
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_task_manager.rb
@@ -0,0 +1,178 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeTaskManager < Rake::TestCase
+
+ def setup
+ super
+
+ @tm = Rake::TestCase::TaskManager.new
+ end
+
+ def test_create_task_manager
+ refute_nil @tm
+ assert_equal [], @tm.tasks
+ end
+
+ def test_define_task
+ t = @tm.define_task(Rake::Task, :t)
+ assert_equal "t", t.name
+ assert_equal @tm, t.application
+ end
+
+ def test_index
+ e = assert_raises RuntimeError do
+ @tm['bad']
+ end
+
+ assert_equal "Don't know how to build task 'bad'", e.message
+ end
+
+ def test_name_lookup
+ t = @tm.define_task(Rake::Task, :t)
+ assert_equal t, @tm[:t]
+ end
+
+ def test_namespace_task_create
+ @tm.in_namespace("x") do
+ t = @tm.define_task(Rake::Task, :t)
+ assert_equal "x:t", t.name
+ end
+ assert_equal ["x:t"], @tm.tasks.map { |t| t.name }
+ end
+
+ def test_define_namespaced_task
+ t = @tm.define_task(Rake::Task, 'n:a:m:e:t')
+ assert_equal Rake::Scope.make("e", "m", "a", "n"), t.scope
+ assert_equal "n:a:m:e:t", t.name
+ assert_equal @tm, t.application
+ end
+
+ def test_define_namespace_in_namespace
+ t = nil
+ @tm.in_namespace("n") do
+ t = @tm.define_task(Rake::Task, 'a:m:e:t')
+ end
+ assert_equal Rake::Scope.make("e", "m", "a", "n"), t.scope
+ assert_equal "n:a:m:e:t", t.name
+ assert_equal @tm, t.application
+ end
+
+ def test_anonymous_namespace
+ anon_ns = @tm.in_namespace(nil) do
+ t = @tm.define_task(Rake::Task, :t)
+ assert_equal "_anon_1:t", t.name
+ end
+ task = anon_ns[:t]
+ assert_equal "_anon_1:t", task.name
+ end
+
+ def test_create_filetask_in_namespace
+ @tm.in_namespace("x") do
+ t = @tm.define_task(Rake::FileTask, "fn")
+ assert_equal "fn", t.name
+ end
+
+ assert_equal ["fn"], @tm.tasks.map { |t| t.name }
+ end
+
+ def test_namespace_yields_same_namespace_as_returned
+ yielded_namespace = nil
+ returned_namespace = @tm.in_namespace("x") do |ns|
+ yielded_namespace = ns
+ end
+ assert_equal returned_namespace, yielded_namespace
+ end
+
+ def test_name_lookup_with_implicit_file_tasks
+ FileUtils.touch 'README.rdoc'
+
+ t = @tm["README.rdoc"]
+
+ assert_equal "README.rdoc", t.name
+ assert Rake::FileTask === t
+ end
+
+ def test_name_lookup_with_nonexistent_task
+ assert_raises(RuntimeError) {
+ @tm["DOES NOT EXIST"]
+ }
+ end
+
+ def test_name_lookup_in_multiple_scopes
+ aa = nil
+ bb = nil
+ xx = @tm.define_task(Rake::Task, :xx)
+ top_z = @tm.define_task(Rake::Task, :z)
+ @tm.in_namespace("a") do
+ aa = @tm.define_task(Rake::Task, :aa)
+ mid_z = @tm.define_task(Rake::Task, :z)
+ ns_d = @tm.define_task(Rake::Task, "n:t")
+ @tm.in_namespace("b") do
+ bb = @tm.define_task(Rake::Task, :bb)
+ bot_z = @tm.define_task(Rake::Task, :z)
+
+ assert_equal Rake::Scope.make("b", "a"), @tm.current_scope
+
+ assert_equal bb, @tm["a:b:bb"]
+ assert_equal aa, @tm["a:aa"]
+ assert_equal xx, @tm["xx"]
+ assert_equal bot_z, @tm["z"]
+ assert_equal mid_z, @tm["^z"]
+ assert_equal top_z, @tm["^^z"]
+ assert_equal top_z, @tm["^^^z"] # Over the top
+ assert_equal top_z, @tm["rake:z"]
+ end
+
+ assert_equal Rake::Scope.make("a"), @tm.current_scope
+
+ assert_equal bb, @tm["a:b:bb"]
+ assert_equal aa, @tm["a:aa"]
+ assert_equal xx, @tm["xx"]
+ assert_equal bb, @tm["b:bb"]
+ assert_equal aa, @tm["aa"]
+ assert_equal mid_z, @tm["z"]
+ assert_equal top_z, @tm["^z"]
+ assert_equal top_z, @tm["^^z"] # Over the top
+ assert_equal top_z, @tm["rake:z"]
+ assert_equal ns_d, @tm["n:t"]
+ assert_equal ns_d, @tm["a:n:t"]
+ end
+
+ assert_equal Rake::Scope.make, @tm.current_scope
+
+ assert_equal Rake::Scope.make, xx.scope
+ assert_equal Rake::Scope.make('a'), aa.scope
+ assert_equal Rake::Scope.make('b', 'a'), bb.scope
+ end
+
+ def test_lookup_with_explicit_scopes
+ t1, t2, t3, s = (0...4).map { nil }
+ t1 = @tm.define_task(Rake::Task, :t)
+ @tm.in_namespace("a") do
+ t2 = @tm.define_task(Rake::Task, :t)
+ s = @tm.define_task(Rake::Task, :s)
+ @tm.in_namespace("b") do
+ t3 = @tm.define_task(Rake::Task, :t)
+ end
+ end
+ assert_equal t1, @tm[:t, Rake::Scope.make]
+ assert_equal t2, @tm[:t, Rake::Scope.make("a")]
+ assert_equal t3, @tm[:t, Rake::Scope.make("b", "a")]
+ assert_equal s, @tm[:s, Rake::Scope.make("b", "a")]
+ assert_equal s, @tm[:s, Rake::Scope.make("a")]
+ end
+
+ def test_correctly_scoped_prerequisites_are_invoked
+ values = []
+ @tm = Rake::Application.new
+ @tm.define_task(Rake::Task, :z) do values << "top z" end
+ @tm.in_namespace("a") do
+ @tm.define_task(Rake::Task, :z) do values << "next z" end
+ @tm.define_task(Rake::Task, :x => :z)
+ end
+
+ @tm["a:x"].invoke
+ assert_equal ["next z"], values
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_task_manager_argument_resolution.rb b/jni/ruby/test/rake/test_rake_task_manager_argument_resolution.rb
new file mode 100644
index 0000000..43fa2ac
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_task_manager_argument_resolution.rb
@@ -0,0 +1,19 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeTaskManagerArgumentResolution < Rake::TestCase
+
+ def test_good_arg_patterns
+ assert_equal [:t, [], []], task(:t)
+ assert_equal [:t, [], [:x]], task(:t => :x)
+ assert_equal [:t, [], [:x, :y]], task(:t => [:x, :y])
+
+ assert_equal [:t, [:a, :b], []], task(:t, [:a, :b])
+ assert_equal [:t, [:a, :b], [:x]], task(:t, [:a, :b] => :x)
+ assert_equal [:t, [:a, :b], [:x, :y]], task(:t, [:a, :b] => [:x, :y])
+ end
+
+ def task(*args)
+ tm = Rake::TestCase::TaskManager.new
+ tm.resolve_args(args)
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_task_with_arguments.rb b/jni/ruby/test/rake/test_rake_task_with_arguments.rb
new file mode 100644
index 0000000..8646fc0
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_task_with_arguments.rb
@@ -0,0 +1,172 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeTaskWithArguments < Rake::TestCase
+ include Rake
+
+ def setup
+ super
+
+ Task.clear
+ Rake::TaskManager.record_task_metadata = true
+ end
+
+ def teardown
+ Rake::TaskManager.record_task_metadata = false
+ Rake.application.thread_pool.join
+
+ super
+ end
+
+ def test_no_args_given
+ t = task :t
+ assert_equal [], t.arg_names
+ end
+
+ def test_args_given
+ t = task :t, :a, :b
+ assert_equal [:a, :b], t.arg_names
+ end
+
+ def test_name_and_needs
+ t = task(:t => [:pre])
+ assert_equal "t", t.name
+ assert_equal [], t.arg_names
+ assert_equal ["pre"], t.prerequisites
+ end
+
+ def test_name_args_and_prereqs
+ t = task(:t, [:x, :y] => [:pre])
+ assert_equal "t", t.name
+ assert_equal [:x, :y], t.arg_names
+ assert_equal ["pre"], t.prerequisites
+ end
+
+ def test_arg_list_is_empty_if_no_args_given
+ t = task(:t) { |tt, args| assert_equal({}, args.to_hash) }
+ t.invoke(1, 2, 3)
+ end
+
+ def test_tasks_can_access_arguments_as_hash
+ t = task :t, :a, :b, :c do |tt, args|
+ assert_equal({:a => 1, :b => 2, :c => 3}, args.to_hash)
+ assert_equal 1, args[:a]
+ assert_equal 2, args[:b]
+ assert_equal 3, args[:c]
+ assert_equal 1, args.a
+ assert_equal 2, args.b
+ assert_equal 3, args.c
+ end
+ t.invoke(1, 2, 3)
+ end
+
+ def test_actions_of_various_arity_are_ok_with_args
+ notes = []
+ t = task(:t, :x) do
+ notes << :a
+ end
+ t.enhance do | |
+ notes << :b
+ end
+ t.enhance do |task|
+ notes << :c
+ assert_kind_of Task, task
+ end
+ t.enhance do |t2, args|
+ notes << :d
+ assert_equal t, t2
+ assert_equal({:x => 1}, args.to_hash)
+ end
+ t.invoke(1)
+ assert_equal [:a, :b, :c, :d], notes
+ end
+
+ def test_arguments_are_passed_to_block
+ t = task(:t, :a, :b) { |tt, args|
+ assert_equal({ :a => 1, :b => 2 }, args.to_hash)
+ }
+ t.invoke(1, 2)
+ end
+
+ def test_extra_parameters_are_ignored
+ t = task(:t, :a) { |tt, args|
+ assert_equal 1, args.a
+ assert_nil args.b
+ }
+ t.invoke(1, 2)
+ end
+
+ def test_arguments_are_passed_to_all_blocks
+ counter = 0
+ t = task :t, :a
+ task :t do |tt, args|
+ assert_equal 1, args.a
+ counter += 1
+ end
+ task :t do |tt, args|
+ assert_equal 1, args.a
+ counter += 1
+ end
+ t.invoke(1)
+ assert_equal 2, counter
+ end
+
+ def test_block_with_no_parameters_is_ok
+ t = task(:t) { }
+ t.invoke(1, 2)
+ end
+
+ def test_name_with_args
+ desc "T"
+ t = task(:tt, :a, :b)
+ assert_equal "tt", t.name
+ assert_equal "T", t.comment
+ assert_equal "[a,b]", t.arg_description
+ assert_equal "tt[a,b]", t.name_with_args
+ assert_equal [:a, :b], t.arg_names
+ end
+
+ def test_named_args_are_passed_to_prereqs
+ value = nil
+ task(:pre, :rev) { |t, args| value = args.rev }
+ t = task(:t, [:name, :rev] => [:pre])
+ t.invoke("bill", "1.2")
+ assert_equal "1.2", value
+ end
+
+ def test_args_not_passed_if_no_prereq_names_on_task
+ task(:pre) { |t, args|
+ assert_equal({}, args.to_hash)
+ assert_equal "bill", args.name
+ }
+ t = task(:t, [:name, :rev] => [:pre])
+ t.invoke("bill", "1.2")
+ end
+
+ def test_args_not_passed_if_no_prereq_names_on_multitask
+ task(:pre) { |t, args|
+ assert_equal({}, args.to_hash)
+ assert_equal "bill", args.name
+ }
+ t = multitask(:t, [:name, :rev] => [:pre])
+ t.invoke("bill", "1.2")
+ end
+
+ def test_args_not_passed_if_no_arg_names
+ task(:pre, :rev) { |t, args|
+ assert_equal({}, args.to_hash)
+ }
+ t = task(:t => [:pre])
+ t.invoke("bill", "1.2")
+ end
+
+ def test_values_at
+ t = task(:pre, [:a, :b, :c]) { |task, args|
+ a, b, c = args.values_at(:a, :b, :c)
+ assert_equal %w[1 2 3], [a, b, c]
+ }
+
+ t.invoke(*%w[1 2 3])
+
+ # HACK no assertions
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_test_task.rb b/jni/ruby/test/rake/test_rake_test_task.rb
new file mode 100644
index 0000000..5c4be79
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_test_task.rb
@@ -0,0 +1,146 @@
+require File.expand_path('../helper', __FILE__)
+require 'rake/testtask'
+
+class TestRakeTestTask < Rake::TestCase
+ include Rake
+
+ def test_initialize
+ tt = Rake::TestTask.new do |t| end
+ refute_nil tt
+ assert_equal :test, tt.name
+ assert_equal ['lib'], tt.libs
+ assert_equal 'test/test*.rb', tt.pattern
+ assert_equal false, tt.verbose
+ assert Task.task_defined?(:test)
+ end
+
+ def test_initialize_override
+ tt = Rake::TestTask.new(:example) do |t|
+ t.description = "Run example tests"
+ t.libs = ['src', 'ext']
+ t.pattern = 'test/tc_*.rb'
+ t.verbose = true
+ end
+ refute_nil tt
+ assert_equal "Run example tests", tt.description
+ assert_equal :example, tt.name
+ assert_equal ['src', 'ext'], tt.libs
+ assert_equal 'test/tc_*.rb', tt.pattern
+ assert_equal true, tt.verbose
+ assert Task.task_defined?(:example)
+ end
+
+ def test_file_list_env_test
+ ENV['TEST'] = 'testfile.rb'
+ tt = Rake::TestTask.new do |t|
+ t.pattern = '*'
+ end
+
+ assert_equal ["testfile.rb"], tt.file_list.to_a
+ ensure
+ ENV.delete 'TEST'
+ end
+
+ def test_libs_equals
+ test_task = Rake::TestTask.new do |t|
+ t.libs << ["A", "B"]
+ end
+
+ path = %w[lib A B].join File::PATH_SEPARATOR
+
+ assert_equal "-I\"#{path}\"", test_task.ruby_opts_string
+ end
+
+ def test_libs_equals_empty
+ test_task = Rake::TestTask.new do |t|
+ t.libs = []
+ end
+
+ assert_equal '', test_task.ruby_opts_string
+ end
+
+ def test_pattern_equals
+ tt = Rake::TestTask.new do |t|
+ t.pattern = '*.rb'
+ end
+ assert_equal ['*.rb'], tt.file_list.to_a
+ end
+
+ def test_pattern_equals_test_files_equals
+ tt = Rake::TestTask.new do |t|
+ t.test_files = FileList['a.rb', 'b.rb']
+ t.pattern = '*.rb'
+ end
+ assert_equal ['a.rb', 'b.rb', '*.rb'], tt.file_list.to_a
+ end
+
+ def test_run_code_direct
+ test_task = Rake::TestTask.new do |t|
+ t.loader = :direct
+ end
+
+ assert_equal '-e "ARGV.each{|f| require f}"', test_task.run_code
+ end
+
+ def test_run_code_rake
+ spec = Gem::Specification.new 'rake', 0
+ spec.loaded_from = File.join Gem::Specification.dirs.last, 'rake-0.gemspec'
+ rake, Gem.loaded_specs['rake'] = Gem.loaded_specs['rake'], spec
+
+ test_task = Rake::TestTask.new do |t|
+ t.loader = :rake
+ end
+
+ assert_match(/\A-I".*?" ".*?"\Z/, test_task.run_code)
+ ensure
+ Gem.loaded_specs['rake'] = rake
+ end
+
+ def test_run_code_rake_default_gem
+ skip 'this ruby does not have default gems' unless
+ Gem::Specification.method_defined? :default_specifications_dir
+
+ default_spec = Gem::Specification.new 'rake', 0
+ default_spec.loaded_from = File.join Gem::Specification.default_specifications_dir, 'rake-0.gemspec'
+ begin
+ rake, Gem.loaded_specs['rake'] = Gem.loaded_specs['rake'], default_spec
+
+ test_task = Rake::TestTask.new do |t|
+ t.loader = :rake
+ end
+
+ assert_match(/\A(-I".*?" *)* ".*?"\Z/, test_task.run_code)
+ ensure
+ Gem.loaded_specs['rake'] = rake
+ end
+ end
+
+ def test_run_code_testrb_ruby_1_8_2
+ test_task = Rake::TestTask.new do |t|
+ t.loader = :testrb
+ end
+
+ def test_task.ruby_version() '1.8.2' end
+
+ assert_match(/^-S testrb +".*"$/, test_task.run_code)
+ end
+
+ def test_run_code_testrb_ruby_1_8_6
+ test_task = Rake::TestTask.new do |t|
+ t.loader = :testrb
+ end
+
+ def test_task.ruby_version() '1.8.6' end
+
+ assert_match(/^-S testrb +$/, test_task.run_code)
+ end
+
+ def test_test_files_equals
+ tt = Rake::TestTask.new do |t|
+ t.test_files = FileList['a.rb', 'b.rb']
+ end
+
+ assert_equal ["a.rb", 'b.rb'], tt.file_list.to_a
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_thread_pool.rb b/jni/ruby/test/rake/test_rake_thread_pool.rb
new file mode 100644
index 0000000..35a1fe9
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_thread_pool.rb
@@ -0,0 +1,145 @@
+require File.expand_path('../helper', __FILE__)
+require 'rake/thread_pool'
+
+class TestRakeTestThreadPool < Rake::TestCase
+ include Rake
+
+ def test_pool_executes_in_current_thread_for_zero_threads
+ pool = ThreadPool.new(0)
+ f = pool.future { Thread.current }
+ pool.join
+ assert_equal Thread.current, f.value
+ end
+
+ def test_pool_executes_in_other_thread_for_pool_of_size_one
+ pool = ThreadPool.new(1)
+ f = pool.future { Thread.current }
+ pool.join
+ refute_equal Thread.current, f.value
+ end
+
+ def test_pool_executes_in_two_other_threads_for_pool_of_size_two
+ pool = ThreadPool.new(2)
+ threads = 2.times.map {
+ pool.future {
+ sleep 0.1
+ Thread.current
+ }
+ }.each { |f|
+ f.value
+ }
+
+ refute_equal threads[0], threads[1]
+ refute_equal Thread.current, threads[0]
+ refute_equal Thread.current, threads[1]
+ ensure
+ pool.join
+ end
+
+ def test_pool_creates_the_correct_number_of_threads
+ pool = ThreadPool.new(2)
+ threads = Set.new
+ t_mutex = Mutex.new
+ 10.times.each do
+ pool.future do
+ sleep 0.02
+ t_mutex.synchronize { threads << Thread.current }
+ end
+ end
+ pool.join
+ assert_equal 2, threads.count
+ end
+
+ def test_pool_future_does_not_duplicate_arguments
+ pool = ThreadPool.new(2)
+ obj = Object.new
+ captured = nil
+ pool.future(obj) { |var| captured = var }
+ pool.join
+ assert_equal obj, captured
+ end
+
+ def test_pool_join_empties_queue
+ pool = ThreadPool.new(2)
+ repeat = 25
+ repeat.times {
+ pool.future do
+ repeat.times {
+ pool.future do
+ repeat.times {
+ pool.future do end
+ }
+ end
+ }
+ end
+ }
+
+ pool.join
+ assert_equal(
+ true,
+ pool.__send__(:__queue__).empty?,
+ "queue should be empty")
+ end
+
+ CustomError = Class.new(StandardError)
+
+ # test that throwing an exception way down in the blocks propagates
+ # to the top
+ def test_exceptions
+ pool = ThreadPool.new(10)
+
+ deep_exception_block = lambda do |count|
+ raise CustomError if count < 1
+ pool.future(count - 1, &deep_exception_block).value
+ end
+
+ assert_raises(CustomError) do
+ pool.future(2, &deep_exception_block).value
+ end
+ ensure
+ pool.join
+ end
+
+ def test_pool_prevents_deadlock
+ pool = ThreadPool.new(5)
+
+ common_dependency_a = pool.future { sleep 0.2 }
+ futures_a = 10.times.map {
+ pool.future {
+ common_dependency_a.value
+ sleep(rand() * 0.01)
+ }
+ }
+
+ common_dependency_b = pool.future { futures_a.each { |f| f.value } }
+ futures_b = 10.times.map {
+ pool.future {
+ common_dependency_b.value
+ sleep(rand() * 0.01)
+ }
+ }
+
+ futures_b.each { |f| f.value }
+ pool.join
+ end
+
+ def test_pool_reports_correct_results
+ pool = ThreadPool.new(7)
+
+ a = 18
+ b = 5
+ c = 3
+
+ result = a.times.map do
+ pool.future do
+ b.times.map do
+ pool.future { sleep rand * 0.001; c }
+ end.reduce(0) { |m, f| m + f.value }
+ end
+ end.reduce(0) { |m, f| m + f.value }
+
+ assert_equal a * b * c, result
+ pool.join
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_rake_top_level_functions.rb b/jni/ruby/test/rake/test_rake_top_level_functions.rb
new file mode 100644
index 0000000..fee702d
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_top_level_functions.rb
@@ -0,0 +1,71 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeTopLevelFunctions < Rake::TestCase
+
+ def setup
+ super
+
+ @app = Object.new
+
+ def @app.called
+ @called
+ end
+
+ def @app.method_missing(*a, &b)
+ @called ||= []
+ @called << [a, b]
+ nil
+ end
+
+ Rake.application = @app
+ end
+
+ def test_namespace
+ block = proc do end
+
+ namespace("xyz", &block)
+
+ expected = [
+ [[:in_namespace, 'xyz'], block]
+ ]
+
+ assert_equal expected, @app.called
+ end
+
+ def test_import
+ import('x', 'y', 'z')
+
+ expected = [
+ [[:add_import, 'x'], nil],
+ [[:add_import, 'y'], nil],
+ [[:add_import, 'z'], nil],
+ ]
+
+ assert_equal expected, @app.called
+ end
+
+ def test_when_writing
+ out, = capture_io do
+ when_writing("NOTWRITING") do
+ puts "WRITING"
+ end
+ end
+ assert_equal "WRITING\n", out
+ end
+
+ def test_when_not_writing
+ Rake::FileUtilsExt.nowrite_flag = true
+ _, err = capture_io do
+ when_writing("NOTWRITING") do
+ puts "WRITING"
+ end
+ end
+ assert_equal "DRYRUN: NOTWRITING\n", err
+ ensure
+ Rake::FileUtilsExt.nowrite_flag = false
+ end
+
+ def test_missing_other_constant
+ assert_raises(NameError) do Object.const_missing(:Xyz) end
+ end
+end
diff --git a/jni/ruby/test/rake/test_rake_win32.rb b/jni/ruby/test/rake/test_rake_win32.rb
new file mode 100644
index 0000000..fc2746a
--- /dev/null
+++ b/jni/ruby/test/rake/test_rake_win32.rb
@@ -0,0 +1,72 @@
+require File.expand_path('../helper', __FILE__)
+
+class TestRakeWin32 < Rake::TestCase
+
+ Win32 = Rake::Win32
+
+ def test_win32_system_dir_uses_home_if_defined
+ ENV['HOME'] = 'C:\\HP'
+
+ assert_equal "C:/HP/Rake", Win32.win32_system_dir
+ end
+
+ def test_win32_system_dir_uses_homedrive_homepath_when_no_home_defined
+ ENV['HOME'] = nil
+ ENV['HOMEDRIVE'] = 'C:'
+ ENV['HOMEPATH'] = '\\HP'
+
+ assert_equal "C:/HP/Rake", Win32.win32_system_dir
+ end
+
+ def test_win32_system_dir_uses_appdata_when_no_home_or_home_combo
+ ENV['APPDATA'] = "C:\\Documents and Settings\\HP\\Application Data"
+ ENV['HOME'] = nil
+ ENV['HOMEDRIVE'] = nil
+ ENV['HOMEPATH'] = nil
+
+ assert_equal "C:/Documents and Settings/HP/Application Data/Rake",
+ Win32.win32_system_dir
+ end
+
+ def test_win32_system_dir_fallback_to_userprofile_otherwise
+ ENV['HOME'] = nil
+ ENV['HOMEDRIVE'] = nil
+ ENV['HOMEPATH'] = nil
+ ENV['APPDATA'] = nil
+ ENV['USERPROFILE'] = "C:\\Documents and Settings\\HP"
+
+ assert_equal "C:/Documents and Settings/HP/Rake", Win32.win32_system_dir
+ end
+
+ def test_win32_system_dir_nil_of_no_env_vars
+ ENV['APPDATA'] = nil
+ ENV['HOME'] = nil
+ ENV['HOMEDRIVE'] = nil
+ ENV['HOMEPATH'] = nil
+ ENV['RAKE_SYSTEM'] = nil
+ ENV['USERPROFILE'] = nil
+
+ assert_raises(Rake::Win32::Win32HomeError) do
+ Win32.win32_system_dir
+ end
+ end
+
+ def test_win32_backtrace_with_different_case
+ ex = nil
+ begin
+ raise 'test exception'
+ rescue => ex
+ end
+
+ ex.set_backtrace ['abc', 'rakefile']
+
+ rake = Rake::Application.new
+ rake.options.trace = true
+ rake.instance_variable_set(:@rakefile, 'Rakefile')
+
+ _, err = capture_io { rake.display_error_message(ex) }
+
+ assert_match(/rakefile/, err)
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_thread_history_display.rb b/jni/ruby/test/rake/test_thread_history_display.rb
new file mode 100644
index 0000000..bb5879c
--- /dev/null
+++ b/jni/ruby/test/rake/test_thread_history_display.rb
@@ -0,0 +1,101 @@
+require File.expand_path('../helper', __FILE__)
+
+require 'rake/thread_history_display'
+
+class TestThreadHistoryDisplay < Rake::TestCase
+ def setup
+ super
+ @time = 1_000_000
+ @stats = []
+ @display = Rake::ThreadHistoryDisplay.new(@stats)
+ end
+
+ def test_banner
+ out, _ = capture_io do
+ @display.show
+ end
+ assert_match(/Job History/i, out)
+ end
+
+ def test_item_queued
+ @stats << event(:item_queued, :item_id => 123)
+ out, _ = capture_io do
+ @display.show
+ end
+ assert_match(/^ *1000000 +A +item_queued +item_id:1$/, out)
+ end
+
+ def test_item_dequeued
+ @stats << event(:item_dequeued, :item_id => 123)
+ out, _ = capture_io do
+ @display.show
+ end
+ assert_match(/^ *1000000 +A +item_dequeued +item_id:1$/, out)
+ end
+
+ def test_multiple_items
+ @stats << event(:item_queued, :item_id => 123)
+ @stats << event(:item_queued, :item_id => 124)
+ out, _ = capture_io do
+ @display.show
+ end
+ assert_match(/^ *1000000 +A +item_queued +item_id:1$/, out)
+ assert_match(/^ *1000001 +A +item_queued +item_id:2$/, out)
+ end
+
+ def test_waiting
+ @stats << event(:waiting, :item_id => 123)
+ out, _ = capture_io do
+ @display.show
+ end
+ assert_match(/^ *1000000 +A +waiting +item_id:1$/, out)
+ end
+
+ def test_continue
+ @stats << event(:continue, :item_id => 123)
+ out, _ = capture_io do
+ @display.show
+ end
+ assert_match(/^ *1000000 +A +continue +item_id:1$/, out)
+ end
+
+ def test_thread_deleted
+ @stats << event(
+ :thread_deleted,
+ :deleted_thread => 123_456,
+ :thread_count => 12)
+ out, _ = capture_io do
+ @display.show
+ end
+ assert_match(
+ /^ *1000000 +A +thread_deleted( +deleted_thread:B| +thread_count:12){2}$/,
+ out)
+ end
+
+ def test_thread_created
+ @stats << event(
+ :thread_created,
+ :new_thread => 123_456,
+ :thread_count => 13)
+ out, _ = capture_io do
+ @display.show
+ end
+ assert_match(
+ /^ *1000000 +A +thread_created( +new_thread:B| +thread_count:13){2}$/,
+ out)
+ end
+
+ private
+
+ def event(type, data = {})
+ result = {
+ :event => type,
+ :time => @time / 1_000_000.0,
+ :data => data,
+ :thread => Thread.current.object_id
+ }
+ @time += 1
+ result
+ end
+
+end
diff --git a/jni/ruby/test/rake/test_trace_output.rb b/jni/ruby/test/rake/test_trace_output.rb
new file mode 100644
index 0000000..f9aead9
--- /dev/null
+++ b/jni/ruby/test/rake/test_trace_output.rb
@@ -0,0 +1,52 @@
+require File.expand_path('../helper', __FILE__)
+require 'stringio'
+
+class TestTraceOutput < Rake::TestCase
+ include Rake::TraceOutput
+
+ class PrintSpy
+ attr_reader :result, :calls
+
+ def initialize
+ @result = ""
+ @calls = 0
+ end
+
+ def print(string)
+ @result << string
+ @calls += 1
+ end
+ end
+
+ def test_trace_issues_single_io_for_args_with_empty_args
+ spy = PrintSpy.new
+ trace_on(spy)
+ assert_equal "\n", spy.result
+ assert_equal 1, spy.calls
+ end
+
+ def test_trace_issues_single_io_for_args_multiple_strings
+ spy = PrintSpy.new
+ trace_on(spy, "HI\n", "LO")
+ assert_equal "HI\nLO\n", spy.result
+ assert_equal 1, spy.calls
+ end
+
+ def test_trace_handles_nil_objects
+ spy = PrintSpy.new
+ trace_on(spy, "HI\n", nil, "LO")
+ assert_equal "HI\nLO\n", spy.result
+ assert_equal 1, spy.calls
+ end
+
+ def test_trace_issues_single_io_for_args_multiple_strings_and_alternate_sep
+ old_sep = $\
+ $\ = "\r"
+ spy = PrintSpy.new
+ trace_on(spy, "HI\r", "LO")
+ assert_equal "HI\rLO\r", spy.result
+ assert_equal 1, spy.calls
+ ensure
+ $\ = old_sep
+ end
+end
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Amps and angle encoding.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Amps and angle encoding.text
new file mode 100644
index 0000000..0e9527f
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Amps and angle encoding.text
@@ -0,0 +1,21 @@
+AT&T has an ampersand in their name.
+
+AT&amp;T is another way to write it.
+
+This & that.
+
+4 < 5.
+
+6 > 5.
+
+Here's a [link] [1] with an ampersand in the URL.
+
+Here's a link with an amersand in the link text: [AT&T] [2].
+
+Here's an inline [link](/script?foo=1&bar=2).
+
+Here's an inline [link](</script?foo=1&bar=2>).
+
+
+[1]: http://example.com/?foo=1&bar=2
+[2]: http://att.com/ "AT&T" \ No newline at end of file
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Auto links.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Auto links.text
new file mode 100644
index 0000000..abbc488
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Auto links.text
@@ -0,0 +1,13 @@
+Link: <http://example.com/>.
+
+With an ampersand: <http://example.com/?foo=1&bar=2>
+
+* In a list?
+* <http://example.com/>
+* It should.
+
+> Blockquoted: <http://example.com/>
+
+Auto-links should not occur here: `<http://example.com/>`
+
+ or here: <http://example.com/> \ No newline at end of file
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Backslash escapes.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Backslash escapes.text
new file mode 100644
index 0000000..5b014cb
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Backslash escapes.text
@@ -0,0 +1,120 @@
+These should all get escaped:
+
+Backslash: \\
+
+Backtick: \`
+
+Asterisk: \*
+
+Underscore: \_
+
+Left brace: \{
+
+Right brace: \}
+
+Left bracket: \[
+
+Right bracket: \]
+
+Left paren: \(
+
+Right paren: \)
+
+Greater-than: \>
+
+Hash: \#
+
+Period: \.
+
+Bang: \!
+
+Plus: \+
+
+Minus: \-
+
+
+
+These should not, because they occur within a code block:
+
+ Backslash: \\
+
+ Backtick: \`
+
+ Asterisk: \*
+
+ Underscore: \_
+
+ Left brace: \{
+
+ Right brace: \}
+
+ Left bracket: \[
+
+ Right bracket: \]
+
+ Left paren: \(
+
+ Right paren: \)
+
+ Greater-than: \>
+
+ Hash: \#
+
+ Period: \.
+
+ Bang: \!
+
+ Plus: \+
+
+ Minus: \-
+
+
+Nor should these, which occur in code spans:
+
+Backslash: `\\`
+
+Backtick: `` \` ``
+
+Asterisk: `\*`
+
+Underscore: `\_`
+
+Left brace: `\{`
+
+Right brace: `\}`
+
+Left bracket: `\[`
+
+Right bracket: `\]`
+
+Left paren: `\(`
+
+Right paren: `\)`
+
+Greater-than: `\>`
+
+Hash: `\#`
+
+Period: `\.`
+
+Bang: `\!`
+
+Plus: `\+`
+
+Minus: `\-`
+
+
+These should get escaped, even though they're matching pairs for
+other Markdown constructs:
+
+\*asterisks\*
+
+\_underscores\_
+
+\`backticks\`
+
+This is a code span with a literal backslash-backtick sequence: `` \` ``
+
+This is a tag with unescaped backticks <span attr='`ticks`'>bar</span>.
+
+This is a tag with backslashes <span attr='\\backslashes\\'>bar</span>.
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Blockquotes with code blocks.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Blockquotes with code blocks.text
new file mode 100644
index 0000000..c31d171
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Blockquotes with code blocks.text
@@ -0,0 +1,11 @@
+> Example:
+>
+> sub status {
+> print "working";
+> }
+>
+> Or:
+>
+> sub status {
+> return "working";
+> }
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Code Blocks.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Code Blocks.text
new file mode 100644
index 0000000..b54b092
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Code Blocks.text
@@ -0,0 +1,14 @@
+ code block on the first line
+
+Regular text.
+
+ code block indented by spaces
+
+Regular text.
+
+ the lines in this block
+ all contain trailing spaces
+
+Regular Text.
+
+ code block on the last line \ No newline at end of file
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Code Spans.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Code Spans.text
new file mode 100644
index 0000000..750a197
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Code Spans.text
@@ -0,0 +1,6 @@
+`<test a="` content of attribute `">`
+
+Fix for backticks within HTML tag: <span attr='`ticks`'>like this</span>
+
+Here's how you put `` `backticks` `` in a code span.
+
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Hard-wrapped paragraphs with list-like lines.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Hard-wrapped paragraphs with list-like lines.text
new file mode 100644
index 0000000..f8a5b27
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Hard-wrapped paragraphs with list-like lines.text
@@ -0,0 +1,8 @@
+In Markdown 1.0.0 and earlier. Version
+8. This line turns into a list item.
+Because a hard-wrapped line in the
+middle of a paragraph looked like a
+list item.
+
+Here's one with a bullet.
+* criminey.
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Horizontal rules.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Horizontal rules.text
new file mode 100644
index 0000000..1594bda
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Horizontal rules.text
@@ -0,0 +1,67 @@
+Dashes:
+
+---
+
+ ---
+
+ ---
+
+ ---
+
+ ---
+
+- - -
+
+ - - -
+
+ - - -
+
+ - - -
+
+ - - -
+
+
+Asterisks:
+
+***
+
+ ***
+
+ ***
+
+ ***
+
+ ***
+
+* * *
+
+ * * *
+
+ * * *
+
+ * * *
+
+ * * *
+
+
+Underscores:
+
+___
+
+ ___
+
+ ___
+
+ ___
+
+ ___
+
+_ _ _
+
+ _ _ _
+
+ _ _ _
+
+ _ _ _
+
+ _ _ _
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML (Advanced).text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML (Advanced).text
new file mode 100644
index 0000000..86b7206
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML (Advanced).text
@@ -0,0 +1,15 @@
+Simple block on one line:
+
+<div>foo</div>
+
+And nested without indentation:
+
+<div>
+<div>
+<div>
+foo
+</div>
+<div style=">"/>
+</div>
+<div>bar</div>
+</div>
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML (Simple).text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML (Simple).text
new file mode 100644
index 0000000..14aa2dc
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML (Simple).text
@@ -0,0 +1,69 @@
+Here's a simple block:
+
+<div>
+ foo
+</div>
+
+This should be a code block, though:
+
+ <div>
+ foo
+ </div>
+
+As should this:
+
+ <div>foo</div>
+
+Now, nested:
+
+<div>
+ <div>
+ <div>
+ foo
+ </div>
+ </div>
+</div>
+
+This should just be an HTML comment:
+
+<!-- Comment -->
+
+Multiline:
+
+<!--
+Blah
+Blah
+-->
+
+Code block:
+
+ <!-- Comment -->
+
+Just plain comment, with trailing spaces on the line:
+
+<!-- foo -->
+
+Code:
+
+ <hr />
+
+Hr's:
+
+<hr>
+
+<hr/>
+
+<hr />
+
+<hr>
+
+<hr/>
+
+<hr />
+
+<hr class="foo" id="bar" />
+
+<hr class="foo" id="bar"/>
+
+<hr class="foo" id="bar" >
+
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML comments.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML comments.text
new file mode 100644
index 0000000..41d830d
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Inline HTML comments.text
@@ -0,0 +1,13 @@
+Paragraph one.
+
+<!-- This is a simple comment -->
+
+<!--
+ This is another comment.
+-->
+
+Paragraph two.
+
+<!-- one comment block -- -- with two comments -->
+
+The end.
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, inline style.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, inline style.text
new file mode 100644
index 0000000..09017a9
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, inline style.text
@@ -0,0 +1,12 @@
+Just a [URL](/url/).
+
+[URL and title](/url/ "title").
+
+[URL and title](/url/ "title preceded by two spaces").
+
+[URL and title](/url/ "title preceded by a tab").
+
+[URL and title](/url/ "title has spaces afterward" ).
+
+
+[Empty]().
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, reference style.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, reference style.text
new file mode 100644
index 0000000..341ec88
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, reference style.text
@@ -0,0 +1,71 @@
+Foo [bar] [1].
+
+Foo [bar][1].
+
+Foo [bar]
+[1].
+
+[1]: /url/ "Title"
+
+
+With [embedded [brackets]] [b].
+
+
+Indented [once][].
+
+Indented [twice][].
+
+Indented [thrice][].
+
+Indented [four][] times.
+
+ [once]: /url
+
+ [twice]: /url
+
+ [thrice]: /url
+
+ [four]: /url
+
+
+[b]: /url/
+
+* * *
+
+[this] [this] should work
+
+So should [this][this].
+
+And [this] [].
+
+And [this][].
+
+And [this].
+
+But not [that] [].
+
+Nor [that][].
+
+Nor [that].
+
+[Something in brackets like [this][] should work]
+
+[Same with [this].]
+
+In this case, [this](/somethingelse/) points to something else.
+
+Backslashing should suppress \[this] and [this\].
+
+[this]: foo
+
+
+* * *
+
+Here's one where the [link
+breaks] across lines.
+
+Here's another where the [link
+breaks] across lines, but with a line-ending space.
+
+
+[link breaks]: /url/
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, shortcut references.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, shortcut references.text
new file mode 100644
index 0000000..8c44c98
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Links, shortcut references.text
@@ -0,0 +1,20 @@
+This is the [simple case].
+
+[simple case]: /simple
+
+
+
+This one has a [line
+break].
+
+This one has a [line
+break] with a line-ending space.
+
+[line break]: /foo
+
+
+[this] [that] and the [other]
+
+[this]: /this
+[that]: /that
+[other]: /other
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Literal quotes in titles.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Literal quotes in titles.text
new file mode 100644
index 0000000..29d0e42
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Literal quotes in titles.text
@@ -0,0 +1,7 @@
+Foo [bar][].
+
+Foo [bar](/url/ "Title with "quotes" inside").
+
+
+ [bar]: /url/ "Title with "quotes" inside"
+
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text
new file mode 100644
index 0000000..486055c
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text
@@ -0,0 +1,306 @@
+Markdown: Basics
+================
+
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a class="selected" title="Markdown Basics">Basics</a></li>
+ <li><a href="/projects/markdown/syntax" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+
+
+Getting the Gist of Markdown's Formatting Syntax
+------------------------------------------------
+
+This page offers a brief overview of what it's like to use Markdown.
+The [syntax page] [s] provides complete, detailed documentation for
+every feature, but Markdown should be very easy to pick up simply by
+looking at a few examples of it in action. The examples on this page
+are written in a before/after style, showing example syntax and the
+HTML output produced by Markdown.
+
+It's also helpful to simply try Markdown out; the [Dingus] [d] is a
+web application that allows you type your own Markdown-formatted text
+and translate it to XHTML.
+
+**Note:** This document is itself written using Markdown; you
+can [see the source for it by adding '.text' to the URL] [src].
+
+ [s]: /projects/markdown/syntax "Markdown Syntax"
+ [d]: /projects/markdown/dingus "Markdown Dingus"
+ [src]: /projects/markdown/basics.text
+
+
+## Paragraphs, Headers, Blockquotes ##
+
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing spaces or tabs is considered
+blank.) Normal paragraphs should not be intended with spaces or tabs.
+
+Markdown offers two styles of headers: *Setext* and *atx*.
+Setext-style headers for `<h1>` and `<h2>` are created by
+"underlining" with equal signs (`=`) and hyphens (`-`), respectively.
+To create an atx-style header, you put 1-6 hash marks (`#`) at the
+beginning of the line -- the number of hashes equals the resulting
+HTML header level.
+
+Blockquotes are indicated using email-style '`>`' angle brackets.
+
+Markdown:
+
+ A First Level Header
+ ====================
+
+ A Second Level Header
+ ---------------------
+
+ Now is the time for all good men to come to
+ the aid of their country. This is just a
+ regular paragraph.
+
+ The quick brown fox jumped over the lazy
+ dog's back.
+
+ ### Header 3
+
+ > This is a blockquote.
+ >
+ > This is the second paragraph in the blockquote.
+ >
+ > ## This is an H2 in a blockquote
+
+
+Output:
+
+ <h1>A First Level Header</h1>
+
+ <h2>A Second Level Header</h2>
+
+ <p>Now is the time for all good men to come to
+ the aid of their country. This is just a
+ regular paragraph.</p>
+
+ <p>The quick brown fox jumped over the lazy
+ dog's back.</p>
+
+ <h3>Header 3</h3>
+
+ <blockquote>
+ <p>This is a blockquote.</p>
+
+ <p>This is the second paragraph in the blockquote.</p>
+
+ <h2>This is an H2 in a blockquote</h2>
+ </blockquote>
+
+
+
+### Phrase Emphasis ###
+
+Markdown uses asterisks and underscores to indicate spans of emphasis.
+
+Markdown:
+
+ Some of these words *are emphasized*.
+ Some of these words _are emphasized also_.
+
+ Use two asterisks for **strong emphasis**.
+ Or, if you prefer, __use two underscores instead__.
+
+Output:
+
+ <p>Some of these words <em>are emphasized</em>.
+ Some of these words <em>are emphasized also</em>.</p>
+
+ <p>Use two asterisks for <strong>strong emphasis</strong>.
+ Or, if you prefer, <strong>use two underscores instead</strong>.</p>
+
+
+
+## Lists ##
+
+Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
+`+`, and `-`) as list markers. These three markers are
+interchangable; this:
+
+ * Candy.
+ * Gum.
+ * Booze.
+
+this:
+
+ + Candy.
+ + Gum.
+ + Booze.
+
+and this:
+
+ - Candy.
+ - Gum.
+ - Booze.
+
+all produce the same output:
+
+ <ul>
+ <li>Candy.</li>
+ <li>Gum.</li>
+ <li>Booze.</li>
+ </ul>
+
+Ordered (numbered) lists use regular numbers, followed by periods, as
+list markers:
+
+ 1. Red
+ 2. Green
+ 3. Blue
+
+Output:
+
+ <ol>
+ <li>Red</li>
+ <li>Green</li>
+ <li>Blue</li>
+ </ol>
+
+If you put blank lines between items, you'll get `<p>` tags for the
+list item text. You can create multi-paragraph list items by indenting
+the paragraphs by 4 spaces or 1 tab:
+
+ * A list item.
+
+ With multiple paragraphs.
+
+ * Another item in the list.
+
+Output:
+
+ <ul>
+ <li><p>A list item.</p>
+ <p>With multiple paragraphs.</p></li>
+ <li><p>Another item in the list.</p></li>
+ </ul>
+
+
+
+### Links ###
+
+Markdown supports two styles for creating links: *inline* and
+*reference*. With both styles, you use square brackets to delimit the
+text you want to turn into a link.
+
+Inline-style links use parentheses immediately after the link text.
+For example:
+
+ This is an [example link](http://example.com/).
+
+Output:
+
+ <p>This is an <a href="http://example.com/">
+ example link</a>.</p>
+
+Optionally, you may include a title attribute in the parentheses:
+
+ This is an [example link](http://example.com/ "With a Title").
+
+Output:
+
+ <p>This is an <a href="http://example.com/" title="With a Title">
+ example link</a>.</p>
+
+Reference-style links allow you to refer to your links by names, which
+you define elsewhere in your document:
+
+ I get 10 times more traffic from [Google][1] than from
+ [Yahoo][2] or [MSN][3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+
+Output:
+
+ <p>I get 10 times more traffic from <a href="http://google.com/"
+ title="Google">Google</a> than from <a href="http://search.yahoo.com/"
+ title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/"
+ title="MSN Search">MSN</a>.</p>
+
+The title attribute is optional. Link names may contain letters,
+numbers and spaces, but are *not* case sensitive:
+
+ I start my morning with a cup of coffee and
+ [The New York Times][NY Times].
+
+ [ny times]: http://www.nytimes.com/
+
+Output:
+
+ <p>I start my morning with a cup of coffee and
+ <a href="http://www.nytimes.com/">The New York Times</a>.</p>
+
+
+### Images ###
+
+Image syntax is very much like link syntax.
+
+Inline (titles are optional):
+
+ ![alt text](/path/to/img.jpg "Title")
+
+Reference-style:
+
+ ![alt text][id]
+
+ [id]: /path/to/img.jpg "Title"
+
+Both of the above examples produce the same output:
+
+ <img src="/path/to/img.jpg" alt="alt text" title="Title" />
+
+
+
+### Code ###
+
+In a regular paragraph, you can create code span by wrapping text in
+backtick quotes. Any ampersands (`&`) and angle brackets (`<` or
+`>`) will automatically be translated into HTML entities. This makes
+it easy to use Markdown to write about HTML example code:
+
+ I strongly recommend against using any `<blink>` tags.
+
+ I wish SmartyPants used named entities like `&mdash;`
+ instead of decimal-encoded entites like `&#8212;`.
+
+Output:
+
+ <p>I strongly recommend against using any
+ <code>&lt;blink&gt;</code> tags.</p>
+
+ <p>I wish SmartyPants used named entities like
+ <code>&amp;mdash;</code> instead of decimal-encoded
+ entites like <code>&amp;#8212;</code>.</p>
+
+
+To specify an entire block of pre-formatted code, indent every line of
+the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`,
+and `>` characters will be escaped automatically.
+
+Markdown:
+
+ If you want your page to validate under XHTML 1.0 Strict,
+ you've got to put paragraph tags in your blockquotes:
+
+ <blockquote>
+ <p>For example.</p>
+ </blockquote>
+
+Output:
+
+ <p>If you want your page to validate under XHTML 1.0 Strict,
+ you've got to put paragraph tags in your blockquotes:</p>
+
+ <pre><code>&lt;blockquote&gt;
+ &lt;p&gt;For example.&lt;/p&gt;
+ &lt;/blockquote&gt;
+ </code></pre>
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text
new file mode 100644
index 0000000..57360a1
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Syntax.text
@@ -0,0 +1,888 @@
+Markdown: Syntax
+================
+
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a href="/projects/markdown/basics" title="Markdown Basics">Basics</a></li>
+ <li><a class="selected" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+
+
+* [Overview](#overview)
+ * [Philosophy](#philosophy)
+ * [Inline HTML](#html)
+ * [Automatic Escaping for Special Characters](#autoescape)
+* [Block Elements](#block)
+ * [Paragraphs and Line Breaks](#p)
+ * [Headers](#header)
+ * [Blockquotes](#blockquote)
+ * [Lists](#list)
+ * [Code Blocks](#precode)
+ * [Horizontal Rules](#hr)
+* [Span Elements](#span)
+ * [Links](#link)
+ * [Emphasis](#em)
+ * [Code](#code)
+ * [Images](#img)
+* [Miscellaneous](#misc)
+ * [Backslash Escapes](#backslash)
+ * [Automatic Links](#autolink)
+
+
+**Note:** This document is itself written using Markdown; you
+can [see the source for it by adding '.text' to the URL][src].
+
+ [src]: /projects/markdown/syntax.text
+
+* * *
+
+<h2 id="overview">Overview</h2>
+
+<h3 id="philosophy">Philosophy</h3>
+
+Markdown is intended to be as easy-to-read and easy-to-write as is feasible.
+
+Readability, however, is emphasized above all else. A Markdown-formatted
+document should be publishable as-is, as plain text, without looking
+like it's been marked up with tags or formatting instructions. While
+Markdown's syntax has been influenced by several existing text-to-HTML
+filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4],
+[Grutatext] [5], and [EtText] [6] -- the single biggest source of
+inspiration for Markdown's syntax is the format of plain text email.
+
+ [1]: http://docutils.sourceforge.net/mirror/setext.html
+ [2]: http://www.aaronsw.com/2002/atx/
+ [3]: http://textism.com/tools/textile/
+ [4]: http://docutils.sourceforge.net/rst.html
+ [5]: http://www.triptico.com/software/grutatxt.html
+ [6]: http://ettext.taint.org/doc/
+
+To this end, Markdown's syntax is comprised entirely of punctuation
+characters, which punctuation characters have been carefully chosen so
+as to look like what they mean. E.g., asterisks around a word actually
+look like \*emphasis\*. Markdown lists look like, well, lists. Even
+blockquotes look like quoted passages of text, assuming you've ever
+used email.
+
+
+
+<h3 id="html">Inline HTML</h3>
+
+Markdown's syntax is intended for one purpose: to be used as a
+format for *writing* for the web.
+
+Markdown is not a replacement for HTML, or even close to it. Its
+syntax is very small, corresponding only to a very small subset of
+HTML tags. The idea is *not* to create a syntax that makes it easier
+to insert HTML tags. In my opinion, HTML tags are already easy to
+insert. The idea for Markdown is to make it easy to read, write, and
+edit prose. HTML is a *publishing* format; Markdown is a *writing*
+format. Thus, Markdown's formatting syntax only addresses issues that
+can be conveyed in plain text.
+
+For any markup that is not covered by Markdown's syntax, you simply
+use HTML itself. There's no need to preface it or delimit it to
+indicate that you're switching from Markdown to HTML; you just use
+the tags.
+
+The only restrictions are that block-level HTML elements -- e.g. `<div>`,
+`<table>`, `<pre>`, `<p>`, etc. -- must be separated from surrounding
+content by blank lines, and the start and end tags of the block should
+not be indented with tabs or spaces. Markdown is smart enough not
+to add extra (unwanted) `<p>` tags around HTML block-level tags.
+
+For example, to add an HTML table to a Markdown article:
+
+ This is a regular paragraph.
+
+ <table>
+ <tr>
+ <td>Foo</td>
+ </tr>
+ </table>
+
+ This is another regular paragraph.
+
+Note that Markdown formatting syntax is not processed within block-level
+HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an
+HTML block.
+
+Span-level HTML tags -- e.g. `<span>`, `<cite>`, or `<del>` -- can be
+used anywhere in a Markdown paragraph, list item, or header. If you
+want, you can even use HTML tags instead of Markdown formatting; e.g. if
+you'd prefer to use HTML `<a>` or `<img>` tags instead of Markdown's
+link or image syntax, go right ahead.
+
+Unlike block-level HTML tags, Markdown syntax *is* processed within
+span-level tags.
+
+
+<h3 id="autoescape">Automatic Escaping for Special Characters</h3>
+
+In HTML, there are two characters that demand special treatment: `<`
+and `&`. Left angle brackets are used to start tags; ampersands are
+used to denote HTML entities. If you want to use them as literal
+characters, you must escape them as entities, e.g. `&lt;`, and
+`&amp;`.
+
+Ampersands in particular are bedeviling for web writers. If you want to
+write about 'AT&T', you need to write '`AT&amp;T`'. You even need to
+escape ampersands within URLs. Thus, if you want to link to:
+
+ http://images.google.com/images?num=30&q=larry+bird
+
+you need to encode the URL as:
+
+ http://images.google.com/images?num=30&amp;q=larry+bird
+
+in your anchor tag `href` attribute. Needless to say, this is easy to
+forget, and is probably the single most common source of HTML validation
+errors in otherwise well-marked-up web sites.
+
+Markdown allows you to use these characters naturally, taking care of
+all the necessary escaping for you. If you use an ampersand as part of
+an HTML entity, it remains unchanged; otherwise it will be translated
+into `&amp;`.
+
+So, if you want to include a copyright symbol in your article, you can write:
+
+ &copy;
+
+and Markdown will leave it alone. But if you write:
+
+ AT&T
+
+Markdown will translate it to:
+
+ AT&amp;T
+
+Similarly, because Markdown supports [inline HTML](#html), if you use
+angle brackets as delimiters for HTML tags, Markdown will treat them as
+such. But if you write:
+
+ 4 < 5
+
+Markdown will translate it to:
+
+ 4 &lt; 5
+
+However, inside Markdown code spans and blocks, angle brackets and
+ampersands are *always* encoded automatically. This makes it easy to use
+Markdown to write about HTML code. (As opposed to raw HTML, which is a
+terrible format for writing about HTML syntax, because every single `<`
+and `&` in your example code needs to be escaped.)
+
+
+* * *
+
+
+<h2 id="block">Block Elements</h2>
+
+
+<h3 id="p">Paragraphs and Line Breaks</h3>
+
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing but spaces or tabs is considered
+blank.) Normal paragraphs should not be intended with spaces or tabs.
+
+The implication of the "one or more consecutive lines of text" rule is
+that Markdown supports "hard-wrapped" text paragraphs. This differs
+significantly from most other text-to-HTML formatters (including Movable
+Type's "Convert Line Breaks" option) which translate every line break
+character in a paragraph into a `<br />` tag.
+
+When you *do* want to insert a `<br />` break tag using Markdown, you
+end a line with two or more spaces, then type return.
+
+Yes, this takes a tad more effort to create a `<br />`, but a simplistic
+"every line break is a `<br />`" rule wouldn't work for Markdown.
+Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l]
+work best -- and look better -- when you format them with hard breaks.
+
+ [bq]: #blockquote
+ [l]: #list
+
+
+
+<h3 id="header">Headers</h3>
+
+Markdown supports two styles of headers, [Setext] [1] and [atx] [2].
+
+Setext-style headers are "underlined" using equal signs (for first-level
+headers) and dashes (for second-level headers). For example:
+
+ This is an H1
+ =============
+
+ This is an H2
+ -------------
+
+Any number of underlining `=`'s or `-`'s will work.
+
+Atx-style headers use 1-6 hash characters at the start of the line,
+corresponding to header levels 1-6. For example:
+
+ # This is an H1
+
+ ## This is an H2
+
+ ###### This is an H6
+
+Optionally, you may "close" atx-style headers. This is purely
+cosmetic -- you can use this if you think it looks better. The
+closing hashes don't even need to match the number of hashes
+used to open the header. (The number of opening hashes
+determines the header level.) :
+
+ # This is an H1 #
+
+ ## This is an H2 ##
+
+ ### This is an H3 ######
+
+
+<h3 id="blockquote">Blockquotes</h3>
+
+Markdown uses email-style `>` characters for blockquoting. If you're
+familiar with quoting passages of text in an email message, then you
+know how to create a blockquote in Markdown. It looks best if you hard
+wrap the text and put a `>` before every line:
+
+ > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+ >
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ > id sem consectetuer libero luctus adipiscing.
+
+Markdown allows you to be lazy and only put the `>` before the first
+line of a hard-wrapped paragraph:
+
+ > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ id sem consectetuer libero luctus adipiscing.
+
+Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
+adding additional levels of `>`:
+
+ > This is the first level of quoting.
+ >
+ > > This is nested blockquote.
+ >
+ > Back to the first level.
+
+Blockquotes can contain other Markdown elements, including headers, lists,
+and code blocks:
+
+ > ## This is a header.
+ >
+ > 1. This is the first list item.
+ > 2. This is the second list item.
+ >
+ > Here's some example code:
+ >
+ > return shell_exec("echo $input | $markdown_script");
+
+Any decent text editor should make email-style quoting easy. For
+example, with BBEdit, you can make a selection and choose Increase
+Quote Level from the Text menu.
+
+
+<h3 id="list">Lists</h3>
+
+Markdown supports ordered (numbered) and unordered (bulleted) lists.
+
+Unordered lists use asterisks, pluses, and hyphens -- interchangably
+-- as list markers:
+
+ * Red
+ * Green
+ * Blue
+
+is equivalent to:
+
+ + Red
+ + Green
+ + Blue
+
+and:
+
+ - Red
+ - Green
+ - Blue
+
+Ordered lists use numbers followed by periods:
+
+ 1. Bird
+ 2. McHale
+ 3. Parish
+
+It's important to note that the actual numbers you use to mark the
+list have no effect on the HTML output Markdown produces. The HTML
+Markdown produces from the above list is:
+
+ <ol>
+ <li>Bird</li>
+ <li>McHale</li>
+ <li>Parish</li>
+ </ol>
+
+If you instead wrote the list in Markdown like this:
+
+ 1. Bird
+ 1. McHale
+ 1. Parish
+
+or even:
+
+ 3. Bird
+ 1. McHale
+ 8. Parish
+
+you'd get the exact same HTML output. The point is, if you want to,
+you can use ordinal numbers in your ordered Markdown lists, so that
+the numbers in your source match the numbers in your published HTML.
+But if you want to be lazy, you don't have to.
+
+If you do use lazy list numbering, however, you should still start the
+list with the number 1. At some point in the future, Markdown may support
+starting ordered lists at an arbitrary number.
+
+List markers typically start at the left margin, but may be indented by
+up to three spaces. List markers must be followed by one or more spaces
+or a tab.
+
+To make lists look nice, you can wrap items with hanging indents:
+
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+
+But if you want to be lazy, you don't have to:
+
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+
+If list items are separated by blank lines, Markdown will wrap the
+items in `<p>` tags in the HTML output. For example, this input:
+
+ * Bird
+ * Magic
+
+will turn into:
+
+ <ul>
+ <li>Bird</li>
+ <li>Magic</li>
+ </ul>
+
+But this:
+
+ * Bird
+
+ * Magic
+
+will turn into:
+
+ <ul>
+ <li><p>Bird</p></li>
+ <li><p>Magic</p></li>
+ </ul>
+
+List items may consist of multiple paragraphs. Each subsequent
+paragraph in a list item must be intended by either 4 spaces
+or one tab:
+
+ 1. This is a list item with two paragraphs. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit. Aliquam hendrerit
+ mi posuere lectus.
+
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet
+ vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
+ sit amet velit.
+
+ 2. Suspendisse id sem consectetuer libero luctus adipiscing.
+
+It looks nice if you indent every line of the subsequent
+paragraphs, but here again, Markdown will allow you to be
+lazy:
+
+ * This is a list item with two paragraphs.
+
+ This is the second paragraph in the list item. You're
+ only required to indent the first line. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit.
+
+ * Another item in the same list.
+
+To put a blockquote within a list item, the blockquote's `>`
+delimiters need to be indented:
+
+ * A list item with a blockquote:
+
+ > This is a blockquote
+ > inside a list item.
+
+To put a code block within a list item, the code block needs
+to be indented *twice* -- 8 spaces or two tabs:
+
+ * A list item with a code block:
+
+ <code goes here>
+
+
+It's worth noting that it's possible to trigger an ordered list by
+accident, by writing something like this:
+
+ 1986. What a great season.
+
+In other words, a *number-period-space* sequence at the beginning of a
+line. To avoid this, you can backslash-escape the period:
+
+ 1986\. What a great season.
+
+
+
+<h3 id="precode">Code Blocks</h3>
+
+Pre-formatted code blocks are used for writing about programming or
+markup source code. Rather than forming normal paragraphs, the lines
+of a code block are interpreted literally. Markdown wraps a code block
+in both `<pre>` and `<code>` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab. For example, given this input:
+
+ This is a normal paragraph:
+
+ This is a code block.
+
+Markdown will generate:
+
+ <p>This is a normal paragraph:</p>
+
+ <pre><code>This is a code block.
+ </code></pre>
+
+One level of indentation -- 4 spaces or 1 tab -- is removed from each
+line of the code block. For example, this:
+
+ Here is an example of AppleScript:
+
+ tell application "Foo"
+ beep
+ end tell
+
+will turn into:
+
+ <p>Here is an example of AppleScript:</p>
+
+ <pre><code>tell application "Foo"
+ beep
+ end tell
+ </code></pre>
+
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+
+ <div class="footer">
+ &copy; 2004 Foo Corporation
+ </div>
+
+will turn into:
+
+ <pre><code>&lt;div class="footer"&gt;
+ &amp;copy; 2004 Foo Corporation
+ &lt;/div&gt;
+ </code></pre>
+
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+
+
+
+<h3 id="hr">Horizontal Rules</h3>
+
+You can produce a horizontal rule tag (`<hr />`) by placing three or
+more hyphens, asterisks, or underscores on a line by themselves. If you
+wish, you may use spaces between the hyphens or asterisks. Each of the
+following lines will produce a horizontal rule:
+
+ * * *
+
+ ***
+
+ *****
+
+ - - -
+
+ ---------------------------------------
+
+ _ _ _
+
+
+* * *
+
+<h2 id="span">Span Elements</h2>
+
+<h3 id="link">Links</h3>
+
+Markdown supports two style of links: *inline* and *reference*.
+
+In both styles, the link text is delimited by [square brackets].
+
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+
+ This is [an example](http://example.com/ "Title") inline link.
+
+ [This link](http://example.net/) has no title attribute.
+
+Will produce:
+
+ <p>This is <a href="http://example.com/" title="Title">
+ an example</a> inline link.</p>
+
+ <p><a href="http://example.net/">This link</a> has no
+ title attribute.</p>
+
+If you're referring to a local resource on the same server, you can
+use relative paths:
+
+ See my [About](/about/) page for details.
+
+Reference-style links use a second set of square brackets, inside
+which you place a label of your choosing to identify the link:
+
+ This is [an example][id] reference-style link.
+
+You can optionally use a space to separate the sets of brackets:
+
+ This is [an example] [id] reference-style link.
+
+Then, anywhere in the document, you define your link label like this,
+on a line by itself:
+
+ [id]: http://example.com/ "Optional Title Here"
+
+That is:
+
+* Square brackets containing the link identifier (optionally
+ indented from the left margin using up to three spaces);
+* followed by a colon;
+* followed by one or more spaces (or tabs);
+* followed by the URL for the link;
+* optionally followed by a title attribute for the link, enclosed
+ in double or single quotes.
+
+The link URL may, optionally, be surrounded by angle brackets:
+
+ [id]: <http://example.com/> "Optional Title Here"
+
+You can put the title attribute on the next line and use extra spaces
+or tabs for padding, which tends to look better with longer URLs:
+
+ [id]: http://example.com/longish/path/to/resource/here
+ "Optional Title Here"
+
+Link definitions are only used for creating links during Markdown
+processing, and are stripped from your document in the HTML output.
+
+Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are *not* case sensitive. E.g. these two links:
+
+ [link text][a]
+ [link text][A]
+
+are equivalent.
+
+The *implicit link name* shortcut allows you to omit the name of the
+link, in which case the link text itself is used as the name.
+Just use an empty set of square brackets -- e.g., to link the word
+"Google" to the google.com web site, you could simply write:
+
+ [Google][]
+
+And then define the link:
+
+ [Google]: http://google.com/
+
+Because link names may contain spaces, this shortcut even works for
+multiple words in the link text:
+
+ Visit [Daring Fireball][] for more information.
+
+And then define the link:
+
+ [Daring Fireball]: http://daringfireball.net/
+
+Link definitions can be placed anywhere in your Markdown document. I
+tend to put them immediately after each paragraph in which they're
+used, but if you want, you can put them all at the end of your
+document, sort of like footnotes.
+
+Here's an example of reference links in action:
+
+ I get 10 times more traffic from [Google] [1] than from
+ [Yahoo] [2] or [MSN] [3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+
+Using the implicit link name shortcut, you could instead write:
+
+ I get 10 times more traffic from [Google][] than from
+ [Yahoo][] or [MSN][].
+
+ [google]: http://google.com/ "Google"
+ [yahoo]: http://search.yahoo.com/ "Yahoo Search"
+ [msn]: http://search.msn.com/ "MSN Search"
+
+Both of the above examples will produce the following HTML output:
+
+ <p>I get 10 times more traffic from <a href="http://google.com/"
+ title="Google">Google</a> than from
+ <a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
+ or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
+
+For comparison, here is the same paragraph written using
+Markdown's inline link style:
+
+ I get 10 times more traffic from [Google](http://google.com/ "Google")
+ than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
+ [MSN](http://search.msn.com/ "MSN Search").
+
+The point of reference-style links is not that they're easier to
+write. The point is that with reference-style links, your document
+source is vastly more readable. Compare the above examples: using
+reference-style links, the paragraph itself is only 81 characters
+long; with inline-style links, it's 176 characters; and as raw HTML,
+it's 234 characters. In the raw HTML, there's more markup than there
+is text.
+
+With Markdown's reference-style links, a source document much more
+closely resembles the final output, as rendered in a browser. By
+allowing you to move the markup-related metadata out of the paragraph,
+you can add links without interrupting the narrative flow of your
+prose.
+
+
+<h3 id="em">Emphasis</h3>
+
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`<strong>` tag. E.g., this input:
+
+ *single asterisks*
+
+ _single underscores_
+
+ **double asterisks**
+
+ __double underscores__
+
+will produce:
+
+ <em>single asterisks</em>
+
+ <em>single underscores</em>
+
+ <strong>double asterisks</strong>
+
+ <strong>double underscores</strong>
+
+You can use whichever style you prefer; the lone restriction is that
+the same character must be used to open and close an emphasis span.
+
+Emphasis can be used in the middle of a word:
+
+ un*fucking*believable
+
+But if you surround an `*` or `_` with spaces, it'll be treated as a
+literal asterisk or underscore.
+
+To produce a literal asterisk or underscore at a position where it
+would otherwise be used as an emphasis delimiter, you can backslash
+escape it:
+
+ \*this text is surrounded by literal asterisks\*
+
+
+
+<h3 id="code">Code</h3>
+
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+
+ Use the `printf()` function.
+
+will produce:
+
+ <p>Use the <code>printf()</code> function.</p>
+
+To include a literal backtick character within a code span, you can use
+multiple backticks as the opening and closing delimiters:
+
+ ``There is a literal backtick (`) here.``
+
+which will produce this:
+
+ <p><code>There is a literal backtick (`) here.</code></p>
+
+The backtick delimiters surrounding a code span may include spaces --
+one after the opening, one before the closing. This allows you to place
+literal backtick characters at the beginning or end of a code span:
+
+ A single backtick in a code span: `` ` ``
+
+ A backtick-delimited string in a code span: `` `foo` ``
+
+will produce:
+
+ <p>A single backtick in a code span: <code>`</code></p>
+
+ <p>A backtick-delimited string in a code span: <code>`foo`</code></p>
+
+With a code span, ampersands and angle brackets are encoded as HTML
+entities automatically, which makes it easy to include example HTML
+tags. Markdown will turn this:
+
+ Please don't use any `<blink>` tags.
+
+into:
+
+ <p>Please don't use any <code>&lt;blink&gt;</code> tags.</p>
+
+You can write this:
+
+ `&#8212;` is the decimal-encoded equivalent of `&mdash;`.
+
+to produce:
+
+ <p><code>&amp;#8212;</code> is the decimal-encoded
+ equivalent of <code>&amp;mdash;</code>.</p>
+
+
+
+<h3 id="img">Images</h3>
+
+Admittedly, it's fairly difficult to devise a "natural" syntax for
+placing images into a plain text document format.
+
+Markdown uses an image syntax that is intended to resemble the syntax
+for links, allowing for two styles: *inline* and *reference*.
+
+Inline image syntax looks like this:
+
+ ![Alt text](/path/to/img.jpg)
+
+ ![Alt text](/path/to/img.jpg "Optional title")
+
+That is:
+
+* An exclamation mark: `!`;
+* followed by a set of square brackets, containing the `alt`
+ attribute text for the image;
+* followed by a set of parentheses, containing the URL or path to
+ the image, and an optional `title` attribute enclosed in double
+ or single quotes.
+
+Reference-style image syntax looks like this:
+
+ ![Alt text][id]
+
+Where "id" is the name of a defined image reference. Image references
+are defined using syntax identical to link references:
+
+ [id]: url/to/image "Optional title attribute"
+
+As of this writing, Markdown has no syntax for specifying the
+dimensions of an image; if this is important to you, you can simply
+use regular HTML `<img>` tags.
+
+
+* * *
+
+
+<h2 id="misc">Miscellaneous</h2>
+
+<h3 id="autolink">Automatic Links</h3>
+
+Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:
+
+ <http://example.com/>
+
+Markdown will turn this into:
+
+ <a href="http://example.com/">http://example.com/</a>
+
+Automatic links for email addresses work similarly, except that
+Markdown will also perform a bit of randomized decimal and hex
+entity-encoding to help obscure your address from address-harvesting
+spambots. For example, Markdown will turn this:
+
+ <address@example.com>
+
+into something like this:
+
+ <a href="&#x6D;&#x61;i&#x6C;&#x74;&#x6F;:&#x61;&#x64;&#x64;&#x72;&#x65;
+ &#115;&#115;&#64;&#101;&#120;&#x61;&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;
+ &#109;">&#x61;&#x64;&#x64;&#x72;&#x65;&#115;&#115;&#64;&#101;&#120;&#x61;
+ &#109;&#x70;&#x6C;e&#x2E;&#99;&#111;&#109;</a>
+
+which will render in a browser as a clickable link to "address@example.com".
+
+(This sort of entity-encoding trick will indeed fool many, if not
+most, address-harvesting bots, but it definitely won't fool all of
+them. It's better than nothing, but an address published in this way
+will probably eventually start receiving spam.)
+
+
+
+<h3 id="backslash">Backslash Escapes</h3>
+
+Markdown allows you to use backslash escapes to generate literal
+characters which would otherwise have special meaning in Markdown's
+formatting syntax. For example, if you wanted to surround a word with
+literal asterisks (instead of an HTML `<em>` tag), you can backslashes
+before the asterisks, like this:
+
+ \*literal asterisks\*
+
+Markdown provides backslash escapes for the following characters:
+
+ \ backslash
+ ` backtick
+ * asterisk
+ _ underscore
+ {} curly braces
+ [] square brackets
+ () parentheses
+ # hash mark
+ + plus sign
+ - minus sign (hyphen)
+ . dot
+ ! exclamation mark
+
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Nested blockquotes.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Nested blockquotes.text
new file mode 100644
index 0000000..ed3c624
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Nested blockquotes.text
@@ -0,0 +1,5 @@
+> foo
+>
+> > bar
+>
+> foo
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Ordered and unordered lists.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Ordered and unordered lists.text
new file mode 100644
index 0000000..7f3b497
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Ordered and unordered lists.text
@@ -0,0 +1,131 @@
+## Unordered
+
+Asterisks tight:
+
+* asterisk 1
+* asterisk 2
+* asterisk 3
+
+
+Asterisks loose:
+
+* asterisk 1
+
+* asterisk 2
+
+* asterisk 3
+
+* * *
+
+Pluses tight:
+
++ Plus 1
++ Plus 2
++ Plus 3
+
+
+Pluses loose:
+
++ Plus 1
+
++ Plus 2
+
++ Plus 3
+
+* * *
+
+
+Minuses tight:
+
+- Minus 1
+- Minus 2
+- Minus 3
+
+
+Minuses loose:
+
+- Minus 1
+
+- Minus 2
+
+- Minus 3
+
+
+## Ordered
+
+Tight:
+
+1. First
+2. Second
+3. Third
+
+and:
+
+1. One
+2. Two
+3. Three
+
+
+Loose using tabs:
+
+1. First
+
+2. Second
+
+3. Third
+
+and using spaces:
+
+1. One
+
+2. Two
+
+3. Three
+
+Multiple paragraphs:
+
+1. Item 1, graf one.
+
+ Item 2. graf two. The quick brown fox jumped over the lazy dog's
+ back.
+
+2. Item 2.
+
+3. Item 3.
+
+
+
+## Nested
+
+* Tab
+ * Tab
+ * Tab
+
+Here's another:
+
+1. First
+2. Second:
+ * Fee
+ * Fie
+ * Foe
+3. Third
+
+Same thing but with paragraphs:
+
+1. First
+
+2. Second:
+ * Fee
+ * Fie
+ * Foe
+
+3. Third
+
+
+This was an error in Markdown 1.0.1:
+
+* this
+
+ * sub
+
+ that
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Strong and em together.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Strong and em together.text
new file mode 100644
index 0000000..95ee690
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Strong and em together.text
@@ -0,0 +1,7 @@
+***This is strong and em.***
+
+So is ***this*** word.
+
+___This is strong and em.___
+
+So is ___this___ word.
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Tabs.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Tabs.text
new file mode 100644
index 0000000..589d113
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Tabs.text
@@ -0,0 +1,21 @@
++ this is a list item
+ indented with tabs
+
++ this is a list item
+ indented with spaces
+
+Code:
+
+ this code block is indented by one tab
+
+And:
+
+ this code block is indented by two tabs
+
+And:
+
+ + this is an example list item
+ indented with tabs
+
+ + this is an example list item
+ indented with spaces
diff --git a/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Tidyness.text b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Tidyness.text
new file mode 100644
index 0000000..5f18b8d
--- /dev/null
+++ b/jni/ruby/test/rdoc/MarkdownTest_1.0.3/Tidyness.text
@@ -0,0 +1,5 @@
+> A list within a blockquote:
+>
+> * asterisk 1
+> * asterisk 2
+> * asterisk 3
diff --git a/jni/ruby/test/rdoc/README b/jni/ruby/test/rdoc/README
new file mode 100644
index 0000000..4ef17a7
--- /dev/null
+++ b/jni/ruby/test/rdoc/README
@@ -0,0 +1 @@
+you don't have to
diff --git a/jni/ruby/test/rdoc/binary.dat b/jni/ruby/test/rdoc/binary.dat
new file mode 100644
index 0000000..371950e
--- /dev/null
+++ b/jni/ruby/test/rdoc/binary.dat
Binary files differ
diff --git a/jni/ruby/test/rdoc/hidden.zip.txt b/jni/ruby/test/rdoc/hidden.zip.txt
new file mode 100644
index 0000000..2400e38
--- /dev/null
+++ b/jni/ruby/test/rdoc/hidden.zip.txt
@@ -0,0 +1 @@
+PK
diff --git a/jni/ruby/test/rdoc/test.ja.largedoc b/jni/ruby/test/rdoc/test.ja.largedoc
new file mode 100644
index 0000000..a9c6c46
--- /dev/null
+++ b/jni/ruby/test/rdoc/test.ja.largedoc
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+ 吾輩(わがはい)は猫である。名前はまだ無い。
+ どこで生れたかとんと見当(けんとう)がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪(どうあく)な種族であったそうだ。この書生というのは時々我々を捕(つかま)えて煮(に)て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。ただ彼の掌(てのひら)に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始(みはじめ)であろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶(やかん)だ。その後(ご)猫にもだいぶ逢(あ)ったがこんな片輪(かたわ)には一度も出会(でく)わした事がない。のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙(けむり)を吹く。どうも咽(む)せぽくて実に弱った。これが人間の飲む煙草(たばこ)というものである事はようやくこの頃知った。
diff --git a/jni/ruby/test/rdoc/test.ja.rdoc b/jni/ruby/test/rdoc/test.ja.rdoc
new file mode 100644
index 0000000..cd01cab
--- /dev/null
+++ b/jni/ruby/test/rdoc/test.ja.rdoc
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+
+こんにちは!
+
+初めまして。アーロンと申します。
+
+どんな食べ物が好きですか?私はフランスの料理が大好きです。
+日本の料理も大好きです。
+
+食べ物を食べるのが大好きだけど、お皿を洗うのが大嫌いです。
diff --git a/jni/ruby/test/rdoc/test.ja.txt b/jni/ruby/test/rdoc/test.ja.txt
new file mode 100644
index 0000000..96e1db9
--- /dev/null
+++ b/jni/ruby/test/rdoc/test.ja.txt
@@ -0,0 +1,8 @@
+こんにちは!
+
+初めまして。アーロンと申します。
+
+どんな食べ物が好きですか?私はフランスの料理が大好きです。
+日本の料理も大好きです。
+
+食べ物を食べるのが大好きだけど、お皿を洗うのが大嫌いです。
diff --git a/jni/ruby/test/rdoc/test.txt b/jni/ruby/test/rdoc/test.txt
new file mode 100644
index 0000000..16b14f5
--- /dev/null
+++ b/jni/ruby/test/rdoc/test.txt
@@ -0,0 +1 @@
+test file
diff --git a/jni/ruby/test/rdoc/test_rdoc_alias.rb b/jni/ruby/test/rdoc/test_rdoc_alias.rb
new file mode 100644
index 0000000..ff499af
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_alias.rb
@@ -0,0 +1,13 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocAlias < XrefTestCase
+
+ def test_to_s
+ a = RDoc::Alias.new nil, 'a', 'b', ''
+ a.parent = @c2
+
+ assert_equal 'alias: b -> #a in: RDoc::NormalClass C2 < Object', a.to_s
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_any_method.rb b/jni/ruby/test/rdoc/test_rdoc_any_method.rb
new file mode 100644
index 0000000..9030580
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_any_method.rb
@@ -0,0 +1,460 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocAnyMethod < XrefTestCase
+
+ def test_aref
+ m = RDoc::AnyMethod.new nil, 'method?'
+
+ assert_equal 'method-i-method-3F', m.aref
+
+ m.singleton = true
+
+ assert_equal 'method-c-method-3F', m.aref
+ end
+
+ def test_arglists
+ m = RDoc::AnyMethod.new nil, 'method'
+
+ assert_nil m.arglists
+
+ m.params = "(a, b)"
+ m.block_params = "c, d"
+
+ assert_equal "method(a, b) { |c, d| ... }", m.arglists
+
+ call_seq = <<-SEQ
+method(a) { |c| ... }
+method(a, b) { |c, d| ... }
+ SEQ
+
+ m.call_seq = call_seq.dup
+
+ assert_equal call_seq, m.arglists
+ end
+
+ def test_c_function
+ @c1_m.c_function = 'my_c1_m'
+
+ assert_equal 'my_c1_m', @c1_m.c_function
+ end
+
+ def test_call_seq_equals
+ m = RDoc::AnyMethod.new nil, nil
+
+ m.call_seq = ''
+
+ assert_nil m.call_seq
+
+ m.call_seq = 'foo'
+
+ assert_equal 'foo', m.call_seq
+ end
+
+ def test_full_name
+ assert_equal 'C1::m', @c1.method_list.first.full_name
+ end
+
+ def test_is_alias_for
+ assert_equal @c2_b, @c2_a.is_alias_for
+
+ # set string on instance variable
+ loaded = Marshal.load Marshal.dump @c2_a
+
+ loaded.store = @store
+
+ assert_equal @c2_b, loaded.is_alias_for, 'Marshal.load'
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.store = @store
+ m1.instance_variable_set :@is_alias_for, ['Missing', false, 'method']
+
+ assert_nil m1.is_alias_for, 'missing alias'
+ end
+
+ def test_markup_code
+ tokens = [
+ RDoc::RubyToken::TkCONSTANT. new(0, 0, 0, 'CONSTANT'),
+ ]
+
+ @c2_a.collect_tokens
+ @c2_a.add_tokens(*tokens)
+
+ expected = '<span class="ruby-constant">CONSTANT</span>'
+
+ assert_equal expected, @c2_a.markup_code
+ end
+
+ def test_markup_code_empty
+ assert_equal '', @c2_a.markup_code
+ end
+
+ def test_marshal_dump
+ @store.path = Dir.tmpdir
+ top_level = @store.add_file 'file.rb'
+
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.block_params = 'some_block'
+ m.call_seq = 'call_seq'
+ m.comment = 'this is a comment'
+ m.params = 'param'
+ m.record_location top_level
+
+ cm = top_level.add_class RDoc::ClassModule, 'Klass'
+ cm.add_method m
+
+ section = cm.sections.first
+
+ al = RDoc::Alias.new nil, 'method', 'aliased', 'alias comment'
+ al_m = m.add_alias al, cm
+
+ loaded = Marshal.load Marshal.dump m
+ loaded.store = @store
+
+ comment = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+
+ assert_equal m, loaded
+
+ assert_equal [al_m.name], loaded.aliases.map { |alas| alas.name }
+ assert_equal 'some_block', loaded.block_params
+ assert_equal 'call_seq', loaded.call_seq
+ assert_equal comment, loaded.comment
+ assert_equal top_level, loaded.file
+ assert_equal 'Klass#method', loaded.full_name
+ assert_equal 'method', loaded.name
+ assert_equal 'param', loaded.params
+ assert_equal nil, loaded.singleton # defaults to nil
+ assert_equal :public, loaded.visibility
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+ end
+
+ def test_marshal_load_aliased_method
+ aliased_method = Marshal.load Marshal.dump(@c2_a)
+
+ aliased_method.store = @store
+
+ assert_equal 'C2#a', aliased_method.full_name
+ assert_equal 'C2', aliased_method.parent_name
+ assert_equal '()', aliased_method.params
+ assert_equal @c2_b, aliased_method.is_alias_for, 'is_alias_for'
+ assert aliased_method.display?
+ end
+
+ def test_marshal_load_class_method
+ class_method = Marshal.load Marshal.dump(@c1.method_list.first)
+
+ assert_equal 'C1::m', class_method.full_name
+ assert_equal 'C1', class_method.parent_name
+ assert_equal '()', class_method.params
+ assert class_method.display?
+ end
+
+ def test_marshal_load_instance_method
+ instance_method = Marshal.load Marshal.dump(@c1.method_list.last)
+
+ assert_equal 'C1#m', instance_method.full_name
+ assert_equal 'C1', instance_method.parent_name
+ assert_equal '(foo)', instance_method.params
+ assert instance_method.display?
+ end
+
+ def test_marshal_load_version_0
+ @store.path = Dir.tmpdir
+ top_level = @store.add_file 'file.rb'
+
+ m = RDoc::AnyMethod.new nil, 'method'
+
+ cm = top_level.add_class RDoc::ClassModule, 'Klass'
+ cm.add_method m
+
+ section = cm.sections.first
+
+ al = RDoc::Alias.new nil, 'method', 'aliased', 'alias comment'
+ al_m = m.add_alias al, cm
+
+ loaded = Marshal.load "\x04\bU:\x14RDoc::AnyMethod[\x0Fi\x00I" +
+ "\"\vmethod\x06:\x06EF\"\x11Klass#method0:\vpublic" +
+ "o:\eRDoc::Markup::Document\x06:\v@parts[\x06" +
+ "o:\x1CRDoc::Markup::Paragraph\x06;\t[\x06I" +
+ "\"\x16this is a comment\x06;\x06FI" +
+ "\"\rcall_seq\x06;\x06FI\"\x0Fsome_block\x06;\x06F" +
+ "[\x06[\aI\"\faliased\x06;\x06Fo;\b\x06;\t[\x06" +
+ "o;\n\x06;\t[\x06I\"\x12alias comment\x06;\x06FI" +
+ "\"\nparam\x06;\x06F"
+
+ loaded.store = @store
+
+ comment = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+
+ assert_equal m, loaded
+
+ assert_equal [al_m.name], loaded.aliases.map { |alas| alas.name }
+ assert_equal 'some_block', loaded.block_params
+ assert_equal 'call_seq', loaded.call_seq
+ assert_equal comment, loaded.comment
+ assert_equal 'Klass#method', loaded.full_name
+ assert_equal 'method', loaded.name
+ assert_equal 'param', loaded.params
+ assert_equal nil, loaded.singleton # defaults to nil
+ assert_equal :public, loaded.visibility
+ assert_equal nil, loaded.file
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+ assert_nil loaded.is_alias_for
+
+ assert loaded.display?
+ end
+
+ def test_marshal_dump_version_2
+ @store.path = Dir.tmpdir
+ top_level = @store.add_file 'file.rb'
+
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.block_params = 'some_block'
+ m.call_seq = 'call_seq'
+ m.comment = 'this is a comment'
+ m.params = 'param'
+ m.record_location top_level
+
+ cm = top_level.add_class RDoc::ClassModule, 'Klass'
+ cm.add_method m
+
+ section = cm.sections.first
+
+ al = RDoc::Alias.new nil, 'method', 'aliased', 'alias comment'
+ al_m = m.add_alias al, cm
+
+ loaded = Marshal.load "\x04\bU:\x14RDoc::AnyMethod[\x14i\bI" +
+ "\"\vmethod\x06:\x06ETI" +
+ "\"\x11Klass#method\x06;\x06T0:\vpublic" +
+ "o:\eRDoc::Markup::Document\b:\v@parts[\x06" +
+ "o:\x1CRDoc::Markup::Paragraph\x06;\t[\x06I" +
+ "\"\x16this is a comment\x06;\x06T:\n@file0" +
+ ":0@omit_headings_from_table_of_contents_below0" +
+ "I\"\rcall_seq\x06;\x06TI\"\x0Fsome_block\x06" +
+ ";\x06T[\x06[\aI\"\faliased\x06;\x06To;\b\b;\t" +
+ "[\x06o;\n\x06;\t[\x06I\"\x12alias comment\x06" +
+ ";\x06T;\v0;\f0I\"\nparam\x06;\x06TI" +
+ "\"\ffile.rb\x06;\x06TFI\"\nKlass\x06;\x06T" +
+ "c\x16RDoc::ClassModule0"
+
+ loaded.store = @store
+
+ comment = doc(para('this is a comment'))
+
+ assert_equal m, loaded
+
+ assert_equal [al_m.name], loaded.aliases.map { |alas| alas.name }
+ assert_equal 'some_block', loaded.block_params
+ assert_equal 'call_seq', loaded.call_seq
+ assert_equal comment, loaded.comment
+ assert_equal top_level, loaded.file
+ assert_equal 'Klass#method', loaded.full_name
+ assert_equal 'method', loaded.name
+ assert_equal 'param', loaded.params
+ assert_equal nil, loaded.singleton # defaults to nil
+ assert_equal :public, loaded.visibility
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+ assert_nil loaded.is_alias_for
+ end
+
+ def test_name
+ m = RDoc::AnyMethod.new nil, nil
+
+ assert_nil m.name
+ end
+
+ def test_name_call_seq
+ m = RDoc::AnyMethod.new nil, nil
+
+ m.call_seq = "yields(name)\nyields(name, description)"
+
+ assert_equal 'yields', m.name
+ end
+
+ def test_name_call_seq_dot
+ m = RDoc::AnyMethod.new nil, nil
+
+ m.call_seq = "obj.yields(name)\nobj.yields(name, description)"
+
+ assert_equal 'yields', m.name
+ end
+
+ def test_param_list_block_params
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ m.block_params = 'c, d'
+
+ assert_equal %w[c d], m.param_list
+ end
+
+ def test_param_list_call_seq
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ call_seq = <<-SEQ
+method(a) { |c| ... }
+method(a, b) { |c, d| ... }
+ SEQ
+
+ m.call_seq = call_seq
+
+ assert_equal %w[a b c d], m.param_list
+ end
+
+ def test_param_list_default
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ m.params = '(b = default)'
+
+ assert_equal %w[b], m.param_list
+ end
+
+ def test_param_list_params
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ m.params = '(a, b)'
+
+ assert_equal %w[a b], m.param_list
+ end
+
+ def test_param_list_params_block_params
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ m.params = '(a, b)'
+ m.block_params = 'c, d'
+
+ assert_equal %w[a b c d], m.param_list
+ end
+
+ def test_param_list_empty_params_with_block
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ m.params = '()'
+ m.block_params = 'a, b'
+
+ assert_equal %w[a b], m.param_list
+ end
+
+ def test_param_list_ampersand_param_block_params
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ m.params = '(a, b, &block)'
+ m.block_params = 'c, d'
+
+ assert_equal %w[a b c d], m.param_list
+ end
+
+ def test_param_list_ampersand_param
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ m.params = '(a, b, &block)'
+
+ assert_equal %w[a b block], m.param_list
+ end
+
+ def test_param_seq
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+ m.params = 'a'
+
+ assert_equal '(a)', m.param_seq
+
+ m.params = '(a)'
+
+ assert_equal '(a)', m.param_seq
+
+ m.params = "(a,\n b)"
+
+ assert_equal '(a, b)', m.param_seq
+
+ m.block_params = "c,\n d"
+
+ assert_equal '(a, b) { |c, d| ... }', m.param_seq
+ end
+
+ def test_param_seq_call_seq
+ m = RDoc::AnyMethod.new nil, 'method'
+ m.parent = @c1
+
+ call_seq = <<-SEQ
+method(a) { |c| ... }
+method(a, b) { |c, d| ... }
+ SEQ
+
+ m.call_seq = call_seq
+
+ assert_equal '(a, b) { |c, d| }', m.param_seq
+
+ end
+
+ def test_parent_name
+ assert_equal 'C1', @c1.method_list.first.parent_name
+ assert_equal 'C1', @c1.method_list.last.parent_name
+ end
+
+ def test_store_equals
+ loaded = Marshal.load Marshal.dump(@c1.method_list.last)
+
+ loaded.store = @store
+
+ assert_equal @store, loaded.file.store
+ end
+
+ def test_superclass_method
+ m3 = RDoc::AnyMethod.new '', 'no_super'
+
+ m2 = RDoc::AnyMethod.new '', 'supers'
+ m2.calls_super = true
+
+ m1 = RDoc::AnyMethod.new '', 'supers'
+
+ c1 = RDoc::NormalClass.new 'Outer'
+ c1.store = @store
+ c1.add_method m1
+
+ c2 = RDoc::NormalClass.new 'Inner', c1
+ c2.store = @store
+ c2.add_method m2
+ c2.add_method m3
+
+ assert_nil m3.superclass_method,
+ 'no superclass method for no_super'
+
+ assert_equal m1, m2.superclass_method,
+ 'superclass method missing for supers'
+ end
+
+ def test_superclass_method_multilevel
+ m2 = RDoc::AnyMethod.new '', 'supers'
+ m2.calls_super = true
+
+ m1 = RDoc::AnyMethod.new '', 'supers'
+
+ c1 = RDoc::NormalClass.new 'Outer'
+ c1.store = @store
+ c1.add_method m1
+
+ c2 = RDoc::NormalClass.new 'Middle', c1
+ c2.store = @store
+
+ c3 = RDoc::NormalClass.new 'Inner', c2
+ c3.store = @store
+ c3.add_method m2
+
+ assert_equal m1, m2.superclass_method,
+ 'superclass method missing for supers'
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_attr.rb b/jni/ruby/test/rdoc/test_rdoc_attr.rb
new file mode 100644
index 0000000..a4922df
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_attr.rb
@@ -0,0 +1,190 @@
+require 'rdoc/test_case'
+
+class TestRDocAttr < RDoc::TestCase
+
+ def setup
+ super
+
+ @a = RDoc::Attr.new nil, 'attr', 'RW', ''
+ end
+
+ def test_aref
+ m = RDoc::Attr.new nil, 'attr', 'RW', nil
+
+ assert_equal 'attribute-i-attr', m.aref
+ end
+
+ def test_arglists
+ assert_nil @a.arglists
+ end
+
+ def test_block_params
+ assert_nil @a.block_params
+ end
+
+ def test_call_seq
+ assert_nil @a.call_seq
+ end
+
+ def test_definition
+ assert_equal 'attr_accessor', @a.definition
+
+ @a.rw = 'R'
+
+ assert_equal 'attr_reader', @a.definition
+
+ @a.rw = 'W'
+
+ assert_equal 'attr_writer', @a.definition
+ end
+
+ def test_full_name
+ assert_equal '(unknown)#attr', @a.full_name
+ end
+
+ def test_marshal_dump
+ tl = @store.add_file 'file.rb'
+
+ @a.comment = 'this is a comment'
+ @a.record_location tl
+
+ cm = tl.add_class RDoc::NormalClass, 'Klass'
+ cm.add_attribute @a
+
+ section = cm.sections.first
+
+ loaded = Marshal.load Marshal.dump @a
+ loaded.store = @store
+
+ assert_equal @a, loaded
+
+ comment = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+
+ assert_equal comment, loaded.comment
+ assert_equal 'file.rb', loaded.file.relative_name
+ assert_equal 'Klass#attr', loaded.full_name
+ assert_equal 'attr', loaded.name
+ assert_equal 'RW', loaded.rw
+ assert_equal false, loaded.singleton
+ assert_equal :public, loaded.visibility
+ assert_equal tl, loaded.file
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+ end
+
+ def test_marshal_dump_singleton
+ tl = @store.add_file 'file.rb'
+
+ @a.comment = 'this is a comment'
+ @a.record_location tl
+
+ cm = tl.add_class RDoc::NormalClass, 'Klass'
+ cm.add_attribute @a
+
+ section = cm.sections.first
+
+ @a.rw = 'R'
+ @a.singleton = true
+ @a.visibility = :protected
+
+ loaded = Marshal.load Marshal.dump @a
+ loaded.store = @store
+
+ assert_equal @a, loaded
+
+ comment = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+
+ assert_equal comment, loaded.comment
+ assert_equal 'Klass::attr', loaded.full_name
+ assert_equal 'attr', loaded.name
+ assert_equal 'R', loaded.rw
+ assert_equal true, loaded.singleton
+ assert_equal :protected, loaded.visibility
+ assert_equal tl, loaded.file
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+ end
+
+ def test_marshal_load_version_1
+ tl = @store.add_file 'file.rb'
+ cm = tl.add_class RDoc::NormalClass, 'Klass'
+ section = cm.sections.first
+
+ data = "\x04\bU:\x0FRDoc::Attr[\fi\x06I\"\tattr\x06:\x06EF" +
+ "\"\x0FKlass#attrI\"\aRW\x06;\x06F:\vpublic" +
+ "o:\eRDoc::Markup::Document\x06:\v@parts[\x06" +
+ "o:\x1CRDoc::Markup::Paragraph\x06;\t[\x06I" +
+ "\"\x16this is a comment\x06;\x06FF"
+
+ loaded = Marshal.load data
+ loaded.store = @store
+
+ comment = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+
+ assert_equal comment, loaded.comment
+ assert_equal 'Klass#attr', loaded.full_name
+ assert_equal 'attr', loaded.name
+ assert_equal 'RW', loaded.rw
+ assert_equal false, loaded.singleton
+ assert_equal :public, loaded.visibility
+
+ # version 2
+ assert_nil loaded.file
+
+ # version 3
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+
+ assert loaded.display?
+ end
+
+ def test_marshal_load_version_2
+ tl = @store.add_file 'file.rb'
+ cm = tl.add_class RDoc::NormalClass, 'Klass'
+ section = cm.sections.first
+
+ loaded = Marshal.load "\x04\bU:\x0FRDoc::Attr[\ri\aI\"\tattr\x06" +
+ ":\x06ETI\"\x0FKlass#attr\x06;\x06TI\"\aRW\x06" +
+ ";\x06T:\vpublico:\eRDoc::Markup::Document\a" +
+ ":\v@parts[\x06o:\x1CRDoc::Markup::Paragraph\x06;" +
+ "\t[\x06I\"\x16this is a comment\x06;\x06T:\n" +
+ "@file0FI\"\ffile.rb\x06;\x06T"
+ loaded.store = @store
+
+ comment = doc(para('this is a comment'))
+
+ assert_equal comment, loaded.comment
+ assert_equal 'Klass#attr', loaded.full_name
+ assert_equal 'attr', loaded.name
+ assert_equal 'RW', loaded.rw
+ assert_equal false, loaded.singleton
+ assert_equal :public, loaded.visibility
+ assert_equal tl, loaded.file
+
+ # version 3
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+
+ assert loaded.display?
+ end
+
+ def test_params
+ assert_nil @a.params
+ end
+
+ def test_singleton
+ refute @a.singleton
+ end
+
+ def test_type
+ assert_equal 'instance', @a.type
+
+ @a.singleton = true
+ assert_equal 'class', @a.type
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_class_module.rb b/jni/ruby/test/rdoc/test_rdoc_class_module.rb
new file mode 100644
index 0000000..0e06587
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_class_module.rb
@@ -0,0 +1,1492 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocClassModule < XrefTestCase
+
+ def test_add_comment
+ tl1 = @store.add_file 'one.rb'
+ tl2 = @store.add_file 'two.rb'
+ tl3 = @store.add_file 'three.rb'
+
+ cm = RDoc::ClassModule.new 'Klass'
+ cm.add_comment '# comment 1', tl1
+
+ assert_equal [['comment 1', tl1]], cm.comment_location
+ assert_equal 'comment 1', cm.comment
+
+ cm.add_comment '# comment 2', tl2
+
+ assert_equal [['comment 1', tl1], ['comment 2', tl2]], cm.comment_location
+ assert_equal "comment 1\n---\ncomment 2", cm.comment
+
+ cm.add_comment "# * comment 3", tl3
+
+ assert_equal [['comment 1', tl1],
+ ['comment 2', tl2],
+ ['* comment 3', tl3]], cm.comment_location
+ assert_equal "comment 1\n---\ncomment 2\n---\n* comment 3", cm.comment
+ end
+
+ def test_add_comment_comment
+ cm = RDoc::ClassModule.new 'Klass'
+
+ cm.add_comment comment('comment'), @top_level
+
+ assert_equal 'comment', cm.comment.text
+ end
+
+ def test_add_comment_duplicate
+ tl1 = @store.add_file 'one.rb'
+
+ cm = RDoc::ClassModule.new 'Klass'
+ cm.add_comment '# comment 1', tl1
+ cm.add_comment '# comment 2', tl1
+
+ assert_equal [['comment 2', tl1]], cm.comment_location
+ end
+
+ def test_add_comment_stopdoc
+ tl = @store.add_file 'file.rb'
+
+ cm = RDoc::ClassModule.new 'Klass'
+ cm.stop_doc
+
+ cm.add_comment '# comment 1', tl
+
+ assert_empty cm.comment
+ end
+
+ def test_ancestors
+ assert_equal [@parent, "Object"], @child.ancestors
+ end
+
+ def test_comment_equals
+ cm = RDoc::ClassModule.new 'Klass'
+ cm.comment = '# comment 1'
+
+ assert_equal 'comment 1', cm.comment
+
+ cm.comment = '# comment 2'
+
+ assert_equal "comment 1\n---\ncomment 2", cm.comment
+
+ cm.comment = "# * comment 3"
+
+ assert_equal "comment 1\n---\ncomment 2\n---\n* comment 3", cm.comment
+ end
+
+ def test_comment_equals_comment
+ cm = RDoc::ClassModule.new 'Klass'
+
+ cm.comment = comment 'comment'
+
+ assert_equal 'comment', cm.comment.text
+ end
+
+ def test_docuent_self_or_methods
+ assert @c1.document_self_or_methods
+
+ @c1.document_self = false
+
+ assert @c1.document_self_or_methods
+
+ @c1_m.document_self = false
+
+ assert @c1.document_self_or_methods
+
+ @c1__m.document_self = false
+
+ refute @c1.document_self_or_methods
+ end
+
+ def test_documented_eh
+ cm = RDoc::ClassModule.new 'C'
+
+ refute cm.documented?, 'no comments, no markers'
+
+ cm.add_comment '', @top_level
+
+ refute cm.documented?, 'empty comment'
+
+ cm.add_comment 'hi', @top_level
+
+ assert cm.documented?, 'commented'
+
+ cm.comment_location.clear
+
+ refute cm.documented?, 'no comment'
+
+ cm.document_self = nil # notify :nodoc:
+
+ assert cm.documented?, ':nodoc:'
+ end
+
+ def test_each_ancestor
+ assert_equal [@parent], @child.each_ancestor.to_a
+ end
+
+ def test_each_ancestor_cycle
+ m_incl = RDoc::Include.new 'M', nil
+
+ m = @top_level.add_module RDoc::NormalModule, 'M'
+ m.add_include m_incl
+
+ assert_empty m.each_ancestor.to_a
+ end
+
+ # handle making a short module alias of yourself
+
+ def test_find_class_named
+ @c2.classes_hash['C2'] = @c2
+
+ assert_nil @c2.find_class_named('C1')
+ end
+
+ def test_from_module_comment
+ tl = @store.add_file 'file.rb'
+ klass = tl.add_class RDoc::NormalModule, 'Klass'
+ klass.add_comment 'really a class', tl
+
+ klass = RDoc::ClassModule.from_module RDoc::NormalClass, klass
+
+ assert_equal [['really a class', tl]], klass.comment_location
+ end
+
+ def test_marshal_dump
+ @store.path = Dir.tmpdir
+ tl = @store.add_file 'file.rb'
+
+ ns = tl.add_module RDoc::NormalModule, 'Namespace'
+
+ cm = ns.add_class RDoc::NormalClass, 'Klass', 'Super'
+ cm.document_self = true
+ cm.record_location tl
+
+ a1 = RDoc::Attr.new nil, 'a1', 'RW', ''
+ a1.record_location tl
+ a2 = RDoc::Attr.new nil, 'a2', 'RW', '', true
+ a2.record_location tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location tl
+
+ c1 = RDoc::Constant.new 'C1', nil, ''
+ c1.record_location tl
+
+ i1 = RDoc::Include.new 'I1', ''
+ i1.record_location tl
+
+ e1 = RDoc::Extend.new 'E1', ''
+ e1.record_location tl
+
+ section_comment = RDoc::Comment.new('section comment')
+ section_comment.location = tl
+
+ assert_equal 1, cm.sections.length, 'sanity, default section only'
+ s0 = cm.sections.first
+ s1 = cm.add_section 'section', section_comment
+
+ cm.add_attribute a1
+ cm.add_attribute a2
+ cm.add_method m1
+ cm.add_constant c1
+ cm.add_include i1
+ cm.add_extend e1
+ cm.add_comment 'this is a comment', tl
+
+ loaded = Marshal.load Marshal.dump cm
+ loaded.store = @store
+
+ assert_equal cm, loaded
+
+ inner = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+ inner.file = tl
+
+ comment = RDoc::Markup::Document.new inner
+
+ assert_equal [a2, a1], loaded.attributes.sort
+ assert_equal comment, loaded.comment
+ assert_equal [c1], loaded.constants
+ assert_equal 'Namespace::Klass', loaded.full_name
+ assert_equal [i1], loaded.includes
+ assert_equal [e1], loaded.extends
+ assert_equal [m1], loaded.method_list
+ assert_equal 'Klass', loaded.name
+ assert_equal 'Super', loaded.superclass
+ assert_equal [tl], loaded.in_files
+ assert_equal 'Namespace', loaded.parent.name
+
+ expected = { nil => s0, 'section' => s1 }
+ assert_equal expected, loaded.sections_hash
+
+ assert_equal tl, loaded.attributes.first.file
+
+ assert_equal tl, loaded.constants.first.file
+
+ assert_equal tl, loaded.includes.first.file
+
+ assert_equal tl, loaded.extends.first.file
+
+ assert_equal tl, loaded.method_list.first.file
+ end
+
+ def test_marshal_dump_visibilty
+ @store.path = Dir.tmpdir
+ tl = @store.add_file 'file.rb'
+
+ ns = tl.add_module RDoc::NormalModule, 'Namespace'
+
+ cm = ns.add_class RDoc::NormalClass, 'Klass', 'Super'
+ cm.record_location tl
+
+ a1 = RDoc::Attr.new nil, 'a1', 'RW', ''
+ a1.record_location tl
+ a1.document_self = false
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location tl
+ m1.document_self = false
+
+ c1 = RDoc::Constant.new 'C1', nil, ''
+ c1.record_location tl
+ c1.document_self = false
+
+ i1 = RDoc::Include.new 'I1', ''
+ i1.record_location tl
+ i1.document_self = false
+
+ e1 = RDoc::Extend.new 'E1', ''
+ e1.record_location tl
+ e1.document_self = false
+
+ section_comment = RDoc::Comment.new('section comment')
+ section_comment.location = tl
+
+ assert_equal 1, cm.sections.length, 'sanity, default section only'
+
+ cm.add_attribute a1
+ cm.add_method m1
+ cm.add_constant c1
+ cm.add_include i1
+ cm.add_extend e1
+ cm.add_comment 'this is a comment', tl
+
+ loaded = Marshal.load Marshal.dump cm
+ loaded.store = @store
+
+ assert_equal cm, loaded
+
+ assert_empty loaded.attributes
+ assert_empty loaded.constants
+ assert_empty loaded.includes
+ assert_empty loaded.extends
+ assert_empty loaded.method_list
+ end
+
+ def test_marshal_load_version_0
+ tl = @store.add_file 'file.rb'
+ ns = tl.add_module RDoc::NormalModule, 'Namespace'
+ cm = ns.add_class RDoc::NormalClass, 'Klass', 'Super'
+
+ a = RDoc::Attr.new(nil, 'a1', 'RW', '')
+ m = RDoc::AnyMethod.new(nil, 'm1')
+ c = RDoc::Constant.new('C1', nil, '')
+ i = RDoc::Include.new('I1', '')
+
+ s0 = cm.sections.first
+
+ cm.add_attribute a
+ cm.add_method m
+ cm.add_constant c
+ cm.add_include i
+ cm.add_comment 'this is a comment', tl
+
+ loaded = Marshal.load "\x04\bU:\x16RDoc::NormalClass[\x0Ei\x00\"\nKlass" +
+ "\"\x15Namespace::KlassI\"\nSuper\x06:\x06EF" +
+ "o:\eRDoc::Markup::Document\x06:\v@parts[\x06" +
+ "o:\x1CRDoc::Markup::Paragraph\x06;\b[\x06I" +
+ "\"\x16this is a comment\x06;\x06F[\x06[\aI" +
+ "\"\aa1\x06;\x06FI\"\aRW\x06;\x06F[\x06[\aI" +
+ "\"\aC1\x06;\x06Fo;\a\x06;\b[\x00[\x06[\aI" +
+ "\"\aI1\x06;\x06Fo;\a\x06;\b[\x00[\a[\aI" +
+ "\"\nclass\x06;\x06F[\b[\a:\vpublic[\x00[\a" +
+ ":\x0Eprotected[\x00[\a:\fprivate[\x00[\aI" +
+ "\"\rinstance\x06;\x06F[\b[\a;\n[\x06I" +
+ "\"\am1\x06;\x06F[\a;\v[\x00[\a;\f[\x00"
+
+ loaded.store = @store
+
+ assert_equal cm, loaded
+
+ comment = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+
+ assert_equal [a], loaded.attributes
+ assert_equal comment, loaded.comment
+ assert_equal [c], loaded.constants
+ assert_equal 'Namespace::Klass', loaded.full_name
+ assert_equal [i], loaded.includes
+ assert_equal [m], loaded.method_list
+ assert_equal 'Klass', loaded.name
+ assert_equal 'Super', loaded.superclass
+ assert_nil loaded.file
+ assert_empty loaded.in_files
+ assert_nil loaded.parent
+ assert loaded.current_section
+
+ expected = { nil => s0 }
+ assert_equal expected, loaded.sections_hash
+
+ assert loaded.display?
+ end
+
+ def test_marshal_load_version_1
+ tl = @store.add_file 'file.rb'
+
+ ns = tl.add_module RDoc::NormalModule, 'Namespace'
+
+ cm = ns.add_class RDoc::NormalClass, 'Klass', 'Super'
+ cm.record_location tl
+
+ a1 = RDoc::Attr.new nil, 'a1', 'RW', ''
+ a1.record_location tl
+ a2 = RDoc::Attr.new nil, 'a2', 'RW', '', true
+ a2.record_location tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location tl
+
+ c1 = RDoc::Constant.new 'C1', nil, ''
+ c1.record_location tl
+
+ i1 = RDoc::Include.new 'I1', ''
+ i1.record_location tl
+
+ s0 = cm.sections.first
+
+ cm.add_attribute a1
+ cm.add_attribute a2
+ cm.add_method m1
+ cm.add_constant c1
+ cm.add_include i1
+ cm.add_comment 'this is a comment', tl
+
+ loaded = Marshal.load "\x04\bU:\x16RDoc::NormalClass[\x0Ei\x06I\"\nKlass" +
+ "\x06:\x06EFI\"\x15Namespace::Klass\x06;\x06FI" +
+ "\"\nSuper\x06;\x06Fo:\eRDoc::Markup::Document\a" +
+ ":\v@parts[\x06o;\a\a;\b[\x06o" +
+ ":\x1CRDoc::Markup::Paragraph\x06;\b" +
+ "[\x06I\"\x16this is a comment\x06;\x06F" +
+ ":\n@fileI\"\ffile.rb\x06;\x06F;\n0[\a[\nI" +
+ "\"\aa2\x06;\x06FI\"\aRW\x06;\x06F:\vpublicT@\x11" +
+ "[\nI\"\aa1\x06;\x06FI\"\aRW\x06;\x06F;\vF@\x11" +
+ "[\x06[\bI\"\aC1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11" +
+ "[\x06[\bI\"\aI1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11" +
+ "[\a[\aI\"\nclass\x06;\x06F[\b[\a;\v[\x00" +
+ "[\a:\x0Eprotected[\x00[\a:\fprivate[\x00[\aI" +
+ "\"\rinstance\x06;\x06F[\b[\a;\v[\x06[\aI" +
+ "\"\am1\x06;\x06F@\x11[\a;\f[\x00[\a;\r[\x00"
+
+ loaded.store = @store
+
+ assert_equal cm, loaded
+
+ inner = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+ inner.file = tl
+
+ comment = RDoc::Markup::Document.new inner
+
+ assert_equal [a2, a1], loaded.attributes.sort
+ assert_equal comment, loaded.comment
+ assert_equal [c1], loaded.constants
+ assert_equal 'Namespace::Klass', loaded.full_name
+ assert_equal [i1], loaded.includes
+ assert_empty loaded.extends
+ assert_equal [m1], loaded.method_list
+ assert_equal 'Klass', loaded.name
+ assert_equal 'Super', loaded.superclass
+ assert_empty loaded.in_files
+ assert_nil loaded.parent
+ assert loaded.current_section
+
+ assert_equal tl, loaded.attributes.first.file
+ assert_equal tl, loaded.constants.first.file
+ assert_equal tl, loaded.includes.first.file
+ assert_equal tl, loaded.method_list.first.file
+
+ expected = { nil => s0 }
+ assert_equal expected, loaded.sections_hash
+ end
+
+ def test_marshal_load_version_2
+ tl = @store.add_file 'file.rb'
+
+ ns = tl.add_module RDoc::NormalModule, 'Namespace'
+
+ cm = ns.add_class RDoc::NormalClass, 'Klass', 'Super'
+ cm.record_location tl
+
+ a1 = RDoc::Attr.new nil, 'a1', 'RW', ''
+ a1.record_location tl
+ a2 = RDoc::Attr.new nil, 'a2', 'RW', '', true
+ a2.record_location tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location tl
+
+ c1 = RDoc::Constant.new 'C1', nil, ''
+ c1.record_location tl
+
+ i1 = RDoc::Include.new 'I1', ''
+ i1.record_location tl
+
+ e1 = RDoc::Extend.new 'E1', ''
+ e1.record_location tl
+
+ s0 = cm.sections.first
+
+ cm.add_attribute a1
+ cm.add_attribute a2
+ cm.add_method m1
+ cm.add_constant c1
+ cm.add_include i1
+ cm.add_extend e1
+ cm.add_comment 'this is a comment', tl
+
+ loaded = Marshal.load "\x04\bU:\x16RDoc::NormalClass[\x0Fi\aI\"\nKlass" +
+ "\x06:\x06EFI\"\x15Namespace::Klass\x06;\x06FI" +
+ "\"\nSuper\x06;\x06Fo:\eRDoc::Markup::Document\a" +
+ ":\v@parts[\x06o;\a\a;\b[\x06o" +
+ ":\x1CRDoc::Markup::Paragraph\x06;\b" +
+ "[\x06I\"\x16this is a comment\x06;\x06F" +
+ ":\n@fileI\"\ffile.rb\x06;\x06F;\n0[\a[\nI" +
+ "\"\aa2\x06;\x06FI\"\aRW\x06;\x06F:\vpublicT@\x11" +
+ "[\nI\"\aa1\x06;\x06FI\"\aRW\x06;\x06F;\vF@\x11" +
+ "[\x06[\bI\"\aC1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11" +
+ "[\x06[\bI\"\aI1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11" +
+ "[\a[\aI\"\nclass\x06;\x06F[\b[\a;\v[\x00" +
+ "[\a:\x0Eprotected[\x00[\a:\fprivate[\x00[\aI" +
+ "\"\rinstance\x06;\x06F[\b[\a;\v[\x06[\aI" +
+ "\"\am1\x06;\x06F@\x11[\a;\f[\x00[\a;\r[\x00" +
+ "[\x06[\bI\"\aE1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11"
+
+ loaded.store = @store
+
+ assert_equal cm, loaded
+
+ inner = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+ inner.file = tl
+
+ comment = RDoc::Markup::Document.new inner
+
+ assert_equal [a2, a1], loaded.attributes.sort
+ assert_equal comment, loaded.comment
+ assert_equal [c1], loaded.constants
+ assert_equal 'Namespace::Klass', loaded.full_name
+ assert_equal [i1], loaded.includes
+ assert_equal [e1], loaded.extends
+ assert_equal [m1], loaded.method_list
+ assert_equal 'Klass', loaded.name
+ assert_equal 'Super', loaded.superclass
+ assert_empty loaded.in_files
+ assert_nil loaded.parent
+ assert loaded.current_section
+
+ assert_equal tl, loaded.attributes. first.file
+ assert_equal tl, loaded.constants. first.file
+ assert_equal tl, loaded.includes. first.file
+ assert_equal tl, loaded.extends. first.file
+ assert_equal tl, loaded.method_list.first.file
+
+ expected = { nil => s0 }
+ assert_equal expected, loaded.sections_hash
+ end
+
+ def test_marshal_load_version_3
+ tl = @store.add_file 'file.rb'
+
+ ns = tl.add_module RDoc::NormalModule, 'Namespace'
+
+ cm = ns.add_class RDoc::NormalClass, 'Klass', 'Super'
+ cm.record_location tl
+
+ a1 = RDoc::Attr.new nil, 'a1', 'RW', ''
+ a1.record_location tl
+ a2 = RDoc::Attr.new nil, 'a2', 'RW', '', true
+ a2.record_location tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location tl
+
+ c1 = RDoc::Constant.new 'C1', nil, ''
+ c1.record_location tl
+
+ i1 = RDoc::Include.new 'I1', ''
+ i1.record_location tl
+
+ e1 = RDoc::Extend.new 'E1', ''
+ e1.record_location tl
+
+ section_comment = RDoc::Comment.new('section comment')
+ section_comment.location = tl
+
+ assert_equal 1, cm.sections.length, 'sanity, default section only'
+ s0 = cm.sections.first
+ s1 = cm.add_section 'section', section_comment
+
+ cm.add_attribute a1
+ cm.add_attribute a2
+ cm.add_method m1
+ cm.add_constant c1
+ cm.add_include i1
+ cm.add_extend e1
+ cm.add_comment 'this is a comment', tl
+
+ loaded = Marshal.load "\x04\bU:\x16RDoc::NormalClass[\x13i\bI\"\nKlass" +
+ "\x06:\x06ETI\"\x15Namespace::Klass\x06;\x06TI" +
+ "\"\nSuper\x06;\x06To:\eRDoc::Markup::Document\a" +
+ ":\v@parts[\x06o;\a\a;\b[\x06o" +
+ ":\x1CRDoc::Markup::Paragraph\x06;\b[\x06I" +
+ "\"\x16this is a comment\x06;\x06T:\n@fileI" +
+ "\"\ffile.rb\x06;\x06T;\n0[\a[\nI\"\aa2\x06;" +
+ "\x06TI\"\aRW\x06;\x06T:\vpublicT@\x11[\nI" +
+ "\"\aa1\x06;\x06TI\"\aRW\x06;\x06T;\vF@\x11" +
+ "[\x06U:\x13RDoc::Constant[\x0Fi\x00I\"\aC1\x06" +
+ ";\x06TI\"\x19Namespace::Klass::C1\x06;\x06T00o" +
+ ";\a\a;\b[\x00;\n0@\x11@\ac\x16RDoc::NormalClass0" +
+ "[\x06[\bI\"\aI1\x06;\x06To;\a\a;\b[\x00;\n0@\x11" +
+ "[\a[\aI\"\nclass\x06;\x06T[\b[\a;\v[\x00[\a" +
+ ":\x0Eprotected[\x00[\a:\fprivate[\x00[\aI" +
+ "\"\rinstance\x06;\x06T[\b[\a;\v[\x06[\aI" +
+ "\"\am1\x06;\x06T@\x11[\a;\r[\x00[\a;\x0E[\x00" +
+ "[\x06[\bI\"\aE1\x06;\x06To;\a\a;\b[\x00;\n0@\x11" +
+ "[\aU:\eRDoc::Context::Section[\bi\x000o;\a\a;\b" +
+ "[\x00;\n0U;\x0F[\bi\x00I\"\fsection\x06;\x06To" +
+ ";\a\a;\b[\x06o;\a\a;\b[\x06o;\t\x06;\b[\x06I" +
+ "\"\x14section comment\x06;\x06T;\n@\x11;\n0" +
+ "[\x06@\x11I\"\x0ENamespace\x06" +
+ ";\x06Tc\x17RDoc::NormalModule"
+
+ loaded.store = @store
+
+ assert_equal cm, loaded
+
+ inner = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('this is a comment'))
+ inner.file = tl
+
+ comment = RDoc::Markup::Document.new inner
+
+ assert_equal [a2, a1], loaded.attributes.sort
+ assert_equal comment, loaded.comment
+ assert_equal [c1], loaded.constants
+ assert_equal 'Namespace::Klass', loaded.full_name
+ assert_equal [i1], loaded.includes
+ assert_equal [e1], loaded.extends
+ assert_equal [m1], loaded.method_list
+ assert_equal 'Klass', loaded.name
+ assert_equal 'Super', loaded.superclass
+ assert_equal 'Namespace', loaded.parent.name
+ assert loaded.current_section
+
+ expected = {
+ nil => s0,
+ 'section' => s1,
+ }
+
+ assert_equal expected, loaded.sections_hash
+ assert_equal [tl], loaded.in_files
+
+ assert_equal tl, loaded.attributes. first.file
+ assert_equal tl, loaded.constants. first.file
+ assert_equal tl, loaded.includes. first.file
+ assert_equal tl, loaded.extends. first.file
+ assert_equal tl, loaded.method_list.first.file
+ end
+
+ def test_merge
+ tl = @store.add_file 'one.rb'
+ p1 = tl.add_class RDoc::NormalClass, 'Parent'
+ c1 = p1.add_class RDoc::NormalClass, 'Klass'
+
+ c2 = RDoc::NormalClass.new 'Klass'
+
+ c2.merge c1
+
+ assert_equal 'Parent', c1.parent_name, 'original parent name'
+ assert_equal 'Parent', c2.parent_name, 'merged parent name'
+
+ assert c1.current_section, 'original current_section'
+ assert c2.current_section, 'merged current_section'
+ end
+
+ def test_merge_attributes
+ tl1 = @store.add_file 'one.rb'
+ tl2 = @store.add_file 'two.rb'
+
+ cm1 = RDoc::ClassModule.new 'Klass'
+
+ attr = cm1.add_attribute RDoc::Attr.new(nil, 'a1', 'RW', '')
+ attr.record_location tl1
+ attr = cm1.add_attribute RDoc::Attr.new(nil, 'a3', 'R', '')
+ attr.record_location tl1
+ attr = cm1.add_attribute RDoc::Attr.new(nil, 'a4', 'R', '')
+ attr.record_location tl1
+
+ cm2 = RDoc::ClassModule.new 'Klass'
+ # TODO allow merging when comment == ''
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ attr = cm2.add_attribute RDoc::Attr.new(nil, 'a2', 'RW', '')
+ attr.record_location tl2
+ attr = cm2.add_attribute RDoc::Attr.new(nil, 'a3', 'W', '')
+ attr.record_location tl1
+ attr = cm2.add_attribute RDoc::Attr.new(nil, 'a4', 'W', '')
+ attr.record_location tl1
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::Attr.new(nil, 'a2', 'RW', ''),
+ RDoc::Attr.new(nil, 'a3', 'W', ''),
+ RDoc::Attr.new(nil, 'a4', 'W', ''),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+ assert_equal expected, cm1.attributes.sort
+ end
+
+ def test_merge_attributes_version_0
+ tl1 = @store.add_file 'one.rb'
+
+ cm1 = RDoc::ClassModule.new 'Klass'
+
+ attr = cm1.add_attribute RDoc::Attr.new(nil, 'a1', 'RW', '')
+ attr.record_location tl1
+ attr = cm1.add_attribute RDoc::Attr.new(nil, 'a3', 'R', '')
+ attr.record_location tl1
+ attr = cm1.add_attribute RDoc::Attr.new(nil, 'a4', 'R', '')
+ attr.record_location tl1
+
+ cm2 = RDoc::ClassModule.new 'Klass'
+ # TODO allow merging when comment == ''
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ attr = cm2.add_attribute RDoc::Attr.new(nil, 'a2', 'RW', '')
+ attr = cm2.add_attribute RDoc::Attr.new(nil, 'a3', 'W', '')
+ attr = cm2.add_attribute RDoc::Attr.new(nil, 'a4', 'W', '')
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::Attr.new(nil, 'a1', 'RW', ''),
+ RDoc::Attr.new(nil, 'a2', 'RW', ''),
+ RDoc::Attr.new(nil, 'a3', 'RW', ''),
+ RDoc::Attr.new(nil, 'a4', 'RW', ''),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+ assert_equal expected, cm1.attributes.sort
+ end
+
+ def test_merge_collections_drop
+ tl = @store.add_file 'file'
+
+ cm1 = RDoc::ClassModule.new 'C'
+ cm1.record_location tl
+
+ const = cm1.add_constant RDoc::Constant.new('CONST', nil, nil)
+ const.record_location tl
+
+ cm2 = RDoc::ClassModule.new 'C'
+ cm2.record_location tl
+
+ added = []
+ removed = []
+
+ cm1.merge_collections cm1.constants, cm2.constants, cm2.in_files do |add, c|
+ if add then
+ added << c
+ else
+ removed << c
+ end
+ end
+
+ assert_empty added
+ assert_equal [const], removed
+ end
+
+ def test_merge_comment
+ tl1 = @store.add_file 'one.rb'
+ tl2 = @store.add_file 'two.rb'
+
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
+ cm1.add_comment 'klass 1', tl1
+ cm1.record_location tl1
+
+ cm2 = tl1.add_class RDoc::NormalClass, 'Klass'
+ cm2.add_comment 'klass 2', tl2
+ cm2.add_comment 'klass 3', tl1
+ cm2.record_location tl1
+ cm2.record_location tl2
+
+ cm2 = Marshal.load Marshal.dump cm2
+ cm2.store = @store
+
+ cm1.merge cm2
+
+ inner1 = @RM::Document.new @RM::Paragraph.new 'klass 3'
+ inner1.file = 'one.rb'
+ inner2 = @RM::Document.new @RM::Paragraph.new 'klass 2'
+ inner2.file = 'two.rb'
+
+ expected = @RM::Document.new inner2, inner1
+
+ assert_equal expected, cm1.comment
+ end
+
+ def test_merge_comment_version_0
+ tl = @store.add_file 'file.rb'
+
+ cm1 = RDoc::ClassModule.new 'Klass'
+ cm1.add_comment 'klass 1', tl
+
+ cm2 = RDoc::ClassModule.new 'Klass'
+
+ cm2.instance_variable_set(:@comment,
+ @RM::Document.new(
+ @RM::Paragraph.new('klass 2')))
+ cm2.instance_variable_set :@comment_location, @RM::Document.new(cm2.comment)
+
+ cm1.merge cm2
+
+ inner = @RM::Document.new @RM::Paragraph.new 'klass 1'
+ inner.file = 'file.rb'
+
+ expected = @RM::Document.new \
+ inner,
+ @RM::Document.new(@RM::Paragraph.new('klass 2'))
+
+ assert_equal expected, cm1.comment
+ end
+
+ def test_merge_constants
+ tl1 = @store.add_file 'one.rb'
+ tl2 = @store.add_file 'two.rb'
+
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
+
+ const = cm1.add_constant RDoc::Constant.new('C1', nil, 'one')
+ const.record_location tl1
+ const = cm1.add_constant RDoc::Constant.new('C3', nil, 'one')
+ const.record_location tl1
+
+ store = RDoc::Store.new
+ tl = store.add_file 'one.rb'
+ cm2 = tl.add_class RDoc::ClassModule, 'Klass'
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ const = cm2.add_constant RDoc::Constant.new('C2', nil, 'two')
+ const.record_location tl2
+ const = cm2.add_constant RDoc::Constant.new('C3', nil, 'one')
+ const.record_location tl1
+ const = cm2.add_constant RDoc::Constant.new('C4', nil, 'one')
+ const.record_location tl1
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::Constant.new('C2', nil, 'two'),
+ RDoc::Constant.new('C3', nil, 'one'),
+ RDoc::Constant.new('C4', nil, 'one'),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+
+ assert_equal expected, cm1.constants.sort
+ end
+
+ def test_merge_constants_version_0
+ tl1 = @store.add_file 'one.rb'
+
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
+
+ const = cm1.add_constant RDoc::Constant.new('C1', nil, 'one')
+ const.record_location tl1
+ const = cm1.add_constant RDoc::Constant.new('C3', nil, 'one')
+ const.record_location tl1
+
+ store = RDoc::Store.new
+ tl = store.add_file 'one.rb'
+ cm2 = tl.add_class RDoc::ClassModule, 'Klass'
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ const = cm2.add_constant RDoc::Constant.new('C2', nil, 'two')
+ const = cm2.add_constant RDoc::Constant.new('C3', nil, 'two')
+ const = cm2.add_constant RDoc::Constant.new('C4', nil, 'two')
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::Constant.new('C1', nil, 'one'),
+ RDoc::Constant.new('C2', nil, 'two'),
+ RDoc::Constant.new('C3', nil, 'one'),
+ RDoc::Constant.new('C4', nil, 'two'),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+
+ assert_equal expected, cm1.constants.sort
+ end
+
+ def test_merge_extends
+ tl1 = @store.add_file 'one.rb'
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
+
+ ext = cm1.add_extend RDoc::Extend.new('I1', 'one')
+ ext.record_location tl1
+ ext = cm1.add_extend RDoc::Extend.new('I3', 'one')
+ ext.record_location tl1
+
+ tl2 = @store.add_file 'two.rb'
+ tl2.store = RDoc::Store.new
+
+ cm2 = tl2.add_class RDoc::ClassModule, 'Klass'
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ ext = cm2.add_extend RDoc::Extend.new('I2', 'two')
+ ext.record_location tl2
+ ext = cm2.add_extend RDoc::Extend.new('I3', 'one')
+ ext.record_location tl1
+ ext = cm2.add_extend RDoc::Extend.new('I4', 'one')
+ ext.record_location tl1
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::Extend.new('I2', 'two'),
+ RDoc::Extend.new('I3', 'one'),
+ RDoc::Extend.new('I4', 'one'),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+
+ assert_equal expected, cm1.extends.sort
+ end
+
+ def test_merge_includes
+ tl1 = @store.add_file 'one.rb'
+
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
+
+ incl = cm1.add_include RDoc::Include.new('I1', 'one')
+ incl.record_location tl1
+ incl = cm1.add_include RDoc::Include.new('I3', 'one')
+ incl.record_location tl1
+
+ tl2 = @store.add_file 'two.rb'
+ tl2.store = RDoc::Store.new
+
+ cm2 = tl2.add_class RDoc::ClassModule, 'Klass'
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ incl = cm2.add_include RDoc::Include.new('I2', 'two')
+ incl.record_location tl2
+ incl = cm2.add_include RDoc::Include.new('I3', 'one')
+ incl.record_location tl1
+ incl = cm2.add_include RDoc::Include.new('I4', 'one')
+ incl.record_location tl1
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::Include.new('I2', 'two'),
+ RDoc::Include.new('I3', 'one'),
+ RDoc::Include.new('I4', 'one'),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+
+ assert_equal expected, cm1.includes.sort
+ end
+
+ def test_merge_includes_version_0
+ tl1 = @store.add_file 'one.rb'
+
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
+
+ incl = cm1.add_include RDoc::Include.new('I1', 'one')
+ incl.record_location tl1
+ incl = cm1.add_include RDoc::Include.new('I3', 'one')
+ incl.record_location tl1
+
+ tl2 = @store.add_file 'one.rb'
+ tl2.store = RDoc::Store.new
+
+ cm2 = tl2.add_class RDoc::ClassModule, 'Klass'
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ incl = cm2.add_include RDoc::Include.new('I2', 'two')
+ incl = cm2.add_include RDoc::Include.new('I3', 'two')
+ incl = cm2.add_include RDoc::Include.new('I4', 'two')
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::Include.new('I1', 'one'),
+ RDoc::Include.new('I2', 'two'),
+ RDoc::Include.new('I3', 'one'),
+ RDoc::Include.new('I4', 'two'),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+
+ assert_equal expected, cm1.includes.sort
+ end
+
+ def test_merge_methods
+ tl1 = @store.add_file 'one.rb'
+ tl2 = @store.add_file 'two.rb'
+
+ cm1 = tl1.add_class RDoc::NormalClass, 'Klass'
+
+ meth = cm1.add_method RDoc::AnyMethod.new(nil, 'm1')
+ meth.record_location tl1
+ meth = cm1.add_method RDoc::AnyMethod.new(nil, 'm3')
+ meth.record_location tl1
+
+ cm2 = RDoc::ClassModule.new 'Klass'
+ cm2.store = @store
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm2')
+ meth.record_location tl2
+ meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm3')
+ meth.record_location tl1
+ meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm4')
+ meth.record_location tl1
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::AnyMethod.new(nil, 'm2'),
+ RDoc::AnyMethod.new(nil, 'm3'),
+ RDoc::AnyMethod.new(nil, 'm4'),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+
+ assert_equal expected, cm1.method_list.sort
+ end
+
+ def test_merge_methods_version_0
+ tl1 = @store.add_file 'one.rb'
+
+ cm1 = tl1.add_class RDoc::NormalClass, 'Klass'
+
+ meth = cm1.add_method RDoc::AnyMethod.new(nil, 'm1')
+ meth.record_location tl1
+ meth = cm1.add_method RDoc::AnyMethod.new(nil, 'm3')
+ meth.record_location tl1
+
+ cm2 = RDoc::ClassModule.new 'Klass'
+ cm2.store = @store
+ cm2.instance_variable_set :@comment, @RM::Document.new
+
+ meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm2')
+ meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm3')
+ meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm4')
+
+ cm1.merge cm2
+
+ expected = [
+ RDoc::AnyMethod.new(nil, 'm1'),
+ RDoc::AnyMethod.new(nil, 'm2'),
+ RDoc::AnyMethod.new(nil, 'm3'),
+ RDoc::AnyMethod.new(nil, 'm4'),
+ ]
+
+ expected.each do |a| a.parent = cm1 end
+
+ assert_equal expected, cm1.method_list.sort
+ end
+
+ def test_merge_sections
+ store1 = @store
+
+ tl1_1 = store1.add_file 'one.rb'
+
+ cm1 = tl1_1.add_class RDoc::ClassModule, 'Klass'
+ cm1.record_location tl1_1
+
+ s1_0 = cm1.sections.first
+ s1_1 = cm1.add_section 'section 1', comment('comment 1', tl1_1)
+ cm1.add_section 'section 2', comment('comment 2 a', tl1_1)
+ cm1.add_section 'section 4', comment('comment 4 a', tl1_1)
+
+ store2 = RDoc::Store.new
+ tl2_1 = store2.add_file 'one.rb'
+ tl2_2 = store2.add_file 'two.rb'
+
+ cm2 = tl2_1.add_class RDoc::ClassModule, 'Klass'
+ cm2.record_location tl2_1
+ cm2.record_location tl2_2
+
+ cm2.sections.first
+ s2_2 = cm2.add_section 'section 2', comment('comment 2 b', tl2_1)
+ s2_3 = cm2.add_section 'section 3', comment('comment 3', tl2_2)
+ cm2.add_section 'section 4', comment('comment 4 b', tl2_2)
+
+ cm1.merge cm2
+
+ expected = [
+ s1_0,
+ s1_1,
+ s2_2,
+ s2_3,
+ RDoc::Context::Section.new(cm1, 'section 4', nil)
+ ]
+
+ merged_sections = cm1.sections.sort_by do |s|
+ s.title || ''
+ end
+
+ assert_equal expected, merged_sections
+
+ assert_equal [comment('comment 2 b', tl2_1)],
+ cm1.sections_hash['section 2'].comments
+
+ expected_s4_comments = [
+ comment('comment 4 a', tl2_1),
+ comment('comment 4 b', tl2_2),
+ ]
+
+ assert_equal expected_s4_comments, cm1.sections_hash['section 4'].comments
+ end
+
+ def test_merge_sections_overlap
+ store1 = @store
+
+ tl1_1 = store1.add_file 'one.rb'
+ tl1_3 = store1.add_file 'three.rb'
+
+ cm1 = tl1_1.add_class RDoc::ClassModule, 'Klass'
+ cm1.record_location tl1_1
+
+ cm1.add_section 'section', comment('comment 1 a', tl1_1)
+ cm1.add_section 'section', comment('comment 3', tl1_3)
+
+ store2 = RDoc::Store.new
+ tl2_1 = store2.add_file 'one.rb'
+ tl2_2 = store2.add_file 'two.rb'
+ tl2_3 = store2.add_file 'three.rb'
+
+ cm2 = tl2_1.add_class RDoc::ClassModule, 'Klass'
+ cm2.record_location tl2_1
+ cm2.record_location tl2_2
+
+ s2_0 = cm2.sections.first
+ s2_1 = cm2.add_section 'section', comment('comment 1 b', tl1_1)
+ cm2.add_section 'section', comment('comment 2', tl2_2)
+
+ cm1.merge_sections cm2
+
+ expected = [
+ s2_0,
+ s2_1,
+ ]
+
+ merged_sections = cm1.sections.sort_by do |s|
+ s.title || ''
+ end
+
+ assert_equal expected, merged_sections
+
+ expected = [
+ comment('comment 1 b', tl2_1),
+ comment('comment 3', tl2_3),
+ comment('comment 2', tl2_2),
+ ]
+
+ comments = cm1.sections_hash['section'].comments
+
+ assert_equal expected, comments.sort_by { |c| c.file.name }
+ end
+
+ def test_parse
+ tl1 = @store.add_file 'one.rb'
+ tl2 = @store.add_file 'two.rb'
+
+ cm = RDoc::ClassModule.new 'Klass'
+ cm.add_comment 'comment 1', tl1
+ cm.add_comment 'comment 2', tl2
+
+ doc1 = @RM::Document.new @RM::Paragraph.new 'comment 1'
+ doc1.file = tl1
+ doc2 = @RM::Document.new @RM::Paragraph.new 'comment 2'
+ doc2.file = tl2
+
+ expected = @RM::Document.new doc1, doc2
+
+ assert_equal expected, cm.parse(cm.comment_location)
+ end
+
+ def test_parse_comment
+ tl1 = @store.add_file 'one.rb'
+
+ cm = RDoc::ClassModule.new 'Klass'
+ cm.comment = comment 'comment 1', tl1
+
+ doc = @RM::Document.new @RM::Paragraph.new 'comment 1'
+ doc.file = tl1
+
+ assert_equal doc, cm.parse(cm.comment)
+ end
+
+ def test_parse_comment_format
+ tl1 = @store.add_file 'one.rb'
+
+ cm = RDoc::ClassModule.new 'Klass'
+ cm.comment = comment 'comment ((*1*))', tl1
+ cm.comment.format = 'rd'
+
+ doc = @RM::Document.new @RM::Paragraph.new 'comment <em>1</em>'
+ doc.file = tl1
+
+ assert_equal doc, cm.parse(cm.comment)
+ end
+
+ def test_parse_comment_location
+ tl1 = @store.add_file 'one.rb'
+ tl2 = @store.add_file 'two.rb'
+
+ cm = tl1.add_class RDoc::NormalClass, 'Klass'
+ cm.add_comment 'comment 1', tl1
+ cm.add_comment 'comment 2', tl2
+
+ cm = Marshal.load Marshal.dump cm
+
+ doc1 = @RM::Document.new @RM::Paragraph.new 'comment 1'
+ doc1.file = tl1
+ doc2 = @RM::Document.new @RM::Paragraph.new 'comment 2'
+ doc2.file = tl2
+
+ assert_same cm.comment_location, cm.parse(cm.comment_location)
+ end
+
+ def test_remove_nodoc_children
+ parent = @top_level.add_class RDoc::ClassModule, 'A'
+ parent.modules_hash.replace 'B' => true, 'C' => true
+ @store.modules_hash.replace 'A::B' => true
+
+ parent.classes_hash.replace 'D' => true, 'E' => true
+ @store.classes_hash.replace 'A::D' => true
+
+ parent.remove_nodoc_children
+
+ assert_equal %w[B], parent.modules_hash.keys
+ assert_equal %w[D], parent.classes_hash.keys
+ end
+
+ def test_search_record
+ @c2_c3.add_comment 'This is a comment.', @xref_data
+
+ expected = [
+ 'C3',
+ 'C2::C3',
+ 'C2::C3',
+ '',
+ 'C2/C3.html',
+ '',
+ "<p>This is a comment.\n"
+ ]
+
+ assert_equal expected, @c2_c3.search_record
+ end
+
+ def test_search_record_merged
+ @c2_c3.add_comment 'comment A', @store.add_file('a.rb')
+ @c2_c3.add_comment 'comment B', @store.add_file('b.rb')
+
+ expected = [
+ 'C3',
+ 'C2::C3',
+ 'C2::C3',
+ '',
+ 'C2/C3.html',
+ '',
+ "<p>comment A\n<p>comment B\n"
+ ]
+
+ assert_equal expected, @c2_c3.search_record
+ end
+
+ def test_store_equals
+ # version 2
+ loaded = Marshal.load "\x04\bU:\x16RDoc::NormalClass[\x0Fi\aI\"\nKlass" +
+ "\x06:\x06EFI\"\x15Namespace::Klass\x06;\x06FI" +
+ "\"\nSuper\x06;\x06Fo:\eRDoc::Markup::Document\a" +
+ ":\v@parts[\x06o;\a\a;\b[\x06o" +
+ ":\x1CRDoc::Markup::Paragraph\x06;\b" +
+ "[\x06I\"\x16this is a comment\x06;\x06F" +
+ ":\n@fileI\"\ffile.rb\x06;\x06F;\n0[\a[\nI" +
+ "\"\aa2\x06;\x06FI\"\aRW\x06;\x06F:\vpublicT@\x11" +
+ "[\nI\"\aa1\x06;\x06FI\"\aRW\x06;\x06F;\vF@\x11" +
+ "[\x06[\bI\"\aC1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11" +
+ "[\x06[\bI\"\aI1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11" +
+ "[\a[\aI\"\nclass\x06;\x06F[\b[\a;\v[\x00" +
+ "[\a:\x0Eprotected[\x00[\a:\fprivate[\x00[\aI" +
+ "\"\rinstance\x06;\x06F[\b[\a;\v[\x06[\aI" +
+ "\"\am1\x06;\x06F@\x11[\a;\f[\x00[\a;\r[\x00" +
+ "[\x06[\bI\"\aE1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11"
+
+ loaded.store = @store
+
+ assert_same @store, loaded.store
+
+ a = loaded.attributes.first
+ assert_same @store, a.store
+ assert_same @store, a.file.store
+
+ c = loaded.constants.first
+ assert_same @store, c.store
+ assert_same @store, c.file.store
+
+ i = loaded.includes.first
+ assert_same @store, i.store
+ assert_same @store, i.file.store
+
+ e = loaded.extends.first
+ assert_same @store, e.store
+ assert_same @store, e.file.store
+
+ m = loaded.method_list.first
+ assert_same @store, m.store
+ assert_same @store, m.file.store
+ end
+
+ def test_superclass
+ assert_equal @c3_h1, @c3_h2.superclass
+ end
+
+ def test_update_aliases_class
+ n1 = @xref_data.add_module RDoc::NormalClass, 'N1'
+ n1_k2 = n1.add_module RDoc::NormalClass, 'N2'
+
+ n1.add_module_alias n1_k2, 'A1', @xref_data
+
+ n1_a1_c = n1.constants.find { |c| c.name == 'A1' }
+ refute_nil n1_a1_c
+ assert_equal n1_k2, n1_a1_c.is_alias_for, 'sanity check'
+
+ n1.update_aliases
+
+ n1_a1_k = @xref_data.find_class_or_module 'N1::A1'
+ refute_nil n1_a1_k
+ assert_equal n1_k2, n1_a1_k.is_alias_for
+ refute_equal n1_k2, n1_a1_k
+
+ assert_equal 1, n1_k2.aliases.length
+ assert_equal n1_a1_k, n1_k2.aliases.first
+
+ assert_equal 'N1::N2', n1_k2.full_name
+ assert_equal 'N1::A1', n1_a1_k.full_name
+ end
+
+ def test_update_aliases_module
+ n1 = @xref_data.add_module RDoc::NormalModule, 'N1'
+ n1_n2 = n1.add_module RDoc::NormalModule, 'N2'
+
+ n1.add_module_alias n1_n2, 'A1', @xref_data
+
+ n1_a1_c = n1.constants.find { |c| c.name == 'A1' }
+ refute_nil n1_a1_c
+ assert_equal n1_n2, n1_a1_c.is_alias_for, 'sanity check'
+
+ n1.update_aliases
+
+ n1_a1_m = @xref_data.find_class_or_module 'N1::A1'
+ refute_nil n1_a1_m
+ assert_equal n1_n2, n1_a1_m.is_alias_for
+ refute_equal n1_n2, n1_a1_m
+
+ assert_equal 1, n1_n2.aliases.length
+ assert_equal n1_a1_m, n1_n2.aliases.first
+
+ assert_equal 'N1::N2', n1_n2.full_name
+ assert_equal 'N1::A1', n1_a1_m.full_name
+ end
+
+ def test_update_aliases_reparent
+ l1 = @xref_data.add_module RDoc::NormalModule, 'L1'
+ l1_l2 = l1.add_module RDoc::NormalModule, 'L2'
+ o1 = @xref_data.add_module RDoc::NormalModule, 'O1'
+
+ o1.add_module_alias l1_l2, 'A1', @xref_data
+
+ o1_a1_c = o1.constants.find { |c| c.name == 'A1' }
+ refute_nil o1_a1_c
+ assert_equal l1_l2, o1_a1_c.is_alias_for
+ refute_equal l1_l2, o1_a1_c
+
+ o1.update_aliases
+
+ o1_a1_m = @xref_data.find_class_or_module 'O1::A1'
+ refute_nil o1_a1_m
+ assert_equal l1_l2, o1_a1_m.is_alias_for
+
+ assert_equal 1, l1_l2.aliases.length
+ assert_equal o1_a1_m, l1_l2.aliases[0]
+
+ assert_equal 'L1::L2', l1_l2.full_name
+ assert_equal 'O1::A1', o1_a1_m.full_name
+ end
+
+ def test_update_aliases_reparent_root
+ store = RDoc::Store.new
+
+ top_level = store.add_file 'file.rb'
+
+ klass = top_level.add_class RDoc::NormalClass, 'Klass'
+ object = top_level.add_class RDoc::NormalClass, 'Object'
+
+ const = RDoc::Constant.new 'A', nil, ''
+ const.record_location top_level
+ const.is_alias_for = klass
+
+ top_level.add_module_alias klass, 'A', top_level
+
+ object.add_constant const
+
+ object.update_aliases
+
+ assert_equal %w[A Klass Object], store.classes_hash.keys.sort
+
+ assert_equal 'A', store.classes_hash['A'].full_name
+ assert_equal 'Klass', store.classes_hash['Klass'].full_name
+ end
+
+ def test_update_includes
+ a = RDoc::Include.new 'M1', nil
+ b = RDoc::Include.new 'M2', nil
+ c = RDoc::Include.new 'C', nil
+
+ @c1.add_include a
+ @c1.add_include b
+ @c1.add_include c
+ @c1.ancestors # cache included modules
+
+ @m1_m2.document_self = nil
+ assert @m1_m2.remove_from_documentation?
+
+ assert @store.modules_hash.key? @m1_m2.full_name
+ refute @store.modules_hash[@m1_m2.full_name].nil?
+
+ @store.remove_nodoc @store.modules_hash
+ refute @store.modules_hash.key? @m1_m2.full_name
+
+ @c1.update_includes
+
+ assert_equal [a, c], @c1.includes
+ end
+
+ def test_update_includes_trim
+ a = RDoc::Include.new 'D::M', nil
+ b = RDoc::Include.new 'D::M', nil
+
+ @c1.add_include a
+ @c1.add_include b
+ @c1.ancestors # cache included modules
+
+ @c1.update_includes
+
+ assert_equal [a], @c1.includes
+ end
+
+ def test_update_includes_with_colons
+ a = RDoc::Include.new 'M1', nil
+ b = RDoc::Include.new 'M1::M2', nil
+ c = RDoc::Include.new 'C', nil
+
+ @c1.add_include a
+ @c1.add_include b
+ @c1.add_include c
+ @c1.ancestors # cache included modules
+
+ @m1_m2.document_self = nil
+ assert @m1_m2.remove_from_documentation?
+
+ assert @store.modules_hash.key? @m1_m2.full_name
+ refute @store.modules_hash[@m1_m2.full_name].nil?
+ @store.remove_nodoc @store.modules_hash
+ refute @store.modules_hash.key? @m1_m2.full_name
+
+ @c1.update_includes
+
+ assert_equal [a, c], @c1.includes
+ end
+
+ def test_update_extends
+ a = RDoc::Extend.new 'M1', nil
+ b = RDoc::Extend.new 'M2', nil
+ c = RDoc::Extend.new 'C', nil
+
+ @c1.add_extend a
+ @c1.add_extend b
+ @c1.add_extend c
+ @c1.each_extend do |extend| extend.module end # cache extended modules
+
+ @m1_m2.document_self = nil
+ assert @m1_m2.remove_from_documentation?
+
+ assert @store.modules_hash.key? @m1_m2.full_name
+ refute @store.modules_hash[@m1_m2.full_name].nil?
+ @store.remove_nodoc @store.modules_hash
+ refute @store.modules_hash.key? @m1_m2.full_name
+
+ @c1.update_extends
+
+ assert_equal [a, b, c], @c1.extends
+ end
+
+ def test_update_extends_trim
+ a = RDoc::Extend.new 'D::M', nil
+ b = RDoc::Extend.new 'D::M', nil
+
+ @c1.add_extend a
+ @c1.add_extend b
+ @c1.each_extend do |extend| extend.module end # cache extended modules
+
+ @c1.update_extends
+
+ assert_equal [a], @c1.extends
+ end
+
+ def test_update_extends_with_colons
+ a = RDoc::Extend.new 'M1', nil
+ b = RDoc::Extend.new 'M1::M2', nil
+ c = RDoc::Extend.new 'C', nil
+
+ @c1.add_extend a
+ @c1.add_extend b
+ @c1.add_extend c
+ @c1.each_extend do |extend| extend.module end # cache extended modules
+
+ @m1_m2.document_self = nil
+ assert @m1_m2.remove_from_documentation?
+
+ assert @store.modules_hash.key? @m1_m2.full_name
+ refute @store.modules_hash[@m1_m2.full_name].nil?
+
+ @store.remove_nodoc @store.modules_hash
+ refute @store.modules_hash.key? @m1_m2.full_name
+
+ @c1.update_extends
+
+ assert_equal [a, c], @c1.extends
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_code_object.rb b/jni/ruby/test/rdoc/test_rdoc_code_object.rb
new file mode 100644
index 0000000..2fb6ac2
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_code_object.rb
@@ -0,0 +1,450 @@
+# coding: US-ASCII
+
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocCodeObject < XrefTestCase
+
+ def setup
+ super
+
+ @co = RDoc::CodeObject.new
+ end
+
+ def test_initialize
+ assert @co.document_self, 'document_self'
+ assert @co.document_children, 'document_children'
+ refute @co.force_documentation, 'force_documentation'
+ refute @co.done_documenting, 'done_documenting'
+ refute @co.received_nodoc, 'received_nodoc'
+ assert_equal '', @co.comment, 'comment is empty'
+ end
+
+ def test_comment_equals
+ @co.comment = ''
+
+ assert_equal '', @co.comment
+
+ @co.comment = 'I am a comment'
+
+ assert_equal 'I am a comment', @co.comment
+ end
+
+ def test_comment_equals_comment
+ @co.comment = comment ''
+
+ assert_equal '', @co.comment.text
+
+ @co.comment = comment 'I am a comment'
+
+ assert_equal 'I am a comment', @co.comment.text
+ end
+
+ def test_comment_equals_document
+ doc = RDoc::Markup::Document.new
+ @co.comment = doc
+
+ @co.comment = ''
+
+ assert_equal doc, @co.comment
+ end
+
+ def test_comment_equals_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ refute_equal Encoding::UTF_8, ''.encoding, 'Encoding sanity check'
+
+ input = 'text'
+ input.force_encoding Encoding::UTF_8
+
+ @co.comment = input
+
+ assert_equal 'text', @co.comment
+ assert_equal Encoding::UTF_8, @co.comment.encoding
+ end
+
+ def test_comment_equals_encoding_blank
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ refute_equal Encoding::UTF_8, ''.encoding, 'Encoding sanity check'
+
+ input = ''
+ input.force_encoding Encoding::UTF_8
+
+ @co.comment = input
+
+ assert_equal '', @co.comment
+ assert_equal Encoding::UTF_8, @co.comment.encoding
+ end
+
+ def test_display_eh_document_self
+ assert @co.display?
+
+ @co.document_self = false
+
+ refute @co.display?
+ end
+
+ def test_display_eh_ignore
+ assert @co.display?
+
+ @co.ignore
+
+ refute @co.display?
+
+ @co.stop_doc
+
+ refute @co.display?
+
+ @co.done_documenting = false
+
+ refute @co.display?
+ end
+
+ def test_display_eh_suppress
+ assert @co.display?
+
+ @co.suppress
+
+ refute @co.display?
+
+ @co.comment = comment('hi')
+
+ refute @co.display?
+
+ @co.done_documenting = false
+
+ assert @co.display?
+
+ @co.ignore
+ @co.done_documenting = false
+
+ refute @co.display?
+ end
+
+ def test_document_children_equals
+ @co.document_children = false
+
+ refute @co.document_children
+
+ @store.rdoc.options.visibility = :nodoc
+
+ @co.store = @store
+
+ assert @co.document_children
+
+ @co.document_children = false
+
+ assert @co.document_children
+ end
+
+ def test_document_self_equals
+ @co.document_self = false
+ refute @co.document_self
+
+ @store.rdoc.options.visibility = :nodoc
+
+ @co.store = @store
+
+ assert @co.document_self
+
+ @co.document_self = false
+
+ assert @co.document_self
+ end
+
+ def test_documented_eh
+ refute @co.documented?
+
+ @co.comment = 'hi'
+
+ assert @co.documented?
+
+ @co.comment.replace ''
+
+ refute @co.documented?
+
+ @co.document_self = nil # notify :nodoc:
+
+ assert @co.documented?
+ end
+
+ def test_done_documenting
+ # once done_documenting is set, other properties refuse to go to "true"
+ @co.done_documenting = true
+
+ @co.document_self = true
+ refute @co.document_self
+
+ @co.document_children = true
+ refute @co.document_children
+
+ @co.force_documentation = true
+ refute @co.force_documentation
+
+ @co.start_doc
+ refute @co.document_self
+ refute @co.document_children
+
+ # turning done_documenting on
+ # resets others to true
+
+ @co.done_documenting = false
+ assert @co.document_self
+ assert @co.document_children
+
+ @co.done_documenting = true
+
+ @store.rdoc.options.visibility = :nodoc
+
+ @co.store = @store
+
+ refute @co.done_documenting
+
+ @co.done_documenting = true
+
+ refute @co.done_documenting
+ end
+
+ def test_each_parent
+ parents = []
+
+ @parent_m.each_parent do |code_object|
+ parents << code_object
+ end
+
+ assert_equal [@parent, @xref_data], parents
+ end
+
+ def test_file_name
+ assert_equal nil, @co.file_name
+
+ @co.record_location @store.add_file 'lib/file.rb'
+
+ assert_equal 'lib/file.rb', @co.file_name
+ end
+
+ def test_full_name_equals
+ @co.full_name = 'hi'
+
+ assert_equal 'hi', @co.instance_variable_get(:@full_name)
+
+ @co.full_name = nil
+
+ assert_nil @co.instance_variable_get(:@full_name)
+ end
+
+ def test_ignore
+ @co.ignore
+
+ refute @co.document_self
+ refute @co.document_children
+ assert @co.ignored?
+
+ @store.rdoc.options.visibility = :nodoc
+
+ @co.store = @store
+
+ assert @co.document_self
+ assert @co.document_children
+ refute @co.ignored?
+
+ @co.ignore
+
+ refute @co.ignored?
+ end
+
+ def test_ignore_eh
+ refute @co.ignored?
+
+ @co.ignore
+
+ assert @co.ignored?
+ end
+
+ def test_line
+ @c1_m.line = 5
+
+ assert_equal 5, @c1_m.line
+ end
+
+ def test_metadata
+ assert_empty @co.metadata
+
+ @co.metadata['markup'] = 'not_rdoc'
+
+ expected = { 'markup' => 'not_rdoc' }
+
+ assert_equal expected, @co.metadata
+
+ assert_equal 'not_rdoc', @co.metadata['markup']
+ end
+
+ def test_offset
+ @c1_m.offset = 5
+
+ assert_equal 5, @c1_m.offset
+ end
+
+ def test_options
+ assert_kind_of RDoc::Options, @co.options
+
+ @co.store = @store
+
+ assert_same @options, @co.options
+ end
+
+ def test_parent_file_name
+ assert_equal '(unknown)', @co.parent_file_name
+ assert_equal 'xref_data.rb', @c1.parent_file_name
+ end
+
+ def test_parent_name
+ assert_equal '(unknown)', @co.parent_name
+ assert_equal 'xref_data.rb', @c1.parent_name
+ assert_equal 'C2', @c2_c3.parent_name
+ end
+
+ def test_received_ndoc
+ @co.document_self = false
+ refute @co.received_nodoc
+
+ @co.document_self = nil
+ assert @co.received_nodoc
+
+ @co.document_self = true
+ end
+
+ def test_record_location
+ @co.record_location @xref_data
+
+ assert_equal 'xref_data.rb', @co.file.relative_name
+ end
+
+ def test_record_location_ignored
+ @co.ignore
+ @co.record_location @xref_data
+
+ refute @co.ignored?
+ end
+
+ def test_record_location_suppressed
+ @co.suppress
+ @co.record_location @xref_data
+
+ refute @co.suppressed?
+ end
+
+ def test_section
+ parent = RDoc::Context.new
+ section = parent.sections.first
+
+ @co.parent = parent
+ @co.instance_variable_set :@section, section
+
+ assert_equal section, @co.section
+
+ @co.instance_variable_set :@section, nil
+ @co.instance_variable_set :@section_title, nil
+
+ assert_equal section, @co.section
+
+ @co.instance_variable_set :@section, nil
+ @co.instance_variable_set :@section_title, 'new title'
+
+ assert_equal 'new title', @co.section.title
+ end
+
+ def test_start_doc
+ @co.document_self = false
+ @co.document_children = false
+
+ @co.start_doc
+
+ assert @co.document_self
+ assert @co.document_children
+ end
+
+ def test_start_doc_ignored
+ @co.ignore
+
+ @co.start_doc
+
+ assert @co.document_self
+ assert @co.document_children
+ refute @co.ignored?
+ end
+
+ def test_start_doc_suppressed
+ @co.suppress
+
+ @co.start_doc
+
+ assert @co.document_self
+ assert @co.document_children
+ refute @co.suppressed?
+ end
+
+ def test_store_equals
+ @co.document_self = false
+
+ @co.store = @store
+
+ refute @co.document_self
+
+ @store.rdoc.options.visibility = :nodoc
+
+ @co.store = @store
+
+ assert @co.document_self
+ end
+
+ def test_stop_doc
+ @co.document_self = true
+ @co.document_children = true
+
+ @co.stop_doc
+
+ refute @co.document_self
+ refute @co.document_children
+
+ @store.rdoc.options.visibility = :nodoc
+
+ @co.store = @store
+
+ assert @co.document_self
+ assert @co.document_children
+
+ @co.stop_doc
+
+ assert @co.document_self
+ assert @co.document_children
+ end
+
+ def test_suppress
+ @co.suppress
+
+ refute @co.document_self
+ refute @co.document_children
+ assert @co.suppressed?
+
+ @store.rdoc.options.visibility = :nodoc
+
+ @co.store = @store
+
+ refute @co.suppressed?
+
+ @co.suppress
+
+ refute @co.suppressed?
+ end
+
+ def test_suppress_eh
+ refute @co.suppressed?
+
+ @co.suppress
+
+ assert @co.suppressed?
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_comment.rb b/jni/ruby/test/rdoc/test_rdoc_comment.rb
new file mode 100644
index 0000000..2a1318b
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_comment.rb
@@ -0,0 +1,504 @@
+# coding: us-ascii
+
+require 'rdoc/test_case'
+
+class TestRDocComment < RDoc::TestCase
+
+ def setup
+ super
+
+ @top_level = @store.add_file 'file.rb'
+ @comment = RDoc::Comment.new
+ @comment.location = @top_level
+ @comment.text = 'this is a comment'
+ end
+
+ def test_empty_eh
+ refute_empty @comment
+
+ @comment = ''
+
+ assert_empty @comment
+ end
+
+ def test_equals2
+ assert_equal @comment, @comment.dup
+
+ c2 = @comment.dup
+ c2.text = nil
+
+ refute_equal @comment, c2
+
+ c3 = @comment.dup
+ c3.location = nil
+
+ refute_equal @comment, c3
+ end
+
+ def test_extract_call_seq
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ comment = RDoc::Comment.new <<-COMMENT, @top_level
+call-seq:
+ bla => true or false
+
+moar comment
+ COMMENT
+
+ comment.extract_call_seq m
+
+ assert_equal "bla => true or false\n", m.call_seq
+ end
+
+ def test_extract_call_seq_blank
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ comment = RDoc::Comment.new <<-COMMENT, @top_level
+call-seq:
+ bla => true or false
+
+ COMMENT
+
+ comment.extract_call_seq m
+
+ assert_equal "bla => true or false\n", m.call_seq
+ end
+
+ def test_extract_call_seq_commented
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ comment = RDoc::Comment.new <<-COMMENT, @top_level
+# call-seq:
+# bla => true or false
+#
+# moar comment
+ COMMENT
+
+ comment.extract_call_seq m
+
+ assert_equal nil, m.call_seq
+ end
+
+ def test_extract_call_seq_no_blank
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ comment = RDoc::Comment.new <<-COMMENT, @top_level
+call-seq:
+ bla => true or false
+ COMMENT
+
+ comment.extract_call_seq m
+
+ assert_equal "bla => true or false\n", m.call_seq
+ end
+
+ def test_extract_call_seq_undent
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ comment = RDoc::Comment.new <<-COMMENT, @top_level
+call-seq:
+ bla => true or false
+moar comment
+ COMMENT
+
+ comment.extract_call_seq m
+
+ assert_equal "bla => true or false\nmoar comment\n", m.call_seq
+ end
+
+ def test_extract_call_seq_c
+ comment = RDoc::Comment.new <<-COMMENT
+call-seq:
+ commercial() -> Date <br />
+ commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
+ commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
+
+If no arguments are given:
+* ruby 1.8: returns a +Date+ for 1582-10-15 (the Day of Calendar Reform in
+ Italy)
+* ruby 1.9: returns a +Date+ for julian day 0
+
+Otherwise, returns a +Date+ for the commercial week year, commercial week,
+and commercial week day given. Ignores the 4th argument.
+ COMMENT
+
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ comment.extract_call_seq method_obj
+
+ expected = <<-CALL_SEQ.chomp
+commercial() -> Date <br />
+commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
+commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
+
+ CALL_SEQ
+
+ assert_equal expected, method_obj.call_seq
+ end
+
+ def test_extract_call_seq_c_no_blank
+ comment = RDoc::Comment.new <<-COMMENT
+call-seq:
+ commercial() -> Date <br />
+ commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
+ commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
+ COMMENT
+
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ comment.extract_call_seq method_obj
+
+ expected = <<-CALL_SEQ.chomp
+commercial() -> Date <br />
+commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
+commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
+
+ CALL_SEQ
+
+ assert_equal expected, method_obj.call_seq
+ end
+
+ def test_extract_call_seq_c_separator
+ comment = RDoc::Comment.new <<-'COMMENT'
+call-seq:
+ ARGF.readlines(sep=$/) -> array
+ ARGF.readlines(limit) -> array
+ ARGF.readlines(sep, limit) -> array
+
+ ARGF.to_a(sep=$/) -> array
+ ARGF.to_a(limit) -> array
+ ARGF.to_a(sep, limit) -> array
+
+Reads +ARGF+'s current file in its entirety, returning an +Array+ of its
+lines, one line per element. Lines are assumed to be separated by _sep_.
+
+ lines = ARGF.readlines
+ lines[0] #=> "This is line one\n"
+
+ COMMENT
+
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ comment.extract_call_seq method_obj
+
+ expected = <<-CALL_SEQ
+ARGF.readlines(sep=$/) -> array
+ARGF.readlines(limit) -> array
+ARGF.readlines(sep, limit) -> array
+ARGF.to_a(sep=$/) -> array
+ARGF.to_a(limit) -> array
+ARGF.to_a(sep, limit) -> array
+ CALL_SEQ
+
+ assert_equal expected, method_obj.call_seq
+
+ expected = <<-'COMMENT'
+
+Reads +ARGF+'s current file in its entirety, returning an +Array+ of its
+lines, one line per element. Lines are assumed to be separated by _sep_.
+
+ lines = ARGF.readlines
+ lines[0] #=> "This is line one\n"
+
+ COMMENT
+
+ assert_equal expected, comment.text
+ end
+
+ def test_force_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ @comment.force_encoding Encoding::UTF_8
+
+ assert_equal Encoding::UTF_8, @comment.text.encoding
+ end
+
+ def test_format
+ assert_equal 'rdoc', @comment.format
+ end
+
+ def test_format_equals
+ c = comment 'content'
+ document = c.parse
+
+ c.format = RDoc::RD
+
+ assert_equal RDoc::RD, c.format
+ refute_same document, c.parse
+ end
+
+ def test_initialize_copy
+ copy = @comment.dup
+
+ refute_same @comment.text, copy.text
+ assert_same @comment.location, copy.location
+ end
+
+ def test_location
+ assert_equal @top_level, @comment.location
+ end
+
+ def test_normalize
+ @comment.text = <<-TEXT
+ # comment
+ TEXT
+
+ assert_same @comment, @comment.normalize
+
+ assert_equal 'comment', @comment.text
+ end
+
+ def test_normalize_twice
+ @comment.text = <<-TEXT
+ # comment
+ TEXT
+
+ @comment.normalize
+
+ text = @comment.text
+
+ @comment.normalize
+
+ assert_same text, @comment.text, 'normalize not cached'
+ end
+
+ def test_normalize_document
+ @comment.text = nil
+ @comment.document = @RM::Document.new
+
+ assert_same @comment, @comment.normalize
+
+ assert_nil @comment.text
+ end
+
+ def test_normalize_eh
+ refute @comment.normalized?
+
+ @comment.normalize
+
+ assert @comment.normalized?
+ end
+
+ def test_text
+ assert_equal 'this is a comment', @comment.text
+ end
+
+ def test_text_equals
+ @comment.text = 'other'
+
+ assert_equal 'other', @comment.text
+ refute @comment.normalized?
+ end
+
+ def test_text_equals_no_text
+ c = RDoc::Comment.new nil, @top_level
+ c.document = @RM::Document.new
+
+ e = assert_raises RDoc::Error do
+ c.text = 'other'
+ end
+
+ assert_equal 'replacing document-only comment is not allowed', e.message
+ end
+
+ def test_text_equals_parsed
+ document = @comment.parse
+
+ @comment.text = 'other'
+
+ refute_equal document, @comment.parse
+ end
+
+ def test_tomdoc_eh
+ refute @comment.tomdoc?
+
+ @comment.format = 'tomdoc'
+
+ assert @comment.tomdoc?
+ end
+
+ def test_parse
+ parsed = @comment.parse
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new('this is a comment'))
+
+ expected.file = @top_level
+
+ assert_equal expected, parsed
+ assert_same parsed, @comment.parse
+ end
+
+ def test_parse_rd
+ c = comment 'it ((*works*))'
+ c.format = 'rd'
+
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('it <em>works</em>'))
+ expected.file = @top_level
+
+ assert_equal expected, c.parse
+ end
+
+ def test_remove_private_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ comment = RDoc::Comment.new <<-EOS, @top_level
+# This is text
+#--
+# this is private
+ EOS
+
+ comment.force_encoding Encoding::IBM437
+
+ comment.remove_private
+
+ assert_equal Encoding::IBM437, comment.text.encoding
+ end
+
+ def test_remove_private_hash
+ @comment.text = <<-TEXT
+#--
+# private
+#++
+# public
+ TEXT
+
+ @comment.remove_private
+
+ assert_equal "# public\n", @comment.text
+ end
+
+ def test_remove_private_hash_trail
+ comment = RDoc::Comment.new <<-EOS, @top_level
+# This is text
+#--
+# this is private
+ EOS
+
+ expected = RDoc::Comment.new <<-EOS, @top_level
+# This is text
+ EOS
+
+ comment.remove_private
+
+ assert_equal expected, comment
+ end
+
+ def test_remove_private_long
+ comment = RDoc::Comment.new <<-EOS, @top_level
+#-----
+#++
+# this is text
+#-----
+ EOS
+
+ expected = RDoc::Comment.new <<-EOS, @top_level
+# this is text
+ EOS
+
+ comment.remove_private
+
+ assert_equal expected, comment
+ end
+
+ def test_remove_private_rule
+ comment = RDoc::Comment.new <<-EOS, @top_level
+# This is text with a rule:
+# ---
+# this is also text
+ EOS
+
+ expected = comment.dup
+
+ comment.remove_private
+
+ assert_equal expected, comment
+ end
+
+ def test_remove_private_star
+ @comment.text = <<-TEXT
+/*
+ *--
+ * private
+ *++
+ * public
+ */
+ TEXT
+
+ @comment.remove_private
+
+ assert_equal "/*\n * public\n */\n", @comment.text
+ end
+
+ def test_remove_private_star2
+ @comment.text = <<-TEXT
+/*--
+ * private
+ *++
+ * public
+ */
+ TEXT
+
+ @comment.remove_private
+
+ assert_equal "/*--\n * private\n *++\n * public\n */\n", @comment.text
+ end
+
+ def test_remove_private_toggle
+ comment = RDoc::Comment.new <<-EOS, @top_level
+# This is text
+#--
+# this is private
+#++
+# This is text again.
+ EOS
+
+ expected = RDoc::Comment.new <<-EOS, @top_level
+# This is text
+# This is text again.
+ EOS
+
+ comment.remove_private
+
+ assert_equal expected, comment
+ end
+
+ def test_remove_private_toggle_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ comment = RDoc::Comment.new <<-EOS, @top_level
+# This is text
+#--
+# this is private
+#++
+# This is text again.
+ EOS
+
+ comment.force_encoding Encoding::IBM437
+
+ comment.remove_private
+
+ assert_equal Encoding::IBM437, comment.text.encoding
+ end
+
+ def test_remove_private_toggle_encoding_ruby_bug?
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ comment = RDoc::Comment.new <<-EOS, @top_level
+#--
+# this is private
+#++
+# This is text again.
+ EOS
+
+ comment.force_encoding Encoding::IBM437
+
+ comment.remove_private
+
+ assert_equal Encoding::IBM437, comment.text.encoding
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_constant.rb b/jni/ruby/test/rdoc/test_rdoc_constant.rb
new file mode 100644
index 0000000..171963d
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_constant.rb
@@ -0,0 +1,181 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocConstant < XrefTestCase
+
+ def setup
+ super
+
+ @const = @c1.constants.first
+ end
+
+ def test_documented_eh
+ top_level = @store.add_file 'file.rb'
+
+ const = RDoc::Constant.new 'CONST', nil, nil
+ top_level.add_constant const
+
+ refute const.documented?
+
+ const.comment = comment 'comment'
+
+ assert const.documented?
+ end
+
+ def test_documented_eh_alias
+ top_level = @store.add_file 'file.rb'
+
+ const = RDoc::Constant.new 'CONST', nil, nil
+ top_level.add_constant const
+
+ refute const.documented?
+
+ const.is_alias_for = 'C1'
+
+ refute const.documented?
+
+ @c1.add_comment comment('comment'), @top_level
+
+ assert const.documented?
+ end
+
+ def test_full_name
+ assert_equal 'C1::CONST', @const.full_name
+ end
+
+ def test_is_alias_for
+ top_level = @store.add_file 'file.rb'
+
+ c = RDoc::Constant.new 'CONST', nil, 'comment'
+ top_level.add_constant c
+
+ assert_nil c.is_alias_for
+
+ c.is_alias_for = 'C1'
+
+ assert_equal @c1, c.is_alias_for
+
+ c.is_alias_for = 'unknown'
+
+ assert_equal 'unknown', c.is_alias_for
+ end
+
+ def test_marshal_dump
+ top_level = @store.add_file 'file.rb'
+
+ c = RDoc::Constant.new 'CONST', nil, 'this is a comment'
+ c.record_location top_level
+
+ aliased = top_level.add_class RDoc::NormalClass, 'Aliased'
+ c.is_alias_for = aliased
+
+ cm = top_level.add_class RDoc::NormalClass, 'Klass'
+ cm.add_constant c
+
+ section = cm.sections.first
+
+ loaded = Marshal.load Marshal.dump c
+ loaded.store = @store
+
+ comment = doc(para('this is a comment'))
+
+ assert_equal c, loaded
+
+ assert_equal aliased, loaded.is_alias_for
+ assert_equal comment, loaded.comment
+ assert_equal top_level, loaded.file
+ assert_equal 'Klass::CONST', loaded.full_name
+ assert_equal 'CONST', loaded.name
+ assert_nil loaded.visibility
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+ end
+
+ def test_marshal_load
+ top_level = @store.add_file 'file.rb'
+
+ c = RDoc::Constant.new 'CONST', nil, 'this is a comment'
+ c.record_location top_level
+
+ cm = top_level.add_class RDoc::NormalClass, 'Klass'
+ cm.add_constant c
+
+ section = cm.sections.first
+
+ loaded = Marshal.load Marshal.dump c
+ loaded.store = @store
+
+ comment = doc(para('this is a comment'))
+
+ assert_equal c, loaded
+
+ assert_nil loaded.is_alias_for
+ assert_equal comment, loaded.comment
+ assert_equal top_level, loaded.file
+ assert_equal 'Klass::CONST', loaded.full_name
+ assert_equal 'CONST', loaded.name
+ assert_nil loaded.visibility
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+
+ assert loaded.display?
+ end
+
+ def test_marshal_load_version_0
+ top_level = @store.add_file 'file.rb'
+
+ aliased = top_level.add_class RDoc::NormalClass, 'Aliased'
+ cm = top_level.add_class RDoc::NormalClass, 'Klass'
+ section = cm.sections.first
+
+ loaded = Marshal.load "\x04\bU:\x13RDoc::Constant[\x0Fi\x00I" +
+ "\"\nCONST\x06:\x06ETI\"\x11Klass::CONST\x06" +
+ ";\x06T0I\"\fAliased\x06;\x06To" +
+ ":\eRDoc::Markup::Document\a:\v@parts[\x06o" +
+ ":\x1CRDoc::Markup::Paragraph\x06;\b[\x06I" +
+ "\"\x16this is a comment\x06;\x06T:\n@file0I" +
+ "\"\ffile.rb\x06;\x06TI\"\nKlass\x06" +
+ ";\x06Tc\x16RDoc::NormalClass0"
+
+ loaded.store = @store
+
+ comment = doc(para('this is a comment'))
+
+ assert_equal aliased, loaded.is_alias_for
+ assert_equal comment, loaded.comment
+ assert_equal top_level, loaded.file
+ assert_equal 'Klass::CONST', loaded.full_name
+ assert_equal 'CONST', loaded.name
+ assert_nil loaded.visibility
+ assert_equal cm, loaded.parent
+ assert_equal section, loaded.section
+
+ assert loaded.display?
+ end
+
+ def test_marshal_round_trip
+ top_level = @store.add_file 'file.rb'
+
+ c = RDoc::Constant.new 'CONST', nil, 'this is a comment'
+ c.record_location top_level
+ c.is_alias_for = 'Unknown'
+
+ cm = top_level.add_class RDoc::NormalClass, 'Klass'
+ cm.add_constant c
+
+ section = cm.sections.first
+
+ loaded = Marshal.load Marshal.dump c
+ loaded.store = @store
+
+ reloaded = Marshal.load Marshal.dump loaded
+ reloaded.store = @store
+
+ assert_equal section, reloaded.section
+ assert_equal 'Unknown', reloaded.is_alias_for
+ end
+
+ def test_path
+ assert_equal 'C1.html#CONST', @const.path
+ end
+
+end
diff --git a/jni/ruby/test/rdoc/test_rdoc_context.rb b/jni/ruby/test/rdoc/test_rdoc_context.rb
new file mode 100644
index 0000000..c981cf3
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_context.rb
@@ -0,0 +1,901 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocContext < XrefTestCase
+
+ def setup
+ super
+
+ @context = RDoc::Context.new
+ @context.store = @store
+
+ @enumerator = # 1.8 vs 1.9
+ Object.const_defined?(:Enumerator) ? Enumerator : Enumerable::Enumerator
+ end
+
+ def test_initialize
+ assert_empty @context.in_files
+ assert_equal 'unknown', @context.name
+ assert_equal '', @context.comment
+ assert_equal nil, @context.parent
+ assert_equal :public, @context.visibility
+ assert_equal 1, @context.sections.length
+ assert_equal nil, @context.temporary_section
+
+ assert_empty @context.classes_hash
+ assert_empty @context.modules_hash
+
+ assert_empty @context.method_list
+ assert_empty @context.attributes
+ assert_empty @context.aliases
+ assert_empty @context.requires
+ assert_empty @context.includes
+ assert_empty @context.constants
+ end
+
+ def test_add_alias
+ as = RDoc::Alias.new nil, 'old_name', 'new_name', 'comment'
+
+ @context.add_alias as
+
+ assert_equal [as], @context.external_aliases
+ assert_equal [as], @context.unmatched_alias_lists['#old_name']
+ end
+
+ def test_add
+ @context.add RDoc::Extend, 'Ext', 'comment'
+ @context.add RDoc::Include, 'Incl', 'comment'
+
+ refute_empty @context.extends
+ refute_empty @context.includes
+ end
+
+ def test_add_alias_method_attr
+ top_level = @store.add_file 'file.rb'
+
+ attr = RDoc::Attr.new nil, 'old_name', 'R', ''
+
+ as = RDoc::Alias.new nil, 'old_name', 'new_name', 'comment'
+ as.record_location top_level
+ as.parent = @context
+
+ @context.add_attribute attr
+ @context.add_alias as
+
+ assert_empty @context.aliases
+ assert_empty @context.unmatched_alias_lists
+ assert_equal %w[old_name new_name], @context.attributes.map { |m| m.name }
+
+ new = @context.attributes.last
+ assert_equal top_level, new.file
+ end
+
+ def test_add_alias_method
+ top_level = @store.add_file 'file.rb'
+
+ meth = RDoc::AnyMethod.new nil, 'old_name'
+ meth.singleton = false
+
+ as = RDoc::Alias.new nil, 'old_name', 'new_name', 'comment'
+ as.record_location top_level
+ as.parent = @context
+
+ @context.add_method meth
+ @context.add_alias as
+
+ assert_empty @context.aliases
+ assert_empty @context.unmatched_alias_lists
+ assert_equal %w[old_name new_name], @context.method_list.map { |m| m.name }
+
+ new = @context.method_list.last
+ assert_equal top_level, new.file
+ end
+
+ def test_add_alias_method_singleton
+ meth = RDoc::AnyMethod.new nil, 'old_name'
+ meth.singleton = true
+
+ as = RDoc::Alias.new nil, 'old_name', 'new_name', 'comment'
+ as.singleton = true
+
+ as.parent = @context
+
+ @context.add_method meth
+ @context.add_alias as
+
+ assert_empty @context.aliases
+ assert_empty @context.unmatched_alias_lists
+ assert_equal %w[old_name new_name], @context.method_list.map { |m| m.name }
+
+ assert @context.method_list.last.singleton
+ end
+
+ def test_add_class
+ @c1.add_class RDoc::NormalClass, 'Klass', 'Object'
+
+ assert_includes @c1.classes.map { |k| k.full_name }, 'C1::Klass'
+ assert_includes @store.all_classes.map { |k| k.full_name }, 'C1::Klass'
+ end
+
+ def test_add_class_basic_object
+ skip 'BasicObject is 1.9 only' unless defined?(BasicObject)
+
+ @xref_data.add_class RDoc::NormalClass, 'BasicObject'
+
+ basic = @xref_data.find_module_named 'BasicObject'
+
+ assert_nil basic.superclass
+
+ @c1.add_class RDoc::NormalClass, 'BasicObject'
+
+ basic = @c1.find_module_named 'BasicObject'
+
+ assert_equal 'Object', basic.superclass
+ end
+
+ def test_add_class_object
+ root_class = defined?(BasicObject) ? 'BasicObject' : nil
+
+ @xref_data.add_class RDoc::NormalClass, 'Object'
+
+ object = @xref_data.find_module_named 'Object'
+
+ assert_equal root_class, object.superclass
+
+ @c1.add_class RDoc::NormalClass, 'Object'
+
+ object = @c1.find_module_named 'Object'
+
+ assert_equal 'Object', object.superclass.full_name
+ end
+
+ def test_add_class_singleton
+ @c1.add_class RDoc::NormalClass, 'Klass', 'Object'
+
+ assert_includes @c1.classes.map { |k| k.full_name }, 'C1::Klass'
+ assert_includes @store.all_classes.map { |k| k.full_name }, 'C1::Klass'
+ end
+
+ def test_add_class_superclass
+ @c1.add_class RDoc::NormalClass, 'Klass', 'Object'
+ @c1.add_class RDoc::NormalClass, 'Klass', 'Other'
+ @c1.add_class RDoc::NormalClass, 'Klass', 'Object'
+
+ klass = @c1.find_module_named 'Klass'
+ assert_equal 'Other', klass.superclass
+ end
+
+ def test_add_class_upgrade
+ @c1.add_module RDoc::NormalModule, 'Klass'
+ @c1.add_class RDoc::NormalClass, 'Klass', nil
+
+ assert_includes @c1.classes.map { |k| k.full_name }, 'C1::Klass',
+ 'c1 classes'
+ refute_includes @c1.modules.map { |k| k.full_name }, 'C1::Klass',
+ 'c1 modules'
+
+ assert_includes @store.all_classes.map { |k| k.full_name }, 'C1::Klass',
+ 'TopLevel classes'
+ refute_includes @store.all_modules.map { |k| k.full_name }, 'C1::Klass',
+ 'TopLevel modules'
+ end
+
+ def test_add_constant
+ const = RDoc::Constant.new 'NAME', 'value', 'comment'
+ @context.add_constant const
+
+ assert_equal [const], @context.constants
+ end
+
+ def test_add_extend
+ ext = RDoc::Extend.new 'Name', 'comment'
+ @context.add_extend ext
+
+ assert_equal [ext], @context.extends
+ end
+
+ def test_add_include
+ incl = RDoc::Include.new 'Name', 'comment'
+ @context.add_include incl
+
+ assert_equal [incl], @context.includes
+ end
+
+ def test_add_method
+ meth = RDoc::AnyMethod.new nil, 'old_name'
+ meth.visibility = nil
+
+ @context.add_method meth
+
+ assert_equal [meth], @context.method_list
+ assert_equal :public, meth.visibility
+ end
+
+ def test_add_method_alias
+ as = RDoc::Alias.new nil, 'old_name', 'new_name', 'comment'
+ meth = RDoc::AnyMethod.new nil, 'old_name'
+
+ @context.add_alias as
+ refute_empty @context.external_aliases
+
+ @context.add_method meth
+
+ assert_empty @context.external_aliases
+ assert_empty @context.unmatched_alias_lists
+ assert_equal %w[old_name new_name], @context.method_list.map { |m| m.name }
+ end
+
+ def test_add_method_duplicate
+ @store.rdoc.options.verbosity = 2
+
+ meth1 = RDoc::AnyMethod.new nil, 'name'
+ meth1.record_location @store.add_file 'first.rb'
+ meth1.visibility = nil
+ meth1.comment = comment 'first'
+
+ @context.add_method meth1
+
+ meth2 = RDoc::AnyMethod.new nil, 'name'
+ meth2.record_location @store.add_file 'second.rb'
+ meth2.comment = comment 'second'
+
+ _, err = verbose_capture_io do
+ @context.add_method meth2
+ end
+
+ expected = 'Duplicate method (unknown)#name in file second.rb, ' \
+ 'previously in file first.rb'
+
+ assert_equal expected, err.chomp
+
+ method = @context.method_list.first
+
+ assert_equal 'first', method.comment.text
+ end
+
+ def test_add_method_duplicate_loading
+ @context.store = nil
+
+ meth1 = RDoc::AnyMethod.new nil, 'name'
+ meth1.record_location @store.add_file 'first.rb'
+ meth1.visibility = nil
+ meth1.comment = comment 'first'
+
+ @context.add_method meth1
+
+ meth2 = RDoc::AnyMethod.new nil, 'name'
+ meth2.record_location @store.add_file 'second.rb'
+ meth2.comment = comment 'second'
+
+ _, err = verbose_capture_io do
+ @context.add_method meth2
+ end
+
+ assert_empty err
+
+ method = @context.method_list.first
+
+ assert_equal 'first', method.comment.text
+ end
+
+ def test_add_module
+ @c1.add_module RDoc::NormalModule, 'Mod'
+
+ assert_includes @c1.modules.map { |m| m.full_name }, 'C1::Mod'
+ end
+
+ def test_add_module_alias
+ tl = @store.add_file 'file.rb'
+
+ c3_c4 = @c2.add_module_alias @c2_c3, 'C4', tl
+
+ alias_constant = @c2.constants.first
+
+ assert_equal 'C2::C4', c3_c4.full_name
+ assert_equal tl, alias_constant.file
+ end
+
+ def test_add_module_alias_top_level
+ store = RDoc::Store.new
+
+ top_level = store.add_file 'file.rb'
+
+ klass = top_level.add_class RDoc::NormalClass, 'Klass'
+ klass.comment = 'klass comment'
+
+ object = top_level.add_class RDoc::NormalClass, 'Object'
+
+ top_level.add_module_alias klass, 'A', top_level
+
+ refute_empty object.constants
+
+ constant = object.constants.first
+
+ assert_equal 'klass comment', constant.comment
+ end
+
+ def test_add_module_class
+ k = @c1.add_class RDoc::NormalClass, 'Klass', nil
+ m = @c1.add_module RDoc::NormalModule, 'Klass'
+
+ assert_equal k, m, 'returns class'
+ assert_empty @c1.modules
+ end
+
+ def test_add_require
+ req = RDoc::Require.new 'require', 'comment'
+ @c1.add_require req
+
+ assert_empty @c1.requires
+ assert_includes @c1.top_level.requires, req
+ end
+
+ def test_add_section
+ default_section = @context.sections.first
+
+ @context.add_section nil, comment('comment', @top_level)
+
+ assert_equal 1, @context.sections.length
+ assert_equal [comment("comment", @top_level)],
+ @context.sections.first.comments
+
+ @context.add_section nil, comment('new comment', @top_level)
+
+ assert_equal 1, @context.sections.length
+ assert_equal [comment('comment', @top_level),
+ comment('new comment', @top_level)],
+ @context.sections.first.comments
+
+ @context.add_section 'other', comment('', @top_level)
+
+ assert_equal 2, @context.sections.length
+
+ new_section = @context.sections.find { |section| section.title == 'other' }
+ assert new_section
+ assert_equal default_section, @context.current_section
+ end
+
+ def test_add_section_no_comment
+ default_section = @context.sections.first
+
+ @context.add_section nil
+
+ assert_equal 1, @context.sections.length
+
+ @context.add_section 'other'
+
+ assert_equal 2, @context.sections.length
+
+ new_section = @context.sections.find { |section| section.title == 'other' }
+
+ assert new_section
+ assert_equal default_section, @context.current_section
+ end
+
+ def test_add_to
+ incl = RDoc::Include.new 'Name', 'comment'
+ arr = []
+ @context.add_to arr, incl
+
+ assert_includes arr, incl
+ assert_equal @context, incl.parent
+ assert_equal @context.current_section, incl.section
+ end
+
+ def test_add_to_temporary_section
+ incl = RDoc::Include.new 'Name', 'comment'
+ arr = []
+ section =
+ @context.add_section 'temporary', RDoc::Comment.new('', @top_level)
+ @context.temporary_section = section
+
+ @context.add_to arr, incl
+
+ assert_includes arr, incl
+ assert_equal @context, incl.parent
+ assert_equal section, incl.section
+ end
+
+ def test_add_to_no_document_self
+ incl = RDoc::Include.new 'Name', 'comment'
+ arr = []
+ @context.document_self = false
+ @context.add_to arr, incl
+
+ refute_includes arr, incl
+ end
+
+ def test_add_to_done_documenting
+ incl = RDoc::Include.new 'Name', 'comment'
+ arr = []
+ @context.done_documenting = true
+ @context.add_to arr, incl
+
+ refute_includes arr, incl
+ end
+
+ def bench_add_include
+ cm = RDoc::ClassModule.new 'Klass'
+
+ assert_performance_linear 0.9 do |count|
+ count.times do |i|
+ cm.add_include RDoc::Include.new("N::M#{i}", nil)
+ end
+ end
+ end
+
+ def test_child_name
+ assert_equal 'C1::C1', @c1.child_name('C1')
+ end
+
+ def test_classes
+ assert_equal %w[C2::C3], @c2.classes.map { |k| k.full_name }
+ assert_equal %w[C3::H1 C3::H2], @c3.classes.map { |k| k.full_name }.sort
+ end
+
+ def test_current_section
+ default_section = @context.current_section
+
+ new_section =
+ @context.add_section 'other', RDoc::Comment.new('', @top_level)
+ @context.temporary_section = new_section
+
+ assert_equal new_section, @context.current_section
+ assert_equal default_section, @context.current_section
+ end
+
+ def test_defined_in_eh
+ assert @c1.defined_in?(@c1.top_level)
+
+ refute @c1.defined_in?(@store.add_file('name.rb'))
+ end
+
+ def test_equals2
+ assert_equal @c3, @c3
+ refute_equal @c2, @c3
+ refute_equal @c2_c3, @c3
+ end
+
+ def test_each_method_enumerator
+ assert_kind_of @enumerator, @c1.each_method
+ end
+
+ def test_each_section
+ sects = []
+ consts = []
+ attrs = []
+
+ @c1.each_section do |section, constants, attributes|
+ sects << section
+ consts << constants
+ attrs << attributes
+ end
+
+ assert_equal [nil, 'separate'], sects.map { |section| section.title }
+
+ expected_consts = [
+ [@c1.constants.first],
+ [],
+ ]
+
+ assert_equal expected_consts, consts
+
+ expected_attrs = [
+ [@c1.attributes[0], @c1.attributes[3]],
+ [@c1.attributes[1], @c1.attributes[2]],
+ ]
+
+ assert_equal expected_attrs, attrs
+ end
+
+ def test_each_section_enumerator
+ assert_kind_of @enumerator, @c1.each_section
+ end
+
+ def test_find_attribute_named
+ assert_equal nil, @c1.find_attribute_named('none')
+ assert_equal 'R', @c1.find_attribute_named('attr').rw
+ assert_equal 'R', @c1.find_attribute_named('attr_reader').rw
+ assert_equal 'W', @c1.find_attribute_named('attr_writer').rw
+ assert_equal 'RW', @c1.find_attribute_named('attr_accessor').rw
+ end
+
+ def test_find_class_method_named
+ assert_equal nil, @c1.find_class_method_named('none')
+
+ m = @c1.find_class_method_named('m')
+ assert_instance_of RDoc::AnyMethod, m
+ assert m.singleton
+ end
+
+ def test_find_constant_named
+ assert_equal nil, @c1.find_constant_named('NONE')
+ assert_equal ':const', @c1.find_constant_named('CONST').value
+ end
+
+ def test_find_enclosing_module_named
+ assert_equal nil, @c2_c3.find_enclosing_module_named('NONE')
+ assert_equal @c1, @c2_c3.find_enclosing_module_named('C1')
+ assert_equal @c2, @c2_c3.find_enclosing_module_named('C2')
+ end
+
+ def test_find_file_named
+ assert_equal nil, @c1.find_file_named('nonexistent.rb')
+ assert_equal @xref_data, @c1.find_file_named(@file_name)
+ end
+
+ def test_find_instance_method_named
+ assert_equal nil, @c1.find_instance_method_named('none')
+
+ m = @c1.find_instance_method_named('m')
+ assert_instance_of RDoc::AnyMethod, m
+ refute m.singleton
+ end
+
+ def test_find_local_symbol
+ assert_equal true, @c1.find_local_symbol('m').singleton
+ assert_equal ':const', @c1.find_local_symbol('CONST').value
+ assert_equal 'R', @c1.find_local_symbol('attr').rw
+ assert_equal @xref_data, @c1.find_local_symbol(@file_name)
+ assert_equal @c2_c3, @c2.find_local_symbol('C3')
+ end
+
+ def test_find_method_named
+ assert_equal true, @c1.find_method_named('m').singleton
+ end
+
+ def test_find_module_named
+ assert_equal @c2_c3, @c2.find_module_named('C3')
+ assert_equal @c2, @c2.find_module_named('C2')
+ assert_equal @c1, @c2.find_module_named('C1')
+
+ assert_equal 'C2::C3', @c2.find_module_named('C3').full_name
+ end
+
+ def test_find_symbol
+ c3 = @xref_data.find_module_named('C3')
+ assert_equal c3, @xref_data.find_symbol('C3')
+ assert_equal c3, @c2.find_symbol('::C3')
+ assert_equal @c2_c3, @c2.find_symbol('C3')
+ end
+
+ def test_find_symbol_method
+ assert_equal @c1__m, @c1.find_symbol('m')
+ assert_equal @c1_m, @c1.find_symbol('#m')
+ assert_equal @c1__m, @c1.find_symbol('::m')
+ end
+
+ def test_find_symbol_module
+ assert_nil @m1_m2.find_symbol_module 'N'
+ assert_nil @m1_m2.find_symbol_module 'M2::M1'
+
+ @m1_m2.parent = nil # loaded from legacy ri store
+
+ assert_nil @m1_m2.find_symbol_module 'N'
+ assert_nil @m1_m2.find_symbol_module 'M2::M1'
+ end
+
+ def test_fully_documented_eh
+ context = RDoc::Context.new
+
+ refute context.fully_documented?
+
+ context.comment = 'hi'
+
+ assert context.fully_documented?
+
+ m = @c1_m
+
+ context.add_method m
+
+ refute context.fully_documented?
+
+ m.comment = 'hi'
+
+ assert context.fully_documented?
+
+ c = RDoc::Constant.new 'C', '0', nil
+
+ context.add_constant c
+
+ refute context.fully_documented?
+
+ c.comment = 'hi'
+
+ assert context.fully_documented?
+
+ a = RDoc::Attr.new '', 'a', 'RW', nil
+
+ context.add_attribute a
+
+ refute context.fully_documented?
+
+ a.comment = 'hi'
+
+ assert context.fully_documented?
+ end
+
+ def test_spaceship
+ assert_equal(-1, @c2.<=>(@c3))
+ assert_equal 0, @c2.<=>(@c2)
+ assert_equal 1, @c3.<=>(@c2)
+
+ assert_equal 1, @c2_c3.<=>(@c2)
+ assert_equal(-1, @c2_c3.<=>(@c3))
+
+ assert_nil @c2.<=>(Gem.loaded_specs.values.first)
+ end
+
+ def test_methods_by_type
+ expected = {
+ 'instance' => {
+ :private => [],
+ :protected => [],
+ :public => [@c1_m],
+ },
+ 'class' => {
+ :private => [],
+ :protected => [],
+ :public => [@c1__m],
+ },
+ }
+
+ assert_equal expected, @c1.methods_by_type
+ end
+
+ def test_methods_by_type_section
+ separate = @c1.sections_hash['separate']
+ @c1_m.section = separate
+
+ expected = {
+ 'instance' => {
+ :private => [],
+ :protected => [],
+ :public => [@c1_m],
+ },
+ 'class' => {
+ :private => [],
+ :protected => [],
+ :public => [],
+ },
+ }
+
+ assert_equal expected, @c1.methods_by_type(separate)
+ end
+
+ def test_methods_matching
+ methods = []
+
+ @parent.methods_matching 'm' do |m|
+ methods << m
+ end
+
+ assert_equal [@parent_m], methods
+ end
+
+ def test_methods_matching_singleton
+ methods = []
+
+ @parent.methods_matching 'm', true do |m|
+ methods << m
+ end
+
+ assert_equal [@parent__m], methods
+ end
+
+ def test_methods_matching_inherit
+ methods = []
+
+ @child.methods_matching 'm' do |m|
+ methods << m
+ end
+
+ assert_equal [@parent_m], methods
+ end
+
+ def test_remove_invisible_private
+ util_visibilities
+
+ @vis.remove_invisible :private
+
+ assert_equal [@pub, @prot, @priv], @vis.method_list
+ assert_equal [@apub, @aprot, @apriv], @vis.attributes
+ end
+
+ def test_remove_invisible_nodoc
+ util_visibilities
+
+ @vis.remove_invisible :nodoc
+
+ assert_equal [@pub, @prot, @priv], @vis.method_list
+ assert_equal [@apub, @aprot, @apriv], @vis.attributes
+ end
+
+ def test_remove_invisible_protected
+ util_visibilities
+
+ @vis.remove_invisible :protected
+
+ assert_equal [@pub, @prot], @vis.method_list
+ assert_equal [@apub, @aprot], @vis.attributes
+ end
+
+ def test_remove_invisible_public
+ util_visibilities
+
+ @vis.remove_invisible :public
+
+ assert_equal [@pub], @vis.method_list
+ assert_equal [@apub], @vis.attributes
+ end
+
+ def test_remove_invisible_public_force
+ util_visibilities
+
+ @priv.force_documentation = true
+ @prot.force_documentation = true
+ @apriv.force_documentation = true
+ @aprot.force_documentation = true
+
+ @vis.remove_invisible :public
+
+ assert_equal [@pub, @prot, @priv], @vis.method_list
+ assert_equal [@apub, @aprot, @apriv], @vis.attributes
+ end
+
+ def test_remove_invisible_in_protected
+ util_visibilities
+
+ methods = [@pub, @prot, @priv]
+
+ @c1.remove_invisible_in methods, :protected
+
+ assert_equal [@pub, @prot], methods
+ end
+
+ def test_remove_invisible_in_protected_force
+ util_visibilities
+
+ @priv.force_documentation = true
+
+ methods = [@pub, @prot, @priv]
+
+ @c1.remove_invisible_in methods, :protected
+
+ assert_equal [@pub, @prot, @priv], methods
+ end
+
+ def test_remove_invisible_in_public
+ util_visibilities
+
+ methods = [@pub, @prot, @priv]
+
+ @c1.remove_invisible_in methods, :public
+
+ assert_equal [@pub], methods
+ end
+
+ def test_remove_invisible_in_public_force
+ util_visibilities
+
+ @prot.force_documentation = true
+ @priv.force_documentation = true
+
+ methods = [@pub, @prot, @priv]
+
+ @c1.remove_invisible_in methods, :public
+
+ assert_equal [@pub, @prot, @priv], methods
+ end
+
+ def test_section_contents
+ default = @context.sections.first
+ @context.add_method RDoc::AnyMethod.new(nil, 'm1')
+
+ b = @context.add_section 'B'
+ m = @context.add_method RDoc::AnyMethod.new(nil, 'm2')
+ m.section = b
+
+ assert_equal [default, b], @context.section_contents
+ end
+
+ def test_section_contents_no_default
+ @context = RDoc::Context.new
+ b = @context.add_section 'B'
+ m = @context.add_method RDoc::AnyMethod.new(nil, 'm')
+ m.section = b
+
+ assert_equal [b], @context.section_contents
+ end
+
+ def test_section_contents_only_default
+ @context = RDoc::Context.new
+
+ @context.add_method RDoc::AnyMethod.new(nil, 'm')
+
+ assert_empty @context.section_contents
+ end
+
+ def test_section_contents_unused
+ @context = RDoc::Context.new
+
+ @context.add_method RDoc::AnyMethod.new(nil, 'm')
+ @context.add_section 'B'
+
+ assert_empty @context.section_contents
+ end
+
+ def test_set_current_section
+ default_section = @context.sections.first
+
+ @context.set_current_section nil, RDoc::Comment.new('', @top_level)
+
+ assert_equal default_section, @context.current_section
+
+ @context.set_current_section 'other', RDoc::Comment.new('', @top_level)
+
+ new_section = @context.sections.find { |section|
+ section != default_section
+ }
+
+ assert_equal new_section, @context.current_section
+ end
+
+ def test_sort_sections
+ c = RDoc::Context.new
+ c.add_section 'C'
+ c.add_section 'A'
+ c.add_section 'B'
+
+ titles = c.sort_sections.map { |section| section.title }
+
+ assert_equal [nil, 'A', 'B', 'C'], titles
+ end
+
+ def test_sort_sections_tomdoc
+ c = RDoc::Context.new
+ c.add_section 'Public'
+ c.add_section 'Internal'
+ c.add_section 'Deprecated'
+
+ titles = c.sort_sections.map { |section| section.title }
+
+ assert_equal [nil, 'Public', 'Internal', 'Deprecated'], titles
+ end
+
+ def test_sort_sections_tomdoc_missing
+ c = RDoc::Context.new
+ c.add_section 'Internal'
+ c.add_section 'Public'
+
+ titles = c.sort_sections.map { |section| section.title }
+
+ assert_equal [nil, 'Public', 'Internal'], titles
+ end
+
+ def util_visibilities
+ @pub = RDoc::AnyMethod.new nil, 'pub'
+ @prot = RDoc::AnyMethod.new nil, 'prot'
+ @priv = RDoc::AnyMethod.new nil, 'priv'
+
+ @apub = RDoc::Attr.new nil, 'pub', 'RW', nil
+ @aprot = RDoc::Attr.new nil, 'prot', 'RW', nil
+ @apriv = RDoc::Attr.new nil, 'priv', 'RW', nil
+
+ @vis = RDoc::NormalClass.new 'Vis'
+ @vis.add_method @pub
+ @vis.add_method @prot
+ @vis.add_method @priv
+
+ @vis.add_attribute @apub
+ @vis.add_attribute @aprot
+ @vis.add_attribute @apriv
+
+ @prot.visibility = :protected
+ @priv.visibility = :private
+
+ @aprot.visibility = :protected
+ @apriv.visibility = :private
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_context_section.rb b/jni/ruby/test/rdoc/test_rdoc_context_section.rb
new file mode 100644
index 0000000..b8f8c7f
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_context_section.rb
@@ -0,0 +1,130 @@
+require 'rdoc/test_case'
+
+class TestRDocContextSection < RDoc::TestCase
+
+ def setup
+ super
+
+ @top_level = @store.add_file 'file.rb'
+
+ @klass = @top_level.add_class RDoc::NormalClass, 'Object'
+
+ @S = RDoc::Context::Section
+ @s = @S.new @klass, 'section', comment('# comment', @top_level)
+ end
+
+ def test_add_comment
+ file1 = @store.add_file 'file1.rb'
+
+ klass = file1.add_class RDoc::NormalClass, 'Klass'
+
+ c1 = RDoc::Comment.new "# :section: section\n", file1
+ c2 = RDoc::Comment.new "# hello\n", file1
+ c3 = RDoc::Comment.new "# world\n", file1
+
+ s = @S.new klass, 'section', c1
+
+ assert_empty s.comments
+
+ s.add_comment nil
+
+ assert_empty s.comments
+
+ s.add_comment c2
+
+ assert_equal [c2], s.comments
+
+ s.add_comment c3
+
+ assert_equal [c2, c3], s.comments
+ end
+
+ def test_aref
+ assert_equal 'section', @s.aref
+
+ assert_equal '5Buntitled-5D', @S.new(nil, nil, nil).aref
+
+ assert_equal 'one+two', @S.new(nil, 'one two', nil).aref
+ end
+
+ def test_extract_comment
+ assert_equal '', @s.extract_comment(comment('')).text
+ assert_equal '', @s.extract_comment(comment("# :section: b\n")).text
+ assert_equal '# c', @s.extract_comment(comment("# :section: b\n# c")).text
+ assert_equal '# c',
+ @s.extract_comment(comment("# a\n# :section: b\n# c")).text
+ end
+
+ def test_marshal_dump
+ loaded = Marshal.load Marshal.dump @s
+
+ expected = RDoc::Comment.new('comment', @top_level).parse
+ expected = doc(expected)
+
+ assert_equal 'section', loaded.title
+ assert_equal expected, loaded.comments
+ assert_nil loaded.parent, 'parent is set manually'
+ end
+
+ def test_marshal_dump_no_comment
+ s = @S.new @klass, 'section', comment('')
+
+ loaded = Marshal.load Marshal.dump s
+
+ assert_equal 'section', loaded.title
+ assert_empty loaded.comments
+ assert_nil loaded.parent, 'parent is set manually'
+ end
+
+ def test_marshal_load_version_0
+ loaded = Marshal.load "\x04\bU:\eRDoc::Context::Section" +
+ "[\bi\x00I\"\fsection\x06:\x06EFo" +
+ ":\eRDoc::Markup::Document\a:\v@parts" +
+ "[\x06o;\a\a;\b[\x06o" +
+ ":\x1CRDoc::Markup::Paragraph\x06;\b" +
+ "[\x06I\"\fcomment\x06;\x06F:\n@fileI" +
+ "\"\ffile.rb\x06;\x06F;\n0"
+
+ expected = doc RDoc::Comment.new('comment', @top_level).parse
+
+ assert_equal 'section', loaded.title
+ assert_equal expected, loaded.comments
+ assert_nil loaded.parent, 'parent is set manually'
+ end
+
+ def test_remove_comment_array
+ other = @store.add_file 'other.rb'
+
+ other_comment = comment('bogus', other)
+
+ @s.add_comment other_comment
+
+ @s.remove_comment comment('bogus', @top_level)
+
+ assert_equal [other_comment], @s.comments
+ end
+
+ def test_remove_comment_document
+ other = @store.add_file 'other.rb'
+
+ other_comment = comment('bogus', other)
+
+ @s.add_comment other_comment
+
+ loaded = Marshal.load Marshal.dump @s
+
+ loaded.remove_comment comment('bogus', @top_level)
+
+ assert_equal doc(other_comment.parse), loaded.comments
+ end
+
+ def test_sequence
+ _, err = verbose_capture_io do
+ assert_match(/\ASEC\d{5}\Z/, @s.sequence)
+ end
+
+ assert_equal "#{@S}#sequence is deprecated, use #aref\n", err
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_cross_reference.rb b/jni/ruby/test/rdoc/test_rdoc_cross_reference.rb
new file mode 100644
index 0000000..99fc224
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_cross_reference.rb
@@ -0,0 +1,192 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocCrossReference < XrefTestCase
+
+ def setup
+ super
+
+ @xref = RDoc::CrossReference.new @c1
+ end
+
+ def assert_ref expected, name
+ assert_equal expected, @xref.resolve(name, 'fail')
+ end
+
+ def refute_ref name
+ assert_equal name, @xref.resolve(name, name)
+ end
+
+ def test_METHOD_REGEXP_STR
+ re = /#{RDoc::CrossReference::METHOD_REGEXP_STR}/
+
+ %w'=== [] []= << >>'.each do |x|
+ re =~ x
+ assert_equal x, $&
+ end
+ end
+
+ def test_resolve_C2
+ @xref = RDoc::CrossReference.new @c2
+
+ refute_ref '#m'
+
+ assert_ref @c1__m, 'C1::m'
+ assert_ref @c2_c3, 'C2::C3'
+ assert_ref @c2_c3_m, 'C2::C3#m'
+ assert_ref @c2_c3_h1, 'C3::H1'
+ assert_ref @c4, 'C4'
+
+ assert_ref @c3_h2, 'C3::H2'
+ refute_ref 'H1'
+ end
+
+ def test_resolve_C2_C3
+ @xref = RDoc::CrossReference.new @c2_c3
+
+ assert_ref @c2_c3_m, '#m'
+
+ assert_ref @c2_c3, 'C3'
+ assert_ref @c2_c3_m, 'C3#m'
+
+ assert_ref @c2_c3_h1, 'H1'
+ assert_ref @c2_c3_h1, 'C3::H1'
+
+ assert_ref @c4, 'C4'
+
+ assert_ref @c3_h2, 'C3::H2'
+ end
+
+ def test_resolve_C3
+ @xref = RDoc::CrossReference.new @c3
+
+ assert_ref @c3, 'C3'
+
+ refute_ref '#m'
+ refute_ref 'C3#m'
+
+ assert_ref @c3_h1, 'H1'
+
+ assert_ref @c3_h1, 'C3::H1'
+ assert_ref @c3_h2, 'C3::H2'
+
+ assert_ref @c4, 'C4'
+ end
+
+ def test_resolve_C4
+ @xref = RDoc::CrossReference.new @c4
+
+ # C4 ref inside a C4 containing a C4 should resolve to the contained class
+ assert_ref @c4_c4, 'C4'
+ end
+
+ def test_resolve_C4_C4
+ @xref = RDoc::CrossReference.new @c4_c4
+
+ # A C4 reference inside a C4 class contained within a C4 class should
+ # resolve to the inner C4 class.
+ assert_ref @c4_c4, 'C4'
+ end
+
+ def test_resolve_class
+ assert_ref @c1, 'C1'
+ refute_ref 'H1'
+
+ assert_ref @c2, 'C2'
+ assert_ref @c2_c3, 'C2::C3'
+ assert_ref @c2_c3_h1, 'C2::C3::H1'
+
+ assert_ref @c3, '::C3'
+ assert_ref @c3_h1, '::C3::H1'
+
+ assert_ref @c4_c4, 'C4::C4'
+ end
+
+ def test_resolve_file
+ refute_ref 'xref_data.rb'
+ end
+
+ def test_resolve_method
+ assert_ref @c1__m, 'm'
+ assert_ref @c1_m, '#m'
+ assert_ref @c1__m, '::m'
+
+ assert_ref @c1_m, 'C1#m'
+ assert_ref @c1__m, 'C1.m'
+ assert_ref @c1__m, 'C1::m'
+
+ assert_ref @c1_m, 'C1#m'
+ assert_ref @c1_m, 'C1#m()'
+ assert_ref @c1_m, 'C1#m(*)'
+
+ assert_ref @c1__m, 'C1.m'
+ assert_ref @c1__m, 'C1.m()'
+ assert_ref @c1__m, 'C1.m(*)'
+
+ assert_ref @c1__m, 'C1::m'
+ assert_ref @c1__m, 'C1::m()'
+ assert_ref @c1__m, 'C1::m(*)'
+
+ assert_ref @c2_c3_m, 'C2::C3#m'
+
+ assert_ref @c2_c3_m, 'C2::C3.m'
+
+ # TODO stop escaping - HTML5 allows anything but space
+ assert_ref @c2_c3_h1_meh, 'C2::C3::H1#m?'
+
+ assert_ref @c2_c3_m, '::C2::C3#m'
+ assert_ref @c2_c3_m, '::C2::C3#m()'
+ assert_ref @c2_c3_m, '::C2::C3#m(*)'
+ end
+
+ def test_resolve_method_equals3
+ m = RDoc::AnyMethod.new '', '==='
+ @c1.add_method m
+
+ assert_ref m, '==='
+ end
+
+ def test_resolve_page
+ page = @store.add_file 'README.txt'
+ page.parser = RDoc::Parser::Simple
+
+ assert_ref page, 'README'
+ end
+
+ def test_resolve_percent
+ i_percent = RDoc::AnyMethod.new nil, '%'
+ i_percent.singleton = false
+ @c1.add_method i_percent
+
+ c_percent = RDoc::AnyMethod.new nil, '%'
+ c_percent.singleton = true
+ @c1.add_method c_percent
+
+ assert_ref i_percent, '%'
+ assert_ref i_percent, '#%'
+ assert_ref c_percent, '::%'
+
+ assert_ref i_percent, 'C1#%'
+ assert_ref c_percent, 'C1::%'
+ end
+
+ def test_resolve_no_ref
+ assert_equal '', @xref.resolve('', '')
+
+ assert_equal "bogus", @xref.resolve("bogus", "bogus")
+ assert_equal "\\bogus", @xref.resolve("\\bogus", "\\bogus")
+ assert_equal "\\\\bogus", @xref.resolve("\\\\bogus", "\\\\bogus")
+
+ assert_equal "\\#n", @xref.resolve("\\#n", "fail")
+ assert_equal "\\#n()", @xref.resolve("\\#n()", "fail")
+ assert_equal "\\#n(*)", @xref.resolve("\\#n(*)", "fail")
+
+ assert_equal "C1", @xref.resolve("\\C1", "fail")
+ assert_equal "::C3", @xref.resolve("\\::C3", "fail")
+
+ assert_equal "succeed", @xref.resolve("::C3::H1#n", "succeed")
+ assert_equal "succeed", @xref.resolve("::C3::H1#n(*)", "succeed")
+ assert_equal "\\::C3::H1#n", @xref.resolve("\\::C3::H1#n", "fail")
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_encoding.rb b/jni/ruby/test/rdoc/test_rdoc_encoding.rb
new file mode 100644
index 0000000..7ec39f8
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_encoding.rb
@@ -0,0 +1,227 @@
+# coding: US-ASCII
+
+require 'rdoc/test_case'
+
+class TestRDocEncoding < RDoc::TestCase
+
+ def setup
+ super
+
+ @tempfile = Tempfile.new 'test_rdoc_encoding'
+ end
+
+ def teardown
+ @tempfile.close!
+
+ super
+ end
+
+ def test_class_read_file
+ @tempfile.write "hi everybody"
+ @tempfile.flush
+
+ assert_equal "hi everybody", RDoc::Encoding.read_file(@tempfile.path, nil)
+ end
+
+ def test_class_read_file_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ expected = "# coding: utf-8\nhi everybody"
+
+ @tempfile.write expected
+ @tempfile.flush
+
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ assert_equal "hi everybody", contents
+ assert_equal Encoding::UTF_8, contents.encoding
+ end
+
+ def test_class_read_file_encoding_convert
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ content = ""
+ content.encode! 'ISO-8859-1'
+ content << "# coding: ISO-8859-1\nhi \xE9verybody"
+
+ @tempfile.write content
+ @tempfile.flush
+
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ assert_equal Encoding::UTF_8, contents.encoding
+ assert_equal "hi \u00e9verybody", contents.sub("\r", '')
+ end
+
+ def test_class_read_file_encoding_fail
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ @tempfile.write "# coding: utf-8\n\317\200" # pi
+ @tempfile.flush
+
+ contents = :junk
+
+ _, err = verbose_capture_io do
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::US_ASCII
+ end
+
+ assert_nil contents
+
+ assert_match %r%^unable to convert%, err
+ end
+
+ def test_class_read_file_encoding_fancy
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ expected = "# -*- coding: utf-8; fill-column: 74 -*-\nhi everybody"
+ expected.encode! Encoding::UTF_8
+
+ @tempfile.write expected
+ @tempfile.flush
+
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ assert_equal "hi everybody", contents
+ assert_equal Encoding::UTF_8, contents.encoding
+ end
+
+ def test_class_read_file_encoding_force_transcode
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ @tempfile.write "# coding: utf-8\n\317\200" # pi
+ @tempfile.flush
+
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::US_ASCII, true
+
+ assert_equal '?', contents
+ assert_equal Encoding::US_ASCII, contents.encoding
+ end
+
+ def test_class_read_file_encoding_guess
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ path = File.expand_path '../test.ja.txt', __FILE__
+ content = RDoc::Encoding.read_file path, Encoding::UTF_8
+
+ assert_equal Encoding::UTF_8, content.encoding
+ end
+
+ def test_class_read_file_encoding_invalid
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ @tempfile.write "# coding: ascii\nM\xE4r"
+ @tempfile.flush
+
+ contents = :junk
+ _, err = verbose_capture_io do
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ end
+
+ assert_equal "unable to convert \"\\xE4\" on US-ASCII for #{@tempfile.path}, skipping\n", err
+
+ assert_nil contents
+ end
+
+ def test_class_read_file_encoding_with_signature
+ skip "Encoding not implemented" unless defined? ::Encoding
+
+ @tempfile.write "\xEF\xBB\xBFhi everybody"
+ @tempfile.flush
+
+ bug3360 = '[ruby-dev:41452]'
+ content = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ assert_equal Encoding::UTF_8, content.encoding, bug3360
+ assert_equal "hi everybody", content, bug3360
+ end
+
+ def test_class_read_file_encoding_iso_2022_jp
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ input = "# coding: ISO-2022-JP\n:\e$B%3%^%s%I\e(B:"
+
+ @tempfile.write input
+ @tempfile.flush
+
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+
+ expected = ":\xe3\x82\xb3\xe3\x83\x9e\xe3\x83\xb3\xe3\x83\x89:"
+ expected.force_encoding Encoding::UTF_8
+
+ assert_equal expected, contents
+ assert_equal Encoding::UTF_8, contents.encoding
+ end
+
+ def test_class_set_encoding
+ s = "# coding: UTF-8\n"
+ RDoc::Encoding.set_encoding s
+
+ # sanity check for 1.8
+
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ assert_equal Encoding::UTF_8, s.encoding
+
+ s = "#!/bin/ruby\n# coding: UTF-8\n"
+ RDoc::Encoding.set_encoding s
+
+ assert_equal Encoding::UTF_8, s.encoding
+
+ s = "<?xml version='1.0' encoding='UTF-8'?>\n"
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal Encoding::UTF_8, s.encoding
+
+ s = "<?xml version='1.0' encoding=\"UTF-8\"?>\n"
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal Encoding::UTF_8, s.encoding
+ end
+
+ def test_class_set_encoding_strip
+ s = "# coding: UTF-8\n# more comments"
+
+ RDoc::Encoding.set_encoding s
+
+ assert_equal "# more comments", s
+
+ s = "#!/bin/ruby\n# coding: UTF-8\n# more comments"
+
+ RDoc::Encoding.set_encoding s
+
+ assert_equal "#!/bin/ruby\n# more comments", s
+ end
+
+ def test_class_set_encoding_bad
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ s = ""
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal expected, s.encoding
+
+ s = "# vim:set fileencoding=utf-8:\n"
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal expected, s.encoding
+
+ s = "# vim:set fileencoding=utf-8:\n"
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal expected, s.encoding
+
+ assert_raises ArgumentError do
+ RDoc::Encoding.set_encoding "# -*- encoding: undecided -*-\n"
+ end
+ end
+
+ def test_sanity
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ assert_equal Encoding::US_ASCII, ''.encoding,
+ 'If this file is not ASCII tests may incorrectly pass'
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_extend.rb b/jni/ruby/test/rdoc/test_rdoc_extend.rb
new file mode 100644
index 0000000..1499315
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_extend.rb
@@ -0,0 +1,94 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocExtend < XrefTestCase
+
+ def setup
+ super
+
+ @ext = RDoc::Extend.new 'M1', 'comment'
+ @ext.parent = @m1
+ @ext.store = @store
+ end
+
+ def test_module
+ assert_equal @m1, @ext.module
+ assert_equal 'Unknown', RDoc::Extend.new('Unknown', 'comment').module
+ end
+
+ def test_module_extended
+ m1 = @xref_data.add_module RDoc::NormalModule, 'Mod1'
+ m1.add_module RDoc::NormalModule, 'Mod3'
+ m1_m2 = m1.add_module RDoc::NormalModule, 'Mod2'
+ m1_m2_m3 = m1_m2.add_module RDoc::NormalModule, 'Mod3'
+ m1_m2_m3.add_module RDoc::NormalModule, 'Mod4'
+ m1_m2.add_module RDoc::NormalModule, 'Mod4'
+ m1_m2_k0 = m1_m2.add_class RDoc::NormalClass, 'Klass0'
+ m1_m2_k0_m4 = m1_m2_k0.add_module RDoc::NormalModule, 'Mod4'
+ m1_m2_k0_m4.add_module RDoc::NormalModule, 'Mod6'
+ m1_m2_k0.add_module RDoc::NormalModule, 'Mod5'
+
+ e0_m4 = RDoc::Extend.new 'Mod4', nil
+ e0_m5 = RDoc::Extend.new 'Mod5', nil
+ e0_m6 = RDoc::Extend.new 'Mod6', nil
+ e0_m1 = RDoc::Extend.new 'Mod1', nil
+ e0_m2 = RDoc::Extend.new 'Mod2', nil
+ e0_m3 = RDoc::Extend.new 'Mod3', nil
+
+ m1_m2_k0.add_extend e0_m4
+ m1_m2_k0.add_extend e0_m5
+ m1_m2_k0.add_extend e0_m6
+ m1_m2_k0.add_extend e0_m1
+ m1_m2_k0.add_extend e0_m2
+ m1_m2_k0.add_extend e0_m3
+
+ assert_equal [e0_m4, e0_m5, e0_m6, e0_m1, e0_m2, e0_m3], m1_m2_k0.extends
+ assert_equal ['Object'], m1_m2_k0.ancestors
+
+ m1_k1 = m1.add_class RDoc::NormalClass, 'Klass1'
+
+ e1_m1 = RDoc::Extend.new 'Mod1', nil
+ e1_m2 = RDoc::Extend.new 'Mod2', nil
+ e1_m3 = RDoc::Extend.new 'Mod3', nil
+ e1_m4 = RDoc::Extend.new 'Mod4', nil
+ e1_k0_m4 = RDoc::Extend.new 'Klass0::Mod4', nil
+
+ m1_k1.add_extend e1_m1
+ m1_k1.add_extend e1_m2
+ m1_k1.add_extend e1_m3
+ m1_k1.add_extend e1_m4
+ m1_k1.add_extend e1_k0_m4
+
+ assert_equal [e1_m1, e1_m2, e1_m3, e1_m4, e1_k0_m4], m1_k1.extends
+ assert_equal ['Object'], m1_k1.ancestors
+
+ m1_k2 = m1.add_class RDoc::NormalClass, 'Klass2'
+
+ e2_m1 = RDoc::Extend.new 'Mod1', nil
+ e2_m2 = RDoc::Extend.new 'Mod2', nil
+ e2_m3 = RDoc::Extend.new 'Mod3', nil
+ e2_k0_m4 = RDoc::Extend.new 'Klass0::Mod4', nil
+
+ m1_k2.add_extend e2_m1
+ m1_k2.add_extend e2_m3
+ m1_k2.add_extend e2_m2
+ m1_k2.add_extend e2_k0_m4
+
+ assert_equal [e2_m1, e2_m3, e2_m2, e2_k0_m4], m1_k2.extends
+ assert_equal ['Object'], m1_k2.ancestors
+
+ m1_k3 = m1.add_class RDoc::NormalClass, 'Klass3'
+
+ e3_m1 = RDoc::Extend.new 'Mod1', nil
+ e3_m2 = RDoc::Extend.new 'Mod2', nil
+ e3_m4 = RDoc::Extend.new 'Mod4', nil
+
+ m1_k3.add_extend e3_m1
+ m1_k3.add_extend e3_m2
+ m1_k3.add_extend e3_m4
+
+ assert_equal [e3_m1, e3_m2, e3_m4], m1_k3.extends
+ assert_equal ['Object'], m1_k3.ancestors
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_generator_darkfish.rb b/jni/ruby/test/rdoc/test_rdoc_generator_darkfish.rb
new file mode 100644
index 0000000..fc77e4b
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_generator_darkfish.rb
@@ -0,0 +1,229 @@
+require 'rdoc/test_case'
+
+class TestRDocGeneratorDarkfish < RDoc::TestCase
+
+ def setup
+ super
+
+ @lib_dir = "#{@pwd}/lib"
+ $LOAD_PATH.unshift @lib_dir # ensure we load from this RDoc
+
+ @options = RDoc::Options.new
+ @options.option_parser = OptionParser.new
+
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_generator_darkfish_#{$$}"
+ FileUtils.mkdir_p @tmpdir
+ Dir.chdir @tmpdir
+ @options.op_dir = @tmpdir
+ @options.generator = RDoc::Generator::Darkfish
+
+ $LOAD_PATH.each do |path|
+ darkfish_dir = File.join path, 'rdoc/generator/template/darkfish/'
+ next unless File.directory? darkfish_dir
+ @options.template_dir = darkfish_dir
+ break
+ end
+
+ @rdoc.options = @options
+
+ @g = @options.generator.new @store, @options
+ @rdoc.generator = @g
+
+ @top_level = @store.add_file 'file.rb'
+ @top_level.parser = RDoc::Parser::Ruby
+ @klass = @top_level.add_class RDoc::NormalClass, 'Klass'
+
+ @alias_constant = RDoc::Constant.new 'A', nil, ''
+ @alias_constant.record_location @top_level
+
+ @top_level.add_constant @alias_constant
+
+ @klass.add_module_alias @klass, 'A', @top_level
+
+ @meth = RDoc::AnyMethod.new nil, 'method'
+ @meth_bang = RDoc::AnyMethod.new nil, 'method!'
+ @attr = RDoc::Attr.new nil, 'attr', 'RW', ''
+
+ @klass.add_method @meth
+ @klass.add_method @meth_bang
+ @klass.add_attribute @attr
+
+ @ignored = @top_level.add_class RDoc::NormalClass, 'Ignored'
+ @ignored.ignore
+
+ @store.complete :private
+
+ @object = @store.find_class_or_module 'Object'
+ @klass_alias = @store.find_class_or_module 'Klass::A'
+ end
+
+ def teardown
+ super
+
+ $LOAD_PATH.shift
+ Dir.chdir @pwd
+ FileUtils.rm_rf @tmpdir
+ end
+
+ def test_generate
+ top_level = @store.add_file 'file.rb'
+ top_level.add_class @klass.class, @klass.name
+
+ @g.generate
+
+ assert_file 'index.html'
+ assert_file 'Object.html'
+ assert_file 'table_of_contents.html'
+ assert_file 'js/search_index.js'
+
+ assert_hard_link 'css/rdoc.css'
+ assert_hard_link 'css/fonts.css'
+
+ assert_hard_link 'fonts/SourceCodePro-Bold.ttf'
+ assert_hard_link 'fonts/SourceCodePro-Regular.ttf'
+
+ encoding = if Object.const_defined? :Encoding then
+ Regexp.escape Encoding::UTF_8.name
+ else
+ Regexp.escape 'UTF-8'
+ end
+
+ assert_match %r%<meta charset="#{encoding}">%, File.read('index.html')
+ assert_match %r%<meta charset="#{encoding}">%, File.read('Object.html')
+
+ refute_match(/Ignored/, File.read('index.html'))
+ end
+
+ def test_generate_dry_run
+ @g.dry_run = true
+ top_level = @store.add_file 'file.rb'
+ top_level.add_class @klass.class, @klass.name
+
+ @g.generate
+
+ refute_file 'index.html'
+ refute_file 'Object.html'
+ end
+
+ def test_generate_static
+ FileUtils.mkdir_p 'dir/images'
+ FileUtils.touch 'dir/images/image.png'
+ FileUtils.mkdir_p 'file'
+ FileUtils.touch 'file/file.txt'
+
+ @options.static_path = [
+ File.expand_path('dir'),
+ File.expand_path('file/file.txt'),
+ ]
+
+ @g.generate
+
+ assert_file 'images/image.png'
+ assert_file 'file.txt'
+ end
+
+ def test_generate_static_dry_run
+ FileUtils.mkdir 'static'
+ FileUtils.touch 'static/image.png'
+
+ @options.static_path = [File.expand_path('static')]
+ @g.dry_run = true
+
+ @g.generate
+
+ refute_file 'image.png'
+ end
+
+ def test_install_rdoc_static_file
+ src = Pathname(__FILE__)
+ dst = File.join @tmpdir, File.basename(src)
+ options = {}
+
+ @g.install_rdoc_static_file src, dst, options
+
+ assert_file dst
+
+ begin
+ assert_hard_link dst
+ rescue MiniTest::Assertion
+ return # hard links are not supported, no further tests needed
+ end
+
+ @g.install_rdoc_static_file src, dst, options
+
+ assert_hard_link dst
+ end
+
+ def test_install_rdoc_static_file_missing
+ src = Pathname(__FILE__) + 'nonexistent'
+ dst = File.join @tmpdir, File.basename(src)
+ options = {}
+
+ @g.install_rdoc_static_file src, dst, options
+
+ refute_file dst
+ end
+
+ def test_setup
+ @g.setup
+
+ assert_equal [@klass_alias, @ignored, @klass, @object],
+ @g.classes.sort_by { |klass| klass.full_name }
+ assert_equal [@top_level], @g.files
+ assert_equal [@meth, @meth, @meth_bang, @meth_bang], @g.methods
+ assert_equal [@klass_alias, @klass, @object], @g.modsort
+ end
+
+ def test_template_for
+ classpage = Pathname.new @options.template_dir + 'class.rhtml'
+
+ template = @g.send(:template_for, classpage, true, RDoc::ERBIO)
+ assert_kind_of RDoc::ERBIO, template
+
+ assert_same template, @g.send(:template_for, classpage)
+ end
+
+ def test_template_for_dry_run
+ classpage = Pathname.new @options.template_dir + 'class.rhtml'
+
+ template = @g.send(:template_for, classpage, true, ERB)
+ assert_kind_of ERB, template
+
+ assert_same template, @g.send(:template_for, classpage)
+ end
+
+ def test_template_for_partial
+ partial = Pathname.new @options.template_dir + '_sidebar_classes.rhtml'
+
+ template = @g.send(:template_for, partial, false, RDoc::ERBPartial)
+
+ assert_kind_of RDoc::ERBPartial, template
+
+ assert_same template, @g.send(:template_for, partial)
+ end
+
+ ##
+ # Asserts that +filename+ has a link count greater than 1 if hard links to
+ # @tmpdir are supported.
+
+ def assert_hard_link filename
+ assert_file filename
+
+ src = @g.template_dir + '_head.rhtml'
+ dst = File.join @tmpdir, 'hardlinktest'
+
+ begin
+ FileUtils.ln src, dst
+ nlink = File.stat(dst).nlink if File.identical? src, dst
+ FileUtils.rm dst
+ return if nlink == 1
+ rescue SystemCallError
+ return
+ end
+
+ assert_operator File.stat(filename).nlink, :>, 1,
+ "#{filename} is not hard-linked"
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_generator_json_index.rb b/jni/ruby/test/rdoc/test_rdoc_generator_json_index.rb
new file mode 100644
index 0000000..06a5ddc
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_generator_json_index.rb
@@ -0,0 +1,320 @@
+# coding: US-ASCII
+
+require 'rdoc/test_case'
+
+class TestRDocGeneratorJsonIndex < RDoc::TestCase
+
+ def setup
+ super
+
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_generator_darkfish_#{$$}"
+ FileUtils.mkdir_p @tmpdir
+
+ @options = RDoc::Options.new
+ @options.files = []
+ # JsonIndex is used in conjunction with another generator
+ @options.setup_generator 'darkfish'
+ @options.template_dir = ''
+ @options.op_dir = @tmpdir
+ @options.option_parser = OptionParser.new
+ @options.finish
+
+ @darkfish = RDoc::Generator::Darkfish.new @store, @options
+ @g = RDoc::Generator::JsonIndex.new @darkfish, @options
+
+ @rdoc.options = @options
+ @rdoc.generator = @g
+
+ @top_level = @store.add_file 'file.rb'
+ @top_level.parser = RDoc::Parser::Ruby
+
+ @klass = @top_level.add_class RDoc::NormalClass, 'C'
+
+ @meth = @klass.add_method RDoc::AnyMethod.new(nil, 'meth')
+ @meth.record_location @top_level
+
+ @nest_klass = @klass.add_class RDoc::NormalClass, 'D'
+ @nest_klass.record_location @top_level
+
+ @nest_meth = @nest_klass.add_method RDoc::AnyMethod.new(nil, 'meth')
+
+ @ignored = @top_level.add_class RDoc::NormalClass, 'Ignored'
+ @ignored.ignore
+
+ @page = @store.add_file 'page.rdoc'
+ @page.parser = RDoc::Parser::Simple
+
+ @top_levels = [@top_level, @page].sort
+ @klasses = [@klass, @nest_klass, @ignored]
+
+ Dir.chdir @tmpdir
+ end
+
+ def teardown
+ super
+
+ Dir.chdir @pwd
+ FileUtils.rm_rf @tmpdir
+ end
+
+ def test_build_index
+ index = @g.build_index
+
+ expected = {
+ :index => {
+ :searchIndex => %w[c d meth() meth() page],
+ :longSearchIndex => %w[c c::d c#meth() c::d#meth()],
+ :info => [
+ @klass.search_record[2..-1],
+ @nest_klass.search_record[2..-1],
+ @meth.search_record[2..-1],
+ @nest_meth.search_record[2..-1],
+ @page.search_record[2..-1],
+ ],
+ },
+ }
+
+ expected[:index][:longSearchIndex] << ''
+
+ assert_equal expected, index
+ end
+
+ def test_class_dir
+ assert_equal @darkfish.class_dir, @g.class_dir
+ end
+
+ def test_file_dir
+ assert_equal @darkfish.file_dir, @g.file_dir
+ end
+
+ def test_generate
+ @g.generate
+
+ assert_file 'js/searcher.js'
+ assert_file 'js/navigation.js'
+ assert_file 'js/search_index.js'
+
+ json = File.read 'js/search_index.js'
+
+ json =~ /\Avar search_data = /
+
+ assignment = $&
+ index = $'
+
+ refute_empty assignment
+
+ index = JSON.parse index
+
+ info = [
+ @klass.search_record[2..-1],
+ @nest_klass.search_record[2..-1],
+ @meth.search_record[2..-1],
+ @nest_meth.search_record[2..-1],
+ @page.search_record[2..-1],
+ ]
+
+ expected = {
+ 'index' => {
+ 'searchIndex' => [
+ 'c',
+ 'd',
+ 'meth()',
+ 'meth()',
+ 'page',
+ ],
+ 'longSearchIndex' => [
+ 'c',
+ 'c::d',
+ 'c#meth()',
+ 'c::d#meth()',
+ '',
+ ],
+ 'info' => info,
+ },
+ }
+
+ assert_equal expected, index
+ end
+
+ def test_generate_gzipped
+ require 'zlib'
+ @g.generate
+ @g.generate_gzipped
+
+ assert_file 'js/searcher.js'
+ assert_file 'js/searcher.js.gz'
+ assert_file 'js/navigation.js'
+ assert_file 'js/navigation.js.gz'
+ assert_file 'js/search_index.js'
+ assert_file 'js/search_index.js.gz'
+
+ json = File.open('js/search_index.js.gz') {|gzip|
+ Zlib::GzipReader.new(gzip).read
+ }
+
+ json =~ /\Avar search_data = /
+
+ assignment = $&
+ index = $'
+
+ refute_empty assignment
+
+ index = JSON.parse index
+
+ info = [
+ @klass.search_record[2..-1],
+ @nest_klass.search_record[2..-1],
+ @meth.search_record[2..-1],
+ @nest_meth.search_record[2..-1],
+ @page.search_record[2..-1],
+ ]
+
+ expected = {
+ 'index' => {
+ 'searchIndex' => [
+ 'c',
+ 'd',
+ 'meth()',
+ 'meth()',
+ 'page',
+ ],
+ 'longSearchIndex' => [
+ 'c',
+ 'c::d',
+ 'c#meth()',
+ 'c::d#meth()',
+ '',
+ ],
+ 'info' => info,
+ },
+ }
+
+ assert_equal expected, index
+ end
+
+ def test_generate_utf_8
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ text = "5\xB0"
+ text.force_encoding Encoding::ISO_8859_1
+ @klass.add_comment comment(text), @top_level
+
+ @g.generate
+
+ json = File.read 'js/search_index.js'
+ json.force_encoding Encoding::UTF_8
+
+ json =~ /\Avar search_data = /
+
+ index = $'
+
+ index = JSON.parse index
+
+ klass_record = @klass.search_record[2..-1]
+ klass_record[-1] = "<p>5\xc2\xb0\n"
+ klass_record.last.force_encoding Encoding::UTF_8
+
+ info = [
+ klass_record,
+ @nest_klass.search_record[2..-1],
+ @meth.search_record[2..-1],
+ @nest_meth.search_record[2..-1],
+ @page.search_record[2..-1],
+ ]
+
+ expected = {
+ 'index' => {
+ 'searchIndex' => [
+ 'c',
+ 'd',
+ 'meth()',
+ 'meth()',
+ 'page',
+ ],
+ 'longSearchIndex' => [
+ 'c',
+ 'c::d',
+ 'c#meth()',
+ 'c::d#meth()',
+ '',
+ ],
+ 'info' => info,
+ },
+ }
+
+ assert_equal expected, index
+ end
+
+ def test_index_classes
+ @g.reset @top_levels, @klasses
+
+ @g.index_classes
+
+ expected = {
+ :searchIndex => %w[c d],
+ :longSearchIndex => %w[c c::d],
+ :info => [
+ @klass.search_record[2..-1],
+ @nest_klass.search_record[2..-1],
+ ],
+ }
+
+ assert_equal expected, @g.index
+ end
+
+ def test_index_classes_nodoc
+ @klass.document_self = false
+ @nest_klass.document_self = false
+ @meth.document_self = false
+ @nest_meth.document_self = false
+
+ @g.reset @top_levels, @klasses
+
+ @g.index_classes
+
+ expected = {
+ :searchIndex => [],
+ :longSearchIndex => [],
+ :info => [],
+ }
+
+ assert_equal expected, @g.index
+ end
+
+ def test_index_methods
+ @g.reset @top_levels, @klasses
+
+ @g.index_methods
+
+ expected = {
+ :searchIndex => %w[meth() meth()],
+ :longSearchIndex => %w[c#meth() c::d#meth()],
+ :info => [
+ @meth.search_record[2..-1],
+ @nest_meth.search_record[2..-1],
+ ],
+ }
+
+ assert_equal expected, @g.index
+ end
+
+ def test_index_pages
+ @g.reset @top_levels, @klasses
+
+ @g.index_pages
+
+ expected = {
+ :searchIndex => %w[page],
+ :longSearchIndex => [''],
+ :info => [@page.search_record[2..-1]],
+ }
+
+ assert_equal expected, @g.index
+ end
+
+ def test_search_string
+ assert_equal 'cd', @g.search_string('C d')
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_generator_markup.rb b/jni/ruby/test/rdoc/test_rdoc_generator_markup.rb
new file mode 100644
index 0000000..5f8a45b
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_generator_markup.rb
@@ -0,0 +1,59 @@
+require 'rdoc/test_case'
+
+class TestRDocGeneratorMarkup < RDoc::TestCase
+
+ include RDoc::Text
+ include RDoc::Generator::Markup
+
+ attr_reader :store
+
+ def setup
+ super
+
+ @options = RDoc::Options.new
+ @rdoc.options = @options
+
+ @parent = self
+ @path = '/index.html'
+ @symbols = {}
+ end
+
+ def test_aref_to
+ assert_equal 'Foo/Bar.html', aref_to('Foo/Bar.html')
+ end
+
+ def test_as_href
+ assert_equal '../index.html', as_href('Foo/Bar.html')
+ end
+
+ def test_cvs_url
+ assert_equal 'http://example/this_page',
+ cvs_url('http://example/', 'this_page')
+
+ assert_equal 'http://example/?page=this_page&foo=bar',
+ cvs_url('http://example/?page=%s&foo=bar', 'this_page')
+ end
+
+ def test_description
+ @comment = '= Hello'
+
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h1 id=\"label-Hello\">Hello#{links}</h1>\n", description
+ end
+
+ def test_formatter
+ assert_kind_of RDoc::Markup::ToHtmlCrossref, formatter
+ refute formatter.show_hash
+ assert_same self, formatter.context
+ end
+
+ attr_reader :path
+
+ def find_symbol name
+ @symbols[name]
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_generator_pot.rb b/jni/ruby/test/rdoc/test_rdoc_generator_pot.rb
new file mode 100644
index 0000000..d028ce7
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_generator_pot.rb
@@ -0,0 +1,91 @@
+require 'rdoc/test_case'
+
+class TestRDocGeneratorPOT < RDoc::TestCase
+
+ def setup
+ super
+
+ @options = RDoc::Options.new
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_generator_pot_#{$$}"
+ FileUtils.mkdir_p @tmpdir
+
+ @generator = RDoc::Generator::POT.new @store, @options
+
+ @top_level = @store.add_file 'file.rb'
+ @klass = @top_level.add_class RDoc::NormalClass, 'Object'
+ @klass.add_comment 'This is a class', @top_level
+ @klass.add_section 'This is a section', comment('This is a section comment')
+
+ @const = RDoc::Constant.new "CONSTANT", "29", "This is a constant"
+
+ @meth = RDoc::AnyMethod.new nil, 'method'
+ @meth.record_location @top_level
+ @meth.comment = 'This is a method'
+
+ @attr = RDoc::Attr.new nil, 'attr', 'RW', ''
+ @attr.record_location @top_level
+ @attr.comment = 'This is an attribute'
+
+ @klass.add_constant @const
+ @klass.add_method @meth
+ @klass.add_attribute @attr
+
+ Dir.chdir @tmpdir
+ end
+
+ def teardown
+ super
+
+ Dir.chdir @pwd
+ FileUtils.rm_rf @tmpdir
+ end
+
+ def test_generate
+ @generator.generate
+
+ assert_equal <<-POT, File.read(File.join(@tmpdir, 'rdoc.pot'))
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSEION\\n"
+"Report-Msgid-Bugs-To:\\n"
+"PO-Revision-Date: YEAR-MO_DA HO:MI+ZONE\\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
+"Language-Team: LANGUAGE <LL@li.org>\\n"
+"Language:\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=CHARSET\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n"
+
+#. Object
+msgid "This is a class"
+msgstr ""
+
+#. Object::CONSTANT
+msgid "This is a constant"
+msgstr ""
+
+#. Object#method
+msgid "This is a method"
+msgstr ""
+
+#. Object: section title
+msgid "This is a section"
+msgstr ""
+
+#. Object: This is a section
+msgid "This is a section comment"
+msgstr ""
+
+#. Object#attr
+msgid "This is an attribute"
+msgstr ""
+ POT
+ end
+
+end
diff --git a/jni/ruby/test/rdoc/test_rdoc_generator_pot_po.rb b/jni/ruby/test/rdoc/test_rdoc_generator_pot_po.rb
new file mode 100644
index 0000000..fae7f7e
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_generator_pot_po.rb
@@ -0,0 +1,51 @@
+require 'rdoc/test_case'
+
+class TestRDocGeneratorPOTPO < RDoc::TestCase
+
+ def setup
+ super
+ @po = RDoc::Generator::POT::PO.new
+ end
+
+ def test_empty
+ assert_equal header, @po.to_s
+ end
+
+ def test_have_entry
+ @po.add(entry("Hello", {}))
+ assert_equal <<-PO, @po.to_s
+#{header}
+msgid "Hello"
+msgstr ""
+ PO
+ end
+
+ private
+
+ def entry(msgid, options)
+ RDoc::Generator::POT::POEntry.new(msgid, options)
+ end
+
+ def header
+ <<-'HEADER'
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSEION\n"
+"Report-Msgid-Bugs-To:\n"
+"PO-Revision-Date: YEAR-MO_DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language:\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+ HEADER
+ end
+
+end
diff --git a/jni/ruby/test/rdoc/test_rdoc_generator_pot_po_entry.rb b/jni/ruby/test/rdoc/test_rdoc_generator_pot_po_entry.rb
new file mode 100644
index 0000000..8620d98
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_generator_pot_po_entry.rb
@@ -0,0 +1,139 @@
+require 'rdoc/test_case'
+
+class TestRDocGeneratorPOTPOEntry < RDoc::TestCase
+
+ def test_msgid_normal
+ assert_equal <<-'ENTRY', entry("Hello", {}).to_s
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_msgid_multiple_lines
+ assert_equal <<-'ENTRY', entry("Hello\nWorld", {}).to_s
+msgid ""
+"Hello\n"
+"World"
+msgstr ""
+ ENTRY
+ end
+
+ def test_msgid_tab
+ assert_equal <<-'ENTRY', entry("Hello\tWorld", {}).to_s
+msgid "Hello\tWorld"
+msgstr ""
+ ENTRY
+ end
+
+ def test_msgid_back_slash
+ assert_equal <<-'ENTRY', entry("Hello \\ World", {}).to_s
+msgid "Hello \\ World"
+msgstr ""
+ ENTRY
+ end
+
+ def test_msgid_double_quote
+ assert_equal <<-'ENTRY', entry("Hello \"World\"!", {}).to_s
+msgid "Hello \"World\"!"
+msgstr ""
+ ENTRY
+ end
+
+ def test_translator_comment_normal
+ options = {:translator_comment => "Greeting"}
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+# Greeting
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_translator_comment_multiple_lines
+ options = {:translator_comment => "Greeting\nfor morning"}
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+# Greeting
+# for morning
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_extracted_comment_normal
+ options = {:extracted_comment => "Object"}
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+#. Object
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_extracted_comment_multiple_lines
+ options = {:extracted_comment => "Object\nMorning#greeting"}
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+#. Object
+#. Morning#greeting
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_references_normal
+ options = {:references => [["lib/rdoc.rb", 29]]}
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+#: lib/rdoc.rb:29
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_references_multiple
+ options = {:references => [["lib/rdoc.rb", 29], ["lib/rdoc/i18n.rb", 9]]}
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+#: lib/rdoc.rb:29
+#: lib/rdoc/i18n.rb:9
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_flags_normal
+ options = {:flags => ["fuzzy"]}
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+#, fuzzy
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_flags_multiple
+ options = {:flags => ["fuzzy", "ruby-format"]}
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+#, fuzzy,ruby-format
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ def test_full
+ options = {
+ :translator_comment => "Greeting",
+ :extracted_comment => "Morning#greeting",
+ :references => [["lib/rdoc.rb", 29]],
+ :flags => ["fuzzy"],
+ }
+ assert_equal <<-'ENTRY', entry("Hello", options).to_s
+# Greeting
+#. Morning#greeting
+#: lib/rdoc.rb:29
+#, fuzzy
+msgid "Hello"
+msgstr ""
+ ENTRY
+ end
+
+ private
+ def entry(msgid, options)
+ RDoc::Generator::POT::POEntry.new(msgid, options)
+ end
+
+end
diff --git a/jni/ruby/test/rdoc/test_rdoc_generator_ri.rb b/jni/ruby/test/rdoc/test_rdoc_generator_ri.rb
new file mode 100644
index 0000000..f8ac973
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_generator_ri.rb
@@ -0,0 +1,78 @@
+require 'rdoc/test_case'
+
+class TestRDocGeneratorRI < RDoc::TestCase
+
+ def setup
+ super
+
+ @options = RDoc::Options.new
+ if Object.const_defined? :Encoding then
+ @options.encoding = Encoding::UTF_8
+ @store.encoding = Encoding::UTF_8
+ end
+
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_generator_ri_#{$$}"
+ FileUtils.mkdir_p @tmpdir
+
+ @g = RDoc::Generator::RI.new @store, @options
+
+ @top_level = @store.add_file 'file.rb'
+ @klass = @top_level.add_class RDoc::NormalClass, 'Object'
+
+ @meth = RDoc::AnyMethod.new nil, 'method'
+ @meth.record_location @top_level
+
+ @meth_bang = RDoc::AnyMethod.new nil, 'method!'
+ @meth_bang.record_location @top_level
+
+ @attr = RDoc::Attr.new nil, 'attr', 'RW', ''
+ @attr.record_location @top_level
+
+ @klass.add_method @meth
+ @klass.add_method @meth_bang
+ @klass.add_attribute @attr
+
+ Dir.chdir @tmpdir
+ end
+
+ def teardown
+ super
+
+ Dir.chdir @pwd
+ FileUtils.rm_rf @tmpdir
+ end
+
+ def test_generate
+ @g.generate
+
+ assert_file File.join(@tmpdir, 'cache.ri')
+
+ assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
+
+ assert_file File.join(@tmpdir, 'Object', 'attr-i.ri')
+ assert_file File.join(@tmpdir, 'Object', 'method-i.ri')
+ assert_file File.join(@tmpdir, 'Object', 'method%21-i.ri')
+
+ store = RDoc::RI::Store.new @tmpdir
+ store.load_cache
+
+ encoding = Object.const_defined?(:Encoding) ? Encoding::UTF_8 : nil
+
+ assert_equal encoding, store.encoding
+ end
+
+ def test_generate_dry_run
+ @store.dry_run = true
+ @g = RDoc::Generator::RI.new @store, @options
+
+ top_level = @store.add_file 'file.rb'
+ top_level.add_class @klass.class, @klass.name
+
+ @g.generate
+
+ refute_file File.join(@tmpdir, 'cache.ri')
+ refute_file File.join(@tmpdir, 'Object')
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_i18n_locale.rb b/jni/ruby/test/rdoc/test_rdoc_i18n_locale.rb
new file mode 100644
index 0000000..69de88a
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_i18n_locale.rb
@@ -0,0 +1,73 @@
+require 'rdoc/test_case'
+
+class TestRDocI18nLocale < RDoc::TestCase
+
+ def setup
+ super
+ @locale = locale('fr')
+
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_i18n_locale_#{$$}"
+ FileUtils.mkdir_p @tmpdir
+
+ @locale_dir = @tmpdir
+ end
+
+ def teardown
+ FileUtils.rm_rf @tmpdir
+ super
+ end
+
+ def test_name
+ assert_equal 'fr', locale('fr').name
+ end
+
+ def test_load_nonexistent_po
+ File.stub(:exist?, false) do
+ refute @locale.load('nonexsitent-locale')
+ end
+ end
+
+ def test_load_existent_po
+ begin
+ require 'gettext/po_parser'
+ rescue LoadError
+ skip 'gettext gem is not found'
+ end
+
+ fr_locale_dir = File.join @locale_dir, 'fr'
+ FileUtils.mkdir_p fr_locale_dir
+ File.open File.join(fr_locale_dir, 'rdoc.po'), 'w' do |po|
+ po.puts <<-PO
+msgid ""
+msgstr ""
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Hello"
+msgstr "Bonjour"
+ PO
+ end
+
+ assert @locale.load(@locale_dir)
+ assert_equal 'Bonjour', @locale.translate('Hello')
+ end
+
+ def test_translate_existent_message
+ messages = @locale.instance_variable_get(:@messages)
+ messages['Hello'] = 'Bonjour'
+ assert_equal 'Bonjour', @locale.translate('Hello')
+ end
+
+ def test_translate_nonexistent_message
+ assert_equal 'Hello', @locale.translate('Hello')
+ end
+
+ private
+
+ def locale(name)
+ RDoc::I18n::Locale.new(name)
+ end
+
+end
diff --git a/jni/ruby/test/rdoc/test_rdoc_i18n_text.rb b/jni/ruby/test/rdoc/test_rdoc_i18n_text.rb
new file mode 100644
index 0000000..c47f03c
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_i18n_text.rb
@@ -0,0 +1,123 @@
+require 'rdoc/test_case'
+
+class TestRDocI18nText < RDoc::TestCase
+
+ def test_multiple_paragraphs
+ paragraph1 = <<-PARAGRAPH.strip
+RDoc produces HTML and command-line documentation for Ruby projects. RDoc
+includes the +rdoc+ and +ri+ tools for generating and displaying documentation
+from the command-line.
+ PARAGRAPH
+
+ paragraph2 = <<-PARAGRAPH.strip
+This command generates documentation for all the Ruby and C source
+files in and below the current directory. These will be stored in a
+documentation tree starting in the subdirectory +doc+.
+ PARAGRAPH
+
+ raw = <<-RAW
+#{paragraph1}
+
+#{paragraph2}
+ RAW
+
+ expected = [
+ {
+ :type => :paragraph,
+ :paragraph => paragraph1,
+ :line_no => 1,
+ },
+ {
+ :type => :paragraph,
+ :paragraph => paragraph2,
+ :line_no => 5,
+ },
+ ]
+ assert_equal expected, extract_messages(raw)
+ end
+
+ def test_translate_multiple_paragraphs
+ paragraph1 = <<-PARAGRAPH.strip
+Paragraph 1.
+ PARAGRAPH
+ paragraph2 = <<-PARAGRAPH.strip
+Paragraph 2.
+ PARAGRAPH
+
+ raw = <<-RAW
+#{paragraph1}
+
+#{paragraph2}
+ RAW
+
+ expected = <<-TRANSLATED
+Paragraphe 1.
+
+Paragraphe 2.
+ TRANSLATED
+ assert_equal expected, translate(raw)
+ end
+
+ def test_translate_not_transalted_message
+ nonexistent_paragraph = <<-PARAGRAPH.strip
+Nonexistent paragraph.
+ PARAGRAPH
+
+ raw = <<-RAW
+#{nonexistent_paragraph}
+ RAW
+
+ expected = <<-TRANSLATED
+#{nonexistent_paragraph}
+ TRANSLATED
+ assert_equal expected, translate(raw)
+ end
+
+ def test_translate_keep_empty_lines
+ raw = <<-RAW
+Paragraph 1.
+
+
+
+
+Paragraph 2.
+ RAW
+
+ expected = <<-TRANSLATED
+Paragraphe 1.
+
+
+
+
+Paragraphe 2.
+ TRANSLATED
+ assert_equal expected, translate(raw)
+ end
+
+ private
+
+ def extract_messages(raw)
+ text = RDoc::I18n::Text.new(raw)
+ messages = []
+ text.extract_messages do |message|
+ messages << message
+ end
+ messages
+ end
+
+ def locale
+ locale = RDoc::I18n::Locale.new('fr')
+ messages = locale.instance_variable_get(:@messages)
+ messages['markdown'] = 'markdown (markdown in fr)'
+ messages['Hello'] = 'Bonjour (Hello in fr)'
+ messages['Paragraph 1.'] = 'Paragraphe 1.'
+ messages['Paragraph 2.'] = 'Paragraphe 2.'
+ locale
+ end
+
+ def translate(raw)
+ text = RDoc::I18n::Text.new(raw)
+ text.translate(locale)
+ end
+
+end
diff --git a/jni/ruby/test/rdoc/test_rdoc_include.rb b/jni/ruby/test/rdoc/test_rdoc_include.rb
new file mode 100644
index 0000000..464a698
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_include.rb
@@ -0,0 +1,108 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocInclude < XrefTestCase
+
+ def setup
+ super
+
+ @inc = RDoc::Include.new 'M1', 'comment'
+ @inc.parent = @m1
+ @inc.record_location @top_level
+ @inc.store = @store
+ end
+
+ def test_module
+ assert_equal @m1, @inc.module
+ assert_equal 'Unknown', RDoc::Include.new('Unknown', 'comment').module
+ end
+
+ def test_module_extended
+ m1 = @xref_data.add_module RDoc::NormalModule, 'Mod1'
+ m1_m3 = m1.add_module RDoc::NormalModule, 'Mod3'
+ m1_m2 = m1.add_module RDoc::NormalModule, 'Mod2'
+ m1_m2_m3 = m1_m2.add_module RDoc::NormalModule, 'Mod3'
+ m1_m2_m3_m4 = m1_m2_m3.add_module RDoc::NormalModule, 'Mod4'
+ m1_m2_m4 = m1_m2.add_module RDoc::NormalModule, 'Mod4'
+ m1_m2_k0 = m1_m2.add_class RDoc::NormalClass, 'Klass0'
+ m1_m2_k0_m4 = m1_m2_k0.add_module RDoc::NormalModule, 'Mod4'
+ #m1_m2_k0_m4_m5 = m1_m2_k0_m4.add_module RDoc::NormalModule, 'Mod5'
+ m1_m2_k0_m4_m6 = m1_m2_k0_m4.add_module RDoc::NormalModule, 'Mod6'
+ m1_m2_k0_m5 = m1_m2_k0.add_module RDoc::NormalModule, 'Mod5'
+
+ i0_m4 = RDoc::Include.new 'Mod4', nil
+ i0_m5 = RDoc::Include.new 'Mod5', nil
+ i0_m6 = RDoc::Include.new 'Mod6', nil
+ i0_m1 = RDoc::Include.new 'Mod1', nil
+ i0_m2 = RDoc::Include.new 'Mod2', nil
+ i0_m3 = RDoc::Include.new 'Mod3', nil
+
+ m1_m2_k0.add_include i0_m4
+ m1_m2_k0.add_include i0_m5
+ m1_m2_k0.add_include i0_m6
+ m1_m2_k0.add_include i0_m1
+ m1_m2_k0.add_include i0_m2
+ m1_m2_k0.add_include i0_m3
+
+ assert_equal [i0_m4, i0_m5, i0_m6, i0_m1, i0_m2, i0_m3], m1_m2_k0.includes
+ assert_equal [m1_m2_m3, m1_m2, m1, m1_m2_k0_m4_m6, m1_m2_k0_m5,
+ m1_m2_k0_m4, 'Object'], m1_m2_k0.ancestors
+
+ m1_k1 = m1.add_class RDoc::NormalClass, 'Klass1'
+
+ i1_m1 = RDoc::Include.new 'Mod1', nil
+ i1_m2 = RDoc::Include.new 'Mod2', nil
+ i1_m3 = RDoc::Include.new 'Mod3', nil
+ i1_m4 = RDoc::Include.new 'Mod4', nil
+ i1_k0_m4 = RDoc::Include.new 'Klass0::Mod4', nil
+
+ m1_k1.add_include i1_m1
+ m1_k1.add_include i1_m2
+ m1_k1.add_include i1_m3
+ m1_k1.add_include i1_m4
+ m1_k1.add_include i1_k0_m4
+
+ assert_equal [i1_m1, i1_m2, i1_m3, i1_m4, i1_k0_m4], m1_k1.includes
+ assert_equal [m1_m2_k0_m4, m1_m2_m3_m4, m1_m2_m3, m1_m2, m1, 'Object'],
+ m1_k1.ancestors
+
+ m1_k2 = m1.add_class RDoc::NormalClass, 'Klass2'
+
+ i2_m1 = RDoc::Include.new 'Mod1', nil
+ i2_m2 = RDoc::Include.new 'Mod2', nil
+ i2_m3 = RDoc::Include.new 'Mod3', nil
+ i2_k0_m4 = RDoc::Include.new 'Klass0::Mod4', nil
+
+ m1_k2.add_include i2_m1
+ m1_k2.add_include i2_m3
+ m1_k2.add_include i2_m2
+ m1_k2.add_include i2_k0_m4
+
+ assert_equal [i2_m1, i2_m3, i2_m2, i2_k0_m4], m1_k2.includes
+ assert_equal [m1_m2_k0_m4, m1_m2, m1_m3, m1, 'Object'], m1_k2.ancestors
+
+ m1_k3 = m1.add_class RDoc::NormalClass, 'Klass3'
+
+ i3_m1 = RDoc::Include.new 'Mod1', nil
+ i3_m2 = RDoc::Include.new 'Mod2', nil
+ i3_m4 = RDoc::Include.new 'Mod4', nil
+
+ m1_k3.add_include i3_m1
+ m1_k3.add_include i3_m2
+ m1_k3.add_include i3_m4
+
+ assert_equal [i3_m1, i3_m2, i3_m4], m1_k3.includes
+ assert_equal [m1_m2_m4, m1_m2, m1, 'Object'], m1_k3.ancestors
+ end
+
+ def test_store_equals
+ incl = RDoc::Include.new 'M', nil
+ incl.record_location RDoc::TopLevel.new @top_level.name
+
+ incl.store = @store
+
+ assert_same @top_level, incl.file
+ assert_same @store, incl.file.store
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markdown.rb b/jni/ruby/test/rdoc/test_rdoc_markdown.rb
new file mode 100644
index 0000000..ea5dc73
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markdown.rb
@@ -0,0 +1,980 @@
+# coding: UTF-8
+
+require 'rdoc/test_case'
+require 'rdoc/markup/block_quote'
+require 'rdoc/markdown'
+
+class TestRDocMarkdown < RDoc::TestCase
+
+ def setup
+ @RM = RDoc::Markup
+
+ @parser = RDoc::Markdown.new
+
+ @to_html = RDoc::Markup::ToHtml.new(RDoc::Options.new, nil)
+ end
+
+ def test_class_parse
+ doc = RDoc::Markdown.parse "hello\n\nworld"
+
+ expected = doc(para("hello"), para("world"))
+
+ assert_equal expected, doc
+ end
+
+ def test_emphasis
+ assert_equal '_word_', @parser.emphasis('word')
+ assert_equal '<em>two words</em>', @parser.emphasis('two words')
+ assert_equal '<em>*bold*</em>', @parser.emphasis('*bold*')
+ end
+
+ def test_parse_auto_link_email
+ doc = parse "Autolink: <nobody-0+_./!%~$@example>"
+
+ expected = doc(para("Autolink: mailto:nobody-0+_./!%~$@example"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_auto_link_url
+ doc = parse "Autolink: <http://example>"
+
+ expected = doc(para("Autolink: http://example"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_block_quote
+ doc = parse <<-BLOCK_QUOTE
+> this is
+> a block quote
+ BLOCK_QUOTE
+
+ expected =
+ doc(
+ block(
+ para("this is\na block quote")))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_block_quote_continue
+ doc = parse <<-BLOCK_QUOTE
+> this is
+a block quote
+ BLOCK_QUOTE
+
+ expected =
+ doc(
+ block(
+ para("this is\na block quote")))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_block_quote_list
+ doc = parse <<-BLOCK_QUOTE
+> text
+>
+> * one
+> * two
+ BLOCK_QUOTE
+
+ expected =
+ doc(
+ block(
+ para("text"),
+ list(:BULLET,
+ item(nil, para("one")),
+ item(nil, para("two")))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_block_quote_newline
+ doc = parse <<-BLOCK_QUOTE
+> this is
+a block quote
+
+ BLOCK_QUOTE
+
+ expected =
+ doc(
+ block(
+ para("this is\na block quote")))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_block_quote_separate
+ doc = parse <<-BLOCK_QUOTE
+> this is
+a block quote
+
+> that continues
+ BLOCK_QUOTE
+
+ expected =
+ doc(
+ block(
+ para("this is\na block quote"),
+ para("that continues")))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_char_entity
+ doc = parse '&pi; &nn;'
+
+ expected = doc(para('π &nn;'))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_code
+ doc = parse "Code: `text`"
+
+ expected = doc(para("Code: <code>text</code>"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_code_github
+ doc = parse <<-MD
+Example:
+
+```
+code goes here
+```
+ MD
+
+ expected =
+ doc(
+ para("Example:"),
+ verb("code goes here\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_code_github_format
+ doc = parse <<-MD
+Example:
+
+``` ruby
+code goes here
+```
+ MD
+
+ code = verb("code goes here\n")
+ code.format = :ruby
+
+ expected =
+ doc(
+ para("Example:"),
+ code)
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_definition_list
+ doc = parse <<-MD
+one
+: This is a definition
+
+two
+: This is another definition
+ MD
+
+ expected = doc(
+ list(:NOTE,
+ item(%w[one], para("This is a definition")),
+ item(%w[two], para("This is another definition"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_definition_list_indents
+ doc = parse <<-MD
+zero
+: Indented zero characters
+
+one
+ : Indented one characters
+
+two
+ : Indented two characters
+
+three
+ : Indented three characters
+
+four
+ : Indented four characters
+
+ MD
+
+ expected = doc(
+ list(:NOTE,
+ item(%w[zero], para("Indented zero characters")),
+ item(%w[one], para("Indented one characters")),
+ item(%w[two], para("Indented two characters")),
+ item(%w[three], para("Indented three characters"))),
+ para("four\n : Indented four characters"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_definition_list_multi_description
+ doc = parse <<-MD
+label
+: This is a definition
+
+: This is another definition
+ MD
+
+ expected = doc(
+ list(:NOTE,
+ item(%w[label], para("This is a definition")),
+ item(nil, para("This is another definition"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_definition_list_multi_label
+ doc = parse <<-MD
+one
+two
+: This is a definition
+ MD
+
+ expected = doc(
+ list(:NOTE,
+ item(%w[one two], para("This is a definition"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_definition_list_multi_line
+ doc = parse <<-MD
+one
+: This is a definition
+that extends to two lines
+
+two
+: This is another definition
+that also extends to two lines
+ MD
+
+ expected = doc(
+ list(:NOTE,
+ item(%w[one],
+ para("This is a definition\nthat extends to two lines")),
+ item(%w[two],
+ para("This is another definition\nthat also extends to two lines"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_definition_list_no
+ @parser.definition_lists = false
+
+ doc = parse <<-MD
+one
+: This is a definition
+
+two
+: This is another definition
+ MD
+
+ expected = doc(
+ para("one\n: This is a definition"),
+ para("two\n: This is another definition"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_entity_dec
+ doc = parse "Entity: &#65;"
+
+ expected = doc(para("Entity: A"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_entity_hex
+ doc = parse "Entity: &#x41;"
+
+ expected = doc(para("Entity: A"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_entity_named
+ doc = parse "Entity: &pi;"
+
+ expected = doc(para("Entity: π"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_emphasis_star
+ doc = parse "it *works*\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it _works_"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_emphasis_underscore
+ doc = parse "it _works_\n"
+
+ expected =
+ doc(
+ para("it _works_"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_emphasis_underscore_embedded
+ doc = parse "foo_bar bar_baz\n"
+
+ expected =
+ doc(
+ para("foo_bar bar_baz"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_emphasis_underscore_in_word
+ doc = parse "it foo_bar_baz\n"
+
+ expected =
+ doc(
+ para("it foo_bar_baz"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_escape
+ assert_equal doc(para("Backtick: `")), parse("Backtick: \\`")
+
+ assert_equal doc(para("Backslash: \\")), parse("Backslash: \\\\")
+
+ assert_equal doc(para("Colon: :")), parse("Colon: \\:")
+ end
+
+ def test_parse_heading_atx
+ doc = parse "# heading\n"
+
+ expected = @RM::Document.new(
+ @RM::Heading.new(1, "heading"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_heading_setext_dash
+ doc = parse <<-MD
+heading
+---
+ MD
+
+ expected = @RM::Document.new(
+ @RM::Heading.new(2, "heading"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_heading_setext_equals
+ doc = parse <<-MD
+heading
+===
+ MD
+
+ expected = @RM::Document.new(
+ @RM::Heading.new(1, "heading"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_html
+ @parser.html = true
+
+ doc = parse "<address>Links here</address>\n"
+
+ expected = doc(
+ @RM::Raw.new("<address>Links here</address>"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_html_hr
+ @parser.html = true
+
+ doc = parse "<hr>\n"
+
+ expected = doc(raw("<hr>"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_html_no_html
+ @parser.html = false
+
+ doc = parse "<address>Links here</address>\n"
+
+ expected = doc()
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_image
+ doc = parse "image ![alt text](path/to/image.jpg)"
+
+ expected = doc(para("image rdoc-image:path/to/image.jpg"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_image_link
+ @parser.html = true
+
+ doc = parse "[![alt text](path/to/image.jpg)](http://example.com)"
+
+ expected =
+ doc(
+ para('{rdoc-image:path/to/image.jpg}[http://example.com]'))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_line_break
+ doc = parse "Some text \nwith extra lines"
+
+ expected = doc(
+ para("Some text", hard_break, "with extra lines"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_link_reference_id
+ doc = parse <<-MD
+This is [an example][id] reference-style link.
+
+[id]: http://example.com "Optional Title Here"
+ MD
+
+ expected = doc(
+ para("This is {an example}[http://example.com] reference-style link."))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_link_reference_id_adjacent
+ doc = parse <<-MD
+[this] [this] should work
+
+[this]: example
+ MD
+
+ expected = doc(
+ para("{this}[example] should work"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_link_reference_id_eof
+ doc = parse <<-MD.chomp
+This is [an example][id] reference-style link.
+
+[id]: http://example.com "Optional Title Here"
+ MD
+
+ expected = doc(
+ para("This is {an example}[http://example.com] reference-style link."))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_link_reference_id_many
+ doc = parse <<-MD
+This is [an example][id] reference-style link.
+
+And [another][id].
+
+[id]: http://example.com "Optional Title Here"
+ MD
+
+ expected = doc(
+ para("This is {an example}[http://example.com] reference-style link."),
+ para("And {another}[http://example.com]."))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_link_reference_implicit
+ doc = parse <<-MD
+This is [an example][] reference-style link.
+
+[an example]: http://example.com "Optional Title Here"
+ MD
+
+ expected = doc(
+ para("This is {an example}[http://example.com] reference-style link."))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_bullet
+ doc = parse <<-MD
+* one
+* two
+ MD
+
+ expected = doc(
+ list(:BULLET,
+ item(nil, para("one")),
+ item(nil, para("two"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_bullet_auto_link
+ doc = parse <<-MD
+* <http://example/>
+ MD
+
+ expected = doc(
+ list(:BULLET,
+ item(nil, para("http://example/"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_bullet_continue
+ doc = parse <<-MD
+* one
+
+* two
+ MD
+
+ expected = doc(
+ list(:BULLET,
+ item(nil, para("one")),
+ item(nil, para("two"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_bullet_multiline
+ doc = parse <<-MD
+* one
+ two
+ MD
+
+ expected = doc(
+ list(:BULLET,
+ item(nil, para("one\n two"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_bullet_nest
+ doc = parse <<-MD
+* outer
+ * inner
+ MD
+
+ expected = doc(
+ list(:BULLET,
+ item(nil,
+ para("outer"),
+ list(:BULLET,
+ item(nil,
+ para("inner"))))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_bullet_nest_loose
+ doc = parse <<-MD
+* outer
+
+ * inner
+ MD
+
+ expected = doc(
+ list(:BULLET,
+ item(nil,
+ para("outer"),
+ list(:BULLET,
+ item(nil, para("inner"))))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_bullet_nest_continue
+ doc = parse <<-MD
+* outer
+ * inner
+ continue inner
+* outer 2
+ MD
+
+ expected = doc(
+ list(:BULLET,
+ item(nil,
+ para("outer"),
+ list(:BULLET,
+ item(nil,
+ para("inner\n continue inner")))),
+ item(nil,
+ para("outer 2"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_number
+ doc = parse <<-MD
+1. one
+1. two
+ MD
+
+ expected = doc(
+ list(:NUMBER,
+ item(nil, para("one")),
+ item(nil, para("two"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_list_number_continue
+ doc = parse <<-MD
+1. one
+
+1. two
+ MD
+
+ expected = doc(
+ list(:NUMBER,
+ item(nil, para("one")),
+ item(nil, para("two"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_note
+ @parser.notes = true
+
+ doc = parse <<-MD
+Some text.[^1]
+
+[^1]: With a footnote
+ MD
+
+ expected = doc(
+ para("Some text.{*1}[rdoc-label:foottext-1:footmark-1]"),
+ @RM::Rule.new(1),
+ para("{^1}[rdoc-label:footmark-1:foottext-1] With a footnote"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_note_indent
+ @parser.notes = true
+
+ doc = parse <<-MD
+Some text.[^1]
+
+[^1]: With a footnote
+
+ more
+ MD
+
+ expected = doc(
+ para("Some text.{*1}[rdoc-label:foottext-1:footmark-1]"),
+ rule(1),
+ para("{^1}[rdoc-label:footmark-1:foottext-1] With a footnote\n\nmore"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_note_inline
+ @parser.notes = true
+
+ doc = parse <<-MD
+Some text. ^[With a footnote]
+ MD
+
+ expected = doc(
+ para("Some text. {*1}[rdoc-label:foottext-1:footmark-1]"),
+ @RM::Rule.new(1),
+ para("{^1}[rdoc-label:footmark-1:foottext-1] With a footnote"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_note_no_notes
+ @parser.notes = false
+
+ assert_raises RuntimeError do # TODO use a real error
+ parse "Some text.[^1]"
+ end
+ end
+
+ def test_parse_note_multiple
+ @parser.notes = true
+
+ doc = parse <<-MD
+Some text[^1]
+with inline notes^[like this]
+and an extra note.[^2]
+
+[^1]: With a footnote
+
+[^2]: Which should be numbered correctly
+ MD
+
+ expected = doc(
+ para("Some text{*1}[rdoc-label:foottext-1:footmark-1]\n" +
+ "with inline notes{*2}[rdoc-label:foottext-2:footmark-2]\n" +
+ "and an extra note.{*3}[rdoc-label:foottext-3:footmark-3]"),
+
+ rule(1),
+
+ para("{^1}[rdoc-label:footmark-1:foottext-1] With a footnote"),
+ para("{^2}[rdoc-label:footmark-2:foottext-2] like this"),
+ para("{^3}[rdoc-label:footmark-3:foottext-3] " +
+ "Which should be numbered correctly"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph
+ doc = parse "it worked\n"
+
+ expected = doc(para("it worked"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_break_on_newline
+ @parser.break_on_newline = true
+
+ doc = parse "one\ntwo\n"
+
+ expected = doc(para("one", hard_break, "two"))
+
+ assert_equal expected, doc
+
+ doc = parse "one \ntwo\nthree\n"
+
+ expected = doc(para("one", hard_break, "two", hard_break, "three"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_stars
+ doc = parse "it worked ****\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it worked ****"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_html
+ @parser.html = true
+
+ doc = parse "<address>Links here</address>"
+
+ expected = doc(raw("<address>Links here</address>"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_html_no_html
+ @parser.html = false
+
+ doc = parse "<address>Links here</address>"
+
+ expected = doc()
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_indent_one
+ doc = parse <<-MD
+ text
+ MD
+
+ expected = doc(para("text"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_indent_two
+ doc = parse <<-MD
+ text
+ MD
+
+ expected = doc(para("text"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_indent_three
+ doc = parse <<-MD
+ text
+ MD
+
+ expected = doc(para("text"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_multiline
+ doc = parse "one\ntwo"
+
+ expected = doc(para("one\ntwo"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_paragraph_two
+ doc = parse "one\n\ntwo"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("one"),
+ @RM::Paragraph.new("two"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_plain
+ doc = parse "it worked"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it worked"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_reference_link_embedded_bracket
+ doc = parse "With [embedded [brackets]] [b].\n\n[b]: /url/\n"
+
+ expected =
+ doc(
+ para("With {embedded [brackets]}[/url/]."))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_rule_dash
+ doc = parse "- - -\n\n"
+
+ expected = @RM::Document.new(@RM::Rule.new(1))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_rule_underscore
+ doc = parse "_ _ _\n\n"
+
+ expected = @RM::Document.new(@RM::Rule.new(1))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_rule_star
+ doc = parse "* * *\n\n"
+
+ expected = @RM::Document.new(@RM::Rule.new(1))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_strong_star
+ doc = parse "it **works**\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it *works*"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_strong_underscore
+ doc = parse "it __works__\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it *works*"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_strong_emphasis_star
+ doc = parse "it ***works***\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it <b>_works_</b>"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_strong_emphasis_underscore
+ doc = parse "it ___works___\n"
+
+ expected = @RM::Document.new(
+ @RM::Paragraph.new("it <b>_works_</b>"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_style
+ @parser.css = true
+
+ doc = parse "<style>h1 { color: red }</style>\n"
+
+ expected = doc(
+ @RM::Raw.new("<style>h1 { color: red }</style>"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_style_disabled
+ doc = parse "<style>h1 { color: red }</style>\n"
+
+ expected = doc()
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_verbatim
+ doc = parse <<-MD
+ text
+ MD
+
+ expected = doc(verb("text\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_parse_verbatim_eof
+ doc = parse " text"
+
+ expected = doc(verb("text\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_strong
+ assert_equal '*word*', @parser.strong('word')
+ assert_equal '<b>two words</b>', @parser.strong('two words')
+ assert_equal '<b>_emphasis_</b>', @parser.strong('_emphasis_')
+ end
+
+ def parse text
+ @parser.parse text
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markdown_test.rb b/jni/ruby/test/rdoc/test_rdoc_markdown_test.rb
new file mode 100644
index 0000000..d464cba
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markdown_test.rb
@@ -0,0 +1,1884 @@
+require 'rubygems'
+require 'minitest/autorun'
+require 'pp'
+
+require 'rdoc'
+require 'rdoc/markdown'
+
+class TestRDocMarkdownTest < RDoc::TestCase
+
+ MARKDOWN_TEST_PATH = File.expand_path '../MarkdownTest_1.0.3/', __FILE__
+
+ def setup
+ super
+
+ @parser = RDoc::Markdown.new
+ end
+
+ def test_amps_and_angle_encoding
+ input = File.read "#{MARKDOWN_TEST_PATH}/Amps and angle encoding.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("AT&T has an ampersand in their name."),
+ para("AT&T is another way to write it."),
+ para("This & that."),
+ para("4 < 5."),
+ para("6 > 5."),
+ para("Here's a {link}[http://example.com/?foo=1&bar=2] with " +
+ "an ampersand in the URL."),
+ para("Here's a link with an amersand in the link text: " +
+ "{AT&T}[http://att.com/]."),
+ para("Here's an inline {link}[/script?foo=1&bar=2]."),
+ para("Here's an inline {link}[/script?foo=1&bar=2]."))
+
+ assert_equal expected, doc
+ end
+
+ def test_auto_links
+ input = File.read "#{MARKDOWN_TEST_PATH}/Auto links.text"
+
+ doc = @parser.parse input
+
+ # TODO verify rdoc auto-links too
+ expected =
+ doc(
+ para("Link: http://example.com/."),
+ para("With an ampersand: http://example.com/?foo=1&bar=2"),
+ list(:BULLET,
+ item(nil, para("In a list?")),
+ item(nil, para("http://example.com/")),
+ item(nil, para("It should."))),
+ block(
+ para("Blockquoted: http://example.com/")),
+ para("Auto-links should not occur here: " +
+ "<code><http://example.com/></code>"),
+ verb("or here: <http://example.com/>\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_backslash_escapes
+ input = File.read "#{MARKDOWN_TEST_PATH}/Backslash escapes.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("These should all get escaped:"),
+
+ para("Backslash: \\"),
+ para("Backtick: `"),
+ para("Asterisk: *"),
+ para("Underscore: _"),
+ para("Left brace: {"),
+ para("Right brace: }"),
+ para("Left bracket: ["),
+ para("Right bracket: ]"),
+ para("Left paren: ("),
+ para("Right paren: )"),
+ para("Greater-than: >"),
+ para("Hash: #"),
+ para("Period: ."),
+ para("Bang: !"),
+ para("Plus: +"),
+ para("Minus: -"),
+
+ para("These should not, because they occur within a code block:"),
+
+ verb("Backslash: \\\\\n",
+ "\n",
+ "Backtick: \\`\n",
+ "\n",
+ "Asterisk: \\*\n",
+ "\n",
+ "Underscore: \\_\n",
+ "\n",
+ "Left brace: \\{\n",
+ "\n",
+ "Right brace: \\}\n",
+ "\n",
+ "Left bracket: \\[\n",
+ "\n",
+ "Right bracket: \\]\n",
+ "\n",
+ "Left paren: \\(\n",
+ "\n",
+ "Right paren: \\)\n",
+ "\n",
+ "Greater-than: \\>\n",
+ "\n",
+ "Hash: \\#\n",
+ "\n",
+ "Period: \\.\n",
+ "\n",
+ "Bang: \\!\n",
+ "\n",
+ "Plus: \\+\n",
+ "\n",
+ "Minus: \\-\n"),
+
+ para("Nor should these, which occur in code spans:"),
+
+ para("Backslash: <code>\\\\</code>"),
+ para("Backtick: <code>\\`</code>"),
+ para("Asterisk: <code>\\*</code>"),
+ para("Underscore: <code>\\_</code>"),
+ para("Left brace: <code>\\{</code>"),
+ para("Right brace: <code>\\}</code>"),
+ para("Left bracket: <code>\\[</code>"),
+ para("Right bracket: <code>\\]</code>"),
+ para("Left paren: <code>\\(</code>"),
+ para("Right paren: <code>\\)</code>"),
+ para("Greater-than: <code>\\></code>"),
+ para("Hash: <code>\\#</code>"),
+ para("Period: <code>\\.</code>"),
+ para("Bang: <code>\\!</code>"),
+ para("Plus: <code>\\+</code>"),
+ para("Minus: <code>\\-</code>"),
+
+ para("These should get escaped, even though they're matching pairs for\n" +
+ "other Markdown constructs:"),
+
+ para("\*asterisks\*"),
+ para("\_underscores\_"),
+ para("`backticks`"),
+
+ para("This is a code span with a literal backslash-backtick " +
+ "sequence: <code>\\`</code>"),
+
+ para("This is a tag with unescaped backticks " +
+ "<span attr='`ticks`'>bar</span>."),
+
+ para("This is a tag with backslashes " +
+ "<span attr='\\\\backslashes\\\\'>bar</span>."))
+
+ assert_equal expected, doc
+ end
+
+ def test_blockquotes_with_code_blocks
+ input = File.read "#{MARKDOWN_TEST_PATH}/Blockquotes with code blocks.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ block(
+ para("Example:"),
+ verb("sub status {\n",
+ " print \"working\";\n",
+ "}\n"),
+ para("Or:"),
+ verb("sub status {\n",
+ " return \"working\";\n",
+ "}\n")))
+
+ assert_equal expected, doc
+ end
+
+ def test_code_blocks
+ input = File.read "#{MARKDOWN_TEST_PATH}/Code Blocks.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ verb("code block on the first line\n"),
+ para("Regular text."),
+
+ verb("code block indented by spaces\n"),
+ para("Regular text."),
+
+ verb("the lines in this block \n",
+ "all contain trailing spaces \n"),
+ para("Regular Text."),
+
+ verb("code block on the last line\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_code_spans
+ input = File.read "#{MARKDOWN_TEST_PATH}/Code Spans.text"
+
+ doc = @parser.parse input
+
+ expected = doc(
+ para("<code><test a=\"</code> content of attribute <code>\"></code>"),
+ para("Fix for backticks within HTML tag: " +
+ "<span attr='`ticks`'>like this</span>"),
+ para("Here's how you put <code>`backticks`</code> in a code span."))
+
+ assert_equal expected, doc
+ end
+
+ def test_hard_wrapped_paragraphs_with_list_like_lines
+ input = File.read "#{MARKDOWN_TEST_PATH}/Hard-wrapped paragraphs with list-like lines.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("In Markdown 1.0.0 and earlier. Version\n" +
+ "8. This line turns into a list item.\n" +
+ "Because a hard-wrapped line in the\n" +
+ "middle of a paragraph looked like a\n" +
+ "list item."),
+ para("Here's one with a bullet.\n" +
+ "* criminey."))
+
+ assert_equal expected, doc
+ end
+
+ def test_horizontal_rules
+ input = File.read "#{MARKDOWN_TEST_PATH}/Horizontal rules.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("Dashes:"),
+
+ rule(1),
+ rule(1),
+ rule(1),
+ rule(1),
+ verb("---\n"),
+
+ rule(1),
+ rule(1),
+ rule(1),
+ rule(1),
+ verb("- - -\n"),
+
+ para("Asterisks:"),
+
+ rule(1),
+ rule(1),
+ rule(1),
+ rule(1),
+ verb("***\n"),
+
+ rule(1),
+ rule(1),
+ rule(1),
+ rule(1),
+ verb("* * *\n"),
+
+ para("Underscores:"),
+
+ rule(1),
+ rule(1),
+ rule(1),
+ rule(1),
+ verb("___\n"),
+
+ rule(1),
+ rule(1),
+ rule(1),
+ rule(1),
+ verb("_ _ _\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_inline_html_advanced
+ input = File.read "#{MARKDOWN_TEST_PATH}/Inline HTML (Advanced).text"
+
+ @parser.html = true
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("Simple block on one line:"),
+ raw("<div>foo</div>"),
+ para("And nested without indentation:"),
+ raw(<<-RAW.chomp))
+<div>
+<div>
+<div>
+foo
+</div>
+<div style=">"/>
+</div>
+<div>bar</div>
+</div>
+ RAW
+
+ assert_equal expected, doc
+ end
+
+ def test_inline_html_simple
+ input = File.read "#{MARKDOWN_TEST_PATH}/Inline HTML (Simple).text"
+
+ @parser.html = true
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("Here's a simple block:"),
+ raw("<div>\n\tfoo\n</div>"),
+
+ para("This should be a code block, though:"),
+ verb("<div>\n",
+ "\tfoo\n",
+ "</div>\n"),
+
+ para("As should this:"),
+ verb("<div>foo</div>\n"),
+
+ para("Now, nested:"),
+ raw("<div>\n\t<div>\n\t\t<div>\n\t\t\tfoo\n" +
+ "\t\t</div>\n\t</div>\n</div>"),
+
+ para("This should just be an HTML comment:"),
+ raw("<!-- Comment -->"),
+
+ para("Multiline:"),
+ raw("<!--\nBlah\nBlah\n-->"),
+
+ para("Code block:"),
+ verb("<!-- Comment -->\n"),
+
+ para("Just plain comment, with trailing spaces on the line:"),
+ raw("<!-- foo -->"),
+
+ para("Code:"),
+ verb("<hr />\n"),
+
+ para("Hr's:"),
+ raw("<hr>"),
+ raw("<hr/>"),
+ raw("<hr />"),
+
+ raw("<hr>"),
+ raw("<hr/>"),
+ raw("<hr />"),
+
+ raw("<hr class=\"foo\" id=\"bar\" />"),
+ raw("<hr class=\"foo\" id=\"bar\"/>"),
+ raw("<hr class=\"foo\" id=\"bar\" >"))
+
+ assert_equal expected, doc
+ end
+
+ def test_inline_html_comments
+ input = File.read "#{MARKDOWN_TEST_PATH}/Inline HTML comments.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("Paragraph one."),
+
+ raw("<!-- This is a simple comment -->"),
+
+ raw("<!--\n\tThis is another comment.\n-->"),
+
+ para("Paragraph two."),
+
+ raw("<!-- one comment block -- -- with two comments -->"),
+
+ para("The end."))
+
+ assert_equal expected, doc
+ end
+
+ def test_links_inline_style
+ input = File.read "#{MARKDOWN_TEST_PATH}/Links, inline style.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("Just a {URL}[/url/]."),
+ para("{URL and title}[/url/]."),
+ para("{URL and title}[/url/]."),
+ para("{URL and title}[/url/]."),
+ para("{URL and title}[/url/]."),
+ para("{Empty}[]."))
+
+ assert_equal expected, doc
+ end
+
+ def test_links_reference_style
+ input = File.read "#{MARKDOWN_TEST_PATH}/Links, reference style.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("Foo {bar}[/url/]."),
+ para("Foo {bar}[/url/]."),
+ para("Foo {bar}[/url/]."),
+
+ para("With {embedded [brackets]}[/url/]."),
+
+ para("Indented {once}[/url]."),
+ para("Indented {twice}[/url]."),
+ para("Indented {thrice}[/url]."),
+ para("Indented [four][] times."),
+
+ verb("[four]: /url\n"),
+
+ rule(1),
+
+ para("{this}[foo] should work"),
+ para("So should {this}[foo]."),
+ para("And {this}[foo]."),
+ para("And {this}[foo]."),
+ para("And {this}[foo]."),
+
+ para("But not [that] []."),
+ para("Nor [that][]."),
+ para("Nor [that]."),
+
+ para("[Something in brackets like {this}[foo] should work]"),
+ para("[Same with {this}[foo].]"),
+
+ para("In this case, {this}[/somethingelse/] points to something else."),
+ para("Backslashing should suppress [this] and [this]."),
+
+ rule(1),
+
+ para("Here's one where the {link breaks}[/url/] across lines."),
+ para("Here's another where the {link breaks}[/url/] across lines, " +
+ "but with a line-ending space."))
+
+ assert_equal expected, doc
+ end
+
+ def test_links_shortcut_references
+ input = File.read "#{MARKDOWN_TEST_PATH}/Links, shortcut references.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("This is the {simple case}[/simple]."),
+ para("This one has a {line break}[/foo]."),
+ para("This one has a {line break}[/foo] with a line-ending space."),
+ para("{this}[/that] and the {other}[/other]"))
+
+ assert_equal expected, doc
+ end
+
+ def test_literal_quotes_in_titles
+ input = File.read "#{MARKDOWN_TEST_PATH}/Literal quotes in titles.text"
+
+ doc = @parser.parse input
+
+ # TODO support title attribute
+ expected =
+ doc(
+ para("Foo {bar}[/url/]."),
+ para("Foo {bar}[/url/]."))
+
+ assert_equal expected, doc
+ end
+
+ def test_markdown_documentation_basics
+ input = File.read "#{MARKDOWN_TEST_PATH}/Markdown Documentation - Basics.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ head(1, "Markdown: Basics"),
+
+ raw(<<-RAW.chomp),
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a class="selected" title="Markdown Basics">Basics</a></li>
+ <li><a href="/projects/markdown/syntax" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+ RAW
+
+ head(2, "Getting the Gist of Markdown's Formatting Syntax"),
+
+ para("This page offers a brief overview of what it's like to use Markdown.\n" +
+ "The {syntax page}[/projects/markdown/syntax] provides complete, detailed documentation for\n" +
+ "every feature, but Markdown should be very easy to pick up simply by\n" +
+ "looking at a few examples of it in action. The examples on this page\n" +
+ "are written in a before/after style, showing example syntax and the\n" +
+ "HTML output produced by Markdown."),
+
+ para("It's also helpful to simply try Markdown out; the {Dingus}[/projects/markdown/dingus] is a\n" +
+ "web application that allows you type your own Markdown-formatted text\n" +
+ "and translate it to XHTML."),
+
+ para("<b>Note:</b> This document is itself written using Markdown; you\n" +
+ "can {see the source for it by adding '.text' to the URL}[/projects/markdown/basics.text]."),
+
+ head(2, "Paragraphs, Headers, Blockquotes"),
+
+ para("A paragraph is simply one or more consecutive lines of text, separated\n" +
+ "by one or more blank lines. (A blank line is any line that looks like a\n" +
+ "blank line -- a line containing nothing spaces or tabs is considered\n" +
+ "blank.) Normal paragraphs should not be intended with spaces or tabs."),
+
+ para("Markdown offers two styles of headers: _Setext_ and _atx_.\n" +
+ "Setext-style headers for <code><h1></code> and <code><h2></code> are created by\n" +
+ "\"underlining\" with equal signs (<code>=</code>) and hyphens (<code>-</code>), respectively.\n" +
+ "To create an atx-style header, you put 1-6 hash marks (<code>#</code>) at the\n" +
+ "beginning of the line -- the number of hashes equals the resulting\n" +
+ "HTML header level."),
+
+ para("Blockquotes are indicated using email-style '<code>></code>' angle brackets."),
+
+ para("Markdown:"),
+
+ verb("A First Level Header\n",
+ "====================\n",
+ "\n",
+ "A Second Level Header\n",
+ "---------------------\n",
+ "\n",
+ "Now is the time for all good men to come to\n",
+ "the aid of their country. This is just a\n",
+ "regular paragraph.\n",
+ "\n",
+ "The quick brown fox jumped over the lazy\n",
+ "dog's back.\n",
+ "\n",
+ "### Header 3\n",
+ "\n",
+ "> This is a blockquote.\n",
+ "> \n",
+ "> This is the second paragraph in the blockquote.\n",
+ ">\n",
+ "> ## This is an H2 in a blockquote\n"),
+
+ para("Output:"),
+
+ verb("<h1>A First Level Header</h1>\n",
+ "\n",
+ "<h2>A Second Level Header</h2>\n",
+ "\n",
+ "<p>Now is the time for all good men to come to\n",
+ "the aid of their country. This is just a\n",
+ "regular paragraph.</p>\n",
+ "\n",
+ "<p>The quick brown fox jumped over the lazy\n",
+ "dog's back.</p>\n",
+ "\n",
+ "<h3>Header 3</h3>\n",
+ "\n",
+ "<blockquote>\n",
+ " <p>This is a blockquote.</p>\n",
+ "\n",
+ " <p>This is the second paragraph in the blockquote.</p>\n",
+ "\n",
+ " <h2>This is an H2 in a blockquote</h2>\n",
+ "</blockquote>\n"),
+
+ head(3, "Phrase Emphasis"),
+ para("Markdown uses asterisks and underscores to indicate spans of emphasis."),
+
+ para("Markdown:"),
+
+ verb("Some of these words *are emphasized*.\n",
+ "Some of these words _are emphasized also_.\n",
+ "\n",
+ "Use two asterisks for **strong emphasis**.\n",
+ "Or, if you prefer, __use two underscores instead__.\n"),
+
+ para("Output:"),
+
+ verb("<p>Some of these words <em>are emphasized</em>.\n",
+ "Some of these words <em>are emphasized also</em>.</p>\n",
+ "\n",
+ "<p>Use two asterisks for <strong>strong emphasis</strong>.\n",
+ "Or, if you prefer, <strong>use two underscores instead</strong>.</p>\n"),
+
+ head(2, "Lists"),
+
+ para("Unordered (bulleted) lists use asterisks, pluses, and hyphens (<code>*</code>,\n" +
+ "<code>+</code>, and <code>-</code>) as list markers. These three markers are\n" +
+ "interchangable; this:"),
+
+ verb("* Candy.\n",
+ "* Gum.\n",
+ "* Booze.\n"),
+
+ para("this:"),
+
+ verb("+ Candy.\n",
+ "+ Gum.\n",
+ "+ Booze.\n"),
+
+ para("and this:"),
+
+ verb("- Candy.\n",
+ "- Gum.\n",
+ "- Booze.\n"),
+
+ para("all produce the same output:"),
+
+ verb("<ul>\n",
+ "<li>Candy.</li>\n",
+ "<li>Gum.</li>\n",
+ "<li>Booze.</li>\n",
+ "</ul>\n"),
+
+ para("Ordered (numbered) lists use regular numbers, followed by periods, as\n" +
+ "list markers:"),
+
+ verb("1. Red\n",
+ "2. Green\n",
+ "3. Blue\n"),
+
+ para("Output:"),
+
+ verb("<ol>\n",
+ "<li>Red</li>\n",
+ "<li>Green</li>\n",
+ "<li>Blue</li>\n",
+ "</ol>\n"),
+
+ para("If you put blank lines between items, you'll get <code><p></code> tags for the\n" +
+ "list item text. You can create multi-paragraph list items by indenting\n" +
+ "the paragraphs by 4 spaces or 1 tab:"),
+
+ verb("* A list item.\n",
+ "\n",
+ " With multiple paragraphs.\n",
+ "\n",
+ "* Another item in the list.\n"),
+
+ para("Output:"),
+
+ verb("<ul>\n",
+ "<li><p>A list item.</p>\n",
+ "<p>With multiple paragraphs.</p></li>\n",
+ "<li><p>Another item in the list.</p></li>\n",
+ "</ul>\n"),
+
+ head(3, "Links"),
+
+ para("Markdown supports two styles for creating links: _inline_ and\n" +
+ "_reference_. With both styles, you use square brackets to delimit the\n" +
+ "text you want to turn into a link."),
+
+ para("Inline-style links use parentheses immediately after the link text.\n" +
+ "For example:"),
+
+ verb("This is an [example link](http://example.com/).\n"),
+
+ para("Output:"),
+
+ verb("<p>This is an <a href=\"http://example.com/\">\n",
+ "example link</a>.</p>\n"),
+
+ para("Optionally, you may include a title attribute in the parentheses:"),
+
+ verb("This is an [example link](http://example.com/ \"With a Title\").\n"),
+
+ para("Output:"),
+
+ verb("<p>This is an <a href=\"http://example.com/\" title=\"With a Title\">\n",
+ "example link</a>.</p>\n"),
+
+ para("Reference-style links allow you to refer to your links by names, which\n" +
+ "you define elsewhere in your document:"),
+
+ verb("I get 10 times more traffic from [Google][1] than from\n",
+ "[Yahoo][2] or [MSN][3].\n",
+ "\n",
+ "[1]: http://google.com/ \"Google\"\n",
+ "[2]: http://search.yahoo.com/ \"Yahoo Search\"\n",
+ "[3]: http://search.msn.com/ \"MSN Search\"\n"),
+
+ para("Output:"),
+
+ verb("<p>I get 10 times more traffic from <a href=\"http://google.com/\"\n",
+ "title=\"Google\">Google</a> than from <a href=\"http://search.yahoo.com/\"\n",
+ "title=\"Yahoo Search\">Yahoo</a> or <a href=\"http://search.msn.com/\"\n",
+ "title=\"MSN Search\">MSN</a>.</p>\n"),
+
+ para("The title attribute is optional. Link names may contain letters,\n" +
+ "numbers and spaces, but are _not_ case sensitive:"),
+
+ verb("I start my morning with a cup of coffee and\n",
+ "[The New York Times][NY Times].\n",
+ "\n",
+ "[ny times]: http://www.nytimes.com/\n"),
+
+ para("Output:"),
+
+ verb("<p>I start my morning with a cup of coffee and\n",
+ "<a href=\"http://www.nytimes.com/\">The New York Times</a>.</p>\n"),
+
+ head(3, "Images"),
+
+ para("Image syntax is very much like link syntax."),
+
+ para("Inline (titles are optional):"),
+
+ verb("![alt text](/path/to/img.jpg \"Title\")\n"),
+
+ para("Reference-style:"),
+
+ verb("![alt text][id]\n",
+ "\n",
+ "[id]: /path/to/img.jpg \"Title\"\n"),
+
+ para("Both of the above examples produce the same output:"),
+
+ verb("<img src=\"/path/to/img.jpg\" alt=\"alt text\" title=\"Title\" />\n"),
+
+ head(3, "Code"),
+
+ para("In a regular paragraph, you can create code span by wrapping text in\n" +
+ "backtick quotes. Any ampersands (<code>&</code>) and angle brackets (<code><</code> or\n" +
+ "<code>></code>) will automatically be translated into HTML entities. This makes\n" +
+ "it easy to use Markdown to write about HTML example code:"),
+
+ verb(
+ "I strongly recommend against using any `<blink>` tags.\n",
+ "\n",
+ "I wish SmartyPants used named entities like `&mdash;`\n",
+ "instead of decimal-encoded entites like `&#8212;`.\n"),
+
+ para("Output:"),
+
+ verb("<p>I strongly recommend against using any\n",
+ "<code>&lt;blink&gt;</code> tags.</p>\n",
+ "\n",
+ "<p>I wish SmartyPants used named entities like\n",
+ "<code>&amp;mdash;</code> instead of decimal-encoded\n",
+ "entites like <code>&amp;#8212;</code>.</p>\n"),
+
+ para("To specify an entire block of pre-formatted code, indent every line of\n" +
+ "the block by 4 spaces or 1 tab. Just like with code spans, <code>&</code>, <code><</code>,\n" +
+ "and <code>></code> characters will be escaped automatically."),
+
+ para("Markdown:"),
+
+ verb("If you want your page to validate under XHTML 1.0 Strict,\n",
+ "you've got to put paragraph tags in your blockquotes:\n",
+ "\n",
+ " <blockquote>\n",
+ " <p>For example.</p>\n",
+ " </blockquote>\n"),
+
+ para("Output:"),
+
+ verb("<p>If you want your page to validate under XHTML 1.0 Strict,\n",
+ "you've got to put paragraph tags in your blockquotes:</p>\n",
+ "\n",
+ "<pre><code>&lt;blockquote&gt;\n",
+ " &lt;p&gt;For example.&lt;/p&gt;\n",
+ "&lt;/blockquote&gt;\n",
+ "</code></pre>\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_markdown_documentation_syntax
+ input = File.read "#{MARKDOWN_TEST_PATH}/Markdown Documentation - Syntax.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ head(1, "Markdown: Syntax"),
+
+ raw(<<-RAW.chomp),
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a href="/projects/markdown/basics" title="Markdown Basics">Basics</a></li>
+ <li><a class="selected" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+ RAW
+
+ list(:BULLET,
+ item(nil,
+ para("{Overview}[#overview]"),
+ list(:BULLET,
+ item(nil,
+ para("{Philosophy}[#philosophy]")),
+ item(nil,
+ para("{Inline HTML}[#html]")),
+ item(nil,
+ para("{Automatic Escaping for Special Characters}[#autoescape]")))),
+ item(nil,
+ para("{Block Elements}[#block]"),
+ list(:BULLET,
+ item(nil,
+ para("{Paragraphs and Line Breaks}[#p]")),
+ item(nil,
+ para("{Headers}[#header]")),
+ item(nil,
+ para("{Blockquotes}[#blockquote]")),
+ item(nil,
+ para("{Lists}[#list]")),
+ item(nil,
+ para("{Code Blocks}[#precode]")),
+ item(nil,
+ para("{Horizontal Rules}[#hr]")))),
+ item(nil,
+ para("{Span Elements}[#span]"),
+ list(:BULLET,
+ item(nil,
+ para("{Links}[#link]")),
+ item(nil,
+ para("{Emphasis}[#em]")),
+ item(nil,
+ para("{Code}[#code]")),
+ item(nil,
+ para("{Images}[#img]")))),
+ item(nil,
+ para("{Miscellaneous}[#misc]"),
+ list(:BULLET,
+ item(nil,
+ para("{Backslash Escapes}[#backslash]")),
+ item(nil,
+ para("{Automatic Links}[#autolink]"))))),
+
+ para("<b>Note:</b> This document is itself written using Markdown; you\n" +
+ "can {see the source for it by adding '.text' to the URL}[/projects/markdown/syntax.text]."),
+
+ rule(1),
+
+ raw("<h2 id=\"overview\">Overview</h2>"),
+
+ raw("<h3 id=\"philosophy\">Philosophy</h3>"),
+
+ para("Markdown is intended to be as easy-to-read and easy-to-write as is feasible."),
+
+ para("Readability, however, is emphasized above all else. A Markdown-formatted\n" +
+ "document should be publishable as-is, as plain text, without looking\n" +
+ "like it's been marked up with tags or formatting instructions. While\n" +
+ "Markdown's syntax has been influenced by several existing text-to-HTML\n" +
+ "filters -- including {Setext}[http://docutils.sourceforge.net/mirror/setext.html], {atx}[http://www.aaronsw.com/2002/atx/], {Textile}[http://textism.com/tools/textile/], {reStructuredText}[http://docutils.sourceforge.net/rst.html],\n" +
+ "{Grutatext}[http://www.triptico.com/software/grutatxt.html], and {EtText}[http://ettext.taint.org/doc/] -- the single biggest source of\n" +
+ "inspiration for Markdown's syntax is the format of plain text email."),
+
+ para("To this end, Markdown's syntax is comprised entirely of punctuation\n" +
+ "characters, which punctuation characters have been carefully chosen so\n" +
+ "as to look like what they mean. E.g., asterisks around a word actually\n" +
+ "look like \*emphasis\*. Markdown lists look like, well, lists. Even\n" +
+ "blockquotes look like quoted passages of text, assuming you've ever\n" +
+ "used email."),
+
+ raw("<h3 id=\"html\">Inline HTML</h3>"),
+
+ para("Markdown's syntax is intended for one purpose: to be used as a\n" +
+ "format for _writing_ for the web."),
+
+ para("Markdown is not a replacement for HTML, or even close to it. Its\n" +
+ "syntax is very small, corresponding only to a very small subset of\n" +
+ "HTML tags. The idea is _not_ to create a syntax that makes it easier\n" +
+ "to insert HTML tags. In my opinion, HTML tags are already easy to\n" +
+ "insert. The idea for Markdown is to make it easy to read, write, and\n" +
+ "edit prose. HTML is a _publishing_ format; Markdown is a _writing_\n" +
+ "format. Thus, Markdown's formatting syntax only addresses issues that\n" +
+ "can be conveyed in plain text."),
+
+ para("For any markup that is not covered by Markdown's syntax, you simply\n" +
+ "use HTML itself. There's no need to preface it or delimit it to\n" +
+ "indicate that you're switching from Markdown to HTML; you just use\n" +
+ "the tags."),
+
+ para("The only restrictions are that block-level HTML elements -- e.g. <code><div></code>,\n" +
+ "<code><table></code>, <code><pre></code>, <code><p></code>, etc. -- must be separated from surrounding\n" +
+ "content by blank lines, and the start and end tags of the block should\n" +
+ "not be indented with tabs or spaces. Markdown is smart enough not\n" +
+ "to add extra (unwanted) <code><p></code> tags around HTML block-level tags."),
+
+ para("For example, to add an HTML table to a Markdown article:"),
+
+ verb("This is a regular paragraph.\n",
+ "\n",
+ "<table>\n",
+ " <tr>\n",
+ " <td>Foo</td>\n",
+ " </tr>\n",
+ "</table>\n",
+ "\n",
+ "This is another regular paragraph.\n"),
+
+ para("Note that Markdown formatting syntax is not processed within block-level\n" +
+ "HTML tags. E.g., you can't use Markdown-style <code>*emphasis*</code> inside an\n" +
+ "HTML block."),
+
+ para("Span-level HTML tags -- e.g. <code><span></code>, <code><cite></code>, or <code><del></code> -- can be\n" +
+ "used anywhere in a Markdown paragraph, list item, or header. If you\n" +
+ "want, you can even use HTML tags instead of Markdown formatting; e.g. if\n" +
+ "you'd prefer to use HTML <code><a></code> or <code><img></code> tags instead of Markdown's\n" +
+ "link or image syntax, go right ahead."),
+
+ para("Unlike block-level HTML tags, Markdown syntax _is_ processed within\n" +
+ "span-level tags."),
+
+ raw("<h3 id=\"autoescape\">Automatic Escaping for Special Characters</h3>"),
+
+ para("In HTML, there are two characters that demand special treatment: <code><</code>\n" +
+ "and <code>&</code>. Left angle brackets are used to start tags; ampersands are\n" +
+ "used to denote HTML entities. If you want to use them as literal\n" +
+ "characters, you must escape them as entities, e.g. <code>&lt;</code>, and\n" +
+ "<code>&amp;</code>."),
+
+ para("Ampersands in particular are bedeviling for web writers. If you want to\n" +
+ "write about 'AT&T', you need to write '<code>AT&amp;T</code>'. You even need to\n" +
+ "escape ampersands within URLs. Thus, if you want to link to:"),
+
+ verb("http://images.google.com/images?num=30&q=larry+bird\n"),
+
+ para("you need to encode the URL as:"),
+
+ verb("http://images.google.com/images?num=30&amp;q=larry+bird\n"),
+
+ para("in your anchor tag <code>href</code> attribute. Needless to say, this is easy to\n" +
+ "forget, and is probably the single most common source of HTML validation\n" +
+ "errors in otherwise well-marked-up web sites."),
+
+ para("Markdown allows you to use these characters naturally, taking care of\n" +
+ "all the necessary escaping for you. If you use an ampersand as part of\n" +
+ "an HTML entity, it remains unchanged; otherwise it will be translated\n" +
+ "into <code>&amp;</code>."),
+
+ para("So, if you want to include a copyright symbol in your article, you can write:"),
+
+ verb("&copy;\n"),
+
+ para("and Markdown will leave it alone. But if you write:"),
+
+ verb("AT&T\n"),
+
+ para("Markdown will translate it to:"),
+
+ verb("AT&amp;T\n"),
+
+ para("Similarly, because Markdown supports {inline HTML}[#html], if you use\n" +
+ "angle brackets as delimiters for HTML tags, Markdown will treat them as\n" +
+ "such. But if you write:"),
+
+ verb("4 < 5\n"),
+
+ para("Markdown will translate it to:"),
+
+ verb("4 &lt; 5\n"),
+
+ para("However, inside Markdown code spans and blocks, angle brackets and\n" +
+ "ampersands are _always_ encoded automatically. This makes it easy to use\n" +
+ "Markdown to write about HTML code. (As opposed to raw HTML, which is a\n" +
+ "terrible format for writing about HTML syntax, because every single <code><</code>\n" +
+ "and <code>&</code> in your example code needs to be escaped.)"),
+
+ rule(1),
+
+ raw("<h2 id=\"block\">Block Elements</h2>"),
+
+ raw("<h3 id=\"p\">Paragraphs and Line Breaks</h3>"),
+
+ para("A paragraph is simply one or more consecutive lines of text, separated\n" +
+ "by one or more blank lines. (A blank line is any line that looks like a\n" +
+ "blank line -- a line containing nothing but spaces or tabs is considered\n" +
+ "blank.) Normal paragraphs should not be intended with spaces or tabs."),
+
+ para("The implication of the \"one or more consecutive lines of text\" rule is\n" +
+ "that Markdown supports \"hard-wrapped\" text paragraphs. This differs\n" +
+ "significantly from most other text-to-HTML formatters (including Movable\n" +
+ "Type's \"Convert Line Breaks\" option) which translate every line break\n" +
+ "character in a paragraph into a <code><br /></code> tag."),
+
+ para("When you _do_ want to insert a <code><br /></code> break tag using Markdown, you\n" +
+ "end a line with two or more spaces, then type return."),
+
+ para("Yes, this takes a tad more effort to create a <code><br /></code>, but a simplistic\n" +
+ "\"every line break is a <code><br /></code>\" rule wouldn't work for Markdown.\n" +
+ "Markdown's email-style {blockquoting}[#blockquote] and multi-paragraph {list items}[#list]\n" +
+ "work best -- and look better -- when you format them with hard breaks."),
+
+ raw("<h3 id=\"header\">Headers</h3>"),
+
+ para("Markdown supports two styles of headers, {Setext}[http://docutils.sourceforge.net/mirror/setext.html] and {atx}[http://www.aaronsw.com/2002/atx/]."),
+
+ para("Setext-style headers are \"underlined\" using equal signs (for first-level\n" +
+ "headers) and dashes (for second-level headers). For example:"),
+
+ verb("This is an H1\n",
+ "=============\n",
+ "\n",
+ "This is an H2\n",
+ "-------------\n"),
+
+ para("Any number of underlining <code>=</code>'s or <code>-</code>'s will work."),
+
+ para("Atx-style headers use 1-6 hash characters at the start of the line,\n" +
+ "corresponding to header levels 1-6. For example:"),
+
+ verb("# This is an H1\n",
+ "\n",
+ "## This is an H2\n",
+ "\n",
+ "###### This is an H6\n"),
+
+ para("Optionally, you may \"close\" atx-style headers. This is purely\n" +
+ "cosmetic -- you can use this if you think it looks better. The\n" +
+ "closing hashes don't even need to match the number of hashes\n" +
+ "used to open the header. (The number of opening hashes\n" +
+ "determines the header level.) :"),
+
+ verb("# This is an H1 #\n",
+ "\n",
+ "## This is an H2 ##\n",
+ "\n",
+ "### This is an H3 ######\n"),
+
+ raw("<h3 id=\"blockquote\">Blockquotes</h3>"),
+
+ para(
+ "Markdown uses email-style <code>></code> characters for blockquoting. If you're\n" +
+ "familiar with quoting passages of text in an email message, then you\n" +
+ "know how to create a blockquote in Markdown. It looks best if you hard\n" +
+ "wrap the text and put a <code>></code> before every line:"),
+
+ verb("> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,\n",
+ "> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.\n",
+ "> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.\n",
+ "> \n",
+ "> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse\n",
+ "> id sem consectetuer libero luctus adipiscing.\n"),
+
+ para("Markdown allows you to be lazy and only put the <code>></code> before the first\n" +
+ "line of a hard-wrapped paragraph:"),
+
+ verb("> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,\n",
+ "consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.\n",
+ "Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.\n",
+ "\n",
+ "> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse\n",
+ "id sem consectetuer libero luctus adipiscing.\n"),
+
+ para("Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by\n" +
+ "adding additional levels of <code>></code>:"),
+
+ verb("> This is the first level of quoting.\n",
+ ">\n",
+ "> > This is nested blockquote.\n",
+ ">\n",
+ "> Back to the first level.\n"),
+
+ para("Blockquotes can contain other Markdown elements, including headers, lists,\n" +
+ "and code blocks:"),
+
+ verb("> ## This is a header.\n",
+ "> \n",
+ "> 1. This is the first list item.\n",
+ "> 2. This is the second list item.\n",
+ "> \n",
+ "> Here's some example code:\n",
+ "> \n",
+ "> return shell_exec(\"echo $input | $markdown_script\");\n"),
+
+ para("Any decent text editor should make email-style quoting easy. For\n" +
+ "example, with BBEdit, you can make a selection and choose Increase\n" +
+ "Quote Level from the Text menu."),
+
+ raw("<h3 id=\"list\">Lists</h3>"),
+
+ para("Markdown supports ordered (numbered) and unordered (bulleted) lists."),
+
+ para("Unordered lists use asterisks, pluses, and hyphens -- interchangably\n" +
+ "-- as list markers:"),
+
+ verb("* Red\n",
+ "* Green\n",
+ "* Blue\n"),
+
+ para("is equivalent to:"),
+
+ verb("+ Red\n",
+ "+ Green\n",
+ "+ Blue\n"),
+
+ para("and:"),
+
+ verb("- Red\n",
+ "- Green\n",
+ "- Blue\n"),
+
+ para("Ordered lists use numbers followed by periods:"),
+
+ verb("1. Bird\n",
+ "2. McHale\n",
+ "3. Parish\n"),
+
+ para("It's important to note that the actual numbers you use to mark the\n" +
+ "list have no effect on the HTML output Markdown produces. The HTML\n" +
+ "Markdown produces from the above list is:"),
+
+ verb("<ol>\n",
+ "<li>Bird</li>\n",
+ "<li>McHale</li>\n",
+ "<li>Parish</li>\n",
+ "</ol>\n"),
+
+ para("If you instead wrote the list in Markdown like this:"),
+
+ verb("1. Bird\n",
+ "1. McHale\n",
+ "1. Parish\n"),
+
+ para("or even:"),
+
+ verb("3. Bird\n",
+ "1. McHale\n",
+ "8. Parish\n"),
+
+ para("you'd get the exact same HTML output. The point is, if you want to,\n" +
+ "you can use ordinal numbers in your ordered Markdown lists, so that\n" +
+ "the numbers in your source match the numbers in your published HTML.\n" +
+ "But if you want to be lazy, you don't have to."),
+
+ para("If you do use lazy list numbering, however, you should still start the\n" +
+ "list with the number 1. At some point in the future, Markdown may support\n" +
+ "starting ordered lists at an arbitrary number."),
+
+ para("List markers typically start at the left margin, but may be indented by\n" +
+ "up to three spaces. List markers must be followed by one or more spaces\n" +
+ "or a tab."),
+
+ para("To make lists look nice, you can wrap items with hanging indents:"),
+
+ verb("* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\n",
+ " Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,\n",
+ " viverra nec, fringilla in, laoreet vitae, risus.\n",
+ "* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.\n",
+ " Suspendisse id sem consectetuer libero luctus adipiscing.\n"),
+
+ para("But if you want to be lazy, you don't have to:"),
+
+ verb("* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\n",
+ "Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,\n",
+ "viverra nec, fringilla in, laoreet vitae, risus.\n",
+ "* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.\n",
+ "Suspendisse id sem consectetuer libero luctus adipiscing.\n"),
+
+ para("If list items are separated by blank lines, Markdown will wrap the\n" +
+ "items in <code><p></code> tags in the HTML output. For example, this input:"),
+
+ verb("* Bird\n",
+ "* Magic\n"),
+
+ para("will turn into:"),
+
+ verb("<ul>\n",
+ "<li>Bird</li>\n",
+ "<li>Magic</li>\n",
+ "</ul>\n"),
+
+ para("But this:"),
+
+ verb("* Bird\n",
+ "\n",
+ "* Magic\n"),
+
+ para("will turn into:"),
+
+ verb("<ul>\n",
+ "<li><p>Bird</p></li>\n",
+ "<li><p>Magic</p></li>\n",
+ "</ul>\n"),
+
+ para("List items may consist of multiple paragraphs. Each subsequent\n" +
+ "paragraph in a list item must be intended by either 4 spaces\n" +
+ "or one tab:"),
+
+ verb("1. This is a list item with two paragraphs. Lorem ipsum dolor\n",
+ " sit amet, consectetuer adipiscing elit. Aliquam hendrerit\n",
+ " mi posuere lectus.\n",
+ "\n",
+ " Vestibulum enim wisi, viverra nec, fringilla in, laoreet\n",
+ " vitae, risus. Donec sit amet nisl. Aliquam semper ipsum\n",
+ " sit amet velit.\n",
+ "\n",
+ "2. Suspendisse id sem consectetuer libero luctus adipiscing.\n"),
+
+ para("It looks nice if you indent every line of the subsequent\n" +
+ "paragraphs, but here again, Markdown will allow you to be\n" +
+ "lazy:"),
+
+ verb("* This is a list item with two paragraphs.\n",
+ "\n",
+ " This is the second paragraph in the list item. You're\n",
+ "only required to indent the first line. Lorem ipsum dolor\n",
+ "sit amet, consectetuer adipiscing elit.\n",
+ "\n",
+ "* Another item in the same list.\n"),
+
+ para("To put a blockquote within a list item, the blockquote's <code>></code>\n" +
+ "delimiters need to be indented:"),
+
+ verb("* A list item with a blockquote:\n",
+ "\n",
+ " > This is a blockquote\n",
+ " > inside a list item.\n"),
+
+ para(
+ "To put a code block within a list item, the code block needs\n" +
+ "to be indented _twice_ -- 8 spaces or two tabs:"),
+
+ verb("* A list item with a code block:\n",
+ "\n",
+ " <code goes here>\n"),
+
+ para("It's worth noting that it's possible to trigger an ordered list by\n" +
+ "accident, by writing something like this:"),
+
+ verb("1986. What a great season.\n"),
+
+ para("In other words, a <em>number-period-space</em> sequence at the beginning of a\n" +
+ "line. To avoid this, you can backslash-escape the period:"),
+
+ verb("1986\\. What a great season.\n"),
+
+ raw("<h3 id=\"precode\">Code Blocks</h3>"),
+
+ para("Pre-formatted code blocks are used for writing about programming or\n" +
+ "markup source code. Rather than forming normal paragraphs, the lines\n" +
+ "of a code block are interpreted literally. Markdown wraps a code block\n" +
+ "in both <code><pre></code> and <code><code></code> tags."),
+
+ para("To produce a code block in Markdown, simply indent every line of the\n" +
+ "block by at least 4 spaces or 1 tab. For example, given this input:"),
+
+ verb("This is a normal paragraph:\n",
+ "\n",
+ " This is a code block.\n"),
+
+ para("Markdown will generate:"),
+
+ verb("<p>This is a normal paragraph:</p>\n",
+ "\n",
+ "<pre><code>This is a code block.\n",
+ "</code></pre>\n"),
+
+ para("One level of indentation -- 4 spaces or 1 tab -- is removed from each\n" +
+ "line of the code block. For example, this:"),
+
+ verb("Here is an example of AppleScript:\n",
+ "\n",
+ " tell application \"Foo\"\n",
+ " beep\n",
+ " end tell\n"),
+
+ para("will turn into:"),
+
+ verb("<p>Here is an example of AppleScript:</p>\n",
+ "\n",
+ "<pre><code>tell application \"Foo\"\n",
+ " beep\n",
+ "end tell\n",
+ "</code></pre>\n"),
+
+ para("A code block continues until it reaches a line that is not indented\n" +
+ "(or the end of the article)."),
+
+ para("Within a code block, ampersands (<code>&</code>) and angle brackets (<code><</code> and <code>></code>)\n" +
+ "are automatically converted into HTML entities. This makes it very\n" +
+ "easy to include example HTML source code using Markdown -- just paste\n" +
+ "it and indent it, and Markdown will handle the hassle of encoding the\n" +
+ "ampersands and angle brackets. For example, this:"),
+
+ verb(" <div class=\"footer\">\n",
+ " &copy; 2004 Foo Corporation\n",
+ " </div>\n"),
+
+ para("will turn into:"),
+
+ verb("<pre><code>&lt;div class=\"footer\"&gt;\n",
+ " &amp;copy; 2004 Foo Corporation\n",
+ "&lt;/div&gt;\n",
+ "</code></pre>\n"),
+
+ para("Regular Markdown syntax is not processed within code blocks. E.g.,\n" +
+ "asterisks are just literal asterisks within a code block. This means\n" +
+ "it's also easy to use Markdown to write about Markdown's own syntax."),
+
+ raw("<h3 id=\"hr\">Horizontal Rules</h3>"),
+
+ para("You can produce a horizontal rule tag (<code><hr /></code>) by placing three or\n" +
+ "more hyphens, asterisks, or underscores on a line by themselves. If you\n" +
+ "wish, you may use spaces between the hyphens or asterisks. Each of the\n" +
+ "following lines will produce a horizontal rule:"),
+
+ verb("* * *\n",
+ "\n",
+ "***\n",
+ "\n",
+ "*****\n",
+ "\n",
+ "- - -\n",
+ "\n",
+ "---------------------------------------\n",
+ "\n",
+ "_ _ _\n"),
+
+ rule(1),
+
+ raw("<h2 id=\"span\">Span Elements</h2>"),
+
+ raw("<h3 id=\"link\">Links</h3>"),
+
+ para("Markdown supports two style of links: _inline_ and _reference_."),
+
+ para("In both styles, the link text is delimited by [square brackets]."),
+
+ para("To create an inline link, use a set of regular parentheses immediately\n" +
+ "after the link text's closing square bracket. Inside the parentheses,\n" +
+ "put the URL where you want the link to point, along with an _optional_\n" +
+ "title for the link, surrounded in quotes. For example:"),
+
+ verb("This is [an example](http://example.com/ \"Title\") inline link.\n",
+ "\n",
+ "[This link](http://example.net/) has no title attribute.\n"),
+
+ para("Will produce:"),
+
+ verb("<p>This is <a href=\"http://example.com/\" title=\"Title\">\n",
+ "an example</a> inline link.</p>\n",
+ "\n",
+ "<p><a href=\"http://example.net/\">This link</a> has no\n",
+ "title attribute.</p>\n"),
+
+ para("If you're referring to a local resource on the same server, you can\n" +
+ "use relative paths:"),
+
+ verb("See my [About](/about/) page for details.\n"),
+
+ para("Reference-style links use a second set of square brackets, inside\n" +
+ "which you place a label of your choosing to identify the link:"),
+
+ verb("This is [an example][id] reference-style link.\n"),
+
+ para("You can optionally use a space to separate the sets of brackets:"),
+
+ verb("This is [an example] [id] reference-style link.\n"),
+
+ para("Then, anywhere in the document, you define your link label like this,\n" +
+ "on a line by itself:"),
+
+ verb("[id]: http://example.com/ \"Optional Title Here\"\n"),
+
+ para("That is:"),
+
+ list(:BULLET,
+ item(nil,
+ para("Square brackets containing the link identifier (optionally\n" +
+ "indented from the left margin using up to three spaces);")),
+ item(nil,
+ para("followed by a colon;")),
+ item(nil,
+ para("followed by one or more spaces (or tabs);")),
+ item(nil,
+ para("followed by the URL for the link;")),
+ item(nil,
+ para("optionally followed by a title attribute for the link, enclosed\n" +
+ "in double or single quotes."))),
+
+ para("The link URL may, optionally, be surrounded by angle brackets:"),
+
+ verb("[id]: <http://example.com/> \"Optional Title Here\"\n"),
+
+ para("You can put the title attribute on the next line and use extra spaces\n" +
+ "or tabs for padding, which tends to look better with longer URLs:"),
+
+ verb("[id]: http://example.com/longish/path/to/resource/here\n",
+ " \"Optional Title Here\"\n"),
+
+ para("Link definitions are only used for creating links during Markdown\n" +
+ "processing, and are stripped from your document in the HTML output."),
+
+ para("Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are _not_ case sensitive. E.g. these two links:"),
+
+ verb("[link text][a]\n",
+ "[link text][A]\n"),
+
+ para("are equivalent."),
+
+ para("The <em>implicit link name</em> shortcut allows you to omit the name of the\n" +
+ "link, in which case the link text itself is used as the name.\n" +
+ "Just use an empty set of square brackets -- e.g., to link the word\n" +
+ "\"Google\" to the google.com web site, you could simply write:"),
+
+ verb("[Google][]\n"),
+
+ para("And then define the link:"),
+
+ verb("[Google]: http://google.com/\n"),
+
+ para("Because link names may contain spaces, this shortcut even works for\n" +
+ "multiple words in the link text:"),
+
+
+ verb("Visit [Daring Fireball][] for more information.\n"),
+
+ para("And then define the link:"),
+
+ verb("[Daring Fireball]: http://daringfireball.net/\n"),
+
+ para("Link definitions can be placed anywhere in your Markdown document. I\n" +
+ "tend to put them immediately after each paragraph in which they're\n" +
+ "used, but if you want, you can put them all at the end of your\n" +
+ "document, sort of like footnotes."),
+
+ para("Here's an example of reference links in action:"),
+
+ verb("I get 10 times more traffic from [Google] [1] than from\n",
+ "[Yahoo] [2] or [MSN] [3].\n",
+ "\n",
+ " [1]: http://google.com/ \"Google\"\n",
+ " [2]: http://search.yahoo.com/ \"Yahoo Search\"\n",
+ " [3]: http://search.msn.com/ \"MSN Search\"\n"),
+
+ para("Using the implicit link name shortcut, you could instead write:"),
+
+ verb("I get 10 times more traffic from [Google][] than from\n",
+ "[Yahoo][] or [MSN][].\n",
+ "\n",
+ " [google]: http://google.com/ \"Google\"\n",
+ " [yahoo]: http://search.yahoo.com/ \"Yahoo Search\"\n",
+ " [msn]: http://search.msn.com/ \"MSN Search\"\n"),
+
+ para("Both of the above examples will produce the following HTML output:"),
+
+ verb("<p>I get 10 times more traffic from <a href=\"http://google.com/\"\n",
+ "title=\"Google\">Google</a> than from\n",
+ "<a href=\"http://search.yahoo.com/\" title=\"Yahoo Search\">Yahoo</a>\n",
+ "or <a href=\"http://search.msn.com/\" title=\"MSN Search\">MSN</a>.</p>\n"),
+
+ para("For comparison, here is the same paragraph written using\n" +
+ "Markdown's inline link style:"),
+
+ verb("I get 10 times more traffic from [Google](http://google.com/ \"Google\")\n",
+ "than from [Yahoo](http://search.yahoo.com/ \"Yahoo Search\") or\n",
+ "[MSN](http://search.msn.com/ \"MSN Search\").\n"),
+
+ para("The point of reference-style links is not that they're easier to\n" +
+ "write. The point is that with reference-style links, your document\n" +
+ "source is vastly more readable. Compare the above examples: using\n" +
+ "reference-style links, the paragraph itself is only 81 characters\n" +
+ "long; with inline-style links, it's 176 characters; and as raw HTML,\n" +
+ "it's 234 characters. In the raw HTML, there's more markup than there\n" +
+ "is text."),
+
+ para("With Markdown's reference-style links, a source document much more\n" +
+ "closely resembles the final output, as rendered in a browser. By\n" +
+ "allowing you to move the markup-related metadata out of the paragraph,\n" +
+ "you can add links without interrupting the narrative flow of your\n" +
+ "prose."),
+
+ raw("<h3 id=\"em\">Emphasis</h3>"),
+
+ para("Markdown treats asterisks (<code>*</code>) and underscores (<code>_</code>) as indicators of\n" +
+ "emphasis. Text wrapped with one <code>*</code> or <code>_</code> will be wrapped with an\n" +
+ "HTML <code><em></code> tag; double <code>*</code>'s or <code>_</code>'s will be wrapped with an HTML\n" +
+ "<code><strong></code> tag. E.g., this input:"),
+
+ verb("*single asterisks*\n",
+ "\n",
+ "_single underscores_\n",
+ "\n",
+ "**double asterisks**\n",
+ "\n",
+ "__double underscores__\n"),
+
+ para("will produce:"),
+
+ verb("<em>single asterisks</em>\n",
+ "\n",
+ "<em>single underscores</em>\n",
+ "\n",
+ "<strong>double asterisks</strong>\n",
+ "\n",
+ "<strong>double underscores</strong>\n"),
+
+ para("You can use whichever style you prefer; the lone restriction is that\n" +
+ "the same character must be used to open and close an emphasis span."),
+
+ para("Emphasis can be used in the middle of a word:"),
+
+ verb("un*fucking*believable\n"),
+
+ para("But if you surround an <code>*</code> or <code>_</code> with spaces, it'll be treated as a\n" +
+ "literal asterisk or underscore."),
+
+ para("To produce a literal asterisk or underscore at a position where it\n" +
+ "would otherwise be used as an emphasis delimiter, you can backslash\n" +
+ "escape it:"),
+
+ verb("\\*this text is surrounded by literal asterisks\\*\n"),
+
+ raw("<h3 id=\"code\">Code</h3>"),
+
+ para("To indicate a span of code, wrap it with backtick quotes (<code>`</code>).\n" +
+ "Unlike a pre-formatted code block, a code span indicates code within a\n" +
+ "normal paragraph. For example:"),
+
+ verb("Use the `printf()` function.\n"),
+
+ para("will produce:"),
+
+ verb("<p>Use the <code>printf()</code> function.</p>\n"),
+
+ para("To include a literal backtick character within a code span, you can use\n" +
+ "multiple backticks as the opening and closing delimiters:"),
+
+ verb("``There is a literal backtick (`) here.``\n"),
+
+ para("which will produce this:"),
+
+ verb("<p><code>There is a literal backtick (`) here.</code></p>\n"),
+
+ para("The backtick delimiters surrounding a code span may include spaces --\n" +
+ "one after the opening, one before the closing. This allows you to place\n" +
+ "literal backtick characters at the beginning or end of a code span:"),
+
+ verb("A single backtick in a code span: `` ` ``\n",
+ "\n",
+ "A backtick-delimited string in a code span: `` `foo` ``\n"),
+
+ para("will produce:"),
+
+ verb("<p>A single backtick in a code span: <code>`</code></p>\n",
+ "\n",
+ "<p>A backtick-delimited string in a code span: <code>`foo`</code></p>\n"),
+
+ para("With a code span, ampersands and angle brackets are encoded as HTML\n" +
+ "entities automatically, which makes it easy to include example HTML\n" +
+ "tags. Markdown will turn this:"),
+
+ verb("Please don't use any `<blink>` tags.\n"),
+
+ para("into:"),
+
+ verb("<p>Please don't use any <code>&lt;blink&gt;</code> tags.</p>\n"),
+
+ para("You can write this:"),
+
+ verb("`&#8212;` is the decimal-encoded equivalent of `&mdash;`.\n"),
+
+ para("to produce:"),
+
+ verb( "<p><code>&amp;#8212;</code> is the decimal-encoded\n",
+ "equivalent of <code>&amp;mdash;</code>.</p>\n"),
+
+ raw("<h3 id=\"img\">Images</h3>"),
+
+ para("Admittedly, it's fairly difficult to devise a \"natural\" syntax for\n" +
+ "placing images into a plain text document format."),
+
+ para("Markdown uses an image syntax that is intended to resemble the syntax\n" +
+ "for links, allowing for two styles: _inline_ and _reference_."),
+
+ para("Inline image syntax looks like this:"),
+
+ verb("![Alt text](/path/to/img.jpg)\n",
+ "\n",
+ "![Alt text](/path/to/img.jpg \"Optional title\")\n"),
+
+ para("That is:"),
+
+ list(:BULLET,
+ item(nil,
+ para("An exclamation mark: <code>!</code>;")),
+ item(nil,
+ para("followed by a set of square brackets, containing the <code>alt</code>\n" +
+ "attribute text for the image;")),
+ item(nil,
+ para("followed by a set of parentheses, containing the URL or path to\n" +
+ "the image, and an optional <code>title</code> attribute enclosed in double\n" +
+ "or single quotes."))),
+
+ para("Reference-style image syntax looks like this:"),
+
+ verb("![Alt text][id]\n"),
+
+ para("Where \"id\" is the name of a defined image reference. Image references\n" +
+ "are defined using syntax identical to link references:"),
+
+ verb("[id]: url/to/image \"Optional title attribute\"\n"),
+
+ para("As of this writing, Markdown has no syntax for specifying the\n" +
+ "dimensions of an image; if this is important to you, you can simply\n" +
+ "use regular HTML <code><img></code> tags."),
+
+ rule(1),
+
+ raw("<h2 id=\"misc\">Miscellaneous</h2>"),
+
+ raw("<h3 id=\"autolink\">Automatic Links</h3>"),
+
+ para("Markdown supports a shortcut style for creating \"automatic\" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:"),
+
+ verb("<http://example.com/>\n"),
+
+ para("Markdown will turn this into:"),
+
+ verb("<a href=\"http://example.com/\">http://example.com/</a>\n"),
+
+ para("Automatic links for email addresses work similarly, except that\n" +
+ "Markdown will also perform a bit of randomized decimal and hex\n" +
+ "entity-encoding to help obscure your address from address-harvesting\n" +
+ "spambots. For example, Markdown will turn this:"),
+
+ verb("<address@example.com>\n"),
+
+ para("into something like this:"),
+
+ verb("<a href=\"&#x6D;&#x61;i&#x6C;&#x74;&#x6F;:&#x61;&#x64;&#x64;&#x72;&#x65;\n",
+ "&#115;&#115;&#64;&#101;&#120;&#x61;&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;\n",
+ "&#109;\">&#x61;&#x64;&#x64;&#x72;&#x65;&#115;&#115;&#64;&#101;&#120;&#x61;\n",
+ "&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;&#109;</a>\n"),
+
+ para("which will render in a browser as a clickable link to \"address@example.com\"."),
+
+ para("(This sort of entity-encoding trick will indeed fool many, if not\n" +
+ "most, address-harvesting bots, but it definitely won't fool all of\n" +
+ "them. It's better than nothing, but an address published in this way\n" +
+ "will probably eventually start receiving spam.)"),
+
+ raw("<h3 id=\"backslash\">Backslash Escapes</h3>"),
+
+ para("Markdown allows you to use backslash escapes to generate literal\n" +
+ "characters which would otherwise have special meaning in Markdown's\n" +
+ "formatting syntax. For example, if you wanted to surround a word with\n" +
+ "literal asterisks (instead of an HTML <code><em></code> tag), you can backslashes\n" +
+ "before the asterisks, like this:"),
+
+ verb("\\*literal asterisks\\*\n"),
+
+ para("Markdown provides backslash escapes for the following characters:"),
+
+ verb("\\ backslash\n",
+ "` backtick\n",
+ "* asterisk\n",
+ "_ underscore\n",
+ "{} curly braces\n",
+ "[] square brackets\n",
+ "() parentheses\n",
+ "# hash mark\n",
+ "+ plus sign\n",
+ "- minus sign (hyphen)\n",
+ ". dot\n",
+ "! exclamation mark\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_nested_blockquotes
+ input = File.read "#{MARKDOWN_TEST_PATH}/Nested blockquotes.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ block(
+ para("foo"),
+ block(
+ para("bar")),
+ para("foo")))
+
+ assert_equal expected, doc
+ end
+
+ def test_ordered_and_unordered_lists
+ input = File.read "#{MARKDOWN_TEST_PATH}/Ordered and unordered lists.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ head(2, 'Unordered'),
+
+ para('Asterisks tight:'),
+ list(:BULLET,
+ item(nil, para("asterisk 1")),
+ item(nil, para("asterisk 2")),
+ item(nil, para("asterisk 3"))),
+ para('Asterisks loose:'),
+ list(:BULLET,
+ item(nil, para("asterisk 1")),
+ item(nil, para("asterisk 2")),
+ item(nil, para("asterisk 3"))),
+
+ rule(1),
+
+ para("Pluses tight:"),
+ list(:BULLET,
+ item(nil, para("Plus 1")),
+ item(nil, para("Plus 2")),
+ item(nil, para("Plus 3"))),
+ para("Pluses loose:"),
+ list(:BULLET,
+ item(nil, para("Plus 1")),
+ item(nil, para("Plus 2")),
+ item(nil, para("Plus 3"))),
+
+ rule(1),
+
+ para("Minuses tight:"),
+ list(:BULLET,
+ item(nil, para("Minus 1")),
+ item(nil, para("Minus 2")),
+ item(nil, para("Minus 3"))),
+ para("Minuses loose:"),
+ list(:BULLET,
+ item(nil, para("Minus 1")),
+ item(nil, para("Minus 2")),
+ item(nil, para("Minus 3"))),
+
+ head(2, "Ordered"),
+
+ para("Tight:"),
+ list(:NUMBER,
+ item(nil, para("First")),
+ item(nil, para("Second")),
+ item(nil, para("Third"))),
+ para("and:"),
+ list(:NUMBER,
+ item(nil, para("One")),
+ item(nil, para("Two")),
+ item(nil, para("Three"))),
+
+ para("Loose using tabs:"),
+ list(:NUMBER,
+ item(nil, para("First")),
+ item(nil, para("Second")),
+ item(nil, para("Third"))),
+ para("and using spaces:"),
+ list(:NUMBER,
+ item(nil, para("One")),
+ item(nil, para("Two")),
+ item(nil, para("Three"))),
+
+ para("Multiple paragraphs:"),
+ list(:NUMBER,
+ item(nil,
+ para("Item 1, graf one."),
+ para("Item 2. graf two. The quick brown fox " +
+ "jumped over the lazy dog's\nback.")),
+ item(nil, para("Item 2.")),
+ item(nil, para("Item 3."))),
+
+ head(2, "Nested"),
+ list(:BULLET,
+ item(nil,
+ para("Tab"),
+ list(:BULLET,
+ item(nil,
+ para("Tab"),
+ list(:BULLET,
+ item(nil,
+ para("Tab"))))))),
+
+ para("Here's another:"),
+ list(:NUMBER,
+ item(nil, para("First")),
+ item(nil, para("Second:"),
+ list(:BULLET,
+ item(nil, para("Fee")),
+ item(nil, para("Fie")),
+ item(nil, para("Foe")))),
+ item(nil, para("Third"))),
+
+ para("Same thing but with paragraphs:"),
+ list(:NUMBER,
+ item(nil, para("First")),
+ item(nil, para("Second:"),
+ list(:BULLET,
+ item(nil, para("Fee")),
+ item(nil, para("Fie")),
+ item(nil, para("Foe")))),
+ item(nil, para("Third"))),
+
+ para("This was an error in Markdown 1.0.1:"),
+ list(:BULLET,
+ item(nil,
+ para("this"),
+ list(:BULLET,
+ item(nil, para("sub"))),
+ para("that"))))
+
+ assert_equal expected, doc
+ end
+
+ def test_strong_and_em_together
+ input = File.read "#{MARKDOWN_TEST_PATH}/Strong and em together.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ para("<b><em>This is strong and em.</em></b>"),
+ para("So is <b>_this_</b> word."),
+ para("<b><em>This is strong and em.</em></b>"),
+ para("So is <b>_this_</b> word."))
+
+ assert_equal expected, doc
+ end
+
+ def test_tabs
+ input = File.read "#{MARKDOWN_TEST_PATH}/Tabs.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ list(:BULLET,
+ item(nil,
+ para("this is a list item\nindented with tabs")),
+ item(nil,
+ para("this is a list item\nindented with spaces"))),
+
+ para("Code:"),
+
+ verb("this code block is indented by one tab\n"),
+
+ para("And:"),
+
+ verb("\tthis code block is indented by two tabs\n"),
+
+ para("And:"),
+
+ verb(
+ "+\tthis is an example list item\n",
+ "\tindented with tabs\n",
+ "\n",
+ "+ this is an example list item\n",
+ " indented with spaces\n"))
+
+ assert_equal expected, doc
+ end
+
+ def test_tidyness
+ input = File.read "#{MARKDOWN_TEST_PATH}/Tidyness.text"
+
+ doc = @parser.parse input
+
+ expected =
+ doc(
+ block(
+ para("A list within a blockquote:"),
+ list(:BULLET,
+ item(nil, para("asterisk 1")),
+ item(nil, para("asterisk 2")),
+ item(nil, para("asterisk 3")))))
+
+ assert_equal expected, doc
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup.rb b/jni/ruby/test/rdoc/test_rdoc_markup.rb
new file mode 100644
index 0000000..5c28a2c
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup.rb
@@ -0,0 +1,95 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkup < RDoc::TestCase
+
+ def test_class_parse
+ expected = @RM::Document.new(
+ @RM::Paragraph.new('hello'))
+
+ assert_equal expected, RDoc::Markup.parse('hello')
+ end
+
+ def test_convert
+ str = <<-STR
+now is
+the time
+
+ hello
+ dave
+
+1. l1
+2. l2
+ STR
+
+ m = RDoc::Markup.new
+
+ tt = RDoc::Markup::ToTest.new m
+
+ out = m.convert str, tt
+
+ expected = [
+ "now is the time",
+ "\n",
+ " hello\n dave\n",
+ "1: ",
+ "l1",
+ "1: ",
+ "l2",
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_convert_custom_markup
+ str = <<-STR
+{stricken}
+ STR
+
+ m = RDoc::Markup.new
+ m.add_word_pair '{', '}', :STRIKE
+
+ tt = RDoc::Markup::ToTest.new nil, m
+ tt.add_tag :STRIKE, 'STRIKE ', ' STRIKE'
+
+ out = m.convert str, tt
+
+ expected = [
+ "STRIKE stricken STRIKE",
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_convert_document
+ doc = RDoc::Markup::Parser.parse <<-STR
+now is
+the time
+
+ hello
+ dave
+
+1. l1
+2. l2
+ STR
+
+ m = RDoc::Markup.new
+
+ tt = RDoc::Markup::ToTest.new m
+
+ out = m.convert doc, tt
+
+ expected = [
+ "now is the time",
+ "\n",
+ " hello\n dave\n",
+ "1: ",
+ "l1",
+ "1: ",
+ "l2",
+ ]
+
+ assert_equal expected, out
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_attribute_manager.rb b/jni/ruby/test/rdoc/test_rdoc_markup_attribute_manager.rb
new file mode 100644
index 0000000..6a085fe
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_attribute_manager.rb
@@ -0,0 +1,364 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupAttributeManager < RDoc::TestCase
+
+ def setup
+ super
+
+ @am = RDoc::Markup::AttributeManager.new
+
+ @bold_on = @am.changed_attribute_by_name([], [:BOLD])
+ @bold_off = @am.changed_attribute_by_name([:BOLD], [])
+
+ @tt_on = @am.changed_attribute_by_name([], [:TT])
+ @tt_off = @am.changed_attribute_by_name([:TT], [])
+
+ @em_on = @am.changed_attribute_by_name([], [:EM])
+ @em_off = @am.changed_attribute_by_name([:EM], [])
+
+ @bold_em_on = @am.changed_attribute_by_name([], [:BOLD] | [:EM])
+ @bold_em_off = @am.changed_attribute_by_name([:BOLD] | [:EM], [])
+
+ @em_then_bold = @am.changed_attribute_by_name([:EM], [:EM] | [:BOLD])
+
+ @em_to_bold = @am.changed_attribute_by_name([:EM], [:BOLD])
+
+ @am.add_word_pair("{", "}", :WOMBAT)
+ @wombat_on = @am.changed_attribute_by_name([], [:WOMBAT])
+ @wombat_off = @am.changed_attribute_by_name([:WOMBAT], [])
+
+ @klass = RDoc::Markup::AttributeManager
+ @formatter = RDoc::Markup::Formatter.new @rdoc.options
+ @formatter.add_tag :BOLD, '<B>', '</B>'
+ @formatter.add_tag :EM, '<EM>', '</EM>'
+ @formatter.add_tag :TT, '<CODE>', '</CODE>'
+ end
+
+ def crossref(text)
+ crossref_bitmap = @am.attributes.bitmap_for(:_SPECIAL_) |
+ @am.attributes.bitmap_for(:CROSSREF)
+
+ [ @am.changed_attribute_by_name([], [:CROSSREF, :_SPECIAL_]),
+ RDoc::Markup::Special.new(crossref_bitmap, text),
+ @am.changed_attribute_by_name([:CROSSREF, :_SPECIAL_], [])
+ ]
+ end
+
+ def test_adding
+ assert_equal(["cat ", @wombat_on, "and", @wombat_off, " dog" ],
+ @am.flow("cat {and} dog"))
+ #assert_equal(["cat {and} dog" ], @am.flow("cat \\{and} dog"))
+ end
+
+ def test_add_html_tag
+ @am.add_html("Test", :TEST)
+ tags = @am.html_tags
+ assert_equal(6, tags.size)
+ assert(tags.has_key?("test"))
+ end
+
+ def test_add_special
+ @am.add_special "WikiWord", :WIKIWORD
+ specials = @am.special
+
+ assert_equal 1, specials.size
+ assert specials.assoc "WikiWord"
+ end
+
+ def test_add_word_pair
+ @am.add_word_pair '%', '&', 'percent and'
+
+ assert @am.word_pair_map.include?(/(%)(\S+)(&)/)
+ assert @am.protectable.include?('%')
+ assert !@am.protectable.include?('&')
+ end
+
+ def test_add_word_pair_angle
+ e = assert_raises ArgumentError do
+ @am.add_word_pair '<', '>', 'angles'
+ end
+
+ assert_equal "Word flags may not start with '<'", e.message
+ end
+
+ def test_add_word_pair_invalid
+ assert_raises ArgumentError do
+ @am.add_word_pair("<", "<", :TEST)
+ end
+ end
+
+ def test_add_word_pair_map
+ @am.add_word_pair("x", "y", :TEST)
+
+ word_pair_map = @am.word_pair_map
+
+ assert_includes word_pair_map.keys.map { |r| r.source }, "(x)(\\S+)(y)"
+ end
+
+ def test_add_word_pair_matching
+ @am.add_word_pair '^', '^', 'caret'
+
+ assert @am.matching_word_pairs.include?('^')
+ assert @am.protectable.include?('^')
+ end
+
+ def test_basic
+ assert_equal(["cat"], @am.flow("cat"))
+
+ assert_equal(["cat ", @bold_on, "and", @bold_off, " dog"],
+ @am.flow("cat *and* dog"))
+
+ assert_equal(["cat ", @bold_on, "AND", @bold_off, " dog"],
+ @am.flow("cat *AND* dog"))
+
+ assert_equal(["cat ", @em_on, "And", @em_off, " dog"],
+ @am.flow("cat _And_ dog"))
+
+ assert_equal(["cat *and dog*"], @am.flow("cat *and dog*"))
+
+ assert_equal(["*cat and* dog"], @am.flow("*cat and* dog"))
+
+ assert_equal(["cat *and ", @bold_on, "dog", @bold_off],
+ @am.flow("cat *and *dog*"))
+
+ assert_equal(["cat ", @em_on, "and", @em_off, " dog"],
+ @am.flow("cat _and_ dog"))
+
+ assert_equal(["cat_and_dog"],
+ @am.flow("cat_and_dog"))
+
+ assert_equal(["cat ", @tt_on, "and", @tt_off, " dog"],
+ @am.flow("cat +and+ dog"))
+
+ assert_equal(["cat ", @tt_on, "X::Y", @tt_off, " dog"],
+ @am.flow("cat +X::Y+ dog"))
+
+ assert_equal(["cat ", @bold_on, "a_b_c", @bold_off, " dog"],
+ @am.flow("cat *a_b_c* dog"))
+
+ assert_equal(["cat __ dog"],
+ @am.flow("cat __ dog"))
+
+ assert_equal(["cat ", @em_on, "_", @em_off, " dog"],
+ @am.flow("cat ___ dog"))
+
+ assert_equal(["cat and ", @em_on, "5", @em_off, " dogs"],
+ @am.flow("cat and _5_ dogs"))
+ end
+
+ def test_bold
+ assert_equal [@bold_on, 'bold', @bold_off],
+ @am.flow("*bold*")
+
+ assert_equal [@bold_on, 'Bold:', @bold_off],
+ @am.flow("*Bold:*")
+
+ assert_equal [@bold_on, '\\bold', @bold_off],
+ @am.flow("*\\bold*")
+ end
+
+ def test_bold_html_escaped
+ assert_equal ['cat <b>dog</b>'], @am.flow('cat \<b>dog</b>')
+ end
+
+ def test_combined
+ assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
+ @am.flow("cat _and_ *dog*"))
+
+ assert_equal(["cat ", @em_on, "a__nd", @em_off, " ", @bold_on, "dog", @bold_off],
+ @am.flow("cat _a__nd_ *dog*"))
+ end
+
+ def test_convert_attrs
+ str = '+foo+'
+ attrs = RDoc::Markup::AttrSpan.new str.length
+
+ @am.convert_attrs str, attrs
+
+ assert_equal "\000foo\000", str
+
+ str = '+:foo:+'
+ attrs = RDoc::Markup::AttrSpan.new str.length
+
+ @am.convert_attrs str, attrs
+
+ assert_equal "\000:foo:\000", str
+
+ str = '+x-y+'
+ attrs = RDoc::Markup::AttrSpan.new str.length
+
+ @am.convert_attrs str, attrs
+
+ assert_equal "\000x-y\000", str
+ end
+
+ def test_convert_attrs_ignores_code
+ assert_equal 'foo <CODE>__send__</CODE> bar', output('foo <code>__send__</code> bar')
+ end
+
+ def test_convert_attrs_ignores_tt
+ assert_equal 'foo <CODE>__send__</CODE> bar', output('foo <tt>__send__</tt> bar')
+ end
+
+ def test_convert_attrs_preserves_double
+ assert_equal 'foo.__send__ :bar', output('foo.__send__ :bar')
+ assert_equal 'use __FILE__ to', output('use __FILE__ to')
+ end
+
+ def test_convert_attrs_does_not_ignore_after_tt
+ assert_equal 'the <CODE>IF:</CODE><EM>key</EM> directive', output('the <tt>IF:</tt>_key_ directive')
+ end
+
+ def test_escapes
+ assert_equal '<CODE>text</CODE>', output('<tt>text</tt>')
+ assert_equal '<tt>text</tt>', output('\\<tt>text</tt>')
+ assert_equal '<tt>', output('\\<tt>')
+ assert_equal '<CODE><tt></CODE>', output('<tt>\\<tt></tt>')
+ assert_equal '<CODE>\\<tt></CODE>', output('<tt>\\\\<tt></tt>')
+ assert_equal '<B>text</B>', output('*text*')
+ assert_equal '*text*', output('\\*text*')
+ assert_equal '\\', output('\\')
+ assert_equal '\\text', output('\\text')
+ assert_equal '\\\\text', output('\\\\text')
+ assert_equal 'text \\ text', output('text \\ text')
+
+ assert_equal 'and <CODE>\\s</CODE> matches space',
+ output('and <tt>\\s</tt> matches space')
+ assert_equal 'use <CODE><tt>text</CODE></tt> for code',
+ output('use <tt>\\<tt>text</tt></tt> for code')
+ assert_equal 'use <CODE><tt>text</tt></CODE> for code',
+ output('use <tt>\\<tt>text\\</tt></tt> for code')
+ assert_equal 'use <tt><tt>text</tt></tt> for code',
+ output('use \\<tt>\\<tt>text</tt></tt> for code')
+ assert_equal 'use <tt><CODE>text</CODE></tt> for code',
+ output('use \\<tt><tt>text</tt></tt> for code')
+ assert_equal 'use <CODE>+text+</CODE> for code',
+ output('use <tt>\\+text+</tt> for code')
+ assert_equal 'use <tt><CODE>text</CODE></tt> for code',
+ output('use \\<tt>+text+</tt> for code')
+ assert_equal 'illegal <tag>not</tag> changed',
+ output('illegal <tag>not</tag> changed')
+ assert_equal 'unhandled <p>tag</p> unchanged',
+ output('unhandled <p>tag</p> unchanged')
+ end
+
+ def test_html_like_em_bold
+ assert_equal ["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+ @am.flow("cat <i>and </i><b>dog</b>")
+ end
+
+ def test_html_like_em_bold_SGML
+ assert_equal ["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+ @am.flow("cat <i>and <b></i>dog</b>")
+ end
+
+ def test_html_like_em_bold_nested_1
+ assert_equal(["cat ", @bold_em_on, "and", @bold_em_off, " dog"],
+ @am.flow("cat <i><b>and</b></i> dog"))
+ end
+
+ def test_html_like_em_bold_nested_2
+ assert_equal ["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
+ @am.flow("cat <i>and <b>dog</b></i>")
+ end
+
+ def test_html_like_em_bold_nested_mixed_case
+ assert_equal ["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
+ @am.flow("cat <i>and <B>dog</B></I>")
+ end
+
+ def test_html_like_em_bold_mixed_case
+ assert_equal ["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
+ @am.flow("cat <i>and</i> <B>dog</b>")
+ end
+
+ def test_html_like_teletype
+ assert_equal ["cat ", @tt_on, "dog", @tt_off],
+ @am.flow("cat <tt>dog</Tt>")
+ end
+
+ def test_html_like_teletype_em_bold_SGML
+ assert_equal [@tt_on, "cat", @tt_off, " ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+ @am.flow("<tt>cat</tt> <i>and <b></i>dog</b>")
+ end
+
+ def test_initial_html
+ html_tags = @am.html_tags
+ assert html_tags.is_a?(Hash)
+ assert_equal(5, html_tags.size)
+ end
+
+ def test_initial_word_pairs
+ word_pairs = @am.matching_word_pairs
+ assert word_pairs.is_a?(Hash)
+ assert_equal(3, word_pairs.size)
+ end
+
+ def test_mask_protected_sequence
+ def @am.str() @str end
+ def @am.str=(str) @str = str end
+
+ @am.str = '<code>foo</code>'
+ @am.mask_protected_sequences
+
+ assert_equal "<code>foo</code>", @am.str
+
+ @am.str = '<code>foo\\</code>'
+ @am.mask_protected_sequences
+
+ assert_equal "<code>foo<\x04/code>", @am.str, 'escaped close'
+
+ @am.str = '<code>foo\\\\</code>'
+ @am.mask_protected_sequences
+
+ assert_equal "<code>foo\\</code>", @am.str, 'escaped backslash'
+ end
+
+ def test_protect
+ assert_equal(['cat \\ dog'],
+ @am.flow('cat \\ dog'))
+
+ assert_equal(["cat <tt>dog</Tt>"],
+ @am.flow("cat \\<tt>dog</Tt>"))
+
+ assert_equal(["cat ", @em_on, "and", @em_off, " <B>dog</b>"],
+ @am.flow("cat <i>and</i> \\<B>dog</b>"))
+
+ assert_equal(["*word* or <b>text</b>"],
+ @am.flow("\\*word* or \\<b>text</b>"))
+
+ assert_equal(["_cat_", @em_on, "dog", @em_off],
+ @am.flow("\\_cat_<i>dog</i>"))
+ end
+
+ def test_special
+ @am.add_special(RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF)
+
+ #
+ # The apostrophes in "cats'" and "dogs'" suppress the flagging of these
+ # words as potential cross-references, which is necessary for the unit
+ # tests. Unfortunately, the markup engine right now does not actually
+ # check whether a cross-reference is valid before flagging it.
+ #
+ assert_equal(["cats'"], @am.flow("cats'"))
+
+ assert_equal(["cats' ", crossref("#fred"), " dogs'"].flatten,
+ @am.flow("cats' #fred dogs'"))
+
+ assert_equal([crossref("#fred"), " dogs'"].flatten,
+ @am.flow("#fred dogs'"))
+
+ assert_equal(["cats' ", crossref("#fred")].flatten, @am.flow("cats' #fred"))
+ end
+
+ def test_tt_html
+ assert_equal [@tt_on, '"\n"', @tt_off],
+ @am.flow('<tt>"\n"</tt>')
+ end
+
+ def output str
+ @formatter.convert_flow @am.flow str
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_attributes.rb b/jni/ruby/test/rdoc/test_rdoc_markup_attributes.rb
new file mode 100644
index 0000000..636e0cc
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_attributes.rb
@@ -0,0 +1,39 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupAttributes < RDoc::TestCase
+
+ def setup
+ super
+
+ @as = RDoc::Markup::Attributes.new
+ end
+
+ def test_bitmap_for
+ assert_equal 2, @as.bitmap_for('two')
+ assert_equal 2, @as.bitmap_for('two')
+ assert_equal 4, @as.bitmap_for('three')
+ end
+
+ def test_as_string
+ @as.bitmap_for 'two'
+ @as.bitmap_for 'three'
+
+ assert_equal 'none', @as.as_string(0)
+ assert_equal '_SPECIAL_', @as.as_string(1)
+ assert_equal 'two', @as.as_string(2)
+ assert_equal '_SPECIAL_,two', @as.as_string(3)
+ end
+
+ def test_each_name_of
+ @as.bitmap_for 'two'
+ @as.bitmap_for 'three'
+
+ assert_equal %w[], @as.each_name_of(0).to_a
+ assert_equal %w[], @as.each_name_of(1).to_a
+ assert_equal %w[two], @as.each_name_of(2).to_a
+ assert_equal %w[three], @as.each_name_of(4).to_a
+ assert_equal %w[two three], @as.each_name_of(6).to_a
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_document.rb b/jni/ruby/test/rdoc/test_rdoc_markup_document.rb
new file mode 100644
index 0000000..718ae6d
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_document.rb
@@ -0,0 +1,207 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupDocument < RDoc::TestCase
+
+ def setup
+ super
+
+ @d = @RM::Document.new
+ end
+
+ def test_append
+ @d << @RM::Paragraph.new('hi')
+
+ expected = @RM::Document.new @RM::Paragraph.new('hi')
+
+ assert_equal expected, @d
+ end
+
+ def test_append_document
+ @d << @RM::Document.new
+
+ assert_empty @d
+
+ @d << @RM::Document.new(@RM::Paragraph.new('hi'))
+
+ expected = @RM::Document.new @RM::Paragraph.new('hi'), @RM::BlankLine.new
+
+ assert_equal expected, @d
+ end
+
+ def test_append_string
+ @d << ''
+
+ assert_empty @d
+
+ assert_raises ArgumentError do
+ @d << 'hi'
+ end
+ end
+
+ def test_concat
+ @d.concat [@RM::BlankLine.new, @RM::BlankLine.new]
+
+ refute_empty @d
+ end
+
+ def test_each
+ a = @RM::Document.new
+ b = @RM::Document.new(@RM::Paragraph.new('hi'))
+
+ @d.push a, b
+
+ assert_equal [a, b], @d.map { |sub_doc| sub_doc }
+ end
+
+ def test_empty_eh
+ assert_empty @d
+
+ @d << @RM::BlankLine.new
+
+ refute_empty @d
+ end
+
+ def test_empty_eh_document
+ d = @RM::Document.new @d
+
+ assert_empty d
+ end
+
+ def test_equals2
+ d2 = @RM::Document.new
+
+ assert_equal @d, d2
+
+ d2 << @RM::BlankLine.new
+
+ refute_equal @d, d2
+ end
+
+ def test_equals2_file
+ d2 = @RM::Document.new
+ d2.file = 'file.rb'
+
+ refute_equal @d, d2
+
+ @d.file = 'file.rb'
+
+ assert_equal @d, d2
+ end
+
+ def test_file_equals
+ @d.file = 'file.rb'
+
+ assert_equal 'file.rb', @d.file
+ end
+
+ def test_file_equals_top_level
+ @d.file = @store.add_file 'file.rb'
+
+ assert_equal 'file.rb', @d.file
+ end
+
+ def test_lt2
+ @d << @RM::BlankLine.new
+
+ refute_empty @d
+ end
+
+ def test_merge
+ original = @RM::Document.new @RM::Paragraph.new 'original'
+ original.file = 'file.rb'
+ root = @RM::Document.new original
+
+ replace = @RM::Document.new @RM::Paragraph.new 'replace'
+ replace.file = 'file.rb'
+
+ other = @RM::Document.new replace
+
+ result = root.merge other
+
+ inner = @RM::Document.new @RM::Paragraph.new 'replace'
+ inner.file = 'file.rb'
+ expected = @RM::Document.new inner
+
+ assert_equal expected, result
+ end
+
+ def test_merge_add
+ original = @RM::Document.new @RM::Paragraph.new 'original'
+ original.file = 'file.rb'
+ root = @RM::Document.new original
+
+ addition = @RM::Document.new @RM::Paragraph.new 'addition'
+ addition.file = 'other.rb'
+
+ other = @RM::Document.new addition
+
+ result = root.merge other
+
+ expected = @RM::Document.new original, addition
+
+ assert_equal expected, result
+ end
+
+ def test_merge_empty
+ original = @RM::Document.new
+ root = @RM::Document.new original
+
+ replace = @RM::Document.new @RM::Paragraph.new 'replace'
+ replace.file = 'file.rb'
+
+ other = @RM::Document.new replace
+
+ result = root.merge other
+
+ inner = @RM::Document.new @RM::Paragraph.new 'replace'
+ inner.file = 'file.rb'
+ expected = @RM::Document.new inner
+
+ assert_equal expected, result
+ end
+
+ def test_push
+ @d.push @RM::BlankLine.new, @RM::BlankLine.new
+
+ refute_empty @d
+ end
+
+ def test_table_of_contents
+ doc = @RM::Document.new(
+ @RM::Heading.new(1, 'A'),
+ @RM::Paragraph.new('B'),
+ @RM::Heading.new(2, 'C'),
+ @RM::Paragraph.new('D'),
+ @RM::Heading.new(1, 'E'),
+ @RM::Paragraph.new('F'))
+
+ expected = [
+ @RM::Heading.new(1, 'A'),
+ @RM::Heading.new(2, 'C'),
+ @RM::Heading.new(1, 'E'),
+ ]
+
+ assert_equal expected, doc.table_of_contents
+ end
+
+ def test_table_of_contents_omit_headings_below
+ document = doc(
+ head(1, 'A'),
+ para('B'),
+ head(2, 'C'),
+ para('D'),
+ head(1, 'E'),
+ para('F'))
+
+ document.omit_headings_below = 1
+
+ expected = [
+ head(1, 'A'),
+ head(1, 'E'),
+ ]
+
+ assert_equal expected, document.table_of_contents
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_formatter.rb b/jni/ruby/test/rdoc/test_rdoc_markup_formatter.rb
new file mode 100644
index 0000000..d01a42f
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_formatter.rb
@@ -0,0 +1,175 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupFormatter < RDoc::TestCase
+
+ class ToTest < RDoc::Markup::Formatter
+
+ def initialize markup
+ super nil, markup
+
+ add_tag :TT, '<code>', '</code>'
+ end
+
+ def accept_paragraph paragraph
+ @res << attributes(paragraph.text)
+ end
+
+ def attributes text
+ convert_flow @am.flow text.dup
+ end
+
+ def handle_special_CAPS special
+ "handled #{special.text}"
+ end
+
+ def start_accepting
+ @res = ""
+ end
+
+ def end_accepting
+ @res
+ end
+
+ end
+
+ def setup
+ super
+
+ @markup = @RM.new
+ @markup.add_special(/[A-Z]+/, :CAPS)
+
+ @attribute_manager = @markup.attribute_manager
+ @attributes = @attribute_manager.attributes
+
+ @to = ToTest.new @markup
+
+ @caps = @attributes.bitmap_for :CAPS
+ @special = @attributes.bitmap_for :_SPECIAL_
+ @tt = @attributes.bitmap_for :TT
+ end
+
+ def test_class_gen_relative_url
+ def gen(from, to)
+ RDoc::Markup::ToHtml.gen_relative_url from, to
+ end
+
+ assert_equal 'a.html', gen('a.html', 'a.html')
+ assert_equal 'b.html', gen('a.html', 'b.html')
+
+ assert_equal 'd.html', gen('a/c.html', 'a/d.html')
+ assert_equal '../a.html', gen('a/c.html', 'a.html')
+ assert_equal 'a/c.html', gen('a.html', 'a/c.html')
+ end
+
+ def special_names
+ @attribute_manager.special.map do |_, mask|
+ @attributes.as_string mask
+ end
+ end
+
+ def test_add_special_RDOCLINK
+ @to.add_special_RDOCLINK
+
+ assert_includes special_names, 'RDOCLINK'
+
+ def @to.handle_special_RDOCLINK special
+ "<#{special.text}>"
+ end
+
+ document = doc(para('{foo}[rdoc-label:bar].'))
+
+ formatted = document.accept @to
+
+ assert_equal '{foo}[<rdoc-label:bar>].', formatted
+ end
+
+ def test_add_special_TIDYLINK
+ @to.add_special_TIDYLINK
+
+ assert_includes special_names, 'TIDYLINK'
+
+ def @to.handle_special_TIDYLINK special
+ "<#{special.text}>"
+ end
+
+ document = doc(para('foo[rdoc-label:bar].'))
+
+ formatted = document.accept @to
+
+ assert_equal '<foo[rdoc-label:bar]>.', formatted
+
+ document = doc(para('{foo}[rdoc-label:bar].'))
+
+ formatted = document.accept @to
+
+ assert_equal '<{foo}[rdoc-label:bar]>.', formatted
+ end
+
+ def test_parse_url
+ scheme, url, id = @to.parse_url 'example/foo'
+
+ assert_equal 'http', scheme
+ assert_equal 'example/foo', url
+ assert_equal nil, id
+ end
+
+ def test_parse_url_anchor
+ scheme, url, id = @to.parse_url '#foottext-1'
+
+ assert_equal nil, scheme
+ assert_equal '#foottext-1', url
+ assert_equal nil, id
+ end
+
+ def test_parse_url_link
+ scheme, url, id = @to.parse_url 'link:README.txt'
+
+ assert_equal 'link', scheme
+ assert_equal 'README.txt', url
+ assert_equal nil, id
+ end
+
+ def test_parse_url_link_id
+ scheme, url, id = @to.parse_url 'link:README.txt#label-foo'
+
+ assert_equal 'link', scheme
+ assert_equal 'README.txt#label-foo', url
+ assert_equal nil, id
+ end
+
+ def test_parse_url_rdoc_label
+ scheme, url, id = @to.parse_url 'rdoc-label:foo'
+
+ assert_equal 'link', scheme
+ assert_equal '#foo', url
+ assert_equal nil, id
+
+ scheme, url, id = @to.parse_url 'rdoc-label:foo:bar'
+
+ assert_equal 'link', scheme
+ assert_equal '#foo', url
+ assert_equal ' id="bar"', id
+ end
+
+ def test_parse_url_scheme
+ scheme, url, id = @to.parse_url 'http://example/foo'
+
+ assert_equal 'http', scheme
+ assert_equal 'http://example/foo', url
+ assert_equal nil, id
+
+ scheme, url, id = @to.parse_url 'https://example/foo'
+
+ assert_equal 'https', scheme
+ assert_equal 'https://example/foo', url
+ assert_equal nil, id
+ end
+
+ def test_convert_tt_special
+ converted = @to.convert '<code>AAA</code>'
+
+ assert_equal '<code>AAA</code>', converted
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_hard_break.rb b/jni/ruby/test/rdoc/test_rdoc_markup_hard_break.rb
new file mode 100644
index 0000000..b9f7873
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_hard_break.rb
@@ -0,0 +1,31 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupHardBreak < RDoc::TestCase
+
+ def setup
+ super
+
+ @hb = RDoc::Markup::HardBreak.new
+ end
+
+ def test_accept
+ visitor = Object.new
+
+ def visitor.accept_hard_break(obj) @obj = obj end
+ def visitor.obj() @obj end
+
+ @hb.accept visitor
+
+ assert_same @hb, visitor.obj
+ end
+
+ def test_equals2
+ other = RDoc::Markup::HardBreak.new
+
+ assert_equal @hb, other
+
+ refute_equal @hb, Object.new
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_heading.rb b/jni/ruby/test/rdoc/test_rdoc_markup_heading.rb
new file mode 100644
index 0000000..26d4b5b
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_heading.rb
@@ -0,0 +1,29 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupHeading < RDoc::TestCase
+
+ def setup
+ super
+
+ @h = RDoc::Markup::Heading.new 1, 'Hello *Friend*!'
+ end
+
+ def test_aref
+ assert_equal 'label-Hello+Friend-21', @h.aref
+ end
+
+ def test_label
+ assert_equal 'label-Hello+Friend-21', @h.label
+ assert_equal 'label-Hello+Friend-21', @h.label(nil)
+
+ context = RDoc::NormalClass.new 'Foo'
+
+ assert_equal 'class-Foo-label-Hello+Friend-21', @h.label(context)
+ end
+
+ def test_plain_html
+ assert_equal 'Hello <strong>Friend</strong>!', @h.plain_html
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_include.rb b/jni/ruby/test/rdoc/test_rdoc_markup_include.rb
new file mode 100644
index 0000000..37a5b32
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_include.rb
@@ -0,0 +1,19 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupInclude < RDoc::TestCase
+
+ def setup
+ super
+
+ @include = @RM::Include.new 'file', [Dir.tmpdir]
+ end
+
+ def test_equals2
+ assert_equal @include, @RM::Include.new('file', [Dir.tmpdir])
+ refute_equal @include, @RM::Include.new('file', %w[.])
+ refute_equal @include, @RM::Include.new('other', [Dir.tmpdir])
+ refute_equal @include, Object.new
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_indented_paragraph.rb b/jni/ruby/test/rdoc/test_rdoc_markup_indented_paragraph.rb
new file mode 100644
index 0000000..d8dd795
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_indented_paragraph.rb
@@ -0,0 +1,53 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupIndentedParagraph < RDoc::TestCase
+
+ def setup
+ super
+
+ @IP = RDoc::Markup::IndentedParagraph
+ end
+
+ def test_initialize
+ ip = @IP.new 2, 'a', 'b'
+
+ assert_equal 2, ip.indent
+ assert_equal %w[a b], ip.parts
+ end
+
+ def test_accept
+ visitor = Object.new
+ def visitor.accept_indented_paragraph(obj) @obj = obj end
+ def visitor.obj() @obj end
+
+ paragraph = @IP.new 0
+
+ paragraph.accept visitor
+
+ assert_equal paragraph, visitor.obj
+ end
+
+ def test_equals2
+ one = @IP.new 1
+ two = @IP.new 2
+
+ assert_equal one, one
+ refute_equal one, two
+ end
+
+ def test_text
+ paragraph = @IP.new(2, 'hello', ' world')
+
+ assert_equal 'hello world', paragraph.text
+ end
+
+ def test_text_break
+ paragraph = @IP.new(2, 'hello', hard_break, 'world')
+
+ assert_equal 'helloworld', paragraph.text
+
+ assert_equal "hello\n world", paragraph.text("\n")
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_paragraph.rb b/jni/ruby/test/rdoc/test_rdoc_markup_paragraph.rb
new file mode 100644
index 0000000..8de1c3c
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_paragraph.rb
@@ -0,0 +1,32 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupParagraph < RDoc::TestCase
+
+ def test_accept
+ visitor = Object.new
+ def visitor.accept_paragraph(obj) @obj = obj end
+ def visitor.obj() @obj end
+
+ paragraph = RDoc::Markup::Paragraph.new
+
+ paragraph.accept visitor
+
+ assert_same paragraph, visitor.obj
+ end
+
+ def test_text
+ paragraph = para('hello', ' world')
+
+ assert_equal 'hello world', paragraph.text
+ end
+
+ def test_text_break
+ paragraph = para('hello', hard_break, 'world')
+
+ assert_equal 'helloworld', paragraph.text
+
+ assert_equal "hello\nworld", paragraph.text("\n")
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_parser.rb b/jni/ruby/test/rdoc/test_rdoc_markup_parser.rb
new file mode 100644
index 0000000..d27fb42
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_parser.rb
@@ -0,0 +1,1680 @@
+# coding: utf-8
+
+require 'rdoc/test_case'
+
+class TestRDocMarkupParser < RDoc::TestCase
+
+ def setup
+ super
+
+ @have_byteslice = ''.respond_to? :byteslice
+
+ @RMP = @RM::Parser
+ end
+
+ def test_build_heading
+ parser = @RMP.new
+
+ parser.tokens.replace [
+ [:TEXT, 'heading three', 4, 0],
+ [:NEWLINE, "\n", 17, 0],
+ ]
+
+ assert_equal @RM::Heading.new(3, 'heading three'), parser.build_heading(3)
+ end
+
+ def test_char_pos
+ parser = @RMP.new
+ s = parser.setup_scanner 'cät'
+
+ s.scan(/\S+/)
+
+ if @have_byteslice or @have_encoding then
+ assert_equal 3, parser.char_pos(s.pos)
+ else
+ assert_equal 4, parser.char_pos(s.pos)
+ end
+ end
+
+ def test_get
+ parser = util_parser
+
+ assert_equal [:HEADER, 1, 0, 0], parser.get
+
+ assert_equal 7, parser.tokens.length
+ end
+
+ def test_parse_bullet
+ str = <<-STR
+* l1
+* l2
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_bullet_utf_8
+ str = <<-STR
+* 新しい機能
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('新しい機能'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_bullet_verbatim_heading
+ str = <<-STR
+* l1
+ v
+
+= H
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1'),
+ @RM::Verbatim.new("v\n"))]),
+ @RM::Heading.new(1, 'H')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_bullet_heading
+ str = <<-STR
+* = l1
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Heading.new(1, 'l1'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_bullet_indent
+ str = <<-STR
+* l1
+ * l1.1
+* l2
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1'),
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1.1'))])),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_bullet_paragraph
+ str = <<-STR
+now is
+* l1
+* l2
+the time
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is'),
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2')),
+ ]),
+ @RM::Paragraph.new('the time'),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_bullet_multiline
+ str = <<-STR
+* l1
+ l1+
+* l2
+ STR
+
+ expected = [
+ list(:BULLET,
+ item(nil,
+ para('l1 ', 'l1+')),
+ item(nil,
+ para('l2')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_bullet_multiparagraph
+ str = <<-STR
+* l1
+
+ l1+
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1'),
+ @RM::BlankLine.new,
+ @RM::Paragraph.new('l1+')),
+ ]),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_bullet_indent_verbatim
+ str = <<-STR
+* l1
+ * l1.1
+ text
+ code
+ code
+
+ text
+* l2
+ STR
+
+ expected = [
+ list(:BULLET,
+ item(nil,
+ para('l1'),
+ list(:BULLET,
+ item(nil,
+ para('l1.1 ', 'text'),
+ verb("code\n", " code\n"),
+ para('text')))),
+ item(nil,
+ para('l2')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_dash
+ str = <<-STR
+- one
+- two
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('one')),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('two'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading
+ str = '= heading one'
+
+ expected = [
+ @RM::Heading.new(1, 'heading one')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_three
+ str = '=== heading three'
+
+ expected = [
+ @RM::Heading.new(3, 'heading three')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_bullet
+ str = '= * heading one'
+
+ expected = [
+ @RM::Heading.new(1, '* heading one')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_empty
+ str = <<-STR
+===
+* bullet
+ STR
+
+ expected = [
+ @RM::Heading.new(3, ''),
+ @RM::BlankLine.new,
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('bullet'))]),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_heading
+ str = '= ='
+
+ expected = [
+ @RM::Heading.new(1, '=')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_lalpha
+ str = '= b. heading one'
+
+ expected = [
+ @RM::Heading.new(1, 'b. heading one')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_label
+ str = '= [heading one]'
+
+ expected = [
+ @RM::Heading.new(1, '[heading one]')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_note
+ str = '= heading one::'
+
+ expected = [
+ @RM::Heading.new(1, 'heading one::')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_number
+ str = '= 5. heading one'
+
+ expected = [
+ @RM::Heading.new(1, '5. heading one')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_heading_ualpha
+ str = '= B. heading one'
+
+ expected = [
+ @RM::Heading.new(1, 'B. heading one')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_label
+ str = <<-STR
+[one] item one
+[two] item two
+ STR
+
+ expected = [
+ list(:LABEL,
+ item(%w[one],
+ para('item one')),
+ item(%w[two],
+ para('item two')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_label_bullet
+ str = <<-STR
+[cat] l1
+ * l1.1
+[dog] l2
+ STR
+
+ expected = [
+ list(:LABEL,
+ item(%w[cat],
+ para('l1'),
+ list(:BULLET,
+ item(nil,
+ para('l1.1')))),
+ item(%w[dog],
+ para('l2')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_label_multi_label
+ str = <<-STR
+[one]
+[two] some description
+ STR
+
+ expected = [
+ list(:LABEL,
+ item(%w[one two],
+ para('some description')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_label_multi_line
+ str = <<-STR
+[cat] l1
+ continuation
+[dog] l2
+ STR
+
+ expected = [
+ list(:LABEL,
+ item(%w[cat],
+ para('l1 ', 'continuation')),
+ item(%w[dog],
+ para('l2')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_label_newline
+ str = <<-STR
+[one]
+ item one
+[two]
+ item two
+ STR
+
+ expected = [
+ list(:LABEL,
+ item(%w[one],
+ para('item one')),
+ item(%w[two],
+ para('item two')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_lalpha
+ str = <<-STR
+a. l1
+b. l2
+ STR
+
+ expected = [
+ @RM::List.new(:LALPHA, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_lalpha_ualpha
+ str = <<-STR
+a. l1
+b. l2
+A. l3
+A. l4
+ STR
+
+ expected = [
+ @RM::List.new(:LALPHA, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2'))]),
+ @RM::List.new(:UALPHA, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l3')),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l4'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_lalpha_utf_8
+ str = <<-STR
+a. 新しい機能
+ STR
+
+ expected = [
+ @RM::List.new(:LALPHA, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('新しい機能'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_line_break
+ str = "now is\nthe time \nfor all"
+
+ expected = [
+ para('now is ', 'the time'),
+ blank_line,
+ para('for all')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_list_list_1
+ str = <<-STR
+10. para 1
+
+ [label 1]
+ para 1.1
+
+ code
+
+ para 1.2
+ STR
+
+ expected = [
+ list(:NUMBER,
+ item(nil,
+ para('para 1'),
+ blank_line,
+ list(:LABEL,
+ item(%w[label\ 1],
+ para('para 1.1'),
+ blank_line,
+ verb("code\n"),
+ para('para 1.2')))))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_list_list_2
+ str = <<-STR
+6. para
+
+ label 1:: text 1
+ label 2:: text 2
+ STR
+
+ expected = [
+ list(:NUMBER,
+ item(nil,
+ para('para'),
+ blank_line,
+ list(:NOTE,
+ item(%w[label\ 1],
+ para('text 1')),
+ item(%w[label\ 2],
+ para('text 2')))))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_list_verbatim
+ str = <<-STR
+* one
+ verb1
+ verb2
+* two
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('one'),
+ @RM::Verbatim.new("verb1\n", "verb2\n")),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('two'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_lists
+ str = <<-STR
+now is
+* l1
+1. n1
+2. n2
+* l2
+the time
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is'),
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1'))]),
+ @RM::List.new(:NUMBER, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('n1')),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('n2'))]),
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2'))]),
+ @RM::Paragraph.new('the time')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_note
+ str = <<-STR
+one:: item one
+two:: item two
+ STR
+
+ expected = [
+ list(:NOTE,
+ item(%w[one],
+ para('item one')),
+ item(%w[two],
+ para('item two')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_note_empty
+ str = <<-STR
+one::
+two::
+ STR
+
+ expected = [
+ list(:NOTE,
+ item(%w[one two],
+ blank_line))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_note_note
+ str = <<-STR
+one:: two::
+ STR
+
+ expected = [
+ list(:NOTE,
+ item(%w[one],
+ list(:NOTE,
+ item(%w[two],
+ blank_line))))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_number_bullet
+ str = <<-STR
+1. l1
+ * l1.1
+2. l2
+ STR
+
+ expected = [
+ list(:NUMBER,
+ item(nil,
+ para('l1'),
+ list(:BULLET,
+ item(nil,
+ para('l1.1')))),
+ item(nil,
+ para('l2')))]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_paragraph
+ str = <<-STR
+now is the time
+
+for all good men
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is the time'),
+ @RM::BlankLine.new,
+ @RM::Paragraph.new('for all good men')]
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_paragraph_multiline
+ str = "now is the time\nfor all good men"
+
+ expected = @RM::Paragraph.new 'now is the time ', 'for all good men'
+ assert_equal [expected], @RMP.parse(str).parts
+ end
+
+ def test_parse_paragraph_verbatim
+ str = <<-STR
+now is the time
+ code _line_ here
+for all good men
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is the time'),
+ @RM::Verbatim.new("code _line_ here\n"),
+ @RM::Paragraph.new('for all good men'),
+ ]
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_rule
+ str = <<-STR
+now is the time
+
+---
+
+for all good men
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is the time'),
+ @RM::BlankLine.new,
+ @RM::Rule.new(1),
+ @RM::BlankLine.new,
+ @RM::Paragraph.new('for all good men')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_ualpha
+ str = <<-STR
+A. l1
+B. l2
+ STR
+
+ expected = [
+ @RM::List.new(:UALPHA, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_trailing_cr
+ expected = [ @RM::Paragraph.new('Text') ]
+ # FIXME hangs the parser:
+ assert_equal expected, @RMP.parse("Text\r").parts
+ end
+
+ def test_parse_verbatim
+ str = <<-STR
+now is
+ code
+the time
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is'),
+ @RM::Verbatim.new("code\n"),
+ @RM::Paragraph.new('the time'),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_bullet
+ str = <<-STR
+ * blah
+ STR
+
+ expected = [
+ @RM::Verbatim.new("* blah\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_dash
+ str = <<-STR
+ - blah
+ STR
+
+ expected = [
+ @RM::Verbatim.new("- blah\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_fold
+ str = <<-STR
+now is
+ code
+
+
+ code1
+
+the time
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is'),
+ @RM::Verbatim.new("code\n", "\n", "code1\n"),
+ @RM::Paragraph.new('the time'),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_heading
+ str = <<-STR
+text
+ === heading three
+ STR
+
+ expected = [
+ @RM::Paragraph.new('text'),
+ @RM::Verbatim.new("=== heading three\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_heading2
+ str = "text\n code\n=== heading three"
+
+ expected = [
+ @RM::Paragraph.new('text'),
+ @RM::Verbatim.new("code\n"),
+ @RM::Heading.new(3, 'heading three')]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_label
+ str = <<-STR
+ [blah] blah
+ STR
+
+ expected = [
+ @RM::Verbatim.new("[blah] blah\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_lalpha
+ str = <<-STR
+ b. blah
+ STR
+
+ expected = [
+ @RM::Verbatim.new("b. blah\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_markup_example
+ str = <<-STR
+text
+ code
+ === heading three
+ STR
+
+ expected = [
+ @RM::Paragraph.new('text'),
+ @RM::Verbatim.new("code\n", "=== heading three\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_merge
+ str = <<-STR
+now is
+ code
+
+ code1
+the time
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is'),
+ @RM::Verbatim.new("code\n", "\n", "code1\n"),
+ @RM::Paragraph.new('the time'),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_merge2
+ str = <<-STR
+now is
+ code
+
+ code1
+
+ code2
+the time
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is'),
+ @RM::Verbatim.new("code\n", "\n", "code1\n", "\n", "code2\n"),
+ @RM::Paragraph.new('the time'),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_multiline
+ str = <<-STR
+now is
+ code
+ code1
+the time
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is'),
+ @RM::Verbatim.new("code\n", "code1\n"),
+ @RM::Paragraph.new('the time'),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_multilevel
+ str = <<-STR
+now is the time
+ code
+ more code
+for all good men
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is the time'),
+ @RM::Verbatim.new(" code\n",
+ "more code\n"),
+ @RM::Paragraph.new('for all good men'),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_note
+ str = <<-STR
+ blah:: blah
+ STR
+
+ expected = [
+ @RM::Verbatim.new("blah:: blah\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_number
+ str = <<-STR
+ 2. blah
+ STR
+
+ expected = [
+ @RM::Verbatim.new("2. blah\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_rule
+ str = <<-STR
+text
+
+ --- lib/blah.rb.orig
+ +++ lib/blah.rb
+ STR
+
+ expected = [
+ @RM::Paragraph.new('text'),
+ @RM::BlankLine.new,
+ @RM::Verbatim.new("--- lib/blah.rb.orig\n",
+ "+++ lib/blah.rb\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_rule2
+ str = <<-STR.chomp
+text
+
+ ---
+ STR
+
+ expected = [
+ @RM::Paragraph.new('text'),
+ @RM::BlankLine.new,
+ @RM::Verbatim.new("---")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_trim
+ str = <<-STR
+now is
+ code
+
+ code1
+
+the time
+ STR
+
+ expected = [
+ @RM::Paragraph.new('now is'),
+ @RM::Verbatim.new("code\n",
+ "\n",
+ "code1\n"),
+ @RM::Paragraph.new('the time'),
+ ]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_ualpha
+ str = <<-STR
+ B. blah
+ STR
+
+ expected = [
+ @RM::Verbatim.new("B. blah\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_whitespace
+ expected = [
+ @RM::Paragraph.new('hello'),
+ ]
+
+ assert_equal expected, @RMP.parse('hello').parts
+
+ expected = [
+ @RM::Verbatim.new('hello '),
+ ]
+
+ assert_equal expected, @RMP.parse(' hello ').parts
+
+ expected = [
+ @RM::Verbatim.new('hello '),
+ ]
+
+ assert_equal expected, @RMP.parse(' hello ').parts
+
+ expected = [
+ @RM::Paragraph.new('1'),
+ @RM::Verbatim.new("2\n", ' 3'),
+ ]
+
+ assert_equal expected, @RMP.parse("1\n 2\n 3").parts
+
+ expected = [
+ @RM::Verbatim.new("1\n",
+ " 2\n",
+ " 3"),
+ ]
+
+ assert_equal expected, @RMP.parse(" 1\n 2\n 3").parts
+
+ expected = [
+ @RM::Paragraph.new('1'),
+ @RM::Verbatim.new("2\n",
+ " 3\n"),
+ @RM::Paragraph.new('1'),
+ @RM::Verbatim.new('2'),
+ ]
+
+ assert_equal expected, @RMP.parse("1\n 2\n 3\n1\n 2").parts
+
+ expected = [
+ @RM::Verbatim.new("1\n",
+ " 2\n",
+ " 3\n",
+ "1\n",
+ ' 2'),
+ ]
+
+ assert_equal expected, @RMP.parse(" 1\n 2\n 3\n 1\n 2").parts
+
+ expected = [
+ @RM::Verbatim.new("1\n",
+ " 2\n",
+ "\n",
+ ' 3'),
+ ]
+
+ assert_equal expected, @RMP.parse(" 1\n 2\n\n 3").parts
+ end
+
+ def test_peek_token
+ parser = util_parser
+
+ assert_equal [:HEADER, 1, 0, 0], parser.peek_token
+
+ assert_equal 8, parser.tokens.length
+ end
+
+ def test_skip
+ parser = util_parser
+
+ assert_equal [:HEADER, 1, 0, 0], parser.skip(:HEADER)
+
+ assert_equal [:TEXT, 'Heading', 2, 0], parser.get
+
+ assert_equal [:NEWLINE, "\n", 9, 0], parser.peek_token
+
+ assert_raises RDoc::Markup::Parser::ParseError do
+ parser.skip :NONE
+ end
+
+ assert_equal [:NEWLINE, "\n", 9, 0], parser.peek_token
+
+ assert_equal nil, parser.skip(:NONE, false)
+
+ assert_equal [:NEWLINE, "\n", 9, 0], parser.peek_token
+ end
+
+ def test_tokenize_bullet
+ str = <<-STR
+* l1
+ STR
+
+ expected = [
+ [:BULLET, '*', 0, 0],
+ [:TEXT, 'l1', 2, 0],
+ [:NEWLINE, "\n", 4, 0],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_bullet_indent
+ str = <<-STR
+* l1
+ * l1.1
+ STR
+
+ expected = [
+ [:BULLET, '*', 0, 0],
+ [:TEXT, 'l1', 2, 0],
+ [:NEWLINE, "\n", 4, 0],
+ [:BULLET, '*', 2, 1],
+ [:TEXT, 'l1.1', 4, 1],
+ [:NEWLINE, "\n", 8, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_heading
+ str = <<-STR
+= Heading
+== Heading 2
+ STR
+
+ expected = [
+ [:HEADER, 1, 0, 0],
+ [:TEXT, 'Heading', 2, 0],
+ [:NEWLINE, "\n", 9, 0],
+ [:HEADER, 2, 0, 1],
+ [:TEXT, 'Heading 2', 3, 1],
+ [:NEWLINE, "\n", 12, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_heading_empty
+ str = <<-STR
+===
+* bullet
+ STR
+
+ expected = [
+ [:HEADER, 3, 0, 0],
+ [:NEWLINE, "\n", 3, 0],
+ [:BULLET, "*", 0, 1],
+ [:TEXT, "bullet", 2, 1],
+ [:NEWLINE, "\n", 8, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_heading_heading
+ str = <<-STR
+= =
+ STR
+
+ expected = [
+ [:HEADER, 1, 0, 0],
+ [:TEXT, '=', 2, 0],
+ [:NEWLINE, "\n", 3, 0],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_heading_no_space
+ str = <<-STR
+=Heading
+==Heading 2
+ STR
+
+ expected = [
+ [:HEADER, 1, 0, 0],
+ [:TEXT, 'Heading', 1, 0],
+ [:NEWLINE, "\n", 8, 0],
+ [:HEADER, 2, 0, 1],
+ [:TEXT, 'Heading 2', 2, 1],
+ [:NEWLINE, "\n", 11, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_label
+ str = <<-STR
+[cat] l1
+[dog] l1.1
+ STR
+
+ expected = [
+ [:LABEL, 'cat', 0, 0],
+ [:TEXT, 'l1', 6, 0],
+ [:NEWLINE, "\n", 8, 0],
+ [:LABEL, 'dog', 0, 1],
+ [:TEXT, 'l1.1', 6, 1],
+ [:NEWLINE, "\n", 10, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_label_note
+ str = <<-STR
+[label]
+ note::
+ STR
+
+ expected = [
+ [:LABEL, 'label', 0, 0],
+ [:NEWLINE, "\n", 7, 0],
+ [:NOTE, 'note', 2, 1],
+ [:NEWLINE, "\n", 8, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_label_newline
+ str = <<-STR
+[cat]
+ l1
+ STR
+
+ expected = [
+ [:LABEL, 'cat', 0, 0],
+ [:NEWLINE, "\n", 5, 0],
+ [:TEXT, 'l1', 2, 1],
+ [:NEWLINE, "\n", 4, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_label_newline_windows
+ str = <<-STR
+[cat]\r
+ l1\r
+ STR
+
+ expected = [
+ [:LABEL, 'cat', 0, 0],
+ [:NEWLINE, "\n", 6, 0],
+ [:TEXT, 'l1', 2, 1],
+ [:NEWLINE, "\n", 5, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_lalpha
+ str = <<-STR
+a. l1
+b. l1.1
+ STR
+
+ expected = [
+ [:LALPHA, 'a', 0, 0],
+ [:TEXT, 'l1', 3, 0],
+ [:NEWLINE, "\n", 5, 0],
+ [:LALPHA, 'b', 0, 1],
+ [:TEXT, 'l1.1', 3, 1],
+ [:NEWLINE, "\n", 7, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_line_break
+ str = "now is\nthe time \nfor all\n"
+
+ expected = [
+ [:TEXT, 'now is', 0, 0],
+ [:NEWLINE, "\n", 6, 0],
+ [:TEXT, 'the time', 0, 1],
+ [:BREAK, " ", 8, 1],
+ [:NEWLINE, "\n", 10, 1],
+ [:TEXT, 'for all', 0, 2],
+ [:NEWLINE, "\n", 7, 2],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_line_break_long
+ str = "now is\nthe time \nfor all\n"
+
+ expected = [
+ [:TEXT, 'now is', 0, 0],
+ [:NEWLINE, "\n", 6, 0],
+ [:TEXT, 'the time ', 0, 1],
+ [:BREAK, ' ', 9, 1],
+ [:NEWLINE, "\n", 11, 1],
+ [:TEXT, 'for all', 0, 2],
+ [:NEWLINE, "\n", 7, 2],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_line_break_no_short
+ str = "now is\nthe time \nfor all\n"
+
+ expected = [
+ [:TEXT, 'now is', 0, 0],
+ [:NEWLINE, "\n", 6, 0],
+ [:TEXT, 'the time ', 0, 1],
+ [:NEWLINE, "\n", 9, 1],
+ [:TEXT, 'for all', 0, 2],
+ [:NEWLINE, "\n", 7, 2],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_note
+ str = <<-STR
+cat:: l1
+dog:: l1.1
+ STR
+
+ expected = [
+ [:NOTE, 'cat', 0, 0],
+ [:TEXT, 'l1', 6, 0],
+ [:NEWLINE, "\n", 8, 0],
+ [:NOTE, 'dog', 0, 1],
+ [:TEXT, 'l1.1', 6, 1],
+ [:NEWLINE, "\n", 10, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_note_empty
+ str = <<-STR
+cat::
+dog::
+ STR
+
+ expected = [
+ [:NOTE, 'cat', 0, 0],
+ [:NEWLINE, "\n", 5, 0],
+ [:NOTE, 'dog', 0, 1],
+ [:NEWLINE, "\n", 5, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_note_newline
+ str = <<-STR
+cat::
+ l1
+ STR
+
+ expected = [
+ [:NOTE, 'cat', 0, 0],
+ [:NEWLINE, "\n", 5, 0],
+ [:TEXT, 'l1', 2, 1],
+ [:NEWLINE, "\n", 4, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_note_utf_8
+ skip 'Encoding not implemented' unless @have_encoding
+
+ str = <<-STR
+cät:: l1a
+ l1b
+døg:: l2a
+ l2b
+ STR
+
+ expected = [
+ [:NOTE, 'cät', 0, 0],
+ [:TEXT, 'l1a', 6, 0],
+ [:NEWLINE, "\n", 9, 0],
+ [:TEXT, 'l1b', 6, 1],
+ [:NEWLINE, "\n", 9, 1],
+ [:NOTE, 'døg', 0, 2],
+ [:TEXT, 'l2a', 6, 2],
+ [:NEWLINE, "\n", 9, 2],
+ [:TEXT, 'l2b', 6, 3],
+ [:NEWLINE, "\n", 9, 3],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_note_newline_windows
+ str = <<-STR
+cat::\r
+ l1\r
+ STR
+
+ expected = [
+ [:NOTE, 'cat', 0, 0],
+ [:NEWLINE, "\n", 6, 0],
+ [:TEXT, 'l1', 2, 1],
+ [:NEWLINE, "\n", 5, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_note_not
+ str = <<-STR
+Cat::Dog
+ STR
+
+ expected = [
+ [:TEXT, 'Cat::Dog', 0, 0],
+ [:NEWLINE, "\n", 8, 0],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_number
+ str = <<-STR
+1. l1
+2. l1.1
+ STR
+
+ expected = [
+ [:NUMBER, '1', 0, 0],
+ [:TEXT, 'l1', 3, 0],
+ [:NEWLINE, "\n", 5, 0],
+ [:NUMBER, '2', 0, 1],
+ [:TEXT, 'l1.1', 3, 1],
+ [:NEWLINE, "\n", 7, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_number_period
+ str = <<-STR
+1. blah blah blah
+ l.
+2. blah blah blah blah
+ d.
+ STR
+
+ expected = [
+ [:NUMBER, "1", 0, 0],
+ [:TEXT, "blah blah blah", 3, 0],
+ [:NEWLINE, "\n", 17, 0],
+
+ [:TEXT, "l.", 3, 1],
+ [:NEWLINE, "\n", 5, 1],
+
+ [:NUMBER, "2", 0, 2],
+ [:TEXT, "blah blah blah blah", 3, 2],
+ [:NEWLINE, "\n", 22, 2],
+
+ [:TEXT, "d.", 3, 3],
+ [:NEWLINE, "\n", 5, 3]
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_number_period_continue
+ str = <<-STR
+1. blah blah blah
+ l. more stuff
+2. blah blah blah blah
+ d. other stuff
+ STR
+
+ expected = [
+ [:NUMBER, "1", 0, 0],
+ [:TEXT, "blah blah blah", 3, 0],
+ [:NEWLINE, "\n", 17, 0],
+
+ [:LALPHA, "l", 3, 1],
+ [:TEXT, "more stuff", 7, 1],
+ [:NEWLINE, "\n", 17, 1],
+
+ [:NUMBER, "2", 0, 2],
+ [:TEXT, "blah blah blah blah", 3, 2],
+ [:NEWLINE, "\n", 22, 2],
+
+ [:LALPHA, "d", 3, 3],
+ [:TEXT, "other stuff", 6, 3],
+ [:NEWLINE, "\n", 17, 3]
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_paragraphs
+ str = <<-STR
+now is
+the time
+
+for all
+ STR
+
+ expected = [
+ [:TEXT, 'now is', 0, 0],
+ [:NEWLINE, "\n", 6, 0],
+ [:TEXT, 'the time', 0, 1],
+ [:NEWLINE, "\n", 8, 1],
+ [:NEWLINE, "\n", 0, 2],
+ [:TEXT, 'for all', 0, 3],
+ [:NEWLINE, "\n", 7, 3],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_rule
+ str = <<-STR
+---
+
+--- blah ---
+ STR
+
+ expected = [
+ [:RULE, 1, 0, 0],
+ [:NEWLINE, "\n", 3, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:TEXT, "--- blah ---", 0, 2],
+ [:NEWLINE, "\n", 12, 2],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_rule_windows
+ str = <<-STR
+---\r
+
+--- blah ---\r
+ STR
+
+ expected = [
+ [:RULE, 1, 0, 0],
+ [:NEWLINE, "\n", 4, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:TEXT, "--- blah ---", 0, 2],
+ [:NEWLINE, "\n", 13, 2],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_ualpha
+ str = <<-STR
+A. l1
+B. l1.1
+ STR
+
+ expected = [
+ [:UALPHA, 'A', 0, 0],
+ [:TEXT, 'l1', 3, 0],
+ [:NEWLINE, "\n", 5, 0],
+ [:UALPHA, 'B', 0, 1],
+ [:TEXT, 'l1.1', 3, 1],
+ [:NEWLINE, "\n", 7, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_verbatim_heading
+ str = <<-STR
+Example heading:
+
+ === heading three
+ STR
+
+ expected = [
+ [:TEXT, 'Example heading:', 0, 0],
+ [:NEWLINE, "\n", 16, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:HEADER, 3, 3, 2],
+ [:TEXT, 'heading three', 7, 2],
+ [:NEWLINE, "\n", 20, 2],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_verbatim_rule
+ str = <<-STR
+ Verbatim section here that is double-underlined
+ ===============================================
+ STR
+
+ expected = [
+ [:TEXT, 'Verbatim section here that is double-underlined', 2, 0],
+ [:NEWLINE, "\n", 49, 0],
+ [:HEADER, 47, 2, 1],
+ [:NEWLINE, "\n", 49, 1],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_tokenize_verbatim_rule_fancy
+ str = <<-STR
+ A
+ b
+ ===============================================
+ c
+ STR
+
+ expected = [
+ [:TEXT, 'A', 2, 0],
+ [:NEWLINE, "\n", 3, 0],
+ [:TEXT, 'b', 4, 1],
+ [:NEWLINE, "\n", 5, 1],
+ [:HEADER, 47, 2, 2],
+ [:NEWLINE, "\n", 49, 2],
+ [:TEXT, 'c', 4, 3],
+ [:NEWLINE, "\n", 5, 3],
+ ]
+
+ assert_equal expected, @RMP.tokenize(str)
+ end
+
+ def test_token_pos
+ parser = @RMP.new
+ s = parser.setup_scanner 'cät'
+
+ s.scan(/\S+/)
+
+ if @have_encoding or @have_byteslice then
+ assert_equal [3, 0], parser.token_pos(s.pos)
+ else
+ assert_equal [4, 0], parser.token_pos(s.pos)
+ end
+ end
+
+ # HACK move to Verbatim test case
+ def test_verbatim_normalize
+ v = @RM::Verbatim.new "foo\n", "\n", "\n", "bar\n"
+
+ v.normalize
+
+ assert_equal ["foo\n", "\n", "bar\n"], v.parts
+
+ v = @RM::Verbatim.new "foo\n", "\n"
+
+ v.normalize
+
+ assert_equal ["foo\n"], v.parts
+ end
+
+ def test_unget
+ parser = util_parser
+
+ parser.get
+
+ parser.unget
+
+ assert_equal [:HEADER, 1, 0, 0], parser.peek_token
+
+ assert_raises @RMP::Error do
+ parser.unget
+ end
+
+ assert_equal 8, parser.tokens.length
+ end
+
+ def util_parser
+ str = <<-STR
+= Heading
+
+Some text here
+some more text over here
+ STR
+
+ @parser = @RMP.new
+ @parser.tokenize str
+ @parser
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_pre_process.rb b/jni/ruby/test/rdoc/test_rdoc_markup_pre_process.rb
new file mode 100644
index 0000000..7cbe29c
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_pre_process.rb
@@ -0,0 +1,473 @@
+# coding: utf-8
+
+require 'rdoc/test_case'
+
+class TestRDocMarkupPreProcess < RDoc::TestCase
+
+ def setup
+ super
+
+ @tempfile = Tempfile.new 'test_rdoc_markup_pre_process'
+ @file_name = File.basename @tempfile.path
+ @dir = File.dirname @tempfile.path
+
+ @pp = RDoc::Markup::PreProcess.new @tempfile.path, [@dir, File.dirname(__FILE__)]
+ end
+
+ def teardown
+ super
+
+ @tempfile.close!
+ end
+
+ def test_class_register
+ RDoc::Markup::PreProcess.register 'for_test' do raise 'fail' end
+
+ assert_equal %w[for_test], RDoc::Markup::PreProcess.registered.keys
+ end
+
+ def test_class_post_process
+ RDoc::Markup::PreProcess.post_process do end
+
+ assert_equal 1, RDoc::Markup::PreProcess.post_processors.length
+ end
+
+ def test_include_file
+ @tempfile.write <<-INCLUDE
+# -*- mode: rdoc; coding: utf-8; fill-column: 74; -*-
+
+Regular expressions (<i>regexp</i>s) are patterns which describe the
+contents of a string.
+ INCLUDE
+
+ @tempfile.flush
+ @tempfile.rewind
+
+ content = @pp.include_file @file_name, '', nil
+
+ expected = <<-EXPECTED
+Regular expressions (<i>regexp</i>s) are patterns which describe the
+contents of a string.
+ EXPECTED
+
+ assert_equal expected, content
+ end
+
+ def test_include_file_encoding_incompatible
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ @tempfile.write <<-INCLUDE
+# -*- mode: rdoc; coding: utf-8; fill-column: 74; -*-
+
+ INCLUDE
+
+ @tempfile.flush
+ @tempfile.rewind
+
+ content = @pp.include_file @file_name, '', Encoding::US_ASCII
+
+ expected = "?\n"
+
+ assert_equal expected, content
+ end
+
+ def test_include_file_in_other_directory
+ content = nil
+ out, err = capture_io do
+ content = @pp.include_file "test.txt", '', nil
+ end
+
+ assert_empty out
+ assert_empty err
+
+ assert_equal "test file\n", content
+ end
+
+ def test_handle
+ text = "# :main: M\n"
+ out = @pp.handle text
+
+ assert_same out, text
+ assert_equal "#\n", text
+ end
+
+ def test_handle_comment
+ text = "# :main: M\n"
+ c = comment text
+
+ out = @pp.handle c
+
+ assert_same out, text
+ assert_equal "#\n", text
+ end
+
+ def test_handle_markup
+ c = comment ':markup: rd'
+
+ @pp.handle c
+
+ assert_equal 'rd', c.format
+ end
+
+ def test_handle_markup_empty
+ c = comment ':markup:'
+
+ @pp.handle c
+
+ assert_equal 'rdoc', c.format
+ end
+
+ def test_handle_post_process
+ cd = RDoc::CodeObject.new
+
+ RDoc::Markup::PreProcess.post_process do |text, code_object|
+ code_object.metadata[:stuff] = text
+
+ :junk
+ end
+
+ text = "# a b c\n"
+
+ out = @pp.handle text, cd
+
+ assert_same out, text
+ assert_equal "# a b c\n", text
+ assert_equal "# a b c\n", cd.metadata[:stuff]
+ end
+
+ def test_handle_unregistered
+ text = "# :x: y\n"
+ out = @pp.handle text
+
+ assert_same out, text
+ assert_equal "# :x: y\n", text
+ end
+
+ def test_handle_directive_blankline
+ result = @pp.handle_directive '#', 'arg', 'a, b'
+
+ assert_equal "#:arg: a, b\n", result
+ end
+
+ def test_handle_directive_downcase
+ method = RDoc::AnyMethod.new nil, 'm'
+
+ @pp.handle_directive '', 'ARG', 'a, b', method
+
+ assert_equal 'a, b', method.params
+ end
+
+ def test_handle_directive_arg
+ method = RDoc::AnyMethod.new nil, 'm'
+
+ @pp.handle_directive '', 'arg', 'a, b', method
+
+ assert_equal 'a, b', method.params
+ end
+
+ def test_handle_directive_arg_no_context
+ result = @pp.handle_directive '', 'arg', 'a, b', nil
+
+ assert_equal ":arg: a, b\n", result
+ end
+
+ def test_handle_directive_args
+ method = RDoc::AnyMethod.new nil, 'm'
+
+ @pp.handle_directive '', 'args', 'a, b', method
+
+ assert_equal 'a, b', method.params
+ end
+
+ def test_handle_directive_block
+ result = @pp.handle_directive '', 'x', 'y' do |directive, param|
+ ''
+ end
+
+ assert_empty result
+ end
+
+ def test_handle_directive_block_false
+ result = @pp.handle_directive '', 'x', 'y' do |directive, param|
+ false
+ end
+
+ assert_equal ":x: y\n", result
+ end
+
+ def test_handle_directive_block_nil
+ result = @pp.handle_directive '', 'x', 'y' do |directive, param|
+ nil
+ end
+
+ assert_equal ":x: y\n", result
+ end
+
+ def test_handle_directive_category
+ context = RDoc::Context.new
+ original_section = context.current_section
+
+ @pp.handle_directive '', 'category', 'other', context
+
+ refute_equal original_section, context.current_section
+ end
+
+ def test_handle_directive_doc
+ code_object = RDoc::CodeObject.new
+ code_object.document_self = false
+ code_object.force_documentation = false
+
+ @pp.handle_directive '', 'doc', nil, code_object
+
+ assert code_object.document_self
+ assert code_object.force_documentation
+ end
+
+ def test_handle_directive_doc_no_context
+ result = @pp.handle_directive '', 'doc', nil
+
+ assert_equal "\n", result
+ end
+
+ def test_handle_directive_enddoc
+ code_object = RDoc::CodeObject.new
+
+ @pp.handle_directive '', 'enddoc', nil, code_object
+
+ assert code_object.done_documenting
+ end
+
+ def test_handle_directive_include
+ @tempfile.write 'included'
+ @tempfile.flush
+
+ result = @pp.handle_directive '', 'include', @file_name
+
+ assert_equal 'included', result
+ end
+
+ def test_handle_directive_main
+ @pp.options = RDoc::Options.new
+
+ @pp.handle_directive '', 'main', 'M'
+
+ assert_equal 'M', @pp.options.main_page
+ end
+
+ def test_handle_directive_notnew
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ @pp.handle_directive '', 'notnew', nil, m
+
+ assert m.dont_rename_initialize
+ end
+
+ def test_handle_directive_not_new
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ @pp.handle_directive '', 'not_new', nil, m
+
+ assert m.dont_rename_initialize
+ end
+
+ def test_handle_directive_not_dash_new
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ @pp.handle_directive '', 'not-new', nil, m
+
+ assert m.dont_rename_initialize
+ end
+
+ def test_handle_directive_nodoc
+ code_object = RDoc::CodeObject.new
+ code_object.document_self = true
+ code_object.document_children = true
+
+ @pp.handle_directive '', 'nodoc', nil, code_object
+
+ refute code_object.document_self
+ assert code_object.document_children
+ end
+
+ def test_handle_directive_nodoc_all
+ code_object = RDoc::CodeObject.new
+ code_object.document_self = true
+ code_object.document_children = true
+
+ @pp.handle_directive '', 'nodoc', 'all', code_object
+
+ refute code_object.document_self
+ refute code_object.document_children
+ end
+
+ def test_handle_directive_nodoc_no_context
+ result = @pp.handle_directive '', 'nodoc', nil
+
+ assert_equal "\n", result
+ end
+
+ def test_handle_directive_registered
+ RDoc::Markup::PreProcess.register 'x'
+
+ result = @pp.handle_directive '', 'x', 'y'
+
+ assert_nil result
+
+ result = @pp.handle_directive '', 'x', 'y' do |directive, param|
+ false
+ end
+
+ assert_equal ":x: y\n", result
+
+ result = @pp.handle_directive '', 'x', 'y' do |directive, param|
+ ''
+ end
+
+ assert_equal '', result
+ end
+
+ def test_handle_directive_registered_block
+ called = nil
+
+ RDoc::Markup::PreProcess.register 'x' do |directive, param|
+ called = [directive, param]
+ 'blah'
+ end
+
+ result = @pp.handle_directive '', 'x', 'y'
+
+ assert_equal 'blah', result
+ assert_equal %w[x y], called
+ end
+
+ def test_handle_directive_registered_code_object
+ RDoc::Markup::PreProcess.register 'x'
+ code_object = RDoc::CodeObject.new
+
+ @pp.handle_directive '', 'x', 'y', code_object
+
+ assert_equal 'y', code_object.metadata['x']
+
+ code_object.metadata.clear
+
+ result = @pp.handle_directive '', 'x', 'y' do |directive, param|
+ false
+ end
+
+ assert_equal ":x: y\n", result
+ assert_empty code_object.metadata
+
+ result = @pp.handle_directive '', 'x', 'y' do |directive, param|
+ ''
+ end
+
+ assert_equal '', result
+ assert_empty code_object.metadata
+ end
+
+ def test_handle_directive_startdoc
+ code_object = RDoc::CodeObject.new
+ code_object.stop_doc
+ code_object.force_documentation = false
+
+ @pp.handle_directive '', 'startdoc', nil, code_object
+
+ assert code_object.document_self
+ assert code_object.document_children
+ assert code_object.force_documentation
+ end
+
+ def test_handle_directive_stopdoc
+ code_object = RDoc::CodeObject.new
+
+ @pp.handle_directive '', 'stopdoc', nil, code_object
+
+ refute code_object.document_self
+ refute code_object.document_children
+ end
+
+ def test_handle_directive_title
+ @pp.options = RDoc::Options.new
+
+ @pp.handle_directive '', 'title', 'T'
+
+ assert_equal 'T', @pp.options.title
+ end
+
+ def test_handle_directive_unhandled
+ code_object = RDoc::CodeObject.new
+
+ @pp.handle_directive '', 'x', 'y', code_object
+
+ assert_equal 'y', code_object.metadata['x']
+
+ code_object.metadata.clear
+
+ @pp.handle_directive '', 'x', '', code_object
+
+ assert_includes code_object.metadata, 'x'
+ end
+
+ def test_handle_directive_unhandled_block
+ code_object = RDoc::CodeObject.new
+
+ @pp.handle_directive '', 'x', 'y', code_object do
+ false
+ end
+
+ assert_empty code_object.metadata
+
+ @pp.handle_directive '', 'x', 'y', code_object do
+ nil
+ end
+
+ assert_equal 'y', code_object.metadata['x']
+
+ code_object.metadata.clear
+
+ @pp.handle_directive '', 'x', 'y', code_object do
+ ''
+ end
+
+ assert_empty code_object.metadata
+ end
+
+ def test_handle_directive_yield
+ method = RDoc::AnyMethod.new nil, 'm'
+ method.params = 'index, &block'
+
+ @pp.handle_directive '', 'yield', 'item', method
+
+ assert_equal 'item', method.block_params
+ assert_equal 'index', method.params
+ end
+
+ def test_handle_directive_yield_block_param
+ method = RDoc::AnyMethod.new nil, 'm'
+ method.params = '&block'
+
+ @pp.handle_directive '', 'yield', 'item', method
+
+ assert_equal 'item', method.block_params
+ assert_empty method.params
+ end
+
+ def test_handle_directive_yield_no_context
+ method = RDoc::AnyMethod.new nil, 'm'
+
+ @pp.handle_directive '', 'yield', 'item', method
+
+ assert_equal 'item', method.block_params
+ end
+
+ def test_handle_directive_yields
+ method = RDoc::AnyMethod.new nil, 'm'
+
+ @pp.handle_directive '', 'yields', 'item', method
+
+ assert_equal 'item', method.block_params
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_raw.rb b/jni/ruby/test/rdoc/test_rdoc_markup_raw.rb
new file mode 100644
index 0000000..43bfe0c
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_raw.rb
@@ -0,0 +1,22 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupRaw < RDoc::TestCase
+
+ def setup
+ super
+
+ @p = @RM::Raw.new
+ end
+
+ def test_push
+ @p.push 'hi', 'there'
+
+ assert_equal @RM::Raw.new('hi', 'there'), @p
+ end
+
+ def test_pretty_print
+ assert_equal '[raw: ]', mu_pp(@p)
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_ansi.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_ansi.rb
new file mode 100644
index 0000000..5afaf94
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_ansi.rb
@@ -0,0 +1,369 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToAnsi < RDoc::Markup::TextFormatterTestCase
+
+ add_visitor_tests
+ add_text_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToAnsi.new
+ end
+
+ def accept_blank_line
+ assert_equal "\e[0m\n", @to.res.join
+ end
+
+ def accept_block_quote
+ assert_equal "\e[0m> quote\n", @to.res.join
+ end
+
+ def accept_document
+ assert_equal "\e[0mhello\n", @to.res.join
+ end
+
+ def accept_heading
+ assert_equal "\e[0mHello\n", @to.res.join
+ end
+
+ def accept_list_end_bullet
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_label
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_lalpha
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_note
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_number
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_ualpha
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_item_end_bullet
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_label
+ assert_equal "\e[0mcat:\n", @to.res.join
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_lalpha
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 'b', @to.list_index.last
+ end
+
+ def accept_list_item_end_note
+ assert_equal "\e[0mcat:\n", @to.res.join
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_number
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 2, @to.list_index.last
+ end
+
+ def accept_list_item_end_ualpha
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 'B', @to.list_index.last
+ end
+
+ def accept_list_item_start_bullet
+ assert_equal %W"\e[0m", @to.res
+ assert_equal '* ', @to.prefix
+ end
+
+ def accept_list_item_start_label
+ assert_equal %W"\e[0m", @to.res
+ assert_equal "cat:\n ", @to.prefix
+
+ assert_equal 2, @to.indent
+ end
+
+ def accept_list_item_start_lalpha
+ assert_equal %W"\e[0m", @to.res
+ assert_equal 'a. ', @to.prefix
+
+ assert_equal 'a', @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_item_start_note
+ assert_equal %W"\e[0m", @to.res
+ assert_equal "cat:\n ", @to.prefix
+
+ assert_equal 2, @to.indent
+ end
+
+ def accept_list_item_start_number
+ assert_equal %W"\e[0m", @to.res
+ assert_equal '1. ', @to.prefix
+
+ assert_equal 1, @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_item_start_ualpha
+ assert_equal %W"\e[0m", @to.res
+ assert_equal 'A. ', @to.prefix
+
+ assert_equal 'A', @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_start_bullet
+ assert_equal "\e[0m", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:BULLET], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_label
+ assert_equal "\e[0m", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:LABEL], @to.list_type
+ assert_equal [2], @to.list_width
+ end
+
+ def accept_list_start_lalpha
+ assert_equal "\e[0m", @to.res.join
+ assert_equal ['a'], @to.list_index
+ assert_equal [:LALPHA], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_note
+ assert_equal "\e[0m", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:NOTE], @to.list_type
+ assert_equal [2], @to.list_width
+ end
+
+ def accept_list_start_number
+ assert_equal "\e[0m", @to.res.join
+ assert_equal [1], @to.list_index
+ assert_equal [:NUMBER], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_ualpha
+ assert_equal "\e[0m", @to.res.join
+ assert_equal ['A'], @to.list_index
+ assert_equal [:UALPHA], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_paragraph
+ assert_equal "\e[0mhi\n", @to.res.join
+ end
+
+ def accept_raw
+ raw = <<-RAW.rstrip
+\e[0m<table>
+<tr><th>Name<th>Count
+<tr><td>a<td>1
+<tr><td>b<td>2
+</table>
+ RAW
+
+ assert_equal raw, @to.res.join
+ end
+
+ def accept_rule
+ assert_equal "\e[0m#{'-' * 78}\n", @to.res.join
+ end
+
+ def accept_verbatim
+ assert_equal "\e[0m hi\n world\n\n", @to.res.join
+ end
+
+ def end_accepting
+ assert_equal "\e[0mhi", @to.end_accepting
+ end
+
+ def start_accepting
+ assert_equal 0, @to.indent
+ assert_equal %W"\e[0m", @to.res
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_heading_1
+ assert_equal "\e[0m\e[1;32mHello\e[m\n", @to.end_accepting
+ end
+
+ def accept_heading_2
+ assert_equal "\e[0m\e[4;32mHello\e[m\n", @to.end_accepting
+ end
+
+ def accept_heading_3
+ assert_equal "\e[0m\e[32mHello\e[m\n", @to.end_accepting
+ end
+
+ def accept_heading_4
+ assert_equal "\e[0mHello\n", @to.end_accepting
+ end
+
+ def accept_heading_indent
+ assert_equal "\e[0m \e[1;32mHello\e[m\n", @to.end_accepting
+ end
+
+ def accept_heading_b
+ assert_equal "\e[0m\e[1;32m\e[1mHello\e[m\e[m\n", @to.end_accepting
+ end
+
+ def accept_heading_suppressed_crossref
+ assert_equal "\e[0m\e[1;32mHello\e[m\n", @to.end_accepting
+ end
+
+ def accept_list_item_start_note_2
+ assert_equal "\e[0m\e[7mteletype\e[m:\n teletype description\n\n",
+ @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_description
+ assert_equal "\e[0mlabel:\n description one\n\n description two\n\n",
+ @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_label
+ assert_equal "\e[0mone\ntwo:\n two headers\n\n",
+ @to.res.join
+ end
+
+ def accept_paragraph_b
+ assert_equal "\e[0mreg \e[1mbold words\e[m reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_br
+ assert_equal "\e[0mone\ntwo\n", @to.end_accepting
+ end
+
+ def accept_paragraph_break
+ assert_equal "\e[0mhello\nworld\n", @to.end_accepting
+ end
+
+ def accept_paragraph_i
+ assert_equal "\e[0mreg \e[4mitalic words\e[m reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_indent
+ expected = <<-EXPECTED
+\e[0m words words words words words words words words words words words words
+ words words words words words words words words words words words words
+ words words words words words words
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def accept_paragraph_plus
+ assert_equal "\e[0mreg \e[7mteletype\e[m reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_star
+ assert_equal "\e[0mreg \e[1mbold\e[m reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_underscore
+ assert_equal "\e[0mreg \e[4mitalic\e[m reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_wrap
+ expected = <<-EXPECTED
+\e[0mwords words words words words words words words words words words words words
+words words words words words words words words words words words words words
+words words words words
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def accept_rule_indent
+ assert_equal "\e[0m #{'-' * 75}\n", @to.end_accepting
+ end
+
+ def accept_verbatim_indent
+ assert_equal "\e[0m hi\n world\n\n", @to.end_accepting
+ end
+
+ def accept_verbatim_big_indent
+ assert_equal "\e[0m hi\n world\n\n", @to.end_accepting
+ end
+
+ def list_nested
+ expected = <<-EXPECTED
+\e[0m* l1
+ * l1.1
+* l2
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def list_verbatim
+ expected = <<-EXPECTED # HACK overblown
+\e[0m* list stuff
+
+ * list
+ with
+
+ second
+
+ 1. indented
+ 2. numbered
+
+ third
+
+ * second
+
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ # functional test
+ def test_convert_list_note
+ note_list = <<-NOTE_LIST
+foo ::
+bar ::
+ hi
+ NOTE_LIST
+
+ expected = <<-EXPECTED
+\e[0mfoo
+bar:
+ hi
+
+ EXPECTED
+
+ assert_equal expected, @to.convert(note_list)
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_bs.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_bs.rb
new file mode 100644
index 0000000..f2e6352
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_bs.rb
@@ -0,0 +1,366 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToBs < RDoc::Markup::TextFormatterTestCase
+
+ add_visitor_tests
+ add_text_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToBs.new
+ end
+
+ def accept_blank_line
+ assert_equal "\n", @to.res.join
+ end
+
+ def accept_block_quote
+ assert_equal "> quote\n", @to.res.join
+ end
+
+ def accept_document
+ assert_equal "hello\n", @to.res.join
+ end
+
+ def accept_heading
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "===== H\bHe\bel\bll\blo\bo\n", @to.res.join
+ end
+
+ def accept_list_end_bullet
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_label
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_lalpha
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_note
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_number
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_ualpha
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_item_end_bullet
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_label
+ assert_equal "cat:\n", @to.res.join
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_lalpha
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 'b', @to.list_index.last
+ end
+
+ def accept_list_item_end_note
+ assert_equal "cat:\n", @to.res.join
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_number
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 2, @to.list_index.last
+ end
+
+ def accept_list_item_end_ualpha
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 'B', @to.list_index.last
+ end
+
+ def accept_list_item_start_bullet
+ assert_equal [''], @to.res
+ assert_equal '* ', @to.prefix
+ end
+
+ def accept_list_item_start_label
+ assert_equal [''], @to.res
+ assert_equal "cat:\n ", @to.prefix
+
+ assert_equal 2, @to.indent
+ end
+
+ def accept_list_item_start_lalpha
+ assert_equal [''], @to.res
+ assert_equal 'a. ', @to.prefix
+
+ assert_equal 'a', @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_item_start_note
+ assert_equal [''], @to.res
+ assert_equal "cat:\n ", @to.prefix
+
+ assert_equal 2, @to.indent
+ end
+
+ def accept_list_item_start_number
+ assert_equal [''], @to.res
+ assert_equal '1. ', @to.prefix
+
+ assert_equal 1, @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_item_start_ualpha
+ assert_equal [''], @to.res
+ assert_equal 'A. ', @to.prefix
+
+ assert_equal 'A', @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_start_bullet
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:BULLET], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_label
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:LABEL], @to.list_type
+ assert_equal [2], @to.list_width
+ end
+
+ def accept_list_start_lalpha
+ assert_equal "", @to.res.join
+ assert_equal ['a'], @to.list_index
+ assert_equal [:LALPHA], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_note
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:NOTE], @to.list_type
+ assert_equal [2], @to.list_width
+ end
+
+ def accept_list_start_number
+ assert_equal "", @to.res.join
+ assert_equal [1], @to.list_index
+ assert_equal [:NUMBER], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_ualpha
+ assert_equal "", @to.res.join
+ assert_equal ['A'], @to.list_index
+ assert_equal [:UALPHA], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_paragraph
+ assert_equal "hi\n", @to.res.join
+ end
+
+ def accept_raw
+ raw = <<-RAW.rstrip
+<table>
+<tr><th>Name<th>Count
+<tr><td>a<td>1
+<tr><td>b<td>2
+</table>
+ RAW
+
+ assert_equal raw, @to.res.join
+ end
+
+ def accept_rule
+ assert_equal "#{'-' * 78}\n", @to.res.join
+ end
+
+ def accept_verbatim
+ assert_equal " hi\n world\n\n", @to.res.join
+ end
+
+ def end_accepting
+ assert_equal "hi", @to.end_accepting
+ end
+
+ def start_accepting
+ assert_equal 0, @to.indent
+ assert_equal [''], @to.res
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_heading_1
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "= H\bHe\bel\bll\blo\bo\n", @to.end_accepting
+ end
+
+ def accept_heading_2
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "== H\bHe\bel\bll\blo\bo\n", @to.end_accepting
+ end
+
+ def accept_heading_3
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "=== H\bHe\bel\bll\blo\bo\n", @to.end_accepting
+ end
+
+ def accept_heading_4
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "==== H\bHe\bel\bll\blo\bo\n", @to.end_accepting
+ end
+
+ def accept_heading_indent
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal " = H\bHe\bel\bll\blo\bo\n", @to.end_accepting
+ end
+
+ def accept_heading_b
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "= H\bHe\bel\bll\blo\bo\n", @to.end_accepting
+ end
+
+ def accept_heading_suppressed_crossref
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "= H\bHe\bel\bll\blo\bo\n", @to.end_accepting
+ end
+
+ def accept_list_item_start_note_2
+ assert_equal "teletype:\n teletype description\n\n", @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_description
+ assert_equal "label:\n description one\n\n description two\n\n",
+ @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_label
+ assert_equal "one\ntwo:\n two headers\n\n", @to.res.join
+ end
+
+ def accept_paragraph_b
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "reg b\bbo\bol\bld\bd \b w\bwo\bor\brd\bds\bs reg\n",
+ @to.end_accepting
+ end
+
+ def accept_paragraph_br
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "one\ntwo\n", @to.end_accepting
+ end
+
+ def accept_paragraph_break
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "hello\nworld\n",
+ @to.end_accepting
+ end
+
+ def accept_paragraph_i
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "reg _\bi_\bt_\ba_\bl_\bi_\bc_\b _\bw_\bo_\br_\bd_\bs reg\n",
+ @to.end_accepting
+ end
+
+ def accept_paragraph_indent
+ expected = <<-EXPECTED
+ words words words words words words words words words words words words
+ words words words words words words words words words words words words
+ words words words words words words
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def accept_paragraph_plus
+ assert_equal "reg teletype reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_star
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "reg b\bbo\bol\bld\bd reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_underscore
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+ assert_equal "reg _\bi_\bt_\ba_\bl_\bi_\bc reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_wrap
+ expected = <<-EXPECTED
+words words words words words words words words words words words words words
+words words words words words words words words words words words words words
+words words words words
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def accept_rule_indent
+ assert_equal " #{'-' * 75}\n", @to.end_accepting
+ end
+
+ def accept_verbatim_indent
+ assert_equal " hi\n world\n\n", @to.end_accepting
+ end
+
+ def accept_verbatim_big_indent
+ assert_equal " hi\n world\n\n", @to.end_accepting
+ end
+
+ def list_nested
+ expected = <<-EXPECTED
+* l1
+ * l1.1
+* l2
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def list_verbatim
+ expected = <<-EXPECTED # HACK overblown
+* list stuff
+
+ * list
+ with
+
+ second
+
+ 1. indented
+ 2. numbered
+
+ third
+
+ * second
+
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_html.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_html.rb
new file mode 100644
index 0000000..1e4b84f
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_html.rb
@@ -0,0 +1,663 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
+
+ add_visitor_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToHtml.new @options
+ end
+
+ def accept_blank_line
+ assert_empty @to.res.join
+ end
+
+ def accept_block_quote
+ assert_equal "\n<blockquote>\n<p>quote</p>\n</blockquote>\n", @to.res.join
+ end
+
+ def accept_document
+ assert_equal "\n<p>hello</p>\n", @to.res.join
+ end
+
+ def accept_heading
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+ expected = "\n<h5 id=\"label-Hello\">Hello#{links}</h5>\n"
+
+ assert_equal expected, @to.res.join
+ end
+
+ def accept_heading_1
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h1 id=\"label-Hello\">Hello#{links}</h1>\n", @to.res.join
+ end
+
+ def accept_heading_2
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h2 id=\"label-Hello\">Hello#{links}</h2>\n", @to.res.join
+ end
+
+ def accept_heading_3
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h3 id=\"label-Hello\">Hello#{links}</h3>\n", @to.res.join
+ end
+
+ def accept_heading_4
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h4 id=\"label-Hello\">Hello#{links}</h4>\n", @to.res.join
+ end
+
+ def accept_heading_b
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+ inner = "<strong>Hello</strong>"
+
+ assert_equal "\n<h1 id=\"label-Hello\">#{inner}#{links}</h1>\n",
+ @to.res.join
+ end
+
+ def accept_heading_suppressed_crossref
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h1 id=\"label-Hello\">Hello#{links}</h1>\n", @to.res.join
+ end
+
+ def accept_list_end_bullet
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "<ul></ul>\n", @to.res.join
+ end
+
+ def accept_list_end_label
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "<dl class=\"rdoc-list label-list\"></dl>\n", @to.res.join
+ end
+
+ def accept_list_end_lalpha
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "<ol style=\"list-style-type: lower-alpha\"></ol>\n", @to.res.join
+ end
+
+ def accept_list_end_number
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "<ol></ol>\n", @to.res.join
+ end
+
+ def accept_list_end_note
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "<dl class=\"rdoc-list note-list\"></dl>\n", @to.res.join
+ end
+
+ def accept_list_end_ualpha
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "<ol style=\"list-style-type: upper-alpha\"></ol>\n", @to.res.join
+ end
+
+ def accept_list_item_end_bullet
+ assert_equal %w[</li>], @to.in_list_entry
+ end
+
+ def accept_list_item_end_label
+ assert_equal %w[</dd>], @to.in_list_entry
+ end
+
+ def accept_list_item_end_lalpha
+ assert_equal %w[</li>], @to.in_list_entry
+ end
+
+ def accept_list_item_end_note
+ assert_equal %w[</dd>], @to.in_list_entry
+ end
+
+ def accept_list_item_end_number
+ assert_equal %w[</li>], @to.in_list_entry
+ end
+
+ def accept_list_item_end_ualpha
+ assert_equal %w[</li>], @to.in_list_entry
+ end
+
+ def accept_list_item_start_bullet
+ assert_equal "<ul><li>", @to.res.join
+ end
+
+ def accept_list_item_start_label
+ assert_equal "<dl class=\"rdoc-list label-list\"><dt>cat\n<dd>", @to.res.join
+ end
+
+ def accept_list_item_start_lalpha
+ assert_equal "<ol style=\"list-style-type: lower-alpha\"><li>", @to.res.join
+ end
+
+ def accept_list_item_start_note
+ assert_equal "<dl class=\"rdoc-list note-list\"><dt>cat\n<dd>",
+ @to.res.join
+ end
+
+ def accept_list_item_start_note_2
+ expected = <<-EXPECTED
+<dl class="rdoc-list note-list"><dt><code>teletype</code>
+<dd>
+<p>teletype description</p>
+</dd></dl>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_description
+ expected = <<-EXPECTED
+<dl class="rdoc-list note-list"><dt>label
+<dd>
+<p>description one</p>
+</dd><dd>
+<p>description two</p>
+</dd></dl>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_label
+ expected = <<-EXPECTED
+<dl class="rdoc-list note-list"><dt>one
+<dt>two
+<dd>
+<p>two headers</p>
+</dd></dl>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def accept_list_item_start_number
+ assert_equal "<ol><li>", @to.res.join
+ end
+
+ def accept_list_item_start_ualpha
+ assert_equal "<ol style=\"list-style-type: upper-alpha\"><li>", @to.res.join
+ end
+
+ def accept_list_start_bullet
+ assert_equal [:BULLET], @to.list
+ assert_equal [false], @to.in_list_entry
+
+ assert_equal "<ul>", @to.res.join
+ end
+
+ def accept_list_start_label
+ assert_equal [:LABEL], @to.list
+ assert_equal [false], @to.in_list_entry
+
+ assert_equal '<dl class="rdoc-list label-list">', @to.res.join
+ end
+
+ def accept_list_start_lalpha
+ assert_equal [:LALPHA], @to.list
+ assert_equal [false], @to.in_list_entry
+
+ assert_equal "<ol style=\"list-style-type: lower-alpha\">", @to.res.join
+ end
+
+ def accept_list_start_note
+ assert_equal [:NOTE], @to.list
+ assert_equal [false], @to.in_list_entry
+
+ assert_equal "<dl class=\"rdoc-list note-list\">", @to.res.join
+ end
+
+ def accept_list_start_number
+ assert_equal [:NUMBER], @to.list
+ assert_equal [false], @to.in_list_entry
+
+ assert_equal "<ol>", @to.res.join
+ end
+
+ def accept_list_start_ualpha
+ assert_equal [:UALPHA], @to.list
+ assert_equal [false], @to.in_list_entry
+
+ assert_equal "<ol style=\"list-style-type: upper-alpha\">", @to.res.join
+ end
+
+ def accept_paragraph
+ assert_equal "\n<p>hi</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_b
+ assert_equal "\n<p>reg <strong>bold words</strong> reg</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_br
+ assert_equal "\n<p>one<br>two</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_break
+ assert_equal "\n<p>hello<br> world</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_i
+ assert_equal "\n<p>reg <em>italic words</em> reg</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_plus
+ assert_equal "\n<p>reg <code>teletype</code> reg</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_star
+ assert_equal "\n<p>reg <strong>bold</strong> reg</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_underscore
+ assert_equal "\n<p>reg <em>italic</em> reg</p>\n", @to.res.join
+ end
+
+ def accept_raw
+ raw = <<-RAW.rstrip
+<table>
+<tr><th>Name<th>Count
+<tr><td>a<td>1
+<tr><td>b<td>2
+</table>
+ RAW
+
+ assert_equal raw, @to.res.join
+ end
+
+ def accept_rule
+ assert_equal "<hr>\n", @to.res.join
+ end
+
+ def accept_verbatim
+ assert_equal "\n<pre class=\"ruby\"><span class=\"ruby-identifier\">hi</span>\n <span class=\"ruby-identifier\">world</span>\n</pre>\n", @to.res.join
+ end
+
+ def end_accepting
+ assert_equal 'hi', @to.end_accepting
+ end
+
+ def start_accepting
+ assert_equal [], @to.res
+ assert_equal [], @to.in_list_entry
+ assert_equal [], @to.list
+ end
+
+ def list_nested
+ expected = <<-EXPECTED
+<ul><li>
+<p>l1</p>
+<ul><li>
+<p>l1.1</p>
+</li></ul>
+</li><li>
+<p>l2</p>
+</li></ul>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def list_verbatim
+ expected = <<-EXPECTED
+<ul><li>
+<p>list stuff</p>
+
+<pre>* list
+ with
+
+ second
+
+ 1. indented
+ 2. numbered
+
+ third
+
+* second</pre>
+</li></ul>
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def test_accept_heading_7
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(7, 'Hello')
+
+ links = '<span><a href="#label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h6 id=\"label-Hello\">Hello#{links}</h6>\n", @to.res.join
+ end
+
+ def test_accept_heading_aref_class
+ @to.code_object = RDoc::NormalClass.new 'Foo'
+ @to.start_accepting
+
+ @to.accept_heading head(1, 'Hello')
+
+ links = '<span><a href="#class-Foo-label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h1 id=\"class-Foo-label-Hello\">Hello#{links}</h1>\n",
+ @to.res.join
+ end
+
+ def test_accept_heading_aref_method
+ @to.code_object = RDoc::AnyMethod.new nil, 'foo'
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ links = '<span><a href="#method-i-foo-label-Hello">&para;</a> ' +
+ '<a href="#top">&uarr;</a></span>'
+
+ assert_equal "\n<h1 id=\"method-i-foo-label-Hello\">Hello#{links}</h1>\n",
+ @to.res.join
+ end
+
+ def test_accept_heading_pipe
+ @options.pipe = true
+
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ assert_equal "\n<h1 id=\"label-Hello\">Hello</h1>\n", @to.res.join
+ end
+
+ def test_accept_paragraph_newline
+ @to.start_accepting
+
+ @to.accept_paragraph para("hello\n", "world\n")
+
+ assert_equal "\n<p>hello world</p>\n", @to.res.join
+ end
+
+ def test_accept_heading_output_decoration
+ @options.output_decoration = false
+
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ assert_equal "\n<h1>Hello<span><a href=\"#label-Hello\">&para;</a> <a href=\"#top\">&uarr;</a></span></h1>\n", @to.res.join
+ end
+
+ def test_accept_heading_output_decoration_with_pipe
+ @options.pipe = true
+ @options.output_decoration = false
+
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ assert_equal "\n<h1>Hello</h1>\n", @to.res.join
+ end
+
+ def test_accept_verbatim_parseable
+ verb = @RM::Verbatim.new("class C\n", "end\n")
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ expected = <<-EXPECTED
+
+<pre class="ruby"><span class="ruby-keyword">class</span> <span class="ruby-constant">C</span>
+<span class="ruby-keyword">end</span>
+</pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def test_accept_verbatim_parseable_error
+ verb = @RM::Verbatim.new("a % 09 # => blah\n")
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ inner = CGI.escapeHTML "a % 09 # => blah"
+
+ expected = <<-EXPECTED
+
+<pre>#{inner}</pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def test_accept_verbatim_pipe
+ @options.pipe = true
+
+ verb = @RM::Verbatim.new("1 + 1\n")
+ verb.format = :ruby
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ expected = <<-EXPECTED
+
+<pre><code>1 + 1
+</code></pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def test_accept_verbatim_ruby
+ verb = @RM::Verbatim.new("1 + 1\n")
+ verb.format = :ruby
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ expected = <<-EXPECTED
+
+<pre class="ruby"><span class="ruby-value">1</span> <span class="ruby-operator">+</span> <span class="ruby-value">1</span>
+</pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ end
+
+ def test_convert_string
+ assert_equal '&lt;&gt;', @to.convert_string('<>')
+ end
+
+ def test_convert_HYPERLINK_irc
+ result = @to.convert 'irc://irc.freenode.net/#ruby-lang'
+
+ assert_equal "\n<p><a href=\"irc://irc.freenode.net/#ruby-lang\">irc.freenode.net/#ruby-lang</a></p>\n", result
+ end
+
+ def test_convert_RDOCLINK_label_label
+ result = @to.convert 'rdoc-label:label-One'
+
+ assert_equal "\n<p><a href=\"#label-One\">One</a></p>\n", result
+ end
+
+ def test_convert_RDOCLINK_label_foottext
+ result = @to.convert 'rdoc-label:foottext-1'
+
+ assert_equal "\n<p><a href=\"#foottext-1\">1</a></p>\n", result
+ end
+
+ def test_convert_RDOCLINK_label_footmark
+ result = @to.convert 'rdoc-label:footmark-1'
+
+ assert_equal "\n<p><a href=\"#footmark-1\">1</a></p>\n", result
+ end
+
+ def test_convert_RDOCLINK_ref
+ result = @to.convert 'rdoc-ref:C'
+
+ assert_equal "\n<p>C</p>\n", result
+ end
+
+ def test_convert_TIDYLINK_footnote
+ result = @to.convert 'text{*1}[rdoc-label:foottext-1:footmark-1]'
+
+ assert_equal "\n<p>text<sup><a id=\"footmark-1\" href=\"#foottext-1\">1</a></sup></p>\n", result
+ end
+
+ def test_convert_TIDYLINK_multiple
+ result = @to.convert '{a}[http://example] {b}[http://example]'
+
+ expected = <<-EXPECTED
+
+<p><a href=\"http://example\">a</a> <a href=\"http://example\">b</a></p>
+ EXPECTED
+
+ assert_equal expected, result
+ end
+
+ def test_convert_TIDYLINK_image
+ result =
+ @to.convert '{rdoc-image:path/to/image.jpg}[http://example.com]'
+
+ expected =
+ "\n<p><a href=\"http://example.com\"><img src=\"path/to/image.jpg\"></a></p>\n"
+
+ assert_equal expected, result
+ end
+
+ def test_convert_TIDYLINK_rdoc_label
+ result = @to.convert '{foo}[rdoc-label:foottext-1]'
+
+ assert_equal "\n<p><a href=\"#foottext-1\">foo</a></p>\n", result
+ end
+
+ def test_convert_TIDYLINK_irc
+ result = @to.convert '{ruby-lang}[irc://irc.freenode.net/#ruby-lang]'
+
+ assert_equal "\n<p><a href=\"irc://irc.freenode.net/#ruby-lang\">ruby-lang</a></p>\n", result
+ end
+
+ def test_gen_url
+ assert_equal '<a href="example">example</a>',
+ @to.gen_url('link:example', 'example')
+ end
+
+ def test_gen_url_rdoc_label
+ assert_equal '<a href="#foottext-1">example</a>',
+ @to.gen_url('rdoc-label:foottext-1', 'example')
+ end
+
+ def test_gen_url_rdoc_label_id
+ assert_equal '<sup><a id="footmark-1" href="#foottext-1">example</a></sup>',
+ @to.gen_url('rdoc-label:foottext-1:footmark-1', 'example')
+ end
+
+ def test_gen_url_image_url
+ assert_equal '<img src="http://example.com/image.png" />', @to.gen_url('http://example.com/image.png', 'ignored')
+ end
+
+ def test_gen_url_ssl_image_url
+ assert_equal '<img src="https://example.com/image.png" />', @to.gen_url('https://example.com/image.png', 'ignored')
+ end
+
+ def test_handle_special_HYPERLINK_link
+ special = RDoc::Markup::Special.new 0, 'link:README.txt'
+
+ link = @to.handle_special_HYPERLINK special
+
+ assert_equal '<a href="README.txt">README.txt</a>', link
+ end
+
+ def test_handle_special_HYPERLINK_irc
+ special = RDoc::Markup::Special.new 0, 'irc://irc.freenode.net/#ruby-lang'
+
+ link = @to.handle_special_HYPERLINK special
+
+ assert_equal '<a href="irc://irc.freenode.net/#ruby-lang">irc.freenode.net/#ruby-lang</a>', link
+ end
+
+ def test_list_verbatim_2
+ str = "* one\n verb1\n verb2\n* two\n"
+
+ expected = <<-EXPECTED
+<ul><li>
+<p>one</p>
+
+<pre class=\"ruby\"><span class=\"ruby-identifier\">verb1</span>
+<span class=\"ruby-identifier\">verb2</span>
+</pre>
+</li><li>
+<p>two</p>
+</li></ul>
+ EXPECTED
+
+ assert_equal expected, @m.convert(str, @to)
+ end
+
+ def test_parseable_eh
+ valid_syntax = [
+ 'def x() end',
+ 'def x; end',
+ 'class C; end',
+ "module M end",
+ 'a # => blah',
+ 'x { |y| nil }',
+ 'x do |y| nil end',
+ '# only a comment',
+ 'require "foo"',
+ 'cls="foo"'
+ ]
+ invalid_syntax = [
+ 'def x end',
+ 'class C end',
+ 'class C < end',
+ 'module M < C end',
+ 'a=># blah',
+ 'x { |y| ... }',
+ 'x do |y| ... end',
+ '// only a comment',
+ '<% require "foo" %>',
+ 'class="foo"'
+ ]
+ valid_syntax.each do |t|
+ assert @to.parseable?(t), "valid syntax considered invalid: #{t}"
+ end
+ invalid_syntax.each do |t|
+ refute @to.parseable?(t), "invalid syntax considered valid: #{t}"
+ end
+ end
+
+ def test_to_html
+ assert_equal "\n<p><code>--</code></p>\n", util_format("<tt>--</tt>")
+ end
+
+ def util_format text
+ paragraph = RDoc::Markup::Paragraph.new text
+
+ @to.start_accepting
+ @to.accept_paragraph paragraph
+ @to.end_accepting
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_html_crossref.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_html_crossref.rb
new file mode 100644
index 0000000..872daea
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_html_crossref.rb
@@ -0,0 +1,225 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocMarkupToHtmlCrossref < XrefTestCase
+
+ def setup
+ super
+
+ @options.hyperlink_all = true
+
+ @to = RDoc::Markup::ToHtmlCrossref.new @options, 'index.html', @c1
+ end
+
+ def test_convert_CROSSREF
+ result = @to.convert 'C1'
+
+ assert_equal para("<a href=\"C1.html\">C1</a>"), result
+ end
+
+ def test_convert_CROSSREF_label
+ result = @to.convert 'C1@foo'
+ assert_equal para("<a href=\"C1.html#label-foo\">foo at C1</a>"), result
+
+ result = @to.convert 'C1#m@foo'
+ assert_equal para("<a href=\"C1.html#method-i-m-label-foo\">foo at C1#m</a>"),
+ result
+ end
+
+ def test_convert_CROSSREF_label_period
+ result = @to.convert 'C1@foo.'
+ assert_equal para("<a href=\"C1.html#label-foo\">foo at C1</a>."), result
+ end
+
+ def test_convert_CROSSREF_label_space
+ result = @to.convert 'C1@foo+bar'
+ assert_equal para("<a href=\"C1.html#label-foo+bar\">foo bar at C1</a>"),
+ result
+ end
+
+ def test_convert_CROSSREF_section
+ @c1.add_section 'Section'
+
+ result = @to.convert 'C1@Section'
+ assert_equal para("<a href=\"C1.html#Section\">Section at C1</a>"), result
+ end
+
+ def test_convert_RDOCLINK_rdoc_ref
+ result = @to.convert 'rdoc-ref:C1'
+
+ assert_equal para("<a href=\"C1.html\">C1</a>"), result
+ end
+
+ def test_convert_RDOCLINK_rdoc_ref_method
+ result = @to.convert 'rdoc-ref:C1#m'
+
+ assert_equal para("<a href=\"C1.html#method-i-m\">#m</a>"), result
+ end
+
+ def test_convert_RDOCLINK_rdoc_ref_method_label
+ result = @to.convert 'rdoc-ref:C1#m@foo'
+
+ assert_equal para("<a href=\"C1.html#method-i-m-label-foo\">foo at C1#m</a>"),
+ result, 'rdoc-ref:C1#m@foo'
+ end
+
+ def test_convert_RDOCLINK_rdoc_ref_method_percent
+ m = @c1.add_method RDoc::AnyMethod.new nil, '%'
+ m.singleton = false
+
+ result = @to.convert 'rdoc-ref:C1#%'
+
+ assert_equal para("<a href=\"C1.html#method-i-25\">#%</a>"), result
+
+ m.singleton = true
+
+ result = @to.convert 'rdoc-ref:C1::%'
+
+ assert_equal para("<a href=\"C1.html#method-c-25\">::%</a>"), result
+ end
+
+ def test_convert_RDOCLINK_rdoc_ref_method_percent_label
+ m = @c1.add_method RDoc::AnyMethod.new nil, '%'
+ m.singleton = false
+
+ result = @to.convert 'rdoc-ref:C1#%@f'
+
+ assert_equal para("<a href=\"C1.html#method-i-25-label-f\">f at C1#%</a>"),
+ result
+
+ m.singleton = true
+
+ result = @to.convert 'rdoc-ref:C1::%@f'
+
+ assert_equal para("<a href=\"C1.html#method-c-25-label-f\">f at C1::%</a>"),
+ result
+ end
+
+ def test_convert_RDOCLINK_rdoc_ref_label
+ result = @to.convert 'rdoc-ref:C1@foo'
+
+ assert_equal para("<a href=\"C1.html#label-foo\">foo at C1</a>"), result,
+ 'rdoc-ref:C1@foo'
+ end
+
+ def test_gen_url
+ assert_equal '<a href="C1.html">Some class</a>',
+ @to.gen_url('rdoc-ref:C1', 'Some class')
+
+ assert_equal '<a href="http://example">HTTP example</a>',
+ @to.gen_url('http://example', 'HTTP example')
+ end
+
+ def test_handle_special_CROSSREF
+ assert_equal "<a href=\"C2/C3.html\">C2::C3</a>", SPECIAL('C2::C3')
+ end
+
+ def test_handle_special_CROSSREF_label
+ assert_equal "<a href=\"C1.html#method-i-m-label-foo\">foo at C1#m</a>",
+ SPECIAL('C1#m@foo')
+ end
+
+ def test_handle_special_CROSSREF_show_hash_false
+ @to.show_hash = false
+
+ assert_equal "<a href=\"C1.html#method-i-m\">m</a>",
+ SPECIAL('#m')
+ end
+
+ def test_handle_special_HYPERLINK_rdoc
+ readme = @store.add_file 'README.txt'
+ readme.parser = RDoc::Parser::Simple
+
+ @to = RDoc::Markup::ToHtmlCrossref.new @options, 'C2.html', @c2
+
+ link = @to.handle_special_HYPERLINK hyper 'C2::C3'
+
+ assert_equal '<a href="C2/C3.html">C2::C3</a>', link
+
+ link = @to.handle_special_HYPERLINK hyper 'C4'
+
+ assert_equal '<a href="C4.html">C4</a>', link
+
+ link = @to.handle_special_HYPERLINK hyper 'README.txt'
+
+ assert_equal '<a href="README_txt.html">README.txt</a>', link
+ end
+
+ def test_handle_special_TIDYLINK_rdoc
+ readme = @store.add_file 'README.txt'
+ readme.parser = RDoc::Parser::Simple
+
+ @to = RDoc::Markup::ToHtmlCrossref.new @options, 'C2.html', @c2
+
+ link = @to.handle_special_TIDYLINK tidy 'C2::C3'
+
+ assert_equal '<a href="C2/C3.html">tidy</a>', link
+
+ link = @to.handle_special_TIDYLINK tidy 'C4'
+
+ assert_equal '<a href="C4.html">tidy</a>', link
+
+ link = @to.handle_special_TIDYLINK tidy 'C1#m'
+
+ assert_equal '<a href="C1.html#method-i-m">tidy</a>', link
+
+ link = @to.handle_special_TIDYLINK tidy 'README.txt'
+
+ assert_equal '<a href="README_txt.html">tidy</a>', link
+ end
+
+ def test_handle_special_TIDYLINK_label
+ link = @to.handle_special_TIDYLINK tidy 'C1#m@foo'
+
+ assert_equal "<a href=\"C1.html#method-i-m-label-foo\">tidy</a>",
+ link, 'C1#m@foo'
+ end
+
+ def test_to_html_CROSSREF_email
+ @options.hyperlink_all = false
+
+ @to = RDoc::Markup::ToHtmlCrossref.new @options, 'index.html', @c1
+
+ result = @to.to_html 'first.last@example.com'
+
+ assert_equal 'first.last@example.com', result
+ end
+
+ def test_to_html_CROSSREF_email_hyperlink_all
+ result = @to.to_html 'first.last@example.com'
+
+ assert_equal 'first.last@example.com', result
+ end
+
+ def test_link
+ assert_equal 'n', @to.link('n', 'n')
+
+ assert_equal '<a href="C1.html#method-c-m">::m</a>', @to.link('m', 'm')
+ end
+
+ def test_link_class_method_full
+ assert_equal '<a href="Parent.html#method-c-m">Parent.m</a>',
+ @to.link('Parent::m', 'Parent::m')
+ end
+
+ def para text
+ "\n<p>#{text}</p>\n"
+ end
+
+ def SPECIAL text
+ @to.handle_special_CROSSREF special text
+ end
+
+ def hyper reference
+ RDoc::Markup::Special.new 0, "rdoc-ref:#{reference}"
+ end
+
+ def special text
+ RDoc::Markup::Special.new 0, text
+ end
+
+ def tidy reference
+ RDoc::Markup::Special.new 0, "{tidy}[rdoc-ref:#{reference}]"
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_html_snippet.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_html_snippet.rb
new file mode 100644
index 0000000..f861db1
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_html_snippet.rb
@@ -0,0 +1,711 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToHtmlSnippet < RDoc::Markup::FormatterTestCase
+
+ add_visitor_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToHtmlSnippet.new @options, 100, 100
+ @ellipsis = @to.to_html '...'
+ end
+
+ def accept_blank_line
+ assert_empty @to.res.join
+ end
+
+ def accept_block_quote
+ assert_equal "\n<blockquote><p>quote\n</blockquote>\n", @to.res.join
+
+ assert_equal 5, @to.characters
+ end
+
+ def accept_document
+ assert_equal "<p>hello\n", @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def accept_heading
+ assert_equal "<p>Hello\n", @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def accept_heading_1
+ assert_equal "<p>Hello\n", @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def accept_heading_2
+ assert_equal "<p>Hello\n", @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def accept_heading_3
+ assert_equal "<p>Hello\n", @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def accept_heading_4
+ assert_equal "<p>Hello\n", @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def accept_heading_b
+ assert_equal "<p><strong>Hello</strong>\n",
+ @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def accept_heading_suppressed_crossref
+ assert_equal "<p>Hello\n", @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def accept_list_end_bullet
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "\n", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_end_label
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "\n", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_end_lalpha
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "\n", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_end_number
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "\n", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_end_note
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "\n", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_end_ualpha
+ assert_equal [], @to.list
+ assert_equal [], @to.in_list_entry
+
+ assert_equal "\n", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_item_end_bullet
+ assert_equal [''], @to.in_list_entry
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_item_end_label
+ assert_equal [''], @to.in_list_entry
+ assert_equal 4, @to.characters
+ end
+
+ def accept_list_item_end_lalpha
+ assert_equal [''], @to.in_list_entry
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_item_end_note
+ assert_equal [''], @to.in_list_entry
+ assert_equal 4, @to.characters
+ end
+
+ def accept_list_item_end_number
+ assert_equal [''], @to.in_list_entry
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_item_end_ualpha
+ assert_equal [''], @to.in_list_entry
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_item_start_bullet
+ assert_equal "<p>", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_item_start_label
+ assert_equal "<p>cat &mdash; ", @to.res.join
+ assert_equal 4, @to.characters
+ end
+
+ def accept_list_item_start_lalpha
+ assert_equal "<p>", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_item_start_note
+ assert_equal "<p>cat &mdash; ",
+ @to.res.join
+ assert_equal 4, @to.characters
+ end
+
+ def accept_list_item_start_note_2
+ expected = <<-EXPECTED
+<p><code>teletype</code> &mdash; teletype description
+
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ assert_equal 29, @to.characters
+ end
+
+ def accept_list_item_start_note_multi_description
+ expected = <<-EXPECTED
+<p>label &mdash; description one
+<p>description two
+
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ assert_equal 37, @to.characters
+ end
+
+ def accept_list_item_start_note_multi_label
+ expected = <<-EXPECTED
+<p>one, two &mdash; two headers
+
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ assert_equal 18, @to.characters
+ end
+
+ def accept_list_item_start_number
+ assert_equal "<p>", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_item_start_ualpha
+ assert_equal "<p>", @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_start_bullet
+ assert_equal [:BULLET], @to.list
+ assert_equal [''], @to.in_list_entry
+
+ assert_equal '', @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_start_label
+ assert_equal [:LABEL], @to.list
+ assert_equal [''], @to.in_list_entry
+
+ assert_equal '', @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_start_lalpha
+ assert_equal [:LALPHA], @to.list
+ assert_equal [''], @to.in_list_entry
+
+ assert_equal '', @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_start_note
+ assert_equal [:NOTE], @to.list
+ assert_equal [''], @to.in_list_entry
+
+ assert_equal '', @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_start_number
+ assert_equal [:NUMBER], @to.list
+ assert_equal [''], @to.in_list_entry
+
+ assert_equal '', @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_list_start_ualpha
+ assert_equal [:UALPHA], @to.list
+ assert_equal [''], @to.in_list_entry
+
+ assert_equal '', @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_paragraph
+ assert_equal "<p>hi\n", @to.res.join
+
+ assert_equal 2, @to.characters
+ end
+
+ def accept_paragraph_b
+ assert_equal "<p>reg <strong>bold words</strong> reg\n", @to.res.join
+
+ assert_equal 18, @to.characters
+ end
+
+ def accept_paragraph_br
+ assert_equal "<p>one<br>two\n", @to.res.join
+
+ assert_equal 6, @to.characters
+ end
+
+ def accept_paragraph_break
+ assert_equal "<p>hello<br>\nworld\n", @to.res.join
+
+ assert_equal 11, @to.characters
+ end
+
+ def accept_paragraph_i
+ assert_equal "<p>reg <em>italic words</em> reg\n", @to.res.join
+
+ assert_equal 20, @to.characters
+ end
+
+ def accept_paragraph_plus
+ assert_equal "<p>reg <code>teletype</code> reg\n", @to.res.join
+
+ assert_equal 16, @to.characters
+ end
+
+ def accept_paragraph_star
+ assert_equal "<p>reg <strong>bold</strong> reg\n", @to.res.join
+
+ assert_equal 12, @to.characters
+ end
+
+ def accept_paragraph_underscore
+ assert_equal "<p>reg <em>italic</em> reg\n", @to.res.join
+
+ assert_equal 14, @to.characters
+ end
+
+ def accept_raw
+ assert_equal '', @to.res.join
+ assert_equal 0, @to.characters
+ end
+
+ def accept_rule
+ assert_empty @to.res
+ assert_equal 0, @to.characters
+ end
+
+ def accept_verbatim
+ assert_equal "\n<pre class=\"ruby\"><span class=\"ruby-identifier\">hi</span>\n <span class=\"ruby-identifier\">world</span>\n</pre>\n", @to.res.join
+ assert_equal 10, @to.characters
+ end
+
+ def end_accepting
+ assert_equal 'hi', @to.res.join
+ end
+
+ def start_accepting
+ assert_equal [], @to.res
+ assert_equal [], @to.in_list_entry
+ assert_equal [], @to.list
+ assert_equal 0, @to.characters
+ end
+
+ def list_nested
+ expected = <<-EXPECTED
+<p>l1
+<p>l1.1
+
+<p>l2
+
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ assert_equal 8, @to.characters
+ end
+
+ def list_verbatim
+ expected = <<-EXPECTED
+<p>list stuff
+
+<pre>* list
+ with
+
+ second
+
+ 1. indented
+ 2. numbered
+
+ third
+
+* second</pre>
+
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ assert_equal 81, @to.characters
+ end
+
+ def test_accept_heading_7
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(7, 'Hello')
+
+ assert_equal "<p>Hello\n", @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def test_accept_heading_aref_class
+ @to.code_object = RDoc::NormalClass.new 'Foo'
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ assert_equal "<p>Hello\n",
+ @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def test_accept_heading_aref_method
+ @to.code_object = RDoc::AnyMethod.new nil, 'foo'
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ assert_equal "<p>Hello\n",
+ @to.res.join
+ assert_equal 5, @to.characters
+ end
+
+ def test_accept_verbatim_ruby
+ options = RDoc::Options.new
+ rdoc = RDoc::RDoc.new
+ rdoc.options = options
+ RDoc::RDoc.current = rdoc
+
+ verb = @RM::Verbatim.new("class C\n", "end\n")
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ expected = <<-EXPECTED
+
+<pre class="ruby"><span class="ruby-keyword">class</span> <span class="ruby-constant">C</span>
+<span class="ruby-keyword">end</span>
+</pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ assert_equal 11, @to.characters
+ end
+
+ def test_accept_verbatim_ruby_error
+ options = RDoc::Options.new
+ rdoc = RDoc::RDoc.new
+ rdoc.options = options
+ RDoc::RDoc.current = rdoc
+
+ verb = @RM::Verbatim.new("a % 09 # => blah\n")
+
+ @to.start_accepting
+ @to.accept_verbatim verb
+
+ inner = CGI.escapeHTML "a % 09 # => blah"
+
+ expected = <<-EXPECTED
+
+<pre>#{inner}</pre>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
+ assert_equal 16, @to.characters
+ end
+
+ def test_add_paragraph
+ @to = RDoc::Markup::ToHtmlSnippet.new @options, 0, 3
+ assert_throws :done do
+ @to.add_paragraph
+ @to.add_paragraph
+ @to.add_paragraph
+ end
+
+ assert_equal 3, @to.paragraphs
+ end
+
+ def test_convert_limit
+ rdoc = <<-RDOC
+= Hello
+
+This is some text, it *will* be cut off after 100 characters and an ellipsis
+must follow
+
+So there you have it
+ RDOC
+
+ expected = <<-EXPECTED
+<p>Hello
+<p>This is some text, it <strong>will</strong> be cut off after 100 characters
+and an ellipsis must follow
+<p>So there you #{@ellipsis}
+ EXPECTED
+
+ actual = @to.convert rdoc
+
+ assert_equal expected, actual
+ assert_equal 111, @to.characters, 'snippet character length'
+ end
+
+ def test_convert_limit_2
+ rdoc = <<-RDOC
+Outputs formatted RI data for the class or method +name+.
+
+Returns true if +name+ was found, false if it was not an alternative could
+be guessed, raises an error if +name+ couldn't be guessed.
+ RDOC
+
+ expected = <<-EXPECTED
+<p>Outputs formatted RI data for the class or method <code>name</code>.
+<p>Returns true if <code>name</code> was found, false if it was #{@ellipsis}
+ EXPECTED
+
+ actual = @to.convert rdoc
+
+ assert_equal expected, actual
+ assert_equal 159, @to.characters, 'snippet character length'
+ end
+
+ def test_convert_limit_paragraphs
+ @to = RDoc::Markup::ToHtmlSnippet.new @options, 100, 3
+
+ rdoc = <<-RDOC
+= \RDoc - Ruby Documentation System
+
+* {RDoc Project Page}[https://github.com/rdoc/rdoc/]
+* {RDoc Documentation}[http://docs.seattlerb.org/rdoc]
+* {RDoc Bug Tracker}[https://github.com/rdoc/rdoc/issues]
+
+== DESCRIPTION:
+
+RDoc produces HTML and command-line documentation for Ruby projects. RDoc
+includes the +rdoc+ and +ri+ tools for generating and displaying online
+documentation.
+
+See RDoc for a description of RDoc's markup and basic use.
+ RDOC
+
+ expected = <<-EXPECTED
+<p>RDoc - Ruby Documentation System
+<p>RDoc Project Page
+<p>RDoc Documentation
+ EXPECTED
+
+ actual = @to.convert rdoc
+
+ assert_equal expected, actual
+ assert_equal 67, @to.characters
+ end
+
+ def test_convert_limit_in_tag
+ @to = RDoc::Markup::ToHtmlSnippet.new @options, 4
+ rdoc = "* ab *c* d\n"
+
+ expected = "<p>ab <strong>c</strong> #{@ellipsis}\n\n"
+
+ actual = @to.convert rdoc
+
+ assert_equal 4, @to.characters
+ assert_equal expected, actual
+ end
+
+ def test_convert_limit_verbatim
+ rdoc = <<-RDOC
+= Hello There
+
+This is some text, it *will* be cut off after 100 characters
+
+ This one is cut off in this verbatim section
+ RDOC
+
+ expected = <<-EXPECTED
+<p>Hello There
+<p>This is some text, it <strong>will</strong> be cut off after 100 characters
+
+<pre>This one is cut off in this verbatim ...</pre>
+ EXPECTED
+
+ actual = @to.convert rdoc
+
+ assert_equal expected, actual
+ assert_equal 113, @to.characters
+ end
+
+ def test_convert_limit_verbatim_2
+ rdoc = <<-RDOC
+Extracts the class, selector and method name parts from +name+ like
+Foo::Bar#baz.
+
+NOTE: Given Foo::Bar, Bar is considered a class even though it may be a
+ method
+ RDOC
+
+ expected = <<-EXPECTED
+<p>Extracts the class, selector and method name parts from <code>name</code>
+like Foo::Bar#baz.
+<p>NOTE: Given Foo::Bar, #{@ellipsis}
+ EXPECTED
+
+ actual = @to.convert rdoc
+
+ assert_equal expected, actual
+ assert_equal 101, @to.characters
+ end
+
+ def test_convert_limit_verbatim_multiline
+ rdoc = <<-RDOC
+Look for directives in a normal comment block:
+
+ # :stopdoc:
+ # Don't display comment from this point forward
+
+This routine modifies its +comment+ parameter.
+ RDOC
+
+ inner = CGI.escapeHTML "# Don't display comment from this point forward"
+ expected = <<-EXPECTED
+<p>Look for directives in a normal comment block:
+
+<pre class=\"ruby\"><span class=\"ruby-comment\"># :stopdoc:</span>
+<span class=\"ruby-comment\">#{inner}</span>
+</pre>
+ EXPECTED
+
+ actual = @to.convert rdoc
+
+ assert_equal expected, actual
+ assert_equal 105, @to.characters
+ end
+
+ def test_convert_limit_over
+ @to = RDoc::Markup::ToHtmlSnippet.new @options, 4
+ rdoc = "* text\n" * 2
+
+ expected = "<p>text\n"
+ expected.chomp!
+ expected << " #{@ellipsis}\n"
+
+ actual = @to.convert rdoc
+
+ assert_equal 4, @to.characters
+ assert_equal expected, actual
+ end
+
+ def test_convert_string
+ assert_equal '&lt;&gt;', @to.convert_string('<>')
+ end
+
+ def test_convert_RDOCLINK_label_label
+ result = @to.convert 'rdoc-label:label-One'
+
+ assert_equal "<p>One\n", result
+ assert_equal 3, @to.characters
+ end
+
+ def test_convert_RDOCLINK_label_foottext
+ result = @to.convert 'rdoc-label:foottext-1'
+
+ assert_equal "<p>1\n", result
+ assert_equal 1, @to.characters
+ end
+
+ def test_convert_RDOCLINK_label_footmark
+ result = @to.convert 'rdoc-label:footmark-1'
+
+ assert_equal "<p>1\n", result
+ assert_equal 1, @to.characters
+ end
+
+ def test_convert_RDOCLINK_ref
+ result = @to.convert 'rdoc-ref:C'
+
+ assert_equal "<p>C\n", result
+ assert_equal 1, @to.characters
+ end
+
+ def test_convert_TIDYLINK_rdoc_label
+ result = @to.convert '{foo}[rdoc-label:foottext-1]'
+
+ assert_equal "<p>foo\n", result
+ assert_equal 3, @to.characters
+ end
+
+ def test_handle_special_HYPERLINK_link
+ special = RDoc::Markup::Special.new 0, 'link:README.txt'
+
+ link = @to.handle_special_HYPERLINK special
+
+ assert_equal 'README.txt', link
+ end
+
+ def test_list_verbatim_2
+ str = "* one\n verb1\n verb2\n* two\n"
+
+ expected = <<-EXPECTED
+<p>one
+
+<pre class=\"ruby\"><span class=\"ruby-identifier\">verb1</span>
+<span class=\"ruby-identifier\">verb2</span>
+</pre>
+<p>two
+
+ EXPECTED
+
+ assert_equal expected, @m.convert(str, @to)
+ assert_equal 17, @to.characters
+ end
+
+ def test_on_tags
+ on = RDoc::Markup::AttrChanger.new 2, 0
+
+ @to.on_tags [], on
+
+ assert_equal 2, @to.mask
+ end
+
+ def test_off_tags
+ on = RDoc::Markup::AttrChanger.new 2, 0
+ off = RDoc::Markup::AttrChanger.new 0, 2
+
+ @to.on_tags [], on
+ @to.off_tags [], off
+
+ assert_equal 0, @to.mask
+ end
+
+ def test_to_html
+ assert_equal "<p><code>--</code>\n", util_format("<tt>--</tt>")
+ assert_equal 2, @to.characters
+ end
+
+ def util_format text
+ paragraph = RDoc::Markup::Paragraph.new text
+
+ @to.start_accepting
+ @to.accept_paragraph paragraph
+ @to.end_accepting
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_joined_paragraph.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_joined_paragraph.rb
new file mode 100644
index 0000000..148edb1
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_joined_paragraph.rb
@@ -0,0 +1,32 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToJoinedParagraph < RDoc::TestCase
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToJoinedParagraph.new
+ end
+
+ def test_accept_paragraph
+ parsed = para('hello', ' ', 'world')
+
+ @to.accept_paragraph parsed
+
+ expected = para('hello world')
+
+ assert_equal expected, parsed
+ end
+
+ def test_accept_paragraph_break
+ parsed = para('hello', ' ', 'world', hard_break, 'everyone')
+
+ @to.accept_paragraph parsed
+
+ expected = para('hello world', hard_break, 'everyone')
+
+ assert_equal expected, parsed
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_label.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_label.rb
new file mode 100644
index 0000000..d8cc365
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_label.rb
@@ -0,0 +1,112 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToLabel < RDoc::Markup::FormatterTestCase
+
+ add_visitor_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToLabel.new
+ end
+
+ def empty
+ assert_empty @to.res
+ end
+
+ def end_accepting
+ assert_equal %w[hi], @to.res
+ end
+
+ alias accept_blank_line empty
+ alias accept_block_quote empty
+ alias accept_document empty
+ alias accept_heading empty
+ alias accept_heading_1 empty
+ alias accept_heading_2 empty
+ alias accept_heading_3 empty
+ alias accept_heading_4 empty
+ alias accept_heading_b empty
+ alias accept_heading_suppressed_crossref empty
+ alias accept_list_end_bullet empty
+ alias accept_list_end_label empty
+ alias accept_list_end_lalpha empty
+ alias accept_list_end_note empty
+ alias accept_list_end_number empty
+ alias accept_list_end_ualpha empty
+ alias accept_list_item_end_bullet empty
+ alias accept_list_item_end_label empty
+ alias accept_list_item_end_lalpha empty
+ alias accept_list_item_end_note empty
+ alias accept_list_item_end_number empty
+ alias accept_list_item_end_ualpha empty
+ alias accept_list_item_start_bullet empty
+ alias accept_list_item_start_label empty
+ alias accept_list_item_start_lalpha empty
+ alias accept_list_item_start_note empty
+ alias accept_list_item_start_note_2 empty
+ alias accept_list_item_start_note_multi_description empty
+ alias accept_list_item_start_note_multi_label empty
+ alias accept_list_item_start_number empty
+ alias accept_list_item_start_ualpha empty
+ alias accept_list_start_bullet empty
+ alias accept_list_start_label empty
+ alias accept_list_start_lalpha empty
+ alias accept_list_start_note empty
+ alias accept_list_start_number empty
+ alias accept_list_start_ualpha empty
+ alias accept_paragraph empty
+ alias accept_paragraph_b empty
+ alias accept_paragraph_br empty
+ alias accept_paragraph_break empty
+ alias accept_paragraph_i empty
+ alias accept_paragraph_plus empty
+ alias accept_paragraph_star empty
+ alias accept_paragraph_underscore empty
+ alias accept_raw empty
+ alias accept_rule empty
+ alias accept_verbatim empty
+ alias list_nested empty
+ alias list_verbatim empty
+ alias start_accepting empty
+
+ def test_convert_bold
+ assert_equal 'bold', @to.convert('<b>bold</b>')
+ assert_equal 'bold', @to.convert('*bold*')
+ end
+
+ def test_convert_crossref
+ assert_equal 'SomeClass', @to.convert('SomeClass')
+ assert_equal 'SomeClass', @to.convert('\\SomeClass')
+
+ assert_equal 'some_method', @to.convert('some_method')
+ assert_equal 'some_method', @to.convert('\\some_method')
+
+ assert_equal '23some_method', @to.convert('#some_method')
+ assert_equal '23some_method', @to.convert('\\#some_method')
+ end
+
+ def test_convert_em
+ assert_equal 'em', @to.convert('<em>em</em>')
+ assert_equal 'em', @to.convert('*em*')
+ end
+
+ def test_convert_em_dash # for HTML conversion
+ assert_equal '-', @to.convert('--')
+ end
+
+ def test_convert_escape
+ assert_equal 'a+-3E+b', @to.convert('a > b')
+ end
+
+ def test_convert_tidylink
+ assert_equal 'text', @to.convert('{text}[stuff]')
+ assert_equal 'text', @to.convert('text[stuff]')
+ end
+
+ def test_convert_tt
+ assert_equal 'tt', @to.convert('<tt>tt</tt>')
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_markdown.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_markdown.rb
new file mode 100644
index 0000000..442bb19
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_markdown.rb
@@ -0,0 +1,389 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToMarkdown < RDoc::Markup::TextFormatterTestCase
+
+ add_visitor_tests
+ add_text_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToMarkdown.new
+ end
+
+ def accept_blank_line
+ assert_equal "\n", @to.res.join
+ end
+
+ def accept_block_quote
+ assert_equal "> quote\n", @to.res.join
+ end
+
+ def accept_document
+ assert_equal "hello\n", @to.res.join
+ end
+
+ def accept_heading
+ assert_equal "##### Hello\n", @to.res.join
+ end
+
+ def accept_list_end_bullet
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_label
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_lalpha
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_note
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_number
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_ualpha
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_item_end_bullet
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_label
+ assert_equal "cat\n: ", @to.res.join
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_lalpha
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 2, @to.list_index.last
+ end
+
+ def accept_list_item_end_note
+ assert_equal "cat\n: ", @to.res.join
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_number
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 2, @to.list_index.last
+ end
+
+ def accept_list_item_end_ualpha
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 2, @to.list_index.last
+ end
+
+ def accept_list_item_start_bullet
+ assert_equal [""], @to.res
+ assert_equal '* ', @to.prefix
+ end
+
+ def accept_list_item_start_label
+ assert_equal [""], @to.res
+ assert_equal "cat\n: ", @to.prefix
+
+ assert_equal 4, @to.indent
+ end
+
+ def accept_list_item_start_lalpha
+ assert_equal [""], @to.res
+ assert_equal '1. ', @to.prefix
+
+ assert_equal 1, @to.list_index.last
+ assert_equal 4, @to.indent
+ end
+
+ def accept_list_item_start_note
+ assert_equal [""], @to.res
+ assert_equal "cat\n: ", @to.prefix
+
+ assert_equal 4, @to.indent
+ end
+
+ def accept_list_item_start_number
+ assert_equal [""], @to.res
+ assert_equal '1. ', @to.prefix
+
+ assert_equal 1, @to.list_index.last
+ assert_equal 4, @to.indent
+ end
+
+ def accept_list_item_start_ualpha
+ assert_equal [""], @to.res
+ assert_equal '1. ', @to.prefix
+
+ assert_equal 1, @to.list_index.last
+ assert_equal 4, @to.indent
+ end
+
+ def accept_list_start_bullet
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:BULLET], @to.list_type
+ assert_equal [4], @to.list_width
+ end
+
+ def accept_list_start_label
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:LABEL], @to.list_type
+ assert_equal [4], @to.list_width
+ end
+
+ def accept_list_start_lalpha
+ assert_equal "", @to.res.join
+ assert_equal [1], @to.list_index
+ assert_equal [:LALPHA], @to.list_type
+ assert_equal [4], @to.list_width
+ end
+
+ def accept_list_start_note
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:NOTE], @to.list_type
+ assert_equal [4], @to.list_width
+ end
+
+ def accept_list_start_number
+ assert_equal "", @to.res.join
+ assert_equal [1], @to.list_index
+ assert_equal [:NUMBER], @to.list_type
+ assert_equal [4], @to.list_width
+ end
+
+ def accept_list_start_ualpha
+ assert_equal "", @to.res.join
+ assert_equal [1], @to.list_index
+ assert_equal [:UALPHA], @to.list_type
+ assert_equal [4], @to.list_width
+ end
+
+ def accept_paragraph
+ assert_equal "hi\n", @to.res.join
+ end
+
+ def accept_raw
+ raw = <<-RAW.rstrip
+<table>
+<tr><th>Name<th>Count
+<tr><td>a<td>1
+<tr><td>b<td>2
+</table>
+ RAW
+
+ assert_equal raw, @to.res.join
+ end
+
+ def accept_rule
+ assert_equal "---\n", @to.res.join
+ end
+
+ def accept_verbatim
+ assert_equal " hi\n world\n\n", @to.res.join
+ end
+
+ def end_accepting
+ assert_equal "hi", @to.end_accepting
+ end
+
+ def start_accepting
+ assert_equal 0, @to.indent
+ assert_equal [""], @to.res
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_heading_1
+ assert_equal "# Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_2
+ assert_equal "## Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_3
+ assert_equal "### Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_4
+ assert_equal "#### Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_indent
+ assert_equal " # Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_b
+ assert_equal "# **Hello**\n", @to.end_accepting
+ end
+
+ def accept_heading_suppressed_crossref
+ assert_equal "# Hello\n", @to.end_accepting
+ end
+
+ def accept_list_item_start_note_2
+ assert_equal "`teletype`\n: teletype description\n\n", @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_description
+ assert_equal "label\n: description one\n\n: description two\n\n",
+ @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_label
+ assert_equal "one\ntwo\n: two headers\n\n", @to.res.join
+ end
+
+ def accept_paragraph_b
+ assert_equal "reg **bold words** reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_br
+ assert_equal "one \ntwo\n", @to.end_accepting
+ end
+
+ def accept_paragraph_break
+ assert_equal "hello \nworld\n", @to.end_accepting
+ end
+
+ def accept_paragraph_i
+ assert_equal "reg *italic words* reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_indent
+ expected = <<-EXPECTED
+ words words words words words words words words words words words words
+ words words words words words words words words words words words words
+ words words words words words words
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def accept_paragraph_plus
+ assert_equal "reg `teletype` reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_star
+ assert_equal "reg **bold** reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_underscore
+ assert_equal "reg *italic* reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_wrap
+ expected = <<-EXPECTED
+words words words words words words words words words words words words words
+words words words words words words words words words words words words words
+words words words words
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def accept_rule_indent
+ assert_equal " ---\n", @to.end_accepting
+ end
+
+ def accept_verbatim_indent
+ assert_equal " hi\n world\n\n", @to.end_accepting
+ end
+
+ def accept_verbatim_big_indent
+ assert_equal " hi\n world\n\n", @to.end_accepting
+ end
+
+ def list_nested
+ expected = <<-EXPECTED
+* l1
+ * l1.1
+
+* l2
+
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def list_verbatim
+ expected = <<-EXPECTED # HACK overblown
+* list stuff
+
+ * list
+ with
+
+ second
+
+ 1. indented
+ 2. numbered
+
+ third
+
+ * second
+
+
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def test_convert_RDOCLINK
+ result = @to.convert 'rdoc-garbage:C'
+
+ assert_equal "C\n", result
+ end
+
+ def test_convert_RDOCLINK_image
+ result = @to.convert 'rdoc-image:/path/to/image.jpg'
+
+ assert_equal "![](/path/to/image.jpg)\n", result
+ end
+
+ def test_convert_TIDYLINK
+ result = @to.convert \
+ '{DSL}[http://en.wikipedia.org/wiki/Domain-specific_language]'
+
+ expected = "[DSL](http://en.wikipedia.org/wiki/Domain-specific_language)\n"
+
+ assert_equal expected, result
+ end
+
+ def test_handle_rdoc_link_label_footmark
+ assert_equal '[^1]:', @to.handle_rdoc_link('rdoc-label:footmark-1:x')
+ end
+
+ def test_handle_rdoc_link_label_foottext
+ assert_equal '[^1]', @to.handle_rdoc_link('rdoc-label:foottext-1:x')
+ end
+
+ def test_handle_rdoc_link_label_label
+ assert_equal '[x](#label-x)', @to.handle_rdoc_link('rdoc-label:label-x')
+ end
+
+ def test_handle_rdoc_link_ref
+ assert_equal 'x', @to.handle_rdoc_link('rdoc-ref:x')
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_rdoc.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_rdoc.rb
new file mode 100644
index 0000000..4b60d01
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_rdoc.rb
@@ -0,0 +1,377 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase
+
+ add_visitor_tests
+ add_text_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToRdoc.new
+ end
+
+ def accept_blank_line
+ assert_equal "\n", @to.res.join
+ end
+
+ def accept_block_quote
+ assert_equal "> quote\n", @to.res.join
+ end
+
+ def accept_document
+ assert_equal "hello\n", @to.res.join
+ end
+
+ def accept_heading
+ assert_equal "===== Hello\n", @to.res.join
+ end
+
+ def accept_list_end_bullet
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_label
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_lalpha
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_note
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_number
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_end_ualpha
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_list_item_end_bullet
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_label
+ assert_equal "cat:\n", @to.res.join
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_lalpha
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 'b', @to.list_index.last
+ end
+
+ def accept_list_item_end_note
+ assert_equal "cat:\n", @to.res.join
+ assert_equal 0, @to.indent, 'indent'
+ end
+
+ def accept_list_item_end_number
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 2, @to.list_index.last
+ end
+
+ def accept_list_item_end_ualpha
+ assert_equal 0, @to.indent, 'indent'
+ assert_equal 'B', @to.list_index.last
+ end
+
+ def accept_list_item_start_bullet
+ assert_equal [""], @to.res
+ assert_equal '* ', @to.prefix
+ end
+
+ def accept_list_item_start_label
+ assert_equal [""], @to.res
+ assert_equal "cat:\n ", @to.prefix
+
+ assert_equal 2, @to.indent
+ end
+
+ def accept_list_item_start_lalpha
+ assert_equal [""], @to.res
+ assert_equal 'a. ', @to.prefix
+
+ assert_equal 'a', @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_item_start_note
+ assert_equal [""], @to.res
+ assert_equal "cat:\n ", @to.prefix
+
+ assert_equal 2, @to.indent
+ end
+
+ def accept_list_item_start_number
+ assert_equal [""], @to.res
+ assert_equal '1. ', @to.prefix
+
+ assert_equal 1, @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_item_start_ualpha
+ assert_equal [""], @to.res
+ assert_equal 'A. ', @to.prefix
+
+ assert_equal 'A', @to.list_index.last
+ assert_equal 3, @to.indent
+ end
+
+ def accept_list_start_bullet
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:BULLET], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_label
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:LABEL], @to.list_type
+ assert_equal [2], @to.list_width
+ end
+
+ def accept_list_start_lalpha
+ assert_equal "", @to.res.join
+ assert_equal ['a'], @to.list_index
+ assert_equal [:LALPHA], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_note
+ assert_equal "", @to.res.join
+ assert_equal [nil], @to.list_index
+ assert_equal [:NOTE], @to.list_type
+ assert_equal [2], @to.list_width
+ end
+
+ def accept_list_start_number
+ assert_equal "", @to.res.join
+ assert_equal [1], @to.list_index
+ assert_equal [:NUMBER], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_list_start_ualpha
+ assert_equal "", @to.res.join
+ assert_equal ['A'], @to.list_index
+ assert_equal [:UALPHA], @to.list_type
+ assert_equal [1], @to.list_width
+ end
+
+ def accept_paragraph
+ assert_equal "hi\n", @to.res.join
+ end
+
+ def accept_raw
+ raw = <<-RAW.rstrip
+<table>
+<tr><th>Name<th>Count
+<tr><td>a<td>1
+<tr><td>b<td>2
+</table>
+ RAW
+
+ assert_equal raw, @to.res.join
+ end
+
+ def accept_rule
+ assert_equal "#{'-' * 78}\n", @to.res.join
+ end
+
+ def accept_verbatim
+ assert_equal " hi\n world\n\n", @to.res.join
+ end
+
+ def end_accepting
+ assert_equal "hi", @to.end_accepting
+ end
+
+ def start_accepting
+ assert_equal 0, @to.indent
+ assert_equal [""], @to.res
+ assert_empty @to.list_index
+ assert_empty @to.list_type
+ assert_empty @to.list_width
+ end
+
+ def accept_heading_1
+ assert_equal "= Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_2
+ assert_equal "== Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_3
+ assert_equal "=== Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_4
+ assert_equal "==== Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_indent
+ assert_equal " = Hello\n", @to.end_accepting
+ end
+
+ def accept_heading_b
+ assert_equal "= <b>Hello</b>\n", @to.end_accepting
+ end
+
+ def accept_heading_suppressed_crossref
+ assert_equal "= Hello\n", @to.end_accepting
+ end
+
+ def accept_list_item_start_note_2
+ assert_equal "<tt>teletype</tt>:\n teletype description\n\n", @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_description
+ assert_equal "label:\n description one\n\n description two\n\n",
+ @to.res.join
+ end
+
+ def accept_list_item_start_note_multi_label
+ assert_equal "one\ntwo:\n two headers\n\n", @to.res.join
+ end
+
+ def accept_paragraph_b
+ assert_equal "reg <b>bold words</b> reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_br
+ assert_equal "one\ntwo\n", @to.end_accepting
+ end
+
+ def accept_paragraph_break
+ assert_equal "hello\nworld\n", @to.end_accepting
+ end
+
+ def accept_paragraph_i
+ assert_equal "reg <em>italic words</em> reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_indent
+ expected = <<-EXPECTED
+ words words words words words words words words words words words words
+ words words words words words words words words words words words words
+ words words words words words words
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def accept_paragraph_plus
+ assert_equal "reg <tt>teletype</tt> reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_star
+ assert_equal "reg <b>bold</b> reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_underscore
+ assert_equal "reg <em>italic</em> reg\n", @to.end_accepting
+ end
+
+ def accept_paragraph_wrap
+ expected = <<-EXPECTED
+words words words words words words words words words words words words words
+words words words words words words words words words words words words words
+words words words words
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def accept_rule_indent
+ assert_equal " #{'-' * 75}\n", @to.end_accepting
+ end
+
+ def accept_verbatim_indent
+ assert_equal " hi\n world\n\n", @to.end_accepting
+ end
+
+ def accept_verbatim_big_indent
+ assert_equal " hi\n world\n\n", @to.end_accepting
+ end
+
+ def list_nested
+ expected = <<-EXPECTED
+* l1
+ * l1.1
+* l2
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ def list_verbatim
+ expected = <<-EXPECTED # HACK overblown
+* list stuff
+
+ * list
+ with
+
+ second
+
+ 1. indented
+ 2. numbered
+
+ third
+
+ * second
+
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
+ end
+
+ # functional test
+ def test_convert_list_note
+ note_list = <<-NOTE_LIST
+foo ::
+bar ::
+ hi
+ NOTE_LIST
+
+ expected = <<-EXPECTED
+foo
+bar:
+ hi
+
+ EXPECTED
+
+ assert_equal expected, @to.convert(note_list)
+ end
+
+ def test_accept_indented_paragraph
+ ip = RDoc::Markup::IndentedParagraph.new 2, 'cats are cool'
+
+ @to.start_accepting
+
+ @to.accept_indented_paragraph ip
+
+ assert_equal " cats are cool\n", @to.end_accepting
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_table_of_contents.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_table_of_contents.rb
new file mode 100644
index 0000000..ba17b84
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_table_of_contents.rb
@@ -0,0 +1,126 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToTableOfContents < RDoc::Markup::FormatterTestCase
+
+ add_visitor_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToTableOfContents.new
+ end
+
+ def end_accepting
+ assert_equal %w[hi], @to.res
+ end
+
+ def empty
+ assert_empty @to.res
+ end
+
+ def accept_heading
+ assert_equal [@RM::Heading.new(5, 'Hello')], @to.res
+ end
+
+ def accept_heading_1
+ assert_equal [@RM::Heading.new(1, 'Hello')], @to.res
+ end
+
+ def accept_heading_2
+ assert_equal [@RM::Heading.new(2, 'Hello')], @to.res
+ end
+
+ def accept_heading_3
+ assert_equal [@RM::Heading.new(3, 'Hello')], @to.res
+ end
+
+ def accept_heading_4
+ assert_equal [@RM::Heading.new(4, 'Hello')], @to.res
+ end
+
+ def accept_heading_b
+ assert_equal [@RM::Heading.new(1, '*Hello*')], @to.res
+ end
+
+ def accept_heading_suppressed_crossref
+ assert_equal [@RM::Heading.new(1, '\\Hello')], @to.res
+ end
+
+ alias accept_blank_line empty
+ alias accept_block_quote empty
+ alias accept_document empty
+ alias accept_list_end_bullet empty
+ alias accept_list_end_label empty
+ alias accept_list_end_lalpha empty
+ alias accept_list_end_note empty
+ alias accept_list_end_number empty
+ alias accept_list_end_ualpha empty
+ alias accept_list_item_end_bullet empty
+ alias accept_list_item_end_label empty
+ alias accept_list_item_end_lalpha empty
+ alias accept_list_item_end_note empty
+ alias accept_list_item_end_number empty
+ alias accept_list_item_end_ualpha empty
+ alias accept_list_item_start_bullet empty
+ alias accept_list_item_start_label empty
+ alias accept_list_item_start_lalpha empty
+ alias accept_list_item_start_note empty
+ alias accept_list_item_start_note_2 empty
+ alias accept_list_item_start_note_multi_description empty
+ alias accept_list_item_start_note_multi_label empty
+ alias accept_list_item_start_number empty
+ alias accept_list_item_start_ualpha empty
+ alias accept_list_start_bullet empty
+ alias accept_list_start_label empty
+ alias accept_list_start_lalpha empty
+ alias accept_list_start_note empty
+ alias accept_list_start_number empty
+ alias accept_list_start_ualpha empty
+ alias accept_paragraph empty
+ alias accept_paragraph_b empty
+ alias accept_paragraph_br empty
+ alias accept_paragraph_break empty
+ alias accept_paragraph_i empty
+ alias accept_paragraph_plus empty
+ alias accept_paragraph_star empty
+ alias accept_paragraph_underscore empty
+ alias accept_raw empty
+ alias accept_rule empty
+ alias accept_verbatim empty
+ alias list_nested empty
+ alias list_verbatim empty
+ alias start_accepting empty
+
+ def test_accept_document_omit_headings_below
+ document = doc
+ document.omit_headings_below = 2
+
+ @to.accept_document document
+
+ assert_equal 2, @to.omit_headings_below
+ end
+
+ def test_accept_heading_suppressed
+ @to.start_accepting
+ @to.omit_headings_below = 4
+
+ suppressed = head 5, 'Hello'
+
+ @to.accept_heading suppressed
+
+ assert_empty @to.res
+ end
+
+ def test_suppressed_eh
+ @to.omit_headings_below = nil
+
+ refute @to.suppressed? head(1, '')
+
+ @to.omit_headings_below = 1
+
+ refute @to.suppressed? head(1, '')
+ assert @to.suppressed? head(2, '')
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_to_tt_only.rb b/jni/ruby/test/rdoc/test_rdoc_markup_to_tt_only.rb
new file mode 100644
index 0000000..2e950dd
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_to_tt_only.rb
@@ -0,0 +1,246 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupToTtOnly < RDoc::Markup::FormatterTestCase
+
+ add_visitor_tests
+
+ def setup
+ super
+
+ @to = RDoc::Markup::ToTtOnly.new
+ end
+
+ def accept_blank_line
+ assert_empty @to.end_accepting
+ end
+
+ def accept_block_quote
+ assert_empty @to.end_accepting
+ end
+
+ def accept_document
+ assert_equal [], @to.res
+ end
+
+ def accept_heading
+ assert_empty @to.end_accepting
+ end
+
+ def accept_list_end_bullet
+ assert_empty @to.res
+ end
+
+ def accept_list_end_label
+ assert_empty @to.res
+ end
+
+ def accept_list_end_lalpha
+ assert_empty @to.res
+ end
+
+ def accept_list_end_note
+ assert_empty @to.res
+ end
+
+ def accept_list_end_number
+ assert_empty @to.res
+ end
+
+ def accept_list_end_ualpha
+ assert_empty @to.res
+ end
+
+ def accept_list_item_end_bullet
+ assert_empty @to.res
+ end
+
+ def accept_list_item_end_label
+ assert_empty @to.res
+ end
+
+ def accept_list_item_end_lalpha
+ assert_empty @to.res
+ end
+
+ def accept_list_item_end_note
+ assert_empty @to.res
+ end
+
+ def accept_list_item_end_number
+ assert_empty @to.res
+ end
+
+ def accept_list_item_end_ualpha
+ assert_empty @to.res
+ end
+
+ def accept_list_item_start_bullet
+ assert_empty @to.res
+ end
+
+ def accept_list_item_start_label
+ assert_empty @to.res
+ end
+
+ def accept_list_item_start_lalpha
+ assert_empty @to.res
+ end
+
+ def accept_list_item_start_note
+ assert_empty @to.res
+ end
+
+ def accept_list_item_start_number
+ assert_empty @to.res
+ end
+
+ def accept_list_item_start_ualpha
+ assert_empty @to.res
+ end
+
+ def accept_list_start_bullet
+ assert_empty @to.res
+ end
+
+ def accept_list_start_label
+ assert_empty @to.res
+ end
+
+ def accept_list_start_lalpha
+ assert_empty @to.res
+ end
+
+ def accept_list_start_note
+ assert_empty @to.res
+ end
+
+ def accept_list_start_number
+ assert_empty @to.res
+ end
+
+ def accept_list_start_ualpha
+ assert_empty @to.res
+ end
+
+ def accept_paragraph
+ assert_empty @to.end_accepting
+ end
+
+ def accept_paragraph_break
+ assert_empty @to.end_accepting
+ end
+
+ def accept_raw
+ assert_empty @to.end_accepting
+ end
+
+ def accept_rule
+ assert_empty @to.end_accepting
+ end
+
+ def accept_verbatim
+ assert_empty @to.end_accepting
+ end
+
+ def end_accepting
+ assert_equal %w[hi], @to.end_accepting
+ end
+
+ def start_accepting
+ assert_empty @to.end_accepting
+ end
+
+ def accept_heading_1
+ assert_empty @to.end_accepting
+ end
+
+ def accept_heading_2
+ assert_empty @to.end_accepting
+ end
+
+ def accept_heading_3
+ assert_empty @to.end_accepting
+ end
+
+ def accept_heading_4
+ assert_empty @to.end_accepting
+ end
+
+ def accept_heading_indent
+ assert_empty @to.end_accepting
+ end
+
+ def accept_heading_b
+ assert_empty @to.end_accepting
+ end
+
+ def accept_heading_suppressed_crossref
+ assert_empty @to.end_accepting
+ end
+
+ def accept_list_item_start_note_2
+ assert_equal [nil, 'teletype', nil], @to.res
+ end
+
+ def accept_list_item_start_note_multi_description
+ assert_empty @to.res
+ end
+
+ def accept_list_item_start_note_multi_label
+ assert_empty @to.res
+ end
+
+ def accept_paragraph_b
+ assert_empty @to.end_accepting
+ end
+
+ def accept_paragraph_br
+ assert_empty @to.end_accepting
+ end
+
+ def accept_paragraph_i
+ assert_empty @to.end_accepting
+ end
+
+ def accept_paragraph_indent
+ assert_empty @to.end_accepting
+ end
+
+ def accept_paragraph_plus
+ assert_equal %w[teletype], @to.end_accepting
+ end
+
+ def accept_paragraph_star
+ assert_empty @to.end_accepting
+ end
+
+ def accept_paragraph_underscore
+ assert_empty @to.end_accepting
+ end
+
+ def accept_paragraph_wrap
+ assert_empty @to.end_accepting
+ end
+
+ def accept_rule_indent
+ assert_empty @to.end_accepting
+ end
+
+ def accept_verbatim_indent
+ assert_empty @to.end_accepting
+ end
+
+ def accept_verbatim_big_indent
+ assert_empty @to.end_accepting
+ end
+
+ def list_nested
+ assert_empty @to.end_accepting
+ end
+
+ def list_verbatim
+ assert_empty @to.end_accepting
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_markup_verbatim.rb b/jni/ruby/test/rdoc/test_rdoc_markup_verbatim.rb
new file mode 100644
index 0000000..781d528
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_markup_verbatim.rb
@@ -0,0 +1,29 @@
+require 'rdoc/test_case'
+
+class TestRDocMarkupVerbatim < RDoc::TestCase
+
+ def test_equals2
+ v1 = verb('1 + 1')
+ v2 = verb('1 + 1')
+ v3 = verb('1 + 2')
+ v4 = verb('1 + 1')
+ v4.format = :ruby
+
+ assert_equal v1, v2
+
+ refute_equal v1, v3
+ refute_equal v1, v4
+ end
+
+ def test_ruby_eh
+ verbatim = RDoc::Markup::Verbatim.new
+
+ refute verbatim.ruby?
+
+ verbatim.format = :ruby
+
+ assert verbatim.ruby?
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_method_attr.rb b/jni/ruby/test/rdoc/test_rdoc_method_attr.rb
new file mode 100644
index 0000000..e93e81c
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_method_attr.rb
@@ -0,0 +1,193 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocMethodAttr < XrefTestCase
+
+ def test_initialize_copy
+ refute_same @c1_m.full_name, @c1_m.dup.full_name
+ end
+
+ def test_block_params_equal
+ m = RDoc::MethodAttr.new(nil, 'foo')
+
+ m.block_params = ''
+ assert_equal '', m.block_params
+
+ m.block_params = 'a_var'
+ assert_equal 'a_var', m.block_params
+
+ m.block_params = '()'
+ assert_equal '', m.block_params
+
+ m.block_params = '(a_var, b_var)'
+ assert_equal 'a_var, b_var', m.block_params
+
+ m.block_params = '.to_s + "</#{element.upcase}>"'
+ assert_equal '', m.block_params
+
+ m.block_params = 'i.name'
+ assert_equal 'name', m.block_params
+
+ m.block_params = 'attr.expanded_name, attr.value'
+ assert_equal 'expanded_name, value', m.block_params
+
+ m.block_params = 'expanded_name, attr.value'
+ assert_equal 'expanded_name, value', m.block_params
+
+ m.block_params = 'attr.expanded_name, value'
+ assert_equal 'expanded_name, value', m.block_params
+
+ m.block_params = '(@base_notifier)'
+ assert_equal 'base_notifier', m.block_params
+
+ m.block_params = 'if @signal_status == :IN_LOAD'
+ assert_equal '', m.block_params
+
+ m.block_params = 'e if e.kind_of? Element'
+ assert_equal 'e', m.block_params
+
+ m.block_params = '(e, f) if e.kind_of? Element'
+ assert_equal 'e, f', m.block_params
+
+ m.block_params = 'back_path, back_name'
+ assert_equal 'back_path, back_name', m.block_params
+
+ m.block_params = '(*a[1..-1])'
+ assert_equal '*a', m.block_params
+
+ m.block_params = '@@context[:node] if defined? @@context[:node].namespace'
+ assert_equal 'context', m.block_params
+
+ m.block_params = '(result, klass.const_get(constant_name))'
+ assert_equal 'result, const', m.block_params
+
+ m.block_params = 'name.to_s if (bitmap & bit) != 0'
+ assert_equal 'name', m.block_params
+
+ m.block_params = 'line unless line.deleted'
+ assert_equal 'line', m.block_params
+
+ m.block_params = 'str + rs'
+ assert_equal 'str', m.block_params
+
+ m.block_params = 'f+rs'
+ assert_equal 'f', m.block_params
+
+ m.block_params = '[user, realm, hash[user]]'
+ assert_equal 'user, realm, hash', m.block_params
+
+ m.block_params = 'proc{|rc| rc == "rc" ? irbrc : irbrc+rc| ... }'
+ assert_equal 'proc', m.block_params
+
+ m.block_params = 'lambda { |x| x.to_i }'
+ assert_equal 'lambda', m.block_params
+
+ m.block_params = '$&'
+ assert_equal 'str', m.block_params
+
+ m.block_params = 'Inflections.instance'
+ assert_equal 'instance', m.block_params
+
+ m.block_params = 'self.class::STARTED'
+ assert_equal 'STARTED', m.block_params
+
+ m.block_params = 'Test::Unit::TestCase::STARTED'
+ assert_equal 'STARTED', m.block_params
+
+ m.block_params = 'ActiveSupport::OptionMerger.new(self, options)'
+ assert_equal 'option_merger', m.block_params
+
+ m.block_params = ', msg'
+ assert_equal '', m.block_params
+
+ m.block_params = '[size.to_s(16), term, chunk, term].join'
+ assert_equal '[size, term, chunk, term].join', m.block_params
+
+ m.block_params = 'YPath.new( path )'
+ assert_equal 'y_path', m.block_params
+
+ end
+
+ def test_find_method_or_attribute_recursive
+ inc = RDoc::Include.new 'M1', nil
+ @m1.add_include inc # M1 now includes itself
+
+ assert_nil @m1_m.find_method_or_attribute 'm'
+ end
+
+ def test_full_name
+ assert_equal 'C1#m', @c1_m.full_name
+ assert_equal 'C1::m', @c1__m.full_name
+ end
+
+ def test_is_alias_for
+ assert_equal @c2_b, @c2_a.is_alias_for
+ end
+
+ def test_output_name
+ assert_equal '#m', @c1_m.output_name(@c1)
+ assert_equal '::m', @c1__m.output_name(@c1)
+
+ assert_equal 'C1#m', @c1_m.output_name(@c2)
+ assert_equal 'C1.m', @c1__m.output_name(@c2)
+ end
+
+ def test_search_record
+ @c1_m.comment = 'This is a comment.'
+
+ expected = [
+ 'm',
+ 'C1#m',
+ 'm',
+ 'C1',
+ 'C1.html#method-i-m',
+ '(foo)',
+ "<p>This is a comment.\n",
+ ]
+
+ assert_equal expected, @c1_m.search_record
+ end
+
+ def test_spaceship
+ assert_nil @c1_m.<=>(RDoc::CodeObject.new)
+ end
+
+ def test_equals2
+ assert_equal @c1_m, @c1_m
+ refute_equal @c1_m, @parent_m
+ end
+
+ def test_pretty_print
+ temp_dir do |tmpdir|
+ s = RDoc::RI::Store.new tmpdir
+ s.rdoc = @rdoc
+
+ top_level = s.add_file 'file.rb'
+ meth_bang = RDoc::AnyMethod.new nil, 'method!'
+ meth_bang.record_location top_level
+
+ meth_bang_alias = RDoc::Alias.new nil, 'method!', 'method_bang', ''
+ meth_bang_alias.record_location top_level
+
+ klass = top_level.add_class RDoc::NormalClass, 'Object'
+ klass.add_method meth_bang
+
+ meth_bang.add_alias meth_bang_alias, klass
+
+ s.save
+
+ meth_alias_from_store = s.load_method 'Object', '#method_bang'
+
+ expected = "[RDoc::AnyMethod Object#method_bang public alias for method!]"
+ actual = mu_pp meth_alias_from_store
+ assert_equal expected, actual
+ end
+ end
+
+ def test_to_s
+ assert_equal 'RDoc::AnyMethod: C1#m', @c1_m.to_s
+ assert_equal 'RDoc::AnyMethod: C2#b', @c2_b.to_s
+ assert_equal 'RDoc::AnyMethod: C1::m', @c1__m.to_s
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_normal_class.rb b/jni/ruby/test/rdoc/test_rdoc_normal_class.rb
new file mode 100644
index 0000000..ab31a8d
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_normal_class.rb
@@ -0,0 +1,47 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocNormalClass < XrefTestCase
+
+ def test_ancestors
+ klass = @top_level.add_class RDoc::NormalClass, 'Klass'
+ incl = RDoc::Include.new 'Incl', ''
+
+ sub_klass = @top_level.add_class RDoc::NormalClass, 'SubClass'
+ sub_klass.superclass = klass
+ sub_klass.add_include incl
+
+ assert_equal [incl.name, klass, 'Object'], sub_klass.ancestors
+ end
+
+ def test_ancestors_multilevel
+ c1 = @top_level.add_class RDoc::NormalClass, 'Outer'
+ c2 = @top_level.add_class RDoc::NormalClass, 'Middle', c1.full_name
+ c3 = @top_level.add_class RDoc::NormalClass, 'Inner', c2.full_name
+
+ assert_equal [c2, c1, 'Object'], c3.ancestors
+ end
+
+ def test_aref
+ assert_equal 'class-C1', @c1.aref
+ assert_equal 'class-C2::C3', @c2_c3.aref
+ end
+
+ def test_direct_ancestors
+ incl = RDoc::Include.new 'Incl', ''
+
+ c1 = @top_level.add_class RDoc::NormalClass, 'Outer'
+ c2 = @top_level.add_class RDoc::NormalClass, 'Middle', c1.full_name
+ c3 = @top_level.add_class RDoc::NormalClass, 'Inner', c2.full_name
+ c3.add_include incl
+
+ assert_equal [incl.name, c2], c3.direct_ancestors
+ end
+
+ def test_definition
+ c = RDoc::NormalClass.new 'C'
+
+ assert_equal 'class C', c.definition
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_normal_module.rb b/jni/ruby/test/rdoc/test_rdoc_normal_module.rb
new file mode 100644
index 0000000..1944564
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_normal_module.rb
@@ -0,0 +1,42 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocNormalModule < XrefTestCase
+
+ def setup
+ super
+
+ @mod = RDoc::NormalModule.new 'Mod'
+ end
+
+ def test_ancestors_module
+ top_level = @store.add_file 'file.rb'
+ mod = top_level.add_module RDoc::NormalModule, 'Mod'
+ incl = RDoc::Include.new 'Incl', ''
+
+ mod.add_include incl
+
+ assert_equal [incl.name], mod.ancestors
+
+ mod2 = top_level.add_module RDoc::NormalModule, 'Inc2'
+ inc2 = RDoc::Include.new 'Inc2', ''
+ mod.add_include inc2
+ assert_equal [mod2, incl.name], mod.ancestors
+ end
+
+ def test_aref
+ assert_equal 'module-M1', @m1.aref
+ assert_equal 'module-M1::M2', @m1_m2.aref
+ end
+
+ def test_definition
+ m = RDoc::NormalModule.new 'M'
+
+ assert_equal 'module M', m.definition
+ end
+
+ def test_module_eh
+ assert @mod.module?
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_options.rb b/jni/ruby/test/rdoc/test_rdoc_options.rb
new file mode 100644
index 0000000..67053e3
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_options.rb
@@ -0,0 +1,766 @@
+require 'rdoc/test_case'
+
+class TestRDocOptions < RDoc::TestCase
+
+ def setup
+ super
+
+ @options = RDoc::Options.new
+ @generators = RDoc::RDoc::GENERATORS.dup
+ end
+
+ def teardown
+ super
+
+ RDoc::RDoc::GENERATORS.replace @generators
+ end
+
+ def test_check_files
+ skip "assumes UNIX permission model" if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ out, err = capture_io do
+ temp_dir do
+ FileUtils.touch 'unreadable'
+ FileUtils.chmod 0, 'unreadable'
+
+ @options.files = %w[nonexistent unreadable]
+
+ @options.check_files
+ end
+ end
+
+ assert_empty @options.files
+
+ assert_empty out
+ assert_empty err
+ end
+
+ def test_check_files_warn
+ @options.verbosity = 2
+
+ out, err = verbose_capture_io do
+ @options.files = %w[nonexistent]
+
+ @options.check_files
+ end
+
+ assert_empty out
+ assert_equal "file 'nonexistent' not found\n", err
+ assert_empty @options.files
+ end
+
+ def test_dry_run_default
+ refute @options.dry_run
+ end
+
+ def test_encode_with
+ coder = {}
+ class << coder; alias add []=; end
+
+ @options.encode_with coder
+
+ encoding = Object.const_defined?(:Encoding) ? 'UTF-8' : nil
+
+ expected = {
+ 'charset' => 'UTF-8',
+ 'encoding' => encoding,
+ 'exclude' => [],
+ 'hyperlink_all' => false,
+ 'line_numbers' => false,
+ 'locale' => nil,
+ 'locale_dir' => 'locale',
+ 'locale_name' => nil,
+ 'main_page' => nil,
+ 'markup' => 'rdoc',
+ 'output_decoration' => true,
+ 'page_dir' => nil,
+ 'rdoc_include' => [],
+ 'show_hash' => false,
+ 'static_path' => [],
+ 'tab_width' => 8,
+ 'template_stylesheets' => [],
+ 'title' => nil,
+ 'visibility' => :protected,
+ 'webcvs' => nil,
+ }
+
+ assert_equal expected, coder
+ end
+
+ def test_encode_with_trim_paths
+ subdir = nil
+ coder = {}
+ class << coder; alias add []=; end
+
+ temp_dir do |dir|
+ FileUtils.mkdir 'project'
+ FileUtils.mkdir 'dir'
+ FileUtils.touch 'file'
+
+ Dir.chdir 'project' do
+ subdir = File.expand_path 'subdir'
+ FileUtils.mkdir 'subdir'
+ @options.parse %w[
+ --copy subdir
+ --copy ../file
+ --copy ../
+ --copy /
+ --include subdir
+ --include ../dir
+ --include ../
+ --include /
+ ]
+
+ @options.encode_with coder
+ end
+ end
+
+ assert_equal [subdir], coder['rdoc_include']
+
+ assert_equal [subdir], coder['static_path']
+ end
+
+ def test_encoding_default
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ assert_equal Encoding::UTF_8, @options.encoding
+ end
+
+ def test_generator_descriptions
+ # HACK autotest/isolate should take care of this
+ RDoc::RDoc::GENERATORS.clear
+ RDoc::RDoc::GENERATORS['darkfish'] = RDoc::Generator::Darkfish
+ RDoc::RDoc::GENERATORS['ri'] = RDoc::Generator::RI
+
+ expected = <<-EXPECTED.chomp
+ darkfish - HTML generator, written by Michael Granger
+ ri - creates ri data files
+ EXPECTED
+
+ assert_equal expected, @options.generator_descriptions
+ end
+
+ def test_init_with_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+ RDoc.load_yaml
+
+ @options.encoding = Encoding::IBM437
+
+ options = YAML.load YAML.dump @options
+
+ assert_equal Encoding::IBM437, options.encoding
+ end
+
+ def test_init_with_trim_paths
+ RDoc.load_yaml
+
+ yaml = <<-YAML
+--- !ruby/object:RDoc::Options
+static_path:
+- /etc
+rdoc_include:
+- /etc
+ YAML
+
+ options = YAML.load yaml
+
+ assert_empty options.rdoc_include
+ assert_empty options.static_path
+ end
+
+ def test_parse_copy_files_file_relative
+ file = File.basename __FILE__
+ expected = File.expand_path __FILE__
+
+ Dir.chdir File.expand_path('..', __FILE__) do
+ @options.parse %W[--copy-files #{file}]
+
+ assert_equal [expected], @options.static_path
+ end
+ end
+
+ def test_parse_copy_files_file_absolute
+ @options.parse %W[--copy-files #{File.expand_path __FILE__}]
+
+ assert_equal [File.expand_path(__FILE__)], @options.static_path
+ end
+
+ def test_parse_copy_files_directory_relative
+ @options.parse %w[--copy-files .]
+
+ assert_equal [@pwd], @options.static_path
+ end
+
+ def test_parse_copy_files_directory_absolute
+ @options.parse %w[--copy-files /]
+
+ assert_equal 1, @options.static_path.length
+
+ assert_match %r%^([A-Z]:)?/$%i, @options.static_path.first
+ end
+
+ def test_parse_coverage
+ @options.parse %w[--dcov]
+
+ assert @options.coverage_report
+ assert @options.force_update
+ end
+
+ def test_parse_coverage_no
+ @options.parse %w[--no-dcov]
+
+ refute @options.coverage_report
+ end
+
+ def test_parse_coverage_level_1
+ @options.parse %w[--dcov=1]
+
+ assert_equal 1, @options.coverage_report
+ end
+
+ def test_parse_dash_p
+ out, err = capture_io do
+ @options.parse %w[-p]
+ end
+
+ assert @options.pipe
+ refute_match %r%^Usage: %, err
+ refute_match %r%^invalid options%, err
+
+ assert_empty out
+ end
+
+ def test_parse_dash_p_files
+ out, err = capture_io do
+ @options.parse ['-p', File.expand_path(__FILE__)]
+ end
+
+ refute @options.pipe
+ refute_match %r%^Usage: %, err
+ assert_match %r%^invalid options: -p .with files.%, err
+
+ assert_empty out
+ end
+
+ def test_parse_default
+ @options.parse []
+
+ assert_equal RDoc::Generator::Darkfish, @options.generator
+ assert_equal 'darkfish', @options.template
+ assert_match %r%rdoc/generator/template/darkfish$%, @options.template_dir
+ end
+
+ def test_parse_deprecated
+ dep_hash = RDoc::Options::DEPRECATED
+ options = dep_hash.keys.sort
+
+ out, err = capture_io do
+ @options.parse options
+ end
+
+ dep_hash.each_pair do |opt, message|
+ assert_match %r%.*#{opt}.+#{message}%, err
+ end
+
+ assert_empty out
+ end
+
+ def test_parse_dry_run
+ @options.parse %w[--dry-run]
+
+ assert @options.dry_run
+ end
+
+ def test_parse_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ @options.parse %w[--encoding Big5]
+
+ assert_equal Encoding::Big5, @options.encoding
+ assert_equal 'Big5', @options.charset
+ end
+
+ def test_parse_encoding_invalid
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ out, err = capture_io do
+ @options.parse %w[--encoding invalid]
+ end
+
+ assert_match %r%^invalid options: --encoding invalid%, err
+
+ assert_empty out
+ end
+
+ def test_parse_formatter
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish --format ri]
+ end
+
+ assert_equal 'invalid option: --format generator already set to darkfish',
+ e.message
+ end
+
+ def test_parse_formatter_ri
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish --ri]
+ end
+
+ assert_equal 'invalid option: --ri generator already set to darkfish',
+ e.message
+
+ @options = RDoc::Options.new
+
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish -r]
+ end
+
+ assert_equal 'invalid option: -r generator already set to darkfish',
+ e.message
+ end
+
+ def test_parse_formatter_ri_site
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish --ri-site]
+ end
+
+ assert_equal 'invalid option: --ri-site generator already set to darkfish',
+ e.message
+
+ @options = RDoc::Options.new
+
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish -R]
+ end
+
+ assert_equal 'invalid option: -R generator already set to darkfish',
+ e.message
+ end
+
+ def test_parse_h
+ out, = capture_io do
+ begin
+ @options.parse %w[-h]
+ rescue SystemExit
+ end
+ end
+
+ assert_equal 1, out.scan(/HTML generator options:/).length
+ assert_equal 1, out.scan(/ri generator options:/). length
+ end
+
+ def test_parse_help
+ out, = capture_io do
+ begin
+ @options.parse %w[--help]
+ rescue SystemExit
+ end
+ end
+
+ assert_equal 1, out.scan(/HTML generator options:/).length
+ assert_equal 1, out.scan(/ri generator options:/). length
+ end
+
+ def test_parse_help_extra_generator
+ RDoc::RDoc::GENERATORS['test'] = Class.new do
+ def self.setup_options options
+ op = options.option_parser
+
+ op.separator 'test generator options:'
+ end
+ end
+
+ out, = capture_io do
+ begin
+ @options.parse %w[--help]
+ rescue SystemExit
+ end
+ end
+
+ assert_equal 1, out.scan(/HTML generator options:/).length
+ assert_equal 1, out.scan(/ri generator options:/). length
+ assert_equal 1, out.scan(/test generator options:/).length
+ end
+
+ def test_parse_format_for_extra_generator
+ RDoc::RDoc::GENERATORS['test'] = Class.new do
+ def self.setup_options options
+ op = options.option_parser
+
+ op.separator 'test generator options:'
+ end
+ end
+
+ @options.setup_generator 'test'
+
+ assert_equal @options.generator_name, 'test'
+ end
+
+ def test_parse_ignore_invalid
+ out, err = capture_io do
+ @options.parse %w[--ignore-invalid --bogus]
+ end
+
+ refute_match %r%^Usage: %, err
+ assert_match %r%^invalid options: --bogus%, err
+
+ assert_empty out
+ end
+
+ def test_parse_ignore_invalid_default
+ out, err = capture_io do
+ @options.parse %w[--bogus --main BLAH]
+ end
+
+ refute_match %r%^Usage: %, err
+ assert_match %r%^invalid options: --bogus%, err
+
+ assert_equal 'BLAH', @options.main_page
+
+ assert_empty out
+ end
+
+ def test_parse_ignore_invalid_no
+ out, err = capture_io do
+ assert_raises SystemExit do
+ @options.parse %w[--no-ignore-invalid --bogus=arg --bobogus --visibility=extended]
+ end
+ end
+
+ assert_match %r%^Usage: %, err
+ assert_match %r%^invalid options: --bogus=arg, --bobogus, --visibility=extended%, err
+
+ assert_empty out
+ end
+
+ def test_parse_ignore_invalid_no_quiet
+ out, err = capture_io do
+ assert_raises SystemExit do
+ @options.parse %w[--quiet --no-ignore-invalid --bogus=arg --bobogus --visibility=extended]
+ end
+ end
+
+ refute_match %r%^Usage: %, err
+ assert_match %r%^invalid options: --bogus=arg, --bobogus, --visibility=extended%, err
+
+ assert_empty out
+ end
+
+ def test_ignore_needless_arg
+ out, err = capture_io do
+ @options.parse %w[--ri=foo]
+ end
+
+ assert_match %r%^invalid options: --ri=foo%, err
+
+ assert_empty out
+ end
+
+ def test_ignore_missing_arg
+ out, err = capture_io do
+ @options.parse %w[--copy-files]
+ end
+
+ assert_match %r%^invalid options: --copy-files%, err
+
+ assert_empty out
+ end
+
+ def test_parse_main
+ out, err = capture_io do
+ @options.parse %w[--main MAIN]
+ end
+
+ assert_empty out
+ assert_empty err
+
+ assert_equal 'MAIN', @options.main_page
+ end
+
+ def test_parse_markup
+ out, err = capture_io do
+ @options.parse %w[--markup tomdoc]
+ end
+
+ assert_empty out
+ assert_empty err
+
+ assert_equal 'tomdoc', @options.markup
+ end
+
+ def test_parse_page_dir
+ assert_nil @options.page_dir
+
+ out, err = capture_io do
+ @options.parse %W[--page-dir #{Dir.tmpdir}]
+ end
+
+ assert_empty out
+ assert_empty err
+
+ expected =
+ Pathname(Dir.tmpdir).expand_path.relative_path_from @options.root
+
+ assert_equal expected, @options.page_dir
+ assert_equal [Dir.tmpdir], @options.files
+ end
+
+ def test_parse_page_dir_root
+ assert_nil @options.page_dir
+
+ Dir.mktmpdir do |dir|
+ abs_root = dir
+ abs_page_dir = File.join dir, 'pages'
+ FileUtils.mkdir abs_page_dir
+
+ out, err = capture_io do
+ @options.parse %W[--page-dir #{abs_page_dir} --root #{abs_root}]
+ end
+
+ assert_empty out
+ assert_empty err
+
+ assert_equal Pathname('pages'), @options.page_dir
+ assert_equal [abs_page_dir], @options.files
+ end
+ end
+
+ def test_parse_ri_site
+ @options.parse %w[--ri-site]
+
+ assert_equal RDoc::Generator::RI, @options.generator
+ assert_equal RDoc::RI::Paths.site_dir, @options.op_dir
+ end
+
+ def test_parse_root
+ assert_equal Pathname(Dir.pwd), @options.root
+
+ out, err = capture_io do
+ @options.parse %W[--root #{Dir.tmpdir}]
+ end
+
+ assert_empty out
+ assert_empty err
+
+ assert_equal Pathname(Dir.tmpdir), @options.root
+ assert_includes @options.rdoc_include, @options.root.to_s
+ end
+
+ def test_parse_tab_width
+ @options.parse %w[--tab-width=1]
+ assert_equal 1, @options.tab_width
+
+ @options.parse %w[-w2]
+ assert_equal 2, @options.tab_width
+
+ _, err = capture_io do
+ @options.parse %w[-w=2]
+ end
+
+ assert_match 'invalid options', err
+
+ _, err = capture_io do
+ @options.parse %w[-w0]
+ end
+
+ assert_match 'invalid options', err
+ end
+
+ def test_parse_template
+ out, err = capture_io do
+ @options.parse %w[--template darkfish]
+ end
+
+ assert_empty out
+ assert_empty err
+
+ assert_equal 'darkfish', @options.template
+
+ assert_match %r%rdoc/generator/template/darkfish$%, @options.template_dir
+ end
+
+ def test_parse_template_nonexistent
+ out, err = capture_io do
+ @options.parse %w[--template NONEXISTENT]
+ end
+
+ assert_empty out
+ assert_equal "could not find template NONEXISTENT\n", err
+
+ assert_equal 'darkfish', @options.template
+ assert_match %r%rdoc/generator/template/darkfish$%, @options.template_dir
+ end
+
+ def test_parse_template_load_path
+ orig_LOAD_PATH = $LOAD_PATH.dup
+
+ template_dir = nil
+
+ Dir.mktmpdir do |dir|
+ $LOAD_PATH << dir
+
+ template_dir = File.join dir, 'rdoc', 'generator', 'template', 'load_path'
+
+ FileUtils.mkdir_p template_dir
+
+ out, err = capture_io do
+ @options.parse %w[--template load_path]
+ end
+
+ assert_empty out
+ assert_empty err
+ end
+
+ assert_equal 'load_path', @options.template
+ assert_equal template_dir, @options.template_dir
+ ensure
+ $LOAD_PATH.replace orig_LOAD_PATH
+ end
+
+ def test_parse_visibility
+ @options.parse %w[--visibility=public]
+ assert_equal :public, @options.visibility
+
+ @options.parse %w[--visibility=protected]
+ assert_equal :protected, @options.visibility
+
+ @options.parse %w[--visibility=private]
+ assert_equal :private, @options.visibility
+
+ @options.parse %w[--visibility=nodoc]
+ assert_equal :nodoc, @options.visibility
+ end
+
+ def test_parse_write_options
+ tmpdir = File.join Dir.tmpdir, "test_rdoc_options_#{$$}"
+ FileUtils.mkdir_p tmpdir
+
+ Dir.chdir tmpdir do
+ e = assert_raises SystemExit do
+ @options.parse %w[--write-options]
+ end
+
+ assert_equal 0, e.status
+
+ assert File.exist? '.rdoc_options'
+ end
+ ensure
+ FileUtils.rm_rf tmpdir
+ end
+
+ def test_parse_extension_alias
+ out, err = capture_io do
+ @options.parse %w[--extension foobar=rdoc]
+ end
+
+ assert_includes RDoc::Parser.parsers, [/\.foobar$/, RDoc::Parser::Simple]
+
+ assert_empty out
+ assert_empty err
+ end
+
+ def test_setup_generator
+ test_generator = Class.new do
+ def self.setup_options op
+ @op = op
+ end
+
+ def self.op() @op end
+ end
+
+ RDoc::RDoc::GENERATORS['test'] = test_generator
+
+ @options.setup_generator 'test'
+
+ assert_equal test_generator, @options.generator
+ assert_equal [test_generator], @options.generator_options
+
+ assert_equal @options, test_generator.op
+ ensure
+ RDoc::RDoc::GENERATORS.delete 'test'
+ end
+
+ def test_setup_generator_no_option_parser
+ test_generator = Class.new do
+ def self.setup_options op
+ op.option_parser.separator nil
+ @op = op
+ end
+
+ def self.op() @op end
+ end
+
+ RDoc::RDoc::GENERATORS['test'] = test_generator
+
+ @options.setup_generator 'test'
+
+ assert_equal test_generator, @options.generator
+ assert_equal [test_generator], @options.generator_options
+
+ assert_equal @options, test_generator.op
+ ensure
+ RDoc::RDoc::GENERATORS.delete 'test'
+ end
+
+ def test_update_output_dir
+ assert @options.update_output_dir
+
+ @options.update_output_dir = false
+
+ refute @options.update_output_dir
+ end
+
+ def test_warn
+ out, err = capture_io do
+ @options.warn "warnings off"
+ end
+
+ assert_empty out
+ assert_empty err
+
+ @options.verbosity = 2
+
+ out, err = verbose_capture_io do
+ @options.warn "warnings on"
+ end
+
+ assert_empty out
+ assert_equal "warnings on\n", err
+ end
+
+ def test_write_options
+ temp_dir do |dir|
+ @options.write_options
+
+ assert File.exist? '.rdoc_options'
+
+ assert_equal @options, YAML.load(File.read('.rdoc_options'))
+ end
+ end
+
+ def test_version
+ out, _ = capture_io do
+ begin
+ @options.parse %w[--version]
+ rescue SystemExit
+ end
+ end
+
+ assert out.include?(RDoc::VERSION)
+
+ out, _ = capture_io do
+ begin
+ @options.parse %w[-v]
+ rescue SystemExit
+ end
+ end
+
+ assert out.include?(RDoc::VERSION)
+ end
+
+ def test_visibility
+ @options.visibility = :all
+ assert_equal :private, @options.visibility
+ end
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_parser.rb b/jni/ruby/test/rdoc/test_rdoc_parser.rb
new file mode 100644
index 0000000..34d4486
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_parser.rb
@@ -0,0 +1,327 @@
+# -*- coding: us-ascii -*-
+
+require 'rdoc/test_case'
+
+class TestRDocParser < RDoc::TestCase
+
+ def setup
+ super
+
+ @RP = RDoc::Parser
+ @binary_dat = File.expand_path '../binary.dat', __FILE__
+
+ @fn = 'file.rb'
+ @top_level = RDoc::TopLevel.new @fn
+ @options = RDoc::Options.new
+ end
+
+ def test_class_binary_eh_ISO_2022_JP
+ iso_2022_jp = File.join Dir.tmpdir, "test_rdoc_parser_#{$$}.rd"
+
+ open iso_2022_jp, 'wb' do |io|
+ io.write "# coding: ISO-2022-JP\n"
+ io.write ":\e$B%3%^%s%I\e(B:\n"
+ end
+
+ refute @RP.binary? iso_2022_jp
+ ensure
+ File.unlink iso_2022_jp
+ end
+
+ def test_class_binary_eh_marshal
+ marshal = File.join Dir.tmpdir, "test_rdoc_parser_#{$$}.marshal"
+ open marshal, 'wb' do |io|
+ io.write Marshal.dump('')
+ io.write 'lots of text ' * 500
+ end
+
+ assert @RP.binary?(marshal)
+ ensure
+ File.unlink marshal
+ end
+
+ def test_class_binary_japanese_text
+ file_name = File.expand_path '../test.ja.txt', __FILE__
+ refute @RP.binary?(file_name)
+ end
+
+ def test_class_binary_large_japanese_rdoc
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ capture_io do
+ begin
+ extenc, Encoding.default_external =
+ Encoding.default_external, Encoding::US_ASCII
+ file_name = File.expand_path '../test.ja.largedoc', __FILE__
+ assert !@RP.binary?(file_name)
+ ensure
+ Encoding.default_external = extenc
+ end
+ end
+ end
+
+ def test_class_binary_japanese_rdoc
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ file_name = File.expand_path '../test.ja.rdoc', __FILE__
+ refute @RP.binary?(file_name)
+ end
+
+ def test_class_can_parse
+ assert_equal @RP.can_parse(__FILE__), @RP::Ruby
+
+ readme_file_name = File.expand_path '../test.txt', __FILE__
+
+ assert_equal @RP::Simple, @RP.can_parse(readme_file_name)
+
+ assert_equal @RP::Simple, @RP.can_parse(@binary_dat)
+
+ jtest_file_name = File.expand_path '../test.ja.txt', __FILE__
+ assert_equal @RP::Simple, @RP.can_parse(jtest_file_name)
+
+ jtest_rdoc_file_name = File.expand_path '../test.ja.rdoc', __FILE__
+ assert_equal @RP::Simple, @RP.can_parse(jtest_rdoc_file_name)
+
+ readme_file_name = File.expand_path '../README', __FILE__
+ assert_equal @RP::Simple, @RP.can_parse(readme_file_name)
+
+ jtest_largerdoc_file_name = File.expand_path '../test.ja.largedoc', __FILE__
+ assert_equal @RP::Simple, @RP.can_parse(jtest_largerdoc_file_name)
+
+ @RP.alias_extension 'rdoc', 'largedoc'
+ assert_equal @RP::Simple, @RP.can_parse(jtest_largerdoc_file_name)
+ end
+
+ def test_class_for_executable
+ temp_dir do
+ content = "#!/usr/bin/env ruby -w\n"
+ open 'app', 'w' do |io| io.write content end
+ app = @store.add_file 'app'
+
+ parser = @RP.for app, 'app', content, @options, :stats
+
+ assert_kind_of RDoc::Parser::Ruby, parser
+
+ assert_equal 'app', parser.file_name
+ end
+ end
+
+ def test_class_for_forbidden
+ skip 'chmod not supported' if Gem.win_platform?
+
+ tf = Tempfile.open 'forbidden' do |io|
+ begin
+ File.chmod 0000, io.path
+ forbidden = @store.add_file io.path
+
+ parser = @RP.for forbidden, 'forbidden', '', @options, :stats
+
+ assert_nil parser
+ ensure
+ File.chmod 0400, io.path
+ end
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+ end
+
+ def test_class_for_modeline
+ temp_dir do
+ content = "# -*- rdoc -*-\n= NEWS\n"
+
+ open 'NEWS', 'w' do |io| io.write content end
+ app = @store.add_file 'NEWS'
+
+ parser = @RP.for app, 'NEWS', content, @options, :stats
+
+ assert_kind_of RDoc::Parser::Simple, parser
+
+ assert_equal "= NEWS\n", parser.content
+ end
+ end
+
+ def test_can_parse_modeline
+ readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
+
+ open readme_ext, 'w' do |io|
+ io.puts "# README.EXT - -*- rdoc -*- created at: Mon Aug 7 16:45:54 JST 1995"
+ io.puts
+ io.puts "This document explains how to make extension libraries for Ruby."
+ end
+
+ assert_equal RDoc::Parser::Simple, @RP.can_parse(readme_ext)
+ ensure
+ File.unlink readme_ext
+ end
+
+ ##
+ # Selenium hides a .jar file using a .txt extension.
+
+ def test_class_can_parse_zip
+ hidden_zip = File.expand_path '../hidden.zip.txt', __FILE__
+ assert_nil @RP.can_parse(hidden_zip)
+ end
+
+ def test_check_modeline
+ readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
+
+ open readme_ext, 'w' do |io|
+ io.puts "# README.EXT - -*- RDoc -*- created at: Mon Aug 7 16:45:54 JST 1995"
+ io.puts
+ io.puts "This document explains how to make extension libraries for Ruby."
+ end
+
+ assert_equal 'rdoc', @RP.check_modeline(readme_ext)
+ ensure
+ File.unlink readme_ext
+ end
+
+ def test_check_modeline_coding
+ readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
+
+ open readme_ext, 'w' do |io|
+ io.puts "# -*- coding: utf-8 -*-"
+ end
+
+ assert_nil @RP.check_modeline readme_ext
+ ensure
+ File.unlink readme_ext
+ end
+
+ def test_check_modeline_with_other
+ readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
+
+ open readme_ext, 'w' do |io|
+ io.puts "# README.EXT - -*- mode: RDoc; indent-tabs-mode: nil -*-"
+ io.puts
+ io.puts "This document explains how to make extension libraries for Ruby."
+ end
+
+ assert_equal 'rdoc', @RP.check_modeline(readme_ext)
+ ensure
+ File.unlink readme_ext
+ end
+
+ def test_check_modeline_no_modeline
+ readme_ext = File.join Dir.tmpdir, "README.EXT.#{$$}"
+
+ open readme_ext, 'w' do |io|
+ io.puts "This document explains how to make extension libraries for Ruby."
+ end
+
+ assert_nil @RP.check_modeline(readme_ext)
+ ensure
+ File.unlink readme_ext
+ end
+
+ def test_class_for_binary
+ rp = @RP.dup
+
+ class << rp
+ alias old_can_parse can_parse
+ end
+
+ def rp.can_parse(*args) nil end
+
+ assert_nil @RP.for(nil, @binary_dat, nil, nil, nil)
+ end
+
+ def test_class_for_markup
+ content = <<-CONTENT
+# coding: utf-8 markup: rd
+ CONTENT
+
+ parser = @RP.for @top_level, __FILE__, content, @options, nil
+
+ assert_kind_of @RP::RD, parser
+ end
+
+ def test_class_use_markup
+ content = <<-CONTENT
+# coding: utf-8 markup: rd
+ CONTENT
+
+ parser = @RP.use_markup content
+
+ assert_equal @RP::RD, parser
+ end
+
+ def test_class_use_markup_markdown
+ content = <<-CONTENT
+# coding: utf-8 markup: markdown
+ CONTENT
+
+ parser = @RP.use_markup content
+
+ assert_equal @RP::Ruby, parser
+ end
+
+ def test_class_use_markup_modeline
+ content = <<-CONTENT
+# -*- coding: utf-8 -*-
+# markup: rd
+ CONTENT
+
+ parser = @RP.use_markup content
+
+ assert_equal @RP::RD, parser
+ end
+
+ def test_class_use_markup_modeline_shebang
+ content = <<-CONTENT
+#!/bin/sh
+/* -*- coding: utf-8 -*-
+ * markup: rd
+ */
+ CONTENT
+
+ parser = @RP.use_markup content
+
+ assert_equal @RP::RD, parser
+ end
+
+ def test_class_use_markup_shebang
+ content = <<-CONTENT
+#!/usr/bin/env ruby
+# coding: utf-8 markup: rd
+ CONTENT
+
+ parser = @RP.use_markup content
+
+ assert_equal @RP::RD, parser
+ end
+
+ def test_class_use_markup_tomdoc
+ content = <<-CONTENT
+# coding: utf-8 markup: tomdoc
+ CONTENT
+
+ parser = @RP.use_markup content
+
+ assert_equal @RP::Ruby, parser
+ end
+
+ def test_class_use_markup_none
+ parser = @RP.use_markup ''
+
+ assert_nil parser
+ end
+
+ def test_class_use_markup_unknown
+ content = <<-CONTENT
+# :markup: RDoc
+ CONTENT
+
+ parser = @RP.use_markup content
+
+ assert_nil parser
+ end
+
+ def test_initialize
+ @RP.new @top_level, @fn, '', @options, nil
+
+ assert_equal @RP, @top_level.parser
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_parser_c.rb b/jni/ruby/test/rdoc/test_rdoc_parser_c.rb
new file mode 100644
index 0000000..71ffce5
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_parser_c.rb
@@ -0,0 +1,1896 @@
+require 'rdoc/test_case'
+
+=begin
+ TODO: test call-seq parsing
+
+/*
+ * call-seq:
+ * ARGF.readlines(sep=$/) -> array
+ * ARGF.readlines(limit) -> array
+ * ARGF.readlines(sep, limit) -> array
+ *
+ * ARGF.to_a(sep=$/) -> array
+ * ARGF.to_a(limit) -> array
+ * ARGF.to_a(sep, limit) -> array
+ *
+ * Reads +ARGF+'s current file in its entirety, returning an +Array+ of its
+ * lines, one line per element. Lines are assumed to be separated by _sep_.
+ *
+ * lines = ARGF.readlines
+ * lines[0] #=> "This is line one\n"
+ */
+
+assert call-seq did not stop at first empty line
+
+/*
+ * call-seq:
+ *
+ * flt ** other -> float
+ *
+ * Raises <code>float</code> the <code>other</code> power.
+ *
+ * 2.0**3 #=> 8.0
+ */
+
+assert call-seq correct (bug: was empty)
+
+/* call-seq: flt ** other -> float */
+
+assert call-seq correct
+
+=end
+
+class TestRDocParserC < RDoc::TestCase
+
+ def setup
+ super
+
+ @tempfile = Tempfile.new self.class.name
+ filename = @tempfile.path
+
+ @top_level = @store.add_file filename
+ @fn = filename
+ @options = RDoc::Options.new
+ @options.verbosity = 2
+ @stats = RDoc::Stats.new @store, 0
+ end
+
+ def teardown
+ super
+
+ @tempfile.close!
+ end
+
+ def test_class_can_parse
+ c_parser = RDoc::Parser::C
+
+ temp_dir do
+ FileUtils.touch 'file.C'
+ assert_equal c_parser, c_parser.can_parse('file.C')
+
+ FileUtils.touch 'file.CC'
+ assert_equal c_parser, c_parser.can_parse('file.CC')
+
+ FileUtils.touch 'file.H'
+ assert_equal c_parser, c_parser.can_parse('file.H')
+
+ FileUtils.touch 'file.HH'
+ assert_equal c_parser, c_parser.can_parse('file.HH')
+
+ FileUtils.touch 'file.c'
+ assert_equal c_parser, c_parser.can_parse('file.c')
+
+ FileUtils.touch 'file.cc'
+ assert_equal c_parser, c_parser.can_parse('file.cc')
+
+ FileUtils.touch 'file.cpp'
+ assert_equal c_parser, c_parser.can_parse('file.cpp')
+
+ FileUtils.touch 'file.cxx'
+ assert_equal c_parser, c_parser.can_parse('file.cxx')
+
+ FileUtils.touch 'file.h'
+ assert_equal c_parser, c_parser.can_parse('file.h')
+
+ FileUtils.touch 'file.hh'
+ assert_equal c_parser, c_parser.can_parse('file.hh')
+
+ FileUtils.touch 'file.y'
+ assert_equal c_parser, c_parser.can_parse('file.y')
+ end
+ end
+
+ def test_initialize
+ some_ext = @top_level.add_class RDoc::NormalClass, 'SomeExt'
+ @top_level.add_class RDoc::SingleClass, 'SomeExtSingle'
+
+ @store.cache[:c_class_variables] = {
+ @fn => { 'cSomeExt' => 'SomeExt' }
+ }
+
+ @store.cache[:c_singleton_class_variables] = {
+ @fn => { 'cSomeExtSingle' => 'SomeExtSingle' }
+ }
+
+ parser = RDoc::Parser::C.new @top_level, @fn, '', @options, @stats
+
+ expected = { 'cSomeExt' => some_ext }
+ assert_equal expected, parser.classes
+
+ expected = { 'cSomeExtSingle' => 'SomeExtSingle' }
+ assert_equal expected, parser.singleton_classes
+
+ expected = {
+ 'cSomeExt' => 'SomeExt',
+ 'cSomeExtSingle' => 'SomeExtSingle',
+ }
+ known_classes = parser.known_classes.delete_if do |key, _|
+ RDoc::KNOWN_CLASSES.keys.include? key
+ end
+
+ assert_equal expected, known_classes
+ end
+
+ def test_do_attr_rb_attr
+ content = <<-EOF
+void Init_Blah(void) {
+ cBlah = rb_define_class("Blah", rb_cObject);
+
+ /*
+ * This is an accessor
+ */
+ rb_attr(cBlah, rb_intern("accessor"), 1, 1, Qfalse);
+
+ /*
+ * This is a reader
+ */
+ rb_attr(cBlah, rb_intern("reader"), 1, 0, Qfalse);
+
+ /*
+ * This is a writer
+ */
+ rb_attr(cBlah, rb_intern("writer"), 0, 1, Qfalse);
+}
+ EOF
+
+ klass = util_get_class content, 'cBlah'
+
+ attrs = klass.attributes
+ assert_equal 3, attrs.length, attrs.inspect
+
+ accessor = attrs.shift
+ assert_equal 'accessor', accessor.name
+ assert_equal 'RW', accessor.rw
+ assert_equal 'This is an accessor', accessor.comment.text
+ assert_equal @top_level, accessor.file
+
+ reader = attrs.shift
+ assert_equal 'reader', reader.name
+ assert_equal 'R', reader.rw
+ assert_equal 'This is a reader', reader.comment.text
+
+ writer = attrs.shift
+ assert_equal 'writer', writer.name
+ assert_equal 'W', writer.rw
+ assert_equal 'This is a writer', writer.comment.text
+ end
+
+ def test_do_attr_rb_define_attr
+ content = <<-EOF
+void Init_Blah(void) {
+ cBlah = rb_define_class("Blah", rb_cObject);
+
+ /*
+ * This is an accessor
+ */
+ rb_define_attr(cBlah, "accessor", 1, 1);
+}
+ EOF
+
+ klass = util_get_class content, 'cBlah'
+
+ attrs = klass.attributes
+ assert_equal 1, attrs.length, attrs.inspect
+
+ accessor = attrs.shift
+ assert_equal 'accessor', accessor.name
+ assert_equal 'RW', accessor.rw
+ assert_equal 'This is an accessor', accessor.comment.text
+ assert_equal @top_level, accessor.file
+ end
+
+ def test_do_aliases
+ content = <<-EOF
+/*
+ * This should show up as an alias with documentation
+ */
+VALUE blah(VALUE klass, VALUE year) {
+}
+
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+
+ rb_define_method(cDate, "blah", blah, 1);
+
+ rb_define_alias(cDate, "bleh", "blah");
+}
+ EOF
+
+ klass = util_get_class content, 'cDate'
+
+ methods = klass.method_list
+ assert_equal 2, methods.length
+ assert_equal 'bleh', methods.last.name
+ assert_equal 'blah', methods.last.is_alias_for.name
+
+ assert_equal @top_level, methods.last.is_alias_for.file
+ assert_equal @top_level, methods.last.file
+ end
+
+ def test_do_aliases_singleton
+ content = <<-EOF
+/*
+ * This should show up as a method with documentation
+ */
+VALUE blah(VALUE klass, VALUE year) {
+}
+
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+ sDate = rb_singleton_class(cDate);
+
+ rb_define_method(sDate, "blah", blah, 1);
+
+ /*
+ * This should show up as an alias
+ */
+ rb_define_alias(sDate, "bleh", "blah");
+}
+ EOF
+
+ klass = util_get_class content, 'cDate'
+
+ methods = klass.method_list
+
+ assert_equal 2, methods.length
+ assert_equal 'bleh', methods.last.name
+ assert methods.last.singleton
+ assert_equal 'blah', methods.last.is_alias_for.name
+ assert_equal 'This should show up as an alias', methods.last.comment.text
+ end
+
+ def test_do_classes_boot_class
+ content = <<-EOF
+/* Document-class: Foo
+ * this is the Foo boot class
+ */
+VALUE cFoo = boot_defclass("Foo", rb_cObject);
+ EOF
+
+ klass = util_get_class content, 'cFoo'
+ assert_equal "this is the Foo boot class", klass.comment.text
+ assert_equal 'Object', klass.superclass
+ end
+
+ def test_do_classes_boot_class_nil
+ content = <<-EOF
+/* Document-class: Foo
+ * this is the Foo boot class
+ */
+VALUE cFoo = boot_defclass("Foo", 0);
+ EOF
+
+ klass = util_get_class content, 'cFoo'
+ assert_equal "this is the Foo boot class", klass.comment.text
+ assert_equal nil, klass.superclass
+ end
+
+ def test_do_aliases_missing_class
+ content = <<-EOF
+void Init_Blah(void) {
+ rb_define_alias(cDate, "b", "a");
+}
+ EOF
+
+ _, err = verbose_capture_io do
+ refute util_get_class(content, 'cDate')
+ end
+
+ assert_equal "Enclosing class or module \"cDate\" for alias b a is not known\n",
+ err
+ end
+
+ def test_do_classes_class
+ content = <<-EOF
+/* Document-class: Foo
+ * this is the Foo class
+ */
+VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ EOF
+
+ klass = util_get_class content, 'cFoo'
+ assert_equal "this is the Foo class", klass.comment.text
+ end
+
+ def test_do_classes_struct
+ content = <<-EOF
+/* Document-class: Foo
+ * this is the Foo class
+ */
+VALUE cFoo = rb_struct_define_without_accessor(
+ "Foo", rb_cObject, foo_alloc,
+ "some", "various", "fields", NULL);
+ EOF
+
+ klass = util_get_class content, 'cFoo'
+ assert_equal "this is the Foo class", klass.comment.text
+ end
+
+ def test_do_classes_class_under
+ content = <<-EOF
+/* Document-class: Kernel::Foo
+ * this is the Foo class under Kernel
+ */
+VALUE cFoo = rb_define_class_under(rb_mKernel, "Foo", rb_cObject);
+ EOF
+
+ klass = util_get_class content, 'cFoo'
+ assert_equal 'Kernel::Foo', klass.full_name
+ assert_equal "this is the Foo class under Kernel", klass.comment.text
+ end
+
+ def test_do_classes_class_under_rb_path2class
+ content = <<-EOF
+/* Document-class: Kernel::Foo
+ * this is Kernel::Foo < A::B
+ */
+VALUE cFoo = rb_define_class_under(rb_mKernel, "Foo", rb_path2class("A::B"));
+ EOF
+
+ klass = util_get_class content, 'cFoo'
+
+ assert_equal 'Kernel::Foo', klass.full_name
+ assert_equal 'A::B', klass.superclass
+ assert_equal 'this is Kernel::Foo < A::B', klass.comment.text
+ end
+
+ def test_do_classes_singleton
+ content = <<-EOF
+VALUE cFoo = rb_define_class("Foo", rb_cObject);
+VALUE cFooS = rb_singleton_class(cFoo);
+ EOF
+
+ util_get_class content, 'cFooS'
+
+ assert_equal 'Foo', @parser.singleton_classes['cFooS']
+ end
+
+ def test_do_classes_module
+ content = <<-EOF
+/* Document-module: Foo
+ * this is the Foo module
+ */
+VALUE mFoo = rb_define_module("Foo");
+ EOF
+
+ klass = util_get_class content, 'mFoo'
+ assert_equal "this is the Foo module", klass.comment.text
+ end
+
+ def test_do_classes_module_under
+ content = <<-EOF
+/* Document-module: Kernel::Foo
+ * this is the Foo module under Kernel
+ */
+VALUE mFoo = rb_define_module_under(rb_mKernel, "Foo");
+ EOF
+
+ klass = util_get_class content, 'mFoo'
+ assert_equal "this is the Foo module under Kernel", klass.comment.text
+ end
+
+ def test_do_constants
+ content = <<-EOF
+#include <ruby.h>
+
+void Init_foo(){
+ VALUE cFoo = rb_define_class("Foo", rb_cObject);
+
+ /* 300: The highest possible score in bowling */
+ rb_define_const(cFoo, "PERFECT", INT2FIX(300));
+
+ /* Huzzah!: What you cheer when you roll a perfect game */
+ rb_define_const(cFoo, "CHEER", rb_str_new2("Huzzah!"));
+
+ /* TEST\:TEST: Checking to see if escaped colon works */
+ rb_define_const(cFoo, "TEST", rb_str_new2("TEST:TEST"));
+
+ /* \\: The file separator on MS Windows */
+ rb_define_const(cFoo, "MSEPARATOR", rb_str_new2("\\"));
+
+ /* /: The file separator on Unix */
+ rb_define_const(cFoo, "SEPARATOR", rb_str_new2("/"));
+
+ /* C:\\Program Files\\Stuff: A directory on MS Windows */
+ rb_define_const(cFoo, "STUFF", rb_str_new2("C:\\Program Files\\Stuff"));
+
+ /* Default definition */
+ rb_define_const(cFoo, "NOSEMI", INT2FIX(99));
+
+ rb_define_const(cFoo, "NOCOMMENT", rb_str_new2("No comment"));
+
+ /*
+ * Multiline comment goes here because this comment spans multiple lines.
+ * Multiline comment goes here because this comment spans multiple lines.
+ */
+ rb_define_const(cFoo, "MULTILINE", INT2FIX(1));
+
+ /*
+ * 1: Multiline comment goes here because this comment spans multiple lines.
+ * Multiline comment goes here because this comment spans multiple lines.
+ */
+ rb_define_const(cFoo, "MULTILINE_VALUE", INT2FIX(1));
+
+ /* Multiline comment goes here because this comment spans multiple lines.
+ * Multiline comment goes here because this comment spans multiple lines.
+ */
+ rb_define_const(cFoo, "MULTILINE_NOT_EMPTY", INT2FIX(1));
+
+ /*
+ * Multiline comment goes here because this comment spans multiple lines.
+ * 1: However, the value extraction should only happen for the first line
+ */
+ rb_define_const(cFoo, "MULTILINE_COLON_ON_SECOND_LINE", INT2FIX(1));
+
+}
+ EOF
+
+ @parser = util_parser content
+
+ @parser.do_classes
+ @parser.do_constants
+
+ klass = @parser.classes['cFoo']
+ assert klass
+
+ constants = klass.constants
+ assert !klass.constants.empty?
+
+ assert_equal @top_level, constants.first.file
+
+ constants = constants.map { |c| [c.name, c.value, c.comment.text] }
+
+ assert_equal ['PERFECT', '300', 'The highest possible score in bowling '],
+ constants.shift
+ assert_equal ['CHEER', 'Huzzah!',
+ 'What you cheer when you roll a perfect game '],
+ constants.shift
+ assert_equal ['TEST', 'TEST:TEST',
+ 'Checking to see if escaped colon works '],
+ constants.shift
+ assert_equal ['MSEPARATOR', '\\',
+ 'The file separator on MS Windows '],
+ constants.shift
+ assert_equal ['SEPARATOR', '/',
+ 'The file separator on Unix '],
+ constants.shift
+ assert_equal ['STUFF', 'C:\\Program Files\\Stuff',
+ 'A directory on MS Windows '],
+ constants.shift
+ assert_equal ['NOSEMI', 'INT2FIX(99)',
+ 'Default definition '],
+ constants.shift
+ assert_equal ['NOCOMMENT', 'rb_str_new2("No comment")', ''],
+ constants.shift
+
+ comment = <<-EOF.chomp
+Multiline comment goes here because this comment spans multiple lines.
+Multiline comment goes here because this comment spans multiple lines.
+ EOF
+ assert_equal ['MULTILINE', 'INT2FIX(1)', comment], constants.shift
+ assert_equal ['MULTILINE_VALUE', '1', comment], constants.shift
+ assert_equal ['MULTILINE_NOT_EMPTY', 'INT2FIX(1)', comment], constants.shift
+
+ comment = <<-EOF.chomp
+Multiline comment goes here because this comment spans multiple lines.
+1: However, the value extraction should only happen for the first line
+ EOF
+ assert_equal ['MULTILINE_COLON_ON_SECOND_LINE', 'INT2FIX(1)', comment],
+ constants.shift
+
+ assert constants.empty?, constants.inspect
+ end
+
+ def test_do_constants_curses
+ content = <<-EOF
+void Init_curses(){
+ mCurses = rb_define_module("Curses");
+
+ /*
+ * Document-const: Curses::COLOR_BLACK
+ *
+ * Value of the color black
+ */
+ rb_curses_define_const(COLOR_BLACK);
+}
+ EOF
+
+ @parser = util_parser content
+
+ @parser.do_modules
+ @parser.do_classes
+ @parser.do_constants
+
+ klass = @parser.classes['mCurses']
+
+ constants = klass.constants
+ refute_empty klass.constants
+
+ assert_equal 'COLOR_BLACK', constants.first.name
+ assert_equal 'UINT2NUM(COLOR_BLACK)', constants.first.value
+ assert_equal 'Value of the color black', constants.first.comment.text
+ end
+
+ def test_do_constants_file
+ content = <<-EOF
+void Init_File(void) {
+ /* Document-const: LOCK_SH
+ *
+ * Shared lock
+ */
+ rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
+}
+ EOF
+
+ @parser = util_parser content
+
+ @parser.do_modules
+ @parser.do_classes
+ @parser.do_constants
+
+ klass = @parser.classes['rb_mFConst']
+
+ constants = klass.constants
+ refute_empty klass.constants
+
+ constant = constants.first
+
+ assert_equal 'LOCK_SH', constant.name
+ assert_equal 'INT2FIX(LOCK_SH)', constant.value
+ assert_equal 'Shared lock', constant.comment.text
+ end
+
+ def test_do_includes
+ content = <<-EOF
+Init_foo() {
+ VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ VALUE mInc = rb_define_module("Inc");
+
+ rb_include_module(cFoo, mInc);
+}
+ EOF
+
+ klass = util_get_class content, 'cFoo'
+
+ incl = klass.includes.first
+ assert_equal 'Inc', incl.name
+ assert_equal '', incl.comment.text
+ assert_equal @top_level, incl.file
+ end
+
+ # HACK parsing warning instead of setting up in file
+ def test_do_methods_in_c
+ content = <<-EOF
+VALUE blah(VALUE klass, VALUE year) {
+}
+
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+
+ rb_define_method(cDate, "blah", blah, 1); /* in blah.c */
+}
+ EOF
+
+ klass = nil
+
+ _, err = verbose_capture_io do
+ klass = util_get_class content, 'cDate'
+ end
+
+ assert_match ' blah.c ', err
+ end
+
+ # HACK parsing warning instead of setting up in file
+ def test_do_methods_in_cpp
+ content = <<-EOF
+VALUE blah(VALUE klass, VALUE year) {
+}
+
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+
+ rb_define_method(cDate, "blah", blah, 1); /* in blah.cpp */
+}
+ EOF
+
+ klass = nil
+
+ _, err = verbose_capture_io do
+ klass = util_get_class content, 'cDate'
+ end
+
+ assert_match ' blah.cpp ', err
+ end
+
+ # HACK parsing warning instead of setting up in file
+ def test_do_methods_in_y
+ content = <<-EOF
+VALUE blah(VALUE klass, VALUE year) {
+}
+
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+
+ rb_define_method(cDate, "blah", blah, 1); /* in blah.y */
+}
+ EOF
+
+ klass = nil
+
+ _, err = verbose_capture_io do
+ klass = util_get_class content, 'cDate'
+ end
+
+ assert_match ' blah.y ', err
+ end
+
+ def test_do_methods_singleton_class
+ content = <<-EOF
+VALUE blah(VALUE klass, VALUE year) {
+}
+
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+ sDate = rb_singleton_class(cDate);
+
+ rb_define_method(sDate, "blah", blah, 1);
+}
+ EOF
+
+ klass = util_get_class content, 'cDate'
+
+ methods = klass.method_list
+ assert_equal 1, methods.length
+ assert_equal 'blah', methods.first.name
+ assert methods.first.singleton
+ end
+
+ def test_do_missing
+ parser = util_parser
+
+ klass_a = @top_level.add_class RDoc::ClassModule, 'A'
+ parser.classes['a'] = klass_a
+
+ parser.enclosure_dependencies['c'] << 'b'
+ parser.enclosure_dependencies['b'] << 'a'
+ parser.enclosure_dependencies['d'] << 'a'
+
+ parser.missing_dependencies['d'] = ['d', :class, 'D', 'Object', 'a']
+ parser.missing_dependencies['c'] = ['c', :class, 'C', 'Object', 'b']
+ parser.missing_dependencies['b'] = ['b', :class, 'B', 'Object', 'a']
+
+ parser.do_missing
+
+ assert_equal %w[A A::B A::B::C A::D],
+ @store.all_classes_and_modules.map { |m| m.full_name }.sort
+ end
+
+ def test_do_missing_cycle
+ parser = util_parser
+
+ klass_a = @top_level.add_class RDoc::ClassModule, 'A'
+ parser.classes['a'] = klass_a
+
+ parser.enclosure_dependencies['c'] << 'b'
+ parser.enclosure_dependencies['b'] << 'a'
+
+ parser.missing_dependencies['c'] = ['c', :class, 'C', 'Object', 'b']
+ parser.missing_dependencies['b'] = ['b', :class, 'B', 'Object', 'a']
+
+ parser.enclosure_dependencies['y'] << 'z'
+ parser.enclosure_dependencies['z'] << 'y'
+
+ parser.missing_dependencies['y'] = ['y', :class, 'Y', 'Object', 'z']
+ parser.missing_dependencies['z'] = ['z', :class, 'Z', 'Object', 'y']
+
+ _, err = verbose_capture_io do
+ parser.do_missing
+ end
+
+ expected = 'Unable to create class Y (y), class Z (z) ' +
+ 'due to a cyclic class or module creation'
+
+ assert_equal expected, err.chomp
+
+ assert_equal %w[A A::B A::B::C],
+ @store.all_classes_and_modules.map { |m| m.full_name }.sort
+ end
+
+ def test_find_alias_comment
+ parser = util_parser
+
+ comment = parser.find_alias_comment 'C', '[]', 'index'
+
+ assert_equal '', comment.text
+
+ parser = util_parser <<-C
+/*
+ * comment
+ */
+
+rb_define_alias(C, "[]", "index");
+ C
+
+ comment = parser.find_alias_comment 'C', '[]', 'index'
+
+ assert_equal "/*\n * comment\n */\n\n", comment.text
+ end
+
+ def test_find_attr_comment_document_attr
+ parser= util_parser <<-C
+/*
+ * Document-attr: y
+ * comment
+ */
+ C
+
+ comment = parser.find_attr_comment nil, 'y'
+
+ assert_equal "/*\n * \n * comment\n */", comment.text
+ end
+
+ def test_find_attr_comment_document_attr_oneline
+ parser= util_parser <<-C
+/* Document-attr: y
+ * comment
+ */
+ C
+
+ comment = parser.find_attr_comment nil, 'y'
+
+ assert_equal "/* \n * comment\n */", comment.text
+ end
+
+ def test_find_attr_comment_document_attr_overlap
+ parser= util_parser <<-C
+/* Document-attr: x
+ * comment
+ */
+
+/* Document-attr: y
+ * comment
+ */
+ C
+
+ comment = parser.find_attr_comment nil, 'y'
+
+ assert_equal "/* \n * comment\n */", comment.text
+ end
+
+ def test_find_class_comment
+ @options.rdoc_include << File.dirname(__FILE__)
+
+ content = <<-EOF
+/*
+ * Comment 1
+ */
+foo = rb_define_class("MyClassName1", rb_cObject);
+
+/*
+ * Comment 2
+ */
+bar = rb_define_class("MyClassName2", rb_cObject);
+ EOF
+
+ util_get_class content
+
+ assert_equal "Comment 1", @parser.classes['foo'].comment.text
+ assert_equal "Comment 2", @parser.classes['bar'].comment.text
+ end
+
+ def test_find_class_comment_init
+ content = <<-EOF
+/*
+ * a comment for class Foo
+ */
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+
+ assert_equal "a comment for class Foo", klass.comment.text
+ end
+
+ def test_find_class_comment_define_class
+ content = <<-EOF
+/*
+ * a comment for class Foo
+ */
+VALUE foo = rb_define_class("Foo", rb_cObject);
+ EOF
+
+ klass = util_get_class content, 'foo'
+
+ assert_equal "a comment for class Foo", klass.comment.text
+ end
+
+ def test_find_class_comment_define_class_Init_Foo
+ content = <<-EOF
+/*
+ * a comment for class Foo on Init
+ */
+void
+Init_Foo(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+
+ assert_equal "a comment for class Foo on Init", klass.comment.text
+ end
+
+ def test_find_class_comment_define_class_Init_Foo_no_void
+ content = <<-EOF
+/*
+ * a comment for class Foo on Init
+ */
+void
+Init_Foo() {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+
+ assert_equal "a comment for class Foo on Init", klass.comment.text
+ end
+
+ def test_find_class_comment_define_class_bogus_comment
+ content = <<-EOF
+/*
+ * a comment for other_function
+ */
+void
+other_function() {
+}
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+
+ assert_equal '', klass.comment.text
+ end
+
+ def test_find_class_comment_define_class_under
+ content = <<-EOF
+/*
+ * a comment for class Foo
+ */
+VALUE foo = rb_define_class_under(rb_cObject, "Foo", rb_cObject);
+ EOF
+
+ klass = util_get_class content, 'foo'
+
+ assert_equal "a comment for class Foo", klass.comment.text
+ end
+
+ def test_find_class_comment_define_class_under_Init
+ content = <<-EOF
+/*
+ * a comment for class Foo on Init
+ */
+void
+Init_Foo(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE foo = rb_define_class_under(rb_cObject, "Foo", rb_cObject);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+
+ # the inner comment is used since Object::Foo is not necessarily the same
+ # thing as "Foo" for Init_
+ assert_equal "a comment for class Foo on rb_define_class",
+ klass.comment.text
+ end
+
+ def test_find_const_comment_rb_define
+ content = <<-EOF
+/*
+ * A comment
+ */
+rb_define_const(cFoo, "CONST", value);
+ EOF
+
+ parser = util_parser content
+
+ comment = parser.find_const_comment 'const', 'CONST'
+
+ assert_equal "/*\n * A comment\n */\n", comment.text
+ end
+
+ def test_find_const_comment_document_const
+ content = <<-EOF
+/*
+ * Document-const: CONST
+ *
+ * A comment
+ */
+ EOF
+
+ parser = util_parser content
+
+ comment = parser.find_const_comment nil, 'CONST'
+
+ assert_equal "/*\n *\n * A comment\n */", comment.text
+ end
+
+ def test_find_const_comment_document_const_full_name
+ content = <<-EOF
+/*
+ * Document-const: Foo::CONST
+ *
+ * A comment
+ */
+ EOF
+
+ parser = util_parser content
+
+ comment = parser.find_const_comment nil, 'CONST', 'Foo'
+
+ assert_equal "/*\n *\n * A comment\n */", comment.text
+ end
+
+ def test_find_body
+ content = <<-EOF
+/*
+ * a comment for other_function
+ */
+VALUE
+other_function() {
+}
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+
+ rb_define_method(foo, "my_method", other_function, 0);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+ other_function = klass.method_list.first
+
+ assert_equal 'my_method', other_function.name
+ assert_equal "a comment for other_function",
+ other_function.comment.text
+ assert_equal '()', other_function.params
+
+ code = other_function.token_stream.first.text
+
+ assert_equal "VALUE\nother_function() {\n}", code
+ end
+
+ def test_find_body_2
+ content = <<-CONTENT
+/* Copyright (C) 2010 Sven Herzberg
+ *
+ * This file is free software; the author(s) gives unlimited
+ * permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ */
+
+#include <ruby.h>
+
+static VALUE
+wrap_initialize (VALUE self)
+{
+ return self;
+}
+
+/* */
+static VALUE
+wrap_shift (VALUE self,
+ VALUE arg)
+{
+ return self;
+}
+
+void
+init_gi_repository (void)
+{
+ VALUE mTest = rb_define_module ("Test");
+ VALUE cTest = rb_define_class_under (mTest, "Test", rb_cObject);
+
+ rb_define_method (cTest, "initialize", wrap_initialize, 0);
+ rb_define_method (cTest, "shift", wrap_shift, 0);
+}
+ CONTENT
+
+ klass = util_get_class content, 'cTest'
+ assert_equal 2, klass.method_list.length
+ end
+
+ def test_find_body_cast
+ content = <<-EOF
+/*
+ * a comment for other_function
+ */
+VALUE
+other_function() {
+}
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+
+ rb_define_method(foo, "my_method", (METHOD)other_function, 0);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+ other_function = klass.method_list.first
+
+ assert_equal 'my_method', other_function.name
+ assert_equal "a comment for other_function",
+ other_function.comment.text
+ assert_equal '()', other_function.params
+
+ code = other_function.token_stream.first.text
+
+ assert_equal "VALUE\nother_function() {\n}", code
+ end
+
+ def test_find_body_define
+ content = <<-EOF
+#define something something_else
+
+#define other_function rb_other_function
+
+/*
+ * a comment for rb_other_function
+ */
+VALUE
+rb_other_function() {
+}
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+
+ rb_define_method(foo, "my_method", other_function, 0);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+ other_function = klass.method_list.first
+
+ assert_equal 'my_method', other_function.name
+ assert_equal 'a comment for rb_other_function', other_function.comment.text
+ assert_equal '()', other_function.params
+ assert_equal 118, other_function.offset
+ assert_equal 8, other_function.line
+
+ code = other_function.token_stream.first.text
+
+ assert_equal "VALUE\nrb_other_function() {\n}", code
+ end
+
+ def test_find_body_define_comment
+ content = <<-EOF
+/*
+ * a comment for other_function
+ */
+#define other_function rb_other_function
+
+/* */
+VALUE
+rb_other_function() {
+}
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+
+ rb_define_method(foo, "my_method", other_function, 0);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+ other_function = klass.method_list.first
+
+ assert_equal 'my_method', other_function.name
+ assert_equal 'a comment for other_function', other_function.comment.text
+ assert_equal '()', other_function.params
+ assert_equal 39, other_function.offset
+ assert_equal 4, other_function.line
+
+ code = other_function.token_stream.first.text
+
+ assert_equal "#define other_function rb_other_function", code
+ end
+
+ def test_find_body_document_method
+ content = <<-EOF
+/*
+ * Document-method: bar
+ * Document-method: baz
+ *
+ * a comment for bar
+ */
+VALUE
+bar() {
+}
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+
+ rb_define_method(foo, "bar", bar, 0);
+ rb_define_method(foo, "baz", bar, 0);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+ assert_equal 2, klass.method_list.length
+
+ methods = klass.method_list.sort
+
+ bar = methods.first
+ assert_equal 'Foo#bar', bar.full_name
+ assert_equal "a comment for bar", bar.comment.text
+
+ baz = methods.last
+ assert_equal 'Foo#baz', baz.full_name
+ assert_equal "a comment for bar", baz.comment.text
+ end
+
+ def test_find_body_document_method_equals
+ content = <<-EOF
+/*
+ * Document-method: Zlib::GzipFile#mtime=
+ *
+ * A comment
+ */
+static VALUE
+rb_gzfile_set_mtime(VALUE obj, VALUE mtime)
+{
+
+void
+Init_zlib() {
+ mZlib = rb_define_module("Zlib");
+ cGzipFile = rb_define_class_under(mZlib, "GzipFile", rb_cObject);
+ cGzipWriter = rb_define_class_under(mZlib, "GzipWriter", cGzipFile);
+ rb_define_method(cGzipWriter, "mtime=", rb_gzfile_set_mtime, 1);
+}
+ EOF
+
+ klass = util_get_class content, 'cGzipWriter'
+ assert_equal 1, klass.method_list.length
+
+ methods = klass.method_list.sort
+
+ bar = methods.first
+ assert_equal 'Zlib::GzipWriter#mtime=', bar.full_name
+ assert_equal 'A comment', bar.comment.text
+ end
+
+ def test_find_body_document_method_same
+ content = <<-EOF
+VALUE
+s_bar() {
+}
+
+VALUE
+bar() {
+}
+
+/*
+ * Document-method: Foo::bar
+ *
+ * a comment for Foo::bar
+ */
+
+/*
+ * Document-method: Foo#bar
+ *
+ * a comment for Foo#bar
+ */
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+
+ rb_define_singleton_method(foo, "bar", s_bar, 0);
+ rb_define_method(foo, "bar", bar, 0);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+ assert_equal 2, klass.method_list.length
+
+ methods = klass.method_list.sort
+
+ s_bar = methods.first
+ assert_equal 'Foo::bar', s_bar.full_name
+ assert_equal "a comment for Foo::bar", s_bar.comment.text
+
+ bar = methods.last
+ assert_equal 'Foo#bar', bar.full_name
+ assert_equal "a comment for Foo#bar", bar.comment.text
+ end
+
+ def test_find_body_macro
+ content = <<-EOF
+/*
+ * a comment for other_function
+ */
+DLL_LOCAL VALUE
+other_function() {
+}
+
+void
+Init_Foo(void) {
+ VALUE foo = rb_define_class("Foo", rb_cObject);
+
+ rb_define_method(foo, "my_method", other_function, 0);
+}
+ EOF
+
+ klass = util_get_class content, 'foo'
+ other_function = klass.method_list.first
+
+ assert_equal 'my_method', other_function.name
+ assert_equal "a comment for other_function",
+ other_function.comment.text
+ assert_equal '()', other_function.params
+
+ code = other_function.token_stream.first.text
+
+ assert_equal "DLL_LOCAL VALUE\nother_function() {\n}", code
+ end
+
+ def test_find_modifiers_call_seq
+ comment = RDoc::Comment.new <<-COMMENT
+call-seq:
+ commercial() -> Date <br />
+
+If no arguments are given:
+
+ COMMENT
+
+ parser = util_parser
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ parser.find_modifiers comment, method_obj
+
+ expected = <<-CALL_SEQ.chomp
+commercial() -> Date <br />
+
+ CALL_SEQ
+
+ assert_equal expected, method_obj.call_seq
+ end
+
+ def test_find_modifiers_nodoc
+ comment = RDoc::Comment.new <<-COMMENT
+/* :nodoc:
+ *
+ * Blah
+ */
+
+ COMMENT
+
+ parser = util_parser
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ parser.find_modifiers comment, method_obj
+
+ assert_equal nil, method_obj.document_self
+ end
+
+ def test_find_modifiers_yields
+ comment = RDoc::Comment.new <<-COMMENT
+/* :yields: a, b
+ *
+ * Blah
+ */
+
+ COMMENT
+
+ parser = util_parser
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ parser.find_modifiers comment, method_obj
+
+ assert_equal 'a, b', method_obj.block_params
+
+ assert_equal "\n\nBlah", comment.text
+ end
+
+ def test_handle_method_args_minus_1
+ parser = util_parser "Document-method: Object#m\n blah */"
+
+ parser.content = <<-BODY
+VALUE
+rb_other(VALUE obj) {
+ rb_funcall(obj, rb_intern("other"), 0);
+ return rb_str_new2("blah, blah, blah");
+}
+
+VALUE
+rb_m(int argc, VALUE *argv, VALUE obj) {
+ VALUE o1, o2;
+ rb_scan_args(argc, argv, "1", &o1, &o2);
+}
+ BODY
+
+ parser.handle_method 'method', 'rb_cObject', 'm', 'rb_m', -1
+
+ m = @top_level.find_module_named('Object').method_list.first
+
+ assert_equal 'm', m.name
+ assert_equal @top_level, m.file
+ assert_equal 115, m.offset
+ assert_equal 7, m.line
+
+ assert_equal '(p1)', m.params
+ end
+
+ def test_handle_method_args_0
+ parser = util_parser "Document-method: BasicObject#==\n blah */"
+
+ parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', 0
+
+ bo = @top_level.find_module_named 'BasicObject'
+
+ assert_equal 1, bo.method_list.length
+
+ equals2 = bo.method_list.first
+
+ assert_equal '()', equals2.params
+ end
+
+ def test_handle_method_args_1
+ parser = util_parser "Document-method: BasicObject#==\n blah */"
+
+ parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', 1
+
+ bo = @top_level.find_module_named 'BasicObject'
+
+ assert_equal 1, bo.method_list.length
+
+ equals2 = bo.method_list.first
+
+ assert_equal '(p1)', equals2.params
+ end
+
+ def test_handle_method_args_2
+ parser = util_parser "Document-method: BasicObject#==\n blah */"
+
+ parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', 2
+
+ bo = @top_level.find_module_named 'BasicObject'
+
+ assert_equal 1, bo.method_list.length
+
+ equals2 = bo.method_list.first
+
+ assert_equal '(p1, p2)', equals2.params
+ end
+
+ # test_handle_args_minus_1 handled by test_handle_method
+
+ def test_handle_method_args_minus_2
+ parser = util_parser "Document-method: BasicObject#==\n blah */"
+
+ parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', -2
+
+ bo = @top_level.find_module_named 'BasicObject'
+
+ assert_equal 1, bo.method_list.length
+
+ equals2 = bo.method_list.first
+
+ assert_equal '(*args)', equals2.params
+ end
+
+ def test_handle_method_initialize
+ parser = util_parser "Document-method: BasicObject::new\n blah */"
+
+ parser.handle_method('private_method', 'rb_cBasicObject',
+ 'initialize', 'rb_obj_dummy', -1)
+
+ bo = @top_level.find_module_named 'BasicObject'
+
+ assert_equal 1, bo.method_list.length
+
+ new = bo.method_list.first
+
+ assert_equal 'new', new.name
+ assert_equal :public, new.visibility
+ end
+
+ def test_handle_singleton
+ parser = util_parser <<-SINGLE
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+ sDate = rb_singleton_class(cDate);
+}
+ SINGLE
+
+ parser.scan
+
+ assert_equal 'Date', parser.known_classes['sDate']
+ assert_equal 'Date', parser.singleton_classes['sDate']
+ end
+
+ def test_look_for_directives_in
+ parser = util_parser
+
+ comment = RDoc::Comment.new "# :other: not_handled\n"
+
+ parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :other: not_handled\n", comment.text
+ assert_equal 'not_handled', @top_level.metadata['other']
+ end
+
+ def test_load_variable_map
+ some_ext = @top_level.add_class RDoc::NormalClass, 'SomeExt'
+ @top_level.add_class RDoc::NormalClass, 'OtherExt'
+
+ @store.cache[:c_class_variables][@fn] = { 'cSomeExt' => 'SomeExt' }
+ @store.cache[:c_class_variables]['other.c'] = { 'cOtherExt' => 'OtherExt' }
+
+ parser = util_parser
+
+ map = parser.load_variable_map :c_class_variables
+
+ expected = { 'cSomeExt' => some_ext }
+
+ assert_equal expected, map
+
+ assert_equal 'SomeExt', parser.known_classes['cSomeExt']
+ assert_nil parser.known_classes['cOtherExt']
+ end
+
+ def test_load_variable_map_empty
+ parser = util_parser
+
+ map = parser.load_variable_map :c_class_variables
+
+ assert_empty map
+ end
+
+ def test_load_variable_map_legacy
+ @store.cache[:c_class_variables] = nil
+
+ parser = util_parser
+
+ map = parser.load_variable_map :c_class_variables
+
+ assert_empty map
+ end
+
+ def test_load_variable_map_singleton
+ @top_level.add_class RDoc::NormalClass, 'SomeExt'
+ @top_level.add_class RDoc::NormalClass, 'OtherExt'
+
+ @store.cache[:c_singleton_class_variables][@fn] =
+ { 'cSomeExt' => 'SomeExt' }
+ @store.cache[:c_singleton_class_variables]['other.c'] =
+ { 'cOtherExt' => 'OtherExt' }
+
+ parser = util_parser
+
+ map = parser.load_variable_map :c_singleton_class_variables
+
+ expected = { 'cSomeExt' => 'SomeExt' }
+
+ assert_equal expected, map
+
+ assert_equal 'SomeExt', parser.known_classes['cSomeExt']
+ assert_nil parser.known_classes['cOtherExt']
+ end
+
+ def test_load_variable_map_trim
+ a = @top_level.add_class RDoc::NormalClass, 'A'
+
+ @store.cache[:c_class_variables][@fn] = {
+ 'cA' => 'A',
+ 'cB' => 'B',
+ }
+
+ parser = util_parser
+
+ map = parser.load_variable_map :c_class_variables
+
+ expected = { 'cA' => a }
+
+ assert_equal expected, map
+ end
+
+ def test_define_method
+ content = <<-EOF
+/*Method Comment! */
+static VALUE
+rb_io_s_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+}
+
+void
+Init_IO(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE rb_cIO = rb_define_class("IO", rb_cObject);
+ rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1);
+}
+ EOF
+
+ klass = util_get_class content, 'rb_cIO'
+ read_method = klass.method_list.first
+ assert_equal "read", read_method.name
+ assert_equal "Method Comment! ", read_method.comment.text
+ assert_equal "rb_io_s_read", read_method.c_function
+ assert read_method.singleton
+ end
+
+ def test_define_method_with_prototype
+ content = <<-EOF
+static VALUE rb_io_s_read(int, VALUE*, VALUE);
+
+/* Method Comment! */
+static VALUE
+rb_io_s_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+}
+
+void
+Init_IO(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE rb_cIO = rb_define_class("IO", rb_cObject);
+ rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1);
+}
+ EOF
+
+ klass = util_get_class content, 'rb_cIO'
+ read_method = klass.method_list.first
+ assert_equal "read", read_method.name
+ assert_equal "Method Comment! ", read_method.comment.text
+ assert_equal "rb_io_s_read", read_method.c_function
+ assert read_method.singleton
+ end
+
+ def test_define_method_private
+ content = <<-EOF
+/*Method Comment! */
+static VALUE
+rb_io_s_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+}
+
+void
+Init_IO(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE rb_cIO = rb_define_class("IO", rb_cObject);
+ rb_define_private_method(rb_cIO, "read", rb_io_s_read, -1);
+}
+ EOF
+
+ klass = util_get_class content, 'rb_cIO'
+ read_method = klass.method_list.first
+ assert_equal 'IO#read', read_method.full_name
+ assert_equal :private, read_method.visibility
+ assert_equal "Method Comment! ", read_method.comment.text
+ end
+
+ def test_define_method_private_singleton
+ content = <<-EOF
+/*Method Comment! */
+static VALUE
+rb_io_s_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+}
+
+void
+Init_IO(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE rb_cIO = rb_define_class("IO", rb_cObject);
+ VALUE rb_cIO_s = rb_singleton_class(rb_cIO);
+ rb_define_private_method(rb_cIO_s, "read", rb_io_s_read, -1);
+}
+ EOF
+
+ klass = util_get_class content, 'rb_cIO'
+ read_method = klass.method_list.first
+ assert_equal "read", read_method.name
+ assert_equal "Method Comment! ", read_method.comment.text
+ assert_equal :private, read_method.visibility
+ assert read_method.singleton
+ end
+
+ def test_define_method_singleton
+ content = <<-EOF
+/*Method Comment! */
+static VALUE
+rb_io_s_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+}
+
+void
+Init_IO(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE rb_cIO = rb_define_class("IO", rb_cObject);
+ VALUE rb_cIO_s = rb_singleton_class(rb_cIO);
+ rb_define_method(rb_cIO_s, "read", rb_io_s_read, -1);
+}
+ EOF
+
+ klass = util_get_class content, 'rb_cIO'
+ read_method = klass.method_list.first
+ assert_equal "read", read_method.name
+ assert_equal "Method Comment! ", read_method.comment.text
+ assert read_method.singleton
+ end
+
+ def test_rb_scan_args
+ parser = util_parser
+
+ assert_equal '(p1)',
+ parser.rb_scan_args('rb_scan_args(a, b, "1",)')
+ assert_equal '(p1, p2)',
+ parser.rb_scan_args('rb_scan_args(a, b, "2",)')
+
+ assert_equal '(p1 = v1)',
+ parser.rb_scan_args('rb_scan_args(a, b, "01",)')
+ assert_equal '(p1 = v1, p2 = v2)',
+ parser.rb_scan_args('rb_scan_args(a, b, "02",)')
+
+ assert_equal '(p1, p2 = v2)',
+ parser.rb_scan_args('rb_scan_args(a, b, "11",)')
+
+ assert_equal '(p1, *args)',
+ parser.rb_scan_args('rb_scan_args(a, b, "1*",)')
+ assert_equal '(p1, p2 = {})',
+ parser.rb_scan_args('rb_scan_args(a, b, "1:",)')
+ assert_equal '(p1, &block)',
+ parser.rb_scan_args('rb_scan_args(a, b, "1&",)')
+
+ assert_equal '(p1, p2)',
+ parser.rb_scan_args('rb_scan_args(a, b, "101",)')
+
+ assert_equal '(p1, p2 = v2, p3)',
+ parser.rb_scan_args('rb_scan_args(a, b, "111",)')
+
+ assert_equal '(p1, *args, p3)',
+ parser.rb_scan_args('rb_scan_args(a, b, "1*1",)')
+
+ assert_equal '(p1, p2 = v2, *args)',
+ parser.rb_scan_args('rb_scan_args(a, b, "11*",)')
+ assert_equal '(p1, p2 = v2, p3 = {})',
+ parser.rb_scan_args('rb_scan_args(a, b, "11:",)')
+ assert_equal '(p1, p2 = v2, &block)',
+ parser.rb_scan_args('rb_scan_args(a, b, "11&",)')
+
+ assert_equal '(p1, p2 = v2, *args, p4, p5 = {}, &block)',
+ parser.rb_scan_args('rb_scan_args(a, b, "11*1:&",)')
+
+ # The following aren't valid according to spec but are according to the
+ # implementation.
+ assert_equal '(*args)',
+ parser.rb_scan_args('rb_scan_args(a, b, "*",)')
+ assert_equal '(p1 = {})',
+ parser.rb_scan_args('rb_scan_args(a, b, ":",)')
+ assert_equal '(&block)',
+ parser.rb_scan_args('rb_scan_args(a, b, "&",)')
+
+ assert_equal '(*args, p2 = {})',
+ parser.rb_scan_args('rb_scan_args(a, b, "*:",)')
+ assert_equal '(p1 = {}, &block)',
+ parser.rb_scan_args('rb_scan_args(a, b, ":&",)')
+ assert_equal '(*args, p2 = {}, &block)',
+ parser.rb_scan_args('rb_scan_args(a, b, "*:&",)')
+ end
+
+ def test_scan
+ parser = util_parser <<-C
+void Init(void) {
+ mM = rb_define_module("M");
+ cC = rb_define_class("C", rb_cObject);
+ sC = rb_singleton_class(cC);
+}
+ C
+
+ parser.scan
+
+ expected = {
+ @fn => {
+ 'mM' => 'M',
+ 'cC' => 'C', }}
+ assert_equal expected, @store.c_class_variables
+
+ expected = {
+ @fn => {
+ 'sC' => 'C' } }
+ assert_equal expected, @store.c_singleton_class_variables
+ end
+
+ def test_scan_method_copy
+ parser = util_parser <<-C
+/*
+ * call-seq:
+ * pathname.to_s -> string
+ * pathname.to_path -> string
+ *
+ * Return the path as a String.
+ *
+ * to_path is implemented so Pathname objects are usable with File.open, etc.
+ */
+static VALUE
+path_to_s(VALUE self) { }
+
+/*
+ * call-seq:
+ * str[index] -> new_str or nil
+ * str[start, length] -> new_str or nil
+ * str.slice(index) -> new_str or nil
+ * str.slice(start, length) -> new_str or nil
+ */
+static VALUE
+path_aref_m(int argc, VALUE *argv, VALUE str) { }
+
+/*
+ * call-seq:
+ * string <=> other_string -> -1, 0, +1 or nil
+ */
+static VALUE
+path_cmp_m(VALUE str1, VALUE str2) { }
+
+/*
+ * call-seq:
+ * str == obj -> true or false
+ * str === obj -> true or false
+ */
+VALUE
+rb_str_equal(VALUE str1, VALUE str2) { }
+
+Init_pathname()
+{
+ rb_cPathname = rb_define_class("Pathname", rb_cObject);
+
+ rb_define_method(rb_cPathname, "to_s", path_to_s, 0);
+ rb_define_method(rb_cPathname, "to_path", path_to_s, 0);
+ rb_define_method(rb_cPathname, "[]", path_aref_m, -1);
+ rb_define_method(rb_cPathname, "slice", path_aref_m, -1);
+ rb_define_method(rb_cPathname, "<=>", path_cmp_m, 1);
+ rb_define_method(rb_cPathname, "==", rb_str_equal), 2);
+ rb_define_method(rb_cPathname, "===", rb_str_equal), 2);
+}
+ C
+
+ parser.scan
+
+ pathname = @store.classes_hash['Pathname']
+
+ to_path = pathname.method_list.find { |m| m.name == 'to_path' }
+ assert_equal "pathname.to_path -> string", to_path.call_seq
+
+ to_s = pathname.method_list.find { |m| m.name == 'to_s' }
+ assert_equal "pathname.to_s -> string", to_s.call_seq
+
+ index_expected = <<-EXPECTED.chomp
+str[index] -> new_str or nil
+str[start, length] -> new_str or nil
+ EXPECTED
+
+ index = pathname.method_list.find { |m| m.name == '[]' }
+ assert_equal index_expected, index.call_seq, '[]'
+
+ slice_expected = <<-EXPECTED.chomp
+str.slice(index) -> new_str or nil
+str.slice(start, length) -> new_str or nil
+ EXPECTED
+
+ slice = pathname.method_list.find { |m| m.name == 'slice' }
+ assert_equal slice_expected, slice.call_seq
+
+ spaceship = pathname.method_list.find { |m| m.name == '<=>' }
+ assert_equal "string <=> other_string -> -1, 0, +1 or nil",
+ spaceship.call_seq
+
+ equals2 = pathname.method_list.find { |m| m.name == '==' }
+ assert_match 'str == obj', equals2.call_seq
+
+ equals3 = pathname.method_list.find { |m| m.name == '===' }
+ assert_match 'str === obj', equals3.call_seq
+ end
+
+ def test_scan_order_dependent
+ parser = util_parser <<-C
+void a(void) {
+ mA = rb_define_module("A");
+}
+
+void b(void) {
+ cB = rb_define_class_under(mA, "B", rb_cObject);
+}
+
+void c(void) {
+ mC = rb_define_module_under(cB, "C");
+}
+
+void d(void) {
+ mD = rb_define_class_under(mC, "D");
+}
+ C
+
+ parser.scan
+
+ assert_equal %w[A A::B A::B::C],
+ @store.all_classes_and_modules.map { |m| m.full_name }.sort
+ end
+
+ def util_get_class content, name = nil
+ @parser = util_parser content
+ @parser.scan
+
+ @parser.classes[name] if name
+ end
+
+ def util_parser content = ''
+ RDoc::Parser::C.new @top_level, @fn, content, @options, @stats
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_parser_changelog.rb b/jni/ruby/test/rdoc/test_rdoc_parser_changelog.rb
new file mode 100644
index 0000000..4d83983
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_parser_changelog.rb
@@ -0,0 +1,315 @@
+require 'rdoc/test_case'
+
+class TestRDocParserChangeLog < RDoc::TestCase
+
+ def setup
+ super
+
+ @tempfile = Tempfile.new 'ChangeLog'
+ @top_level = @store.add_file @tempfile.path
+ @options = RDoc::Options.new
+ @stats = RDoc::Stats.new @store, 0
+ end
+
+ def teardown
+ @tempfile.close!
+ end
+
+ def test_class_can_parse
+ parser = RDoc::Parser::ChangeLog
+
+ temp_dir do
+ FileUtils.touch 'ChangeLog'
+ assert_equal parser, parser.can_parse('ChangeLog')
+
+ assert_equal parser, parser.can_parse(@tempfile.path)
+
+ FileUtils.touch 'ChangeLog.rb'
+ assert_equal RDoc::Parser::Ruby, parser.can_parse('ChangeLog.rb')
+ end
+ end
+
+ def test_continue_entry_body
+ parser = util_parser
+
+ entry_body = ['a']
+
+ parser.continue_entry_body entry_body, 'b'
+
+ assert_equal ['a b'], entry_body
+ end
+
+ def test_continue_entry_body_empty
+ parser = util_parser
+
+ entry_body = []
+
+ parser.continue_entry_body entry_body, ''
+
+ assert_empty entry_body
+ end
+
+ def test_continue_entry_body_function
+ parser = util_parser
+
+ entry_body = ['file: (func1)']
+
+ parser.continue_entry_body entry_body, '(func2): blah'
+
+ assert_equal ['file: (func1, func2): blah'], entry_body
+ end
+
+ def test_create_document
+ parser = util_parser
+
+ groups = {
+ '2012-12-04' => [
+ ['Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>',
+ %w[a:one b:two]],
+ ['Tue Dec 4 08:32:10 2012 Eric Hodel <drbrain@segment7.net>',
+ %w[c:three d:four]]],
+ '2012-12-03' => [
+ ['Mon Dec 3 20:28:02 2012 Koichi Sasada <ko1@atdot.net>',
+ %w[e:five f:six]]],
+ }
+
+ expected =
+ doc(
+ head(1, File.basename(@tempfile.path)),
+ blank_line,
+ head(2, '2012-12-04'),
+ blank_line,
+ head(3, 'Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>'),
+ blank_line,
+ list(:NOTE, item('a', para('one')), item('b', para('two'))),
+ head(3, 'Tue Dec 4 08:32:10 2012 Eric Hodel <drbrain@segment7.net>'),
+ blank_line,
+ list(:NOTE, item('c', para('three')), item('d', para('four'))),
+ head(2, '2012-12-03'),
+ blank_line,
+ head(3, 'Mon Dec 3 20:28:02 2012 Koichi Sasada <ko1@atdot.net>'),
+ blank_line,
+ list(:NOTE, item('e', para('five')), item('f', para('six'))))
+
+ expected.file = @top_level
+
+ document = parser.create_document(groups)
+
+ assert_equal expected, document
+
+ assert_equal 2, document.omit_headings_below
+
+ headings = document.parts.select do |part|
+ RDoc::Markup::Heading === part and part.level == 2
+ end
+
+ refute headings.all? { |heading| heading.text.frozen? }
+ end
+
+ def test_create_entries
+ parser = util_parser
+
+ entries = [
+ ['Tue Dec 1 02:03:04 2012 Eric Hodel <drbrain@segment7.net>',
+ %w[a:one b:two]],
+ ['Tue Dec 5 06:07:08 2012 Eric Hodel <drbrain@segment7.net>',
+ %w[c:three d:four]],
+ ]
+
+ expected = [
+ head(3, 'Tue Dec 1 02:03:04 2012 Eric Hodel <drbrain@segment7.net>'),
+ blank_line,
+ list(:NOTE, item('a', para('one')), item('b', para('two'))),
+ head(3, 'Tue Dec 5 06:07:08 2012 Eric Hodel <drbrain@segment7.net>'),
+ blank_line,
+ list(:NOTE, item('c', para('three')), item('d', para('four'))),
+ ]
+
+ entries = parser.create_entries(entries)
+ assert_equal expected, entries
+ end
+
+ def test_create_entries_colons
+ parser = util_parser
+
+ entries = [
+ ['Wed Dec 5 12:17:11 2012 Naohisa Goto <ngotogenome@gmail.com>',
+ ['func.rb (DL::Function#bind): log stuff [ruby-core:50562]']],
+ ]
+
+ expected = [
+ head(3,
+ 'Wed Dec 5 12:17:11 2012 Naohisa Goto <ngotogenome@gmail.com>'),
+ blank_line,
+ list(:NOTE,
+ item('func.rb (DL::Function#bind)',
+ para('log stuff [ruby-core:50562]')))]
+
+ assert_equal expected, parser.create_entries(entries)
+ end
+
+ def test_create_items
+ parser = util_parser
+
+ items = [
+ 'README.EXT: Converted to RDoc format',
+ 'README.EXT.ja: ditto',
+ ]
+
+ expected =
+ list(:NOTE,
+ item('README.EXT',
+ para('Converted to RDoc format')),
+ item('README.EXT.ja',
+ para('ditto')))
+
+ assert_equal expected, parser.create_items(items)
+ end
+
+ def test_group_entries
+ parser = util_parser
+
+ entries = [
+ [ 'Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>',
+ %w[one two]],
+ [ 'Tue Dec 4 08:32:10 2012 Eric Hodel <drbrain@segment7.net>',
+ %w[three four]],
+ [ 'Mon Dec 3 20:28:02 2012 Koichi Sasada <ko1@atdot.net>',
+ %w[five six]],
+ [ '2008-01-30 H.J. Lu <hongjiu.lu@intel.com>',
+ %w[seven eight]]]
+
+ expected = {
+ '2012-12-04' => [
+ ['Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>',
+ %w[one two]],
+ ['Tue Dec 4 08:32:10 2012 Eric Hodel <drbrain@segment7.net>',
+ %w[three four]]],
+ '2012-12-03' => [
+ ['Mon Dec 3 20:28:02 2012 Koichi Sasada <ko1@atdot.net>',
+ %w[five six]]],
+ '2008-01-30' => [
+ ['2008-01-30 H.J. Lu <hongjiu.lu@intel.com>',
+ %w[seven eight]]],
+ }
+
+ assert_equal expected, parser.group_entries(entries)
+ end
+
+ def test_parse_entries
+ parser = util_parser <<-ChangeLog
+Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>
+
+ * README.EXT: Converted to RDoc format
+ * README.EXT.ja: ditto
+
+Mon Dec 3 20:28:02 2012 Koichi Sasada <ko1@atdot.net>
+
+ * compile.c (iseq_specialized_instruction):
+ change condition of using `opt_send_simple'.
+ More method invocations can be simple.
+
+Other note that will be ignored
+
+ ChangeLog
+
+ expected = [
+ [ 'Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>',
+ [ 'README.EXT: Converted to RDoc format',
+ 'README.EXT.ja: ditto']],
+ [ 'Mon Dec 3 20:28:02 2012 Koichi Sasada <ko1@atdot.net>',
+ [ 'compile.c (iseq_specialized_instruction): change condition of ' +
+ 'using `opt_send_simple\'. More method invocations can be simple.']]]
+
+ assert_equal expected, parser.parse_entries
+ end
+
+ def test_parse_entries_bad_time
+ parser = util_parser <<-ChangeLog
+2008-01-30 H.J. Lu <hongjiu.lu@intel.com>
+
+ PR libffi/34612
+ * src/x86/sysv.S (ffi_closure_SYSV): Pop 4 byte from stack when
+ returning struct.
+
+ ChangeLog
+
+ expected = [
+ [ '2008-01-30 H.J. Lu <hongjiu.lu@intel.com>',
+ [ 'src/x86/sysv.S (ffi_closure_SYSV): Pop 4 byte from stack when ' +
+ 'returning struct.']]
+ ]
+
+ assert_equal expected, parser.parse_entries
+ end
+
+ def test_parse_entries_gnu
+ parser = util_parser <<-ChangeLog
+1998-08-17 Richard Stallman <rms@gnu.org>
+
+* register.el (insert-register): Return nil.
+(jump-to-register): Likewise.
+
+* sort.el (sort-subr): Return nil.
+
+* keyboard.c (menu_bar_items, tool_bar_items)
+(Fexecute_extended_command): Deal with 'keymap' property.
+ ChangeLog
+
+ expected = [
+ [ '1998-08-17 Richard Stallman <rms@gnu.org>',
+ [ 'register.el (insert-register): Return nil.',
+ '(jump-to-register): Likewise.',
+ 'sort.el (sort-subr): Return nil.',
+ 'keyboard.c (menu_bar_items, tool_bar_items, ' +
+ 'Fexecute_extended_command): Deal with \'keymap\' property.']]]
+
+ assert_equal expected, parser.parse_entries
+ end
+
+ def test_scan
+ parser = util_parser <<-ChangeLog
+Tue Dec 4 08:32:10 2012 Eric Hodel <drbrain@segment7.net>
+
+ * lib/rdoc/ri/driver.rb: Fixed ri page display for files with
+ extensions.
+ * test/rdoc/test_rdoc_ri_driver.rb: Test for above
+
+Mon Dec 3 20:37:22 2012 Koichi Sasada <ko1@atdot.net>
+
+ * vm_exec.c: check VM_COLLECT_USAGE_DETAILS.
+
+ ChangeLog
+
+ parser.scan
+
+ expected = doc(
+ head(1, File.basename(@tempfile.path)),
+ blank_line,
+ head(2, '2012-12-04'),
+ blank_line,
+ head(3, 'Tue Dec 4 08:32:10 2012 Eric Hodel <drbrain@segment7.net>'),
+ blank_line,
+ list(:NOTE,
+ item('lib/rdoc/ri/driver.rb', para('Fixed ri page display for ' +
+ 'files with extensions.')),
+ item('test/rdoc/test_rdoc_ri_driver.rb', para('Test for above'))),
+ head(2, '2012-12-03'),
+ blank_line,
+ head(3, 'Mon Dec 3 20:37:22 2012 Koichi Sasada <ko1@atdot.net>'),
+ blank_line,
+ list(:NOTE,
+ item('vm_exec.c', para('check VM_COLLECT_USAGE_DETAILS.'))))
+
+ expected.file = @top_level
+
+ assert_equal expected, @top_level.comment
+ end
+
+ def util_parser content = ''
+ RDoc::Parser::ChangeLog.new \
+ @top_level, @tempfile.path, content, @options, @stats
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_parser_markdown.rb b/jni/ruby/test/rdoc/test_rdoc_parser_markdown.rb
new file mode 100644
index 0000000..b17e144
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_parser_markdown.rb
@@ -0,0 +1,61 @@
+require 'rdoc/test_case'
+
+class TestRDocParserMarkdown < RDoc::TestCase
+
+ def setup
+ super
+
+ @RP = RDoc::Parser
+
+ @tempfile = Tempfile.new self.class.name
+ filename = @tempfile.path
+
+ @top_level = @store.add_file filename
+ @fn = filename
+ @options = RDoc::Options.new
+ @stats = RDoc::Stats.new @store, 0
+ end
+
+ def teardown
+ super
+
+ @tempfile.close!
+ end
+
+ def test_file
+ assert_kind_of RDoc::Parser::Text, util_parser('')
+ end
+
+ def test_class_can_parse
+ temp_dir do
+ FileUtils.touch 'foo.md'
+ assert_equal @RP::Markdown, @RP.can_parse('foo.md')
+ FileUtils.touch 'foo.md.ja'
+ assert_equal @RP::Markdown, @RP.can_parse('foo.md.ja')
+
+ FileUtils.touch 'foo.markdown'
+ assert_equal @RP::Markdown, @RP.can_parse('foo.markdown')
+ FileUtils.touch 'foo.markdown.ja'
+ assert_equal @RP::Markdown, @RP.can_parse('foo.markdown.ja')
+ end
+ end
+
+ def test_scan
+ parser = util_parser 'it *really* works'
+
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('it _really_ works'))
+ expected.file = @top_level
+
+ parser.scan
+
+ assert_equal expected, @top_level.comment.parse
+ end
+
+ def util_parser content
+ RDoc::Parser::Markdown.new @top_level, @fn, content, @options, @stats
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_parser_rd.rb b/jni/ruby/test/rdoc/test_rdoc_parser_rd.rb
new file mode 100644
index 0000000..9be0d7d
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_parser_rd.rb
@@ -0,0 +1,55 @@
+require 'rdoc/test_case'
+
+class TestRDocParserRd < RDoc::TestCase
+
+ def setup
+ super
+
+ @RP = RDoc::Parser
+
+ @tempfile = Tempfile.new self.class.name
+ filename = @tempfile.path
+
+ @top_level = @store.add_file filename
+ @fn = filename
+ @options = RDoc::Options.new
+ @stats = RDoc::Stats.new @store, 0
+ end
+
+ def teardown
+ super
+
+ @tempfile.close!
+ end
+
+ def test_file
+ assert_kind_of RDoc::Parser::Text, util_parser('')
+ end
+
+ def test_class_can_parse
+ temp_dir do
+ FileUtils.touch 'foo.rd'
+ assert_equal @RP::RD, @RP.can_parse('foo.rd')
+
+ FileUtils.touch 'foo.rd.ja'
+ assert_equal @RP::RD, @RP.can_parse('foo.rd.ja')
+ end
+ end
+
+ def test_scan
+ parser = util_parser 'it ((*really*)) works'
+
+ expected = doc(para('it <em>really</em> works'))
+ expected.file = @top_level
+
+ parser.scan
+
+ assert_equal expected, @top_level.comment.parse
+ end
+
+ def util_parser content
+ RDoc::Parser::RD.new @top_level, @fn, content, @options, @stats
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_parser_ruby.rb b/jni/ruby/test/rdoc/test_rdoc_parser_ruby.rb
new file mode 100644
index 0000000..8750433
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_parser_ruby.rb
@@ -0,0 +1,3322 @@
+# coding: utf-8
+
+require 'rdoc/test_case'
+
+class TestRDocParserRuby < RDoc::TestCase
+
+ def setup
+ super
+
+ @tempfile = Tempfile.new self.class.name
+ @filename = @tempfile.path
+
+ # Some tests need two paths.
+ @tempfile2 = Tempfile.new self.class.name
+ @filename2 = @tempfile2.path
+
+ @top_level = @store.add_file @filename
+ @top_level2 = @store.add_file @filename2
+
+ @options = RDoc::Options.new
+ @options.quiet = true
+ @options.option_parser = OptionParser.new
+
+ @comment = RDoc::Comment.new '', @top_level
+
+ @stats = RDoc::Stats.new @store, 0
+ end
+
+ def teardown
+ super
+
+ @tempfile.close!
+ @tempfile2.close!
+ end
+
+ def test_collect_first_comment
+ p = util_parser <<-CONTENT
+# first
+
+# second
+class C; end
+ CONTENT
+
+ comment = p.collect_first_comment
+
+ assert_equal RDoc::Comment.new("# first\n", @top_level), comment
+ end
+
+ def test_collect_first_comment_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ @options.encoding = Encoding::CP852
+
+ p = util_parser <<-CONTENT
+# first
+
+# second
+class C; end
+ CONTENT
+
+ comment = p.collect_first_comment
+
+ assert_equal Encoding::CP852, comment.text.encoding
+ end
+
+ def test_collect_first_comment_rd_hash
+ parser = util_parser <<-CONTENT
+=begin
+first
+=end
+
+# second
+class C; end
+ CONTENT
+
+ comment = parser.collect_first_comment
+
+ assert_equal RDoc::Comment.new("first\n\n", @top_level), comment
+ end
+
+ def test_get_class_or_module
+ ctxt = RDoc::Context.new
+ ctxt.store = @store
+
+ cont, name_t, given_name = util_parser('A') .get_class_or_module ctxt
+
+ assert_equal ctxt, cont
+ assert_equal 'A', name_t.text
+ assert_equal 'A', given_name
+
+ cont, name_t, given_name = util_parser('B::C') .get_class_or_module ctxt
+
+ b = @store.find_module_named('B')
+ assert_equal b, cont
+ assert_equal [@top_level], b.in_files
+ assert_equal 'C', name_t.text
+ assert_equal 'B::C', given_name
+
+ cont, name_t, given_name = util_parser('D:: E').get_class_or_module ctxt
+
+ assert_equal @store.find_module_named('D'), cont
+ assert_equal 'E', name_t.text
+ assert_equal 'D::E', given_name
+
+ assert_raises NoMethodError do
+ util_parser("A::\nB").get_class_or_module ctxt
+ end
+ end
+
+ def test_get_class_or_module_document_children
+ ctxt = @top_level.add_class RDoc::NormalClass, 'A'
+ ctxt.stop_doc
+
+ util_parser('B::C').get_class_or_module ctxt
+
+ b = @store.find_module_named('A::B')
+ assert b.ignored?
+
+ d = @top_level.add_class RDoc::NormalClass, 'A::D'
+
+ util_parser('D::E').get_class_or_module ctxt
+
+ refute d.ignored?
+ end
+
+ def test_get_class_or_module_ignore_constants
+ ctxt = RDoc::Context.new
+ ctxt.store = @store
+
+ util_parser('A') .get_class_or_module ctxt, true
+ util_parser('A::B').get_class_or_module ctxt, true
+
+ assert_empty ctxt.constants
+ assert_empty @store.modules_hash.keys
+ assert_empty @store.classes_hash.keys
+ end
+
+ def test_get_class_specification
+ assert_equal 'A', util_parser('A') .get_class_specification
+ assert_equal 'A::B', util_parser('A::B').get_class_specification
+ assert_equal '::A', util_parser('::A').get_class_specification
+
+ assert_equal 'self', util_parser('self').get_class_specification
+
+ assert_equal '', util_parser('').get_class_specification
+
+ assert_equal '', util_parser('$g').get_class_specification
+ end
+
+ def test_get_symbol_or_name
+ util_parser "* & | + 5 / 4"
+
+ assert_equal '*', @parser.get_symbol_or_name
+
+ @parser.skip_tkspace
+
+ assert_equal '&', @parser.get_symbol_or_name
+
+ @parser.skip_tkspace
+
+ assert_equal '|', @parser.get_symbol_or_name
+
+ @parser.skip_tkspace
+
+ assert_equal '+', @parser.get_symbol_or_name
+
+ @parser.skip_tkspace
+ @parser.get_tk
+ @parser.skip_tkspace
+
+ assert_equal '/', @parser.get_symbol_or_name
+ end
+
+ def test_suppress_parents
+ a = @top_level.add_class RDoc::NormalClass, 'A'
+ b = a.add_class RDoc::NormalClass, 'B'
+ c = b.add_class RDoc::NormalClass, 'C'
+
+ util_parser ''
+
+ @parser.suppress_parents c, a
+
+ assert c.suppressed?
+ assert b.suppressed?
+ refute a.suppressed?
+ end
+
+ def test_suppress_parents_documented
+ a = @top_level.add_class RDoc::NormalClass, 'A'
+ b = a.add_class RDoc::NormalClass, 'B'
+ b.add_comment RDoc::Comment.new("hello"), @top_level
+ c = b.add_class RDoc::NormalClass, 'C'
+
+ util_parser ''
+
+ @parser.suppress_parents c, a
+
+ assert c.suppressed?
+ refute b.suppressed?
+ refute a.suppressed?
+ end
+
+ def test_look_for_directives_in_attr
+ util_parser ""
+
+ comment = RDoc::Comment.new "# :attr: my_attr\n", @top_level
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :attr: my_attr\n", comment.text
+
+ comment = RDoc::Comment.new "# :attr_reader: my_method\n", @top_level
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :attr_reader: my_method\n", comment.text
+
+ comment = RDoc::Comment.new "# :attr_writer: my_method\n", @top_level
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :attr_writer: my_method\n", comment.text
+ end
+
+ def test_look_for_directives_in_commented
+ util_parser ""
+
+ comment = RDoc::Comment.new <<-COMMENT, @top_level
+# how to make a section:
+# # :section: new section
+ COMMENT
+
+ @parser.look_for_directives_in @top_level, comment
+
+ section = @top_level.current_section
+ assert_equal nil, section.title
+ assert_equal nil, section.comment
+
+ assert_equal "# how to make a section:\n# # :section: new section\n",
+ comment.text
+ end
+
+ def test_look_for_directives_in_method
+ util_parser ""
+
+ comment = RDoc::Comment.new "# :method: my_method\n", @top_level
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :method: my_method\n", comment.text
+
+ comment = RDoc::Comment.new "# :singleton-method: my_method\n", @top_level
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :singleton-method: my_method\n", comment.text
+ end
+
+ def test_look_for_directives_in_section
+ util_parser ""
+
+ comment = RDoc::Comment.new <<-COMMENT, @top_level
+# :section: new section
+# woo stuff
+ COMMENT
+
+ @parser.look_for_directives_in @top_level, comment
+
+ section = @top_level.current_section
+ assert_equal 'new section', section.title
+ assert_equal [comment("# woo stuff\n", @top_level)], section.comments
+
+ assert_empty comment
+ end
+
+ def test_look_for_directives_in_unhandled
+ util_parser ""
+
+ comment = RDoc::Comment.new "# :unhandled: blah\n", @top_level
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal 'blah', @top_level.metadata['unhandled']
+ end
+
+ def test_parse_alias
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "alias :next= :bar"
+
+ tk = @parser.get_tk
+
+ alas = @parser.parse_alias klass, RDoc::Parser::Ruby::NORMAL, tk, 'comment'
+
+ assert_equal 'bar', alas.old_name
+ assert_equal 'next=', alas.new_name
+ assert_equal klass, alas.parent
+ assert_equal 'comment', alas.comment
+ assert_equal @top_level, alas.file
+ assert_equal 0, alas.offset
+ assert_equal 1, alas.line
+ end
+
+ def test_parse_alias_singleton
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "alias :next= :bar"
+
+ tk = @parser.get_tk
+
+ alas = @parser.parse_alias klass, RDoc::Parser::Ruby::SINGLE, tk, 'comment'
+
+ assert_equal 'bar', alas.old_name
+ assert_equal 'next=', alas.new_name
+ assert_equal klass, alas.parent
+ assert_equal 'comment', alas.comment
+ assert_equal @top_level, alas.file
+ assert alas.singleton
+ end
+
+ def test_parse_alias_stopdoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+ klass.stop_doc
+
+ util_parser "alias :next= :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_alias klass, RDoc::Parser::Ruby::NORMAL, tk, 'comment'
+
+ assert_empty klass.aliases
+ assert_empty klass.unmatched_alias_lists
+ end
+
+ def test_parse_alias_meta
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "alias m.chop m"
+
+ tk = @parser.get_tk
+
+ alas = @parser.parse_alias klass, RDoc::Parser::Ruby::NORMAL, tk, 'comment'
+
+ assert_nil alas
+ end
+
+ def test_parse_attr
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my attr\n", @top_level
+
+ util_parser "attr :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 1, klass.attributes.length
+
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'my attr', foo.comment.text
+ assert_equal @top_level, foo.file
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+ end
+
+ def test_parse_attr_stopdoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+ klass.stop_doc
+
+ comment = RDoc::Comment.new "##\n# my attr\n", @top_level
+
+ util_parser "attr :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_empty klass.attributes
+ end
+
+ def test_parse_attr_accessor
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my attr\n", @top_level
+
+ util_parser "attr_accessor :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 2, klass.attributes.length
+
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ assert_equal 'my attr', foo.comment.text
+ assert_equal @top_level, foo.file
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+
+ bar = klass.attributes.last
+ assert_equal 'bar', bar.name
+ assert_equal 'RW', bar.rw
+ assert_equal 'my attr', bar.comment.text
+ end
+
+ def test_parse_attr_accessor_nodoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my attr\n", @top_level
+
+ util_parser "attr_accessor :foo, :bar # :nodoc:"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 0, klass.attributes.length
+ end
+
+ def test_parse_attr_accessor_nodoc_track
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my attr\n", @top_level
+
+ @options.visibility = :nodoc
+
+ util_parser "attr_accessor :foo, :bar # :nodoc:"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ refute_empty klass.attributes
+ end
+
+ def test_parse_attr_accessor_stopdoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+ klass.stop_doc
+
+ comment = RDoc::Comment.new "##\n# my attr\n", @top_level
+
+ util_parser "attr_accessor :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_empty klass.attributes
+ end
+
+ def test_parse_attr_accessor_writer
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my attr\n", @top_level
+
+ util_parser "attr_writer :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 2, klass.attributes.length
+
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'W', foo.rw
+ assert_equal "my attr", foo.comment.text
+ assert_equal @top_level, foo.file
+
+ bar = klass.attributes.last
+ assert_equal 'bar', bar.name
+ assert_equal 'W', bar.rw
+ assert_equal "my attr", bar.comment.text
+ end
+
+ def test_parse_meta_attr
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# :attr: \n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 2, klass.attributes.length
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ assert_equal "my method", foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_attr_accessor
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment =
+ RDoc::Comment.new "##\n# :attr_accessor: \n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 2, klass.attributes.length
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_attr_named
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# :attr: foo\n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 1, klass.attributes.length
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_attr_reader
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment =
+ RDoc::Comment.new "##\n# :attr_reader: \n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'R', foo.rw
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_attr_stopdoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+ klass.stop_doc
+
+ comment = RDoc::Comment.new "##\n# :attr: \n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_empty klass.attributes
+ end
+
+ def test_parse_meta_attr_writer
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment =
+ RDoc::Comment.new "##\n# :attr_writer: \n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'W', foo.rw
+ assert_equal "my method", foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_class
+ comment = RDoc::Comment.new "##\n# my class\n", @top_level
+
+ util_parser "class Foo\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+ assert_equal 'my class', foo.comment.text
+ assert_equal [@top_level], foo.in_files
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+ end
+
+ def test_parse_class_singleton
+ comment = RDoc::Comment.new "##\n# my class\n", @top_level
+
+ util_parser <<-RUBY
+class C
+ class << self
+ end
+end
+ RUBY
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ c = @top_level.classes.first
+ assert_equal 'C', c.full_name
+ assert_equal 0, c.offset
+ assert_equal 1, c.line
+ end
+
+ def test_parse_class_ghost_method
+ util_parser <<-CLASS
+class Foo
+ ##
+ # :method: blah
+ # my method
+end
+ CLASS
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ assert_equal 'Foo#blah', blah.full_name
+ assert_equal @top_level, blah.file
+ end
+
+ def test_parse_class_ghost_method_yields
+ util_parser <<-CLASS
+class Foo
+ ##
+ # :method:
+ # :call-seq:
+ # yields(name)
+end
+ CLASS
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ blah = foo.method_list.first
+ assert_equal 'Foo#yields', blah.full_name
+ assert_equal 'yields(name)', blah.call_seq
+ assert_equal @top_level, blah.file
+ end
+
+ def test_parse_class_multi_ghost_methods
+ util_parser <<-'CLASS'
+class Foo
+ ##
+ # :method: one
+ #
+ # my method
+
+ ##
+ # :method: two
+ #
+ # my method
+
+ [:one, :two].each do |t|
+ eval("def #{t}; \"#{t}\"; end")
+ end
+end
+ CLASS
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ assert_equal 2, foo.method_list.length
+ end
+
+ def test_parse_class_nodoc
+ comment = RDoc::Comment.new "##\n# my class\n", @top_level
+
+ util_parser "class Foo # :nodoc:\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+ assert_empty foo.comment
+ assert_equal [@top_level], foo.in_files
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+ end
+
+ def test_parse_class_single_root
+ comment = RDoc::Comment.new "##\n# my class\n", @top_level
+
+ util_parser "class << ::Foo\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = @store.all_modules.first
+ assert_equal 'Foo', foo.full_name
+ end
+
+ def test_parse_class_stopdoc
+ @top_level.stop_doc
+
+ comment = RDoc::Comment.new "##\n# my class\n", @top_level
+
+ util_parser "class Foo\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_empty @top_level.classes.first.comment
+ end
+
+ def test_parse_multi_ghost_methods
+ util_parser <<-'CLASS'
+class Foo
+ ##
+ # :method: one
+ #
+ # my method
+
+ ##
+ # :method: two
+ #
+ # my method
+
+ [:one, :two].each do |t|
+ eval("def #{t}; \"#{t}\"; end")
+ end
+end
+ CLASS
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ assert_equal 2, foo.method_list.length
+ end
+
+ def test_parse_const_fail_w_meta
+ util_parser <<-CLASS
+class ConstFailMeta
+ ##
+ # :attr: one
+ #
+ # an attribute
+
+ OtherModule.define_attr(self, :one)
+end
+ CLASS
+
+ tk = @parser.get_tk
+
+ @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ const_fail_meta = @top_level.classes.first
+ assert_equal 'ConstFailMeta', const_fail_meta.full_name
+
+ assert_equal 1, const_fail_meta.attributes.length
+ end
+
+ def test_parse_class_nested_superclass
+ foo = @top_level.add_module RDoc::NormalModule, 'Foo'
+
+ util_parser "class Bar < Super\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_class foo, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ bar = foo.classes.first
+ assert_equal 'Super', bar.superclass
+ end
+
+ def test_parse_module
+ comment = RDoc::Comment.new "##\n# my module\n", @top_level
+
+ util_parser "module Foo\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_module @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = @top_level.modules.first
+ assert_equal 'Foo', foo.full_name
+ assert_equal 'my module', foo.comment.text
+ end
+
+ def test_parse_module_nodoc
+ @top_level.stop_doc
+
+ comment = RDoc::Comment.new "##\n# my module\n", @top_level
+
+ util_parser "module Foo # :nodoc:\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_module @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = @top_level.modules.first
+ assert_equal 'Foo', foo.full_name
+ assert_empty foo.comment
+ end
+
+ def test_parse_module_stopdoc
+ @top_level.stop_doc
+
+ comment = RDoc::Comment.new "##\n# my module\n", @top_level
+
+ util_parser "module Foo\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_module @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = @top_level.modules.first
+ assert_equal 'Foo', foo.full_name
+ assert_empty foo.comment
+ end
+
+ def test_parse_class_colon3
+ code = <<-CODE
+class A
+ class ::B
+ end
+end
+ CODE
+
+ util_parser code
+
+ @parser.parse_class @top_level, false, @parser.get_tk, @comment
+
+ assert_equal %w[A B], @store.all_classes.map { |c| c.full_name }.sort
+ end
+
+ def test_parse_class_colon3_self_reference
+ code = <<-CODE
+class A::B
+ class ::A
+ end
+end
+ CODE
+
+ util_parser code
+
+ @parser.parse_class @top_level, false, @parser.get_tk, @comment
+
+ assert_equal %w[A A::B], @store.all_classes.map { |c| c.full_name }.sort
+ end
+
+ def test_parse_class_single
+ code = <<-CODE
+class A
+ class << B
+ end
+ class << d = Object.new
+ def foo; end
+ alias bar foo
+ end
+end
+ CODE
+
+ util_parser code
+
+ @parser.parse_class @top_level, false, @parser.get_tk, @comment
+
+ assert_equal %w[A], @store.all_classes.map { |c| c.full_name }
+
+ modules = @store.all_modules.sort_by { |c| c.full_name }
+ assert_equal %w[A::B A::d], modules.map { |c| c.full_name }
+
+ b = modules.first
+ assert_equal 10, b.offset
+ assert_equal 2, b.line
+
+ # make sure method/alias was not added to enclosing class/module
+ a = @store.classes_hash['A']
+ assert_empty a.method_list
+
+ # make sure non-constant-named module will be removed from documentation
+ d = @store.modules_hash['A::d']
+ assert d.remove_from_documentation?
+ end
+
+ def test_parse_class_single_gvar
+ code = <<-CODE
+class << $g
+ def m
+ end
+end
+ CODE
+
+ util_parser code
+
+ @parser.parse_class @top_level, false, @parser.get_tk, ''
+
+ assert_empty @store.all_classes
+ mod = @store.all_modules.first
+
+ refute mod.document_self
+
+ assert_empty mod.method_list
+ end
+
+ # TODO this is really a Context#add_class test
+ def test_parse_class_object
+ code = <<-CODE
+module A
+ class B
+ end
+ class Object
+ end
+ class C < Object
+ end
+end
+ CODE
+
+ util_parser code
+
+ @parser.parse_module @top_level, false, @parser.get_tk, @comment
+
+ assert_equal %w[A],
+ @store.all_modules.map { |c| c.full_name }
+ assert_equal %w[A::B A::C A::Object],
+ @store.all_classes.map { |c| c.full_name }.sort
+
+ assert_equal 'Object', @store.classes_hash['A::B'].superclass
+ assert_equal 'Object', @store.classes_hash['A::Object'].superclass
+ assert_equal 'A::Object', @store.classes_hash['A::C'].superclass.full_name
+ end
+
+ def test_parse_class_mistaken_for_module
+ # The code below is not strictly legal Ruby (Foo must have been defined
+ # before Foo::Bar is encountered), but RDoc might encounter Foo::Bar
+ # before Foo if they live in different files.
+
+ code = <<-RUBY
+class Foo::Bar
+end
+
+module Foo::Baz
+end
+
+class Foo
+end
+ RUBY
+
+ util_parser code
+
+ @parser.scan
+
+ assert_equal %w[Foo::Baz], @store.modules_hash.keys
+ assert_empty @top_level.modules
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+
+ bar = foo.classes.first
+ assert_equal 'Foo::Bar', bar.full_name
+
+ baz = foo.modules.first
+ assert_equal 'Foo::Baz', baz.full_name
+ end
+
+ def test_parse_class_definition_encountered_after_class_reference
+ # The code below is not legal Ruby (Foo must have been defined before
+ # Foo.bar is encountered), but RDoc might encounter Foo.bar before Foo if
+ # they live in different files.
+
+ code = <<-EOF
+def Foo.bar
+end
+
+class Foo < IO
+end
+ EOF
+
+ util_parser code
+
+ @parser.scan
+
+ assert_empty @store.modules_hash
+ assert_empty @store.all_modules
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.full_name
+ assert_equal 'IO', foo.superclass
+
+ bar = foo.method_list.first
+ assert_equal 'bar', bar.name
+ end
+
+ def test_parse_module_relative_to_top_level_namespace
+ comment = RDoc::Comment.new <<-EOF, @top_level
+#
+# Weirdly named module
+#
+EOF
+
+ code = <<-EOF
+#{comment.text}
+module ::Foo
+ class Helper
+ end
+end
+EOF
+
+ util_parser code
+ @parser.scan()
+
+ foo = @top_level.modules.first
+ assert_equal 'Foo', foo.full_name
+ assert_equal 'Weirdly named module', foo.comment.text
+
+ helper = foo.classes.first
+ assert_equal 'Foo::Helper', helper.full_name
+ end
+
+ def test_parse_comment_attr
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# :attr: foo\n# my attr\n", @top_level
+
+ util_parser "\n"
+
+ tk = @parser.get_tk
+
+ @parser.parse_comment klass, tk, comment
+
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ assert_equal 'my attr', foo.comment.text
+ assert_equal @top_level, foo.file
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+
+ assert_equal nil, foo.viewer
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.force_documentation
+ assert_equal klass, foo.parent
+ assert_equal :public, foo.visibility
+ assert_equal "\n", foo.text
+
+ assert_equal klass.current_section, foo.section
+ end
+
+ def test_parse_comment_attr_attr_reader
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# :attr_reader: foo\n", @top_level
+
+ util_parser "\n"
+
+ tk = @parser.get_tk
+
+ @parser.parse_comment klass, tk, comment
+
+ foo = klass.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'R', foo.rw
+ end
+
+ def test_parse_comment_attr_stopdoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+ klass.stop_doc
+
+ comment = RDoc::Comment.new "##\n# :attr: foo\n# my attr\n", @top_level
+
+ util_parser "\n"
+
+ tk = @parser.get_tk
+
+ @parser.parse_comment klass, tk, comment
+
+ assert_empty klass.attributes
+ end
+
+ def test_parse_comment_method
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# :method: foo\n# my method\n", @top_level
+
+ util_parser "\n"
+
+ tk = @parser.get_tk
+
+ @parser.parse_comment klass, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+
+ assert_equal [], foo.aliases
+ assert_equal nil, foo.block_params
+ assert_equal nil, foo.call_seq
+ assert_equal nil, foo.is_alias_for
+ assert_equal nil, foo.viewer
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal '', foo.params
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.dont_rename_initialize
+ assert_equal false, foo.force_documentation
+ assert_equal klass, foo.parent
+ assert_equal false, foo.singleton
+ assert_equal :public, foo.visibility
+ assert_equal "\n", foo.text
+ assert_equal klass.current_section, foo.section
+
+ stream = [
+ tk(:COMMENT, 0, 1, 1, nil,
+ "# File #{@top_level.relative_name}, line 1"),
+ RDoc::Parser::Ruby::NEWLINE_TOKEN,
+ tk(:SPACE, 0, 1, 1, nil, ''),
+ ]
+
+ assert_equal stream, foo.token_stream
+ end
+
+ def test_parse_comment_method_args
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+
+ util_parser "\n"
+
+ tk = @parser.get_tk
+
+ @parser.parse_comment klass, tk,
+ comment("##\n# :method: foo\n# :args: a, b\n")
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal 'a, b', foo.params
+ end
+
+ def test_parse_comment_method_stopdoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+ klass.stop_doc
+
+ comment = RDoc::Comment.new "##\n# :method: foo\n# my method\n", @top_level
+
+ util_parser "\n"
+
+ tk = @parser.get_tk
+
+ @parser.parse_comment klass, tk, comment
+
+ assert_empty klass.method_list
+ end
+
+ def test_parse_constant
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "A = v"
+
+ tk = @parser.get_tk
+
+ @parser.parse_constant klass, tk, @comment
+
+ foo = klass.constants.first
+
+ assert_equal 'A', foo.name
+ assert_equal @top_level, foo.file
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+ end
+
+ def test_parse_constant_attrasgn
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "A[k] = v"
+
+ tk = @parser.get_tk
+
+ @parser.parse_constant klass, tk, @comment
+
+ assert klass.constants.empty?
+ end
+
+ def test_parse_constant_alias
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+ klass.add_class RDoc::NormalClass, 'B'
+
+ util_parser "A = B"
+
+ tk = @parser.get_tk
+
+ @parser.parse_constant klass, tk, @comment
+
+ assert_equal 'Foo::A', klass.find_module_named('A').full_name
+ end
+
+ def test_parse_constant_alias_same_name
+ foo = @top_level.add_class RDoc::NormalClass, 'Foo'
+ @top_level.add_class RDoc::NormalClass, 'Bar'
+ bar = foo.add_class RDoc::NormalClass, 'Bar'
+
+ assert @store.find_class_or_module('::Bar')
+
+ util_parser "A = ::Bar"
+
+ tk = @parser.get_tk
+
+ @parser.parse_constant foo, tk, @comment
+
+ assert_equal 'A', bar.find_module_named('A').full_name
+ end
+
+ def test_parse_constant_in_method
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser 'A::B = v'
+
+ tk = @parser.get_tk
+
+ @parser.parse_constant klass, tk, @comment, true
+
+ assert_empty klass.constants
+
+ assert_empty @store.modules_hash.keys
+ assert_equal %w[Foo], @store.classes_hash.keys
+ end
+
+ def test_parse_constant_rescue
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "A => e"
+
+ tk = @parser.get_tk
+
+ @parser.parse_constant klass, tk, @comment
+
+ assert_empty klass.constants
+ assert_empty klass.modules
+
+ assert_empty @store.modules_hash.keys
+ assert_equal %w[Foo], @store.classes_hash.keys
+ end
+
+ def test_parse_constant_stopdoc
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+ klass.stop_doc
+
+ util_parser "A = v"
+
+ tk = @parser.get_tk
+
+ @parser.parse_constant klass, tk, @comment
+
+ assert_empty klass.constants
+ end
+
+ def test_parse_comment_nested
+ content = <<-CONTENT
+A::B::C = 1
+ CONTENT
+
+ util_parser content
+
+ tk = @parser.get_tk
+
+ parsed = @parser.parse_constant @top_level, tk, 'comment'
+
+ assert parsed
+
+ a = @top_level.find_module_named 'A'
+ b = a.find_module_named 'B'
+ c = b.constants.first
+
+ assert_equal 'A::B::C', c.full_name
+ assert_equal 'comment', c.comment
+ end
+
+ def test_parse_extend_or_include_extend
+ klass = RDoc::NormalClass.new 'C'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "# my extend\n", @top_level
+
+ util_parser "extend I"
+
+ @parser.get_tk # extend
+
+ @parser.parse_extend_or_include RDoc::Extend, klass, comment
+
+ assert_equal 1, klass.extends.length
+
+ ext = klass.extends.first
+ assert_equal 'I', ext.name
+ assert_equal 'my extend', ext.comment.text
+ assert_equal @top_level, ext.file
+ end
+
+ def test_parse_extend_or_include_include
+ klass = RDoc::NormalClass.new 'C'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "# my include\n", @top_level
+
+ util_parser "include I"
+
+ @parser.get_tk # include
+
+ @parser.parse_extend_or_include RDoc::Include, klass, comment
+
+ assert_equal 1, klass.includes.length
+
+ incl = klass.includes.first
+ assert_equal 'I', incl.name
+ assert_equal 'my include', incl.comment.text
+ assert_equal @top_level, incl.file
+ end
+
+ def test_parse_meta_method
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+
+ assert_equal [], foo.aliases
+ assert_equal nil, foo.block_params
+ assert_equal nil, foo.call_seq
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.dont_rename_initialize
+ assert_equal false, foo.force_documentation
+ assert_equal nil, foo.is_alias_for
+ assert_equal '', foo.params
+ assert_equal klass, foo.parent
+ assert_equal false, foo.singleton
+ assert_equal 'add_my_method :foo', foo.text
+ assert_equal nil, foo.viewer
+ assert_equal :public, foo.visibility
+ assert_equal klass.current_section, foo.section
+
+ stream = [
+ tk(:COMMENT, 0, 1, 1, nil,
+ "# File #{@top_level.relative_name}, line 1"),
+ RDoc::Parser::Ruby::NEWLINE_TOKEN,
+ tk(:SPACE, 0, 1, 1, nil, ''),
+ tk(:IDENTIFIER, 0, 1, 0, 'add_my_method', 'add_my_method'),
+ tk(:SPACE, 0, 1, 13, nil, ' '),
+ tk(:SYMBOL, 0, 1, 14, nil, ':foo'),
+ tk(:COMMA, 0, 1, 18, nil, ','),
+ tk(:SPACE, 0, 1, 19, nil, ' '),
+ tk(:SYMBOL, 0, 1, 20, nil, ':bar'),
+ tk(:NL, 0, 1, 24, nil, "\n"),
+ ]
+
+ assert_equal stream, foo.token_stream
+ end
+
+ def test_parse_meta_method_block
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ content = <<-CONTENT
+inline(:my_method) do |*args|
+ "this method causes z to disappear"
+end
+ CONTENT
+
+ util_parser content
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal tk(:NL, 0, 3, 3, 3, "\n"), @parser.get_tk
+ end
+
+ def test_parse_meta_method_define_method
+ klass = RDoc::NormalClass.new 'Foo'
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ util_parser "define_method :foo do end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_method_name
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment =
+ RDoc::Comment.new "##\n# :method: woo_hoo!\n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'woo_hoo!', foo.name
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_method_singleton
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment =
+ RDoc::Comment.new "##\n# :singleton-method:\n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal true, foo.singleton, 'singleton method'
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_method_singleton_name
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment =
+ RDoc::Comment.new "##\n# :singleton-method: woo_hoo!\n# my method\n",
+ @top_level
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'woo_hoo!', foo.name
+ assert_equal true, foo.singleton, 'singleton method'
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_method_string_name
+ klass = RDoc::NormalClass.new 'Foo'
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ util_parser "add_my_method 'foo'"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_meta_method_stopdoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+ klass.stop_doc
+
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_empty klass.method_list
+ end
+
+ def test_parse_meta_method_unknown
+ klass = RDoc::NormalClass.new 'Foo'
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ util_parser "add_my_method ('foo')"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'unknown', foo.name
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_method
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ util_parser "def foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal 'my method', foo.comment.text
+ assert_equal @top_level, foo.file
+ assert_equal 0, foo.offset
+ assert_equal 1, foo.line
+
+ assert_equal [], foo.aliases
+ assert_equal nil, foo.block_params
+ assert_equal nil, foo.call_seq
+ assert_equal nil, foo.is_alias_for
+ assert_equal nil, foo.viewer
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal '()', foo.params
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.dont_rename_initialize
+ assert_equal false, foo.force_documentation
+ assert_equal klass, foo.parent
+ assert_equal false, foo.singleton
+ assert_equal :public, foo.visibility
+ assert_equal 'def foo', foo.text
+ assert_equal klass.current_section, foo.section
+
+ stream = [
+ tk(:COMMENT, 0, 1, 1, nil,
+ "# File #{@top_level.relative_name}, line 1"),
+ RDoc::Parser::Ruby::NEWLINE_TOKEN,
+ tk(:SPACE, 0, 1, 1, nil, ''),
+ tk(:DEF, 0, 1, 0, 'def', 'def'),
+ tk(:SPACE, 3, 1, 3, nil, ' '),
+ tk(:IDENTIFIER, 4, 1, 4, 'foo', 'foo'),
+ tk(:LPAREN, 7, 1, 7, nil, '('),
+ tk(:RPAREN, 8, 1, 8, nil, ')'),
+ tk(:SPACE, 9, 1, 9, nil, ' '),
+ tk(:COLON, 10, 1, 10, nil, ':'),
+ tk(:IDENTIFIER, 11, 1, 11, 'bar', 'bar'),
+ tk(:SPACE, 14, 1, 14, nil, ' '),
+ tk(:END, 15, 1, 15, 'end', 'end'),
+ ]
+
+ assert_equal stream, foo.token_stream
+ end
+
+ def test_parse_method_alias
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def m() alias a b; end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ assert klass.aliases.empty?
+ end
+
+ def test_parse_method_ampersand
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def self.&\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ ampersand = klass.method_list.first
+ assert_equal '&', ampersand.name
+ assert ampersand.singleton
+ end
+
+ def test_parse_method_constant
+ c = RDoc::Constant.new 'CONST', nil, ''
+ m = @top_level.add_class RDoc::NormalModule, 'M'
+ m.add_constant c
+
+ util_parser "def CONST.m() end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method m, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ assert_empty @store.modules_hash.keys
+ assert_equal %w[M], @store.classes_hash.keys
+ end
+
+ def test_parse_method_false
+ util_parser "def false.foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ klass = @store.find_class_named 'FalseClass'
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ end
+
+ def test_parse_method_funky
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def (blah).foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ assert_empty klass.method_list
+ end
+
+ def test_parse_method_gvar
+ util_parser "def $stdout.foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ assert @top_level.method_list.empty?
+ end
+
+ def test_parse_method_gvar_insane
+ util_parser "def $stdout.foo() class << $other; end; end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ assert @top_level.method_list.empty?
+
+ assert_empty @store.all_classes
+
+ assert_equal 1, @store.all_modules.length
+
+ refute @store.all_modules.first.document_self
+ end
+
+ def test_parse_method_internal_gvar
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo() def $blah.bar() end end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ assert_equal 1, klass.method_list.length
+ end
+
+ def test_parse_method_internal_ivar
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo() def @blah.bar() end end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ assert_equal 1, klass.method_list.length
+ end
+
+ def test_parse_method_internal_lvar
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo() def blah.bar() end end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ assert_equal 1, klass.method_list.length
+ end
+
+ def test_parse_method_nil
+ util_parser "def nil.foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ klass = @store.find_class_named 'NilClass'
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ end
+
+ def test_parse_method_nodoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo # :nodoc:\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment('')
+
+ assert_empty klass.method_list
+ end
+
+ def test_parse_method_nodoc_track
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ @options.visibility = :nodoc
+
+ util_parser "def foo # :nodoc:\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment('')
+
+ refute_empty klass.method_list
+ end
+
+ def test_parse_method_no_parens
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo arg1, arg2 = {}\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = klass.method_list.first
+ assert_equal '(arg1, arg2 = {})', foo.params
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_method_parameters_comment
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo arg1, arg2 # some useful comment\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = klass.method_list.first
+ assert_equal '(arg1, arg2)', foo.params
+ end
+
+ def test_parse_method_parameters_comment_continue
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo arg1, arg2, # some useful comment\narg3\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = klass.method_list.first
+ assert_equal '(arg1, arg2, arg3)', foo.params
+ end
+
+ def test_parse_method_star
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def self.*\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ ampersand = klass.method_list.first
+ assert_equal '*', ampersand.name
+ assert ampersand.singleton
+ end
+
+ def test_parse_method_stopdoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+ klass.stop_doc
+
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ util_parser "def foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_empty klass.method_list
+ end
+
+ def test_parse_method_toplevel
+ klass = @top_level
+
+ util_parser "def foo arg1, arg2\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ object = @store.find_class_named 'Object'
+
+ foo = object.method_list.first
+ assert_equal 'Object#foo', foo.full_name
+ assert_equal @top_level, foo.file
+ end
+
+ def test_parse_method_toplevel_class
+ klass = @top_level
+
+ util_parser "def Object.foo arg1, arg2\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ object = @store.find_class_named 'Object'
+
+ foo = object.method_list.first
+ assert_equal 'Object::foo', foo.full_name
+ end
+
+ def test_parse_method_true
+ util_parser "def true.foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ klass = @store.find_class_named 'TrueClass'
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ end
+
+ def test_parse_method_utf8
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ method = "def ω() end"
+
+ assert_equal Encoding::UTF_8, method.encoding if
+ Object.const_defined? :Encoding
+
+ util_parser method
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ omega = klass.method_list.first
+ assert_equal "def \317\211", omega.text
+ end
+
+ def test_parse_method_dummy
+ util_parser ".method() end"
+
+ @parser.parse_method_dummy @top_level
+
+ assert_nil @parser.get_tk
+ end
+
+ def test_parse_method_or_yield_parameters_hash
+ util_parser "({})\n"
+
+ m = RDoc::AnyMethod.new nil, 'm'
+
+ result = @parser.parse_method_or_yield_parameters m
+
+ assert_equal '({})', result
+ end
+
+ def test_parse_statements_class_if
+ util_parser <<-CODE
+module Foo
+ X = if TRUE then
+ ''
+ end
+
+ def blah
+ end
+end
+ CODE
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil
+
+ foo = @top_level.modules.first
+ assert_equal 'Foo', foo.full_name, 'module Foo'
+
+ methods = foo.method_list
+ assert_equal 1, methods.length
+ assert_equal 'Foo#blah', methods.first.full_name
+ end
+
+ def test_parse_statements_class_nested
+ comment = RDoc::Comment.new "##\n# my method\n", @top_level
+
+ util_parser "module Foo\n#{comment.text}class Bar\nend\nend"
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL
+
+ foo = @top_level.modules.first
+ assert_equal 'Foo', foo.full_name, 'module Foo'
+
+ bar = foo.classes.first
+ assert_equal 'Foo::Bar', bar.full_name, 'class Foo::Bar'
+ assert_equal 'my method', bar.comment.text
+ end
+
+ def test_parse_statements_def_percent_string_pound
+ util_parser "class C\ndef a\n%r{#}\nend\ndef b() end\nend"
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL
+
+ x = @top_level.classes.first
+
+ assert_equal 2, x.method_list.length
+ a = x.method_list.first
+
+ expected = [
+ tk(:COMMENT, 0, 2, 1, nil, "# File #{@filename}, line 2"),
+ tk(:NL, 0, 0, 0, nil, "\n"),
+ tk(:SPACE, 0, 1, 1, nil, ''),
+ tk(:DEF, 8, 2, 0, 'def', 'def'),
+ tk(:SPACE, 11, 2, 3, nil, ' '),
+ tk(:IDENTIFIER, 12, 2, 4, 'a', 'a'),
+ tk(:NL, 13, 2, 5, nil, "\n"),
+ tk(:DREGEXP, 14, 3, 0, nil, '%r{#}'),
+ tk(:NL, 19, 3, 5, nil, "\n"),
+ tk(:END, 20, 4, 0, 'end', 'end'),
+ ]
+
+ assert_equal expected, a.token_stream
+ end
+
+ def test_parse_statements_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+ @options.encoding = Encoding::CP852
+
+ content = <<-EOF
+class Foo
+ ##
+ # this is my method
+ add_my_method :foo
+end
+ EOF
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.classes.first.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal 'this is my method', foo.comment.text
+ assert_equal Encoding::CP852, foo.comment.text.encoding
+ end
+
+ def test_parse_statements_enddoc
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "\n# :enddoc:"
+
+ @parser.parse_statements klass, RDoc::Parser::Ruby::NORMAL, nil
+
+ assert klass.done_documenting
+ end
+
+ def test_parse_statements_enddoc_top_level
+ util_parser "\n# :enddoc:"
+
+ assert_throws :eof do
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil
+ end
+ end
+
+ def test_parse_statements_identifier_meta_method
+ content = <<-EOF
+class Foo
+ ##
+ # this is my method
+ add_my_method :foo
+end
+ EOF
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.classes.first.method_list.first
+ assert_equal 'foo', foo.name
+ end
+
+ def test_parse_statements_identifier_alias_method
+ content = <<-RUBY
+class Foo
+ def foo() end
+ alias_method :foo2, :foo
+end
+ RUBY
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.classes.first.method_list[0]
+ assert_equal 'foo', foo.name
+
+ foo2 = @top_level.classes.first.method_list.last
+ assert_equal 'foo2', foo2.name
+ assert_equal 'foo', foo2.is_alias_for.name
+ assert @top_level.classes.first.aliases.empty?
+ end
+
+ def test_parse_statements_identifier_alias_method_before_original_method
+ # This is not strictly legal Ruby code, but it simulates finding an alias
+ # for a method before finding the original method, which might happen
+ # to rdoc if the alias is in a different file than the original method
+ # and rdoc processes the alias' file first.
+ content = <<-EOF
+class Foo
+ alias_method :foo2, :foo
+
+ alias_method :foo3, :foo
+
+ def foo()
+ end
+
+ alias_method :foo4, :foo
+
+ alias_method :foo5, :unknown
+end
+EOF
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.classes.first.method_list[0]
+ assert_equal 'foo', foo.name
+
+ foo2 = @top_level.classes.first.method_list[1]
+ assert_equal 'foo2', foo2.name
+ assert_equal 'foo', foo2.is_alias_for.name
+
+ foo3 = @top_level.classes.first.method_list[2]
+ assert_equal 'foo3', foo3.name
+ assert_equal 'foo', foo3.is_alias_for.name
+
+ foo4 = @top_level.classes.first.method_list.last
+ assert_equal 'foo4', foo4.name
+ assert_equal 'foo', foo4.is_alias_for.name
+
+ assert_equal 'unknown', @top_level.classes.first.external_aliases[0].old_name
+ end
+
+ def test_parse_statements_identifier_args
+ comment = "##\n# :args: x\n# :method: b\n# my method\n"
+
+ util_parser "module M\n#{comment}def_delegator :a, :b, :b\nend"
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL
+
+ m = @top_level.modules.first
+ assert_equal 'M', m.full_name
+
+ b = m.method_list.first
+ assert_equal 'M#b', b.full_name
+ assert_equal 'x', b.params
+ assert_equal 'my method', b.comment.text
+
+ assert_nil m.params, 'Module parameter not removed'
+ end
+
+ def test_parse_statements_identifier_constant
+ sixth_constant = <<-EOF
+Class.new do
+ rule :file do
+ all(x, y, z) {
+ def value
+ find(:require).each {|r| require r.value }
+ find(:grammar).map {|g| g.value }
+ end
+ def min; end
+ }
+ end
+end
+ EOF
+
+ content = <<-EOF
+class Foo
+ FIRST_CONSTANT = 5
+
+ SECOND_CONSTANT = [
+ 1,
+ 2,
+ 3
+ ]
+
+ THIRD_CONSTANT = {
+ :foo => 'bar',
+ :x => 'y'
+ }
+
+ FOURTH_CONSTANT = SECOND_CONSTANT.map do |element|
+ element + 1
+ element + 2
+ end
+
+ FIFTH_CONSTANT = SECOND_CONSTANT.map { |element| element + 1 }
+
+ SIXTH_CONSTANT = #{sixth_constant}
+
+ SEVENTH_CONSTANT = proc { |i| begin i end }
+end
+EOF
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ constants = @top_level.classes.first.constants
+
+ constant = constants[0]
+ assert_equal 'FIRST_CONSTANT', constant.name
+ assert_equal '5', constant.value
+ assert_equal @top_level, constant.file
+
+ constant = constants[1]
+ assert_equal 'SECOND_CONSTANT', constant.name
+ assert_equal "[\n1,\n2,\n3\n]", constant.value
+ assert_equal @top_level, constant.file
+
+ constant = constants[2]
+ assert_equal 'THIRD_CONSTANT', constant.name
+ assert_equal "{\n:foo => 'bar',\n:x => 'y'\n}", constant.value
+ assert_equal @top_level, constant.file
+
+ constant = constants[3]
+ assert_equal 'FOURTH_CONSTANT', constant.name
+ assert_equal "SECOND_CONSTANT.map do |element|\nelement + 1\nelement + 2\nend", constant.value
+ assert_equal @top_level, constant.file
+
+ constant = constants[4]
+ assert_equal 'FIFTH_CONSTANT', constant.name
+ assert_equal 'SECOND_CONSTANT.map { |element| element + 1 }', constant.value
+ assert_equal @top_level, constant.file
+
+ # TODO: parse as class
+ constant = constants[5]
+ assert_equal 'SIXTH_CONSTANT', constant.name
+ assert_equal sixth_constant.lines.map(&:strip).join("\n"), constant.value
+ assert_equal @top_level, constant.file
+
+ # TODO: parse as method
+ constant = constants[6]
+ assert_equal 'SEVENTH_CONSTANT', constant.name
+ assert_equal "proc { |i| begin i end }", constant.value
+ assert_equal @top_level, constant.file
+ end
+
+ def test_parse_statements_identifier_attr
+ content = "class Foo\nattr :foo\nend"
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.classes.first.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'R', foo.rw
+ end
+
+ def test_parse_statements_identifier_attr_accessor
+ content = "class Foo\nattr_accessor :foo\nend"
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.classes.first.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ end
+
+ def test_parse_statements_identifier_define_method
+ util_parser <<-RUBY
+class C
+ ##
+ # :method: a
+ define_method :a do end
+ ##
+ # :method: b
+ define_method :b do end
+end
+ RUBY
+
+ @parser.parse_statements @top_level
+ c = @top_level.classes.first
+
+ assert_equal %w[a b], c.method_list.map { |m| m.name }
+ end
+
+ def test_parse_statements_identifier_include
+ content = "class Foo\ninclude Bar\nend"
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.name
+ assert_equal 1, foo.includes.length
+ end
+
+ def test_parse_statements_identifier_module_function
+ content = "module Foo\ndef foo() end\nmodule_function :foo\nend"
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo, s_foo = @top_level.modules.first.method_list
+ assert_equal 'foo', foo.name, 'instance method name'
+ assert_equal :private, foo.visibility, 'instance method visibility'
+ assert_equal false, foo.singleton, 'instance method singleton'
+
+ assert_equal 'foo', s_foo.name, 'module function name'
+ assert_equal :public, s_foo.visibility, 'module function visibility'
+ assert_equal true, s_foo.singleton, 'module function singleton'
+ end
+
+ def test_parse_statements_identifier_private
+ content = "class Foo\nprivate\ndef foo() end\nend"
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.classes.first.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal :private, foo.visibility
+ end
+
+ def test_parse_statements_identifier_public_class_method
+ content = <<-CONTENT
+class Date
+ def self.now; end
+ private_class_method :now
+end
+
+class DateTime < Date
+ public_class_method :now
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ date, date_time = @top_level.classes.sort_by { |c| c.full_name }
+
+ date_now = date.method_list.first
+ date_time_now = date_time.method_list.sort_by { |m| m.full_name }.first
+
+ assert_equal :private, date_now.visibility
+ assert_equal :public, date_time_now.visibility
+ end
+
+ def test_parse_statements_identifier_private_class_method
+ content = <<-CONTENT
+class Date
+ def self.now; end
+ public_class_method :now
+end
+
+class DateTime < Date
+ private_class_method :now
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ # TODO sort classes by default
+ date, date_time = @top_level.classes.sort_by { |c| c.full_name }
+
+ date_now = date.method_list.first
+ date_time_now = date_time.method_list.sort_by { |m| m.full_name }.first
+
+ assert_equal :public, date_now.visibility, date_now.full_name
+ assert_equal :private, date_time_now.visibility, date_time_now.full_name
+ end
+
+ def test_parse_statements_identifier_require
+ content = "require 'bar'"
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ assert_equal 1, @top_level.requires.length
+ end
+
+ def test_parse_statements_identifier_yields
+ comment = "##\n# :yields: x\n# :method: b\n# my method\n"
+
+ util_parser "module M\n#{comment}def_delegator :a, :b, :b\nend"
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL
+
+ m = @top_level.modules.first
+ assert_equal 'M', m.full_name
+
+ b = m.method_list.first
+ assert_equal 'M#b', b.full_name
+ assert_equal 'x', b.block_params
+ assert_equal 'my method', b.comment.text
+
+ assert_nil m.params, 'Module parameter not removed'
+ end
+
+ def test_parse_statements_stopdoc_TkALIAS
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "\n# :stopdoc:\nalias old new"
+
+ @parser.parse_statements klass, RDoc::Parser::Ruby::NORMAL, nil
+
+ assert_empty klass.aliases
+ assert_empty klass.unmatched_alias_lists
+ end
+
+ def test_parse_statements_stopdoc_TkIDENTIFIER_alias_method
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "\n# :stopdoc:\nalias_method :old :new"
+
+ @parser.parse_statements klass, RDoc::Parser::Ruby::NORMAL, nil
+
+ assert_empty klass.aliases
+ assert_empty klass.unmatched_alias_lists
+ end
+
+ def test_parse_statements_stopdoc_TkIDENTIFIER_metaprogrammed
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "\n# :stopdoc:\n# attr :meta"
+
+ @parser.parse_statements klass, RDoc::Parser::Ruby::NORMAL, nil
+
+ assert_empty klass.method_list
+ assert_empty klass.attributes
+ end
+
+ def test_parse_statements_stopdoc_TkCONSTANT
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "\n# :stopdoc:\nA = v"
+
+ @parser.parse_statements klass, RDoc::Parser::Ruby::NORMAL, nil
+
+ assert_empty klass.constants
+ end
+
+ def test_parse_statements_stopdoc_TkDEF
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "\n# :stopdoc:\ndef m\n end"
+
+ @parser.parse_statements klass, RDoc::Parser::Ruby::NORMAL, nil
+
+ assert_empty klass.method_list
+ end
+
+ def test_parse_statements_super
+ m = RDoc::AnyMethod.new '', 'm'
+ util_parser 'super'
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, m
+
+ assert m.calls_super
+ end
+
+ def test_parse_statements_super_no_method
+ content = "super"
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ assert_nil @parser.get_tk
+ end
+
+ def test_parse_statements_while_begin
+ util_parser <<-RUBY
+class A
+ def a
+ while begin a; b end
+ end
+ end
+
+ def b
+ end
+end
+ RUBY
+
+ @parser.parse_statements @top_level
+
+ c_a = @top_level.classes.first
+ assert_equal 'A', c_a.full_name
+
+ assert_equal 1, @top_level.classes.length
+
+ m_a = c_a.method_list.first
+ m_b = c_a.method_list.last
+
+ assert_equal 'A#a', m_a.full_name
+ assert_equal 'A#b', m_b.full_name
+ end
+
+ def test_parse_symbol_in_arg
+ util_parser ':blah "blah" "#{blah}" blah'
+
+ assert_equal 'blah', @parser.parse_symbol_in_arg
+
+ @parser.skip_tkspace
+
+ assert_equal 'blah', @parser.parse_symbol_in_arg
+
+ @parser.skip_tkspace
+
+ assert_equal nil, @parser.parse_symbol_in_arg
+
+ @parser.skip_tkspace
+
+ assert_equal nil, @parser.parse_symbol_in_arg
+ end
+
+ def test_parse_statements_alias_method
+ content = <<-CONTENT
+class A
+ alias_method :a, :[] unless c
+end
+
+B = A
+
+class C
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.parse_statements @top_level
+
+ # HACK where are the assertions?
+ end
+
+ def test_parse_top_level_statements_enddoc
+ util_parser <<-CONTENT
+# :enddoc:
+ CONTENT
+
+ assert_throws :eof do
+ @parser.parse_top_level_statements @top_level
+ end
+ end
+
+ def test_parse_top_level_statements_stopdoc
+ @top_level.stop_doc
+ content = "# this is the top-level comment"
+
+ util_parser content
+
+ @parser.parse_top_level_statements @top_level
+
+ assert_empty @top_level.comment
+ end
+
+ def test_parse_top_level_statements_stopdoc_integration
+ content = <<-CONTENT
+# :stopdoc:
+
+class Example
+ def method_name
+ end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.parse_top_level_statements @top_level
+
+ assert_equal 1, @top_level.classes.length
+ assert_empty @top_level.modules
+
+ assert @top_level.find_module_named('Example').ignored?
+ end
+
+ # This tests parse_comment
+ def test_parse_top_level_statements_constant_nodoc_integration
+ content = <<-CONTENT
+class A
+ C = A # :nodoc:
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.parse_top_level_statements @top_level
+
+ klass = @top_level.find_module_named('A')
+
+ c = klass.constants.first
+
+ assert_nil c.document_self, 'C should not be documented'
+ end
+
+ def test_parse_yield_in_braces_with_parens
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo\nn.times { |i| yield nth(i) }\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, @comment
+
+ foo = klass.method_list.first
+ assert_equal 'nth(i)', foo.block_params
+ end
+
+ def test_read_directive
+ parser = util_parser '# :category: test'
+
+ directive, value = parser.read_directive %w[category]
+
+ assert_equal 'category', directive
+ assert_equal 'test', value
+
+ assert_kind_of RDoc::RubyToken::TkNL, parser.get_tk
+ end
+
+ def test_read_directive_allow
+ parser = util_parser '# :category: test'
+
+ directive = parser.read_directive []
+
+ assert_nil directive
+
+ assert_kind_of RDoc::RubyToken::TkNL, parser.get_tk
+ end
+
+ def test_read_directive_empty
+ parser = util_parser '# test'
+
+ directive = parser.read_directive %w[category]
+
+ assert_nil directive
+
+ assert_kind_of RDoc::RubyToken::TkNL, parser.get_tk
+ end
+
+ def test_read_directive_no_comment
+ parser = util_parser ''
+
+ directive = parser.read_directive %w[category]
+
+ assert_nil directive
+
+ assert_kind_of RDoc::RubyToken::TkNL, parser.get_tk
+ end
+
+ def test_read_directive_one_liner
+ parser = util_parser '; end # :category: test'
+
+ directive, value = parser.read_directive %w[category]
+
+ assert_equal 'category', directive
+ assert_equal 'test', value
+
+ assert_kind_of RDoc::RubyToken::TkSEMICOLON, parser.get_tk
+ end
+
+ def test_read_documentation_modifiers
+ c = RDoc::Context.new
+
+ parser = util_parser '# :category: test'
+
+ parser.read_documentation_modifiers c, %w[category]
+
+ assert_equal 'test', c.current_section.title
+ end
+
+ def test_read_documentation_modifiers_notnew
+ m = RDoc::AnyMethod.new nil, 'initialize'
+
+ parser = util_parser '# :notnew: test'
+
+ parser.read_documentation_modifiers m, %w[notnew]
+
+ assert m.dont_rename_initialize
+ end
+
+ def test_read_documentation_modifiers_not_dash_new
+ m = RDoc::AnyMethod.new nil, 'initialize'
+
+ parser = util_parser '# :not-new: test'
+
+ parser.read_documentation_modifiers m, %w[not-new]
+
+ assert m.dont_rename_initialize
+ end
+
+ def test_read_documentation_modifiers_not_new
+ m = RDoc::AnyMethod.new nil, 'initialize'
+
+ parser = util_parser '# :not_new: test'
+
+ parser.read_documentation_modifiers m, %w[not_new]
+
+ assert m.dont_rename_initialize
+ end
+
+ def test_sanity_integer
+ util_parser '1'
+ assert_equal '1', @parser.get_tk.text
+
+ util_parser '1.0'
+ assert_equal '1.0', @parser.get_tk.text
+ end
+
+ def test_sanity_interpolation
+ last_tk = nil
+ util_parser 'class A; B = "#{c}"; end'
+
+ while tk = @parser.get_tk do last_tk = tk end
+
+ assert_equal "\n", last_tk.text
+ end
+
+ # If you're writing code like this you're doing it wrong
+
+ def test_sanity_interpolation_crazy
+ util_parser '"#{"#{"a")}" if b}"'
+
+ assert_equal '"#{"#{"a")}" if b}"', @parser.get_tk.text
+ assert_equal RDoc::RubyToken::TkNL, @parser.get_tk.class
+ end
+
+ def test_sanity_interpolation_curly
+ util_parser '%{ #{} }'
+
+ assert_equal '%Q{ #{} }', @parser.get_tk.text
+ assert_equal RDoc::RubyToken::TkNL, @parser.get_tk.class
+ end
+
+ def test_sanity_interpolation_format
+ util_parser '"#{stftime("%m-%d")}"'
+
+ while @parser.get_tk do end
+ end
+
+ def test_sanity_symbol_interpolation
+ util_parser ':"#{bar}="'
+
+ while @parser.get_tk do end
+ end
+
+ def test_scan_cr
+ content = <<-CONTENT
+class C\r
+ def m\r
+ a=\\\r
+ 123\r
+ end\r
+end\r
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ c = @top_level.classes.first
+
+ assert_equal 1, c.method_list.length
+ end
+
+ def test_scan_block_comment
+ content = <<-CONTENT
+=begin rdoc
+Foo comment
+=end
+
+class Foo
+
+=begin
+m comment
+=end
+
+ def m() end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+
+ assert_equal 'Foo comment', foo.comment.text
+
+ m = foo.method_list.first
+
+ assert_equal 'm comment', m.comment.text
+ end
+
+ def test_scan_block_comment_nested # Issue #41
+ content = <<-CONTENT
+require 'something'
+=begin rdoc
+findmeindoc
+=end
+module Foo
+ class Bar
+ end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ foo = @top_level.modules.first
+
+ assert_equal 'Foo', foo.full_name
+ assert_equal 'findmeindoc', foo.comment.text
+
+ bar = foo.classes.first
+
+ assert_equal 'Foo::Bar', bar.full_name
+ assert_equal '', bar.comment.text
+ end
+
+ def test_scan_block_comment_notflush
+ ##
+ #
+ # The previous test assumes that between the =begin/=end blocs that there is
+ # only one line, or minima formatting directives. This test tests for those
+ # who use the =begin bloc with longer / more advanced formatting within.
+ #
+ ##
+ content = <<-CONTENT
+=begin rdoc
+
+= DESCRIPTION
+
+This is a simple test class
+
+= RUMPUS
+
+Is a silly word
+
+=end
+class StevenSimpleClass
+ # A band on my iPhone as I wrote this test
+ FRUIT_BATS="Make nice music"
+
+=begin rdoc
+A nice girl
+=end
+
+ def lauren
+ puts "Summoning Lauren!"
+ end
+end
+ CONTENT
+ util_parser content
+
+ @parser.scan
+
+ foo = @top_level.classes.first
+
+ assert_equal "= DESCRIPTION\n\nThis is a simple test class\n\n= RUMPUS\n\nIs a silly word",
+ foo.comment.text
+
+ m = foo.method_list.first
+
+ assert_equal 'A nice girl', m.comment.text
+ end
+
+ def test_scan_class_nested_nodoc
+ content = <<-CONTENT
+class A::B # :nodoc:
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ visible = @store.all_classes_and_modules.select { |mod| mod.display? }
+
+ assert_empty visible.map { |mod| mod.full_name }
+ end
+
+ def test_scan_constant_in_method
+ content = <<-CONTENT # newline is after M is important
+module M
+ def m
+ A
+ B::C
+ end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ m = @top_level.modules.first
+
+ assert_empty m.constants
+
+ assert_empty @store.classes_hash.keys
+ assert_equal %w[M], @store.modules_hash.keys
+ end
+
+ def test_scan_constant_in_rescue
+ content = <<-CONTENT # newline is after M is important
+module M
+ def m
+ rescue A::B
+ rescue A::C => e
+ rescue A::D, A::E
+ rescue A::F,
+ A::G
+ rescue H
+ rescue I => e
+ rescue J, K
+ rescue L =>
+ e
+ rescue M;
+ rescue N,
+ O => e
+ end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ m = @top_level.modules.first
+
+ assert_empty m.constants
+
+ assert_empty @store.classes_hash.keys
+ assert_equal %w[M], @store.modules_hash.keys
+ end
+
+ def test_scan_constant_nodoc
+ content = <<-CONTENT # newline is after M is important
+module M
+
+ C = v # :nodoc:
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ c = @top_level.modules.first.constants.first
+
+ assert c.documented?
+ end
+
+ def test_scan_constant_nodoc_block
+ content = <<-CONTENT # newline is after M is important
+module M
+
+ C = v do # :nodoc:
+ end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ c = @top_level.modules.first.constants.first
+
+ assert c.documented?
+ end
+
+ def test_scan_duplicate_module
+ content = <<-CONTENT
+# comment a
+module Foo
+end
+
+# comment b
+module Foo
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ foo = @top_level.modules.first
+
+ expected = [
+ RDoc::Comment.new('comment b', @top_level)
+ ]
+
+ assert_equal expected, foo.comment_location.map { |c, l| c }
+ end
+
+ def test_scan_meta_method_block
+ content = <<-CONTENT
+class C
+
+ ##
+ # my method
+
+ inline(:my_method) do |*args|
+ "this method used to cause z to disappear"
+ end
+
+ def z
+ end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ assert_equal 2, @top_level.classes.first.method_list.length
+ end
+
+ def test_scan_method_semi_method
+ content = <<-CONTENT
+class A
+ def self.m() end; def self.m=() end
+end
+
+class B
+ def self.m() end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ a = @store.find_class_named 'A'
+ assert a, 'missing A'
+
+ assert_equal 2, a.method_list.length
+
+ b = @store.find_class_named 'B'
+ assert b, 'missing B'
+
+ assert_equal 1, b.method_list.length
+ end
+
+ def test_scan_markup_override
+ content = <<-CONTENT
+# *awesome*
+class C
+ # :markup: rd
+ # ((*radical*))
+ def m
+ end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ c = @top_level.classes.first
+
+ assert_equal 'rdoc', c.comment.format
+
+ assert_equal 'rd', c.method_list.first.comment.format
+ end
+
+ def test_scan_markup_first_comment
+ content = <<-CONTENT
+# :markup: rd
+
+# ((*awesome*))
+class C
+ # ((*radical*))
+ def m
+ end
+end
+ CONTENT
+
+ util_parser content
+
+ @parser.scan
+
+ c = @top_level.classes.first
+
+ assert_equal 'rd', c.comment.format
+
+ assert_equal 'rd', c.method_list.first.comment.format
+ end
+
+ def test_scan_rails_routes
+ util_parser <<-ROUTES_RB
+namespace :api do
+ scope module: :v1 do
+ end
+end
+ ROUTES_RB
+
+ @parser.scan
+
+ assert_empty @top_level.classes
+ assert_empty @top_level.modules
+ end
+
+ def test_scan_tomdoc_meta
+ util_parser <<-RUBY
+# :markup: tomdoc
+
+class C
+
+ # Signature
+ #
+ # find_by_<field>[_and_<field>...](args)
+ #
+ # field - A field name.
+
+end
+
+ RUBY
+
+ @parser.scan
+
+ c = @top_level.classes.first
+
+ m = c.method_list.first
+
+ assert_equal "find_by_<field>[_and_<field>...]", m.name
+ assert_equal "find_by_<field>[_and_<field>...](args)\n", m.call_seq
+
+ expected =
+ doc(
+ head(3, 'Signature'),
+ list(:NOTE,
+ item(%w[field],
+ para('A field name.'))))
+ expected.file = @top_level
+
+ assert_equal expected, m.comment.parse
+ end
+
+ def test_scan_stopdoc
+ util_parser <<-RUBY
+class C
+ # :stopdoc:
+ class Hidden
+ end
+end
+ RUBY
+
+ @parser.scan
+
+ c = @top_level.classes.first
+
+ hidden = c.classes.first
+
+ refute hidden.document_self
+ assert hidden.ignored?
+ end
+
+ def test_scan_stopdoc_class_alias
+ util_parser <<-RUBY
+# :stopdoc:
+module A
+ B = C
+end
+ RUBY
+
+ @parser.scan
+
+ assert_empty @store.all_classes
+
+ assert_equal 1, @store.all_modules.length
+ m = @store.all_modules.first
+
+ assert m.ignored?
+ end
+
+ def test_scan_stopdoc_nested
+ util_parser <<-RUBY
+# :stopdoc:
+class A::B
+end
+ RUBY
+
+ @parser.scan
+
+ a = @store.modules_hash['A']
+ a_b = @store.classes_hash['A::B']
+
+ refute a.document_self, 'A is inside stopdoc'
+ assert a.ignored?, 'A is inside stopdoc'
+
+ refute a_b.document_self, 'A::B is inside stopdoc'
+ assert a_b.ignored?, 'A::B is inside stopdoc'
+ end
+
+ def test_scan_struct_self_brackets
+ util_parser <<-RUBY
+class C < M.m
+ def self.[]
+ end
+end
+ RUBY
+
+ @parser.scan
+
+ c = @store.find_class_named 'C'
+ assert_equal %w[C::[]], c.method_list.map { |m| m.full_name }
+ end
+
+ def test_scan_visibility
+ util_parser <<-RUBY
+class C
+ def a() end
+
+ private :a
+
+ class << self
+ def b() end
+ private :b
+ end
+end
+ RUBY
+
+ @parser.scan
+
+ c = @store.find_class_named 'C'
+
+ c_a = c.find_method_named 'a'
+
+ assert_equal :private, c_a.visibility
+ refute c_a.singleton
+
+ c_b = c.find_method_named 'b'
+
+ assert_equal :private, c_b.visibility
+ assert c_b.singleton
+ end
+
+ def test_singleton_method_via_eigenclass
+ util_parser <<-RUBY
+class C
+ class << self
+ def a() end
+ end
+end
+ RUBY
+
+ @parser.scan
+
+ c = @store.find_class_named 'C'
+ c_a = c.find_method_named 'a'
+
+ assert_equal :public, c_a.visibility
+ assert c_a.singleton
+ end
+
+ def test_stopdoc_after_comment
+ util_parser <<-EOS
+ module Bar
+ # hello
+ module Foo
+ # :stopdoc:
+ end
+ # there
+ class Baz
+ # :stopdoc:
+ end
+ end
+ EOS
+
+ @parser.parse_statements @top_level
+
+ foo = @top_level.modules.first.modules.first
+ assert_equal 'Foo', foo.name
+ assert_equal 'hello', foo.comment.text
+
+ baz = @top_level.modules.first.classes.first
+ assert_equal 'Baz', baz.name
+ assert_equal 'there', baz.comment.text
+ end
+
+ def tk klass, scan, line, char, name, text
+ klass = RDoc::RubyToken.const_get "Tk#{klass.to_s.upcase}"
+
+ token = if klass.instance_method(:initialize).arity == 3 then
+ raise ArgumentError, "name not used for #{klass}" if name
+ klass.new scan, line, char
+ else
+ klass.new scan, line, char, name
+ end
+
+ token.set_text text
+
+ token
+ end
+
+ def util_parser(content)
+ @parser = RDoc::Parser::Ruby.new @top_level, @filename, content, @options,
+ @stats
+ end
+
+ def util_two_parsers(first_file_content, second_file_content)
+ util_parser first_file_content
+
+ @parser2 = RDoc::Parser::Ruby.new @top_level2, @filename,
+ second_file_content, @options, @stats
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_parser_simple.rb b/jni/ruby/test/rdoc/test_rdoc_parser_simple.rb
new file mode 100644
index 0000000..d45a993
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_parser_simple.rb
@@ -0,0 +1,115 @@
+require 'rdoc/test_case'
+
+class TestRDocParserSimple < RDoc::TestCase
+
+ def setup
+ super
+
+ @tempfile = Tempfile.new self.class.name
+ filename = @tempfile.path
+
+ @top_level = @store.add_file filename
+ @fn = filename
+ @options = RDoc::Options.new
+ @stats = RDoc::Stats.new @store, 0
+ end
+
+ def teardown
+ super
+
+ @tempfile.close!
+ end
+
+ def test_initialize_metadata
+ parser = util_parser ":unhandled: \n"
+
+ assert_includes @top_level.metadata, 'unhandled'
+
+ assert_equal ":unhandled: \n", parser.content
+ end
+
+ def test_remove_coding_comment
+ parser = util_parser <<-TEXT
+# -*- mode: rdoc; coding: utf-8; fill-column: 74; -*-
+
+Regular expressions (<i>regexp</i>s) are patterns which describe the
+contents of a string.
+ TEXT
+
+ parser.scan
+
+ expected = <<-TEXT.strip
+Regular expressions (<i>regexp</i>s) are patterns which describe the
+contents of a string.
+ TEXT
+
+ assert_equal expected, @top_level.comment.text
+ end
+
+ # RDoc stops processing comments if it finds a comment line CONTAINING
+ # '<tt>#--</tt>'. This can be used to separate external from internal
+ # comments, or to stop a comment being associated with a method,
+ # class, or module. Commenting CAN be turned back on with
+ # a line that STARTS '<tt>#++</tt>'.
+ #
+ # I've seen guys that comment their code like this:
+ # # This method....
+ # #-----------------
+ # def method
+ #
+ # => either we do it only in ruby code, or we require the leading #
+ # (to avoid conflict with rules).
+ #
+ # TODO: require the leading #, to provide the feature in simple text files.
+ # Note: in ruby & C code, we require '#--' & '#++' or '*--' & '*++',
+ # to allow rules:
+ #
+ # # this is a comment
+ # #---
+ # # private text
+ # #+++
+ # # this is a rule:
+ # # ---
+
+ def test_remove_private_comments
+ parser = util_parser "foo\n\n--\nbar\n++\n\nbaz\n"
+
+ parser.scan
+
+ expected = "foo\n\n\nbaz"
+
+ assert_equal expected, @top_level.comment.text
+ end
+
+ def test_remove_private_comments_rule
+ parser = util_parser "foo\n---\nbar"
+
+ parser.scan
+
+ expected = "foo\n---\nbar"
+
+ assert_equal expected, @top_level.comment.text
+ end
+
+ def test_remove_private_comments_star
+ parser = util_parser "* foo\n* bar\n"
+
+ parser.scan
+
+ assert_equal "* foo\n* bar", @top_level.comment.text
+ end
+
+ def test_scan
+ parser = util_parser 'it *really* works'
+
+ parser.scan
+
+ assert_equal 'it *really* works', @top_level.comment.text
+ end
+
+ def util_parser(content)
+ RDoc::Parser::Simple.new @top_level, @fn, content, @options, @stats
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_rd.rb b/jni/ruby/test/rdoc/test_rdoc_rd.rb
new file mode 100644
index 0000000..d917a63
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_rd.rb
@@ -0,0 +1,30 @@
+require 'rdoc/test_case'
+
+class TestRDocRd < RDoc::TestCase
+
+ def test_class_parse
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('hello'))
+
+ assert_equal expected, RDoc::RD.parse("hello")
+ end
+
+ def test_class_parse_begin_end
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('hello'))
+
+ assert_equal expected, RDoc::RD.parse("=begin\nhello\n=end\n")
+ end
+
+ def test_class_parse_newline
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('hello'))
+
+ assert_equal expected, RDoc::RD.parse("hello\n")
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_rd_block_parser.rb b/jni/ruby/test/rdoc/test_rdoc_rd_block_parser.rb
new file mode 100644
index 0000000..956f3d2
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_rd_block_parser.rb
@@ -0,0 +1,535 @@
+require 'rdoc/test_case'
+
+class TestRDocRdBlockParser < RDoc::TestCase
+
+ def setup
+ super
+
+ @block_parser = RDoc::RD::BlockParser.new
+ end
+
+ def test_add_footnote
+ index = @block_parser.add_footnote 'context'
+
+ assert_equal 1, index
+
+ expected = [
+ para('{^1}[rdoc-label:footmark-1:foottext-1]', ' ', 'context'),
+ blank_line,
+ ]
+
+ assert_equal expected, @block_parser.footnotes
+
+ index = @block_parser.add_footnote 'other'
+
+ assert_equal 2, index
+ end
+
+ def test_parse_desclist
+ list = <<-LIST
+:one
+ desc one
+:two
+ desc two
+ LIST
+
+ expected =
+ doc(
+ list(:NOTE,
+ item("one", para("desc one")),
+ item("two", para("desc two"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_enumlist
+ list = <<-LIST
+(1) one
+(1) two
+ LIST
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil, para("one")),
+ item(nil, para("two"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_enumlist_paragraphs
+ list = <<-LIST
+(1) one
+
+ two
+ LIST
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil,
+ para("one"),
+ para("two"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_enumlist_multiline
+ list = <<-LIST
+(1) one
+ two
+ LIST
+
+ contents = "one\n two" # 1.8 vs 1.9
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil, para(*contents))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_enumlist_verbatim
+ list = <<-LIST
+(1) item
+ verbatim
+ LIST
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil,
+ para("item"),
+ verb("verbatim\n"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_enumlist_verbatim_continue
+ list = <<-LIST
+(1) one
+ verbatim
+ two
+ LIST
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil,
+ para("one"),
+ verb("verbatim\n"),
+ para("two"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_footnote
+ expected =
+ doc(
+ para("{*1}[rdoc-label:foottext-1:footmark-1]"),
+ rule(1),
+ para("{^1}[rdoc-label:footmark-1:foottext-1]", " ", "text"),
+ blank_line)
+
+ assert_equal expected, parse("((-text-))")
+ end
+
+ def test_parse_include
+ @block_parser.include_path = [Dir.tmpdir]
+
+ expected = doc(@RM::Include.new("parse_include", [Dir.tmpdir]))
+
+ assert_equal expected, parse("<<< parse_include")
+ end
+
+ def test_parse_include_subtree
+ @block_parser.include_path = [Dir.tmpdir]
+
+ expected =
+ doc(
+ blank_line,
+ para("include <em>worked</em>"),
+ blank_line,
+ blank_line)
+
+ tf = Tempfile.open %w[parse_include .rd] do |io|
+ io.puts "=begin\ninclude ((*worked*))\n=end"
+ io.flush
+
+ str = <<-STR
+<<< #{File.basename io.path}
+ STR
+
+ assert_equal expected, parse(str)
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+ end
+
+ def test_parse_heading
+ assert_equal doc(head(1, "H")), parse("= H")
+ assert_equal doc(head(2, "H")), parse("== H")
+ assert_equal doc(head(3, "H")), parse("=== H")
+ assert_equal doc(head(4, "H")), parse("==== H")
+ assert_equal doc(head(5, "H")), parse("+ H")
+ assert_equal doc(head(6, "H")), parse("++ H")
+ end
+
+ def test_parse_itemlist
+ list = <<-LIST
+* one
+* two
+ LIST
+
+ expected =
+ doc(
+ list(:BULLET,
+ item(nil, para("one")),
+ item(nil, para("two"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_itemlist_multiline
+ list = <<-LIST
+* one
+ two
+ LIST
+
+ contents = "one\n two" # 1.8 vs 1.9
+
+ expected =
+ doc(
+ list(:BULLET,
+ item(nil, para(*contents))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_itemlist_nest
+ list = <<-LIST
+* one
+ * inner
+* two
+ LIST
+
+ expected =
+ doc(
+ list(:BULLET,
+ item(nil,
+ para("one"),
+ list(:BULLET,
+ item(nil, para("inner")))),
+ item(nil,
+ para("two"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_itemlist_paragraphs
+ list = <<-LIST
+* one
+
+ two
+ LIST
+
+ expected =
+ doc(
+ list(:BULLET,
+ item(nil,
+ para("one"),
+ para("two"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_itemlist_verbatim
+ list = <<-LIST
+* item
+ verbatim
+ LIST
+
+ expected =
+ doc(
+ list(:BULLET,
+ item(nil,
+ para("item"),
+ verb("verbatim\n"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_itemlist_verbatim_continue
+ list = <<-LIST
+* one
+ verbatim
+ two
+ LIST
+
+ expected =
+ doc(
+ list(:BULLET,
+ item(nil,
+ para("one"),
+ verb("verbatim\n"),
+ para("two"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_lists
+ list = <<-LIST
+(1) one
+(1) two
+* three
+* four
+(1) five
+(1) six
+ LIST
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil, para("one")),
+ item(nil, para("two"))),
+ list(:BULLET,
+ item(nil, para("three")),
+ item(nil, para("four"))),
+ list(:NUMBER,
+ item(nil, para("five")),
+ item(nil, para("six"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_lists_nest
+ list = <<-LIST
+(1) one
+(1) two
+ * three
+ * four
+(1) five
+(1) six
+ LIST
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil, para("one")),
+ item(nil,
+ para("two"),
+ list(:BULLET,
+ item(nil, para("three")),
+ item(nil, para("four")))),
+ item(nil, para("five")),
+ item(nil, para("six"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_lists_nest_verbatim
+ list = <<-LIST
+(1) one
+(1) two
+ * three
+ * four
+ verbatim
+(1) five
+(1) six
+ LIST
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil, para("one")),
+ item(nil,
+ para("two"),
+ list(:BULLET,
+ item(nil, para("three")),
+ item(nil, para("four"))),
+ verb("verbatim\n")),
+ item(nil, para("five")),
+ item(nil, para("six"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_lists_nest_verbatim2
+ list = <<-LIST
+(1) one
+(1) two
+ * three
+ * four
+ verbatim
+(1) five
+(1) six
+ LIST
+
+ expected =
+ doc(
+ list(:NUMBER,
+ item(nil, para("one")),
+ item(nil,
+ para("two"),
+ list(:BULLET,
+ item(nil, para("three")),
+ item(nil, para("four"))),
+ verb("verbatim\n")),
+ item(nil, para("five")),
+ item(nil, para("six"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_methodlist
+ list = <<-LIST
+--- Array#each {|i| ... }
+ yield block for each item.
+--- Array#index(val)
+ return index of first item which equals with val. if it hasn't
+ same item, return nil.
+ LIST
+
+ expected =
+ doc(
+ list(:LABEL,
+ item(
+ "<tt>Array#each {|i| ... }</tt>",
+ para("yield block for each item.")),
+ item(
+ "<tt>Array#index(val)</tt>",
+ para("return index of first item which equals with val. if it hasn't same item, return nil."))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_methodlist_empty
+ list = <<-LIST
+--- A#b
+
+ LIST
+
+ expected =
+ doc(
+ list(:LABEL,
+ item("<tt>A#b</tt>")))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_methodlist_paragraph
+ list = <<-LIST
+--- A#b
+
+ one
+ LIST
+
+ expected =
+ doc(
+ list(:LABEL,
+ item(
+ "<tt>A#b</tt>",
+ para("one"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_methodlist_paragraph2
+ list = <<-LIST.chomp
+--- A#b
+
+ one
+two
+ LIST
+
+ expected =
+ doc(
+ list(:LABEL,
+ item(
+ "<tt>A#b</tt>",
+ para("one"))),
+ para("two"))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_methodlist_paragraph_verbatim
+ list = <<-LIST.chomp
+--- A#b
+
+ text
+ verbatim
+ LIST
+
+ expected =
+ doc(
+ list(:LABEL,
+ item(
+ "<tt>A#b</tt>",
+ para("text"),
+ verb("verbatim\n"))))
+
+ assert_equal expected, parse(list)
+ end
+
+ def test_parse_verbatim
+ assert_equal doc(verb("verbatim\n")), parse(" verbatim")
+ end
+
+ def test_parse_verbatim_blankline
+ expected = doc(verb("one\n", "\n", "two\n"))
+
+ verbatim = <<-VERBATIM
+ one
+
+ two
+ VERBATIM
+
+ assert_equal expected, parse(verbatim)
+ end
+
+ def test_parse_verbatim_indent
+ expected = doc(verb("one\n", " two\n"))
+
+ verbatim = <<-VERBATIM
+ one
+ two
+ VERBATIM
+
+ assert_equal expected, parse(verbatim)
+ end
+
+ def test_parse_verbatim_multi
+ expected = doc(verb("one\n", "two\n"))
+
+ verbatim = <<-VERBATIM
+ one
+ two
+ VERBATIM
+
+ assert_equal expected, parse(verbatim)
+ end
+
+ def test_parse_textblock
+ assert_equal doc(para("text")), parse("text")
+ end
+
+ def test_parse_textblock_multi
+ expected = doc(para("one two"))
+
+ assert_equal expected, parse("one\ntwo")
+ end
+
+ def parse text
+ text = ["=begin", text, "=end"].join "\n"
+
+ doc = @block_parser.parse text.lines.to_a
+
+ assert_equal blank_line, doc.parts.shift, "=begin blankline"
+ assert_equal blank_line, doc.parts.pop, "=end blankline"
+
+ doc
+ end
+
+end
diff --git a/jni/ruby/test/rdoc/test_rdoc_rd_inline.rb b/jni/ruby/test/rdoc/test_rdoc_rd_inline.rb
new file mode 100644
index 0000000..d601ecc
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_rd_inline.rb
@@ -0,0 +1,63 @@
+require 'rdoc/test_case'
+
+class TestRDocRdInline < RDoc::TestCase
+
+ def setup
+ super
+
+ @inline = RDoc::RD::Inline.new '+text+', 'text'
+ end
+
+ def test_class_new
+ inline = RDoc::RD::Inline.new @inline
+
+ refute_equal inline.rdoc, inline.reference
+ end
+
+ def test_initialize
+ inline = RDoc::RD::Inline.new 'text'
+
+ assert_equal inline.rdoc, inline.reference
+ refute_same inline.rdoc, inline.reference
+ end
+
+ def test_initialize_inline
+ inline = RDoc::RD::Inline.new @inline
+
+ assert_equal '+text+', inline.rdoc
+ assert_equal 'text', inline.reference
+ end
+
+ def test_append_inline
+ out = @inline.append @inline
+
+ assert_same @inline, out
+
+ assert_equal '+text++text+', @inline.rdoc
+ assert_equal 'texttext', @inline.reference
+ end
+
+ def test_append_string
+ @inline.append ' more'
+
+ assert_equal '+text+ more', @inline.rdoc
+ assert_equal 'text more', @inline.reference
+ end
+
+ def test_equals2
+ assert_equal @inline, RDoc::RD::Inline.new('+text+', 'text')
+ refute_equal @inline, RDoc::RD::Inline.new('+text+', 'other')
+ refute_equal @inline, RDoc::RD::Inline.new('+other+', 'text')
+ refute_equal @inline, Object.new
+ end
+
+ def test_inspect
+ assert_equal '(inline: +text+)', @inline.inspect
+ end
+
+ def test_to_s
+ assert_equal '+text+', @inline.to_s
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_rd_inline_parser.rb b/jni/ruby/test/rdoc/test_rdoc_rd_inline_parser.rb
new file mode 100644
index 0000000..e4a765b
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_rd_inline_parser.rb
@@ -0,0 +1,177 @@
+require 'rdoc/test_case'
+
+class TestRDocRdInlineParser < RDoc::TestCase
+
+ def setup
+ super
+
+ @block_parser = RDoc::RD::BlockParser.new
+ @block_parser.instance_variable_set :@i, 0
+ @inline_parser = RDoc::RD::InlineParser.new @block_parser
+ end
+
+ def test_parse
+ assert_equal 'regular <em>emphasis</em>', parse('regular ((*emphasis*))')
+ end
+
+ def test_parse_code
+ assert_equal '<code>text</code>', parse('(({text}))')
+ end
+
+ def test_parse_em
+ assert_equal '<em>text</em>', parse('((*text*))')
+ end
+
+ def test_parse_footnote
+ assert_equal '{*1}[rdoc-label:foottext-1:footmark-1]', parse('((-text-))')
+
+ expected = [
+ @RM::Paragraph.new('{^1}[rdoc-label:footmark-1:foottext-1]', ' ', 'text'),
+ blank_line,
+ ]
+
+ assert_equal expected, @block_parser.footnotes
+ end
+
+ def test_parse_index
+ assert_equal '<span id="label-text">text</span>', parse('((:text:))')
+
+ assert_includes @block_parser.labels, 'text'
+ end
+
+ def test_parse_kbd
+ assert_equal '<tt>text</tt>', parse('((%text%))')
+ end
+
+ def test_parse_multiple
+ assert_equal '<em>one</em> <em>two</em>', parse('((*one*)) ((*two*))')
+ end
+
+ def test_parse_newline
+ assert_equal "one\ntwo", parse("one\ntwo")
+ end
+
+ def test_parse_quote
+ assert_equal 'one " two', parse('one " two')
+ end
+
+ def test_parse_ref
+ assert_equal '{text}[rdoc-label:text]', parse('((<text>))')
+ end
+
+ def test_parse_ref_em
+ assert_equal '{<em>text</em>}[rdoc-label:text]', parse('((<((*text*))>))')
+ end
+
+ def test_parse_ref_filename_quote
+ assert_equal '{RD/foo}[rdoc-label:RD/foo]', parse('((<RD/"foo">))')
+ end
+
+ def test_parse_ref_filename
+ assert_equal '{RD}[rdoc-label:RD/]', parse('((<RD/>))')
+ end
+
+ def test_parse_ref_quote
+ assert_equal '{text \\"}[rdoc-label:text \\"]', parse('((<text \">))')
+ end
+
+ def test_parse_ref_quote_content
+ assert_equal '{<em>text</em>}[rdoc-label:text]',
+ parse('((<"((*text*))">))')
+ end
+
+ def test_parse_ref_quote_content_multi
+ assert_equal '{<em>one</em> <em>two</em>}[rdoc-label:one two]',
+ parse('((<"((*one*)) ((*two*))">))')
+ end
+
+ def test_parse_ref_substitute
+ assert_equal '{text}[rdoc-label:thing]', parse('((<text|thing>))')
+ end
+
+ def test_parse_ref_substitute_element_quote
+ assert_equal '{text}[rdoc-label:"RD"]',
+ parse('((<text|"RD">))')
+ end
+
+ def test_parse_ref_substitute_filename
+ assert_equal '{text}[rdoc-label:RD/]', parse('((<text|RD/>))')
+ end
+
+ def test_parse_ref_substitute_filename_label
+ assert_equal '{text}[rdoc-label:RD/label]',
+ parse('((<text|RD/label>))')
+ end
+
+ def test_parse_ref_substitute_filename_quote
+ assert_equal '{text}[rdoc-label:"RD"/]', parse('((<text|"RD"/>))')
+ end
+
+ def test_parse_ref_substitute_multi_content
+ assert_equal '{<em>one</em> two}[rdoc-label:thing]',
+ parse('((<((*one*)) two|thing>))')
+ end
+
+ def test_parse_ref_substitute_multi_content2
+ assert_equal '{<em>one</em> \\" two}[rdoc-label:thing]',
+ parse('((<((*one*)) \" two|thing>))')
+ end
+
+ def test_parse_ref_substitute_multi_content3
+ assert_equal '{<em>one</em> \\" <em>two</em>}[rdoc-label:thing]',
+ parse('((<((*one*)) \" ((*two*))|thing>))')
+ end
+
+ def test_parse_ref_substitute_quote
+ assert_equal '{one | two}[rdoc-label:thing]',
+ parse('((<"one | two"|thing>))')
+ end
+
+ def test_parse_ref_substitute_quote_content
+ assert_equal '{<em>text</em>}[rdoc-label:thing]',
+ parse('((<"((*text*))"|thing>))')
+ end
+
+ def test_parse_ref_substitute_url
+ assert_equal '{text}[http://example]',
+ parse('((<text|URL:http://example>))')
+ end
+
+ def test_parse_ref_url
+ assert_equal '{http://example}[http://example]',
+ parse('((<URL:http://example>))')
+ end
+
+ def test_parse_var
+ assert_equal '+text+', parse('((|text|))')
+ end
+
+ def test_parse_verb
+ assert_equal '<tt>text</tt>', parse("(('text'))")
+ end
+
+ def test_parse_verb_backslash
+ assert_equal "<tt>(('text'))</tt>", parse("(('(('text\\'))'))")
+ end
+
+ def test_parse_verb_backslash_backslash
+ assert_equal '<tt>text \\</tt>', parse("(('text \\\\'))")
+ end
+
+ def test_parse_verb_backslash_quote
+ assert_equal '<tt>text "</tt>', parse("(('text \\\"'))")
+ end
+
+ def test_parse_verb_emphasis
+ assert_equal '<tt>((*emphasis*))</tt>', parse("(('((*emphasis*))'))")
+ end
+
+ def test_parse_verb_multiple
+ assert_equal '<tt>((*text*))</tt>', parse("(('((*text*))'))")
+ end
+
+ def parse text
+ @inline_parser.parse text
+ end
+
+end
diff --git a/jni/ruby/test/rdoc/test_rdoc_rdoc.rb b/jni/ruby/test/rdoc/test_rdoc_rdoc.rb
new file mode 100644
index 0000000..230c18f
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_rdoc.rb
@@ -0,0 +1,455 @@
+require 'rdoc/test_case'
+
+class TestRDocRDoc < RDoc::TestCase
+
+ def setup
+ super
+
+ @rdoc = RDoc::RDoc.new
+ @rdoc.options = RDoc::Options.new
+
+ @stats = RDoc::Stats.new @store, 0, 0
+ @rdoc.instance_variable_set :@stats, @stats
+ end
+
+ def test_document # functional test
+ options = RDoc::Options.new
+ options.files = [File.expand_path('../xref_data.rb', __FILE__)]
+ options.setup_generator 'ri'
+ options.main_page = 'MAIN_PAGE.rdoc'
+ options.root = Pathname File.expand_path('..', __FILE__)
+ options.title = 'title'
+
+ rdoc = RDoc::RDoc.new
+
+ temp_dir do
+ options.op_dir = 'ri'
+
+ capture_io do
+ rdoc.document options
+ end
+
+ assert File.directory? 'ri'
+ assert_equal rdoc, rdoc.store.rdoc
+ end
+
+ store = rdoc.store
+
+ assert_equal 'MAIN_PAGE.rdoc', store.main
+ assert_equal 'title', store.title
+ end
+
+ def test_gather_files
+ a = File.expand_path __FILE__
+ b = File.expand_path '../test_rdoc_text.rb', __FILE__
+
+ assert_equal [a, b], @rdoc.gather_files([b, a, b])
+ end
+
+ def test_handle_pipe
+ $stdin = StringIO.new "hello"
+
+ out, = capture_io do
+ @rdoc.handle_pipe
+ end
+
+ assert_equal "\n<p>hello</p>\n", out
+ ensure
+ $stdin = STDIN
+ end
+
+ def test_handle_pipe_rd
+ $stdin = StringIO.new "=begin\nhello\n=end"
+
+ @rdoc.options.markup = 'rd'
+
+ out, = capture_io do
+ @rdoc.handle_pipe
+ end
+
+ assert_equal "\n<p>hello</p>\n", out
+ ensure
+ $stdin = STDIN
+ end
+
+ def test_load_options
+ temp_dir do
+ options = RDoc::Options.new
+ options.markup = 'tomdoc'
+ options.write_options
+
+ options = @rdoc.load_options
+
+ assert_equal 'tomdoc', options.markup
+ end
+ end
+
+ def test_load_options_invalid
+ temp_dir do
+ open '.rdoc_options', 'w' do |io|
+ io.write "a: !ruby.yaml.org,2002:str |\nfoo"
+ end
+
+ e = assert_raises RDoc::Error do
+ @rdoc.load_options
+ end
+
+ options_file = File.expand_path '.rdoc_options'
+ assert_equal "#{options_file} is not a valid rdoc options file", e.message
+ end
+ end
+
+ def load_options_no_file
+ temp_dir do
+ options = @rdoc.load_options
+
+ assert_kind_of RDoc::Options, options
+ end
+ end
+
+ def test_normalized_file_list
+ files = temp_dir do |dir|
+ flag_file = @rdoc.output_flag_file dir
+
+ FileUtils.touch flag_file
+
+ @rdoc.normalized_file_list [__FILE__, flag_file]
+ end
+
+ files = files.map { |file| File.expand_path file }
+
+ assert_equal [File.expand_path(__FILE__)], files
+ end
+
+ def test_normalized_file_list_not_modified
+ files = [__FILE__]
+
+ @rdoc.last_modified[__FILE__] = File.stat(__FILE__).mtime
+
+ files = @rdoc.normalized_file_list [__FILE__]
+
+ assert_empty files
+ end
+
+ def test_normalized_file_list_non_file_directory
+ dev = defined?(File::NULL) ? File::NULL : '/dev/stdin'
+ skip "#{dev} is not a character special" unless
+ File.chardev? dev
+
+ files = nil
+
+ out, err = verbose_capture_io do
+ files = @rdoc.normalized_file_list [dev]
+ end
+
+ files = files.map { |file| File.expand_path file }
+
+ assert_empty files
+
+ assert_empty out
+ assert_match %r"^rdoc can't parse", err
+ assert_match %r"#{dev}$", err
+ end
+
+ def test_parse_file
+ @rdoc.store = RDoc::Store.new
+
+ temp_dir do |dir|
+ @rdoc.options.root = Pathname(Dir.pwd)
+
+ open 'test.txt', 'w' do |io|
+ io.puts 'hi'
+ end
+
+ top_level = @rdoc.parse_file 'test.txt'
+
+ assert_equal 'test.txt', top_level.absolute_name
+ assert_equal 'test.txt', top_level.relative_name
+ end
+ end
+
+ def test_parse_file_binary
+ @rdoc.store = RDoc::Store.new
+
+ root = File.dirname __FILE__
+
+ @rdoc.options.root = Pathname root
+
+ out, err = capture_io do
+ Dir.chdir root do
+ assert_nil @rdoc.parse_file 'binary.dat'
+ end
+ end
+
+ assert_empty out
+ assert_empty err
+ end
+
+ def test_parse_file_include_root
+ @rdoc.store = RDoc::Store.new
+
+ top_level = nil
+ temp_dir do |dir|
+ @rdoc.options.parse %W[--root #{File.dirname(__FILE__)}]
+
+ open 'include.txt', 'w' do |io|
+ io.puts ':include: test.txt'
+ end
+
+ out, err = capture_io do
+ top_level = @rdoc.parse_file 'include.txt'
+ end
+ assert_empty out
+ assert_empty err
+ end
+ assert_equal "test file", top_level.comment.text
+ end
+
+ def test_parse_file_page_dir
+ @rdoc.store = RDoc::Store.new
+
+ temp_dir do |dir|
+ FileUtils.mkdir 'pages'
+ @rdoc.options.page_dir = Pathname('pages')
+ @rdoc.options.root = Pathname(Dir.pwd)
+
+ open 'pages/test.txt', 'w' do |io|
+ io.puts 'hi'
+ end
+
+ top_level = @rdoc.parse_file 'pages/test.txt'
+
+ assert_equal 'pages/test.txt', top_level.absolute_name
+ assert_equal 'test.txt', top_level.relative_name
+ end
+ end
+
+ def test_parse_file_relative
+ pwd = Dir.pwd
+
+ @rdoc.store = RDoc::Store.new
+
+ temp_dir do |dir|
+ @rdoc.options.root = Pathname(dir)
+
+ open 'test.txt', 'w' do |io|
+ io.puts 'hi'
+ end
+
+ test_txt = File.join dir, 'test.txt'
+
+ Dir.chdir pwd do
+ top_level = @rdoc.parse_file test_txt
+
+ assert_equal test_txt, top_level.absolute_name
+ assert_equal 'test.txt', top_level.relative_name
+ end
+ end
+ end
+
+ def test_parse_file_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+ @rdoc.options.encoding = Encoding::ISO_8859_1
+ @rdoc.store = RDoc::Store.new
+
+ tf = Tempfile.open 'test.txt' do |io|
+ io.write 'hi'
+ io.rewind
+
+ top_level = @rdoc.parse_file io.path
+
+ assert_equal Encoding::ISO_8859_1, top_level.absolute_name.encoding
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+ end
+
+ def test_parse_file_forbidden
+ skip 'chmod not supported' if Gem.win_platform?
+
+ @rdoc.store = RDoc::Store.new
+
+ tf = Tempfile.open 'test.txt' do |io|
+ io.write 'hi'
+ io.rewind
+
+ File.chmod 0000, io.path
+
+ begin
+ top_level = :bug
+
+ _, err = capture_io do
+ top_level = @rdoc.parse_file io.path
+ end
+
+ assert_match "Unable to read #{io.path},", err
+
+ assert_nil top_level
+ ensure
+ File.chmod 0400, io.path
+ end
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+ end
+
+ def test_remove_unparseable
+ file_list = %w[
+ blah.class
+ blah.eps
+ blah.erb
+ blah.scpt.txt
+ blah.ttf
+ blah.yml
+ ]
+
+ assert_empty @rdoc.remove_unparseable file_list
+ end
+
+ def test_remove_unparseable_tags_emacs
+ temp_dir do
+ open 'TAGS', 'wb' do |io| # emacs
+ io.write "\f\nlib/foo.rb,43\n"
+ end
+
+ file_list = %w[
+ TAGS
+ ]
+
+ assert_empty @rdoc.remove_unparseable file_list
+ end
+ end
+
+ def test_remove_unparseable_tags_vim
+ temp_dir do
+ open 'TAGS', 'w' do |io| # emacs
+ io.write "!_TAG_"
+ end
+
+ file_list = %w[
+ TAGS
+ ]
+
+ assert_empty @rdoc.remove_unparseable file_list
+ end
+ end
+
+ def test_setup_output_dir
+ Dir.mktmpdir {|d|
+ path = File.join d, 'testdir'
+
+ last = @rdoc.setup_output_dir path, false
+
+ assert_empty last
+
+ assert File.directory? path
+ assert File.exist? @rdoc.output_flag_file path
+ }
+ end
+
+ def test_setup_output_dir_dry_run
+ @rdoc.options.dry_run = true
+
+ Dir.mktmpdir do |d|
+ path = File.join d, 'testdir'
+
+ @rdoc.setup_output_dir path, false
+
+ refute File.exist? path
+ end
+ end
+
+ def test_setup_output_dir_exists
+ Dir.mktmpdir {|path|
+ open @rdoc.output_flag_file(path), 'w' do |io|
+ io.puts Time.at 0
+ io.puts "./lib/rdoc.rb\t#{Time.at 86400}"
+ end
+
+ last = @rdoc.setup_output_dir path, false
+
+ assert_equal 1, last.size
+ assert_equal Time.at(86400), last['./lib/rdoc.rb']
+ }
+ end
+
+ def test_setup_output_dir_exists_empty_created_rid
+ Dir.mktmpdir {|path|
+ open @rdoc.output_flag_file(path), 'w' do end
+
+ e = assert_raises RDoc::Error do
+ @rdoc.setup_output_dir path, false
+ end
+
+ assert_match %r%Directory #{Regexp.escape path} already exists%, e.message
+ }
+ end
+
+ def test_setup_output_dir_exists_file
+ tf = Tempfile.open 'test_rdoc_rdoc' do |tempfile|
+ path = tempfile.path
+
+ e = assert_raises RDoc::Error do
+ @rdoc.setup_output_dir path, false
+ end
+
+ assert_match(%r%#{Regexp.escape path} exists and is not a directory%,
+ e.message)
+ tempfile
+ end
+ tf.close! if tf.respond_to? :close!
+ end
+
+ def test_setup_output_dir_exists_not_rdoc
+ Dir.mktmpdir do |dir|
+ e = assert_raises RDoc::Error do
+ @rdoc.setup_output_dir dir, false
+ end
+
+ assert_match %r%Directory #{Regexp.escape dir} already exists%, e.message
+ end
+ end
+
+ def test_update_output_dir
+ Dir.mktmpdir do |d|
+ @rdoc.update_output_dir d, Time.now, {}
+
+ assert File.exist? "#{d}/created.rid"
+ end
+ end
+
+ def test_update_output_dir_dont
+ Dir.mktmpdir do |d|
+ @rdoc.options.update_output_dir = false
+ @rdoc.update_output_dir d, Time.now, {}
+
+ refute File.exist? "#{d}/created.rid"
+ end
+ end
+
+ def test_update_output_dir_dry_run
+ Dir.mktmpdir do |d|
+ @rdoc.options.dry_run = true
+ @rdoc.update_output_dir d, Time.now, {}
+
+ refute File.exist? "#{d}/created.rid"
+ end
+ end
+
+ def test_normalized_file_list_removes_created_rid_dir
+ temp_dir do |d|
+ FileUtils.mkdir "doc"
+ flag_file = @rdoc.output_flag_file "doc"
+ file = File.join "doc", "test"
+ FileUtils.touch flag_file
+ FileUtils.touch file
+
+ file_list = ["doc"]
+
+ output = @rdoc.normalized_file_list file_list
+
+ assert_empty output
+ end
+ end
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_require.rb b/jni/ruby/test/rdoc/test_rdoc_require.rb
new file mode 100644
index 0000000..b7995af
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_require.rb
@@ -0,0 +1,25 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocRequire < XrefTestCase
+
+ def setup
+ super
+
+ @req = RDoc::Require.new 'foo', 'comment'
+ end
+
+ def test_initialize
+ assert_equal 'foo', @req.name
+
+ req = RDoc::Require.new '"foo"', ''
+ assert_equal 'foo', @req.name
+
+ req = RDoc::Require.new '\'foo\'', ''
+ assert_equal 'foo', @req.name
+
+ req = RDoc::Require.new '|foo|', ''
+ assert_equal 'foo', @req.name, 'for fortran?'
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_ri_driver.rb b/jni/ruby/test/rdoc/test_rdoc_ri_driver.rb
new file mode 100644
index 0000000..d0987a0
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_ri_driver.rb
@@ -0,0 +1,1436 @@
+require 'rdoc/test_case'
+
+class TestRDocRIDriver < RDoc::TestCase
+
+ def setup
+ super
+
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_ri_driver_#{$$}"
+ @home_ri = File.join @tmpdir, 'dot_ri'
+
+ FileUtils.mkdir_p @tmpdir
+ FileUtils.mkdir_p @home_ri
+
+ @orig_ri = ENV['RI']
+ @orig_home = ENV['HOME']
+ ENV['HOME'] = @tmpdir
+ ENV.delete 'RI'
+
+ @options = RDoc::RI::Driver.default_options
+ @options[:use_system] = false
+ @options[:use_site] = false
+ @options[:use_home] = false
+ @options[:use_gems] = false
+
+ @options[:home] = @tmpdir
+ @options[:use_stdout] = true
+ @options[:formatter] = @RM::ToRdoc
+
+ @driver = RDoc::RI::Driver.new @options
+ end
+
+ def teardown
+ super
+
+ ENV['HOME'] = @orig_home
+ ENV['RI'] = @orig_ri
+ FileUtils.rm_rf @tmpdir
+ end
+
+ DUMMY_PAGER = ":;\n"
+
+ def with_dummy_pager
+ pager_env, ENV['RI_PAGER'] = ENV['RI_PAGER'], DUMMY_PAGER
+ yield
+ ensure
+ ENV['RI_PAGER'] = pager_env
+ end
+
+ def test_self_dump
+ util_store
+
+ out, = capture_io do
+ RDoc::RI::Driver.dump @store1.cache_path
+ end
+
+ assert_match %r%:class_methods%, out
+ assert_match %r%:modules%, out
+ assert_match %r%:instance_methods%, out
+ assert_match %r%:ancestors%, out
+ end
+
+ def test_add_also_in_empty
+ out = @RM::Document.new
+
+ @driver.add_also_in out, []
+
+ assert_empty out
+ end
+
+ def test_add_also_in
+ util_multi_store
+ @store1.type = :system
+ @store2.type = :home
+
+ out = @RM::Document.new
+
+ @driver.add_also_in out, [@store1, @store2]
+
+ expected = @RM::Document.new(
+ @RM::Rule.new(1),
+ @RM::Paragraph.new('Also found in:'),
+ @RM::Verbatim.new("ruby core", "\n",
+ "~/.rdoc", "\n"))
+
+ assert_equal expected, out
+ end
+
+ def test_add_class
+ util_multi_store
+
+ out = @RM::Document.new
+
+ @driver.add_class out, 'Bar', [@cBar]
+
+ expected = @RM::Document.new(
+ @RM::Heading.new(1, 'Bar < Foo'),
+ @RM::BlankLine.new)
+
+ assert_equal expected, out
+ end
+
+ def test_add_from
+ util_store
+ @store1.type = :system
+
+ out = @RM::Document.new
+
+ @driver.add_from out, @store1
+
+ expected = @RM::Document.new @RM::Paragraph.new("(from ruby core)")
+
+ assert_equal expected, out
+ end
+
+ def test_add_extends
+ util_store
+
+ out = @RM::Document.new
+
+ @driver.add_extends out, [[[@cFooExt], @store1]]
+
+ expected = @RM::Document.new(
+ @RM::Rule.new(1),
+ @RM::Heading.new(1, "Extended by:"),
+ @RM::Paragraph.new("Ext (from #{@store1.friendly_path})"),
+ @RM::BlankLine.new,
+ @RM::Paragraph.new("Extend thingy"),
+ @RM::BlankLine.new)
+
+ assert_equal expected, out
+ end
+
+ def test_add_extension_modules_empty
+ out = @RM::Document.new
+
+ @driver.add_extension_modules out, 'Includes', []
+
+ assert_empty out
+ end
+
+ def test_add_extension_modules_many
+ util_store
+
+ out = @RM::Document.new
+
+ enum = RDoc::Include.new 'Enumerable', nil
+ @cFoo.add_include enum
+
+ @driver.add_extension_modules out, 'Includes', [[[@cFooInc, enum], @store1]]
+
+ expected = @RM::Document.new(
+ @RM::Rule.new(1),
+ @RM::Heading.new(1, "Includes:"),
+ @RM::Paragraph.new("(from #{@store1.friendly_path})"),
+ @RM::BlankLine.new,
+ @RM::Paragraph.new("Inc"),
+ @RM::BlankLine.new,
+ @RM::Paragraph.new("Include thingy"),
+ @RM::BlankLine.new,
+ @RM::Verbatim.new("Enumerable", "\n"))
+
+ assert_equal expected, out
+ end
+
+ def test_add_extension_modules_many_no_doc
+ util_store
+
+ out = @RM::Document.new
+
+ enum = RDoc::Include.new 'Enumerable', nil
+ @cFoo.add_include enum
+ @cFooInc.instance_variable_set :@comment, ''
+
+ @driver.add_extension_modules out, 'Includes', [[[@cFooInc, enum], @store1]]
+
+ expected = @RM::Document.new(
+ @RM::Rule.new(1),
+ @RM::Heading.new(1, "Includes:"),
+ @RM::Paragraph.new("(from #{@store1.friendly_path})"),
+ @RM::Verbatim.new("Inc", "\n",
+ "Enumerable", "\n"))
+
+ assert_equal expected, out
+ end
+
+ def test_add_extension_modules_one
+ util_store
+
+ out = @RM::Document.new
+
+ @driver.add_extension_modules out, 'Includes', [[[@cFooInc], @store1]]
+
+ expected = @RM::Document.new(
+ @RM::Rule.new(1),
+ @RM::Heading.new(1, "Includes:"),
+ @RM::Paragraph.new("Inc (from #{@store1.friendly_path})"),
+ @RM::BlankLine.new,
+ @RM::Paragraph.new("Include thingy"),
+ @RM::BlankLine.new)
+
+ assert_equal expected, out
+ end
+
+ def test_add_includes
+ util_store
+
+ out = @RM::Document.new
+
+ @driver.add_includes out, [[[@cFooInc], @store1]]
+
+ expected = @RM::Document.new(
+ @RM::Rule.new(1),
+ @RM::Heading.new(1, "Includes:"),
+ @RM::Paragraph.new("Inc (from #{@store1.friendly_path})"),
+ @RM::BlankLine.new,
+ @RM::Paragraph.new("Include thingy"),
+ @RM::BlankLine.new)
+
+ assert_equal expected, out
+ end
+
+ def test_add_method
+ util_store
+
+ out = doc
+
+ @driver.add_method out, 'Foo::Bar#blah'
+
+ expected =
+ doc(
+ head(1, 'Foo::Bar#blah'),
+ blank_line,
+ para('(from ~/.rdoc)'),
+ head(3, 'Implementation from Bar'),
+ rule(1),
+ verb("blah(5) => 5\n",
+ "blah(6) => 6\n"),
+ rule(1),
+ blank_line,
+ blank_line)
+
+ assert_equal expected, out
+ end
+
+ def test_add_method_attribute
+ util_store
+
+ out = doc
+
+ @driver.add_method out, 'Foo::Bar#attr'
+
+ expected =
+ doc(
+ head(1, 'Foo::Bar#attr'),
+ blank_line,
+ para('(from ~/.rdoc)'),
+ rule(1),
+ blank_line,
+ blank_line)
+
+ assert_equal expected, out
+ end
+
+ def test_add_method_inherited
+ util_multi_store
+
+ out = doc
+
+ @driver.add_method out, 'Bar#inherit'
+
+ expected =
+ doc(
+ head(1, 'Bar#inherit'),
+ blank_line,
+ para('(from ~/.rdoc)'),
+ head(3, 'Implementation from Foo'),
+ rule(1),
+ blank_line,
+ blank_line)
+
+ assert_equal expected, out
+ end
+
+ def test_add_method_overriden
+ util_multi_store
+
+ out = doc
+
+ @driver.add_method out, 'Bar#override'
+
+ expected =
+ doc(
+ head(1, 'Bar#override'),
+ blank_line,
+ para("(from #{@store2.path})"),
+ rule(1),
+ blank_line,
+ para('must be displayed'),
+ blank_line,
+ blank_line)
+
+ assert_equal expected, out
+ end
+
+ def test_add_method_documentation
+ util_store
+
+ out = doc()
+
+ missing = RDoc::AnyMethod.new nil, 'missing'
+ @cFoo.add_method missing
+
+ @driver.add_method_documentation out, @cFoo
+
+ expected =
+ doc(
+ head(1, 'Foo#inherit'),
+ blank_line,
+ para('(from ~/.rdoc)'),
+ rule(1),
+ blank_line,
+ blank_line,
+ head(1, 'Foo#override'),
+ blank_line,
+ para('(from ~/.rdoc)'),
+ rule(1),
+ blank_line,
+ para('must not be displayed in Bar#override'),
+ blank_line,
+ blank_line)
+
+ assert_equal expected, out
+ end
+
+ def test_add_method_list
+ out = @RM::Document.new
+
+ @driver.add_method_list out, %w[new parse], 'Class methods'
+
+ expected = @RM::Document.new(
+ @RM::Heading.new(1, 'Class methods:'),
+ @RM::BlankLine.new,
+ @RM::Verbatim.new('new'),
+ @RM::Verbatim.new('parse'),
+ @RM::BlankLine.new)
+
+ assert_equal expected, out
+ end
+
+ def test_add_method_list_interative
+ @options[:interactive] = true
+ driver = RDoc::RI::Driver.new @options
+
+ out = @RM::Document.new
+
+ driver.add_method_list out, %w[new parse], 'Class methods'
+
+ expected = @RM::Document.new(
+ @RM::Heading.new(1, 'Class methods:'),
+ @RM::BlankLine.new,
+ @RM::IndentedParagraph.new(2, 'new, parse'),
+ @RM::BlankLine.new)
+
+ assert_equal expected, out
+ end
+
+ def test_add_method_list_none
+ out = @RM::Document.new
+
+ @driver.add_method_list out, [], 'Class'
+
+ assert_equal @RM::Document.new, out
+ end
+
+ def test_ancestors_of
+ util_ancestors_store
+
+ assert_equal %w[X Mixin Object Foo], @driver.ancestors_of('Foo::Bar')
+ end
+
+ def test_classes
+ util_multi_store
+
+ expected = {
+ 'Ambiguous' => [@store1, @store2],
+ 'Bar' => [@store2],
+ 'Ext' => [@store1],
+ 'Foo' => [@store1, @store2],
+ 'Foo::Bar' => [@store1],
+ 'Foo::Baz' => [@store1, @store2],
+ 'Inc' => [@store1],
+ }
+
+ classes = @driver.classes
+
+ assert_equal expected.keys.sort, classes.keys.sort
+
+ expected.each do |klass, stores|
+ assert_equal stores, classes[klass].sort_by { |store| store.path },
+ "mismatch for #{klass}"
+ end
+ end
+
+ def test_class_document
+ util_store
+
+ tl1 = @store1.add_file 'one.rb'
+ tl2 = @store1.add_file 'two.rb'
+
+ @cFoo.add_comment 'one', tl1
+ @cFoo.add_comment 'two', tl2
+
+ @store1.save_class @cFoo
+
+ found = [
+ [@store1, @store1.load_class(@cFoo.full_name)]
+ ]
+
+ extends = [[[@cFooExt], @store1]]
+ includes = [[[@cFooInc], @store1]]
+
+ out = @driver.class_document @cFoo.full_name, found, [], includes, extends
+
+ expected = @RM::Document.new
+ @driver.add_class expected, 'Foo', []
+ @driver.add_includes expected, includes
+ @driver.add_extends expected, extends
+ @driver.add_from expected, @store1
+ expected << @RM::Rule.new(1)
+
+ doc = @RM::Document.new(@RM::Paragraph.new('one'))
+ doc.file = 'one.rb'
+ expected.push doc
+ expected << @RM::BlankLine.new
+ doc = @RM::Document.new(@RM::Paragraph.new('two'))
+ doc.file = 'two.rb'
+ expected.push doc
+
+ expected << @RM::Rule.new(1)
+ expected << @RM::Heading.new(1, 'Instance methods:')
+ expected << @RM::BlankLine.new
+ expected << @RM::Verbatim.new('inherit')
+ expected << @RM::Verbatim.new('override')
+ expected << @RM::BlankLine.new
+
+ assert_equal expected, out
+ end
+
+ def test_complete
+ store = RDoc::RI::Store.new @home_ri
+ store.cache[:ancestors] = {
+ 'Foo' => %w[Object],
+ 'Foo::Bar' => %w[Object],
+ }
+ store.cache[:class_methods] = {
+ 'Foo' => %w[bar]
+ }
+ store.cache[:instance_methods] = {
+ 'Foo' => %w[Bar]
+ }
+ store.cache[:modules] = %w[
+ Foo
+ Foo::Bar
+ ]
+
+ @driver.stores = [store]
+
+ assert_equal %w[Foo ], @driver.complete('F')
+ assert_equal %w[ Foo::Bar], @driver.complete('Foo::B')
+
+ assert_equal %w[Foo#Bar], @driver.complete('Foo#'), 'Foo#'
+ assert_equal %w[Foo#Bar Foo::bar], @driver.complete('Foo.'), 'Foo.'
+ assert_equal %w[Foo::Bar Foo::bar], @driver.complete('Foo::'), 'Foo::'
+
+ assert_equal %w[ Foo::bar], @driver.complete('Foo::b'), 'Foo::b'
+ end
+
+ def test_complete_ancestor
+ util_ancestors_store
+
+ assert_equal %w[Foo::Bar#i_method], @driver.complete('Foo::Bar#')
+
+ assert_equal %w[Foo::Bar#i_method Foo::Bar::c_method Foo::Bar::new],
+ @driver.complete('Foo::Bar.')
+ end
+
+ def test_complete_classes
+ util_store
+
+ assert_equal %w[ ], @driver.complete('[')
+ assert_equal %w[ ], @driver.complete('[::')
+ assert_equal %w[Foo ], @driver.complete('F')
+ assert_equal %w[Foo:: Foo::Bar Foo::Baz], @driver.complete('Foo::')
+ assert_equal %w[ Foo::Bar Foo::Baz], @driver.complete('Foo::B')
+ end
+
+ def test_complete_multistore
+ util_multi_store
+
+ assert_equal %w[Bar], @driver.complete('B')
+ assert_equal %w[Foo], @driver.complete('F')
+ assert_equal %w[Foo::Bar Foo::Baz], @driver.complete('Foo::B')
+ end
+
+ def test_display
+ doc = @RM::Document.new(
+ @RM::Paragraph.new('hi'))
+
+ out, = capture_io do
+ @driver.display doc
+ end
+
+ assert_equal "hi\n", out
+ end
+
+ def test_display_class
+ util_store
+
+ out, = capture_io do
+ @driver.display_class 'Foo::Bar'
+ end
+
+ assert_match %r%^= Foo::Bar%, out
+ assert_match %r%^\(from%, out
+
+ assert_match %r%^= Class methods:%, out
+ assert_match %r%^ new%, out
+ assert_match %r%^= Instance methods:%, out
+ assert_match %r%^ blah%, out
+ assert_match %r%^= Attributes:%, out
+ assert_match %r%^ attr_accessor attr%, out
+
+ assert_equal 1, out.scan(/-\n/).length
+
+ refute_match %r%Foo::Bar#blah%, out
+ end
+
+ def test_display_class_all
+ util_store
+
+ @driver.show_all = true
+
+ out, = capture_io do
+ @driver.display_class 'Foo::Bar'
+ end
+
+ assert_match %r%^= Foo::Bar%, out
+ assert_match %r%^\(from%, out
+
+ assert_match %r%^= Class methods:%, out
+ assert_match %r%^ new%, out
+ assert_match %r%^= Instance methods:%, out
+ assert_match %r%^ blah%, out
+ assert_match %r%^= Attributes:%, out
+ assert_match %r%^ attr_accessor attr%, out
+
+ assert_equal 6, out.scan(/-\n/).length
+
+ assert_match %r%Foo::Bar#blah%, out
+ end
+
+ def test_display_class_ambiguous
+ util_multi_store
+
+ out, = capture_io do
+ @driver.display_class 'Ambiguous'
+ end
+
+ assert_match %r%^= Ambiguous < Object$%, out
+ end
+
+ def test_display_class_multi_no_doc
+ util_multi_store
+
+ out, = capture_io do
+ @driver.display_class 'Foo::Baz'
+ end
+
+ assert_match %r%^= Foo::Baz%, out
+ assert_match %r%-\n%, out
+ assert_match %r%Also found in:%, out
+ assert_match %r%#{Regexp.escape @home_ri}%, out
+ assert_match %r%#{Regexp.escape @home_ri2}%, out
+ end
+
+ def test_display_class_superclass
+ util_multi_store
+
+ out, = capture_io do
+ @driver.display_class 'Bar'
+ end
+
+ assert_match %r%^= Bar < Foo%, out
+ end
+
+ def test_display_class_module
+ util_store
+
+ out, = capture_io do
+ @driver.display_class 'Inc'
+ end
+
+ assert_match %r%^= Inc$%, out
+ end
+
+ def test_display_class_page
+ out, = capture_io do
+ @driver.display_class 'ruby:README'
+ end
+
+ assert_empty out
+ end
+
+ def test_display_method
+ util_store
+
+ out, = capture_io do
+ @driver.display_method 'Foo::Bar#blah'
+ end
+
+ assert_match %r%Foo::Bar#blah%, out
+ assert_match %r%blah.5%, out
+ assert_match %r%blah.6%, out
+ end
+
+ def test_display_method_attribute
+ util_store
+
+ out, = capture_io do
+ @driver.display_method 'Foo::Bar#attr'
+ end
+
+ assert_match %r%Foo::Bar#attr%, out
+ refute_match %r%Implementation from%, out
+ end
+
+ def test_display_method_inherited
+ util_multi_store
+
+ out, = capture_io do
+ @driver.display_method 'Bar#inherit'
+ end
+
+ assert_match %r%^= Bar#inherit%, out
+ assert_match %r%^=== Implementation from Foo%, out
+ end
+
+ def test_display_method_overriden
+ util_multi_store
+
+ out, = capture_io do
+ @driver.display_method 'Bar#override'
+ end
+
+ refute_match %r%must not be displayed%, out
+ end
+
+ def test_display_name_not_found_class
+ util_store
+
+ out, = capture_io do
+ assert_equal false, @driver.display_name('Foo::B')
+ end
+
+ expected = <<-EXPECTED
+Foo::B not found, maybe you meant:
+
+Foo::Bar
+Foo::Baz
+ EXPECTED
+
+ assert_equal expected, out
+ end
+
+ def test_display_name_not_found_method
+ util_store
+
+ out, = capture_io do
+ assert_equal false, @driver.display_name('Foo::Bar#b')
+ end
+
+ expected = <<-EXPECTED
+Foo::Bar#b not found, maybe you meant:
+
+Foo::Bar#blah
+Foo::Bar#bother
+ EXPECTED
+
+ assert_equal expected, out
+ end
+
+ def test_display_name_not_found_special
+ util_store
+
+ assert_raises RDoc::RI::Driver::NotFoundError do
+ assert_equal false, @driver.display_name('Set#[]')
+ end
+ end
+
+ def test_display_method_params
+ util_store
+
+ out, = capture_io do
+ @driver.display_method 'Foo::Bar#bother'
+ end
+
+ assert_match %r%things.*stuff%, out
+ end
+
+ def test_display_page
+ util_store
+
+ out, = capture_io do
+ @driver.display_page 'home:README.rdoc'
+ end
+
+ assert_match %r%= README%, out
+ end
+
+ def test_display_page_add_extension
+ util_store
+
+ out, = capture_io do
+ @driver.display_page 'home:README'
+ end
+
+ assert_match %r%= README%, out
+ end
+
+ def test_display_page_ambiguous
+ util_store
+
+ other = @store1.add_file 'README.md'
+ other.parser = RDoc::Parser::Simple
+ other.comment =
+ doc(
+ head(1, 'README.md'),
+ para('This is the other README'))
+
+ @store1.save_page other
+
+ out, = capture_io do
+ @driver.display_page 'home:README'
+ end
+
+ assert_match %r%= README pages in ~/\.rdoc%, out
+ assert_match %r%README\.rdoc%, out
+ assert_match %r%README\.md%, out
+ end
+
+ def test_display_page_extension
+ util_store
+
+ other = @store1.add_file 'README.EXT'
+ other.parser = RDoc::Parser::Simple
+ other.comment =
+ doc(
+ head(1, 'README.EXT'),
+ para('This is the other README'))
+
+ @store1.save_page other
+
+ out, = capture_io do
+ @driver.display_page 'home:README.EXT'
+ end
+
+ assert_match 'other README', out
+ end
+
+ def test_display_page_ignore_directory
+ util_store
+
+ other = @store1.add_file 'doc/globals.rdoc'
+ other.parser = RDoc::Parser::Simple
+ other.comment =
+ doc(
+ head(1, 'globals.rdoc'),
+ para('Globals go here'))
+
+ @store1.save_page other
+
+ out, = capture_io do
+ @driver.display_page 'home:globals'
+ end
+
+ assert_match %r%= globals\.rdoc%, out
+ end
+
+ def test_display_page_missing
+ util_store
+
+ out, = capture_io do
+ @driver.display_page 'home:missing'
+ end
+
+ out, = capture_io do
+ @driver.display_page_list @store1
+ end
+
+ assert_match %r%= Pages in ~/\.rdoc%, out
+ assert_match %r%README\.rdoc%, out
+ end
+
+ def test_display_page_list
+ util_store
+
+ other = @store1.add_file 'OTHER.rdoc'
+ other.parser = RDoc::Parser::Simple
+ other.comment =
+ doc(
+ head(1, 'OTHER'),
+ para('This is OTHER'))
+
+ @store1.save_page other
+
+ out, = capture_io do
+ @driver.display_page_list @store1
+ end
+
+ assert_match %r%= Pages in ~/\.rdoc%, out
+ assert_match %r%README\.rdoc%, out
+ assert_match %r%OTHER\.rdoc%, out
+ end
+
+ def test_expand_class
+ util_store
+
+ assert_equal 'Foo', @driver.expand_class('F')
+ assert_equal 'Foo::Bar', @driver.expand_class('F::Bar')
+
+ assert_raises RDoc::RI::Driver::NotFoundError do
+ @driver.expand_class 'F::B'
+ end
+ end
+
+ def test_expand_name
+ util_store
+
+ assert_equal '.b', @driver.expand_name('b')
+ assert_equal 'Foo', @driver.expand_name('F')
+ assert_equal 'Foo::Bar#', @driver.expand_name('F::Bar#')
+
+ e = assert_raises RDoc::RI::Driver::NotFoundError do
+ @driver.expand_name 'Z'
+ end
+
+ assert_equal 'Z', e.name
+
+ @driver.stores << RDoc::Store.new(nil, :system)
+
+ assert_equal 'ruby:README', @driver.expand_name('ruby:README')
+ assert_equal 'ruby:', @driver.expand_name('ruby:')
+
+ e = assert_raises RDoc::RI::Driver::NotFoundError do
+ @driver.expand_name 'nonexistent_gem:'
+ end
+
+ assert_equal 'nonexistent_gem', e.name
+ end
+
+ def test_find_methods
+ util_store
+
+ items = []
+
+ @driver.find_methods 'Foo::Bar.' do |store, klass, ancestor, types, method|
+ items << [store, klass, ancestor, types, method]
+ end
+
+ expected = [
+ [@store1, 'Foo::Bar', 'Foo::Bar', :both, nil],
+ ]
+
+ assert_equal expected, items
+ end
+
+ def test_find_methods_method
+ util_store
+
+ items = []
+
+ @driver.find_methods '.blah' do |store, klass, ancestor, types, method|
+ items << [store, klass, ancestor, types, method]
+ end
+
+ expected = [
+ [@store1, 'Ambiguous', 'Ambiguous', :both, 'blah'],
+ [@store1, 'Ext', 'Ext', :both, 'blah'],
+ [@store1, 'Foo', 'Foo', :both, 'blah'],
+ [@store1, 'Foo::Bar', 'Foo::Bar', :both, 'blah'],
+ [@store1, 'Foo::Baz', 'Foo::Baz', :both, 'blah'],
+ [@store1, 'Inc', 'Inc', :both, 'blah'],
+ ]
+
+ assert_equal expected, items
+ end
+
+ def test_filter_methods
+ util_multi_store
+
+ name = 'Bar#override'
+
+ found = @driver.load_methods_matching name
+
+ sorted = @driver.filter_methods found, name
+
+ expected = [[@store2, [@override]]]
+
+ assert_equal expected, sorted
+ end
+
+ def test_filter_methods_not_found
+ util_multi_store
+
+ name = 'Bar#inherit'
+
+ found = @driver.load_methods_matching name
+
+ sorted = @driver.filter_methods found, name
+
+ assert_equal found, sorted
+ end
+
+ def test_find_store
+ @driver.stores << RDoc::Store.new(nil, :system)
+ @driver.stores << RDoc::Store.new('doc/gem-1.0/ri', :gem)
+
+ assert_equal 'ruby', @driver.find_store('ruby')
+ assert_equal 'gem-1.0', @driver.find_store('gem-1.0')
+ assert_equal 'gem-1.0', @driver.find_store('gem')
+
+ e = assert_raises RDoc::RI::Driver::NotFoundError do
+ @driver.find_store 'nonexistent'
+ end
+
+ assert_equal 'nonexistent', e.name
+ end
+
+ def test_formatter
+ tty = Object.new
+ def tty.tty?() true; end
+
+ @options.delete :use_stdout
+ @options.delete :formatter
+
+ driver = RDoc::RI::Driver.new @options
+
+ assert_instance_of @RM::ToAnsi, driver.formatter(tty)
+
+ assert_instance_of @RM::ToBs, driver.formatter(StringIO.new)
+
+ driver.instance_variable_set :@paging, true
+
+ assert_instance_of @RM::ToBs, driver.formatter(StringIO.new)
+ end
+
+ def test_in_path_eh
+ path = ENV['PATH']
+
+ test_path = File.expand_path '..', __FILE__
+
+ temp_dir do |dir|
+ nonexistent = File.join dir, 'nonexistent'
+ refute @driver.in_path?(nonexistent)
+
+ ENV['PATH'] = test_path
+
+ assert @driver.in_path?(File.basename(__FILE__))
+ end
+ ensure
+ ENV['PATH'] = path
+ end
+
+ def test_method_type
+ assert_equal :both, @driver.method_type(nil)
+ assert_equal :both, @driver.method_type('.')
+ assert_equal :instance, @driver.method_type('#')
+ assert_equal :class, @driver.method_type('::')
+ end
+
+ def test_name_regexp
+ assert_equal %r%^RDoc::AnyMethod#new$%,
+ @driver.name_regexp('RDoc::AnyMethod#new')
+
+ assert_equal %r%^RDoc::AnyMethod::new$%,
+ @driver.name_regexp('RDoc::AnyMethod::new')
+
+ assert_equal %r%^RDoc::AnyMethod(#|::)new$%,
+ @driver.name_regexp('RDoc::AnyMethod.new')
+
+ assert_equal %r%^Hash(#|::)\[\]$%,
+ @driver.name_regexp('Hash.[]')
+
+ assert_equal %r%^Hash::\[\]$%,
+ @driver.name_regexp('Hash::[]')
+ end
+
+ def test_list_known_classes
+ util_store
+
+ out, = capture_io do
+ @driver.list_known_classes
+ end
+
+ assert_equal "Ambiguous\nExt\nFoo\nFoo::Bar\nFoo::Baz\nInc\n", out
+ end
+
+ def test_list_known_classes_name
+ util_store
+
+ out, = capture_io do
+ @driver.list_known_classes %w[F I]
+ end
+
+ assert_equal "Foo\nFoo::Bar\nFoo::Baz\nInc\n", out
+ end
+
+ def test_list_methods_matching
+ util_store
+
+ assert_equal %w[
+ Foo::Bar#attr
+ Foo::Bar#blah
+ Foo::Bar#bother
+ Foo::Bar::new
+ ],
+ @driver.list_methods_matching('Foo::Bar.').sort
+ end
+
+ def test_list_methods_matching_inherit
+ util_multi_store
+
+ assert_equal %w[
+ Bar#baz
+ Bar#inherit
+ Bar#override
+ ],
+ @driver.list_methods_matching('Bar.').sort
+ end
+
+ def test_list_methods_matching_regexp
+ util_store
+
+ index = RDoc::AnyMethod.new nil, '[]'
+ index.record_location @top_level
+ @cFoo.add_method index
+ @store1.save_method @cFoo, index
+
+ c_index = RDoc::AnyMethod.new nil, '[]'
+ c_index.singleton = true
+ c_index.record_location @top_level
+ @cFoo.add_method c_index
+ @store1.save_method @cFoo, c_index
+
+ @store1.save_cache
+
+ assert_equal %w[Foo#[]], @driver.list_methods_matching('Foo#[]')
+ assert_equal %w[Foo::[]], @driver.list_methods_matching('Foo::[]')
+ end
+
+ def test_load_method
+ util_store
+
+ method = @driver.load_method(@store1, :instance_methods, 'Foo', '#',
+ 'inherit')
+
+ assert_equal @inherit, method
+ end
+
+ def test_load_method_inherited
+ util_multi_store
+
+ method = @driver.load_method(@store2, :instance_methods, 'Bar', '#',
+ 'inherit')
+
+ assert_equal nil, method
+ end
+
+ def test_load_methods_matching
+ util_store
+
+ expected = [[@store1, [@inherit]]]
+
+ assert_equal expected, @driver.load_methods_matching('Foo#inherit')
+
+ expected = [[@store1, [@blah]]]
+
+ assert_equal expected, @driver.load_methods_matching('.blah')
+
+ assert_empty @driver.load_methods_matching('.b')
+ end
+
+ def test_load_methods_matching_inherited
+ util_multi_store
+
+ expected = [[@store1, [@inherit]]]
+
+ assert_equal expected, @driver.load_methods_matching('Bar#inherit')
+ end
+
+ def test_load_method_missing
+ util_store
+
+ FileUtils.rm @store1.method_file 'Foo', '#inherit'
+
+ method = @driver.load_method(@store1, :instance_methods, 'Foo', '#',
+ 'inherit')
+
+ assert_equal '(unknown)#inherit', method.full_name
+ end
+
+ def _test_page # this test doesn't do anything anymore :(
+ @driver.use_stdout = false
+
+ with_dummy_pager do
+ @driver.page do |io|
+ skip "couldn't find a standard pager" if io == $stdout
+
+ assert @driver.paging?
+ end
+ end
+
+ refute @driver.paging?
+ end
+
+ # this test is too fragile. Perhaps using Process.spawn will make this
+ # reliable
+ def _test_page_in_presence_of_child_status
+ skip 'this test hangs on travis-ci.org' if ENV['CI']
+ @driver.use_stdout = false
+
+ with_dummy_pager do
+ @driver.page do |io|
+ refute_equal $stdout, io
+ assert @driver.paging?
+ end
+ end
+ end
+
+ def test_page_stdout
+ @driver.use_stdout = true
+
+ @driver.page do |io|
+ assert_equal $stdout, io
+ end
+
+ refute @driver.paging?
+ end
+
+ def test_parse_name_method
+ klass, type, meth = @driver.parse_name 'foo'
+
+ assert_equal '', klass, 'foo class'
+ assert_equal '.', type, 'foo type'
+ assert_equal 'foo', meth, 'foo method'
+
+ klass, type, meth = @driver.parse_name '#foo'
+
+ assert_equal '', klass, '#foo class'
+ assert_equal '#', type, '#foo type'
+ assert_equal 'foo', meth, '#foo method'
+
+ klass, type, meth = @driver.parse_name '::foo'
+
+ assert_equal '', klass, '::foo class'
+ assert_equal '::', type, '::foo type'
+ assert_equal 'foo', meth, '::foo method'
+ end
+
+ def test_parse_name_page
+ klass, type, meth = @driver.parse_name 'ruby:README'
+
+ assert_equal 'ruby', klass, 'ruby project'
+ assert_equal ':', type, 'ruby type'
+ assert_equal 'README', meth, 'ruby page'
+
+ klass, type, meth = @driver.parse_name 'ruby:'
+
+ assert_equal 'ruby', klass, 'ruby project'
+ assert_equal ':', type, 'ruby type'
+ assert_equal nil, meth, 'ruby page'
+ end
+
+ def test_parse_name_page_extenson
+ klass, type, meth = @driver.parse_name 'ruby:README.EXT'
+
+ assert_equal 'ruby', klass, 'ruby project'
+ assert_equal ':', type, 'ruby type'
+ assert_equal 'README.EXT', meth, 'ruby page'
+ end
+
+ def test_parse_name_single_class
+ klass, type, meth = @driver.parse_name 'Foo'
+
+ assert_equal 'Foo', klass, 'Foo class'
+ assert_equal nil, type, 'Foo type'
+ assert_equal nil, meth, 'Foo method'
+
+ klass, type, meth = @driver.parse_name 'Foo#'
+
+ assert_equal 'Foo', klass, 'Foo# class'
+ assert_equal '#', type, 'Foo# type'
+ assert_equal nil, meth, 'Foo# method'
+
+ klass, type, meth = @driver.parse_name 'Foo::'
+
+ assert_equal 'Foo', klass, 'Foo:: class'
+ assert_equal '::', type, 'Foo:: type'
+ assert_equal nil, meth, 'Foo:: method'
+
+ klass, type, meth = @driver.parse_name 'Foo.'
+
+ assert_equal 'Foo', klass, 'Foo. class'
+ assert_equal '.', type, 'Foo. type'
+ assert_equal nil, meth, 'Foo. method'
+
+ klass, type, meth = @driver.parse_name 'Foo#Bar'
+
+ assert_equal 'Foo', klass, 'Foo#Bar class'
+ assert_equal '#', type, 'Foo#Bar type'
+ assert_equal 'Bar', meth, 'Foo#Bar method'
+
+ klass, type, meth = @driver.parse_name 'Foo.Bar'
+
+ assert_equal 'Foo', klass, 'Foo.Bar class'
+ assert_equal '.', type, 'Foo.Bar type'
+ assert_equal 'Bar', meth, 'Foo.Bar method'
+
+ klass, type, meth = @driver.parse_name 'Foo::bar'
+
+ assert_equal 'Foo', klass, 'Foo::bar class'
+ assert_equal '::', type, 'Foo::bar type'
+ assert_equal 'bar', meth, 'Foo::bar method'
+ end
+
+ def test_parse_name_namespace
+ klass, type, meth = @driver.parse_name 'Foo::Bar'
+
+ assert_equal 'Foo::Bar', klass, 'Foo::Bar class'
+ assert_equal nil, type, 'Foo::Bar type'
+ assert_equal nil, meth, 'Foo::Bar method'
+
+ klass, type, meth = @driver.parse_name 'Foo::Bar#'
+
+ assert_equal 'Foo::Bar', klass, 'Foo::Bar# class'
+ assert_equal '#', type, 'Foo::Bar# type'
+ assert_equal nil, meth, 'Foo::Bar# method'
+
+ klass, type, meth = @driver.parse_name 'Foo::Bar#baz'
+
+ assert_equal 'Foo::Bar', klass, 'Foo::Bar#baz class'
+ assert_equal '#', type, 'Foo::Bar#baz type'
+ assert_equal 'baz', meth, 'Foo::Bar#baz method'
+ end
+
+ def test_parse_name_special
+ specials = %w[
+ %
+ &
+ *
+ +
+ +@
+ -
+ -@
+ /
+ <
+ <<
+ <=
+ <=>
+ ==
+ ===
+ =>
+ =~
+ >
+ >>
+ []
+ []=
+ ^
+ `
+ |
+ ~
+ ~@
+ ]
+
+ specials.each do |special|
+ parsed = @driver.parse_name special
+
+ assert_equal ['', '.', special], parsed
+ end
+ end
+
+ def _test_setup_pager # this test doesn't do anything anymore :(
+ @driver.use_stdout = false
+
+ pager = with_dummy_pager do @driver.setup_pager end
+
+ skip "couldn't find a standard pager" unless pager
+
+ assert @driver.paging?
+ ensure
+ pager.close if pager
+ end
+
+ def util_ancestors_store
+ store1 = RDoc::RI::Store.new @home_ri
+ store1.cache[:ancestors] = {
+ 'Foo' => %w[Object],
+ 'Foo::Bar' => %w[Foo],
+ }
+ store1.cache[:class_methods] = {
+ 'Foo' => %w[c_method new],
+ 'Foo::Bar' => %w[new],
+ }
+ store1.cache[:instance_methods] = {
+ 'Foo' => %w[i_method],
+ }
+ store1.cache[:modules] = %w[
+ Foo
+ Foo::Bar
+ ]
+
+ store2 = RDoc::RI::Store.new @home_ri
+ store2.cache[:ancestors] = {
+ 'Foo' => %w[Mixin Object],
+ 'Mixin' => %w[],
+ 'Object' => %w[X Object],
+ 'X' => %w[Object],
+ }
+ store2.cache[:class_methods] = {
+ 'Foo' => %w[c_method new],
+ 'Mixin' => %w[],
+ 'X' => %w[],
+ 'Object' => %w[],
+ }
+ store2.cache[:instance_methods] = {
+ 'Foo' => %w[i_method],
+ 'Mixin' => %w[],
+ }
+ store2.cache[:modules] = %w[
+ Foo
+ Mixin
+ Object
+ X
+ ]
+
+ @driver.stores = store1, store2
+ end
+
+ def util_multi_store
+ util_store
+
+ @home_ri2 = "#{@home_ri}2"
+ @store2 = RDoc::RI::Store.new @home_ri2
+
+ @top_level = @store2.add_file 'file.rb'
+
+ # as if seen in a namespace like class Ambiguous::Other
+ @mAmbiguous = @top_level.add_module RDoc::NormalModule, 'Ambiguous'
+
+ @cFoo = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ @cBar = @top_level.add_class RDoc::NormalClass, 'Bar', 'Foo'
+ @cFoo_Baz = @cFoo.add_class RDoc::NormalClass, 'Baz'
+
+ @baz = @cBar.add_method RDoc::AnyMethod.new(nil, 'baz')
+ @baz.record_location @top_level
+
+ @override = @cBar.add_method RDoc::AnyMethod.new(nil, 'override')
+ @override.comment = 'must be displayed'
+ @override.record_location @top_level
+
+ @store2.save
+
+ @driver.stores = [@store1, @store2]
+ end
+
+ def util_store
+ @store1 = RDoc::RI::Store.new @home_ri, :home
+
+ @top_level = @store1.add_file 'file.rb'
+
+ @readme = @store1.add_file 'README.rdoc'
+ @readme.parser = RDoc::Parser::Simple
+ @readme.comment =
+ doc(
+ head(1, 'README'),
+ para('This is a README'))
+
+ @cFoo = @top_level.add_class RDoc::NormalClass, 'Foo'
+ @mExt = @top_level.add_module RDoc::NormalModule, 'Ext'
+ @mInc = @top_level.add_module RDoc::NormalModule, 'Inc'
+ @cAmbiguous = @top_level.add_class RDoc::NormalClass, 'Ambiguous'
+
+ doc = @RM::Document.new @RM::Paragraph.new('Extend thingy')
+ @cFooExt = @cFoo.add_extend RDoc::Extend.new('Ext', doc)
+ @cFooExt.record_location @top_level
+ doc = @RM::Document.new @RM::Paragraph.new('Include thingy')
+ @cFooInc = @cFoo.add_include RDoc::Include.new('Inc', doc)
+ @cFooInc.record_location @top_level
+
+ @cFoo_Bar = @cFoo.add_class RDoc::NormalClass, 'Bar'
+
+ @blah = @cFoo_Bar.add_method RDoc::AnyMethod.new(nil, 'blah')
+ @blah.call_seq = "blah(5) => 5\nblah(6) => 6\n"
+ @blah.record_location @top_level
+
+ @bother = @cFoo_Bar.add_method RDoc::AnyMethod.new(nil, 'bother')
+ @bother.block_params = "stuff"
+ @bother.params = "(things)"
+ @bother.record_location @top_level
+
+ @new = @cFoo_Bar.add_method RDoc::AnyMethod.new nil, 'new'
+ @new.record_location @top_level
+ @new.singleton = true
+
+ @attr = @cFoo_Bar.add_attribute RDoc::Attr.new nil, 'attr', 'RW', ''
+ @attr.record_location @top_level
+
+ @cFoo_Baz = @cFoo.add_class RDoc::NormalClass, 'Baz'
+ @cFoo_Baz.record_location @top_level
+
+ @inherit = @cFoo.add_method RDoc::AnyMethod.new(nil, 'inherit')
+ @inherit.record_location @top_level
+
+ # overriden by Bar in multi_store
+ @overriden = @cFoo.add_method RDoc::AnyMethod.new(nil, 'override')
+ @overriden.comment = 'must not be displayed in Bar#override'
+ @overriden.record_location @top_level
+
+ @store1.save
+
+ @driver.stores = [@store1]
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_ri_paths.rb b/jni/ruby/test/rdoc/test_rdoc_ri_paths.rb
new file mode 100644
index 0000000..e377b0b
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_ri_paths.rb
@@ -0,0 +1,155 @@
+require 'rdoc/test_case'
+
+class TestRDocRIPaths < RDoc::TestCase
+
+ def setup
+ super
+
+ @orig_gem_path = Gem.path
+
+ @tempdir = File.join Dir.tmpdir, "test_rdoc_ri_paths_#{$$}"
+ Gem.use_paths @tempdir
+ Gem.ensure_gem_subdirectories @tempdir
+
+ specs = [
+ @rake_10 = Gem::Specification.new('rake', '10.0.1'),
+ @rdoc_4_0 = Gem::Specification.new('rdoc', '4.0'),
+ @rdoc_3_12 = Gem::Specification.new('rdoc', '3.12'),
+ @nodoc = Gem::Specification.new('nodoc', '1.0'),
+ ]
+
+ specs.each do |spec|
+ spec.loaded_from = spec.spec_file
+
+ open spec.spec_file, 'w' do |file|
+ file.write spec.to_ruby_for_cache
+ end
+
+ FileUtils.mkdir_p File.join(spec.doc_dir, 'ri') unless
+ spec.name == 'nodoc'
+ end
+
+ Gem::Specification.reset
+ Gem::Specification.all = specs
+ end
+
+ def teardown
+ super
+
+ Gem.use_paths(*@orig_gem_path)
+ Gem::Specification.reset
+ FileUtils.rm_rf @tempdir
+ end
+
+ def test_class_each
+ enum = RDoc::RI::Paths.each true, true, true, :all
+
+ path = enum.map { |dir,| dir }
+
+ assert_equal RDoc::RI::Paths.system_dir, path.shift
+ assert_equal RDoc::RI::Paths.site_dir, path.shift
+ assert_equal RDoc::RI::Paths.home_dir, path.shift
+ assert_equal File.join(@nodoc.doc_dir, 'ri'), path.shift
+ assert_equal File.join(@rake_10.doc_dir, 'ri'), path.shift
+ assert_equal File.join(@rdoc_4_0.doc_dir, 'ri'), path.shift
+ assert_equal File.join(@rdoc_3_12.doc_dir, 'ri'), path.shift
+ assert_empty path
+ end
+
+ def test_class_gemdirs_latest
+ Dir.chdir @tempdir do
+ gemdirs = RDoc::RI::Paths.gemdirs :latest
+
+ expected = [
+ File.join(@rake_10.doc_dir, 'ri'),
+ File.join(@rdoc_4_0.doc_dir, 'ri'),
+ ]
+
+ assert_equal expected, gemdirs
+ end
+ end
+
+ def test_class_gemdirs_legacy
+ Dir.chdir @tempdir do
+ gemdirs = RDoc::RI::Paths.gemdirs true
+
+ expected = [
+ File.join(@rake_10.doc_dir, 'ri'),
+ File.join(@rdoc_4_0.doc_dir, 'ri'),
+ ]
+
+ assert_equal expected, gemdirs
+ end
+ end
+
+ def test_class_gemdirs_all
+ Dir.chdir @tempdir do
+ gemdirs = RDoc::RI::Paths.gemdirs :all
+
+ expected = [
+ File.join(@nodoc.doc_dir, 'ri'),
+ File.join(@rake_10.doc_dir, 'ri'),
+ File.join(@rdoc_4_0.doc_dir, 'ri'),
+ File.join(@rdoc_3_12.doc_dir, 'ri'),
+ ]
+
+ assert_equal expected, gemdirs
+ end
+ end
+
+ def test_class_gem_dir
+ dir = RDoc::RI::Paths.gem_dir 'rake', '10.0.1'
+
+ expected = File.join @rake_10.doc_dir, 'ri'
+
+ assert_equal expected, dir
+ end
+
+ def test_class_home_dir
+ dir = RDoc::RI::Paths.home_dir
+
+ assert_equal RDoc::RI::Paths::HOMEDIR, dir
+ end
+
+ def test_class_path_nonexistent
+ temp_dir do |dir|
+ nonexistent = File.join dir, 'nonexistent'
+ dir = RDoc::RI::Paths.path true, true, true, true, nonexistent
+
+ refute_includes dir, nonexistent
+ end
+ end
+
+ def test_class_raw_path
+ path = RDoc::RI::Paths.raw_path true, true, true, true
+
+ assert_equal RDoc::RI::Paths.system_dir, path.shift
+ assert_equal RDoc::RI::Paths.site_dir, path.shift
+ assert_equal RDoc::RI::Paths.home_dir, path.shift
+ assert_equal File.join(@rake_10.doc_dir, 'ri'), path.shift
+ end
+
+ def test_class_raw_path_extra_dirs
+ path = RDoc::RI::Paths.raw_path true, true, true, true, '/nonexistent'
+
+ assert_equal '/nonexistent', path.shift
+ assert_equal RDoc::RI::Paths.system_dir, path.shift
+ assert_equal RDoc::RI::Paths.site_dir, path.shift
+ assert_equal RDoc::RI::Paths.home_dir, path.shift
+ assert_equal File.join(@rake_10.doc_dir, 'ri'), path.shift
+ end
+
+ def test_class_site_dir
+ dir = RDoc::RI::Paths.site_dir
+
+ assert_equal File.join(RDoc::RI::Paths::BASE, 'site'), dir
+ end
+
+ def test_class_system_dir
+ dir = RDoc::RI::Paths.system_dir
+
+ assert_equal File.join(RDoc::RI::Paths::BASE, 'system'), dir
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_ruby_lex.rb b/jni/ruby/test/rdoc/test_rdoc_ruby_lex.rb
new file mode 100644
index 0000000..8871b4f
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_ruby_lex.rb
@@ -0,0 +1,421 @@
+# coding: UTF-8
+
+require 'rdoc/test_case'
+
+class TestRDocRubyLex < RDoc::TestCase
+
+ def setup
+ @TK = RDoc::RubyToken
+ end
+
+ def test_class_tokenize
+ tokens = RDoc::RubyLex.tokenize "def x() end", nil
+
+ expected = [
+ @TK::TkDEF .new( 0, 1, 0, "def"),
+ @TK::TkSPACE .new( 3, 1, 3, " "),
+ @TK::TkIDENTIFIER.new( 4, 1, 4, "x"),
+ @TK::TkLPAREN .new( 5, 1, 5, "("),
+ @TK::TkRPAREN .new( 6, 1, 6, ")"),
+ @TK::TkSPACE .new( 7, 1, 7, " "),
+ @TK::TkEND .new( 8, 1, 8, "end"),
+ @TK::TkNL .new(11, 1, 11, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize___END__
+ tokens = RDoc::RubyLex.tokenize '__END__', nil
+
+ expected = [
+ @TK::TkEND_OF_SCRIPT.new(0, 1, 0, '__END__'),
+ @TK::TkNL .new(7, 1, 7, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_character_literal
+ tokens = RDoc::RubyLex.tokenize "?\\", nil
+
+ expected = [
+ @TK::TkCHAR.new( 0, 1, 0, "?\\"),
+ @TK::TkNL .new( 2, 1, 2, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_def_heredoc
+ tokens = RDoc::RubyLex.tokenize <<-'RUBY', nil
+def x
+ <<E
+Line 1
+Line 2
+E
+end
+ RUBY
+
+ expected = [
+ @TK::TkDEF .new( 0, 1, 0, 'def'),
+ @TK::TkSPACE .new( 3, 1, 3, ' '),
+ @TK::TkIDENTIFIER.new( 4, 1, 4, 'x'),
+ @TK::TkNL .new( 5, 1, 5, "\n"),
+ @TK::TkSPACE .new( 6, 2, 0, ' '),
+ @TK::TkHEREDOC .new( 8, 2, 2,
+ %Q{<<E\nLine 1\nLine 2\nE}),
+ @TK::TkNL .new(27, 5, 28, "\n"),
+ @TK::TkEND .new(28, 6, 0, 'end'),
+ @TK::TkNL .new(31, 6, 28, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_hash_symbol
+ tokens = RDoc::RubyLex.tokenize '{ class:"foo" }', nil
+
+ expected = [
+ @TK::TkLBRACE .new( 0, 1, 0, '{'),
+ @TK::TkSPACE .new( 1, 1, 1, ' '),
+ @TK::TkIDENTIFIER.new( 2, 1, 2, 'class'),
+ @TK::TkSYMBEG .new( 7, 1, 7, ':'),
+ @TK::TkSTRING .new( 8, 1, 8, '"foo"'),
+ @TK::TkSPACE .new(13, 1, 13, ' '),
+ @TK::TkRBRACE .new(14, 1, 14, '}'),
+ @TK::TkNL .new(15, 1, 15, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_heredoc_CR_NL
+ tokens = RDoc::RubyLex.tokenize <<-RUBY, nil
+string = <<-STRING\r
+Line 1\r
+Line 2\r
+ STRING\r
+ RUBY
+
+ expected = [
+ @TK::TkIDENTIFIER.new( 0, 1, 0, 'string'),
+ @TK::TkSPACE .new( 6, 1, 6, ' '),
+ @TK::TkASSIGN .new( 7, 1, 7, '='),
+ @TK::TkSPACE .new( 8, 1, 8, ' '),
+ @TK::TkHEREDOC .new( 9, 1, 9,
+ %Q{<<-STRING\nLine 1\nLine 2\n STRING}),
+ @TK::TkSPACE .new(44, 4, 45, "\r"),
+ @TK::TkNL .new(45, 4, 46, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_heredoc_call
+ tokens = RDoc::RubyLex.tokenize <<-'RUBY', nil
+string = <<-STRING.chomp
+Line 1
+Line 2
+ STRING
+ RUBY
+
+ expected = [
+ @TK::TkIDENTIFIER.new( 0, 1, 0, 'string'),
+ @TK::TkSPACE .new( 6, 1, 6, ' '),
+ @TK::TkASSIGN .new( 7, 1, 7, '='),
+ @TK::TkSPACE .new( 8, 1, 8, ' '),
+ @TK::TkSTRING .new( 9, 1, 9, %Q{"Line 1\nLine 2\n"}),
+ @TK::TkDOT .new(41, 4, 42, '.'),
+ @TK::TkIDENTIFIER.new(42, 4, 43, 'chomp'),
+ @TK::TkNL .new(47, 4, 48, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_heredoc_indent
+ tokens = RDoc::RubyLex.tokenize <<-'RUBY', nil
+string = <<-STRING
+Line 1
+Line 2
+ STRING
+ RUBY
+
+ expected = [
+ @TK::TkIDENTIFIER.new( 0, 1, 0, 'string'),
+ @TK::TkSPACE .new( 6, 1, 6, ' '),
+ @TK::TkASSIGN .new( 7, 1, 7, '='),
+ @TK::TkSPACE .new( 8, 1, 8, ' '),
+ @TK::TkHEREDOC .new( 9, 1, 9,
+ %Q{<<-STRING\nLine 1\nLine 2\n STRING}),
+ @TK::TkNL .new(41, 4, 42, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_heredoc_missing_end
+ e = assert_raises RDoc::RubyLex::Error do
+ RDoc::RubyLex.tokenize <<-'RUBY', nil
+>> string1 = <<-TXT
+>" That's swell
+>" TXT
+ RUBY
+ end
+
+ assert_equal 'Missing terminating TXT for string', e.message
+ end
+
+ def test_class_tokenize_heredoc_percent_N
+ tokens = RDoc::RubyLex.tokenize <<-'RUBY', nil
+a b <<-U
+%N
+U
+ RUBY
+
+ expected = [
+ @TK::TkIDENTIFIER.new( 0, 1, 0, 'a'),
+ @TK::TkSPACE .new( 1, 1, 1, ' '),
+ @TK::TkIDENTIFIER.new( 2, 1, 2, 'b'),
+ @TK::TkSPACE .new( 3, 1, 3, ' '),
+ @TK::TkHEREDOC .new( 4, 1, 4, %Q{<<-U\n%N\nU}),
+ @TK::TkNL .new(13, 3, 14, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_identifier_high_unicode
+ tokens = RDoc::RubyLex.tokenize '𝖒', nil
+
+ expected = @TK::TkIDENTIFIER.new(0, 1, 0, '𝖒')
+
+ assert_equal expected, tokens.first
+ end
+
+ def test_class_tokenize_percent_1
+ tokens = RDoc::RubyLex.tokenize 'v%10==10', nil
+
+ expected = [
+ @TK::TkIDENTIFIER.new(0, 1, 0, 'v'),
+ @TK::TkMOD.new( 1, 1, 1, '%'),
+ @TK::TkINTEGER.new( 2, 1, 2, '10'),
+ @TK::TkEQ.new( 4, 1, 4, '=='),
+ @TK::TkINTEGER.new( 6, 1, 6, '10'),
+ @TK::TkNL.new( 8, 1, 8, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_percent_r
+ tokens = RDoc::RubyLex.tokenize '%r[hi]', nil
+
+ expected = [
+ @TK::TkREGEXP.new( 0, 1, 0, '%r[hi]'),
+ @TK::TkNL .new( 6, 1, 6, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_percent_w
+ tokens = RDoc::RubyLex.tokenize '%w[hi]', nil
+
+ expected = [
+ @TK::TkDSTRING.new( 0, 1, 0, '%w[hi]'),
+ @TK::TkNL .new( 6, 1, 6, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_percent_w_quote
+ tokens = RDoc::RubyLex.tokenize '%w"hi"', nil
+
+ expected = [
+ @TK::TkDSTRING.new( 0, 1, 0, '%w"hi"'),
+ @TK::TkNL .new( 6, 1, 6, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_regexp
+ tokens = RDoc::RubyLex.tokenize "/hay/", nil
+
+ expected = [
+ @TK::TkREGEXP.new( 0, 1, 0, "/hay/"),
+ @TK::TkNL .new( 5, 1, 5, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_regexp_options
+ tokens = RDoc::RubyLex.tokenize "/hAY/i", nil
+
+ expected = [
+ @TK::TkREGEXP.new( 0, 1, 0, "/hAY/i"),
+ @TK::TkNL .new( 6, 1, 6, "\n"),
+ ]
+
+ assert_equal expected, tokens
+
+ tokens = RDoc::RubyLex.tokenize "/hAY/ix", nil
+
+ expected = [
+ @TK::TkREGEXP.new( 0, 1, 0, "/hAY/ix"),
+ @TK::TkNL .new( 7, 1, 7, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_regexp_backref
+ tokens = RDoc::RubyLex.tokenize "/[csh](..) [csh]\\1 in/", nil
+
+ expected = [
+ @TK::TkREGEXP.new( 0, 1, 0, "/[csh](..) [csh]\\1 in/"),
+ @TK::TkNL .new(22, 1, 22, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_regexp_escape
+ tokens = RDoc::RubyLex.tokenize "/\\//", nil
+
+ expected = [
+ @TK::TkREGEXP.new( 0, 1, 0, "/\\//"),
+ @TK::TkNL .new( 4, 1, 4, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_string
+ tokens = RDoc::RubyLex.tokenize "'hi'", nil
+
+ expected = [
+ @TK::TkSTRING.new( 0, 1, 0, "'hi'"),
+ @TK::TkNL .new( 4, 1, 4, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_class_tokenize_string_escape
+ tokens = RDoc::RubyLex.tokenize '"\\n"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\n\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\r"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\r\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\f"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\f\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\\\"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\\\\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\t"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\t\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\v"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\v\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\a"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\a\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\e"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\e\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\b"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\b\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\s"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\s\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\d"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\d\""), tokens.first
+
+ end
+
+ def test_class_tokenize_string_escape_control
+ tokens = RDoc::RubyLex.tokenize '"\\C-a"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\C-a\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\c\\a"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\c\\a\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\C-\\M-a"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\C-\\M-a\""), tokens.first
+ end
+
+ def test_class_tokenize_string_escape_meta
+ tokens = RDoc::RubyLex.tokenize '"\\M-a"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\M-a\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\M-\\C-a"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\M-\\C-a\""), tokens.first
+ end
+
+ def test_class_tokenize_string_escape_hexadecimal
+ tokens = RDoc::RubyLex.tokenize '"\\x0"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\x0\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\x00"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\x00\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\x000"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\x000\""), tokens.first
+ end
+
+ def test_class_tokenize_string_escape_octal
+ tokens = RDoc::RubyLex.tokenize '"\\0"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\0\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\00"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\00\""), tokens.first
+
+ tokens = RDoc::RubyLex.tokenize '"\\000"', nil
+ assert_equal @TK::TkSTRING.new( 0, 1, 0, "\"\\000\""), tokens.first
+ end
+
+ def test_class_tokenize_symbol
+ tokens = RDoc::RubyLex.tokenize 'scope module: :v1', nil
+
+ expected = [
+ @TK::TkIDENTIFIER.new( 0, 1, 0, 'scope'),
+ @TK::TkSPACE .new( 5, 1, 5, ' '),
+ @TK::TkIDENTIFIER.new( 6, 1, 6, 'module'),
+ @TK::TkCOLON .new(12, 1, 12, ':'),
+ @TK::TkSPACE .new(13, 1, 13, ' '),
+ @TK::TkSYMBEG .new(14, 1, 14, ':'),
+ @TK::TkIDENTIFIER.new(15, 1, 15, 'v1'),
+ @TK::TkNL .new(17, 1, 17, "\n"),
+ ]
+
+ assert_equal expected, tokens
+ end
+
+ def test_unary_minus
+ ruby_lex = RDoc::RubyLex.new("-1", nil)
+ assert_equal("-1", ruby_lex.token.value)
+
+ ruby_lex = RDoc::RubyLex.new("a[-2]", nil)
+ 2.times { ruby_lex.token } # skip "a" and "["
+ assert_equal("-2", ruby_lex.token.value)
+
+ ruby_lex = RDoc::RubyLex.new("a[0..-12]", nil)
+ 4.times { ruby_lex.token } # skip "a", "[", "0", and ".."
+ assert_equal("-12", ruby_lex.token.value)
+
+ ruby_lex = RDoc::RubyLex.new("0+-0.1", nil)
+ 2.times { ruby_lex.token } # skip "0" and "+"
+ assert_equal("-0.1", ruby_lex.token.value)
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_ruby_token.rb b/jni/ruby/test/rdoc/test_rdoc_ruby_token.rb
new file mode 100644
index 0000000..ed8c827
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_ruby_token.rb
@@ -0,0 +1,19 @@
+require 'rdoc/test_case'
+
+class TestRDocRubyToken < RDoc::TestCase
+
+ def test_Token_text
+ token = RDoc::RubyToken::Token.new 0, 0, 0, 'text'
+
+ assert_equal 'text', token.text
+ end
+
+ def test_TkOp_name
+ token = RDoc::RubyToken::TkOp.new 0, 0, 0, '&'
+
+ assert_equal '&', token.text
+ assert_equal '&', token.name
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_rubygems_hook.rb b/jni/ruby/test/rdoc/test_rdoc_rubygems_hook.rb
new file mode 100644
index 0000000..960afc9
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_rubygems_hook.rb
@@ -0,0 +1,251 @@
+require 'rubygems'
+require 'rubygems/test_case'
+require 'rdoc/rubygems_hook'
+
+class TestRDocRubygemsHook < Gem::TestCase
+
+ def setup
+ super
+
+ skip 'requires RubyGems 1.9+' unless
+ Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.9')
+
+ @a = util_spec 'a', 2 do |s|
+ s.rdoc_options = %w[--main MyTitle]
+ s.extra_rdoc_files = %w[README]
+ end
+
+ write_file File.join(@tempdir, 'lib', 'a.rb')
+ write_file File.join(@tempdir, 'README')
+
+ install_gem @a
+
+ @hook = RDoc::RubygemsHook.new @a
+
+ begin
+ RDoc::RubygemsHook.load_rdoc
+ rescue Gem::DocumentError => e
+ skip e.message
+ end
+
+ Gem.configuration[:rdoc] = nil
+ end
+
+ def test_initialize
+ refute @hook.generate_rdoc
+ assert @hook.generate_ri
+
+ rdoc = RDoc::RubygemsHook.new @a, false, false
+
+ refute rdoc.generate_rdoc
+ refute rdoc.generate_ri
+ end
+
+ def test_delete_legacy_args
+ args = %w[
+ --inline-source
+ --one-file
+ --promiscuous
+ -p
+ ]
+
+ @hook.delete_legacy_args args
+
+ assert_empty args
+ end
+
+ def test_document
+ options = RDoc::Options.new
+ options.files = []
+
+ rdoc = @hook.new_rdoc
+ rdoc.store = RDoc::Store.new
+ @hook.instance_variable_set :@rdoc, rdoc
+ @hook.instance_variable_set :@file_info, []
+
+ @hook.document 'darkfish', options, @a.doc_dir('rdoc')
+
+ assert @hook.rdoc_installed?
+ end
+
+ def test_generate
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ refute @hook.rdoc_installed?
+ assert @hook.ri_installed?
+
+ rdoc = @hook.instance_variable_get :@rdoc
+
+ refute rdoc.options.hyperlink_all
+ assert_equal Pathname(@a.full_gem_path), rdoc.options.root
+ assert_equal %w[README lib], rdoc.options.files.sort
+
+ assert_equal 'MyTitle', rdoc.store.main
+ end
+
+ def test_generate_all
+ @hook.generate_rdoc = true
+ @hook.generate_ri = true
+
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ assert @hook.rdoc_installed?
+ assert @hook.ri_installed?
+
+ rdoc = @hook.instance_variable_get :@rdoc
+
+ refute rdoc.options.hyperlink_all
+ assert_equal Pathname(@a.full_gem_path), rdoc.options.root
+ assert_equal %w[README lib], rdoc.options.files.sort
+
+ assert_equal 'MyTitle', rdoc.store.main
+ end
+
+ def test_generate_configuration_rdoc_array
+ Gem.configuration[:rdoc] = %w[-A]
+
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ rdoc = @hook.instance_variable_get :@rdoc
+
+ assert rdoc.options.hyperlink_all
+ end
+
+ def test_generate_configuration_rdoc_string
+ Gem.configuration[:rdoc] = '-A'
+
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ rdoc = @hook.instance_variable_get :@rdoc
+
+ assert rdoc.options.hyperlink_all
+ end
+
+ def test_generate_default_gem
+ skip 'RubyGems 2 required' unless @a.respond_to? :default_gem?
+ @a.loaded_from =
+ File.join Gem::Specification.default_specifications_dir, 'a.gemspec'
+
+ @hook.generate
+
+ refute @hook.rdoc_installed?
+ refute @hook.ri_installed?
+ end
+
+ def test_generate_disabled
+ @hook.generate_rdoc = false
+ @hook.generate_ri = false
+
+ @hook.generate
+
+ refute @hook.rdoc_installed?
+ refute @hook.ri_installed?
+ end
+
+ def test_generate_force
+ FileUtils.mkdir_p @a.doc_dir 'ri'
+ FileUtils.mkdir_p @a.doc_dir 'rdoc'
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.force = true
+
+ @hook.generate
+
+ refute_path_exists File.join(@a.doc_dir('rdoc'), 'index.html')
+ assert_path_exists File.join(@a.doc_dir('ri'), 'cache.ri')
+ end
+
+ def test_generate_no_overwrite
+ FileUtils.mkdir_p @a.doc_dir 'ri'
+ FileUtils.mkdir_p @a.doc_dir 'rdoc'
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ refute_path_exists File.join(@a.doc_dir('rdoc'), 'index.html')
+ refute_path_exists File.join(@a.doc_dir('ri'), 'cache.ri')
+ end
+
+ def test_new_rdoc
+ assert_kind_of RDoc::RDoc, @hook.new_rdoc
+ end
+
+ def test_rdoc_installed?
+ refute @hook.rdoc_installed?
+
+ FileUtils.mkdir_p @a.doc_dir 'rdoc'
+
+ assert @hook.rdoc_installed?
+ end
+
+ def test_remove
+ FileUtils.mkdir_p @a.doc_dir 'rdoc'
+ FileUtils.mkdir_p @a.doc_dir 'ri'
+
+ @hook.remove
+
+ refute @hook.rdoc_installed?
+ refute @hook.ri_installed?
+
+ assert_path_exists @a.doc_dir
+ end
+
+ def test_remove_unwritable
+ skip 'chmod not supported' if Gem.win_platform?
+ FileUtils.mkdir_p @a.base_dir
+ FileUtils.chmod 0, @a.base_dir
+
+ e = assert_raises Gem::FilePermissionError do
+ @hook.remove
+ end
+
+ assert_equal @a.base_dir, e.directory
+ ensure
+ FileUtils.chmod(0755, @a.base_dir) if File.directory?(@a.base_dir)
+ end
+
+ def test_ri_installed?
+ refute @hook.ri_installed?
+
+ FileUtils.mkdir_p @a.doc_dir 'ri'
+
+ assert @hook.ri_installed?
+ end
+
+ def test_setup
+ @hook.setup
+
+ assert_path_exists @a.doc_dir
+ end
+
+ def test_setup_unwritable
+ skip 'chmod not supported' if Gem.win_platform?
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.chmod 0, @a.doc_dir
+
+ e = assert_raises Gem::FilePermissionError do
+ @hook.setup
+ end
+
+ assert_equal @a.doc_dir, e.directory
+ ensure
+ if File.exist? @a.doc_dir
+ FileUtils.chmod 0755, @a.doc_dir
+ FileUtils.rm_r @a.doc_dir
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_servlet.rb b/jni/ruby/test/rdoc/test_rdoc_servlet.rb
new file mode 100644
index 0000000..143e2f2
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_servlet.rb
@@ -0,0 +1,535 @@
+require 'rdoc/test_case'
+
+class TestRDocServlet < RDoc::TestCase
+
+ def setup
+ super
+
+ @orig_gem_path = Gem.path
+
+ @tempdir = File.join Dir.tmpdir, "test_rdoc_servlet_#{$$}"
+ Gem.use_paths @tempdir
+ Gem.ensure_gem_subdirectories @tempdir
+
+ @spec = Gem::Specification.new 'spec', '1.0'
+ @spec.loaded_from = @spec.spec_file
+
+ Gem::Specification.reset
+ Gem::Specification.all = [@spec]
+
+ @server = {}
+ def @server.mount(*) end
+
+ @stores = {}
+ @cache = Hash.new { |hash, store| hash[store] = {} }
+
+ @extra_dirs = [File.join(@tempdir, 'extra1'), File.join(@tempdir, 'extra2')]
+
+ @s = RDoc::Servlet.new @server, @stores, @cache, nil, @extra_dirs
+
+ @req = WEBrick::HTTPRequest.new :Logger => nil
+ @res = WEBrick::HTTPResponse.new :HTTPVersion => '1.0'
+
+ def @req.path= path
+ instance_variable_set :@path, path
+ end
+
+ @req.instance_variable_set :@header, Hash.new { |h, k| h[k] = [] }
+
+ @base = File.join @tempdir, 'base'
+ @system_dir = File.join @tempdir, 'base', 'system'
+ @home_dir = File.join @tempdir, 'home'
+ @gem_doc_dir = File.join @tempdir, 'doc'
+
+ @orig_base = RDoc::RI::Paths::BASE
+ RDoc::RI::Paths::BASE.replace @base
+ @orig_ri_path_homedir = RDoc::RI::Paths::HOMEDIR
+ RDoc::RI::Paths::HOMEDIR.replace @home_dir
+
+ RDoc::RI::Paths.instance_variable_set \
+ :@gemdirs, %w[/nonexistent/gems/example-1.0/ri]
+ end
+
+ def teardown
+ super
+
+ Gem.use_paths(*@orig_gem_path)
+ Gem::Specification.reset
+
+ FileUtils.rm_rf @tempdir
+
+ RDoc::RI::Paths::BASE.replace @orig_base
+ RDoc::RI::Paths::HOMEDIR.replace @orig_ri_path_homedir
+ RDoc::RI::Paths.instance_variable_set :@gemdirs, nil
+ end
+
+ def test_asset
+ temp_dir do
+ now = Time.now
+
+ open 'rdoc.css', 'w' do |io| io.write 'h1 { color: red }' end
+ File.utime now, now, 'rdoc.css'
+
+ @s.asset_dirs[:darkfish] = '.'
+
+ @req.path = 'rdoc.css'
+
+ @s.asset :darkfish, @req, @res
+
+ assert_equal 'h1 { color: red }', @res.body
+ assert_equal 'text/css', @res.content_type
+ assert_equal now.httpdate, @res['last-modified']
+ end
+ end
+
+ def test_do_GET
+ touch_system_cache_path
+
+ @req.path = '/ruby/Missing.html'
+
+ @s.do_GET @req, @res
+
+ assert_equal 404, @res.status
+ end
+
+ def test_do_GET_asset_darkfish
+ temp_dir do
+ FileUtils.touch 'rdoc.css'
+
+ @s.asset_dirs[:darkfish] = '.'
+
+ @req.path = '/rdoc.css'
+
+ @s.do_GET @req, @res
+
+ assert_equal 'text/css', @res.content_type
+ end
+ end
+
+ def test_do_GET_asset_json_index
+ temp_dir do
+ FileUtils.mkdir 'js'
+ FileUtils.touch 'js/navigation.js'
+
+ @s.asset_dirs[:json_index] = '.'
+
+ @req.path = '/js/navigation.js'
+
+ @s.do_GET @req, @res
+
+ assert_equal 'application/javascript', @res.content_type
+ end
+ end
+
+ def test_do_GET_error
+ touch_system_cache_path
+
+ def @req.path() raise 'no' end
+
+ @s.do_GET @req, @res
+
+ assert_equal 500, @res.status
+ end
+
+ def test_do_GET_mount_path
+ @s = RDoc::Servlet.new @server, @stores, @cache, '/mount/path'
+
+ temp_dir do
+ FileUtils.touch 'rdoc.css'
+
+ @s.asset_dirs[:darkfish] = '.'
+
+ @req.path = '/mount/path/rdoc.css'
+
+ @s.do_GET @req, @res
+
+ assert_equal 'text/css', @res.content_type
+ end
+ end
+
+ def do_GET_not_found
+ touch_system_cache_path
+
+ @req.path = "/#{@spec.full_name}"
+
+ @s.do_GET @req, @res
+
+ assert_equal 404, @res.status
+ end
+
+ def test_do_GET_not_modified
+ touch_system_cache_path
+ @req.header['if-modified-since'] = [(Time.now + 10).httpdate]
+ @req.path = '/ruby/Missing.html'
+
+ assert_raises WEBrick::HTTPStatus::NotModified do
+ @s.do_GET @req, @res
+ end
+ end
+
+ def test_do_GET_root
+ touch_system_cache_path
+
+ @req.path = '/'
+
+ @s.do_GET @req, @res
+
+ assert_equal 'text/html', @res.content_type
+ assert_match %r%<title>Local RDoc Documentation</title>%, @res.body
+ end
+
+ def test_do_GET_root_search
+ touch_system_cache_path
+
+ @req.path = '/js/search_index.js'
+
+ @s.do_GET @req, @res
+
+ assert_equal 'application/javascript', @res.content_type
+ end
+
+ def test_documentation_page_class
+ store = RDoc::Store.new
+
+ generator = @s.generator_for store
+
+ file = store.add_file 'file.rb'
+ klass = file.add_class RDoc::NormalClass, 'Klass'
+ klass.add_class RDoc::NormalClass, 'Sub'
+
+ @s.documentation_page store, generator, 'Klass::Sub.html', @req, @res
+
+ assert_match %r%<title>class Klass::Sub - </title>%, @res.body
+ assert_match %r%<body id="top" role="document" class="class">%, @res.body
+ end
+
+ def test_documentation_page_not_found
+ store = RDoc::Store.new
+
+ generator = @s.generator_for store
+
+ @req.path = '/ruby/Missing.html'
+
+ @s.documentation_page store, generator, 'Missing.html', @req, @res
+
+ assert_equal 404, @res.status
+ end
+
+ def test_documentation_page_page
+ store = RDoc::Store.new
+
+ generator = @s.generator_for store
+
+ readme = store.add_file 'README.rdoc'
+ readme.parser = RDoc::Parser::Simple
+
+ @s.documentation_page store, generator, 'README_rdoc.html', @req, @res
+
+ assert_match %r%<title>README - </title>%, @res.body
+ assert_match %r%<body [^>]+ class="file">%, @res.body
+ end
+
+ def test_documentation_source
+ store, path = @s.documentation_source '/ruby/Object.html'
+
+ assert_equal @system_dir, store.path
+
+ assert_equal 'Object.html', path
+ end
+
+ def test_documentation_source_cached
+ cached_store = RDoc::Store.new
+
+ @stores['ruby'] = cached_store
+
+ store, path = @s.documentation_source '/ruby/Object.html'
+
+ assert_same cached_store, store
+
+ assert_equal 'Object.html', path
+ end
+
+ def test_error
+ e = RuntimeError.new 'foo'
+ e.set_backtrace caller
+
+ @s.error e, @req, @res
+
+ assert_equal 'text/html', @res.content_type
+ assert_equal 500, @res.status
+ assert_match %r%<title>Error%, @res.body
+ end
+
+ def test_generator_for
+ store = RDoc::Store.new
+ store.main = 'MAIN_PAGE.rdoc'
+ store.title = 'Title'
+
+ generator = @s.generator_for store
+
+ refute generator.file_output
+
+ assert_equal '..', generator.asset_rel_path
+
+ assert_equal 'MAIN_PAGE.rdoc', @s.options.main_page
+ assert_equal 'Title', @s.options.title
+
+ assert_kind_of RDoc::RDoc, store.rdoc
+ assert_same generator, store.rdoc.generator
+ end
+
+ def test_if_modified_since
+ skip 'File.utime on directory not supported' if Gem.win_platform?
+
+ temp_dir do
+ now = Time.now
+ File.utime now, now, '.'
+
+ @s.if_modified_since @req, @res, '.'
+
+ assert_equal now.to_i, Time.parse(@res['last-modified']).to_i
+ end
+ end
+
+ def test_if_modified_since_not_modified
+ skip 'File.utime on directory not supported' if Gem.win_platform?
+
+ temp_dir do
+ now = Time.now
+ File.utime now, now, '.'
+
+ @req.header['if-modified-since'] = [(now + 10).httpdate]
+
+ assert_raises WEBrick::HTTPStatus::NotModified do
+ @s.if_modified_since @req, @res, '.'
+ end
+
+ assert_equal now.to_i, Time.parse(@res['last-modified']).to_i
+ end
+ end
+
+ def test_installed_docs
+ touch_system_cache_path
+ touch_extra_cache_path
+
+ expected = [
+ ['My Extra Documentation', 'extra-1/', true, :extra,
+ @extra_dirs[0]],
+ ['Extra Documentation', 'extra-2/', false, :extra,
+ @extra_dirs[1]],
+ ['Ruby Documentation', 'ruby/', true, :system,
+ @system_dir],
+ ['Site Documentation', 'site/', false, :site,
+ File.join(@base, 'site')],
+ ['Home Documentation', 'home/', false, :home,
+ RDoc::RI::Paths::HOMEDIR],
+ ['spec-1.0', 'spec-1.0/', false, :gem,
+ File.join(@spec.doc_dir, 'ri')],
+ ]
+
+ assert_equal expected, @s.installed_docs
+ end
+
+ def test_not_found
+ generator = @s.generator_for RDoc::Store.new
+
+ @req.path = '/ruby/Missing.html'
+
+ @s.not_found generator, @req, @res
+
+ assert_equal 404, @res.status
+ assert_match %r%<title>Not Found</title>%, @res.body
+ assert_match %r%<kbd>/ruby/Missing\.html</kbd>%, @res.body
+ end
+
+ def test_not_found_message
+ generator = @s.generator_for RDoc::Store.new
+
+ @req.path = '/ruby/Missing.html'
+
+ @s.not_found generator, @req, @res, 'woo, this is a message'
+
+ assert_equal 404, @res.status
+ assert_match %r%<title>Not Found</title>%, @res.body
+ assert_match %r%woo, this is a message%, @res.body
+ end
+
+ def test_ri_paths
+ paths = @s.ri_paths
+
+ expected = [
+ [@extra_dirs[0], :extra],
+ [@extra_dirs[1], :extra],
+ [@system_dir, :system],
+ [File.join(@base, 'site'), :site],
+ [RDoc::RI::Paths::HOMEDIR, :home],
+ [File.join(@spec.doc_dir, 'ri'), :gem],
+ ]
+
+ assert_equal expected, paths.to_a
+ end
+
+ def test_root
+ @s.root @req, @res
+
+ assert_equal 'text/html', @res.content_type
+ assert_match %r%<title>Local RDoc Documentation</title>%, @res.body
+ end
+
+ def test_root_search
+ touch_system_cache_path
+ touch_extra_cache_path
+
+ @s.root_search @req, @res
+
+ assert_equal 'application/javascript', @res.content_type
+
+ @res.body =~ /\{.*\}/
+
+ index = JSON.parse $&
+
+ expected = {
+ 'index' => {
+ 'searchIndex' => %w[
+ My\ Extra\ Documentation
+ Ruby\ Documentation
+ ],
+ 'longSearchIndex' => %w[
+ My\ Extra\ Documentation
+ Ruby\ Documentation
+ ],
+ 'info' => [
+ ['My Extra Documentation', '', @extra_dirs[0], '',
+ 'My Extra Documentation'],
+ ['Ruby Documentation', '', 'ruby', '',
+ 'Documentation for the Ruby standard library'],
+ ],
+ }
+ }
+
+ assert_equal expected, index
+ end
+
+ def test_show_documentation_index
+ touch_system_cache_path
+
+ @req.path = '/ruby'
+
+ @s.show_documentation @req, @res
+
+ assert_equal 'text/html', @res.content_type
+ assert_match %r%<title>Standard Library Documentation%, @res.body
+ end
+
+ def test_show_documentation_table_of_contents
+ touch_system_cache_path
+
+ @req.path = '/ruby/table_of_contents.html'
+
+ @s.show_documentation @req, @res
+
+ assert_equal 'text/html', @res.content_type
+ assert_match %r%<title>Table of Contents - Standard Library Documentation%,
+ @res.body
+ end
+
+ def test_show_documentation_page
+ touch_system_cache_path
+
+ @req.path = '/ruby/Missing.html'
+
+ @s.show_documentation @req, @res
+
+ assert_equal 404, @res.status
+ end
+
+ def test_show_documentation_search_index
+ touch_system_cache_path
+
+ @req.path = '/ruby/js/search_index.js'
+
+ @s.show_documentation @req, @res
+
+ assert_equal 'application/javascript', @res.content_type
+ assert_match %r%\Avar search_data =%, @res.body
+ end
+
+ def test_store_for_gem
+ ri_dir = File.join @gem_doc_dir, 'spec-1.0', 'ri'
+ FileUtils.mkdir_p ri_dir
+ FileUtils.touch File.join ri_dir, 'cache.ri'
+
+ store = @s.store_for 'spec-1.0'
+
+ assert_equal File.join(@gem_doc_dir, 'spec-1.0', 'ri'), store.path
+ assert_equal :gem, store.type
+ end
+
+ def test_store_for_home
+ store = @s.store_for 'home'
+
+ assert_equal @home_dir, store.path
+ assert_equal :home, store.type
+ end
+
+ def test_store_for_missing_documentation
+ FileUtils.mkdir_p(File.join @gem_doc_dir, 'spec-1.0', 'ri')
+
+ e = assert_raises WEBrick::HTTPStatus::NotFound do
+ @s.store_for 'spec-1.0'
+ end
+
+ assert_equal 'Could not find documentation for "spec-1.0". Please run `gem rdoc --ri gem_name`',
+ e.message
+ end
+
+ def test_store_for_missing_gem
+ e = assert_raises WEBrick::HTTPStatus::NotFound do
+ @s.store_for 'missing'
+ end
+
+ assert_equal 'Could not find gem "missing". Are you sure you installed it?',
+ e.message
+ end
+
+ def test_store_for_ruby
+ store = @s.store_for 'ruby'
+
+ assert_equal @system_dir, store.path
+ assert_equal :system, store.type
+ end
+
+ def test_store_for_site
+ store = @s.store_for 'site'
+
+ assert_equal File.join(@base, 'site'), store.path
+ assert_equal :site, store.type
+ end
+
+ def test_store_for_extra
+ store = @s.store_for 'extra-1'
+
+ assert_equal @extra_dirs.first, store.path
+ assert_equal :extra, store.type
+ end
+
+ def touch_system_cache_path
+ store = RDoc::Store.new @system_dir
+ store.title = 'Standard Library Documentation'
+
+ FileUtils.mkdir_p File.dirname store.cache_path
+
+ store.save
+ end
+
+ def touch_extra_cache_path
+ store = RDoc::Store.new @extra_dirs.first
+ store.title = 'My Extra Documentation'
+
+ FileUtils.mkdir_p File.dirname store.cache_path
+
+ store.save
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_single_class.rb b/jni/ruby/test/rdoc/test_rdoc_single_class.rb
new file mode 100644
index 0000000..e368b3d
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_single_class.rb
@@ -0,0 +1,20 @@
+require 'rdoc/test_case'
+
+class TestRDocSingleClass < RDoc::TestCase
+
+ def setup
+ super
+
+ @c = RDoc::SingleClass.new 'C'
+ end
+
+ def test_aref_prefix
+ assert_equal 'sclass', @c.aref_prefix
+ end
+
+ def test_definition
+ assert_equal 'class << C', @c.definition
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_stats.rb b/jni/ruby/test/rdoc/test_rdoc_stats.rb
new file mode 100644
index 0000000..cd37c29
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_stats.rb
@@ -0,0 +1,722 @@
+require 'rdoc/test_case'
+
+class TestRDocStats < RDoc::TestCase
+
+ def setup
+ super
+
+ @s = RDoc::Stats.new @store, 0
+
+ @tl = @store.add_file 'file.rb'
+ @tl.parser = RDoc::Parser::Ruby
+ end
+
+ def test_doc_stats
+ c = RDoc::CodeObject.new
+
+ assert_equal [1, 1], @s.doc_stats([c])
+ end
+
+ def test_doc_stats_documented
+ c = RDoc::CodeObject.new
+ c.comment = comment 'x'
+
+ assert_equal [1, 0], @s.doc_stats([c])
+ end
+
+ def test_doc_stats_display_eh
+ c = RDoc::CodeObject.new
+ c.ignore
+
+ assert_equal [0, 0], @s.doc_stats([c])
+ end
+
+ def test_report_attr
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ a = RDoc::Attr.new nil, 'a', 'RW', nil
+ a.record_location @tl
+ c.add_attribute a
+
+ @store.complete :public
+
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ verb(
+ "class C # is documented\n",
+ "\n",
+ " attr_accessor :a # in file file.rb\n",
+ "\n",
+ "end\n"),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_attr_documented
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ a = RDoc::Attr.new nil, 'a', 'RW', 'a'
+ a.record_location @tl
+ c.add_attribute a
+
+ @store.complete :public
+
+ report = @s.report
+
+ assert_equal @s.great_job, report
+ end
+
+ def test_report_attr_line
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ a = RDoc::Attr.new nil, 'a', 'RW', nil
+ a.record_location @tl
+ a.line = 3
+ c.add_attribute a
+
+ @store.complete :public
+
+ assert_match '# in file file.rb:3', @s.report.accept(to_rdoc)
+ end
+
+ def test_report_constant
+ m = @tl.add_module RDoc::NormalModule, 'M'
+ m.record_location @tl
+ m.add_comment 'M', @tl
+
+ c = RDoc::Constant.new 'C', nil, nil
+ c.record_location @tl
+ m.add_constant c
+
+ @store.complete :public
+
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ verb(
+ "module M # is documented\n",
+ "\n",
+ " # in file file.rb\n",
+ " C = nil\n",
+ "\n",
+ "end\n"),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_constant_alias
+ mod = @tl.add_module RDoc::NormalModule, 'M'
+
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ mod.add_constant c
+
+ ca = RDoc::Constant.new 'CA', nil, nil
+ ca.is_alias_for = c
+
+ @tl.add_constant ca
+
+ @store.complete :public
+
+ report = @s.report
+
+ # TODO change this to refute match, aliases should be ignored as they are
+ # programmer convenience constructs
+ assert_match 'class Object', report.accept(to_rdoc)
+ end
+
+ def test_report_constant_documented
+ m = @tl.add_module RDoc::NormalModule, 'M'
+ m.record_location @tl
+ m.add_comment 'M', @tl
+
+ c = RDoc::Constant.new 'C', nil, 'C'
+ c.record_location @tl
+ m.add_constant c
+
+ @store.complete :public
+
+ report = @s.report
+
+ assert_equal @s.great_job, report
+ end
+
+ def test_report_constant_line
+ m = @tl.add_module RDoc::NormalModule, 'M'
+ m.record_location @tl
+ m.add_comment 'M', @tl
+
+ c = RDoc::Constant.new 'C', nil, nil
+ c.record_location @tl
+ c.line = 5
+ m.add_constant c
+
+ @store.complete :public
+
+ assert_match '# in file file.rb:5', @s.report.accept(to_rdoc)
+ end
+
+ def test_report_class
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ c.add_method m
+ m.comment = 'm'
+
+ @store.complete :public
+
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ para('In files:'),
+ list(:BULLET, *[
+ item(nil, para('file.rb'))]),
+ blank_line,
+ verb("class C\n", "end\n"),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_skip_object
+ c = @tl.add_class RDoc::NormalClass, 'Object'
+ c.record_location @tl
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ c.add_method m
+ m.comment = 'm'
+
+ @store.complete :public
+
+ refute_match %r%^class Object$%, @s.report.accept(to_rdoc)
+ end
+
+ def test_report_class_documented
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ c.add_method m
+ m.comment = 'm'
+
+ @store.complete :public
+
+ report = @s.report
+
+ assert_equal @s.great_job, report
+ end
+
+ def test_report_class_documented_level_1
+ c1 = @tl.add_class RDoc::NormalClass, 'C1'
+ c1.record_location @tl
+ c1.add_comment 'C1', @tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location @tl
+ c1.add_method m1
+ m1.comment = 'm1'
+
+ c2 = @tl.add_class RDoc::NormalClass, 'C2'
+ c2.record_location @tl
+
+ m2 = RDoc::AnyMethod.new nil, 'm2'
+ m2.record_location @tl
+ c2.add_method m2
+ m2.comment = 'm2'
+
+ @store.complete :public
+
+ @s.coverage_level = 1
+
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ para('In files:'),
+ list(:BULLET, *[
+ item(nil, para('file.rb'))]),
+ blank_line,
+ verb("class C2\n", "end\n"),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_class_empty
+ @tl.add_class RDoc::NormalClass, 'C'
+
+ @store.complete :public
+
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ para('class C is referenced but empty.'),
+ para("It probably came from another project. I'm sorry I'm holding it against you."),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_class_empty_2
+ c1 = @tl.add_class RDoc::NormalClass, 'C1'
+ c1.record_location @tl
+
+ c2 = @tl.add_class RDoc::NormalClass, 'C2'
+ c2.record_location @tl
+ c2.add_comment 'C2', @tl
+
+ @store.complete :public
+
+ @s.coverage_level = 1
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ para('In files:'),
+ list(:BULLET, *[
+ item(nil, para('file.rb'))]),
+ blank_line,
+ verb("class C1\n", "end\n"),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_class_method_documented
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ c.add_method m
+ m.comment = 'm'
+
+ @store.complete :public
+
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ para('In files:'),
+ list(:BULLET, *[
+ item(nil, para('file.rb'))]),
+ blank_line,
+ verb("class C\n", "end\n"),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_class_module_ignore
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.ignore
+
+ @store.complete :public
+
+ report = @s.report_class_module c
+
+ assert_nil report
+ end
+
+ def test_report_empty
+ @store.complete :public
+
+ report = @s.report
+
+ assert_equal @s.great_job, report
+ end
+
+ def test_report_method
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location @tl
+ c.add_method m1
+
+ m2 = RDoc::AnyMethod.new nil, 'm2'
+ m2.record_location @tl
+ c.add_method m2
+ m2.comment = 'm2'
+
+ @store.complete :public
+
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ verb(*[
+ "class C # is documented\n",
+ "\n",
+ " # in file file.rb\n",
+ " def m1; end\n",
+ "\n",
+ "end\n"]),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_method_class
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location @tl
+ m1.singleton = true
+ c.add_method m1
+
+ m2 = RDoc::AnyMethod.new nil, 'm2'
+ m2.record_location @tl
+ m2.singleton = true
+ c.add_method m2
+ m2.comment = 'm2'
+
+ @store.complete :public
+
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ verb(*[
+ "class C # is documented\n",
+ "\n",
+ " # in file file.rb\n",
+ " def self.m1; end\n",
+ "\n",
+ "end\n"]),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_method_documented
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ c.add_method m
+ m.comment = 'm'
+
+ @store.complete :public
+
+ report = @s.report
+
+ assert_equal @s.great_job, report
+ end
+
+ def test_report_method_line
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location @tl
+ m1.line = 4
+ c.add_method m1
+
+ @store.complete :public
+
+ assert_match '# in file file.rb:4', @s.report.accept(to_rdoc)
+ end
+
+ def test_report_method_parameters
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m1 = RDoc::AnyMethod.new nil, 'm1'
+ m1.record_location @tl
+ m1.params = '(p1, p2)'
+ m1.comment = 'Stuff with +p1+'
+ c.add_method m1
+
+ m2 = RDoc::AnyMethod.new nil, 'm2'
+ m2.record_location @tl
+ c.add_method m2
+ m2.comment = 'm2'
+
+ @store.complete :public
+
+ @s.coverage_level = 1
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ verb(*[
+ "class C # is documented\n",
+ "\n",
+ " # in file file.rb\n",
+ " # +p2+ is not documented\n",
+ " def m1(p1, p2); end\n",
+ "\n",
+ "end\n"]),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_report_method_parameters_documented
+ @tl.parser = RDoc::Parser::Ruby
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ m.params = '(p1)'
+ m.comment = 'Stuff with +p1+'
+ c.add_method m
+
+ @store.complete :public
+
+ @s.coverage_level = 1
+ report = @s.report
+
+ assert_equal @s.great_job, report
+ end
+
+ def test_report_method_parameters_yield
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ m.call_seq = <<-SEQ
+m(a) { |c| ... }
+m(a, b) { |c, d| ... }
+ SEQ
+ m.comment = 'Stuff with +a+, yields +c+ for you to do stuff with'
+ c.add_method m
+
+ @store.complete :public
+
+ @s.coverage_level = 1
+ report = @s.report
+
+ expected =
+ doc(
+ para('The following items are not documented:'),
+ blank_line,
+ verb(
+ "class C # is documented\n",
+ "\n",
+ " # in file file.rb\n",
+ " # +b+, +d+ is not documented\n",
+ " def m; end\n",
+ "\n",
+ "end\n"),
+ blank_line)
+
+ assert_equal expected, report
+ end
+
+ def test_summary
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+
+ m = @tl.add_module RDoc::NormalModule, 'M'
+ m.record_location @tl
+
+ a = RDoc::Attr.new nil, 'a', 'RW', nil
+ a.record_location @tl
+ c.add_attribute a
+
+ c_c = RDoc::Constant.new 'C', nil, nil
+ c_c.record_location @tl
+ c.add_constant c_c
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ c.add_method m
+
+ @store.complete :public
+
+ summary = @s.summary.accept to_rdoc
+ summary.sub!(/ Elapsed:.*/m, '')
+
+ expected = <<-EXPECTED
+ Files: 0
+
+ Classes: 1 (1 undocumented)
+ Modules: 1 (1 undocumented)
+ Constants: 1 (1 undocumented)
+ Attributes: 1 (1 undocumented)
+ Methods: 1 (1 undocumented)
+
+ Total: 5 (5 undocumented)
+ 0.00% documented
+
+ EXPECTED
+
+ assert_equal summary, expected
+ end
+
+ def test_summary_level_false
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+
+ @store.complete :public
+
+ @s.coverage_level = false
+
+ summary = @s.summary.accept to_rdoc
+ summary.sub!(/ Elapsed:.*/m, '')
+
+ expected = <<-EXPECTED
+ Files: 0
+
+ Classes: 1 (1 undocumented)
+ Modules: 0 (0 undocumented)
+ Constants: 0 (0 undocumented)
+ Attributes: 0 (0 undocumented)
+ Methods: 0 (0 undocumented)
+
+ Total: 1 (1 undocumented)
+ 0.00% documented
+
+ EXPECTED
+
+ assert_equal summary, expected
+ end
+
+ def test_summary_level_1
+ c = @tl.add_class RDoc::NormalClass, 'C'
+ c.record_location @tl
+ c.add_comment 'C', @tl
+
+ m = RDoc::AnyMethod.new nil, 'm'
+ m.record_location @tl
+ m.params = '(p1, p2)'
+ m.comment = 'Stuff with +p1+'
+ c.add_method m
+
+ @store.complete :public
+
+ @s.coverage_level = 1
+ @s.report
+
+ summary = @s.summary.accept to_rdoc
+ summary.sub!(/ Elapsed:.*/m, '')
+
+ expected = <<-EXPECTED
+ Files: 0
+
+ Classes: 1 (0 undocumented)
+ Modules: 0 (0 undocumented)
+ Constants: 0 (0 undocumented)
+ Attributes: 0 (0 undocumented)
+ Methods: 1 (0 undocumented)
+ Parameters: 2 (1 undocumented)
+
+ Total: 4 (1 undocumented)
+ 75.00% documented
+
+ EXPECTED
+
+ assert_equal summary, expected
+ end
+
+ def to_rdoc
+ RDoc::Markup::ToRdoc.new
+ end
+
+ def test_undoc_params
+ method = RDoc::AnyMethod.new [], 'm'
+ method.params = '(a)'
+ method.comment = comment 'comment'
+
+ total, undoc = @s.undoc_params method
+
+ assert_equal 1, total
+ assert_equal %w[a], undoc
+ end
+
+ def test_undoc_params_block
+ method = RDoc::AnyMethod.new [], 'm'
+ method.params = '(&a)'
+ method.comment = comment '+a+'
+
+ total, undoc = @s.undoc_params method
+
+ assert_equal 1, total
+ assert_empty undoc
+ end
+
+ def test_undoc_params_documented
+ method = RDoc::AnyMethod.new [], 'm'
+ method.params = '(a)'
+ method.comment = comment '+a+'
+
+ total, undoc = @s.undoc_params method
+
+ assert_equal 1, total
+ assert_empty undoc
+ end
+
+ def test_undoc_params_keywords
+ method = RDoc::AnyMethod.new [], 'm'
+ method.params = '(**a)'
+ method.comment = comment '+a+'
+
+ total, undoc = @s.undoc_params method
+
+ assert_equal 1, total
+ assert_empty undoc
+ end
+
+ def test_undoc_params_splat
+ method = RDoc::AnyMethod.new [], 'm'
+ method.params = '(*a)'
+ method.comment = comment '+a+'
+
+ total, undoc = @s.undoc_params method
+
+ assert_equal 1, total
+ assert_empty undoc
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_store.rb b/jni/ruby/test/rdoc/test_rdoc_store.rb
new file mode 100644
index 0000000..bd565e7
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_store.rb
@@ -0,0 +1,993 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocStore < XrefTestCase
+
+ OBJECT_ANCESTORS = defined?(::BasicObject) ? %w[BasicObject] : []
+
+ def setup
+ super
+
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_ri_store_#{$$}"
+ @s = RDoc::RI::Store.new @tmpdir
+ @s.rdoc = @rdoc
+
+ @top_level = @s.add_file 'file.rb'
+
+ @page = @s.add_file 'README.txt'
+ @page.parser = RDoc::Parser::Simple
+ @page.comment = RDoc::Comment.new 'This is a page', @page
+
+ @klass = @top_level.add_class RDoc::NormalClass, 'Object'
+ @klass.add_comment 'original', @top_level
+ @klass.record_location @top_level
+
+ @cmeth = RDoc::AnyMethod.new nil, 'cmethod'
+ @cmeth.singleton = true
+ @cmeth.record_location @top_level
+
+ @meth_comment = RDoc::Comment.new 'method comment'
+ @meth_comment.location = @top_level
+
+ @meth = RDoc::AnyMethod.new nil, 'method'
+ @meth.record_location @top_level
+ @meth.comment = @meth_comment
+
+ @meth_bang = RDoc::AnyMethod.new nil, 'method!'
+ @meth_bang.record_location @top_level
+
+ @meth_bang_alias = RDoc::Alias.new nil, 'method!', 'method_bang', ''
+ @meth_bang_alias.record_location @top_level
+
+ @meth_bang.add_alias @meth_bang_alias, @klass
+
+ @attr_comment = RDoc::Comment.new 'attribute comment'
+ @attr_comment.location = @top_level
+
+ @attr = RDoc::Attr.new nil, 'attr', 'RW', ''
+ @attr.record_location @top_level
+ @attr.comment = @attr_comment
+
+ @klass.add_method @cmeth
+ @klass.add_method @meth
+ @klass.add_method @meth_bang
+ @klass.add_attribute @attr
+
+ @nest_klass = @klass.add_class RDoc::NormalClass, 'SubClass'
+ @nest_meth = RDoc::AnyMethod.new nil, 'method'
+ @nest_meth.record_location @top_level
+
+ @nest_incl = RDoc::Include.new 'Incl', ''
+ @nest_incl.record_location @top_level
+
+ @nest_klass.add_method @nest_meth
+ @nest_klass.add_include @nest_incl
+
+ @mod = @top_level.add_module RDoc::NormalModule, 'Mod'
+ @mod.record_location @top_level
+ end
+
+ def teardown
+ super
+
+ FileUtils.rm_rf @tmpdir
+ end
+
+ def assert_cache imethods, cmethods, attrs, modules,
+ ancestors = {}, pages = [], main = nil, title = nil
+ imethods ||= { 'Object' => %w[method method! method_bang] }
+ cmethods ||= { 'Object' => %w[cmethod] }
+ attrs ||= { 'Object' => ['attr_accessor attr'] }
+
+ # this is sort-of a hack
+ @s.clean_cache_collection ancestors
+
+ expected = {
+ :ancestors => ancestors,
+ :attributes => attrs,
+ :class_methods => cmethods,
+ :c_class_variables => {},
+ :c_singleton_class_variables => {},
+ :encoding => nil,
+ :instance_methods => imethods,
+ :modules => modules,
+ :pages => pages,
+ :main => main,
+ :title => title,
+ }
+
+ @s.save_cache
+
+ assert_equal expected, @s.cache
+ end
+
+ def test_add_c_enclosure
+ @s.add_c_enclosure 'cC1', @c1
+
+ expected = { 'cC1' => @c1 }
+
+ assert_equal expected, @s.c_enclosure_classes
+ end
+
+ def test_add_c_variables
+ options = RDoc::Options.new
+
+ c_file = @s.add_file 'ext.c'
+
+ some_ext = c_file.add_class RDoc::NormalClass, 'SomeExt'
+ c_file.add_class RDoc::SingleClass, 'SomeExtSingle'
+
+ c_parser = RDoc::Parser::C.new c_file, 'ext.c', '', options, nil
+
+ c_parser.classes['cSomeExt'] = some_ext
+ c_parser.singleton_classes['s_cSomeExt'] = 'SomeExtSingle'
+
+ @s.add_c_variables c_parser
+
+ expected = { 'ext.c' => { 'cSomeExt' => 'SomeExt' } }
+
+ assert_equal expected, @s.c_class_variables
+
+ expected = { 'ext.c' => { 's_cSomeExt' => 'SomeExtSingle' } }
+
+ assert_equal expected, @s.c_singleton_class_variables
+ end
+
+ def test_add_file
+ top_level = @store.add_file 'file.rb'
+
+ assert_kind_of RDoc::TopLevel, top_level
+ assert_equal @store, top_level.store
+ assert_equal 'file.rb', top_level.name
+ assert_includes @store.all_files, top_level
+
+ assert_same top_level, @store.add_file('file.rb')
+ refute_same top_level, @store.add_file('other.rb')
+ end
+
+ def test_add_file_relative
+ top_level = @store.add_file 'path/file.rb', 'file.rb'
+
+ assert_kind_of RDoc::TopLevel, top_level
+ assert_equal @store, top_level.store
+
+ assert_equal 'path/file.rb', top_level.absolute_name
+ assert_equal 'file.rb', top_level.relative_name
+
+ assert_includes @store.all_files, top_level
+
+ assert_same top_level, @store.add_file('file.rb')
+ refute_same top_level, @store.add_file('other.rb')
+ end
+
+ def test_all_classes_and_modules
+ expected = %w[
+ C1 C2 C2::C3 C2::C3::H1 C3 C3::H1 C3::H2 C4 C4::C4 C5 C5::C1
+ Child
+ M1 M1::M2
+ Parent
+ ]
+
+ assert_equal expected,
+ @store.all_classes_and_modules.map { |m| m.full_name }.sort
+ end
+
+ def test_all_files
+ assert_equal %w[xref_data.rb],
+ @store.all_files.map { |m| m.full_name }.sort
+ end
+
+ def test_all_modules
+ assert_equal %w[M1 M1::M2],
+ @store.all_modules.map { |m| m.full_name }.sort
+ end
+
+ def test_attributes
+ @s.cache[:attributes]['Object'] = %w[attr]
+
+ expected = { 'Object' => %w[attr] }
+
+ assert_equal expected, @s.attributes
+ end
+
+ def test_class_file
+ assert_equal File.join(@tmpdir, 'Object', 'cdesc-Object.ri'),
+ @s.class_file('Object')
+ assert_equal File.join(@tmpdir, 'Object', 'SubClass', 'cdesc-SubClass.ri'),
+ @s.class_file('Object::SubClass')
+ end
+
+ def test_class_methods
+ @s.cache[:class_methods]['Object'] = %w[method]
+
+ expected = { 'Object' => %w[method] }
+
+ assert_equal expected, @s.class_methods
+ end
+
+ def test_class_path
+ assert_equal File.join(@tmpdir, 'Object'), @s.class_path('Object')
+ assert_equal File.join(@tmpdir, 'Object', 'SubClass'),
+ @s.class_path('Object::SubClass')
+ end
+
+ def test_classes
+ expected = %w[
+ C1 C2 C2::C3 C2::C3::H1 C3 C3::H1 C3::H2 C4 C4::C4 C5 C5::C1
+ Child
+ Parent
+ ]
+
+ assert_equal expected, @store.all_classes.map { |m| m.full_name }.sort
+ end
+
+ def test_complete
+ @c2.add_module_alias @c2_c3, 'A1', @top_level
+
+ @store.complete :public
+
+ a1 = @xref_data.find_class_or_module 'C2::A1'
+
+ assert_equal 'C2::A1', a1.full_name
+ refute_empty a1.aliases
+ end
+
+ def test_complete_nodoc
+ c_nodoc = @top_level.add_class RDoc::NormalClass, 'Nodoc'
+ c_nodoc.record_location @top_level
+ c_nodoc.document_self = nil
+
+ @s.complete :nodoc
+
+ assert_includes @s.classes_hash.keys, 'Nodoc'
+ end
+
+ def test_find_c_enclosure
+ assert_nil @s.find_c_enclosure 'cC1'
+
+ @s.add_c_enclosure 'cC1', @c1
+
+ assert_equal @c1, @s.find_c_enclosure('cC1')
+ end
+
+ def test_find_c_enclosure_from_cache
+ @s.save_class @klass
+ @s.classes_hash.clear
+
+ assert_nil @s.find_c_enclosure 'cObject'
+
+ @s.c_enclosure_names['cObject'] = 'Object'
+
+ klass = @s.find_c_enclosure('cObject')
+ assert_equal @klass, klass
+
+ assert_empty klass.comment_location
+ assert_equal @top_level, klass.parent
+
+ assert_includes @s.c_enclosure_classes, 'cObject'
+ end
+
+ def test_find_c_enclosure_from_cache_legacy
+ @klass.in_files.clear
+ @s.save_class @klass
+ @s.classes_hash.clear
+
+ assert_nil @s.find_c_enclosure 'cObject'
+
+ @s.c_enclosure_names['cObject'] = 'Object'
+
+ assert_nil @s.find_c_enclosure('cObject')
+ end
+
+ def test_find_class_named
+ assert_equal @c1, @store.find_class_named('C1')
+
+ assert_equal @c2_c3, @store.find_class_named('C2::C3')
+ end
+
+ def test_find_class_named_from
+ assert_equal @c5_c1, @store.find_class_named_from('C1', 'C5')
+
+ assert_equal @c1, @store.find_class_named_from('C1', 'C4')
+ end
+
+ def test_find_class_or_module
+ assert_equal @m1, @store.find_class_or_module('M1')
+ assert_equal @c1, @store.find_class_or_module('C1')
+
+ assert_equal @m1, @store.find_class_or_module('::M1')
+ assert_equal @c1, @store.find_class_or_module('::C1')
+ end
+
+ def test_find_file_named
+ assert_equal @xref_data, @store.find_file_named(@file_name)
+ end
+
+ def test_find_module_named
+ assert_equal @m1, @store.find_module_named('M1')
+ assert_equal @m1_m2, @store.find_module_named('M1::M2')
+ end
+
+ def test_find_text_page
+ page = @store.add_file 'PAGE.txt'
+ page.parser = RDoc::Parser::Simple
+
+ assert_nil @store.find_text_page 'no such page'
+
+ assert_equal page, @store.find_text_page('PAGE.txt')
+ end
+
+ def test_friendly_path
+ @s.path = @tmpdir
+ @s.type = nil
+ assert_equal @s.path, @s.friendly_path
+
+ @s.type = :extra
+ assert_equal @s.path, @s.friendly_path
+
+ @s.type = :system
+ assert_equal "ruby core", @s.friendly_path
+
+ @s.type = :site
+ assert_equal "ruby site", @s.friendly_path
+
+ @s.type = :home
+ assert_equal "~/.rdoc", @s.friendly_path
+
+ @s.type = :gem
+ @s.path = "#{@tmpdir}/gem_repository/doc/gem_name-1.0/ri"
+ assert_equal "gem gem_name-1.0", @s.friendly_path
+ end
+
+ def test_dry_run
+ refute @s.dry_run
+
+ @s.dry_run = true
+
+ assert @s.dry_run
+ end
+
+ def test_instance_methods
+ @s.cache[:instance_methods]['Object'] = %w[method]
+
+ expected = { 'Object' => %w[method] }
+
+ assert_equal expected, @s.instance_methods
+ end
+
+ def test_load_all
+ FileUtils.mkdir_p @tmpdir
+
+ @s.save
+
+ s = RDoc::Store.new @tmpdir
+
+ s.load_all
+
+ assert_equal [@klass, @nest_klass], s.all_classes.sort
+ assert_equal [@mod], s.all_modules.sort
+ assert_equal [@page, @top_level], s.all_files.sort
+
+ methods = s.all_classes_and_modules.map do |mod|
+ mod.method_list
+ end.flatten.sort
+
+ _meth_bang_alias = RDoc::AnyMethod.new nil, 'method_bang'
+ _meth_bang_alias.parent = @klass
+
+ assert_equal [@meth, @meth_bang, _meth_bang_alias, @nest_meth, @cmeth],
+ methods.sort_by { |m| m.full_name }
+
+ method = methods.find { |m| m == @meth }
+ assert_equal @meth_comment.parse, method.comment
+
+ assert_equal @klass, methods.last.parent
+
+ attributes = s.all_classes_and_modules.map do |mod|
+ mod.attributes
+ end.flatten.sort
+
+ assert_equal [@attr], attributes
+
+ assert_equal @attr_comment.parse, attributes.first.comment
+ end
+
+ def test_load_cache
+ cache = {
+ :c_class_variables =>
+ { 'file.c' => { 'cKlass' => 'Klass' } },
+ :c_singleton_class_variables =>
+ { 'file.c' => { 'sKlass' => 'KlassSingle' } },
+ :encoding => :encoding_value,
+ :methods => { "Object" => %w[Object#method] },
+ :main => @page.full_name,
+ :modules => %w[Object],
+ :pages => [],
+ }
+
+ Dir.mkdir @tmpdir
+
+ open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
+ Marshal.dump cache, io
+ end
+
+ @s.load_cache
+
+ assert_equal cache, @s.cache
+
+ assert_equal :encoding_value, @s.encoding
+ assert_equal 'README.txt', @s.main
+
+ expected = { 'file.c' => { 'cKlass' => 'Klass' } }
+ assert_equal expected, @s.cache[:c_class_variables]
+
+ expected = { 'file.c' => { 'sKlass' => 'KlassSingle' } }
+ assert_equal expected, @s.cache[:c_singleton_class_variables]
+
+ expected = { 'cKlass' => 'Klass' }
+ assert_equal expected, @s.c_enclosure_names
+ end
+
+ def test_load_cache_encoding_differs
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ cache = {
+ :c_class_variables => {},
+ :c_singleton_class_variables => {},
+ :encoding => Encoding::ISO_8859_1,
+ :main => nil,
+ :methods => { "Object" => %w[Object#method] },
+ :modules => %w[Object],
+ :pages => [],
+ }
+
+ Dir.mkdir @tmpdir
+
+ open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
+ Marshal.dump cache, io
+ end
+
+ @s.encoding = Encoding::UTF_8
+
+ @s.load_cache
+
+ assert_equal cache, @s.cache
+
+ assert_equal Encoding::UTF_8, @s.encoding
+ end
+
+ def test_load_cache_no_cache
+ cache = {
+ :ancestors => {},
+ :attributes => {},
+ :class_methods => {},
+ :c_class_variables => {},
+ :c_singleton_class_variables => {},
+ :encoding => nil,
+ :instance_methods => {},
+ :main => nil,
+ :modules => [],
+ :pages => [],
+ :title => nil,
+ }
+
+ @s.load_cache
+
+ assert_equal cache, @s.cache
+ end
+
+ def test_load_cache_legacy
+ cache = {
+ :ancestors => {},
+ :attributes => {},
+ :class_methods => {},
+ :encoding => :encoding_value,
+ :instance_methods => { "Object" => %w[Object#method] },
+ :modules => %w[Object],
+ # no :pages
+ # no :main
+ # no :c_class_variables
+ # no :c_singleton_class_variables
+ }
+
+ Dir.mkdir @tmpdir
+
+ open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
+ Marshal.dump cache, io
+ end
+
+ @s.load_cache
+
+ expected = {
+ :ancestors => {},
+ :attributes => {},
+ :class_methods => {},
+ :c_class_variables => {},
+ :c_singleton_class_variables => {},
+ :encoding => :encoding_value,
+ :instance_methods => { "Object" => %w[Object#method] },
+ :main => nil,
+ :modules => %w[Object],
+ :pages => [],
+ }
+
+ assert_equal expected, @s.cache
+
+ assert_equal :encoding_value, @s.encoding
+ assert_nil @s.main
+ end
+
+ def test_load_class
+ @s.save_class @klass
+ @s.classes_hash.clear
+
+ assert_equal @klass, @s.load_class('Object')
+
+ assert_includes @s.classes_hash, 'Object'
+ end
+
+ def test_load_method
+ @s.save_method @klass, @meth_bang
+
+ meth = @s.load_method('Object', '#method!')
+ assert_equal @meth_bang, meth
+ assert_equal @klass, meth.parent
+ assert_equal @s, meth.store
+ end
+
+ def test_load_method_legacy
+ @s.save_method @klass, @meth
+
+ file = @s.method_file @klass.full_name, @meth.full_name
+
+ open file, 'wb' do |io|
+ io.write "\x04\bU:\x14RDoc::AnyMethod[\x0Fi\x00I" +
+ "\"\vmethod\x06:\x06EF\"\x11Klass#method0:\vpublic" +
+ "o:\eRDoc::Markup::Document\x06:\v@parts[\x06" +
+ "o:\x1CRDoc::Markup::Paragraph\x06;\t[\x06I" +
+ "\"\x16this is a comment\x06;\x06FI" +
+ "\"\rcall_seq\x06;\x06FI\"\x0Fsome_block\x06;\x06F" +
+ "[\x06[\aI\"\faliased\x06;\x06Fo;\b\x06;\t[\x06" +
+ "o;\n\x06;\t[\x06I\"\x12alias comment\x06;\x06FI" +
+ "\"\nparam\x06;\x06F"
+ end
+
+ meth = @s.load_method('Object', '#method')
+ assert_equal 'Klass#method', meth.full_name
+ assert_equal @klass, meth.parent
+ assert_equal @s, meth.store
+ end
+
+ def test_load_page
+ @s.save_page @page
+
+ assert_equal @page, @s.load_page('README.txt')
+ end
+
+ def test_main
+ assert_equal nil, @s.main
+
+ @s.main = 'README.txt'
+
+ assert_equal 'README.txt', @s.main
+ end
+
+ def test_method_file
+ assert_equal File.join(@tmpdir, 'Object', 'method-i.ri'),
+ @s.method_file('Object', 'Object#method')
+
+ assert_equal File.join(@tmpdir, 'Object', 'method%21-i.ri'),
+ @s.method_file('Object', 'Object#method!')
+
+ assert_equal File.join(@tmpdir, 'Object', 'SubClass', 'method%21-i.ri'),
+ @s.method_file('Object::SubClass', 'Object::SubClass#method!')
+
+ assert_equal File.join(@tmpdir, 'Object', 'method-c.ri'),
+ @s.method_file('Object', 'Object::method')
+ end
+
+ def test_module_names
+ @s.save_class @klass
+
+ assert_equal %w[Object], @s.module_names
+ end
+
+ def test_page
+ page = @store.add_file 'PAGE.txt'
+ page.parser = RDoc::Parser::Simple
+
+ assert_nil @store.page 'no such page'
+
+ assert_equal page, @store.page('PAGE')
+ end
+
+ def test_save
+ FileUtils.mkdir_p @tmpdir
+
+ @s.save
+
+ assert_directory File.join(@tmpdir, 'Object')
+
+ assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
+ assert_file File.join(@tmpdir, 'Object', 'method-i.ri')
+ assert_file File.join(@tmpdir, 'page-README_txt.ri')
+
+ assert_file File.join(@tmpdir, 'cache.ri')
+
+ expected = {
+ :ancestors => {
+ 'Object::SubClass' => %w[Incl Object],
+ },
+ :attributes => { 'Object' => ['attr_accessor attr'] },
+ :class_methods => { 'Object' => %w[cmethod] },
+ :c_class_variables => {},
+ :c_singleton_class_variables => {},
+ :instance_methods => {
+ 'Object' => %w[attr method method! method_bang],
+ 'Object::SubClass' => %w[method],
+ },
+ :main => nil,
+ :modules => %w[Mod Object Object::SubClass],
+ :encoding => nil,
+ :pages => %w[README.txt],
+ :title => nil,
+ }
+
+ expected[:ancestors]['Object'] = %w[BasicObject] if defined?(::BasicObject)
+
+ open File.join(@tmpdir, 'cache.ri'), 'rb' do |io|
+ cache = Marshal.load io.read
+
+ assert_equal expected, cache
+ end
+ end
+
+ def test_save_cache
+ @s.save_class @klass
+ @s.save_method @klass, @meth
+ @s.save_method @klass, @cmeth
+ @s.save_class @nest_klass
+ @s.save_page @page
+ @s.encoding = :encoding_value
+ @s.main = @page.full_name
+ @s.title = 'title'
+
+ options = RDoc::Options.new
+
+ c_file = @s.add_file 'ext.c'
+
+ some_ext = c_file.add_class RDoc::NormalClass, 'SomeExt'
+ c_file.add_class RDoc::SingleClass, 'SomeExtSingle'
+
+ c_parser = RDoc::Parser::C.new c_file, 'ext.c', '', options, nil
+
+ c_parser.classes['cSomeExt'] = some_ext
+ c_parser.singleton_classes['s_cSomeExt'] = 'SomeExtSingle'
+
+ @s.add_c_variables c_parser
+
+ @s.save_cache
+
+ assert_file File.join(@tmpdir, 'cache.ri')
+
+ c_class_variables = {
+ 'ext.c' => {
+ 'cSomeExt' => 'SomeExt'
+ }
+ }
+
+ c_singleton_class_variables = {
+ 'ext.c' => {
+ 's_cSomeExt' => 'SomeExtSingle'
+ }
+ }
+
+ expected = {
+ :ancestors => {
+ 'Object::SubClass' => %w[Incl Object],
+ },
+ :attributes => { 'Object' => ['attr_accessor attr'] },
+ :class_methods => { 'Object' => %w[cmethod] },
+ :c_class_variables => c_class_variables,
+ :c_singleton_class_variables => c_singleton_class_variables,
+ :instance_methods => {
+ 'Object' => %w[method method! method_bang],
+ 'Object::SubClass' => %w[method],
+ },
+ :main => @page.full_name,
+ :modules => %w[Object Object::SubClass],
+ :encoding => :encoding_value,
+ :pages => %w[README.txt],
+ :title => 'title',
+ }
+
+ expected[:ancestors]['Object'] = %w[BasicObject] if defined?(::BasicObject)
+
+ open File.join(@tmpdir, 'cache.ri'), 'rb' do |io|
+ cache = Marshal.load io.read
+
+ assert_equal expected, cache
+ end
+ end
+
+ def test_save_cache_dry_run
+ @s.dry_run = true
+
+ @s.save_class @klass
+ @s.save_method @klass, @meth
+ @s.save_method @klass, @cmeth
+ @s.save_class @nest_klass
+
+ @s.save_cache
+
+ refute_file File.join(@tmpdir, 'cache.ri')
+ end
+
+ def test_save_cache_duplicate_methods
+ @s.save_method @klass, @meth
+ @s.save_method @klass, @meth
+
+ @s.save_cache
+
+ assert_cache({ 'Object' => %w[method] }, {}, {}, [])
+ end
+
+ def test_save_cache_duplicate_pages
+ @s.save_page @page
+ @s.save_page @page
+
+ @s.save_cache
+
+ assert_cache({}, {}, {}, [], {}, %w[README.txt])
+ end
+
+ def test_save_class
+ @s.save_class @klass
+
+ assert_directory File.join(@tmpdir, 'Object')
+ assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
+
+ assert_cache nil, nil, nil, %w[Object], 'Object' => OBJECT_ANCESTORS
+
+ assert_equal @klass, @s.load_class('Object')
+ end
+
+ def test_save_class_basic_object
+ @klass.instance_variable_set :@superclass, nil
+
+ @s.save_class @klass
+
+ assert_directory File.join(@tmpdir, 'Object')
+ assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
+
+ assert_cache(nil, nil, nil, %w[Object])
+
+ assert_equal @klass, @s.load_class('Object')
+ end
+
+ def test_save_class_delete
+ # save original
+ @s.save_class @klass
+ @s.save_method @klass, @meth
+ @s.save_method @klass, @meth_bang
+ @s.save_method @klass, @cmeth
+ @s.save_method @klass, @attr
+ @s.save_cache
+
+ klass = RDoc::NormalClass.new 'Object'
+
+ meth = klass.add_method RDoc::AnyMethod.new(nil, 'replace')
+ meth.record_location @top_level
+
+ # load original, save newly updated class
+ @s = RDoc::RI::Store.new @tmpdir
+ @s.load_cache
+ @s.save_class klass
+ @s.save_cache
+
+ # load from disk again
+ @s = RDoc::RI::Store.new @tmpdir
+ @s.load_cache
+
+ @s.load_class 'Object'
+
+ assert_cache({ 'Object' => %w[replace] }, {},
+ { 'Object' => %w[attr_accessor\ attr] }, %w[Object],
+ 'Object' => OBJECT_ANCESTORS)
+
+ # assert these files were deleted
+ refute_file @s.method_file(@klass.full_name, @meth.full_name)
+ refute_file @s.method_file(@klass.full_name, @meth_bang.full_name)
+ refute_file @s.method_file(@klass.full_name, @cmeth.full_name)
+
+ # assert these files were not deleted
+ assert_file @s.method_file(@klass.full_name, @attr.full_name)
+ end
+
+ def test_save_class_dry_run
+ @s.dry_run = true
+
+ @s.save_class @klass
+
+ refute_file File.join(@tmpdir, 'Object')
+ refute_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
+ end
+
+ def test_save_class_loaded
+ @s.save
+
+ assert_directory File.join(@tmpdir, 'Object')
+ assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
+
+ assert_file @s.method_file(@klass.full_name, @attr.full_name)
+ assert_file @s.method_file(@klass.full_name, @cmeth.full_name)
+ assert_file @s.method_file(@klass.full_name, @meth.full_name)
+ assert_file @s.method_file(@klass.full_name, @meth_bang.full_name)
+
+ s = RDoc::Store.new @s.path
+ s.load_cache
+
+ loaded = s.load_class 'Object'
+
+ assert_equal @klass, loaded
+
+ s.save_class loaded
+
+ s = RDoc::Store.new @s.path
+ s.load_cache
+
+ reloaded = s.load_class 'Object'
+
+ assert_equal @klass, reloaded
+
+ # assert these files were not deleted. Bug #171
+ assert_file s.method_file(@klass.full_name, @attr.full_name)
+ assert_file s.method_file(@klass.full_name, @cmeth.full_name)
+ assert_file s.method_file(@klass.full_name, @meth.full_name)
+ assert_file s.method_file(@klass.full_name, @meth_bang.full_name)
+ end
+
+ def test_save_class_merge
+ @s.save_class @klass
+
+ klass = RDoc::NormalClass.new 'Object'
+ klass.add_comment 'new comment', @top_level
+
+ s = RDoc::RI::Store.new @tmpdir
+ s.save_class klass
+
+ s = RDoc::RI::Store.new @tmpdir
+
+ inner = @RM::Document.new @RM::Paragraph.new 'new comment'
+ inner.file = @top_level
+
+ document = @RM::Document.new inner
+
+ assert_equal document, s.load_class('Object').comment_location
+ end
+
+ # This is a functional test
+ def test_save_class_merge_constant
+ store = RDoc::Store.new
+ tl = store.add_file 'file.rb'
+
+ klass = tl.add_class RDoc::NormalClass, 'C'
+ klass.add_comment 'comment', tl
+
+ const = klass.add_constant RDoc::Constant.new('CONST', nil, nil)
+ const.record_location tl
+
+ @s.save_class klass
+
+ # separate parse run, independent store
+ store = RDoc::Store.new
+ tl = store.add_file 'file.rb'
+ klass2 = tl.add_class RDoc::NormalClass, 'C'
+ klass2.record_location tl
+
+ s = RDoc::RI::Store.new @tmpdir
+ s.save_class klass2
+
+ # separate `ri` run, independent store
+ s = RDoc::RI::Store.new @tmpdir
+
+ result = s.load_class 'C'
+
+ assert_empty result.constants
+ end
+
+ def test_save_class_methods
+ @s.save_class @klass
+
+ assert_directory File.join(@tmpdir, 'Object')
+ assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
+
+ assert_cache nil, nil, nil, %w[Object], 'Object' => OBJECT_ANCESTORS
+
+ assert_equal @klass, @s.load_class('Object')
+ end
+
+ def test_save_class_nested
+ @s.save_class @nest_klass
+
+ assert_directory File.join(@tmpdir, 'Object', 'SubClass')
+ assert_file File.join(@tmpdir, 'Object', 'SubClass', 'cdesc-SubClass.ri')
+
+ assert_cache({ 'Object::SubClass' => %w[method] }, {}, {},
+ %w[Object::SubClass], 'Object::SubClass' => %w[Incl Object])
+ end
+
+ def test_save_method
+ @s.save_method @klass, @meth
+
+ assert_directory File.join(@tmpdir, 'Object')
+ assert_file File.join(@tmpdir, 'Object', 'method-i.ri')
+
+ assert_cache({ 'Object' => %w[method] }, {}, {}, [])
+
+ assert_equal @meth, @s.load_method('Object', '#method')
+ end
+
+ def test_save_method_dry_run
+ @s.dry_run = true
+
+ @s.save_method @klass, @meth
+
+ refute_file File.join(@tmpdir, 'Object')
+ refute_file File.join(@tmpdir, 'Object', 'method-i.ri')
+ end
+
+ def test_save_method_nested
+ @s.save_method @nest_klass, @nest_meth
+
+ assert_directory File.join(@tmpdir, 'Object', 'SubClass')
+ assert_file File.join(@tmpdir, 'Object', 'SubClass', 'method-i.ri')
+
+ assert_cache({ 'Object::SubClass' => %w[method] }, {}, {}, [])
+ end
+
+ def test_save_page
+ @s.save_page @page
+
+ assert_file File.join(@tmpdir, 'page-README_txt.ri')
+
+ assert_cache({}, {}, {}, [], {}, %w[README.txt])
+ end
+
+ def test_save_page_file
+ @s.save_page @top_level
+
+ refute_file File.join(@tmpdir, 'page-file_rb.ri')
+ end
+
+ def test_source
+ @s.path = @tmpdir
+ @s.type = nil
+ assert_equal @s.path, @s.source
+
+ @s.type = :extra
+ assert_equal @s.path, @s.source
+
+ @s.type = :system
+ assert_equal "ruby", @s.source
+
+ @s.type = :site
+ assert_equal "site", @s.source
+
+ @s.type = :home
+ assert_equal "home", @s.source
+
+ @s.type = :gem
+ @s.path = "#{@tmpdir}/gem_repository/doc/gem_name-1.0/ri"
+ assert_equal "gem_name-1.0", @s.source
+ end
+
+ def test_title
+ assert_equal nil, @s.title
+
+ @s.title = 'rdoc'
+
+ assert_equal 'rdoc', @s.title
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_task.rb b/jni/ruby/test/rdoc/test_rdoc_task.rb
new file mode 100644
index 0000000..43d4589
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_task.rb
@@ -0,0 +1,170 @@
+require 'rdoc/test_case'
+require 'rake'
+
+class TestRDocTask < RDoc::TestCase
+
+ def setup
+ super
+
+ Rake::Task.clear
+
+ @t = RDoc::Task.new
+ end
+
+ def test_clobber_task_description
+ assert_equal 'Remove RDoc HTML files', @t.clobber_task_description
+ end
+
+ def test_inline_source
+ _, err = verbose_capture_io do
+ assert @t.inline_source
+ end
+
+ assert_equal "RDoc::Task#inline_source is deprecated\n", err
+
+ _, err = verbose_capture_io do
+ @t.inline_source = false
+ end
+
+ assert_equal "RDoc::Task#inline_source is deprecated\n", err
+
+ capture_io do
+ assert @t.inline_source
+ end
+ end
+
+ def test_markup_option
+ rdoc_task = RDoc::Task.new do |rd|
+ rd.markup = "tomdoc"
+ end
+
+ assert_equal %w[-o html --markup tomdoc], rdoc_task.option_list
+ end
+
+ def test_tasks_creation
+ RDoc::Task.new
+ assert Rake::Task[:rdoc]
+ assert Rake::Task[:clobber_rdoc]
+ assert Rake::Task[:rerdoc]
+ assert_equal ["html/created.rid"], Rake::Task[:rdoc].prerequisites
+ end
+
+ def test_tasks_creation_with_custom_name_symbol
+ rd = RDoc::Task.new(:rdoc_dev)
+ assert Rake::Task[:rdoc_dev]
+ assert Rake::Task[:clobber_rdoc_dev]
+ assert Rake::Task[:rerdoc_dev]
+ assert_equal :rdoc_dev, rd.name
+ end
+
+ def test_tasks_option_parser
+ rdoc_task = RDoc::Task.new do |rd|
+ rd.title = "Test Tasks Option Parser"
+ rd.main = "README.md"
+ rd.rdoc_files.include("README.md")
+ rd.options << "--all"
+ end
+
+ assert rdoc_task.title, "Test Tasks Option Parser"
+ assert rdoc_task.main, "README.md"
+ assert rdoc_task.rdoc_files.include?("README.md")
+ assert rdoc_task.options.include?("--all")
+
+ args = %w[--all -o html --main README.md] << "--title" << "Test Tasks Option Parser" << "README.md"
+ assert_equal args, rdoc_task.option_list + rdoc_task.rdoc_files
+ end
+
+ def test_generator_option
+ rdoc_task = RDoc::Task.new do |rd|
+ rd.generator = "ri"
+ end
+
+ assert_equal %w[-o html -f ri], rdoc_task.option_list
+ end
+
+ def test_main_option
+ rdoc_task = RDoc::Task.new do |rd|
+ rd.main = "README.md"
+ end
+
+ assert_equal %w[-o html --main README.md], rdoc_task.option_list
+ end
+
+ def test_output_dir_option
+ rdoc_task = RDoc::Task.new do |rd|
+ rd.rdoc_dir = "zomg"
+ end
+
+ assert_equal %w[-o zomg], rdoc_task.option_list
+ end
+
+ def test_rdoc_task_description
+ assert_equal 'Build RDoc HTML files', @t.rdoc_task_description
+ end
+
+ def test_rerdoc_task_description
+ assert_equal 'Rebuild RDoc HTML files', @t.rerdoc_task_description
+ end
+
+ def test_tasks_creation_with_custom_name_string
+ rd = RDoc::Task.new("rdoc_dev")
+ assert Rake::Task[:rdoc_dev]
+ assert Rake::Task[:clobber_rdoc_dev]
+ assert Rake::Task[:rerdoc_dev]
+ assert_equal "rdoc_dev", rd.name
+ end
+
+ def test_tasks_creation_with_custom_name_hash
+ options = {
+ :rdoc => "rdoc",
+ :clobber_rdoc => "rdoc:clean",
+ :rerdoc => "rdoc:force"
+ }
+
+ Rake::Task.clear
+
+ rd = RDoc::Task.new(options)
+ assert Rake::Task[:"rdoc"]
+ assert Rake::Task[:"rdoc:clean"]
+ assert Rake::Task[:"rdoc:force"]
+ assert_raises(RuntimeError) { Rake::Task[:clobber_rdoc] }
+ assert_equal options, rd.name
+ end
+
+ def test_tasks_creation_with_custom_name_hash_will_use_default_if_an_option_isnt_given
+ RDoc::Task.new(:clobber_rdoc => "rdoc:clean")
+ assert Rake::Task[:rdoc]
+ assert Rake::Task[:"rdoc:clean"]
+ assert Rake::Task[:rerdoc]
+ end
+
+ def test_tasks_creation_with_custom_name_hash_raises_exception_if_invalid_option_given
+ assert_raises(ArgumentError) do
+ RDoc::Task.new(:foo => "bar")
+ end
+
+ begin
+ RDoc::Task.new(:foo => "bar")
+ rescue ArgumentError => e
+ assert_match(/foo/, e.message)
+ end
+ end
+
+ def test_template_option
+ rdoc_task = RDoc::Task.new do |rd|
+ rd.template = "foo"
+ end
+
+ assert_equal %w[-o html -T foo], rdoc_task.option_list
+ end
+
+ def test_title_option
+ rdoc_task = RDoc::Task.new do |rd|
+ rd.title = "Test Title Option"
+ end
+
+ assert_equal %w[-o html] << "--title" << "Test Title Option", rdoc_task.option_list
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_text.rb b/jni/ruby/test/rdoc/test_rdoc_text.rb
new file mode 100644
index 0000000..a69989d
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_text.rb
@@ -0,0 +1,557 @@
+# coding: utf-8
+
+require 'rdoc/test_case'
+
+class TestRDocText < RDoc::TestCase
+
+ include RDoc::Text
+
+ def setup
+ super
+
+ @options = RDoc::Options.new
+
+ @top_level = @store.add_file 'file.rb'
+ end
+
+ def test_self_encode_fallback
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ assert_equal '…',
+ RDoc::Text::encode_fallback('…', Encoding::UTF_8, '...')
+ assert_equal '...',
+ RDoc::Text::encode_fallback('…', Encoding::US_ASCII, '...')
+ end
+
+ def test_expand_tabs
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n dave"), 'spaces')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n\tdave"), 'tab')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n \tdave"), '1 space tab')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n \tdave"), '2 space tab')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n \tdave"), '3 space tab')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n \tdave"), '4 space tab')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n \tdave"), '5 space tab')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n \tdave"), '6 space tab')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n \tdave"), '7 space tab')
+
+ assert_equal("hello\n dave",
+ expand_tabs("hello\n \tdave"), '8 space tab')
+
+ assert_equal('. .',
+ expand_tabs(".\t\t."), 'dot tab tab dot')
+
+ assert_equal('a a',
+ Timeout.timeout(1) {expand_tabs("\ra\ta")}, "carriage return")
+ end
+
+ def test_expand_tabs_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ inn = "hello\ns\tdave"
+ inn.force_encoding Encoding::BINARY
+
+ out = expand_tabs inn
+
+ assert_equal "hello\ns dave", out
+ assert_equal Encoding::BINARY, out.encoding
+ end
+
+ def test_flush_left
+ text = <<-TEXT
+
+ we don't worry too much.
+
+ The comments associated with
+ TEXT
+
+ expected = <<-EXPECTED
+
+we don't worry too much.
+
+The comments associated with
+ EXPECTED
+
+ assert_equal expected, flush_left(text)
+ end
+
+ def test_flush_left_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ text = <<-TEXT
+
+ we don't worry too much.
+
+ The comments associated with
+ TEXT
+
+ text.force_encoding Encoding::US_ASCII
+
+ expected = <<-EXPECTED
+
+we don't worry too much.
+
+The comments associated with
+ EXPECTED
+
+ result = flush_left text
+
+ assert_equal expected, result
+ assert_equal Encoding::US_ASCII, result.encoding
+ end
+
+ def test_markup_string
+ out = markup('hi').gsub("\n", '')
+
+ assert_equal '<p>hi</p>', out
+ end
+
+ def test_markup_comment
+ out = markup(comment('hi')).gsub("\n", '')
+
+ assert_equal '<p>hi</p>', out
+ end
+
+ def test_normalize_comment_hash
+ text = <<-TEXT
+##
+# we don't worry too much.
+#
+# The comments associated with
+ TEXT
+
+ expected = <<-EXPECTED.rstrip
+we don't worry too much.
+
+The comments associated with
+ EXPECTED
+
+ assert_equal expected, normalize_comment(text)
+ end
+
+ def test_normalize_comment_stars_single_space
+ text = <<-TEXT
+/*
+ * we don't worry too much.
+ *
+ * The comments associated with
+ */
+ TEXT
+
+ expected = <<-EXPECTED.rstrip
+we don't worry too much.
+
+The comments associated with
+ EXPECTED
+
+ assert_equal expected, normalize_comment(text)
+ end
+
+ def test_normalize_comment_stars_single_double_space
+ text = <<-TEXT
+/*
+ * we don't worry too much.
+ *
+ * The comments associated with
+ */
+ TEXT
+
+ expected = <<-EXPECTED.rstrip
+we don't worry too much.
+
+The comments associated with
+ EXPECTED
+
+ assert_equal expected, normalize_comment(text)
+ end
+
+ def test_parse
+ assert_kind_of RDoc::Markup::Document, parse('hi')
+ end
+
+ def test_parse_comment
+ expected = RDoc::Markup::Document.new
+ expected.file = @top_level
+
+ c = comment ''
+ parsed = parse c
+
+ assert_equal expected, parsed
+ assert_same parsed, parse(c)
+ end
+
+ def test_parse_document
+ assert_equal RDoc::Markup::Document.new, parse(RDoc::Markup::Document.new)
+ end
+
+ def test_parse_empty
+ assert_equal RDoc::Markup::Document.new, parse('')
+ end
+
+ def test_parse_empty_newline
+ assert_equal RDoc::Markup::Document.new, parse("#\n")
+ end
+
+ def test_parse_format_markdown
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('it _works_'))
+
+ parsed = parse 'it *works*', 'markdown'
+
+ assert_equal expected, parsed
+ end
+
+ def test_parse_format_rd
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('it <em>works</em>'))
+
+ parsed = parse 'it ((*works*))', 'rd'
+
+ assert_equal expected, parsed
+ end
+
+ def test_parse_format_tomdoc
+ code = verb('1 + 1')
+ code.format = :ruby
+
+ expected =
+ doc(
+ para('It does a thing'),
+ blank_line,
+ head(3, 'Examples'),
+ blank_line,
+ code)
+
+ text = <<-TOMDOC
+It does a thing
+
+Examples
+
+ 1 + 1
+ TOMDOC
+
+ parsed = parse text, 'tomdoc'
+
+ assert_equal expected, parsed
+ end
+
+ def test_parse_newline
+ assert_equal RDoc::Markup::Document.new, parse("\n")
+ end
+
+ def test_snippet
+ text = <<-TEXT
+This is one-hundred characters or more of text in a single paragraph. This
+paragraph will be cut off some point after the one-hundredth character.
+ TEXT
+
+ expected = <<-EXPECTED
+<p>This is one-hundred characters or more of text in a single paragraph. This
+paragraph will be cut off …
+ EXPECTED
+
+ assert_equal expected, snippet(text)
+ end
+
+ def test_snippet_comment
+ c = comment 'This is a comment'
+
+ assert_equal "<p>This is a comment\n", snippet(c)
+ end
+
+ def test_snippet_short
+ text = 'This is a comment'
+
+ assert_equal "<p>#{text}\n", snippet(text)
+ end
+
+ def test_strip_hashes
+ text = <<-TEXT
+##
+# we don't worry too much.
+#
+# The comments associated with
+ TEXT
+
+ expected = <<-EXPECTED
+
+ we don't worry too much.
+
+ The comments associated with
+ EXPECTED
+
+ assert_equal expected, strip_hashes(text)
+ end
+
+ def test_strip_hashes_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ text = <<-TEXT
+##
+# we don't worry too much.
+#
+# The comments associated with
+ TEXT
+
+ text.force_encoding Encoding::CP852
+
+ expected = <<-EXPECTED
+
+ we don't worry too much.
+
+ The comments associated with
+ EXPECTED
+
+ stripped = strip_hashes text
+
+ assert_equal expected, stripped
+ assert_equal Encoding::CP852, stripped.encoding
+ end
+
+ def test_strip_newlines
+ assert_equal ' ', strip_newlines("\n \n")
+
+ assert_equal 'hi', strip_newlines("\n\nhi")
+
+ assert_equal 'hi', strip_newlines( "hi\n\n")
+
+ assert_equal 'hi', strip_newlines("\n\nhi\n\n")
+ end
+
+ def test_strip_newlines_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ assert_equal Encoding::UTF_8, ''.encoding, 'Encoding sanity check'
+
+ text = " \n"
+ text.force_encoding Encoding::US_ASCII
+
+ stripped = strip_newlines text
+
+ assert_equal ' ', stripped
+
+ assert_equal Encoding::US_ASCII, stripped.encoding
+ end
+
+ def test_strip_stars
+ text = <<-TEXT
+/*
+ * * we don't worry too much.
+ *
+ * The comments associated with
+ */
+ TEXT
+
+ expected = <<-EXPECTED
+
+ * we don't worry too much.
+
+ The comments associated with
+ EXPECTED
+
+ assert_equal expected, strip_stars(text)
+ end
+
+ def test_strip_stars_document_method
+ text = <<-TEXT
+/*
+ * Document-method: Zlib::GzipFile#mtime=
+ *
+ * A comment
+ */
+ TEXT
+
+ expected = <<-EXPECTED
+
+ A comment
+ EXPECTED
+
+ assert_equal expected, strip_stars(text)
+ end
+
+ def test_strip_stars_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ text = <<-TEXT
+/*
+ * * we don't worry too much.
+ *
+ * The comments associated with
+ */
+ TEXT
+
+ text.force_encoding Encoding::CP852
+
+ expected = <<-EXPECTED
+
+ * we don't worry too much.
+
+ The comments associated with
+ EXPECTED
+
+ result = strip_stars text
+
+ assert_equal expected, result
+ assert_equal Encoding::CP852, result.encoding
+ end
+
+ def test_strip_stars_encoding2
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ text = <<-TEXT
+/*
+ * * we don't worry too much.
+ *
+ * The comments associated with
+ */
+ TEXT
+
+ text.force_encoding Encoding::BINARY
+
+ expected = <<-EXPECTED
+
+ * we don't worry too much.
+
+ The comments associated with
+ EXPECTED
+
+ result = strip_stars text
+
+ assert_equal expected, result
+ assert_equal Encoding::BINARY, result.encoding
+ end
+
+ def test_strip_stars_no_stars
+ text = <<-TEXT
+* we don't worry too much.
+
+The comments associated with
+
+ TEXT
+
+ expected = <<-EXPECTED
+* we don't worry too much.
+
+The comments associated with
+
+ EXPECTED
+
+ assert_equal expected, strip_stars(text)
+ end
+
+ def test_to_html_apostrophe
+ assert_equal '‘a', to_html("'a")
+ assert_equal 'a’', to_html("a'")
+
+ assert_equal '‘a’ ‘', to_html("'a' '")
+ end
+
+ def test_to_html_backslash
+ assert_equal 'S', to_html('\\S')
+ end
+
+ def test_to_html_br
+ assert_equal '<br>', to_html('<br>')
+ end
+
+ def test_to_html_copyright
+ assert_equal '©', to_html('(c)')
+ end
+
+ def test_to_html_dash
+ assert_equal '-', to_html('-')
+ assert_equal '–', to_html('--')
+ assert_equal '—', to_html('---')
+ assert_equal '—-', to_html('----')
+ end
+
+ def test_to_html_double_backtick
+ assert_equal '“a', to_html('``a')
+ assert_equal '“a“', to_html('``a``')
+ end
+
+ def test_to_html_double_quote
+ assert_equal '“a', to_html('"a')
+ assert_equal '“a”', to_html('"a"')
+ end
+
+ def test_to_html_double_quote_quot
+ assert_equal '“a', to_html('&quot;a')
+ assert_equal '“a”', to_html('&quot;a&quot;')
+ end
+
+ def test_to_html_double_tick
+ assert_equal '”a', to_html("''a")
+ assert_equal '”a”', to_html("''a''")
+ end
+
+ def test_to_html_ellipsis
+ assert_equal '..', to_html('..')
+ assert_equal '…', to_html('...')
+ assert_equal '.…', to_html('....')
+ end
+
+ def test_to_html_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ s = '...(c)'.encode Encoding::Shift_JIS
+
+ html = to_html s
+
+ assert_equal Encoding::Shift_JIS, html.encoding
+
+ expected = '…(c)'.encode Encoding::Shift_JIS
+
+ assert_equal expected, html
+ end
+
+ def test_to_html_html_tag
+ assert_equal '<a href="http://example">hi’s</a>',
+ to_html('<a href="http://example">hi\'s</a>')
+ end
+
+ def test_to_html_registered_trademark
+ assert_equal '®', to_html('(r)')
+ end
+
+ def test_to_html_tt_tag
+ assert_equal '<tt>hi\'s</tt>', to_html('<tt>hi\'s</tt>')
+ assert_equal '<tt>hi\\\'s</tt>', to_html('<tt>hi\\\\\'s</tt>')
+ end
+
+ def test_to_html_tt_tag_mismatch
+ _, err = verbose_capture_io do
+ assert_equal '<tt>hi', to_html('<tt>hi')
+ end
+
+ assert_equal "mismatched <tt> tag\n", err
+ end
+
+ def formatter
+ RDoc::Markup::ToHtml.new @options
+ end
+
+ def options
+ @options
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_token_stream.rb b/jni/ruby/test/rdoc/test_rdoc_token_stream.rb
new file mode 100644
index 0000000..3c1a225
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_token_stream.rb
@@ -0,0 +1,42 @@
+require 'rdoc/test_case'
+
+class TestRDocTokenStream < RDoc::TestCase
+
+ def test_class_to_html
+ tokens = [
+ RDoc::RubyToken::TkCONSTANT. new(0, 0, 0, 'CONSTANT'),
+ RDoc::RubyToken::TkDEF. new(0, 0, 0, 'KW'),
+ RDoc::RubyToken::TkIVAR. new(0, 0, 0, 'IVAR'),
+ RDoc::RubyToken::TkOp. new(0, 0, 0, 'Op'),
+ RDoc::RubyToken::TkId. new(0, 0, 0, 'Id'),
+ RDoc::RubyToken::TkNode. new(0, 0, 0, 'Node'),
+ RDoc::RubyToken::TkCOMMENT. new(0, 0, 0, 'COMMENT'),
+ RDoc::RubyToken::TkREGEXP. new(0, 0, 0, 'REGEXP'),
+ RDoc::RubyToken::TkSTRING. new(0, 0, 0, 'STRING'),
+ RDoc::RubyToken::TkVal. new(0, 0, 0, 'Val'),
+ RDoc::RubyToken::TkBACKSLASH.new(0, 0, 0, '\\'),
+ ]
+
+ expected = [
+ '<span class="ruby-constant">CONSTANT</span>',
+ '<span class="ruby-keyword">KW</span>',
+ '<span class="ruby-ivar">IVAR</span>',
+ '<span class="ruby-operator">Op</span>',
+ '<span class="ruby-identifier">Id</span>',
+ '<span class="ruby-node">Node</span>',
+ '<span class="ruby-comment">COMMENT</span>',
+ '<span class="ruby-regexp">REGEXP</span>',
+ '<span class="ruby-string">STRING</span>',
+ '<span class="ruby-value">Val</span>',
+ '\\'
+ ].join
+
+ assert_equal expected, RDoc::TokenStream.to_html(tokens)
+ end
+
+ def test_class_to_html_empty
+ assert_equal '', RDoc::TokenStream.to_html([])
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_tom_doc.rb b/jni/ruby/test/rdoc/test_rdoc_tom_doc.rb
new file mode 100644
index 0000000..63d3a5e
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_tom_doc.rb
@@ -0,0 +1,520 @@
+require 'rdoc/test_case'
+
+class TestRDocTomDoc < RDoc::TestCase
+
+ def setup
+ super
+
+ @top_level = @store.add_file 'file.rb'
+
+ @TD = RDoc::TomDoc
+ @td = @TD.new
+ end
+
+ def test_class_add_post_processor
+ RDoc::TomDoc.add_post_processor
+
+ pp = RDoc::Markup::PreProcess.new __FILE__, []
+
+ text = "# Public: Do some stuff\n"
+
+ comment = RDoc::Comment.new text, nil
+ comment.format = 'tomdoc'
+
+ parent = RDoc::Context.new
+
+ pp.handle comment, parent
+
+ method = parent.add_method RDoc::AnyMethod.new(nil, 'm')
+
+ assert_equal 'Public', method.section.title
+ assert_equal "# Do some stuff\n", comment.text
+ end
+
+ def test_class_signature
+ c = comment <<-COMMENT
+Signature
+
+ method_<here>(args)
+
+here - something
+ COMMENT
+ c.format = 'tomdoc'
+
+ signature = @TD.signature c
+
+ assert_equal "method_<here>(args)\n", signature
+ end
+
+ def test_class_signature_no_space
+ c = comment <<-COMMENT
+Signature
+ method_<here>(args)
+
+here - something
+ COMMENT
+ c.format = 'tomdoc'
+
+ signature = @TD.signature c
+
+ assert_equal "method_<here>(args)\n", signature
+
+ expected =
+ doc(
+ head(3, 'Signature'),
+ list(:NOTE,
+ item(%w[here],
+ para('something'))))
+ expected.file = @top_level
+
+ assert_equal expected, c.parse
+ end
+
+ def test_class_signature_none
+ c = comment ''
+ c.format = 'tomdoc'
+
+ assert_nil @TD.signature c
+ end
+
+ def test_class_rdoc
+ c = comment <<-COMMENT
+=== Signature
+
+ method_<here>(args)
+
+here - something
+ COMMENT
+ c.format = 'rdoc'
+
+ signature = @TD.signature c
+
+ assert_nil signature
+ end
+
+ def test_class_signature_two_space
+ c = comment <<-COMMENT
+Signature
+
+
+ method_<here>(args)
+
+here - something
+ COMMENT
+ c.format = 'tomdoc'
+
+ signature = @TD.signature c
+
+ assert_equal "method_<here>(args)\n", signature
+
+ expected =
+ doc(
+ head(3, 'Signature'),
+ list(:NOTE,
+ item(%w[here],
+ para('something'))))
+ expected.file = @top_level
+
+ assert_equal expected, c.parse
+ end
+
+ def test_parse_paragraph
+ text = "Public: Do some stuff\n"
+
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('Do some stuff'))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_multiline_paragraph
+ text = "Public: Do some stuff\n"
+ text << "On a new line\n"
+
+ expected =
+ doc(
+ para('Do some stuff', ' ', 'On a new line'))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_arguments
+ text = <<-TEXT
+Create new Arg object.
+
+name - name of argument
+description - arguments description
+ TEXT
+
+ expected =
+ doc(
+ para('Create new Arg object.'),
+ blank_line,
+ list(:NOTE,
+ item(%w[name],
+ para('name of argument')),
+ item(%w[description],
+ para('arguments description'))))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_arguments_array
+ text = <<-TEXT
+Create new Arg object.
+
+names[] - names of arguments
+ TEXT
+
+ expected =
+ doc(
+ para('Create new Arg object.'),
+ blank_line,
+ list(:NOTE,
+ item(%w[names[]],
+ para('names of arguments'))))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_arguments_multiline
+ text = <<-TEXT
+Do some stuff
+
+foo - A comment goes here
+ and is more than one line
+ TEXT
+
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ list(:NOTE,
+ item(%w[foo],
+ para('A comment goes here', ' ', 'and is more than one line'))))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_arguments_nested
+ text = <<-TEXT
+Do some stuff
+
+foo - A comment goes here
+ :bar - bar documentation
+ TEXT
+
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ list(:NOTE,
+ item(%w[foo],
+ para('A comment goes here'),
+ list(:NOTE,
+ item(%w[:bar],
+ para('bar documentation'))))))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_examples
+ text = <<-TEXT
+Do some stuff
+
+Examples
+
+ 1 + 1
+ TEXT
+
+ code = verb("1 + 1\n")
+ code.format = :ruby
+
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ head(3, 'Examples'),
+ blank_line,
+ code)
+
+ document = @TD.parse(text)
+ assert_equal expected, document
+ assert document.parts.last.ruby?
+ end
+
+ def test_parse_examples_signature
+ text = <<-TEXT
+Do some stuff
+
+Examples
+
+ 1 + 1
+
+Signature
+
+ foo(args)
+ TEXT
+
+ code1 = verb("1 + 1\n")
+ code1.format = :ruby
+
+ code2 = verb("foo(args)\n")
+
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ head(3, 'Examples'),
+ blank_line,
+ code1,
+ head(3, 'Signature'),
+ blank_line,
+ code2)
+
+ document = @TD.parse text
+
+ assert_equal expected, document
+ end
+
+ def test_parse_returns
+ text = <<-TEXT
+Do some stuff
+
+Returns a thing
+
+Returns another thing
+ TEXT
+
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ head(3, 'Returns'),
+ blank_line,
+ para('Returns a thing'),
+ blank_line,
+ para('Returns another thing'))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_returns_multiline
+ text = <<-TEXT
+Do some stuff
+
+Returns a thing
+ that is multiline
+ TEXT
+
+ expected =
+ doc(
+ para('Do some stuff'),
+ blank_line,
+ head(3, 'Returns'),
+ blank_line,
+ para('Returns a thing', ' ', 'that is multiline'))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_parse_signature
+ text = <<-TEXT
+Do some stuff
+
+Signature
+
+ some_method(args)
+ TEXT
+
+ expected =
+ @RM::Document.new(
+ @RM::Paragraph.new('Do some stuff'),
+ @RM::BlankLine.new,
+ @RM::Heading.new(3, 'Signature'),
+ @RM::BlankLine.new,
+ @RM::Verbatim.new("some_method(args)\n"))
+
+ assert_equal expected, @TD.parse(text)
+ end
+
+ def test_tokenize_paragraph
+ @td.tokenize "Public: Do some stuff\n"
+
+ expected = [
+ [:TEXT, "Do some stuff", 0, 0],
+ [:NEWLINE, "\n", 13, 0],
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+ def test_tokenize_multiline_paragraph
+ text = "Public: Do some stuff\n"
+ text << "On a new line\n"
+
+ @td.tokenize text
+
+ expected = [
+ [:TEXT, "Do some stuff", 0, 0],
+ [:NEWLINE, "\n", 13, 0],
+ [:TEXT, "On a new line", 0, 1],
+ [:NEWLINE, "\n", 13, 1]
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+ def test_tokenize_arguments
+ @td.tokenize <<-TEXT
+Create new Arg object.
+
+name - name of argument
+description - arguments description
+ TEXT
+
+ expected = [
+ [:TEXT, "Create new Arg object.", 0, 0],
+ [:NEWLINE, "\n", 22, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:NOTE, "name", 0, 2],
+ [:TEXT, "name of argument", 14, 2],
+ [:NEWLINE, "\n", 30, 2],
+ [:NOTE, "description", 0, 3],
+ [:TEXT, "arguments description", 14, 3],
+ [:NEWLINE, "\n", 35, 3],
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+ def test_tokenize_arguments_array
+ @td.tokenize <<-TEXT
+Create new Arg object.
+
+names[stuff] - names of arguments
+ TEXT
+
+ expected = [
+ [:TEXT, "Create new Arg object.", 0, 0],
+ [:NEWLINE, "\n", 22, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:NOTE, "names[stuff]", 0, 2],
+ [:TEXT, "names of arguments", 15, 2],
+ [:NEWLINE, "\n", 33, 2],
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+ def test_tokenize_arguments_multiline
+ @td.tokenize <<-TEXT
+Do some stuff
+
+foo - A comment goes here
+ and is more than one line
+ TEXT
+
+ expected = [
+ [:TEXT, "Do some stuff", 0, 0],
+ [:NEWLINE, "\n", 13, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:NOTE, "foo", 0, 2],
+ [:TEXT, "A comment goes here", 6, 2],
+ [:NEWLINE, "\n", 25, 2],
+ [:TEXT, "and is more than one line", 2, 3],
+ [:NEWLINE, "\n", 27, 3],
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+ def test_tokenize_arguments_nested
+ @td.tokenize <<-TEXT
+Do some stuff
+
+foo - A comment goes here
+ :bar - bar documentation
+ TEXT
+
+ expected = [
+ [:TEXT, "Do some stuff", 0, 0],
+ [:NEWLINE, "\n", 13, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:NOTE, "foo", 0, 2],
+ [:TEXT, "A comment goes here", 6, 2],
+ [:NEWLINE, "\n", 25, 2],
+ [:NOTE, ":bar", 6, 3],
+ [:TEXT, "bar documentation", 13, 3],
+ [:NEWLINE, "\n", 30, 3],
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+ def test_tokenize_examples
+ @td.tokenize <<-TEXT
+Do some stuff
+
+Examples
+
+ 1 + 1
+ TEXT
+
+ expected = [
+ [:TEXT, "Do some stuff", 0, 0],
+ [:NEWLINE, "\n", 13, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:HEADER, 3, 0, 2],
+ [:TEXT, "Examples", 0, 2],
+ [:NEWLINE, "\n", 8, 2],
+ [:NEWLINE, "\n", 0, 3],
+ [:TEXT, "1 + 1", 2, 4],
+ [:NEWLINE, "\n", 7, 4],
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+ def test_tokenize_returns
+ @td.tokenize <<-TEXT
+Do some stuff
+
+Returns a thing
+ TEXT
+
+ expected = [
+ [:TEXT, "Do some stuff", 0, 0],
+ [:NEWLINE, "\n", 13, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:TEXT, "Returns a thing", 0, 2],
+ [:NEWLINE, "\n", 15, 2],
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+ def test_tokenize_returns_multiline
+ @td.tokenize <<-TEXT
+Do some stuff
+
+Returns a thing
+ that is multiline
+ TEXT
+
+ expected = [
+ [:TEXT, "Do some stuff", 0, 0],
+ [:NEWLINE, "\n", 13, 0],
+ [:NEWLINE, "\n", 0, 1],
+ [:TEXT, "Returns a thing", 0, 2],
+ [:NEWLINE, "\n", 15, 2],
+ [:TEXT, "that is multiline", 2, 3],
+ [:NEWLINE, "\n", 19, 3],
+ ]
+
+ assert_equal expected, @td.tokens
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/test_rdoc_top_level.rb b/jni/ruby/test/rdoc/test_rdoc_top_level.rb
new file mode 100644
index 0000000..f3ee6a4
--- /dev/null
+++ b/jni/ruby/test/rdoc/test_rdoc_top_level.rb
@@ -0,0 +1,287 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocTopLevel < XrefTestCase
+
+ def setup
+ super
+
+ @top_level = @store.add_file 'path/top_level.rb'
+ @top_level.parser = RDoc::Parser::Ruby
+ end
+
+ def test_initialize
+ t = RDoc::TopLevel.new 'path/file.rb'
+
+ assert_equal 'path/file.rb', t.absolute_name
+ assert_equal 'path/file.rb', t.relative_name
+ end
+
+ def test_initialize_relative
+ t = RDoc::TopLevel.new 'path/file.rb', 'file.rb'
+
+ assert_equal 'path/file.rb', t.absolute_name
+ assert_equal 'file.rb', t.relative_name
+ end
+
+ def test_add_alias
+ a = RDoc::Alias.new nil, 'old', 'new', nil
+ @top_level.add_alias a
+
+ object = @store.find_class_named 'Object'
+ expected = { '#old' => [a] }
+ assert_equal expected, object.unmatched_alias_lists
+ assert_includes object.in_files, @top_level
+ end
+
+ def test_add_alias_nodoc
+ @top_level.document_self = false
+
+ a = RDoc::Alias.new nil, 'old', 'new', nil
+ @top_level.add_alias a
+
+ object = @store.find_class_named('Object')
+ assert_empty object.unmatched_alias_lists
+ assert_includes object.in_files, @top_level
+ end
+
+ def test_add_constant
+ const = RDoc::Constant.new 'C', nil, nil
+ @top_level.add_constant const
+
+ object = @store.find_class_named 'Object'
+ assert_equal [const], object.constants
+ assert_includes object.in_files, @top_level
+ end
+
+ def test_add_constant_nodoc
+ @top_level.document_self = false
+
+ const = RDoc::Constant.new 'C', nil, nil
+ @top_level.add_constant const
+
+ object = @store.find_class_named 'Object'
+ assert_empty object.constants
+ assert_includes object.in_files, @top_level
+ end
+
+ def test_add_include
+ include = RDoc::Include.new 'C', nil
+ @top_level.add_include include
+
+ object = @store.find_class_named 'Object'
+ assert_equal [include], object.includes
+ assert_includes object.in_files, @top_level
+ end
+
+ def test_add_include_nodoc
+ @top_level.document_self = false
+
+ include = RDoc::Include.new 'C', nil
+ @top_level.add_include include
+
+ object = @store.find_class_named('Object')
+ assert_empty object.includes
+ assert_includes object.in_files, @top_level
+ end
+
+ def test_add_method
+ method = RDoc::AnyMethod.new nil, 'm'
+ @top_level.add_method method
+
+ object = @store.find_class_named 'Object'
+ assert_equal [method], object.method_list
+ assert_includes object.in_files, @top_level
+ end
+
+ def test_add_method_stopdoc
+ @top_level.document_self = false
+
+ method = RDoc::AnyMethod.new nil, 'm'
+ @top_level.add_method method
+
+ object = @store.find_class_named('Object')
+ assert_empty object.method_list
+ assert_includes object.in_files, @top_level
+ end
+
+ def test_base_name
+ assert_equal 'top_level.rb', @top_level.base_name
+ end
+
+ def test_display_eh
+ refute @top_level.display?
+
+ page = @store.add_file 'README.txt'
+ page.parser = RDoc::Parser::Simple
+
+ assert page.display?
+ end
+
+ def test_eql_eh
+ top_level2 = @store.add_file 'path/top_level.rb'
+ other_level = @store.add_file 'path/other_level.rb'
+
+ assert_operator @top_level, :eql?, top_level2
+
+ refute_operator other_level, :eql?, @top_level
+ end
+
+ def test_equals2
+ top_level2 = @store.add_file 'path/top_level.rb'
+ other_level = @store.add_file 'path/other_level.rb'
+
+ assert_equal @top_level, top_level2
+
+ refute_equal other_level, @top_level
+ end
+
+ def test_find_class_or_module
+ assert_equal @c1, @xref_data.find_class_or_module('C1')
+ assert_equal @c2_c3, @xref_data.find_class_or_module('C2::C3')
+ assert_equal @c4, @xref_data.find_class_or_module('C4')
+ assert_equal @m1_m2, @xref_data.find_class_or_module('M1::M2')
+ end
+
+ def test_full_name
+ assert_equal 'path/top_level.rb', @top_level.full_name
+ end
+
+ def test_hash
+ tl2 = @store.add_file 'path/top_level.rb'
+ tl3 = @store.add_file 'other/top_level.rb'
+
+ assert_equal @top_level.hash, tl2.hash
+ refute_equal @top_level.hash, tl3.hash
+ end
+
+ def test_http_url
+ assert_equal 'prefix/path/top_level_rb.html', @top_level.http_url('prefix')
+ end
+
+ def test_last_modified
+ assert_equal nil, @top_level.last_modified
+ stat = Object.new
+ def stat.mtime() 0 end
+ @top_level.file_stat = stat
+ assert_equal 0, @top_level.last_modified
+ end
+
+ def test_marshal_dump
+ page = @store.add_file 'README.txt'
+ page.parser = RDoc::Parser::Simple
+ page.comment = RDoc::Comment.new 'This is a page', page
+
+ loaded = Marshal.load Marshal.dump page
+
+ comment = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('This is a page'))
+ comment.file = loaded
+
+ assert_equal page, loaded
+
+ assert_equal 'README.txt', loaded.absolute_name
+ assert_equal 'README.txt', loaded.relative_name
+
+ assert_equal RDoc::Parser::Simple, loaded.parser
+
+ assert_equal comment, loaded.comment
+ end
+
+ def test_marshal_load_version_0
+ loaded = Marshal.load "\x04\bU:\x13RDoc::TopLevel" +
+ "[\ti\x00I\"\x0FREADME.txt\x06:\x06EF" +
+ "c\x19RDoc::Parser::Simple" +
+ "o:\eRDoc::Markup::Document\a:\v@parts" +
+ "[\x06o:\x1CRDoc::Markup::Paragraph\x06;\b" +
+ "[\x06I\"\x13This is a page\x06;\x06F:\n@file@\a"
+
+ comment = RDoc::Markup::Document.new(
+ RDoc::Markup::Paragraph.new('This is a page'))
+ comment.file = loaded
+
+ assert_equal 'README.txt', loaded.absolute_name
+ assert_equal 'README.txt', loaded.relative_name
+
+ assert_equal RDoc::Parser::Simple, loaded.parser
+
+ assert_equal comment, loaded.comment
+
+ assert loaded.display?
+ end
+
+ def test_name
+ assert_equal 'top_level.rb', @top_level.name
+ end
+
+ def test_page_name
+ assert_equal 'top_level', @top_level.page_name
+
+ tl = @store.add_file 'README.ja'
+
+ assert_equal 'README.ja', tl.page_name
+
+ tl = @store.add_file 'Rakefile'
+
+ assert_equal 'Rakefile', tl.page_name
+ end
+
+ def test_page_name_trim_extension
+ tl = @store.add_file 'README.ja.rdoc'
+
+ assert_equal 'README.ja', tl.page_name
+
+ tl = @store.add_file 'README.ja.md'
+
+ assert_equal 'README.ja', tl.page_name
+
+ tl = @store.add_file 'README.txt'
+
+ assert_equal 'README', tl.page_name
+ end
+
+ def test_search_record
+ assert_nil @xref_data.search_record
+ end
+
+ def test_search_record_page
+ page = @store.add_file 'README.txt'
+ page.parser = RDoc::Parser::Simple
+ page.comment = 'This is a comment.'
+
+ expected = [
+ 'README',
+ '',
+ 'README',
+ '',
+ 'README_txt.html',
+ '',
+ "<p>This is a comment.\n",
+ ]
+
+ assert_equal expected, page.search_record
+ end
+
+ def test_text_eh
+ refute @xref_data.text?
+
+ rd = @store.add_file 'rd_format.rd'
+ rd.parser = RDoc::Parser::RD
+
+ assert rd.text?
+
+ simple = @store.add_file 'simple.txt'
+ simple.parser = RDoc::Parser::Simple
+
+ assert simple.text?
+ end
+
+ def test_text_eh_no_parser
+ refute @xref_data.text?
+
+ rd = @store.add_file 'rd_format.rd'
+
+ refute rd.text?
+ end
+
+end
+
diff --git a/jni/ruby/test/rdoc/xref_data.rb b/jni/ruby/test/rdoc/xref_data.rb
new file mode 100644
index 0000000..4525a29
--- /dev/null
+++ b/jni/ruby/test/rdoc/xref_data.rb
@@ -0,0 +1,76 @@
+XREF_DATA = <<-XREF_DATA
+class C1
+
+ attr :attr
+
+ # :section: separate
+
+ attr_reader :attr_reader
+ attr_writer :attr_writer
+
+ # :section:
+ attr_accessor :attr_accessor
+
+ CONST = :const
+
+ def self.m
+ end
+
+ def m foo
+ end
+
+end
+
+class C2
+ def b
+ end
+
+ alias a b
+
+ class C3
+ def m
+ end
+
+ class H1
+ def m?
+ end
+ end
+ end
+end
+
+class C3
+ class H1
+ end
+
+ class H2 < H1
+ end
+end
+
+class C4
+ class C4
+ end
+end
+
+class C5
+ class C1
+ end
+end
+
+module M1
+ def m
+ end
+end
+
+module M1::M2
+end
+
+class Parent
+ def m() end
+ def self.m() end
+end
+
+class Child < Parent
+end
+
+XREF_DATA
+
diff --git a/jni/ruby/test/rdoc/xref_test_case.rb b/jni/ruby/test/rdoc/xref_test_case.rb
new file mode 100644
index 0000000..a56fa71
--- /dev/null
+++ b/jni/ruby/test/rdoc/xref_test_case.rb
@@ -0,0 +1,67 @@
+ENV['RDOC_TEST'] = 'yes'
+
+require 'rdoc'
+require File.expand_path '../xref_data', __FILE__
+
+class XrefTestCase < RDoc::TestCase
+
+ def setup
+ super
+
+ @options = RDoc::Options.new
+ @options.quiet = true
+
+ @rdoc.options = @options
+
+ @file_name = 'xref_data.rb'
+ @xref_data = @store.add_file @file_name
+ @top_level = @xref_data
+
+ stats = RDoc::Stats.new @store, 0
+
+ parser = RDoc::Parser::Ruby.new @xref_data, @file_name, XREF_DATA, @options,
+ stats
+ @top_levels = []
+ @top_levels.push parser.scan
+
+ generator = Object.new
+ def generator.class_dir() nil end
+ def generator.file_dir() nil end
+ @rdoc.options = @options
+ @rdoc.generator = generator
+
+ @c1 = @xref_data.find_module_named 'C1'
+ @c1_m = @c1.method_list.last # C1#m
+ @c1__m = @c1.method_list.first # C1::m
+
+ @c2 = @xref_data.find_module_named 'C2'
+ @c2_a = @c2.method_list.last
+ @c2_b = @c2.method_list.first
+
+ @c2_c3 = @xref_data.find_module_named 'C2::C3'
+ @c2_c3_m = @c2_c3.method_list.first # C2::C3#m
+
+ @c2_c3_h1 = @xref_data.find_module_named 'C2::C3::H1'
+ @c2_c3_h1_meh = @c2_c3_h1.method_list.first # C2::C3::H1#m?
+
+ @c3 = @xref_data.find_module_named 'C3'
+ @c4 = @xref_data.find_module_named 'C4'
+ @c4_c4 = @xref_data.find_module_named 'C4::C4'
+ @c5_c1 = @xref_data.find_module_named 'C5::C1'
+ @c3_h1 = @xref_data.find_module_named 'C3::H1'
+ @c3_h2 = @xref_data.find_module_named 'C3::H2'
+
+ @m1 = @xref_data.find_module_named 'M1'
+ @m1_m = @m1.method_list.first
+
+ @m1_m2 = @xref_data.find_module_named 'M1::M2'
+
+ @parent = @xref_data.find_module_named 'Parent'
+ @child = @xref_data.find_module_named 'Child'
+
+ @parent_m = @parent.method_list.first # Parent#m
+ @parent__m = @parent.method_list.last # Parent::m
+ end
+
+end
+
diff --git a/jni/ruby/test/readline/test_readline.rb b/jni/ruby/test/readline/test_readline.rb
new file mode 100644
index 0000000..0c50a99
--- /dev/null
+++ b/jni/ruby/test/readline/test_readline.rb
@@ -0,0 +1,530 @@
+begin
+ require "readline"
+rescue LoadError
+else
+ require "test/unit"
+ require "tempfile"
+ require "timeout"
+end
+
+class TestReadline < Test::Unit::TestCase
+ INPUTRC = "INPUTRC"
+
+ def setup
+ @inputrc, ENV[INPUTRC] = ENV[INPUTRC], IO::NULL
+ end
+
+ def teardown
+ ENV[INPUTRC] = @inputrc
+ Readline.instance_variable_set("@completion_proc", nil)
+ begin
+ Readline.delete_text
+ Readline.point = 0
+ rescue NotImplementedError
+ end
+ Readline.input = nil
+ Readline.output = nil
+ end
+
+ if !/EditLine/n.match(Readline::VERSION)
+ def test_readline
+ with_temp_stdio do |stdin, stdout|
+ stdin.write("hello\n")
+ stdin.close
+ stdout.flush
+ line = replace_stdio(stdin.path, stdout.path) {
+ Readline.readline("> ", true)
+ }
+ assert_equal("hello", line)
+ assert_equal(true, line.tainted?)
+ stdout.rewind
+ assert_equal("> ", stdout.read(2))
+ assert_equal(1, Readline::HISTORY.length)
+ assert_equal("hello", Readline::HISTORY[0])
+ assert_raise(SecurityError) do
+ Thread.start {
+ $SAFE = 1
+ replace_stdio(stdin.path, stdout.path) do
+ Readline.readline("> ".taint)
+ end
+ }.join
+ end
+ end
+ end
+
+ # line_buffer
+ # point
+ def test_line_buffer__point
+ begin
+ Readline.line_buffer
+ Readline.point
+ rescue NotImplementedError
+ return
+ end
+
+ with_temp_stdio do |stdin, stdout|
+ actual_text = nil
+ actual_line_buffer = nil
+ actual_point = nil
+ Readline.completion_proc = ->(text) {
+ actual_text = text
+ actual_point = Readline.point
+ actual_line_buffer = Readline.line_buffer
+ stdin.write(" finish\n")
+ stdin.flush
+ stdout.flush
+ return ["complete"]
+ }
+
+ stdin.write("first second\t")
+ stdin.flush
+ Readline.completion_append_character = " "
+ line = replace_stdio(stdin.path, stdout.path) {
+ Readline.readline("> ", false)
+ }
+ assert_equal("second", actual_text)
+ assert_equal("first second", actual_line_buffer)
+ assert_equal(12, actual_point)
+ assert_equal("first complete finish", Readline.line_buffer)
+ assert_equal(Encoding.find("locale"), Readline.line_buffer.encoding)
+ assert_equal(true, Readline.line_buffer.tainted?)
+ assert_equal(22, Readline.point)
+
+ stdin.rewind
+ stdout.rewind
+
+ stdin.write("first second\t")
+ stdin.flush
+ Readline.completion_append_character = nil
+ line = replace_stdio(stdin.path, stdout.path) {
+ Readline.readline("> ", false)
+ }
+ assert_equal("second", actual_text)
+ assert_equal("first second", actual_line_buffer)
+ assert_equal(12, actual_point)
+ assert_equal("first complete finish", Readline.line_buffer)
+ assert_equal(Encoding.find("locale"), Readline.line_buffer.encoding)
+ assert_equal(true, Readline.line_buffer.tainted?)
+ assert_equal(21, Readline.point)
+ end
+ end
+ end
+
+ def test_input=
+ assert_raise(TypeError) do
+ Readline.input = "This is not a file."
+ end
+ end
+
+ def test_output=
+ assert_raise(TypeError) do
+ Readline.output = "This is not a file."
+ end
+ end
+
+ def test_completion_proc
+ expected = proc { |input| input }
+ Readline.completion_proc = expected
+ assert_equal(expected, Readline.completion_proc)
+
+ assert_raise(ArgumentError) do
+ Readline.completion_proc = "This does not have call method."
+ end
+ end
+
+ def test_completion_case_fold
+ expected = [true, false, "string", {"a" => "b"}]
+ expected.each do |e|
+ Readline.completion_case_fold = e
+ assert_equal(e, Readline.completion_case_fold)
+ end
+ end
+
+ def test_completion_proc_empty_result
+ with_temp_stdio do |stdin, stdout|
+ stdin.write("first\t")
+ stdin.flush
+ Readline.completion_proc = ->(text) {[]}
+ line1 = line2 = nil
+ replace_stdio(stdin.path, stdout.path) {
+ assert_nothing_raised(NoMemoryError) {line1 = Readline.readline("> ")}
+ stdin.write("\n")
+ stdin.flush
+ assert_nothing_raised(NoMemoryError) {line2 = Readline.readline("> ")}
+ }
+ assert_equal("first", line1)
+ assert_equal("", line2)
+ begin
+ assert_equal("", Readline.line_buffer)
+ rescue NotimplementedError
+ end
+ end
+ end if !/EditLine/n.match(Readline::VERSION)
+
+ def test_get_screen_size
+ begin
+ res = Readline.get_screen_size
+ assert(res.is_a?(Array))
+ rows, columns = *res
+ assert(rows.is_a?(Integer))
+ assert(rows >= 0)
+ assert(columns.is_a?(Integer))
+ assert(columns >= 0)
+ rescue NotImplementedError
+ end
+ end
+
+ # vi_editing_mode
+ # emacs_editing_mode
+ def test_editing_mode
+ begin
+ assert_equal(false, Readline.vi_editing_mode?)
+ assert_equal(true, Readline.emacs_editing_mode?)
+
+ assert_equal(nil, Readline.vi_editing_mode)
+ assert_equal(true, Readline.vi_editing_mode?)
+ assert_equal(false, Readline.emacs_editing_mode?)
+ assert_equal(nil, Readline.vi_editing_mode)
+ assert_equal(true, Readline.vi_editing_mode?)
+ assert_equal(false, Readline.emacs_editing_mode?)
+
+ assert_equal(nil, Readline.emacs_editing_mode)
+ assert_equal(false, Readline.vi_editing_mode?)
+ assert_equal(true, Readline.emacs_editing_mode?)
+ assert_equal(nil, Readline.emacs_editing_mode)
+ assert_equal(false, Readline.vi_editing_mode?)
+ assert_equal(true, Readline.emacs_editing_mode?)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_completion_append_character
+ begin
+ enc = get_default_internal_encoding
+ data_expected = [
+ ["x", "x"],
+ ["xyx", "x"],
+ [" ", " "],
+ ["\t", "\t"],
+ ]
+ data_expected.each do |(data, expected)|
+ Readline.completion_append_character = data
+ assert_equal(expected, Readline.completion_append_character)
+ assert_equal(enc, Readline.completion_append_character.encoding)
+ end
+ Readline.completion_append_character = ""
+ assert_equal(nil, Readline.completion_append_character)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_completion_encoding
+ bug5941 = '[Bug #5941]'
+ append_character = Readline.completion_append_character
+ Readline.completion_append_character = ""
+ completion_case_fold = Readline.completion_case_fold
+ locale = Encoding.find("locale")
+ if locale == Encoding::UTF_8
+ enc1 = Encoding::EUC_JP
+ else
+ enc1 = Encoding::UTF_8
+ end
+ results = nil
+ Readline.completion_proc = ->(text) {results}
+
+ [%W"\u{3042 3042} \u{3042 3044}", %W"\u{fe5b fe5b} \u{fe5b fe5c}"].any? do |w|
+ begin
+ results = w.map {|s| s.encode(locale)}
+ rescue Encoding::UndefinedConversionError
+ end
+ end or
+ begin
+ "\xa1\xa2".encode(Encoding::UTF_8, locale)
+ rescue
+ else
+ results = %W"\xa1\xa1 \xa1\xa2".map {|s| s.force_encoding(locale)}
+ end or
+ begin
+ return if assert_under_utf8
+ skip("missing test for locale #{locale.name}")
+ end
+ expected = results[0][0...1]
+ Readline.completion_case_fold = false
+ assert_equal(expected, with_pipe {|r, w| w << "\t"}, bug5941)
+ Readline.completion_case_fold = true
+ assert_equal(expected, with_pipe {|r, w| w << "\t"}, bug5941)
+ results.map! {|s| s.encode(enc1)}
+ assert_raise(Encoding::CompatibilityError, bug5941) do
+ with_pipe {|r, w| w << "\t"}
+ end
+ ensure
+ Readline.completion_case_fold = completion_case_fold
+ Readline.completion_append_character = append_character
+ end if !/EditLine/n.match(Readline::VERSION)
+
+ # basic_word_break_characters
+ # completer_word_break_characters
+ # basic_quote_characters
+ # completer_quote_characters
+ # filename_quote_characters
+ # special_prefixes
+ def test_some_characters_methods
+ method_names = [
+ "basic_word_break_characters",
+ "completer_word_break_characters",
+ "basic_quote_characters",
+ "completer_quote_characters",
+ "filename_quote_characters",
+ "special_prefixes",
+ ]
+ method_names.each do |method_name|
+ begin
+ begin
+ enc = get_default_internal_encoding
+ saved = Readline.send(method_name.to_sym)
+ expecteds = [" ", " .,|\t", ""]
+ expecteds.each do |e|
+ Readline.send((method_name + "=").to_sym, e)
+ res = Readline.send(method_name.to_sym)
+ assert_equal(e, res)
+ assert_equal(enc, res.encoding)
+ end
+ ensure
+ Readline.send((method_name + "=").to_sym, saved) if saved
+ end
+ rescue NotImplementedError
+ end
+ end
+ end
+
+ def test_closed_outstream
+ bug5803 = '[ruby-dev:45043]'
+ IO.pipe do |r, w|
+ Readline.input = r
+ Readline.output = w
+ (w << "##\t").close
+ assert_raise(IOError, bug5803) {Readline.readline}
+ end
+ end
+
+ def test_pre_input_hook
+ begin
+ pr = proc {}
+ Readline.pre_input_hook = pr
+ assert_equal(pr, Readline.pre_input_hook)
+ Readline.pre_input_hook = nil
+ assert_nil(Readline.pre_input_hook)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_point
+ assert_equal(0, Readline.point)
+ Readline.insert_text('12345')
+ assert_equal(5, Readline.point)
+
+ assert_equal(4, Readline.point=(4))
+
+ Readline.insert_text('abc')
+ assert_equal(7, Readline.point)
+
+ assert_equal('1234abc5', Readline.line_buffer)
+ rescue NotImplementedError
+ end if !/EditLine/n.match(Readline::VERSION)
+
+ def test_insert_text
+ str = "test_insert_text"
+ assert_equal(0, Readline.point)
+ assert_equal(Readline, Readline.insert_text(str))
+ assert_equal(str, Readline.line_buffer)
+ assert_equal(16, Readline.point)
+ assert_equal(get_default_internal_encoding,
+ Readline.line_buffer.encoding)
+
+ Readline.delete_text(1, 3)
+ assert_equal("t_insert_text", Readline.line_buffer)
+ Readline.delete_text(11)
+ assert_equal("t_insert_te", Readline.line_buffer)
+ Readline.delete_text(-3...-1)
+ assert_equal("t_inserte", Readline.line_buffer)
+ Readline.delete_text(-3..-1)
+ assert_equal("t_inse", Readline.line_buffer)
+ Readline.delete_text(3..-3)
+ assert_equal("t_ise", Readline.line_buffer)
+ Readline.delete_text(3, 1)
+ assert_equal("t_ie", Readline.line_buffer)
+ Readline.delete_text(1..1)
+ assert_equal("tie", Readline.line_buffer)
+ Readline.delete_text(1...2)
+ assert_equal("te", Readline.line_buffer)
+ Readline.delete_text
+ assert_equal("", Readline.line_buffer)
+ rescue NotImplementedError
+ end if !/EditLine/n.match(Readline::VERSION)
+
+ def test_delete_text
+ str = "test_insert_text"
+ assert_equal(0, Readline.point)
+ assert_equal(Readline, Readline.insert_text(str))
+ assert_equal(16, Readline.point)
+ assert_equal(str, Readline.line_buffer)
+ Readline.delete_text
+
+ # NOTE: unexpected but GNU Readline's spec
+ assert_equal(16, Readline.point)
+ assert_equal("", Readline.line_buffer)
+ assert_equal(Readline, Readline.insert_text(str))
+ assert_equal(32, Readline.point)
+ assert_equal("", Readline.line_buffer)
+ rescue NotImplementedError
+ end if !/EditLine/n.match(Readline::VERSION)
+
+ def test_modify_text_in_pre_input_hook
+ with_temp_stdio {|stdin, stdout|
+ begin
+ stdin.write("world\n")
+ stdin.close
+ Readline.pre_input_hook = proc do
+ assert_equal("", Readline.line_buffer)
+ Readline.insert_text("hello ")
+ Readline.redisplay
+ end
+ replace_stdio(stdin.path, stdout.path) do
+ line = Readline.readline("> ")
+ assert_equal("hello world", line)
+ end
+ assert_equal("> hello world\n", stdout.read)
+ stdout.close
+ rescue NotImplementedError
+ ensure
+ begin
+ Readline.pre_input_hook = nil
+ rescue NotImplementedError
+ end
+ end
+ }
+ end if !/EditLine|\A4\.3\z/n.match(Readline::VERSION)
+
+ def test_input_metachar
+ bug6601 = '[ruby-core:45682]'
+ Readline::HISTORY << "hello"
+ wo = nil
+ line = with_pipe do |r, w|
+ wo = w.dup
+ wo.write("\C-re\ef\n")
+ end
+ assert_equal("hello", line, bug6601)
+ ensure
+ wo.close
+ Readline.delete_text
+ Readline::HISTORY.clear
+ end if !/EditLine/n.match(Readline::VERSION)
+
+ def test_input_metachar_multibyte
+ unless Encoding.find("locale") == Encoding::UTF_8
+ return if assert_under_utf8
+ skip 'this test needs UTF-8 locale'
+ end
+ bug6602 = '[ruby-core:45683]'
+ Readline::HISTORY << "\u3042\u3093"
+ Readline::HISTORY << "\u3044\u3093"
+ Readline::HISTORY << "\u3046\u3093"
+ open(IO::NULL, 'w') do |null|
+ IO.pipe do |r, w|
+ Readline.input = r
+ Readline.output = null
+ w << "\cr\u3093\n\n"
+ w << "\cr\u3042\u3093"
+ w.reopen(IO::NULL)
+ assert_equal("\u3046\u3093", Readline.readline("", true), bug6602)
+ Timeout.timeout(2) do
+ assert_equal("\u3042\u3093", Readline.readline("", true), bug6602)
+ end
+ assert_equal(nil, Readline.readline("", true), bug6602)
+ end
+ end
+ ensure
+ Readline.delete_text
+ Readline::HISTORY.clear
+ end if !/EditLine/n.match(Readline::VERSION)
+
+ def test_refresh_line
+ bug6232 = '[ruby-core:43957] [Bug #6232] refresh_line after set_screen_size'
+ with_temp_stdio do |stdin, stdout|
+ replace_stdio(stdin.path, stdout.path) do
+ assert_ruby_status(%w[-rreadline -], <<-'end;', bug6232)
+ Readline.set_screen_size(40, 80)
+ Readline.refresh_line
+ end;
+ end
+ end
+ end if Readline.respond_to?(:refresh_line)
+
+ private
+
+ def replace_stdio(stdin_path, stdout_path)
+ open(stdin_path, "r"){|stdin|
+ open(stdout_path, "w"){|stdout|
+ orig_stdin = STDIN.dup
+ orig_stdout = STDOUT.dup
+ orig_stderr = STDERR.dup
+ STDIN.reopen(stdin)
+ STDOUT.reopen(stdout)
+ STDERR.reopen(stdout)
+ begin
+ Readline.input = STDIN
+ Readline.output = STDOUT
+ yield
+ ensure
+ STDERR.reopen(orig_stderr)
+ STDIN.reopen(orig_stdin)
+ STDOUT.reopen(orig_stdout)
+ orig_stdin.close
+ orig_stdout.close
+ orig_stderr.close
+ end
+ }
+ }
+ end
+
+ def with_temp_stdio
+ Tempfile.create("test_readline_stdin") {|stdin|
+ Tempfile.create("test_readline_stdout") {|stdout|
+ yield stdin, stdout
+ }
+ }
+ end
+
+ def with_pipe
+ stderr = nil
+ IO.pipe do |r, w|
+ yield(r, w)
+ Readline.input = r
+ Readline.output = w.reopen(IO::NULL)
+ stderr = STDERR.dup
+ STDERR.reopen(w)
+ Readline.readline
+ end
+ ensure
+ if stderr
+ STDERR.reopen(stderr)
+ stderr.close
+ end
+ Readline.input = STDIN
+ Readline.output = STDOUT
+ end
+
+ def get_default_internal_encoding
+ return Encoding.default_internal || Encoding.find("locale")
+ end
+
+ def assert_under_utf8
+ return false if ENV['LC_ALL'] == 'UTF-8'
+ loc = caller_locations(1, 1)[0].base_label.to_s
+ assert_separately([{"LC_ALL"=>"UTF-8"}, "-r", __FILE__], <<SRC)
+#skip "test \#{ENV['LC_ALL']}"
+#{self.class.name}.new(#{loc.dump}).run(Test::Unit::Runner.new)
+SRC
+ return true
+ end
+end if defined?(::Readline)
diff --git a/jni/ruby/test/readline/test_readline_history.rb b/jni/ruby/test/readline/test_readline_history.rb
new file mode 100644
index 0000000..4bcd7b3
--- /dev/null
+++ b/jni/ruby/test/readline/test_readline_history.rb
@@ -0,0 +1,292 @@
+begin
+ require "readline"
+=begin
+ class << Readline::HISTORY
+ def []=(index, str)
+ raise NotImplementedError
+ end
+
+ def pop
+ raise NotImplementedError
+ end
+
+ def shift
+ raise NotImplementedError
+ end
+
+ def delete_at(index)
+ raise NotImplementedError
+ end
+ end
+=end
+
+=begin
+ class << Readline::HISTORY
+ def clear
+ raise NotImplementedError
+ end
+ end
+=end
+rescue LoadError
+else
+ require "test/unit"
+end
+
+class Readline::TestHistory < Test::Unit::TestCase
+ include Readline
+
+ def setup
+ HISTORY.clear
+ end
+
+ def test_to_s
+ expected = "HISTORY"
+ assert_equal(expected, HISTORY.to_s)
+ end
+
+ def test_get
+ lines = push_history(5)
+ lines.each_with_index do |s, i|
+ assert_external_string_equal(s, HISTORY[i])
+ end
+ end
+
+ def test_get__negative
+ lines = push_history(5)
+ (1..5).each do |i|
+ assert_equal(lines[-i], HISTORY[-i])
+ end
+ end
+
+ def test_get__out_of_range
+ push_history(5)
+ invalid_indexes = [5, 6, 100, -6, -7, -100]
+ invalid_indexes.each do |i|
+ assert_raise(IndexError, "i=<#{i}>") do
+ HISTORY[i]
+ end
+ end
+
+ invalid_indexes = [100_000_000_000_000_000_000,
+ -100_000_000_000_000_000_000]
+ invalid_indexes.each do |i|
+ assert_raise(RangeError, "i=<#{i}>") do
+ HISTORY[i]
+ end
+ end
+ end
+
+ def test_set
+ begin
+ push_history(5)
+ 5.times do |i|
+ expected = "set: #{i}"
+ HISTORY[i] = expected
+ assert_external_string_equal(expected, HISTORY[i])
+ end
+ rescue NotImplementedError
+ end
+ end
+
+ def test_set__out_of_range
+ assert_raise(IndexError, NotImplementedError, "index=<0>") do
+ HISTORY[0] = "set: 0"
+ end
+
+ push_history(5)
+ invalid_indexes = [5, 6, 100, -6, -7, -100]
+ invalid_indexes.each do |i|
+ assert_raise(IndexError, NotImplementedError, "index=<#{i}>") do
+ HISTORY[i] = "set: #{i}"
+ end
+ end
+
+ invalid_indexes = [100_000_000_000_000_000_000,
+ -100_000_000_000_000_000_000]
+ invalid_indexes.each do |i|
+ assert_raise(RangeError, NotImplementedError, "index=<#{i}>") do
+ HISTORY[i] = "set: #{i}"
+ end
+ end
+ end
+
+ def test_push
+ 5.times do |i|
+ s = i.to_s
+ assert_equal(HISTORY, HISTORY.push(s))
+ assert_external_string_equal(s, HISTORY[i])
+ end
+ assert_equal(5, HISTORY.length)
+ end
+
+ def test_push__operator
+ 5.times do |i|
+ s = i.to_s
+ assert_equal(HISTORY, HISTORY << s)
+ assert_external_string_equal(s, HISTORY[i])
+ end
+ assert_equal(5, HISTORY.length)
+ end
+
+ def test_push__plural
+ assert_equal(HISTORY, HISTORY.push("0", "1", "2", "3", "4"))
+ (0..4).each do |i|
+ assert_external_string_equal(i.to_s, HISTORY[i])
+ end
+ assert_equal(5, HISTORY.length)
+
+ assert_equal(HISTORY, HISTORY.push("5", "6", "7", "8", "9"))
+ (5..9).each do |i|
+ assert_external_string_equal(i.to_s, HISTORY[i])
+ end
+ assert_equal(10, HISTORY.length)
+ end
+
+ def test_pop
+ begin
+ assert_equal(nil, HISTORY.pop)
+
+ lines = push_history(5)
+ (1..5).each do |i|
+ assert_external_string_equal(lines[-i], HISTORY.pop)
+ assert_equal(lines.length - i, HISTORY.length)
+ end
+
+ assert_equal(nil, HISTORY.pop)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_shift
+ begin
+ assert_equal(nil, HISTORY.shift)
+
+ lines = push_history(5)
+ (0..4).each do |i|
+ assert_external_string_equal(lines[i], HISTORY.shift)
+ assert_equal(lines.length - (i + 1), HISTORY.length)
+ end
+
+ assert_equal(nil, HISTORY.shift)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_each
+ e = HISTORY.each do |s|
+ assert(false) # not reachable
+ end
+ assert_equal(HISTORY, e)
+ lines = push_history(5)
+ i = 0
+ e = HISTORY.each do |s|
+ assert_external_string_equal(HISTORY[i], s)
+ assert_external_string_equal(lines[i], s)
+ i += 1
+ end
+ assert_equal(HISTORY, e)
+ end
+
+ def test_each__enumerator
+ e = HISTORY.each
+ assert_instance_of(Enumerator, e)
+ end
+
+ def test_length
+ assert_equal(0, HISTORY.length)
+ push_history(1)
+ assert_equal(1, HISTORY.length)
+ push_history(4)
+ assert_equal(5, HISTORY.length)
+ HISTORY.clear
+ assert_equal(0, HISTORY.length)
+ end
+
+ def test_empty_p
+ 2.times do
+ assert(HISTORY.empty?)
+ HISTORY.push("s")
+ assert_equal(false, HISTORY.empty?)
+ HISTORY.clear
+ assert(HISTORY.empty?)
+ end
+ end
+
+ def test_delete_at
+ begin
+ lines = push_history(5)
+ (0..4).each do |i|
+ assert_external_string_equal(lines[i], HISTORY.delete_at(0))
+ end
+ assert(HISTORY.empty?)
+
+ lines = push_history(5)
+ (1..5).each do |i|
+ assert_external_string_equal(lines[lines.length - i], HISTORY.delete_at(-1))
+ end
+ assert(HISTORY.empty?)
+
+ lines = push_history(5)
+ assert_external_string_equal(lines[0], HISTORY.delete_at(0))
+ assert_external_string_equal(lines[4], HISTORY.delete_at(3))
+ assert_external_string_equal(lines[1], HISTORY.delete_at(0))
+ assert_external_string_equal(lines[3], HISTORY.delete_at(1))
+ assert_external_string_equal(lines[2], HISTORY.delete_at(0))
+ assert(HISTORY.empty?)
+ rescue NotImplementedError
+ end
+ end
+
+ def test_delete_at__out_of_range
+ assert_raise(IndexError, NotImplementedError, "index=<0>") do
+ HISTORY.delete_at(0)
+ end
+
+ push_history(5)
+ invalid_indexes = [5, 6, 100, -6, -7, -100]
+ invalid_indexes.each do |i|
+ assert_raise(IndexError, NotImplementedError, "index=<#{i}>") do
+ HISTORY.delete_at(i)
+ end
+ end
+
+ invalid_indexes = [100_000_000_000_000_000_000,
+ -100_000_000_000_000_000_000]
+ invalid_indexes.each do |i|
+ assert_raise(RangeError, NotImplementedError, "index=<#{i}>") do
+ HISTORY.delete_at(i)
+ end
+ end
+ end
+
+ private
+
+ def push_history(num)
+ lines = []
+ num.times do |i|
+ s = "a"
+ i.times do
+ s = s.succ
+ end
+ lines.push("#{i + 1}:#{s}")
+ end
+ HISTORY.push(*lines)
+ return lines
+ end
+
+ def assert_external_string_equal(expected, actual)
+ assert_equal(expected, actual)
+ assert_equal(get_default_internal_encoding, actual.encoding)
+ end
+
+ def get_default_internal_encoding
+ return Encoding.default_internal || Encoding.find("locale")
+ end
+end if defined?(::Readline) && defined?(::Readline::HISTORY) &&
+ (
+ begin
+ Readline::HISTORY.clear
+ rescue NotImplementedError
+ false
+ end
+ )
diff --git a/jni/ruby/test/resolv/test_addr.rb b/jni/ruby/test/resolv/test_addr.rb
new file mode 100644
index 0000000..e06c3c2
--- /dev/null
+++ b/jni/ruby/test/resolv/test_addr.rb
@@ -0,0 +1,29 @@
+require 'test/unit'
+require 'resolv'
+require 'socket'
+require 'tempfile'
+
+class TestResolvAddr < Test::Unit::TestCase
+ def test_invalid_ipv4_address
+ assert_not_match(Resolv::IPv4::Regex, "1.2.3.256", "[ruby-core:29501]")
+ 1000.times {|i|
+ if i < 256
+ assert_match(Resolv::IPv4::Regex, "#{i}.#{i}.#{i}.#{i}")
+ else
+ assert_not_match(Resolv::IPv4::Regex, "#{i}.#{i}.#{i}.#{i}")
+ end
+ }
+ end
+
+ def test_invalid_byte_comment
+ bug9273 = '[ruby-core:59239] [Bug #9273]'
+ Tempfile.create('resolv_test_addr_') do |tmpfile|
+ tmpfile.print("\xff\x00\x40")
+ tmpfile.close
+ hosts = Resolv::Hosts.new(tmpfile.path)
+ assert_nothing_raised(ArgumentError, bug9273) do
+ hosts.each_address("") {break}
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/resolv/test_dns.rb b/jni/ruby/test/resolv/test_dns.rb
new file mode 100644
index 0000000..d7e5c36
--- /dev/null
+++ b/jni/ruby/test/resolv/test_dns.rb
@@ -0,0 +1,200 @@
+require 'test/unit'
+require 'resolv'
+require 'socket'
+require 'tempfile'
+
+class TestResolvDNS < Test::Unit::TestCase
+ def setup
+ @save_do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = true
+ end
+
+ def teardown
+ BasicSocket.do_not_reverse_lookup = @save_do_not_reverse_lookup
+ end
+
+ def with_udp(host, port)
+ u = UDPSocket.new
+ begin
+ u.bind(host, port)
+ yield u
+ ensure
+ u.close
+ end
+ end
+
+ # [ruby-core:65836]
+ def test_resolve_with_2_ndots
+ conf = Resolv::DNS::Config.new :nameserver => ['127.0.0.1'], :ndots => 2
+ assert conf.single?
+
+ candidates = []
+ conf.resolv('example.com') { |candidate, *args|
+ candidates << candidate
+ raise Resolv::DNS::Config::NXDomain
+ }
+ n = Resolv::DNS::Name.create 'example.com.'
+ assert_equal n, candidates.last
+ end
+
+ def test_query_ipv4_address
+ begin
+ OpenSSL
+ rescue LoadError
+ skip 'autoload problem. see [ruby-dev:45021][Bug #5786]'
+ end if defined?(OpenSSL)
+
+ with_udp('127.0.0.1', 0) {|u|
+ _, server_port, _, server_address = u.addr
+ begin
+ client_thread = Thread.new {
+ Resolv::DNS.open(:nameserver_port => [[server_address, server_port]]) {|dns|
+ dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
+ }
+ }
+ server_thread = Thread.new {
+ msg, (_, client_port, _, client_address) = timeout(5) {u.recvfrom(4096)}
+ id, word2, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn")
+ qr = (word2 & 0x8000) >> 15
+ opcode = (word2 & 0x7800) >> 11
+ aa = (word2 & 0x0400) >> 10
+ tc = (word2 & 0x0200) >> 9
+ rd = (word2 & 0x0100) >> 8
+ ra = (word2 & 0x0080) >> 7
+ z = (word2 & 0x0070) >> 4
+ rcode = word2 & 0x000f
+ rest = msg[12..-1]
+ assert_equal(0, qr) # 0:query 1:response
+ assert_equal(0, opcode) # 0:QUERY 1:IQUERY 2:STATUS
+ assert_equal(0, aa) # Authoritative Answer
+ assert_equal(0, tc) # TrunCation
+ assert_equal(1, rd) # Recursion Desired
+ assert_equal(0, ra) # Recursion Available
+ assert_equal(0, z) # Reserved for future use
+ assert_equal(0, rcode) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
+ assert_equal(1, qdcount) # number of entries in the question section.
+ assert_equal(0, ancount) # number of entries in the answer section.
+ assert_equal(0, nscount) # number of entries in the authority records section.
+ assert_equal(0, arcount) # number of entries in the additional records section.
+ name = [3, "foo", 7, "example", 3, "org", 0].pack("Ca*Ca*Ca*C")
+ assert_operator(rest, :start_with?, name)
+ rest = rest[name.length..-1]
+ assert_equal(4, rest.length)
+ qtype, _ = rest.unpack("nn")
+ assert_equal(1, qtype) # A
+ assert_equal(1, qtype) # IN
+ id = id
+ qr = 1
+ opcode = opcode
+ aa = 0
+ tc = 0
+ rd = rd
+ ra = 1
+ z = 0
+ rcode = 0
+ qdcount = 0
+ ancount = 1
+ nscount = 0
+ arcount = 0
+ word2 = (qr << 15) |
+ (opcode << 11) |
+ (aa << 10) |
+ (tc << 9) |
+ (rd << 8) |
+ (ra << 7) |
+ (z << 4) |
+ rcode
+ msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
+ type = 1
+ klass = 1
+ ttl = 3600
+ rdlength = 4
+ rdata = [192,0,2,1].pack("CCCC") # 192.0.2.1 (TEST-NET address) RFC 3330
+ rr = [name, type, klass, ttl, rdlength, rdata].pack("a*nnNna*")
+ msg << rr
+ u.send(msg, 0, client_address, client_port)
+ }
+ result, _ = assert_join_threads([client_thread, server_thread])
+ assert_instance_of(Array, result)
+ assert_equal(1, result.length)
+ rr = result[0]
+ assert_instance_of(Resolv::DNS::Resource::IN::A, rr)
+ assert_instance_of(Resolv::IPv4, rr.address)
+ assert_equal("192.0.2.1", rr.address.to_s)
+ assert_equal(3600, rr.ttl)
+ end
+ }
+ end
+
+ def test_query_ipv4_address_timeout
+ with_udp('127.0.0.1', 0) {|u|
+ _, port , _, host = u.addr
+ start = nil
+ rv = Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
+ dns.timeouts = 0.1
+ start = Time.now
+ dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
+ }
+ t2 = Time.now
+ diff = t2 - start
+ assert rv.empty?, "unexpected: #{rv.inspect} (expected empty)"
+ assert_operator 0.1, :<=, diff
+
+ rv = Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
+ dns.timeouts = [ 0.1, 0.2 ]
+ start = Time.now
+ dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
+ }
+ t2 = Time.now
+ diff = t2 - start
+ assert rv.empty?, "unexpected: #{rv.inspect} (expected empty)"
+ assert_operator 0.3, :<=, diff
+ }
+ end
+
+ def test_no_server
+ u = UDPSocket.new
+ u.bind("127.0.0.1", 0)
+ _, port, _, host = u.addr
+ u.close
+ # A rase condition here.
+ # Another program may use the port.
+ # But no way to prevent it.
+ timeout(5) do
+ Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
+ assert_equal([], dns.getresources("test-no-server.example.org", Resolv::DNS::Resource::IN::A))
+ }
+ end
+ end
+
+ def test_invalid_byte_comment
+ bug9273 = '[ruby-core:59239] [Bug #9273]'
+ Tempfile.create('resolv_test_dns_') do |tmpfile|
+ tmpfile.print("\xff\x00\x40")
+ tmpfile.close
+ assert_nothing_raised(ArgumentError, bug9273) do
+ Resolv::DNS::Config.parse_resolv_conf(tmpfile.path)
+ end
+ end
+ end
+
+ def test_dots_diffences
+ name1 = Resolv::DNS::Name.create("example.org")
+ name2 = Resolv::DNS::Name.create("ex.ampl.eo.rg")
+ assert_not_equal(name1, name2, "different dots")
+ end
+
+ def test_case_insensitive_name
+ bug10550 = '[ruby-core:66498] [Bug #10550]'
+ lower = Resolv::DNS::Name.create("ruby-lang.org")
+ upper = Resolv::DNS::Name.create("Ruby-Lang.org")
+ assert_equal(lower, upper, bug10550)
+ end
+
+ def test_ipv6_name
+ addr = Resolv::IPv6.new("\0"*16)
+ labels = addr.to_name.to_a
+ expected = (['0'] * 32 + ['ip6', 'arpa']).map {|label| Resolv::DNS::Label::Str.new(label) }
+ assert_equal(expected, labels)
+ end
+end
diff --git a/jni/ruby/test/resolv/test_resource.rb b/jni/ruby/test/resolv/test_resource.rb
new file mode 100644
index 0000000..7ec17e3
--- /dev/null
+++ b/jni/ruby/test/resolv/test_resource.rb
@@ -0,0 +1,21 @@
+require 'test/unit'
+require 'resolv'
+
+class TestResolvResource < Test::Unit::TestCase
+ def setup
+ address = "192.168.0.1"
+ @name1 = Resolv::DNS::Resource::IN::A.new(address)
+ @name1.instance_variable_set(:@ttl, 100)
+ @name2 = Resolv::DNS::Resource::IN::A.new(address)
+ end
+
+ def test_equality
+ bug10857 = '[ruby-core:68128] [Bug #10857]'
+ assert_equal(@name1, @name2, bug10857)
+ end
+
+ def test_hash
+ bug10857 = '[ruby-core:68128] [Bug #10857]'
+ assert_equal(@name1.hash, @name2.hash, bug10857)
+ end
+end
diff --git a/jni/ruby/test/rexml/data/LostineRiver.kml.gz b/jni/ruby/test/rexml/data/LostineRiver.kml.gz
new file mode 100644
index 0000000..68a00c5
--- /dev/null
+++ b/jni/ruby/test/rexml/data/LostineRiver.kml.gz
Binary files differ
diff --git a/jni/ruby/test/rexml/data/ProductionSupport.xml b/jni/ruby/test/rexml/data/ProductionSupport.xml
new file mode 100644
index 0000000..083cf64
--- /dev/null
+++ b/jni/ruby/test/rexml/data/ProductionSupport.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+<ProductionSupport version="1.1" >
+ <Errors>
+ <CommonErrors>
+ <CommonError>
+ <Key><![CDATA[RoyUpdatePolicyBusReq(Object)>>#error:]]></Key>
+ <Patterns>
+ <Pattern><![CDATA[The error code is '9997']]></Pattern>
+ </Patterns>
+ <Message>
+ <String>Update Policy request 9997: Please check CICS log</String>
+ </Message>
+ <BackendSupport/>
+ </CommonError>
+ <CommonError>
+ <Key>MotorInsuranceContract(Object)>>#error:</Key>
+ <Patterns>
+ <Pattern>Have not got a complete</Pattern>
+ </Patterns>
+ <Message>
+ <String>Have not got a complete and consistent set of price matrices for policy period - ask back-end prod supp to sort out</String>
+ </Message>
+ <BackendSupport/>
+ </CommonError>
+</CommonErrors>
+</Errors>
+</ProductionSupport>
diff --git a/jni/ruby/test/rexml/data/axis.xml b/jni/ruby/test/rexml/data/axis.xml
new file mode 100644
index 0000000..bc996c5
--- /dev/null
+++ b/jni/ruby/test/rexml/data/axis.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<root>
+
+ <a>
+ <a.1/>
+ <a.2/>
+ <a.3/>
+ <a.4/>
+ <a.5/>
+ </a>
+
+ <b>
+ <b.1/>
+ <b.2/>
+ <b.3/>
+ <b.4/>
+ <b.5/>
+ <b.6/>
+ <b.7/>
+ <b.8/>
+ <b.9/>
+ </b>
+
+</root>
diff --git a/jni/ruby/test/rexml/data/bad.xml b/jni/ruby/test/rexml/data/bad.xml
new file mode 100644
index 0000000..18786f2
--- /dev/null
+++ b/jni/ruby/test/rexml/data/bad.xml
@@ -0,0 +1,5 @@
+<a>
+ Here is an XML document.
+ <b>
+ It has some elements, but it also has a hidden < error! (or two)
+ </a>
diff --git a/jni/ruby/test/rexml/data/basic.xml b/jni/ruby/test/rexml/data/basic.xml
new file mode 100644
index 0000000..88385fb
--- /dev/null
+++ b/jni/ruby/test/rexml/data/basic.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<foo>
+ <bar>
+ <baz/>
+ <cheese/>
+ <baz/>
+ <cheese/>
+ <baz/>
+ </bar>
+</foo>
diff --git a/jni/ruby/test/rexml/data/basicupdate.xml b/jni/ruby/test/rexml/data/basicupdate.xml
new file mode 100644
index 0000000..57d458c
--- /dev/null
+++ b/jni/ruby/test/rexml/data/basicupdate.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<xu:modifications xmlns:xu="http://www.xmldb.org/xupdate">
+
+ <xu:append select="/foo/bar/cheese[1]">
+ Goudse kaas
+ <edam type="jong belegen">Rond</edam>
+ </xu:append>
+
+ <xu:remove select="/foo/bar/baz[2]"/>
+
+ <xu:if test="/foo">
+ <xu:insert-before select="/foo/bar/baz[2]">
+ <cheese>More cheese!</cheese>
+ </xu:insert-before>
+ </xu:if>
+
+ <xu:insert-before select="/foo/bar/baz[2]">
+ <cheese>Even more cheese!</cheese>
+ </xu:insert-before>
+
+ <xu:if test="/bar">
+ <xu:insert-before select="/foo/bar/baz[2]">
+ <sausages>No sausages today</sausages>
+ </xu:insert-before>
+ </xu:if>
+
+ <xu:variable
+ xmlns:private="http://www.jaxen.org/private"
+ name="private:twice">
+ <cracker/>
+ <!-- champagne -->
+ <?oisters with a bit of lemon?>
+ </xu:variable>
+
+ <xu:variable name="twice" select="'Twice'"/>
+
+ <xu:insert-after
+ select="/foo/bar"
+ xmlns:private="http://www.jaxen.org/private"
+ >
+ <xu:value-of select="$private:twice"/>
+ <xu:value-of select="$private:twice"/>
+ <xu:value-of select="$twice"/>
+ </xu:insert-after>
+
+</xu:modifications>
diff --git a/jni/ruby/test/rexml/data/broken.rss b/jni/ruby/test/rexml/data/broken.rss
new file mode 100644
index 0000000..d5f29e5
--- /dev/null
+++ b/jni/ruby/test/rexml/data/broken.rss
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE rdf:RDF [
+ <!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
+ %HTMLlat1;
+]>
+
+<rdf:RDF
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:on="http://www.oreillynet.com/csrss/"
+ xmlns="http://purl.org/rss/1.0/"
+>
+
+ <channel rdf:about="http://www.oreillynet.com/">
+ <title>O'Reilly Network Articles</title>
+ <link>http://www.oreillynet.com/</link>
+ </channel>
+</rdf:RDF>
diff --git a/jni/ruby/test/rexml/data/contents.xml b/jni/ruby/test/rexml/data/contents.xml
new file mode 100644
index 0000000..35e3ac7
--- /dev/null
+++ b/jni/ruby/test/rexml/data/contents.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="XSL\JavaXML.html.xsl" type="text/xsl"?>
+<?xml-stylesheet href="XSL\JavaXML.wml.xsl" type="text/xsl"
+ media="wap"?>
+<?cocoon-process type="xslt"?>
+
+<!-- Java and XML -->
+<JavaXML:Book xmlns:JavaXML="http://www.oreilly.com/catalog/javaxml/"
+ xmlns:ora="http://www.oreilly.com"
+ xmlns:unused="http://www.unused.com"
+ ora:category="Java"
+>
+ <!-- comment one -->
+ <!-- comment two -->
+
+ <JavaXML:Title>Java and XML</JavaXML:Title>
+ <JavaXML:Contents xmlns:topic="http://www.oreilly.com/topics">
+ <JavaXML:Chapter topic:focus="XML">
+ <JavaXML:Heading>Introduction</JavaXML:Heading>
+ <JavaXML:Topic subSections="7">
+ What Is It?
+ </JavaXML:Topic>
+ <JavaXML:Topic subSections="3">
+ How Do I Use It?
+ </JavaXML:Topic>
+ <JavaXML:Topic subSections="4">
+ Why Should I Use It?
+ </JavaXML:Topic>
+ <JavaXML:Topic subSections="0">
+ What's Next?
+ </JavaXML:Topic>
+ </JavaXML:Chapter>
+
+ <JavaXML:Chapter topic:focus="XML">
+ <JavaXML:Heading>Creating XML</JavaXML:Heading>
+ <JavaXML:Topic subSections="0">An XML Document</JavaXML:Topic>
+ <JavaXML:Topic subSections="2">The Header</JavaXML:Topic>
+ <JavaXML:Topic subSections="6">The Content</JavaXML:Topic>
+ <JavaXML:Topic subSections="1">What's Next?</JavaXML:Topic>
+ </JavaXML:Chapter>
+
+ <JavaXML:Chapter topic:focus="Java">
+ <JavaXML:Heading>Parsing XML</JavaXML:Heading>
+ <JavaXML:Topic subSections="3">Getting Prepared</JavaXML:Topic>
+ <JavaXML:Topic subSections="3">SAX Readers</JavaXML:Topic>
+ <JavaXML:Topic subSections="9">Content Handlers</JavaXML:Topic>
+ <JavaXML:Topic subSections="4">Error Handlers</JavaXML:Topic>
+ <JavaXML:Topic subSections="0">
+ A Better Way to Load a Parser
+ </JavaXML:Topic>
+ <JavaXML:Topic subSections="4">"Gotcha!"</JavaXML:Topic>
+ <JavaXML:Topic subSections="0">What's Next?</JavaXML:Topic>
+ </JavaXML:Chapter>
+
+ <JavaXML:SectionBreak/>
+
+ <JavaXML:Chapter topic:focus="Java">
+ <JavaXML:Heading>Web Publishing Frameworks</JavaXML:Heading>
+ <JavaXML:Topic subSections="4">Selecting a Framework</JavaXML:Topic>
+ <JavaXML:Topic subSections="4">Installation</JavaXML:Topic>
+ <JavaXML:Topic subSections="3">
+ Using a Publishing Framework
+ </JavaXML:Topic>
+ <JavaXML:Topic subSections="2">XSP</JavaXML:Topic>
+ <JavaXML:Topic subSections="3">Cocoon 2.0 and Beyond</JavaXML:Topic>
+ <JavaXML:Topic subSections="0">What's Next?</JavaXML:Topic>
+ </JavaXML:Chapter>
+ </JavaXML:Contents>
+</JavaXML:Book>
diff --git a/jni/ruby/test/rexml/data/dash.xml b/jni/ruby/test/rexml/data/dash.xml
new file mode 100644
index 0000000..e1be655
--- /dev/null
+++ b/jni/ruby/test/rexml/data/dash.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<content-repository>
+ <content-repository-child-1>
+ <content-1>content-1-text</content-1>
+ </content-repository-child-1>
+ <content-repository-child-2>
+ <content-2>content-2-text</content-2>
+ </content-repository-child-2>
+ <content-repository-child-3>
+ <content-3>content-3-text</content-3>
+ </content-repository-child-3>
+</content-repository>
diff --git a/jni/ruby/test/rexml/data/defaultNamespace.xml b/jni/ruby/test/rexml/data/defaultNamespace.xml
new file mode 100644
index 0000000..1e32981
--- /dev/null
+++ b/jni/ruby/test/rexml/data/defaultNamespace.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<a xmlns="http://dummyNamespace/">
+ <b>
+ <c>Hello</c>
+ </b>
+</a>
diff --git a/jni/ruby/test/rexml/data/doctype_test.xml b/jni/ruby/test/rexml/data/doctype_test.xml
new file mode 100644
index 0000000..a690cab
--- /dev/null
+++ b/jni/ruby/test/rexml/data/doctype_test.xml
@@ -0,0 +1,34 @@
+<!DOCTYPE internationalization SYSTEM "l10n.dtd" [
+<!ENTITY af SYSTEM "af.xml">
+<!ENTITY ca SYSTEM "ca.xml">
+<!ENTITY cs SYSTEM "cs.xml">
+<!ENTITY da SYSTEM "da.xml">
+<!ENTITY de SYSTEM "de.xml">
+<!ENTITY el SYSTEM "el.xml">
+<!ENTITY en SYSTEM "en.xml">
+<!ENTITY es SYSTEM "es.xml">
+<!ENTITY et SYSTEM "et.xml">
+<!ENTITY fi SYSTEM "fi.xml">
+<!ENTITY fr SYSTEM "fr.xml">
+<!ENTITY hu SYSTEM "hu.xml">
+<!ENTITY id SYSTEM "id.xml">
+<!ENTITY it SYSTEM "it.xml">
+<!ENTITY ja SYSTEM "ja.xml">
+<!ENTITY ko SYSTEM "ko.xml">
+<!ENTITY nl SYSTEM "nl.xml">
+<!ENTITY no SYSTEM "no.xml">
+<!ENTITY no_ny SYSTEM "no_ny.xml">
+<!ENTITY pl SYSTEM "pl.xml">
+<!ENTITY pt SYSTEM "pt.xml">
+<!ENTITY pt_br SYSTEM "pt_br.xml">
+<!ENTITY ro SYSTEM "ro.xml">
+<!ENTITY ru SYSTEM "ru.xml">
+<!ENTITY sk SYSTEM "sk.xml">
+<!ENTITY sl SYSTEM "sl.xml">
+<!ENTITY sr SYSTEM "sr.xml">
+<!ENTITY sv SYSTEM "sv.xml">
+<!ENTITY tr SYSTEM "tr.xml">
+<!ENTITY zh_cn SYSTEM "zh_cn.xml">
+<!ENTITY zh_tw SYSTEM "zh_tw.xml">
+]>
+<x/>
diff --git a/jni/ruby/test/rexml/data/documentation.xml b/jni/ruby/test/rexml/data/documentation.xml
new file mode 100644
index 0000000..a1ad6e8
--- /dev/null
+++ b/jni/ruby/test/rexml/data/documentation.xml
@@ -0,0 +1,542 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/css" href="http://www.germane-software.com/repositories/public/documentation/documentation.css"?>
+<?xml-stylesheet alternative="yes" type="text/css" href="file:/home/ser/Work/documentation/documentation.css"?>
+<?xml-stylesheet alternative="yes" type="text/xsl" href="http://www.germane-software.com/repositories/public/documentation/paged.xsl"?>
+<!DOCTYPE documentation SYSTEM "http://www.germane-software.com/repositories/public/documentation/documentation.dtd">
+<documentation>
+ <head>
+ <title>REXML</title>
+
+ <banner href="img/rexml.png" />
+
+ <version>@ANT_VERSION@</version>
+
+ <date>@ANT_DATE@</date>
+
+ <home>http://www.germane-software.com/software/rexml</home>
+
+ <base>rexml</base>
+
+ <language>ruby</language>
+
+ <author email="ser@germane-software.com"
+ href="http://www.ser1.net/" jabber="seanerussell@gmail.com">Sean
+ Russell</author>
+ </head>
+
+ <overview>
+ <purpose lang="en">
+ <p>REXML is a conformant XML processor for the Ruby programming
+ language. REXML passes 100% of the Oasis non-validating tests and
+ includes full XPath support. It is reasonably fast, and is implemented
+ in pure Ruby. Best of all, it has a clean, intuitive API. REXML is
+ included in the standard library of Ruby</p>
+
+ <p>This software is distribute under the <link href="LICENSE.txt">Ruby
+ license</link>.</p>
+ </purpose>
+
+ <general>
+ <p>REXML arose out of a desire for a straightforward XML API, and is an
+ attempt at an API that doesn't require constant referencing of
+ documentation to do common tasks. "Keep the common case simple, and the
+ uncommon, possible."</p>
+
+ <p>REXML avoids The DOM API, which violates the maxim of simplicity. It
+ does provide <em>a</em> DOM model, but one that is Ruby-ized. It is an
+ XML API oriented for Ruby programmers, not for XML programmers coming
+ from Java.</p>
+
+ <p>Some of the common differences are that the Ruby API relies on block
+ enumerations, rather than iterators. For example, the Java code:</p>
+
+ <example>for (Enumeration e=parent.getChildren(); e.hasMoreElements(); ) {
+ Element child = (Element)e.nextElement(); // Do something with child
+}</example>
+
+ <p>in Ruby becomes:</p>
+
+ <example>parent.each_child{ |child| # Do something with child }</example>
+
+ <p>Can't you feel the peace and contentment in this block of code? Ruby
+ is the language Buddha would have programmed in.</p>
+
+ <p>One last thing. If you use and like this software, and you're in a
+ position of power in a company in Western Europe and are looking for a
+ software architect or developer, drop me a line. I took a lot of French
+ classes in college (all of which I've forgotten), and I lived in Munich
+ long enough that I was pretty fluent by the time I left, and I'd love to
+ get back over there.</p>
+ </general>
+
+ <features lang="en">
+ <item>Four intuitive parsing APIs.</item>
+
+ <item>Intuitive, powerful, and reasonably fast tree parsing API (a-la
+ DOM</item>
+
+ <item>Fast stream parsing API (a-la SAX)<footnote>This is not a SAX
+ API.</footnote></item>
+
+ <item>SAX2-based API<footnote>In addition to the native REXML streaming
+ API. This is slower than the native REXML API, but does a lot more work
+ for you.</footnote></item>
+
+ <item>Pull parsing API.</item>
+
+ <item>Small</item>
+
+ <item>Reasonably fast (for interpreted code)</item>
+
+ <item>Native Ruby</item>
+
+ <item>Full XPath support<footnote>Currently only available for the tree
+ API</footnote></item>
+
+ <item>XML 1.0 conformant<footnote>REXML passes all of the non-validating
+ OASIS tests. There are probably places where REXML isn't conformant, but
+ I try to fix them as they're reported.</footnote></item>
+
+ <item>ISO-8859-1, UNILE, UTF-16 and UTF-8 input and output; also,
+ support for any encoding the iconv supports.</item>
+
+ <item>Documentation</item>
+ </features>
+ </overview>
+
+ <operation lang="en">
+ <subsection title="Installation">
+ <p>You don't <em>have</em> to install anything; if you're running a
+ version of Ruby greater than 1.8, REXML is included. However, if you
+ choose to upgrade from the REXML distribution, run the command:
+ <code>ruby bin/install.rb</code>. By the way, you really should look at
+ these sorts of files before you run them as root. They could contain
+ anything, and since (in Ruby, at least) they tend to be mercifully
+ short, it doesn't hurt to glance over them. If you want to uninstall
+ REXML, run <code>ruby bin/install.rb -u</code>.</p>
+ </subsection>
+
+ <subsection title="Unit tests">
+ <p>If you have Test::Unit installed, you can run the unit test cases.
+ Run the command: <code>ruby bin/suite.rb</code>; it runs against the
+ distribution, not against the installed version.</p>
+ </subsection>
+
+ <subsection title="Benchmarks">
+ <p>There is a benchmark suite in <code>benchmarks/</code>. To run the
+ benchmarks, change into that directory and run <code>ruby
+ comparison.rb</code>. If you have nothing else installed, only the
+ benchmarks for REXML will be run. However, if you have any of the
+ following installed, benchmarks for those tools will also be run:</p>
+
+ <list>
+ <item>NQXML</item>
+
+ <item>XMLParser</item>
+
+ <item>Electric XML (you must copy <code>EXML.jar</code> into the
+ <code>benchmarks</code> directory and compile
+ <code>flatbench.java</code> before running the test)</item>
+ </list>
+
+ <p>The results will be written to <code>index.html</code>.</p>
+ </subsection>
+
+ <subsection title="General Usage">
+ <p>Please see <link href="docs/tutorial.html">the Tutorial</link>.</p>
+
+ <p>The API documentation is available <link
+ href="http://www.germane-software.com/software/XML/rexml/doc">on-line</link>,
+ or it can be downloaded as an archive <link
+ href="http://www.germane-software.com/software/archives/rexml_api_@ANT_VERSION@.tgz">in
+ tgz format (~70Kb)</link> or (if you're a masochist) <link
+ href="http://www.germane-software.com/software/archives/rexml_api_@ANT_VERSION@.zip">in
+ zip format (~280Kb)</link>. The best solution is to download and install
+ Dave Thomas' most excellent <link
+ href="http://rdoc.sourceforge.net">rdoc</link> and generate the API docs
+ yourself; then you'll be sure to have the latest API docs and won't have
+ to keep downloading the doc archive.</p>
+
+ <p>The unit tests in <code>test/</code> and the benchmarking code in
+ <code>benchmark/</code> provide additional examples of using REXML. The
+ Tutorial provides examples with commentary. The documentation unpacks
+ into <link href="doc/index.html"><code>rexml/doc</code></link>.</p>
+
+ <p>Kouhei Sutou maintains a <link
+ href="http://www.germane-software.com/software/rexml_doc_ja/current/index.html">Japanese
+ version</link> of the REXML API docs. <link
+ href="http://www.germane-software.com/software/rexml_doc_ja/current/japanese_documentation.html">Kou's
+ documentation page</link> contains links to binary archives for various
+ versions of the documentation.</p>
+ </subsection>
+ </operation>
+
+ <status>
+ <subsection title="Speed and Completeness">
+ <p>Unfortunately, NQXML is the only package REXML can be compared
+ against; XMLParser uses expat, which is a native library, and really is
+ a different beast altogether. So in comparing NQXML and REXML you can
+ look at four things: speed, size, completeness, and API.</p>
+
+ <p><link href="benchmarks/index.html">Benchmarks</link></p>
+
+ <p>REXML is faster than NQXML in some things, and slower than NQXML in a
+ couple of things. You can see this for yourself by running the supplied
+ benchmarks. Most of the places where REXML are slower are because of the
+ convenience methods<footnote>For example,
+ <code>element.elements[index]</code> isn't really an array operation;
+ index can be an Integer or an XPath, and this feature is relatively time
+ expensive.</footnote>. On the positive side, most of the convenience
+ methods can be bypassed if you know what you are doing. Check the <link
+ href="benchmarks/index.html"> benchmark comparison page</link> for a
+ <em>general</em> comparison. You can look at the benchmark code yourself
+ to decide how much salt to take with them.</p>
+
+ <p>The sizes of the XML parsers are close<footnote>As measured with
+ <code>ruby -nle 'print unless /^\s*(#.*|)$/' *.rb | wc -l</code>
+ </footnote>. NQXML 1.1.3 has 1580 non-blank, non-comment lines of code;
+ REXML 2.0 has 2340<footnote>REXML started out with about 1200, but that
+ number has been steadily increasing as features are added. XPath
+ accounts for 541 lines of that code, so the core REXML has about 1800
+ LOC.</footnote>.</p>
+
+ <p>REXML is a conformant XML 1.0 parser. It supports multiple language
+ encodings, and internal processing uses the required UTF-8 and UTF-16
+ encodings. It passes 100% of the Oasis non-validating tests.
+ Furthermore, it provides a full implementation of XPath, a SAX2 and a
+ PullParser API.</p>
+ </subsection>
+
+ <subsection title="XPath">
+ <p>As of release 2.0, XPath 1.0 is fully implemented.</p>
+
+ <p>I fully expect bugs to crop up from time to time, so if you see any
+ bogus XPath results, please let me know. That said, since I'm now
+ following the XPath grammar and spec fairly closely, I suspect that you
+ won't be surprised by REXML's XPath very often, and it should become
+ rock solid fairly quickly.</p>
+
+ <p>Check the "bugs" section for known problems; there are little bits of
+ XPath here and there that are not yet implemented, but I'll get to them
+ soon.</p>
+
+ <p>Namespace support is rather odd, but it isn't my fault. I can only do
+ so much and still conform to the specs. In particular, XPath attempts to
+ help as much as possible. Therefore, in the trivial cases, you can pass
+ namespace prefixes to Element.elements[...] and so on -- in these cases,
+ XPath will use the namespace environment of the base element you're
+ starting your XPath search from. However, if you want to do something
+ more complex, like pass in your own namespace environment, you have to
+ use the XPath first(), each(), and match() methods. Also, default
+ namespaces <em>force</em> you to use the XPath methods, rather than the
+ convenience methods, because there is no way for XPath to know what the
+ mappings for the default namespaces should be. This is exactly why I
+ loath namespaces -- a pox on the person(s) who thought them up!</p>
+ </subsection>
+
+ <subsection title="Namespaces">
+ <p>Namespace support is now fairly stable. One thing to be aware of is
+ that REXML is not (yet) a validating parser. This means that some
+ invalid namespace declarations are not caught.</p>
+ </subsection>
+
+ <subsection title="Mailing list">
+ <p>There is a low-volume mailing list dedicated to REXML. To subscribe,
+ send an empty email to <link
+ href="mailto:ser-rexml-subscribe@germane-software.com">ser-rexml-subscribe@germane-software.com</link>.
+ This list is more or less spam proof. To unsubscribe, similarly send a
+ message to <link
+ href="mailto:ser-rexml-unsubscribe@germane-software.com">ser-rexml-unsubscribe@germane-software.com</link>.</p>
+ </subsection>
+
+ <subsection title="RSS">
+ <p>An <link
+ href="http://www.germane-software.com/projects/rexml/timeline?ticket=on&amp;max=50&amp;daysback=90&amp;format=rss">RSS
+ file</link> for REXML is now being generated from the change log. This
+ allows you to be alerted of bug fixes and feature additions via "pull".
+ <link href="http://www.germane-software.com/software/rexml/rss.xml">Another
+ RSS</link> is available which contains a single item: the release notice
+ for the most recent release. This is an abuse of the RSS
+ mechanism, which was intended to be a distribution system for headlines
+ linked back to full articles, but it works. The headline for REXML is
+ the version number, and the description is the change log. The links all
+ link back to the REXML home page. The URL for the RSS itself is
+ http://www.germane-software.com/software/rexml/rss.xml.</p>
+
+ <p>The <link href="release.html">changelog itself is here</link>.</p>
+
+ <p>For those who are interested, there's a <link
+ href="docs/sloccount.txt">SLOCCount</link> (by David A. Wheeler) file
+ with stats on the REXML sourcecode. Note that the SLOCCount output
+ includes the files in the test/, benchmarks/, and bin/ directories, as
+ well as the main sourcecode for REXML itself.</p>
+ </subsection>
+
+ <subsection title="Applications that use REXML">
+ <list>
+ <item><link
+ href="http://www.pablotron.org/software/raggle/">Raggle</link> is a
+ console-based RSS aggregator.</item>
+
+ <item><link
+ href="http://www.zweknu.org/technical/index.rhtml?s=p|10/">getrss</link>
+ is an RSS aggregator</item>
+
+ <item>Ned Konz's <link
+ href="http://www.bikenomad.microship.com/ruby/">ruby-htmltools</link>
+ uses REXML</item>
+
+ <item>Hiroshi NAKAMURA's <link
+ href="http://www.ruby-lang.org/en/raa-list.rhtml?name=SOAP4R">SOAP4R</link>
+ package can use REXML as the XML processor.</item>
+
+ <item>Chris Morris' <link href="http://clabs.org/clxmlserial.htm">XML
+ Serializer</link>. XML Serializer provides a serialization mechanism
+ for Ruby that provides a bidirectional mapping between Ruby classes
+ and XML documents.</item>
+
+ <item>Much of the <link href="http://www.rubyxml.com">RubyXML</link>
+ site is generated with scripts that use REXML. RubyXML is a great
+ place to find information about th intersection between Ruby and
+ XML.</item>
+ </list>
+ </subsection>
+
+ <bugs lang="en">
+ <p>You can submit bug reports and feature requests, and view the list of
+ known bugs, at the <link
+ href="http://www.germane-software.com/projects/rexml">REXML bug report
+ page.</link> Please do submit bug reports. If you really want your bug
+ fixed fast, include an runit or Test::Unit method (or methods) that
+ illustrates the problem. At the very least, send me some XML that REXML
+ doesn't process properly.</p>
+
+ <p>You don't have to send an entire test suite -- just the unit test
+ methods. If you don't send me a unit test, I'll have to write one
+ myself, which will mean that your bug will take longer to fix.</p>
+
+ <p>When submitting bug reports, please include the version of Ruby and
+ of REXML that you're using, and the operating system you're running on.
+ Just run: <code>ruby -vrrexml/rexml -e 'p
+ REXML::VERSION,PLATFORM'</code> and paste the results in your bug
+ report. Include your email if you want a response about the bug.</p>
+
+ <item>Attributes are not handled internally as nodes, so you can't
+ perform node functions on them. This will have to change. It'll also
+ probably mean that, rather than returning attribute values, XPath will
+ return the Attribute nodes.</item>
+
+ <item>Some of the XPath <em>functions</em> are untested<footnote>Mike
+ Stok has been testing, debugging, and implementing some of these
+ Functions (and he's been doing a good job) so there's steady improvement
+ in this area.</footnote>. Any XPath functions that don't work are also
+ bugs... please report them. If you send a unit test that illustrates the
+ problem, I'll try to fix the problem within a couple of days (if I can)
+ and send you a patch, personally.</item>
+
+ <item>Accessing prefixes for which there is no defined namespace in an
+ XPath should throw an exception. It currently doesn't -- it just fails
+ to match.</item>
+ </bugs>
+
+ <todo lang="en">
+ <item>Reparsing a tree with a pull/SAX parser</item>
+
+ <item>Better namespace support in SAX</item>
+
+ <item>Lazy tree parsing</item>
+
+ <item>Segregate parsers, for optimized minimal distributions</item>
+
+ <item>XML &lt;-&gt; Ruby</item>
+
+ <item>Validation support</item>
+
+ <item>True XML character support</item>
+
+ <item>Add XPath support for streaming APIs</item>
+
+ <item status="request">XQuery support</item>
+
+ <item status="request">XUpdate support</item>
+
+ <item>Make sure namespaces are supported in pull parser</item>
+
+ <item status="request">Add document start and entity replacement events
+ in pull parser</item>
+
+ <item>Better stream parsing exception handling</item>
+
+ <item>I'd like to hack XMLRPC4R to use REXML, for my own
+ purposes.</item>
+ </todo>
+ </status>
+
+ <faq>
+ <q>REXML is hanging while parsing one of my XML files.</q>
+
+ <a>Your XML is probably malformed. Some malformed XML, especially XML that
+ contains literal '&lt;' embedded in the document, causes REXML to hang.
+ REXML should be throwing an exception, but it doesn't; this is a bug. I'm
+ aware that it is an extremely annoying bug, and it is one I'm trying to
+ solve in a way that doesn't significantly reduce REXML's parsing
+ speed.</a>
+
+ <q>I'm using the XPath '//foo' on an XML branch node X, and keep getting
+ all of the 'foo' elements in the entire document. Why? Shouldn't it return
+ only the 'foo' element descendants of X?</q>
+
+ <a>No. XPath specifies that '/' returns the document root, regardless of
+ the context node. '//' also starts at the document root. If you want to
+ limit your search to a branch, you need to use the self:: axe. EG,
+ 'self::node()//foo', or the shorthand './/foo'.</a>
+
+ <q>I want to parse a document both as a tree, and as a stream. Can I do
+ this?</q>
+
+ <a>Yes, and no. There is no mechanism that directly supports this in
+ REXML. However, aside from writing your own traversal layer, there is a
+ way of doing this. To turn a tree into a stream, just turn the branch you
+ want to process as a stream back into a string, and re-parse it with your
+ preferred API. EG: pp = PullParser.new( some_element.to_s ). The other
+ direction is more difficult; you basically have to build a tree from the
+ events. REXML will have one of these builders, eventually, but it doesn't
+ currently exist.</a>
+
+ <q>Why is Element.elements indexed off of '1' instead of '0'?</q>
+
+ <a>Because of XPath. The XPath specification states that the index of the
+ first child node is '1'. Although it may be counter-intuitive to base
+ elements on 1, it is more undesireable to have element.elements[0] ==
+ element.elements[ 'node()[1]' ]. Since I can't change the XPath
+ specification, the result is that Element.elements[1] is the first child
+ element.</a>
+
+ <q>Why isn't REXML a validating parser?</q>
+
+ <a>Because validating parsers must include code that parses and interprets
+ DTDs. I hate DTDs. REXML supports the barest minimum of DTD parsing, and
+ even that isn't complete. There is DTD parsing code in the works, but I
+ only work on it when I'm really, really bored. Rumor has it that a
+ contributor is working on a DTD parser for REXML; rest assured that any
+ such contribution will be included with REXML as soon as it is
+ available.</a>
+
+ <q>I'm trying to create an ISO-8859-1 document, but when I add text to the
+ document it isn't being properly encoded.</q>
+
+ <a>Regardless of what the encoding of your document is, when you add text
+ programmatically to a REXML document you <em>must</em> ensure that you are
+ only adding UTF-8 to the tree. In particular, you can't add ISO-8859-1
+ encoded text that contains characters above 0x80 to REXML trees -- you
+ must convert it to UTF-8 before doing so. Luckily, this is easy:
+ <code>text.unpack('C*').pack('U*')</code> will do the trick. 7-bit ASCII
+ is identical to UTF-8, so you probably won't need to worry about this.</a>
+
+ <q>How do I get the tag name of an Element?</q>
+
+ <a>You take a look at the APIs, and notice that <code>Element</code>
+ includes <code>Namespace</code>. Then you click on the
+ <code>Namespace</code> link and look at the methods that
+ <code>Element</code> includes from <code>Namespace</code>. One of these is
+ <code>name()</code>. Another is <code>expanded_name()</code>. Yet another
+ is <code>prefix()</code>. Then, you email the author of rdoc and ask him
+ to extend rdoc so that it lists methods in the API that are included from
+ other files, so that you don't have to do all of that looking around for
+ your method.</a>
+ </faq>
+
+ <credits>
+ <p>I've had help from a number of resources; if I haven't listed you here,
+ it means that I just haven't gotten around to adding you, or that I'm a
+ dork and have forgotten. In either case, feel free to write me and
+ complain.</p>
+
+ <list>
+ <item>Mike Stok has been very active, sending not only fixes for bugs
+ (especially in Functions), but also by providing unit tests and making
+ sure REXML runs under Ruby 1.7. He also sent the most awesome hand
+ knitted tea cozy, with "REXML" and the Ruby knitted into it.</item>
+
+ <item>Kouhei Sutou translated the REXML API documentation to Japanese!
+ Links are in the API docs section of the main documentation. He has also
+ contributed a large number of bug reports and patches to fix bugs in
+ REXML.</item>
+
+ <item>Erik Terpstra heard my pleas and submitted several logos for
+ REXML. After sagely procrastinating for several weeks, I finally forced
+ my poor slave of a wife to pick one (this is what we call "delegation").
+ She did, with caveats; Erik quickly made the changes, and the result is
+ what you now see at the top of this page. He also supplied a <link
+ href="img/rexml_50p.png">smaller version</link> that you can include
+ with your projects that use REXML, if you'd like.</item>
+
+ <item>Ernest Ellingson contributed the sourcecode for turning UTF16 and
+ UNILE encodings into UTF8, which allowed REXML to get the 100% OASIS
+ valid tests rating.</item>
+
+ <item>Ian Macdonald provided me with a comprehensive, well written RPM
+ spec file.</item>
+
+ <item>Oliver M . Bolzer is maintaining a Debian package distribution of
+ REXML. He also has provided good feedback and bug reports about
+ namespace support.</item>
+
+ <item>Michael Granger supplied a patch for REXML that make the unit
+ tests pass under Ruby 1.7.</item>
+
+ <item>James Britt contributed code that makes using
+ Document.parse_stream easier to use by allowing it to be passed either a
+ Source, File, or String.</item>
+
+ <item>Tobias Reif: Numerous bug reports, and suggestions for
+ improvement.</item>
+
+ <item>Stefan Scholl, who provided a lot of feedback and bug reports
+ while I was trying to get ISO-8859-1 support working.</item>
+
+ <item>Steven E Lumos for volunteering information about XPath
+ particulars.</item>
+
+ <item>Fumitoshi UKAI provided some bug fixes for CData metacharacter
+ quoting.</item>
+
+ <item>TAKAHASHI Masayoshi, for information on UTF</item>
+
+ <item>Robert Feldt: Bug reports and suggestions/recommendations about
+ improving REXML. Testing is one of the most important aspects of
+ software development.</item>
+
+ <item><link
+ href="http://www.themindelectric.com/exml/index.html">Electric
+ XML</link>: This was, after all, the inspiration for REXML. Originally,
+ I was just going to do a straight port, and although REXML doesn't in
+ any way, shape or form resemble Electric XML, still the basic framework
+ and philosophy was inspired by E-XML. And I still use E-XML in my Java
+ projects.</item>
+
+ <item><link
+ href="http://www.io.com/~jimm/downloads/nqxml/index.html">NQXML</link>:
+ While I may complain about the NQXML API, I wrote a few applications
+ using it that wouldn't have been written otherwise, and it was very
+ useful to me. It also encouraged me to write REXML. Never complain about
+ free software *slap*.</item>
+
+ <item>See my <link
+ href="http://www.germane-software.com/~ser/technology.html">technologies
+ page</link> for a more comprehensive list of computer technologies that
+ I depend on for my day-to-day work.</item>
+
+ <item>rdoc, an excellent JavaDoc analog<footnote>When I was first
+ working on REXML, rdoc wasn't, IMO, very good, so I wrote API2XML.
+ API2XML was good enough for a while, and then there was a flurry of work
+ on rdoc, and it quickly surpassed API2XML in features. Since I was never
+ really interested in maintaining a JavaDoc analog, I stopped support of
+ API2XML, and am now recommending that people use
+ rdoc.</footnote>.</item>
+
+ <item>Many, many other people who've submitted bug reports, suggestions,
+ and positive feedback. You're all co-developers!</item>
+ </list>
+ </credits>
+</documentation>
diff --git a/jni/ruby/test/rexml/data/euc.xml b/jni/ruby/test/rexml/data/euc.xml
new file mode 100644
index 0000000..a3c3419
--- /dev/null
+++ b/jni/ruby/test/rexml/data/euc.xml
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="euc-jp"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN">
+<html xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=euc-jp" />
+<meta http-equiv="Content-Style-Type" content="text/css" />
+<meta name="author" content="U.Nakamura" />
+<link rev="made" href="mailto:usa@ruby-lang.org" />
+<link rel="StyleSheet" href="./ruby.css" type="text/css" />
+<title>Ruby-mswin32</title>
+</head>
+<body>
+
+<h1><a id="top">Ruby-mswin32</a></h1>
+<p>뤤ϡWindowsȤνʤ襤 ;-(</p>
+<p>[ܸ / <a href="./index_en.html">English</a>]</p>
+
+
+
+<h2><a id="menu"> ܼ</a></h2>
+<ul>
+<li><a href="#remark">ջ</a></li>
+<li><a href="#ruby">RubyȤ?</a></li>
+<li><a href="#mswin32">mswin32rubyȤ?</a></li>
+<li><a href="#download">Хʥ </a></li>
+<li><a href="#install">󥹥ȡ</a></li>
+<li><a href="#recent">Ƕν</a></li>
+<li><a href="#link"></a></li>
+</ul>
+
+
+
+<h2><a id="remark"> ջ</a></h2>
+<p>ΥڡǤϡmswin32rubyۤѹΤΤ餻ԤäƤޤ</p>
+<p>̤˸ڡǤʤǤʤơ䤬˽񤤤ƤڡǤǤץࡦ(̵)ˤĤƤϡƼȽǤǤѤ<br />
+䤤碌<a href="mailto:usa@ruby-lang.org"></a>ءְäƤ¾οͤǤ򤫤褦ʤȤϤʤǤ͡</p>
+
+
+
+<h2><a id="ruby"> RubyȤ?</a></h2>
+<p><a href="http://www.ruby-lang.org/ja/">RubyΥ</a></p>
+
+
+
+<h2><a id="mswin32"> mswin32rubyȤ?</a></h2>
+<p>mswin32rubyȤϡ32bitWindows(Windows95Windows98WindowsMeWindows NTWindows 2000WindowsXPWindows 2003 ServerʲWindowsɽ)ưRubyΥХʥΰĤǤ<br />
+WindowsưRubyȤƤϡߡ5ΥХʥ꤬¸ߤޤϤ줾mswin32ǡcygwinǡmingw32ǡbccwin32ǡdjgppǤȸƤФƤޤ<br />
+줾ΰ㤤ޤȤȰʲΤ褦ˤʤޤ(¸ǧФΤ餻)</p>
+<dl>
+<dt><a id="label:1">mswin32</a></dt><dd>
+<p>VC++ǥѥ뤵롣Windows鸫ФäȤ̡פΥХʥȸ뤬ȿ̡RubyUNIXħŪʵǽΰѤǤʤ1.7.3ʹߤmingw32Ǥȳĥ饤֥ˤĤƤϥХʥߴ롣<br />
+RUBY_PLATFORM*-mswin32</p>
+</dd>
+<dt><a id="label:2">cygwin</a></dt><dd>
+<p>gccǥѥ뤵졢<a href="http://sources.redhat.com/cygwin/">cygwin</a>Ķư롣cygwinĶUNIX饤ʴĶWindowsǹۤΤǤΤǡcygwinrubyϰ̤UNIXѤΤΤȤƱ褦ư(ȤԤǤ)<br />
+RUBY_PLATFORM*-cygwin</p>
+</dd>
+<dt><a id="label:3">mingw32</a></dt><dd>
+<p>gccǥѥ뤵롣ϤۤȤmswin32Ǥȶ̤Ǥꡢ󥿥饤֥ⶦ(MSVCRT.dll)ʤΤǡư(餯)mswin32ǤȤۤƱ1.7.3ʹߤmswin32Ǥȳĥ饤֥ˤĤƤϥХʥߴ롣<br />
+RUBY_PLATFORM*-mingw32</p>
+</dd>
+<dt><a id="label:4">bccwin32</a></dt><dd>
+<p>BC++ǥѥ뤵롣Ϥʤʬmswin32Ǥȶ̤ǤϤ뤬󥿥饤֥꤬ۤʤΤǡ٤Ȥǵưmswin32ǤȤϰۤʤ(Ϥ)1.7ʹߤǥݡȤ롣<br />
+RUBY_PLATFORM*-bccwin32</p>
+</dd>
+<dt><a id="label:5">djgpp</a></dt><dd>
+<p>DJGPPǥѥ뤵롣DOSѤΥХʥʤΤǡDOSǤư롣ȿ̡WindowsˤäDOSˤʤǽ¿Ȥʤ(ͥåȥϢʤ)<br />
+RUBY_PLATFORM*-msdosdjgpp</p>
+</dd>
+</dl>
+<p>ΥڡǤϡ嵭Τmswin32ǤΤߤ򰷤äƤޤ<br />
+ʤcygwinǡmingw32ǡdjgppǤˤĤƤϤ錄ʤ٤<a href="http://www.os.rim.or.jp/~eban/">Ruby binaries</a>ǽǤޤbccwin32ǤˤĤƤϾ<a href="http://www001.upp.so-net.ne.jp/konishi/ruby/index.htm">Ruby</a>ǽǤ</p>
+
+
+
+<h2><a id="download"> Хʥ </a></h2>
+<p>ƤΥХʥVC++ 5.0(Version 11.00.7022 for 80x86)makeΤǤrubyΤ˴ؤƤϡ<a href="http://www.ruby-lang.org/ja/download.html">ɸۤΥ</a>(ޤ<a href="http://www.ruby-lang.org/~knu/cvsrepo-guide.html">CVS</a>Υ)餽Τޤ޺Ƥޤĥ饤֥ˤĤƤϳơΥ򻲾ȤƤ<br />
+ΥХʥzipǥ֤Ƥޤ</p>
+<p>md5sumΥåˡǤ㤨ruby󥹥ȡ뤵Ƥʤ鲼Τ褦ˡޤ<br />
+<code>ruby -r md5 -e "puts MD5.new(File.open('filename', 'rb').read).hexdigest"</code></p>
+
+
+
+<h3><a id="release">Release</a></h3>
+<ul>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.1-i386-mswin32.zip">ruby-1.8.1-i386-mswin32.zip</a> (3,764KB) <em>ǿRelease</em><br />
+ ruby 1.8.1 (2003-12-25) [i386-mswin32]<br />
+ md5sum : 6bbdabeb29f1a15fa69901e87d1108ac</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.0-i386-mswin32.zip">ruby-1.8.0-i386-mswin32.zip</a> (2,507KB)<br />
+ ruby 1.8.0 (2003-08-04) [i386-mswin32]<br />
+ md5sum : eaf9263062429fd4f722d9a70a38a9dc</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.8-i586-mswin32.zip">ruby-1.6.8-i586-mswin32.zip</a> (1,964KB)<br />
+ ruby 1.6.8 (2002-12-24) [i586-mswin32]<br />
+ md5sum : f704f1248ec25b96e3e1f3070afa915e</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.7-i586-mswin32.zip">ruby-1.6.7-i586-mswin32.zip</a> (1,972KB)<br />
+ ruby 1.6.7 (2002-03-01) [i586-mswin32]<br />
+ md5sum : ddedc40d0fc3b0ea1d6ac74f4976bfc6</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.6-i586-mswin32.zip">ruby-1.6.6-i586-mswin32.zip</a> (1,944KB)<br />
+ ruby 1.6.6 (2001-12-26) [i586-mswin32]<br />
+ md5sum : 96e0d1d19a37e5e7e50ae7ce99e34636</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.5-i586-mswin32.zip">ruby-1.6.5-i586-mswin32.zip</a> (1,896KB)<br />
+ ruby 1.6.5 (2001-09-19) [i586-mswin32]<br />
+ md5sum : c708ae98a05df2ff8dea5a70e3791bfa</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.4-i586-mswin32.zip">ruby-1.6.4-i586-mswin32.zip</a> (1,821KB)<br />
+ ruby 1.6.4 (2001-06-04) [i586-mswin32]<br />
+ md5sum : cf813ca19e40be164057b3562575e4da</li>
+</ul>
+
+
+
+
+
+
+
+<h3><a id="develop">Developing versions snapshots</a></h3>
+<ul>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.9.0-20040126-i386-mswin32.zip">ruby-1.9.0-20040126-i386-mswin32.zip</a> (3,849KB)<br />
+ ruby 1.9.0 (2004-01-26) [i386-mswin32]<br />
+ md5sum : fffafbf881cb6a85982220eae5a5b4f5</li>
+</ul>
+
+
+
+<h3><a id="stable">Stable versions snapshots</a></h3>
+<h4><a id="stable">1.8.0</a></h4>
+<ul>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.1-20040127-i386-mswin32.zip">ruby-1.8.1-20040127-i386-mswin32.zip</a> (3,822KB)<br />
+ ruby 1.8.1 (2004-01-27) [i386-mswin32]<br />
+ md5sum : 1fc0d5f53f0a75d0c6d1ca5d21082089</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.1-20031027-i386-mswin32.zip">ruby-1.8.1-20031027-i386-mswin32.zip</a> (3,075KB)<br />
+ ruby 1.8.1 (2003-10-27) [i386-mswin32]<br />
+ md5sum : 9dbdc644c529d207d0bda5d64478a5c4</li>
+</ul>
+<h4><a id="stable">1.6.8</a></h4>
+<ul>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.8-20030727-i586-mswin32.zip">ruby-1.6.8-20030727-i586-mswin32.zip</a> (2,093KB)<br />
+ ruby 1.6.8 (2003-07-27) [i586-mswin32]<br />
+ md5sum : 28c3b92b162319b3d6bc99c9996cad15</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.8-20030515-i586-mswin32.zip">ruby-1.6.8-20030515-i586-mswin32.zip</a> (2,091KB)<br />
+ ruby 1.6.8 (2003-05-15) [i586-mswin32]<br />
+ md5sum : e5f6558de261d111add4f657ad5e345f</li>
+</ul>
+
+
+
+<h3><a id="ext">Extension libraries</a></h3>
+<h4><a id="ext">1.8.0</a></h4>
+<ul>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/racc-1.4.4-all-i386-mswin32-1.8.zip">racc-1.4.4-all-i386-mswin32-1.8.zip</a> (70KB)<br />
+ <a href="http://www.loveruby.net/en/racc.html">racc</a> 1.4.4-all (for ruby 1.8.1)<br />
+ md5sum : 46c4d48b714fb1ded880e7e7af456b28</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/vrswin-030906-i386-mswin32-1.8.zip">vrswin-030906-i386-mswin32-1.8.zip</a> (54KB)<br />
+ <a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby (swin)</a> 030906 (for ruby 1.8.0)<br />
+ md5sum : 11c2d30e2a05e9ea7e097ec7b066cedf</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/vruby-030906-i386-mswin32-1.8.zip">vruby-030906-i386-mswin32-1.8.zip</a> (96KB)<br />
+ <a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby (vruby)</a> 030906 (for ruby 1.8.0)<br />
+ md5sum : 77a42995e42e869932f5fb282cc297ea</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/eruby-1.0.4-i386-mswin32-1.8.zip">eruby-1.0.4-i386-mswin32-1.8.zip</a> (75KB)<br />
+ <a href="http://www.modruby.net/">eruby</a> 1.0.4 (for ruby 1.8.0)<br />
+ md5sum : de7282647f015b1d20a28dcf7c2b8715</li>
+</ul>
+<h4><a id="ext">1.6.8</a></h4>
+<ul>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/vrswin-030521-i586-mswin32-1.6.zip">vrswin-030521-i586-mswin32-1.6.zip</a> (55KB)<br />
+ <a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby (swin)</a> 030521 (for ruby 1.6.8)<br />
+ md5sum : eae3284c6f79be7a119858ff9e940985</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/vruby-030517-i586-mswin32-1.6.zip">vruby-030517-i586-mswin32-1.6.zip</a> (95KB)<br />
+ <a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby (vruby)</a> 030517 (for ruby 1.6.8)<br />
+ md5sum : a32af752428cf3aa03000d66d8deca33</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/tmail-0.10.7-i586-mswin32-1.6.zip">tmail-0.10.7-i586-mswin32-1.6.zip</a> (160KB)<br />
+ <a href="http://www.loveruby.net/en/tmail.html">TMail</a> 0.10.7 (for ruby 1.6.8)<br />
+ md5sum : 74351ed81550dfbf3bfaf8252c316326</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/eruby-1.0.3-i586-mswin32-1.6.zip">eruby-1.0.3-i586-mswin32-1.6.zip</a> (74KB)<br />
+ <a href="http://www.modruby.net/">eruby</a> 1.0.3 (for ruby 1.6.8)<br />
+ md5sum : e05d654128422846f86ca84f55bf7bcb</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/rubywin-0.0.4.3-i586-mswin32-1.6.zip">rubywin-0.0.4.3-i586-mswin32-1.6.zip</a> (286KB)<br />
+ <a href="http://homepage1.nifty.com/markey/ruby/rubywin/index_e.html">RubyWin</a> 0.0.4.3 (for ruby 1.6.8)<br />
+ md5sum : 3f2226ef0c6e41b31c2f337f778e3e18</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/shim-20021224-i586-mswin32-1.6.zip">shim-20021224-i586-mswin32-1.6.zip</a> (138KB)<br />
+ <a href="http://www.ruby-lang.org/~knu/cgi-bin/cvsweb.cgi/shim/">Ruby Shim</a> 20021224 (for ruby 1.6.8)<br />
+ md5sum : 7ee4363195973a1df0584cb467e5ce82</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/racc-1.4.3-all-i586-mswin32-1.6.zip">racc-1.4.3-all-i586-mswin32-1.6.zip</a> (124KB)<br />
+ <a href="http://www.loveruby.net/en/racc.html">racc</a> 1.4.3-all (for ruby 1.6.8)<br />
+ md5sum : 1f093aabb464bef3074112949228a8c6</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/win32ole-0.5.2-i586-mswin32-1.6.zip">win32ole-0.5.2-i586-mswin32-1.6.zip</a> (59KB)<br />
+ <a href="http://homepage1.nifty.com/markey/ruby/win32ole/index_e.html">Win32OLE</a> 0.5.2 (for ruby 1.6.8)&lt;<br />
+ md5sum : 960f7205923a9243cff567d291b254ad</li>
+<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/uconv-0.4.11-i586-mswin32-1.6.zip">uconv-0.4.11-i586-mswin32-1.6.zip</a> (110KB)<br />
+ <a href="http://www.yoshidam.net/Ruby.html#uconv">uconv</a> 0.4.11 (for ruby 1.6.8)<br />
+ md5sum : c08f3662abee8e7186283741f89b88d7</li>
+</ul>
+
+
+
+<h2><a id="install"> 󥹥ȡ</a></h2>
+<p>嵭ΥХʥ򥤥󥹥ȡ뤹ϡߤΥǥ쥯ȥ(ʲ<code>$TOPDIR</code>ȵ)ŸƤǥ쥯ȥդǰ̤ƤޤΤǡŸˤϥǥ쥯ȥդŸΤ˺줺(̣狼ʤͤϵˤʤƤǤ)<br />
+Ÿϡ<code>$TOPDIR\bin</code><code>PATH</code>̤ƤƤ</p>
+<p>ʤʲγĥ饤֥ϡʪ˴ޤޤʤΥ饤֥˰¸Ƥޤ</p>
+<ul>
+<li>curses.so : PDCurses˰¸Ƥޤ</li>
+<li>dbm.so : GDBM˰¸Ƥޤ</li>
+<li>gdbm.so : GDBM˰¸Ƥޤ</li>
+<li>iconv.so : Iconv˰¸Ƥޤ</li>
+<li>openssl.so : OpenSSL˰¸Ƥޤ</li>
+<li>readline.so : readline˰¸Ƥޤ</li>
+<li>tcltklib.so : Tcl/Tk˰¸Ƥޤ</li>
+<li>zlib.so : Zlib˰¸Ƥޤ</li>
+</ul>
+<p>嵭ΤPDCursesGDBMOpenSSLreadlineZlibˤĤƤϡ<a href="http://jarp.jin.gr.jp/win32/">Porting Libraries to Win32</a>˥Хʥ꤬¸ߤޤ<br />
+IconvˤĤƤϡMeadowy.orgۤƤ<a href="http://www.meadowy.org/meadow/dists/snapshot/iconv-1.8.win32.zip">iconv-1.8.win32.zip</a>ѤƤޤ<br />
+Tcl/TkˤĤƤϡ<a href="http://www.activestate.com/">ActiveState</a>ۤƤ<a href="http://www.activestate.com/Products/ActiveTcl/">ActiveTcl</a>ѤƤޤ</p>
+
+
+
+<h2><a id="recent"> Ƕν</a></h2>
+
+
+
+<h3><a id="d20040127">2004-01-27</a></h3>
+<p>ޤƤǤȤޤä٤!</p>
+<p>ruby-1.8.1-20040127ruby-1.9.0-20040126֤ޤ<br />
+Ԥϼ1.8.1꡼Ǥȯ줿ԶνǤԤϳȯǡ</p>
+
+<h3><a id="d20031225">2003-12-25</a></h3>
+<p>꡼ꥹޥ! ruby-1.8.1꡼ޤ!<br />
+(previewФ㤤ޤʤ...)</p>
+
+<h3><a id="d20031206">2003-12-06</a></h3>
+<p>ruby-1.8.1-preview3֤ޤפΤ֤ۤ㤤ޤ͡</p>
+
+<h3><a id="d20031101">2003-11-01</a></h3>
+<p>ruby-1.8.1-preview2֤ޤ</p>
+
+<h3><a id="d20031028">2003-10-28</a></h3>
+<p>ruby-1.8.1-20031027֤ޤ<br />
+racc-1.4.4-all֤ޤ</p>
+
+<h3><a id="d20030907">2003-09-07</a></h3>
+<p>eruby-1.0.4vrswin-030906vruby-030906֤ޤ</p>
+
+<h3><a id="d20030812">2003-08-12</a></h3>
+<p>ruby-1.8.0-20030812֤ޤ</p>
+<p>vrswin-030811vruby-030811֤ޤ󤫤֤ĥ饤֥1.8Ѥˤʤޤ</p>
+
+<h3><a id="d20030804">2003-08-04</a></h3>
+<p>ruby-1.8.0֤ޤRuby 1.8ϺǽΥ꡼Ȥʤޤ<br />
+1.6ϤѹˤĤƤϡ<a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/changes.1.8.0">changes.1.8.0</a>ʤɤ</p>
+
+<h3><a id="d20030731">2003-07-31</a></h3>
+<p>ruby-1.8.0-preview6֤ޤȡޤƤ2previewФͤǤ :)</p>
+
+<h3><a id="d20030728">2003-07-28</a></h3>
+<p>ruby-1.8.0-preview5֤ޤ餯줬1.8.0κǸpreviewˤʤǤ礦<br />
+ruby-1.6.8-20030727֤ޤ</p>
+
+
+
+<h3><a id="old">ĤƤν</a></h3>
+<p><a href="./old.html"></a>ɤ</p>
+
+
+
+<h2><a id="link"> </a></h2>
+<p>mswin32Ǥ˴ؤ(Ȼפ)󥯤ǤĥäƤޤΤǡԹ礬<a href="mailto:usa@osb.att.ne.jp"></a>ޤǤϢ</p>
+<ul>
+<li><a href="http://www.ruby-lang.org/">Ruby Home Page</a> (<a href="http://www.ruby-lang.org/ja/">ܸ</a> / <a href="http://www.ruby-lang.org/en/">English</a>)<br />
+ 鷺Ȥ줿ܲȥȡ</li>
+<li>Ģ (<a href="http://homepage1.nifty.com/markey/">ܸ</a> / <a href="http://homepage1.nifty.com/markey/index_e.html">English</a>)<br />
+ ĤΥڡ<a href="http://homepage1.nifty.com/markey/ruby/rubywin/index.html">RubWin</a><a href="http://homepage1.nifty.com/markey/ruby/win32ole/index.html">Win32OLE</a>ʤɤޤ</li>
+<li><a href="http://www.moonwolf.com/ruby/">Script/Ruby</a> (ܸ)<br />
+ MoonWolfΥڡWin32Moduleʤɤޤ</li>
+<li>ActiveScriptRuby (<a href="http://www.geocities.co.jp/SiliconValley-PaloAlto/9251/ruby/index.html">ܸ</a> / <a href="http://www.geocities.co.jp/SiliconValley-PaloAlto/9251/ruby/main.html">English</a>)<br />
+ artonΥڡWindowsRuby̤ϤˤΤ⡣</li>
+<li>VisualuRubyײ() (<a href="http://www.osk.3web.ne.jp/~nyasu/software/vrproject.html">ܸ</a> / <a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">English</a>)<br />
+ nyasuΥڡVisualuRubyʤɤޤ(äƤ⤽Τޤ)WindowsRubyȤ<a href="http://www.osk.3web.ne.jp/~nyasu/software/rubyonwin.html">󥯽</a>¤ƤޤʤߤˡȤΥǥϤΥѥǤ¿!</li>
+<li>Ruby (<a href="http://www.yoshidam.net/Ruby_ja.html">ܸ</a> / <a href="http://www.yoshidam.net/Ruby.html">English</a>)<br />
+ 褷वΥڡ<a href="http://www.yoshidam.net/Ruby_ja.html#susie">Susieץ饰饤֥</a><a href="http://www.yoshidam.net/Ruby_ja.html#rddraw">DirectDraw for Ruby</a>ʤɤޤ</li>
+<li><a href="http://homepage2.nifty.com/sakazuki/rde.html">RDE(Ruby Development Environment)</a> (ܸ)<br />
+ sakazukiΥڡmswin32ǥ桼ʤɬǤ侩ĶartonȤActiveScriptRubyǤ (^^; ʪȤäƤʤưޤ</li>
+</ul>
+
+
+
+<p class="footer">
+[<a href="../"></a>]
+</p>
+<address>written by <a href="mailto:usa@ruby-lang.org">U.Nakamura</a></address>
+<p class="versions">
+ruby 1.9.0 (2004-01-13)<br />
+ERb 2.0.4<br />
+RDtool 0.6.11<br />
+rublog 0.0.2
+</p>
+
+</body>
+</html>
diff --git a/jni/ruby/test/rexml/data/evaluate.xml b/jni/ruby/test/rexml/data/evaluate.xml
new file mode 100644
index 0000000..90d06bd
--- /dev/null
+++ b/jni/ruby/test/rexml/data/evaluate.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<evaluate>
+ <data>
+ <jumps>
+ <subject>
+ <the/>
+ <fox color="brown"/>
+ <speed category="quick"/>
+ </subject>
+ <over/>
+ <object>
+ <the/>
+ <dog color="unspecified"/>
+ <speed category="lazy"/>
+ </object>
+ </jumps>
+ </data>
+
+ <!-- there is one element with attribute color="brown" should this
+ meta-test should succeed -->
+
+ <metatest select="//@color">brown</metatest>
+
+ <!-- there is no element with attribute category="moderate" -->
+ <metatest select="//speed/@category">moderate</metatest>
+
+</evaluate>
diff --git a/jni/ruby/test/rexml/data/fibo.xml b/jni/ruby/test/rexml/data/fibo.xml
new file mode 100644
index 0000000..9b5d0ec
--- /dev/null
+++ b/jni/ruby/test/rexml/data/fibo.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Fibonacci_Numbers>
+ <fibonacci index="0">0</fibonacci>
+ <fibonacci index="1">1</fibonacci>
+ <fibonacci index="2">1</fibonacci>
+ <fibonacci index="3">2</fibonacci>
+ <fibonacci index="4">3</fibonacci>
+ <fibonacci index="5">5</fibonacci>
+ <fibonacci index="6">8</fibonacci>
+ <fibonacci index="7">13</fibonacci>
+ <fibonacci index="8">21</fibonacci>
+ <fibonacci index="9">34</fibonacci>
+ <fibonacci index="10">55</fibonacci>
+ <fibonacci index="11">89</fibonacci>
+ <fibonacci index="12">144</fibonacci>
+ <fibonacci index="13">233</fibonacci>
+ <fibonacci index="14">377</fibonacci>
+ <fibonacci index="15">610</fibonacci>
+ <fibonacci index="16">987</fibonacci>
+ <fibonacci index="17">1597</fibonacci>
+ <fibonacci index="18">2584</fibonacci>
+ <fibonacci index="19">4181</fibonacci>
+ <fibonacci index="20">6765</fibonacci>
+ <fibonacci index="21">10946</fibonacci>
+ <fibonacci index="22">17711</fibonacci>
+ <fibonacci index="23">28657</fibonacci>
+ <fibonacci index="24">46368</fibonacci>
+ <fibonacci index="25">75025</fibonacci>
+</Fibonacci_Numbers>
diff --git a/jni/ruby/test/rexml/data/foo.xml b/jni/ruby/test/rexml/data/foo.xml
new file mode 100644
index 0000000..53b9a4e
--- /dev/null
+++ b/jni/ruby/test/rexml/data/foo.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!DOCTYPE schema SYSTEM "foo.dtd" [
+<!ATTLIST root-el
+ xmlns:human CDATA #FIXED "http://www.foo.com/human">
+]>
+<root-el xmlns="http://www.bar.com/doc"
+ xmlns:table="http://www.foo.com/table">
+ <human:leg>human leg</human:leg>
+ <table:leg>table leg</table:leg>
+</root-el>
diff --git a/jni/ruby/test/rexml/data/google.2.xml b/jni/ruby/test/rexml/data/google.2.xml
new file mode 100644
index 0000000..a1df93b
--- /dev/null
+++ b/jni/ruby/test/rexml/data/google.2.xml
@@ -0,0 +1,156 @@
+<form xmlns='http://www.w3.org/1999/xhtml'
+ enctype='application/x-www-form-urlencoded' class='rollover'
+ action='ModifyCampaign' method='POST'
+ onsubmit='return beforeRolloverSubmit(this, &apos;No campaigns selected.&apos;);'>
+ <a name='campaigns' shape='rect'/>
+ <tr bgcolor='#dbe6de'>
+ <th class='boxcolumn' rowspan='1' align='left' colspan='1'
+ width='1%'>
+ <script type='text/javascript'>document.write(" &lt;input
+ type\u003d\"checkbox\" name\u003d\"toggleAll\"
+ onclick\u003d\"rowToggleAll(this);\" title\u003d\"Select or
+ de-select all campaigns on this page\"&gt; ");</script>
+ </th>
+ <th bgcolor='#dbe6de' title='Sort by campaign name' nowrap='nowrap'
+ rowspan='1' align='left' colspan='1'>
+ <b>
+ <a href='CampaignSummary?campaignsummaryt=0%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Campaign Name</a>
+ </b>
+ </th>
+ <th bgcolor='#dbe6de' title='Sort by campaign status'
+ nowrap='nowrap' rowspan='1' align='left' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=1%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Current Status</a>
+ </th>
+ <th bgcolor='#dbe6de'
+ title='Sort by daily budget (maximum spending per day)'
+ nowrap='nowrap' rowspan='1' align='right' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=2%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Current Budget</a>
+ <span style='white-space: nowrap'>
+ <a href='/support/bin/answer.py?answer=6312&amp;hl=en_US'
+ shape='rect' id='' onclick='return helpPopUp(this);' style=''
+ target='google_popup'>[?]</a>
+ </span>
+ </th>
+ <th bgcolor='#c6d7cf' title='Sort by clicks on your ads'
+ nowrap='nowrap' rowspan='1' align='right' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=-3%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Clicks</a>
+ <a href='CampaignSummary?campaignsummaryt=-3%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ shape='rect' style='text-decoration:none;'>
+ <img src='/select/images/sortdown.gif' border='0' alt=''/>
+ </a>
+ </th>
+ <th bgcolor='#dbe6de' title='Sort by ad impressions served'
+ nowrap='nowrap' rowspan='1' align='right' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=4%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Impr.</a>
+ </th>
+ <th bgcolor='#dbe6de' title='Sort by CTR (clickthrough rate)'
+ nowrap='nowrap' rowspan='1' align='right' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=5%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>CTR</a>
+ </th>
+ <th bgcolor='#dbe6de' title='Sort by average cost per click (USD)'
+ nowrap='nowrap' rowspan='1' align='right' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=6%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Avg. CPC</a>
+ </th>
+ <th bgcolor='#dbe6de' class='' title='Sort by total cost (USD)'
+ nowrap='nowrap' rowspan='1' align='right' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=8%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Cost</a>
+ </th>
+ <th bgcolor='#dbe6de' title='Conversion Rate' nowrap='nowrap'
+ rowspan='1' align='right' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=11%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Conv. Rate</a>
+ </th>
+ <th bgcolor='#dbe6de' class='rightcolumn'
+ title='Cost per Conversion' nowrap='nowrap' rowspan='1'
+ align='right' colspan='1'>
+ <a href='CampaignSummary?campaignsummaryt=12%3Aa&amp;gsessionid=ezmXK9aaXnI#campaigns'
+ class='bluelink' shape='rect'>Cost/Conv.</a>
+ </th>
+ </tr>
+ <tr onmouseover='ron(3527627);' id='tr_3527627'
+ onmouseout='roff(3527627);'>
+ <td class='boxcolumn' rowspan='1' onclick='rowToggle(3527627);'
+ colspan='1'>
+ <input name='campaignid' type='checkbox' id='box_3527627'
+ value='3527627' onclick='toggleRow(this);'/>
+ </td>
+ <td rowspan='1' colspan='1'>
+ <a href='CampaignManagement?campaignid=3527627#a' shape='rect'>Test</a>
+ </td>
+ <td rowspan='1' colspan='1'>
+ <b>
+ <font size='-1' color='#b98b00'>Paused</font>
+ </b>
+ </td>
+ <td class='r' rowspan='1' colspan='1'>
+ <font color='#666666'>Test</font>
+ </td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='' rowspan='1' align='right' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='rightcolumn' rowspan='1' align='right' colspan='1'>1</td>
+ </tr>
+ <tr onmouseover='ron(7680287);' id='tr_7680287'
+ onmouseout='roff(7680287);'>
+ <td class='boxcolumn' rowspan='1' onclick='rowToggle(7680287);'
+ colspan='1'>
+ <input name='campaignid' type='checkbox' id='box_7680287'
+ value='7680287' onclick='toggleRow(this);'/>
+ </td>
+ <td rowspan='1' colspan='1'>
+ <a href='CampaignManagement?campaignid=7680287#a' shape='rect'>Test</a>
+ </td>
+ <td rowspan='1' colspan='1'>
+ <b>
+ <font size='-1' color='#b98b00'>Paused</font>
+ </b>
+ </td>
+ <td class='r' rowspan='1' colspan='1'>
+ <font color='#666666'>Test</font>
+ </td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='' rowspan='1' align='right' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='rightcolumn' rowspan='1' align='right' colspan='1'>1</td>
+ </tr>
+ <tr onmouseover='ron(6747347);' id='tr_6747347'
+ onmouseout='roff(6747347);'>
+ <td class='boxcolumn' rowspan='1' onclick='rowToggle(6747347);'
+ colspan='1'>
+ <input name='campaignid' type='checkbox' id='box_6747347'
+ value='6747347' onclick='toggleRow(this);'/>
+ </td>
+ <td rowspan='1' colspan='1'>
+ <a href='CampaignManagement?campaignid=6747347#a' shape='rect'>Test</a>
+ </td>
+ <td rowspan='1' colspan='1'>
+ <b>
+ <font size='-1' color='#b98b00'>Test</font>
+ </b>
+ </td>
+ <td class='r' rowspan='1' colspan='1'>
+ <font color='#666666'>Test</font>
+ </td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='' rowspan='1' align='right' colspan='1'>1</td>
+ <td class='r' rowspan='1' colspan='1'>1</td>
+ <td class='rightcolumn' rowspan='1' align='right' colspan='1'>1</td>
+ </tr>
+</form>
diff --git a/jni/ruby/test/rexml/data/id.xml b/jni/ruby/test/rexml/data/id.xml
new file mode 100644
index 0000000..749ab20
--- /dev/null
+++ b/jni/ruby/test/rexml/data/id.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE foo [
+
+<!ELEMENT foo (bar)>
+<!ATTLIST foo id CDATA #IMPLIED>
+<!ELEMENT bar (#PCDATA|cheese)*>
+<!ATTLIST bar id ID #REQUIRED>
+<!ELEMENT cheese (#PCDATA)>
+<!ATTLIST cheese kind ID #IMPLIED>
+]>
+
+<foo id="foobar">
+ <bar id="fb1">
+ baz
+ <cheese kind="edam">gouda</cheese>
+ baz
+ <cheese kind="gouda">cheddar</cheese>
+ baz
+ </bar>
+</foo>
diff --git a/jni/ruby/test/rexml/data/iso8859-1.xml b/jni/ruby/test/rexml/data/iso8859-1.xml
new file mode 100644
index 0000000..5fb04ec
--- /dev/null
+++ b/jni/ruby/test/rexml/data/iso8859-1.xml
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='ISO-8859-1'?>
+<booh>
+ <image caption='andr is nice'/>
+</booh>
diff --git a/jni/ruby/test/rexml/data/jaxen24.xml b/jni/ruby/test/rexml/data/jaxen24.xml
new file mode 100644
index 0000000..9b81996
--- /dev/null
+++ b/jni/ruby/test/rexml/data/jaxen24.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<body><p><span></span></p><div></div></body>
diff --git a/jni/ruby/test/rexml/data/jaxen3.xml b/jni/ruby/test/rexml/data/jaxen3.xml
new file mode 100644
index 0000000..a87723a
--- /dev/null
+++ b/jni/ruby/test/rexml/data/jaxen3.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration>
+ <hostname>
+ <val>2</val>
+ <attrlist>
+ <hostname>CE-A</hostname>
+ </attrlist>
+ </hostname>
+ <hostname>
+ <val>1</val>
+ <attrlist>
+ <hostname>CE-B</hostname>
+ </attrlist>
+ </hostname>
+</Configuration>
diff --git a/jni/ruby/test/rexml/data/lang.xml b/jni/ruby/test/rexml/data/lang.xml
new file mode 100644
index 0000000..49b45db
--- /dev/null
+++ b/jni/ruby/test/rexml/data/lang.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<e1 xml:lang="hr">
+ <e2 xml:lang="en-US">
+ <e3/>
+ </e2>
+ <e2 xml:lang="hu">
+ <e3/>
+ <e3/>
+ <e3 xml:lang="es"/>
+ </e2>
+</e1> \ No newline at end of file
diff --git a/jni/ruby/test/rexml/data/lang0.xml b/jni/ruby/test/rexml/data/lang0.xml
new file mode 100644
index 0000000..283b4e0
--- /dev/null
+++ b/jni/ruby/test/rexml/data/lang0.xml
@@ -0,0 +1,18 @@
+<programming_languages>
+ <language oop='yes'>
+ <name>
+ Ruby
+ </name>
+ <creator>
+ Yukihiro Matsumoto
+ </creator>
+ </language>
+ <language oop='yes'>
+ <name>
+ Python
+ </name>
+ <creator>
+ Guido van Rossum
+ </creator>
+ </language>
+</programming_languages>
diff --git a/jni/ruby/test/rexml/data/message.xml b/jni/ruby/test/rexml/data/message.xml
new file mode 100644
index 0000000..3b81df2
--- /dev/null
+++ b/jni/ruby/test/rexml/data/message.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<message>
+ <header>
+ <service>lookupformservice</service>
+ <connectionid>9</connectionid>
+ <appid>stammdaten</appid>
+ <action>new</action>
+ </header>
+ <body>
+ <data>
+ <items>
+ <item>
+ <name>iteminfo</name>
+ <value>ELE</value>
+ </item>
+ <item>
+ <name>parentinfo</name>
+ <value>Pruefgebiete</value>
+ </item>
+ <item>
+ <name>id</name>
+ <value>1</value>
+ </item>
+ </items>
+ </data>
+ </body>
+</message>
diff --git a/jni/ruby/test/rexml/data/moreover.xml b/jni/ruby/test/rexml/data/moreover.xml
new file mode 100644
index 0000000..38d4c4f
--- /dev/null
+++ b/jni/ruby/test/rexml/data/moreover.xml
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+ <moreovernews>
+ <article code="13563275">
+ <url>http://c.moreover.com/click/here.pl?x13563273</url>
+ <headline_text>e-Commerce Operators Present Version 1.0 of the XML Standard</headline_text>
+ <source>StockAccess</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.stockaccess.com/index.html</document_url>
+ <harvest_time>Dec 24 2000 6:28AM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13560996">
+ <url>http://c.moreover.com/click/here.pl?x13560995</url>
+ <headline_text>W3C Publishes XML Protocol Requirements Document</headline_text>
+ <source>Xml</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.xml.com/</document_url>
+ <harvest_time>Dec 24 2000 12:22AM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13553522">
+ <url>http://c.moreover.com/click/here.pl?x13553521</url>
+ <headline_text>Prowler: Open Source XML-Based Content Management Framework</headline_text>
+ <source>Xml</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.xml.com/</document_url>
+ <harvest_time>Dec 23 2000 2:05PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13549014">
+ <url>http://c.moreover.com/click/here.pl?x13549013</url>
+ <headline_text>The Middleware Company Debuts Public Training Courses in Ejb, J2ee And Xml</headline_text>
+ <source>Java Industry Connection</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
+ <harvest_time>Dec 23 2000 12:15PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13544468">
+ <url>http://c.moreover.com/click/here.pl?x13544467</url>
+ <headline_text>Revised Working Draft for the W3C XML Information Set</headline_text>
+ <source>Xml</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.xml.com/</document_url>
+ <harvest_time>Dec 23 2000 5:50AM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13534837">
+ <url>http://c.moreover.com/click/here.pl?x13534836</url>
+ <headline_text>XML: Its The Great Peacemaker</headline_text>
+ <source>ZDNet</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.zdnet.com/intweek/</document_url>
+ <harvest_time>Dec 22 2000 9:05PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13533486">
+ <url>http://c.moreover.com/click/here.pl?x13533485</url>
+ <headline_text>Project eL - The XML Leningrad Codex Markup Project</headline_text>
+ <source>Xml</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.xml.com/</document_url>
+ <harvest_time>Dec 22 2000 8:34PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13533489">
+ <url>http://c.moreover.com/click/here.pl?x13533488</url>
+ <headline_text>XML Linking Language (XLink) and XML Base Specifications Issued as W3C Proposed Recommenda</headline_text>
+ <source>Xml</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.xml.com/</document_url>
+ <harvest_time>Dec 22 2000 8:34PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13533493">
+ <url>http://c.moreover.com/click/here.pl?x13533492</url>
+ <headline_text>W3C Releases XHTML Basic Specification as a W3C Recommendation</headline_text>
+ <source>Xml</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.xml.com/</document_url>
+ <harvest_time>Dec 22 2000 8:34PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13521835">
+ <url>http://c.moreover.com/click/here.pl?x13521827</url>
+ <headline_text>Java, Xml And Oracle9i(TM) Make A Great Team</headline_text>
+ <source>Java Industry Connection</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
+ <harvest_time>Dec 22 2000 3:21PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13512020">
+ <url>http://c.moreover.com/click/here.pl?x13511233</url>
+ <headline_text>Competing initiatives to vie for security standard</headline_text>
+ <source>ZDNet</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.zdnet.com/eweek/filters/news/</document_url>
+ <harvest_time>Dec 22 2000 10:54AM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13492401">
+ <url>http://c.moreover.com/click/here.pl?x13492397</url>
+ <headline_text>Oracle Provides Developers with Great Xml Reading This Holiday Season</headline_text>
+ <source>Java Industry Connection</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
+ <harvest_time>Dec 21 2000 8:08PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13491296">
+ <url>http://c.moreover.com/click/here.pl?x13491292</url>
+ <headline_text>XML as the great peacemaker - Extensible Markup Language Accomplished The Seemingly Impossible This Year: It B</headline_text>
+ <source>Hospitality Net</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.hospitalitynet.org/news/list.htm?c=2000</document_url>
+ <harvest_time>Dec 21 2000 7:45PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13484761">
+ <url>http://c.moreover.com/click/here.pl?x13484758</url>
+ <headline_text>XML as the great peacemaker</headline_text>
+ <source>CNET</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://news.cnet.com/news/0-1003.html?tag=st.ne.1002.dir.1003</document_url>
+ <harvest_time>Dec 21 2000 4:41PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13480897">
+ <url>http://c.moreover.com/click/here.pl?x13480896</url>
+ <headline_text>COOP Switzerland Selects Mercator as Integration Platform</headline_text>
+ <source>Stockhouse Canada</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.stockhouse.ca/news/</document_url>
+ <harvest_time>Dec 21 2000 1:55PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13471024">
+ <url>http://c.moreover.com/click/here.pl?x13471023</url>
+ <headline_text>Competing XML Specs Move Toward a Union</headline_text>
+ <source>Internet World</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://www.internetworld.com/</document_url>
+ <harvest_time>Dec 21 2000 11:14AM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13452281">
+ <url>http://c.moreover.com/click/here.pl?x13452280</url>
+ <headline_text>Next-generation XHTML stripped down for handhelds</headline_text>
+ <source>CNET</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://news.cnet.com/news/0-1005.html?tag=st.ne.1002.dir.1005</document_url>
+ <harvest_time>Dec 20 2000 9:11PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13451791">
+ <url>http://c.moreover.com/click/here.pl?x13451789</url>
+ <headline_text>Xml Powers Oracle9i(TM) Dynamic Services</headline_text>
+ <source>Java Industry Connection</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
+ <harvest_time>Dec 20 2000 9:05PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13442098">
+ <url>http://c.moreover.com/click/here.pl?x13442097</url>
+ <headline_text>XML DOM reference guide</headline_text>
+ <source>ASPWire</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://aspwire.com/</document_url>
+ <harvest_time>Dec 20 2000 6:26PM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ <article code="13424118">
+ <url>http://c.moreover.com/click/here.pl?x13424117</url>
+ <headline_text>Repeat/Xqsite And Bowstreet Team to Deliver Integrated Xml Solutions</headline_text>
+ <source>Java Industry Connection</source>
+ <media_type>text</media_type>
+ <cluster>moreover...</cluster>
+ <tagline> </tagline>
+ <document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
+ <harvest_time>Dec 20 2000 9:04AM</harvest_time>
+ <access_registration> </access_registration>
+ <access_status> </access_status>
+ </article>
+ </moreovernews>
+
diff --git a/jni/ruby/test/rexml/data/much_ado.xml b/jni/ruby/test/rexml/data/much_ado.xml
new file mode 100644
index 0000000..f008fad
--- /dev/null
+++ b/jni/ruby/test/rexml/data/much_ado.xml
@@ -0,0 +1,6850 @@
+<?xml version="1.0"?>
+<PLAY>
+<TITLE>Much Ado about Nothing</TITLE>
+
+<FM>
+<P>Text placed in the public domain by Moby Lexical Tools, 1992.</P>
+<P>SGML markup by Jon Bosak, 1992-1994.</P>
+<P>XML version by Jon Bosak, 1996-1998.</P>
+<P>This work may be freely copied and distributed worldwide.</P>
+</FM>
+
+
+<PERSONAE>
+<TITLE>Dramatis Personae</TITLE>
+
+<PERSONA>DON PEDRO, prince of Arragon.</PERSONA>
+<PERSONA>DON JOHN, his bastard brother.</PERSONA>
+<PERSONA>CLAUDIO, a young lord of Florence.</PERSONA>
+<PERSONA>BENEDICK, a young lord of Padua.</PERSONA>
+<PERSONA>LEONATO, governor of Messina.</PERSONA>
+<PERSONA>ANTONIO, his brother.</PERSONA>
+<PERSONA>BALTHASAR, attendant on Don Pedro.</PERSONA>
+
+<PGROUP>
+<PERSONA>CONRADE</PERSONA>
+<PERSONA>BORACHIO</PERSONA>
+<GRPDESCR>followers of Don John.</GRPDESCR>
+</PGROUP>
+
+<PERSONA>FRIAR FRANCIS</PERSONA>
+<PERSONA>DOGBERRY, a constable.</PERSONA>
+<PERSONA>VERGES, a headborough.</PERSONA>
+<PERSONA>A Sexton.</PERSONA>
+<PERSONA>A Boy.</PERSONA>
+<PERSONA>HERO, daughter to Leonato.</PERSONA>
+<PERSONA>BEATRICE, niece to Leonato.</PERSONA>
+
+<PGROUP>
+<PERSONA>MARGARET</PERSONA>
+<PERSONA>URSULA</PERSONA>
+<GRPDESCR>gentlewomen attending on Hero.</GRPDESCR>
+</PGROUP>
+
+<PERSONA>Messengers, Watch, Attendants, &amp;c. </PERSONA>
+</PERSONAE>
+
+<SCNDESCR>SCENE Messina.</SCNDESCR>
+
+<PLAYSUBT>MUCH ADO ABOUT NOTHING</PLAYSUBT>
+
+<ACT><TITLE>ACT I</TITLE>
+
+<SCENE><TITLE>SCENE I. Before LEONATO'S house.</TITLE>
+<STAGEDIR>Enter LEONATO, HERO, and BEATRICE, with a
+Messenger</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I learn in this letter that Don Peter of Arragon</LINE>
+<LINE>comes this night to Messina.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>He is very near by this: he was not three leagues off</LINE>
+<LINE>when I left him.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>How many gentlemen have you lost in this action?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>But few of any sort, and none of name.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>A victory is twice itself when the achiever brings</LINE>
+<LINE>home full numbers. I find here that Don Peter hath</LINE>
+<LINE>bestowed much honour on a young Florentine called Claudio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>Much deserved on his part and equally remembered by</LINE>
+<LINE>Don Pedro: he hath borne himself beyond the</LINE>
+<LINE>promise of his age, doing, in the figure of a lamb,</LINE>
+<LINE>the feats of a lion: he hath indeed better</LINE>
+<LINE>bettered expectation than you must expect of me to</LINE>
+<LINE>tell you how.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>He hath an uncle here in Messina will be very much</LINE>
+<LINE>glad of it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>I have already delivered him letters, and there</LINE>
+<LINE>appears much joy in him; even so much that joy could</LINE>
+<LINE>not show itself modest enough without a badge of</LINE>
+<LINE>bitterness.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Did he break out into tears?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>In great measure.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>A kind overflow of kindness: there are no faces</LINE>
+<LINE>truer than those that are so washed. How much</LINE>
+<LINE>better is it to weep at joy than to joy at weeping!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I pray you, is Signior Mountanto returned from the</LINE>
+<LINE>wars or no?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>I know none of that name, lady: there was none such</LINE>
+<LINE>in the army of any sort.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>What is he that you ask for, niece?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>My cousin means Signior Benedick of Padua.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>O, he's returned; and as pleasant as ever he was.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>He set up his bills here in Messina and challenged</LINE>
+<LINE>Cupid at the flight; and my uncle's fool, reading</LINE>
+<LINE>the challenge, subscribed for Cupid, and challenged</LINE>
+<LINE>him at the bird-bolt. I pray you, how many hath he</LINE>
+<LINE>killed and eaten in these wars? But how many hath</LINE>
+<LINE>he killed? for indeed I promised to eat all of his killing.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Faith, niece, you tax Signior Benedick too much;</LINE>
+<LINE>but he'll be meet with you, I doubt it not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>He hath done good service, lady, in these wars.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>You had musty victual, and he hath holp to eat it:</LINE>
+<LINE>he is a very valiant trencherman; he hath an</LINE>
+<LINE>excellent stomach.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>And a good soldier too, lady.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>And a good soldier to a lady: but what is he to a lord?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>A lord to a lord, a man to a man; stuffed with all</LINE>
+<LINE>honourable virtues.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>It is so, indeed; he is no less than a stuffed man:</LINE>
+<LINE>but for the stuffing,--well, we are all mortal.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>You must not, sir, mistake my niece. There is a</LINE>
+<LINE>kind of merry war betwixt Signior Benedick and her:</LINE>
+<LINE>they never meet but there's a skirmish of wit</LINE>
+<LINE>between them.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Alas! he gets nothing by that. In our last</LINE>
+<LINE>conflict four of his five wits went halting off, and</LINE>
+<LINE>now is the whole man governed with one: so that if</LINE>
+<LINE>he have wit enough to keep himself warm, let him</LINE>
+<LINE>bear it for a difference between himself and his</LINE>
+<LINE>horse; for it is all the wealth that he hath left,</LINE>
+<LINE>to be known a reasonable creature. Who is his</LINE>
+<LINE>companion now? He hath every month a new sworn brother.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>Is't possible?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Very easily possible: he wears his faith but as</LINE>
+<LINE>the fashion of his hat; it ever changes with the</LINE>
+<LINE>next block.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>I see, lady, the gentleman is not in your books.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>No; an he were, I would burn my study. But, I pray</LINE>
+<LINE>you, who is his companion? Is there no young</LINE>
+<LINE>squarer now that will make a voyage with him to the devil?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>He is most in the company of the right noble Claudio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>O Lord, he will hang upon him like a disease: he</LINE>
+<LINE>is sooner caught than the pestilence, and the taker</LINE>
+<LINE>runs presently mad. God help the noble Claudio! if</LINE>
+<LINE>he have caught the Benedick, it will cost him a</LINE>
+<LINE>thousand pound ere a' be cured.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>I will hold friends with you, lady.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Do, good friend.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>You will never run mad, niece.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>No, not till a hot January.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>Don Pedro is approached.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter DON PEDRO, DON JOHN, CLAUDIO, BENEDICK,
+and BALTHASAR</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Good Signior Leonato, you are come to meet your</LINE>
+<LINE>trouble: the fashion of the world is to avoid</LINE>
+<LINE>cost, and you encounter it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Never came trouble to my house in the likeness of</LINE>
+<LINE>your grace: for trouble being gone, comfort should</LINE>
+<LINE>remain; but when you depart from me, sorrow abides</LINE>
+<LINE>and happiness takes his leave.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>You embrace your charge too willingly. I think this</LINE>
+<LINE>is your daughter.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Her mother hath many times told me so.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Were you in doubt, sir, that you asked her?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Signior Benedick, no; for then were you a child.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>You have it full, Benedick: we may guess by this</LINE>
+<LINE>what you are, being a man. Truly, the lady fathers</LINE>
+<LINE>herself. Be happy, lady; for you are like an</LINE>
+<LINE>honourable father.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>If Signior Leonato be her father, she would not</LINE>
+<LINE>have his head on her shoulders for all Messina, as</LINE>
+<LINE>like him as she is.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I wonder that you will still be talking, Signior</LINE>
+<LINE>Benedick: nobody marks you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>What, my dear Lady Disdain! are you yet living?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Is it possible disdain should die while she hath</LINE>
+<LINE>such meet food to feed it as Signior Benedick?</LINE>
+<LINE>Courtesy itself must convert to disdain, if you come</LINE>
+<LINE>in her presence.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Then is courtesy a turncoat. But it is certain I</LINE>
+<LINE>am loved of all ladies, only you excepted: and I</LINE>
+<LINE>would I could find in my heart that I had not a hard</LINE>
+<LINE>heart; for, truly, I love none.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>A dear happiness to women: they would else have</LINE>
+<LINE>been troubled with a pernicious suitor. I thank God</LINE>
+<LINE>and my cold blood, I am of your humour for that: I</LINE>
+<LINE>had rather hear my dog bark at a crow than a man</LINE>
+<LINE>swear he loves me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>God keep your ladyship still in that mind! so some</LINE>
+<LINE>gentleman or other shall 'scape a predestinate</LINE>
+<LINE>scratched face.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Scratching could not make it worse, an 'twere such</LINE>
+<LINE>a face as yours were.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Well, you are a rare parrot-teacher.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>A bird of my tongue is better than a beast of yours.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I would my horse had the speed of your tongue, and</LINE>
+<LINE>so good a continuer. But keep your way, i' God's</LINE>
+<LINE>name; I have done.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>You always end with a jade's trick: I know you of old.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>That is the sum of all, Leonato. Signior Claudio</LINE>
+<LINE>and Signior Benedick, my dear friend Leonato hath</LINE>
+<LINE>invited you all. I tell him we shall stay here at</LINE>
+<LINE>the least a month; and he heartily prays some</LINE>
+<LINE>occasion may detain us longer. I dare swear he is no</LINE>
+<LINE>hypocrite, but prays from his heart.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>If you swear, my lord, you shall not be forsworn.</LINE>
+<STAGEDIR>To DON JOHN</STAGEDIR>
+<LINE>Let me bid you welcome, my lord: being reconciled to</LINE>
+<LINE>the prince your brother, I owe you all duty.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I thank you: I am not of many words, but I thank</LINE>
+<LINE>you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Please it your grace lead on?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Your hand, Leonato; we will go together.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt all except BENEDICK and CLAUDIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Benedick, didst thou note the daughter of Signior Leonato?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I noted her not; but I looked on her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Is she not a modest young lady?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Do you question me, as an honest man should do, for</LINE>
+<LINE>my simple true judgment; or would you have me speak</LINE>
+<LINE>after my custom, as being a professed tyrant to their sex?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>No; I pray thee speak in sober judgment.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Why, i' faith, methinks she's too low for a high</LINE>
+<LINE>praise, too brown for a fair praise and too little</LINE>
+<LINE>for a great praise: only this commendation I can</LINE>
+<LINE>afford her, that were she other than she is, she</LINE>
+<LINE>were unhandsome; and being no other but as she is, I</LINE>
+<LINE>do not like her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Thou thinkest I am in sport: I pray thee tell me</LINE>
+<LINE>truly how thou likest her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Would you buy her, that you inquire after her?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Can the world buy such a jewel?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Yea, and a case to put it into. But speak you this</LINE>
+<LINE>with a sad brow? or do you play the flouting Jack,</LINE>
+<LINE>to tell us Cupid is a good hare-finder and Vulcan a</LINE>
+<LINE>rare carpenter? Come, in what key shall a man take</LINE>
+<LINE>you, to go in the song?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>In mine eye she is the sweetest lady that ever I</LINE>
+<LINE>looked on.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I can see yet without spectacles and I see no such</LINE>
+<LINE>matter: there's her cousin, an she were not</LINE>
+<LINE>possessed with a fury, exceeds her as much in beauty</LINE>
+<LINE>as the first of May doth the last of December. But I</LINE>
+<LINE>hope you have no intent to turn husband, have you?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I would scarce trust myself, though I had sworn the</LINE>
+<LINE>contrary, if Hero would be my wife.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Is't come to this? In faith, hath not the world</LINE>
+<LINE>one man but he will wear his cap with suspicion?</LINE>
+<LINE>Shall I never see a bachelor of three-score again?</LINE>
+<LINE>Go to, i' faith; an thou wilt needs thrust thy neck</LINE>
+<LINE>into a yoke, wear the print of it and sigh away</LINE>
+<LINE>Sundays. Look Don Pedro is returned to seek you.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Re-enter DON PEDRO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>What secret hath held you here, that you followed</LINE>
+<LINE>not to Leonato's?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I would your grace would constrain me to tell.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I charge thee on thy allegiance.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>You hear, Count Claudio: I can be secret as a dumb</LINE>
+<LINE>man; I would have you think so; but, on my</LINE>
+<LINE>allegiance, mark you this, on my allegiance. He is</LINE>
+<LINE>in love. With who? now that is your grace's part.</LINE>
+<LINE>Mark how short his answer is;--With Hero, Leonato's</LINE>
+<LINE>short daughter.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If this were so, so were it uttered.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Like the old tale, my lord: 'it is not so, nor</LINE>
+<LINE>'twas not so, but, indeed, God forbid it should be</LINE>
+<LINE>so.'</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If my passion change not shortly, God forbid it</LINE>
+<LINE>should be otherwise.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Amen, if you love her; for the lady is very well worthy.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>You speak this to fetch me in, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>By my troth, I speak my thought.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And, in faith, my lord, I spoke mine.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>And, by my two faiths and troths, my lord, I spoke mine.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>That I love her, I feel.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>That she is worthy, I know.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>That I neither feel how she should be loved nor</LINE>
+<LINE>know how she should be worthy, is the opinion that</LINE>
+<LINE>fire cannot melt out of me: I will die in it at the stake.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Thou wast ever an obstinate heretic in the despite</LINE>
+<LINE>of beauty.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And never could maintain his part but in the force</LINE>
+<LINE>of his will.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>That a woman conceived me, I thank her; that she</LINE>
+<LINE>brought me up, I likewise give her most humble</LINE>
+<LINE>thanks: but that I will have a recheat winded in my</LINE>
+<LINE>forehead, or hang my bugle in an invisible baldrick,</LINE>
+<LINE>all women shall pardon me. Because I will not do</LINE>
+<LINE>them the wrong to mistrust any, I will do myself the</LINE>
+<LINE>right to trust none; and the fine is, for the which</LINE>
+<LINE>I may go the finer, I will live a bachelor.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I shall see thee, ere I die, look pale with love.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>With anger, with sickness, or with hunger, my lord,</LINE>
+<LINE>not with love: prove that ever I lose more blood</LINE>
+<LINE>with love than I will get again with drinking, pick</LINE>
+<LINE>out mine eyes with a ballad-maker's pen and hang me</LINE>
+<LINE>up at the door of a brothel-house for the sign of</LINE>
+<LINE>blind Cupid.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Well, if ever thou dost fall from this faith, thou</LINE>
+<LINE>wilt prove a notable argument.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>If I do, hang me in a bottle like a cat and shoot</LINE>
+<LINE>at me; and he that hits me, let him be clapped on</LINE>
+<LINE>the shoulder, and called Adam.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Well, as time shall try: 'In time the savage bull</LINE>
+<LINE>doth bear the yoke.'</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>The savage bull may; but if ever the sensible</LINE>
+<LINE>Benedick bear it, pluck off the bull's horns and set</LINE>
+<LINE>them in my forehead: and let me be vilely painted,</LINE>
+<LINE>and in such great letters as they write 'Here is</LINE>
+<LINE>good horse to hire,' let them signify under my sign</LINE>
+<LINE>'Here you may see Benedick the married man.'</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If this should ever happen, thou wouldst be horn-mad.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Nay, if Cupid have not spent all his quiver in</LINE>
+<LINE>Venice, thou wilt quake for this shortly.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I look for an earthquake too, then.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Well, you temporize with the hours. In the</LINE>
+<LINE>meantime, good Signior Benedick, repair to</LINE>
+<LINE>Leonato's: commend me to him and tell him I will</LINE>
+<LINE>not fail him at supper; for indeed he hath made</LINE>
+<LINE>great preparation.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I have almost matter enough in me for such an</LINE>
+<LINE>embassage; and so I commit you--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>To the tuition of God: From my house, if I had it,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>The sixth of July: Your loving friend, Benedick.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Nay, mock not, mock not. The body of your</LINE>
+<LINE>discourse is sometime guarded with fragments, and</LINE>
+<LINE>the guards are but slightly basted on neither: ere</LINE>
+<LINE>you flout old ends any further, examine your</LINE>
+<LINE>conscience: and so I leave you.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>My liege, your highness now may do me good.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>My love is thine to teach: teach it but how,</LINE>
+<LINE>And thou shalt see how apt it is to learn</LINE>
+<LINE>Any hard lesson that may do thee good.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Hath Leonato any son, my lord?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>No child but Hero; she's his only heir.</LINE>
+<LINE>Dost thou affect her, Claudio?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>O, my lord,</LINE>
+<LINE>When you went onward on this ended action,</LINE>
+<LINE>I look'd upon her with a soldier's eye,</LINE>
+<LINE>That liked, but had a rougher task in hand</LINE>
+<LINE>Than to drive liking to the name of love:</LINE>
+<LINE>But now I am return'd and that war-thoughts</LINE>
+<LINE>Have left their places vacant, in their rooms</LINE>
+<LINE>Come thronging soft and delicate desires,</LINE>
+<LINE>All prompting me how fair young Hero is,</LINE>
+<LINE>Saying, I liked her ere I went to wars.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Thou wilt be like a lover presently</LINE>
+<LINE>And tire the hearer with a book of words.</LINE>
+<LINE>If thou dost love fair Hero, cherish it,</LINE>
+<LINE>And I will break with her and with her father,</LINE>
+<LINE>And thou shalt have her. Was't not to this end</LINE>
+<LINE>That thou began'st to twist so fine a story?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>How sweetly you do minister to love,</LINE>
+<LINE>That know love's grief by his complexion!</LINE>
+<LINE>But lest my liking might too sudden seem,</LINE>
+<LINE>I would have salved it with a longer treatise.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>What need the bridge much broader than the flood?</LINE>
+<LINE>The fairest grant is the necessity.</LINE>
+<LINE>Look, what will serve is fit: 'tis once, thou lovest,</LINE>
+<LINE>And I will fit thee with the remedy.</LINE>
+<LINE>I know we shall have revelling to-night:</LINE>
+<LINE>I will assume thy part in some disguise</LINE>
+<LINE>And tell fair Hero I am Claudio,</LINE>
+<LINE>And in her bosom I'll unclasp my heart</LINE>
+<LINE>And take her hearing prisoner with the force</LINE>
+<LINE>And strong encounter of my amorous tale:</LINE>
+<LINE>Then after to her father will I break;</LINE>
+<LINE>And the conclusion is, she shall be thine.</LINE>
+<LINE>In practise let us put it presently.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE II. A room in LEONATO's house.</TITLE>
+<STAGEDIR>Enter LEONATO and ANTONIO, meeting</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>How now, brother! Where is my cousin, your son?</LINE>
+<LINE>hath he provided this music?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>He is very busy about it. But, brother, I can tell</LINE>
+<LINE>you strange news that you yet dreamt not of.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Are they good?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>As the event stamps them: but they have a good</LINE>
+<LINE>cover; they show well outward. The prince and Count</LINE>
+<LINE>Claudio, walking in a thick-pleached alley in mine</LINE>
+<LINE>orchard, were thus much overheard by a man of mine:</LINE>
+<LINE>the prince discovered to Claudio that he loved my</LINE>
+<LINE>niece your daughter and meant to acknowledge it</LINE>
+<LINE>this night in a dance: and if he found her</LINE>
+<LINE>accordant, he meant to take the present time by the</LINE>
+<LINE>top and instantly break with you of it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Hath the fellow any wit that told you this?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>A good sharp fellow: I will send for him; and</LINE>
+<LINE>question him yourself.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>No, no; we will hold it as a dream till it appear</LINE>
+<LINE>itself: but I will acquaint my daughter withal,</LINE>
+<LINE>that she may be the better prepared for an answer,</LINE>
+<LINE>if peradventure this be true. Go you and tell her of it.</LINE>
+<STAGEDIR>Enter Attendants</STAGEDIR>
+<LINE>Cousins, you know what you have to do. O, I cry you</LINE>
+<LINE>mercy, friend; go you with me, and I will use your</LINE>
+<LINE>skill. Good cousin, have a care this busy time.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE III. The same.</TITLE>
+<STAGEDIR>Enter DON JOHN and CONRADE</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>What the good-year, my lord! why are you thus out</LINE>
+<LINE>of measure sad?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>There is no measure in the occasion that breeds;</LINE>
+<LINE>therefore the sadness is without limit.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>You should hear reason.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>And when I have heard it, what blessing brings it?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>If not a present remedy, at least a patient</LINE>
+<LINE>sufferance.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I wonder that thou, being, as thou sayest thou art,</LINE>
+<LINE>born under Saturn, goest about to apply a moral</LINE>
+<LINE>medicine to a mortifying mischief. I cannot hide</LINE>
+<LINE>what I am: I must be sad when I have cause and smile</LINE>
+<LINE>at no man's jests, eat when I have stomach and wait</LINE>
+<LINE>for no man's leisure, sleep when I am drowsy and</LINE>
+<LINE>tend on no man's business, laugh when I am merry and</LINE>
+<LINE>claw no man in his humour.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Yea, but you must not make the full show of this</LINE>
+<LINE>till you may do it without controlment. You have of</LINE>
+<LINE>late stood out against your brother, and he hath</LINE>
+<LINE>ta'en you newly into his grace; where it is</LINE>
+<LINE>impossible you should take true root but by the</LINE>
+<LINE>fair weather that you make yourself: it is needful</LINE>
+<LINE>that you frame the season for your own harvest.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I had rather be a canker in a hedge than a rose in</LINE>
+<LINE>his grace, and it better fits my blood to be</LINE>
+<LINE>disdained of all than to fashion a carriage to rob</LINE>
+<LINE>love from any: in this, though I cannot be said to</LINE>
+<LINE>be a flattering honest man, it must not be denied</LINE>
+<LINE>but I am a plain-dealing villain. I am trusted with</LINE>
+<LINE>a muzzle and enfranchised with a clog; therefore I</LINE>
+<LINE>have decreed not to sing in my cage. If I had my</LINE>
+<LINE>mouth, I would bite; if I had my liberty, I would do</LINE>
+<LINE>my liking: in the meantime let me be that I am and</LINE>
+<LINE>seek not to alter me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Can you make no use of your discontent?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I make all use of it, for I use it only.</LINE>
+<LINE>Who comes here?</LINE>
+<STAGEDIR>Enter BORACHIO</STAGEDIR>
+<LINE>What news, Borachio?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>I came yonder from a great supper: the prince your</LINE>
+<LINE>brother is royally entertained by Leonato: and I</LINE>
+<LINE>can give you intelligence of an intended marriage.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Will it serve for any model to build mischief on?</LINE>
+<LINE>What is he for a fool that betroths himself to</LINE>
+<LINE>unquietness?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Marry, it is your brother's right hand.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Who? the most exquisite Claudio?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Even he.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>A proper squire! And who, and who? which way looks</LINE>
+<LINE>he?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Marry, on Hero, the daughter and heir of Leonato.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>A very forward March-chick! How came you to this?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Being entertained for a perfumer, as I was smoking a</LINE>
+<LINE>musty room, comes me the prince and Claudio, hand</LINE>
+<LINE>in hand in sad conference: I whipt me behind the</LINE>
+<LINE>arras; and there heard it agreed upon that the</LINE>
+<LINE>prince should woo Hero for himself, and having</LINE>
+<LINE>obtained her, give her to Count Claudio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Come, come, let us thither: this may prove food to</LINE>
+<LINE>my displeasure. That young start-up hath all the</LINE>
+<LINE>glory of my overthrow: if I can cross him any way, I</LINE>
+<LINE>bless myself every way. You are both sure, and will assist me?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>To the death, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Let us to the great supper: their cheer is the</LINE>
+<LINE>greater that I am subdued. Would the cook were of</LINE>
+<LINE>my mind! Shall we go prove what's to be done?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>We'll wait upon your lordship.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+</ACT>
+
+<ACT><TITLE>ACT II</TITLE>
+
+<SCENE><TITLE>SCENE I. A hall in LEONATO'S house.</TITLE>
+<STAGEDIR>Enter LEONATO, ANTONIO, HERO, BEATRICE, and others</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Was not Count John here at supper?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>I saw him not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>How tartly that gentleman looks! I never can see</LINE>
+<LINE>him but I am heart-burned an hour after.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>He is of a very melancholy disposition.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>He were an excellent man that were made just in the</LINE>
+<LINE>midway between him and Benedick: the one is too</LINE>
+<LINE>like an image and says nothing, and the other too</LINE>
+<LINE>like my lady's eldest son, evermore tattling.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Then half Signior Benedick's tongue in Count John's</LINE>
+<LINE>mouth, and half Count John's melancholy in Signior</LINE>
+<LINE>Benedick's face,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>With a good leg and a good foot, uncle, and money</LINE>
+<LINE>enough in his purse, such a man would win any woman</LINE>
+<LINE>in the world, if a' could get her good-will.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>By my troth, niece, thou wilt never get thee a</LINE>
+<LINE>husband, if thou be so shrewd of thy tongue.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>In faith, she's too curst.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Too curst is more than curst: I shall lessen God's</LINE>
+<LINE>sending that way; for it is said, 'God sends a curst</LINE>
+<LINE>cow short horns;' but to a cow too curst he sends none.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>So, by being too curst, God will send you no horns.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Just, if he send me no husband; for the which</LINE>
+<LINE>blessing I am at him upon my knees every morning and</LINE>
+<LINE>evening. Lord, I could not endure a husband with a</LINE>
+<LINE>beard on his face: I had rather lie in the woollen.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>You may light on a husband that hath no beard.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>What should I do with him? dress him in my apparel</LINE>
+<LINE>and make him my waiting-gentlewoman? He that hath a</LINE>
+<LINE>beard is more than a youth, and he that hath no</LINE>
+<LINE>beard is less than a man: and he that is more than</LINE>
+<LINE>a youth is not for me, and he that is less than a</LINE>
+<LINE>man, I am not for him: therefore, I will even take</LINE>
+<LINE>sixpence in earnest of the bear-ward, and lead his</LINE>
+<LINE>apes into hell.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Well, then, go you into hell?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>No, but to the gate; and there will the devil meet</LINE>
+<LINE>me, like an old cuckold, with horns on his head, and</LINE>
+<LINE>say 'Get you to heaven, Beatrice, get you to</LINE>
+<LINE>heaven; here's no place for you maids:' so deliver</LINE>
+<LINE>I up my apes, and away to Saint Peter for the</LINE>
+<LINE>heavens; he shows me where the bachelors sit, and</LINE>
+<LINE>there live we as merry as the day is long.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE><STAGEDIR>To HERO</STAGEDIR> Well, niece, I trust you will be ruled</LINE>
+<LINE>by your father.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Yes, faith; it is my cousin's duty to make curtsy</LINE>
+<LINE>and say 'Father, as it please you.' But yet for all</LINE>
+<LINE>that, cousin, let him be a handsome fellow, or else</LINE>
+<LINE>make another curtsy and say 'Father, as it please</LINE>
+<LINE>me.'</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Well, niece, I hope to see you one day fitted with a husband.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Not till God make men of some other metal than</LINE>
+<LINE>earth. Would it not grieve a woman to be</LINE>
+<LINE>overmastered with a pierce of valiant dust? to make</LINE>
+<LINE>an account of her life to a clod of wayward marl?</LINE>
+<LINE>No, uncle, I'll none: Adam's sons are my brethren;</LINE>
+<LINE>and, truly, I hold it a sin to match in my kindred.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Daughter, remember what I told you: if the prince</LINE>
+<LINE>do solicit you in that kind, you know your answer.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>The fault will be in the music, cousin, if you be</LINE>
+<LINE>not wooed in good time: if the prince be too</LINE>
+<LINE>important, tell him there is measure in every thing</LINE>
+<LINE>and so dance out the answer. For, hear me, Hero:</LINE>
+<LINE>wooing, wedding, and repenting, is as a Scotch jig,</LINE>
+<LINE>a measure, and a cinque pace: the first suit is hot</LINE>
+<LINE>and hasty, like a Scotch jig, and full as</LINE>
+<LINE>fantastical; the wedding, mannerly-modest, as a</LINE>
+<LINE>measure, full of state and ancientry; and then comes</LINE>
+<LINE>repentance and, with his bad legs, falls into the</LINE>
+<LINE>cinque pace faster and faster, till he sink into his grave.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Cousin, you apprehend passing shrewdly.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I have a good eye, uncle; I can see a church by daylight.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>The revellers are entering, brother: make good room.</LINE>
+</SPEECH>
+
+<STAGEDIR>All put on their masks</STAGEDIR>
+<STAGEDIR>Enter DON PEDRO, CLAUDIO, BENEDICK, BALTHASAR,
+DON JOHN, BORACHIO, MARGARET, URSULA and others, masked</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Lady, will you walk about with your friend?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>So you walk softly and look sweetly and say nothing,</LINE>
+<LINE>I am yours for the walk; and especially when I walk away.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>With me in your company?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>I may say so, when I please.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>And when please you to say so?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>When I like your favour; for God defend the lute</LINE>
+<LINE>should be like the case!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>My visor is Philemon's roof; within the house is Jove.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Why, then, your visor should be thatched.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Speak low, if you speak love.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Drawing her aside</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>Well, I would you did like me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>So would not I, for your own sake; for I have many</LINE>
+<LINE>ill-qualities.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>Which is one?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>I say my prayers aloud.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>I love you the better: the hearers may cry, Amen.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>God match me with a good dancer!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>Amen.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>And God keep him out of my sight when the dance is</LINE>
+<LINE>done! Answer, clerk.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>No more words: the clerk is answered.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>I know you well enough; you are Signior Antonio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>At a word, I am not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>I know you by the waggling of your head.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>To tell you true, I counterfeit him.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>You could never do him so ill-well, unless you were</LINE>
+<LINE>the very man. Here's his dry hand up and down: you</LINE>
+<LINE>are he, you are he.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>At a word, I am not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>Come, come, do you think I do not know you by your</LINE>
+<LINE>excellent wit? can virtue hide itself? Go to,</LINE>
+<LINE>mum, you are he: graces will appear, and there's an</LINE>
+<LINE>end.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Will you not tell me who told you so?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>No, you shall pardon me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Nor will you not tell me who you are?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Not now.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>That I was disdainful, and that I had my good wit</LINE>
+<LINE>out of the 'Hundred Merry Tales:'--well this was</LINE>
+<LINE>Signior Benedick that said so.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>What's he?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I am sure you know him well enough.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Not I, believe me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Did he never make you laugh?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I pray you, what is he?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Why, he is the prince's jester: a very dull fool;</LINE>
+<LINE>only his gift is in devising impossible slanders:</LINE>
+<LINE>none but libertines delight in him; and the</LINE>
+<LINE>commendation is not in his wit, but in his villany;</LINE>
+<LINE>for he both pleases men and angers them, and then</LINE>
+<LINE>they laugh at him and beat him. I am sure he is in</LINE>
+<LINE>the fleet: I would he had boarded me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>When I know the gentleman, I'll tell him what you say.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Do, do: he'll but break a comparison or two on me;</LINE>
+<LINE>which, peradventure not marked or not laughed at,</LINE>
+<LINE>strikes him into melancholy; and then there's a</LINE>
+<LINE>partridge wing saved, for the fool will eat no</LINE>
+<LINE>supper that night.</LINE>
+<STAGEDIR>Music</STAGEDIR>
+<LINE>We must follow the leaders.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>In every good thing.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Nay, if they lead to any ill, I will leave them at</LINE>
+<LINE>the next turning.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Dance. Then exeunt all except DON JOHN, BORACHIO,
+and CLAUDIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Sure my brother is amorous on Hero and hath</LINE>
+<LINE>withdrawn her father to break with him about it.</LINE>
+<LINE>The ladies follow her and but one visor remains.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>And that is Claudio: I know him by his bearing.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Are not you Signior Benedick?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>You know me well; I am he.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Signior, you are very near my brother in his love:</LINE>
+<LINE>he is enamoured on Hero; I pray you, dissuade him</LINE>
+<LINE>from her: she is no equal for his birth: you may</LINE>
+<LINE>do the part of an honest man in it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>How know you he loves her?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I heard him swear his affection.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>So did I too; and he swore he would marry her to-night.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Come, let us to the banquet.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt DON JOHN and BORACHIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Thus answer I in the name of Benedick,</LINE>
+<LINE>But hear these ill news with the ears of Claudio.</LINE>
+<LINE>'Tis certain so; the prince wooes for himself.</LINE>
+<LINE>Friendship is constant in all other things</LINE>
+<LINE>Save in the office and affairs of love:</LINE>
+<LINE>Therefore, all hearts in love use their own tongues;</LINE>
+<LINE>Let every eye negotiate for itself</LINE>
+<LINE>And trust no agent; for beauty is a witch</LINE>
+<LINE>Against whose charms faith melteth into blood.</LINE>
+<LINE>This is an accident of hourly proof,</LINE>
+<LINE>Which I mistrusted not. Farewell, therefore, Hero!</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Re-enter BENEDICK</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Count Claudio?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Yea, the same.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Come, will you go with me?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Whither?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Even to the next willow, about your own business,</LINE>
+<LINE>county. What fashion will you wear the garland of?</LINE>
+<LINE>about your neck, like an usurer's chain? or under</LINE>
+<LINE>your arm, like a lieutenant's scarf? You must wear</LINE>
+<LINE>it one way, for the prince hath got your Hero.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I wish him joy of her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Why, that's spoken like an honest drovier: so they</LINE>
+<LINE>sell bullocks. But did you think the prince would</LINE>
+<LINE>have served you thus?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I pray you, leave me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Ho! now you strike like the blind man: 'twas the</LINE>
+<LINE>boy that stole your meat, and you'll beat the post.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If it will not be, I'll leave you.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Alas, poor hurt fowl! now will he creep into sedges.</LINE>
+<LINE>But that my Lady Beatrice should know me, and not</LINE>
+<LINE>know me! The prince's fool! Ha? It may be I go</LINE>
+<LINE>under that title because I am merry. Yea, but so I</LINE>
+<LINE>am apt to do myself wrong; I am not so reputed: it</LINE>
+<LINE>is the base, though bitter, disposition of Beatrice</LINE>
+<LINE>that puts the world into her person and so gives me</LINE>
+<LINE>out. Well, I'll be revenged as I may.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Re-enter DON PEDRO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Now, signior, where's the count? did you see him?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Troth, my lord, I have played the part of Lady Fame.</LINE>
+<LINE>I found him here as melancholy as a lodge in a</LINE>
+<LINE>warren: I told him, and I think I told him true,</LINE>
+<LINE>that your grace had got the good will of this young</LINE>
+<LINE>lady; and I offered him my company to a willow-tree,</LINE>
+<LINE>either to make him a garland, as being forsaken, or</LINE>
+<LINE>to bind him up a rod, as being worthy to be whipped.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>To be whipped! What's his fault?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>The flat transgression of a schoolboy, who, being</LINE>
+<LINE>overjoyed with finding a birds' nest, shows it his</LINE>
+<LINE>companion, and he steals it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Wilt thou make a trust a transgression? The</LINE>
+<LINE>transgression is in the stealer.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Yet it had not been amiss the rod had been made,</LINE>
+<LINE>and the garland too; for the garland he might have</LINE>
+<LINE>worn himself, and the rod he might have bestowed on</LINE>
+<LINE>you, who, as I take it, have stolen his birds' nest.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I will but teach them to sing, and restore them to</LINE>
+<LINE>the owner.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>If their singing answer your saying, by my faith,</LINE>
+<LINE>you say honestly.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>The Lady Beatrice hath a quarrel to you: the</LINE>
+<LINE>gentleman that danced with her told her she is much</LINE>
+<LINE>wronged by you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>O, she misused me past the endurance of a block!</LINE>
+<LINE>an oak but with one green leaf on it would have</LINE>
+<LINE>answered her; my very visor began to assume life and</LINE>
+<LINE>scold with her. She told me, not thinking I had been</LINE>
+<LINE>myself, that I was the prince's jester, that I was</LINE>
+<LINE>duller than a great thaw; huddling jest upon jest</LINE>
+<LINE>with such impossible conveyance upon me that I stood</LINE>
+<LINE>like a man at a mark, with a whole army shooting at</LINE>
+<LINE>me. She speaks poniards, and every word stabs:</LINE>
+<LINE>if her breath were as terrible as her terminations,</LINE>
+<LINE>there were no living near her; she would infect to</LINE>
+<LINE>the north star. I would not marry her, though she</LINE>
+<LINE>were endowed with all that Adam bad left him before</LINE>
+<LINE>he transgressed: she would have made Hercules have</LINE>
+<LINE>turned spit, yea, and have cleft his club to make</LINE>
+<LINE>the fire too. Come, talk not of her: you shall find</LINE>
+<LINE>her the infernal Ate in good apparel. I would to God</LINE>
+<LINE>some scholar would conjure her; for certainly, while</LINE>
+<LINE>she is here, a man may live as quiet in hell as in a</LINE>
+<LINE>sanctuary; and people sin upon purpose, because they</LINE>
+<LINE>would go thither; so, indeed, all disquiet, horror</LINE>
+<LINE>and perturbation follows her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Look, here she comes.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter CLAUDIO, BEATRICE, HERO, and LEONATO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Will your grace command me any service to the</LINE>
+<LINE>world's end? I will go on the slightest errand now</LINE>
+<LINE>to the Antipodes that you can devise to send me on;</LINE>
+<LINE>I will fetch you a tooth-picker now from the</LINE>
+<LINE>furthest inch of Asia, bring you the length of</LINE>
+<LINE>Prester John's foot, fetch you a hair off the great</LINE>
+<LINE>Cham's beard, do you any embassage to the Pigmies,</LINE>
+<LINE>rather than hold three words' conference with this</LINE>
+<LINE>harpy. You have no employment for me?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>None, but to desire your good company.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>O God, sir, here's a dish I love not: I cannot</LINE>
+<LINE>endure my Lady Tongue.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Come, lady, come; you have lost the heart of</LINE>
+<LINE>Signior Benedick.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Indeed, my lord, he lent it me awhile; and I gave</LINE>
+<LINE>him use for it, a double heart for his single one:</LINE>
+<LINE>marry, once before he won it of me with false dice,</LINE>
+<LINE>therefore your grace may well say I have lost it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>You have put him down, lady, you have put him down.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>So I would not he should do me, my lord, lest I</LINE>
+<LINE>should prove the mother of fools. I have brought</LINE>
+<LINE>Count Claudio, whom you sent me to seek.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Why, how now, count! wherefore are you sad?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Not sad, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>How then? sick?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Neither, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>The count is neither sad, nor sick, nor merry, nor</LINE>
+<LINE>well; but civil count, civil as an orange, and</LINE>
+<LINE>something of that jealous complexion.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I' faith, lady, I think your blazon to be true;</LINE>
+<LINE>though, I'll be sworn, if he be so, his conceit is</LINE>
+<LINE>false. Here, Claudio, I have wooed in thy name, and</LINE>
+<LINE>fair Hero is won: I have broke with her father,</LINE>
+<LINE>and his good will obtained: name the day of</LINE>
+<LINE>marriage, and God give thee joy!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Count, take of me my daughter, and with her my</LINE>
+<LINE>fortunes: his grace hath made the match, and an</LINE>
+<LINE>grace say Amen to it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Speak, count, 'tis your cue.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Silence is the perfectest herald of joy: I were</LINE>
+<LINE>but little happy, if I could say how much. Lady, as</LINE>
+<LINE>you are mine, I am yours: I give away myself for</LINE>
+<LINE>you and dote upon the exchange.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Speak, cousin; or, if you cannot, stop his mouth</LINE>
+<LINE>with a kiss, and let not him speak neither.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>In faith, lady, you have a merry heart.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Yea, my lord; I thank it, poor fool, it keeps on</LINE>
+<LINE>the windy side of care. My cousin tells him in his</LINE>
+<LINE>ear that he is in her heart.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And so she doth, cousin.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Good Lord, for alliance! Thus goes every one to the</LINE>
+<LINE>world but I, and I am sunburnt; I may sit in a</LINE>
+<LINE>corner and cry heigh-ho for a husband!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Lady Beatrice, I will get you one.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I would rather have one of your father's getting.</LINE>
+<LINE>Hath your grace ne'er a brother like you? Your</LINE>
+<LINE>father got excellent husbands, if a maid could come by them.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Will you have me, lady?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>No, my lord, unless I might have another for</LINE>
+<LINE>working-days: your grace is too costly to wear</LINE>
+<LINE>every day. But, I beseech your grace, pardon me: I</LINE>
+<LINE>was born to speak all mirth and no matter.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Your silence most offends me, and to be merry best</LINE>
+<LINE>becomes you; for, out of question, you were born in</LINE>
+<LINE>a merry hour.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>No, sure, my lord, my mother cried; but then there</LINE>
+<LINE>was a star danced, and under that was I born.</LINE>
+<LINE>Cousins, God give you joy!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Niece, will you look to those things I told you of?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I cry you mercy, uncle. By your grace's pardon.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>By my troth, a pleasant-spirited lady.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>There's little of the melancholy element in her, my</LINE>
+<LINE>lord: she is never sad but when she sleeps, and</LINE>
+<LINE>not ever sad then; for I have heard my daughter say,</LINE>
+<LINE>she hath often dreamed of unhappiness and waked</LINE>
+<LINE>herself with laughing.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>She cannot endure to hear tell of a husband.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>O, by no means: she mocks all her wooers out of suit.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>She were an excellent wife for Benedict.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>O Lord, my lord, if they were but a week married,</LINE>
+<LINE>they would talk themselves mad.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>County Claudio, when mean you to go to church?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>To-morrow, my lord: time goes on crutches till love</LINE>
+<LINE>have all his rites.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Not till Monday, my dear son, which is hence a just</LINE>
+<LINE>seven-night; and a time too brief, too, to have all</LINE>
+<LINE>things answer my mind.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Come, you shake the head at so long a breathing:</LINE>
+<LINE>but, I warrant thee, Claudio, the time shall not go</LINE>
+<LINE>dully by us. I will in the interim undertake one of</LINE>
+<LINE>Hercules' labours; which is, to bring Signior</LINE>
+<LINE>Benedick and the Lady Beatrice into a mountain of</LINE>
+<LINE>affection the one with the other. I would fain have</LINE>
+<LINE>it a match, and I doubt not but to fashion it, if</LINE>
+<LINE>you three will but minister such assistance as I</LINE>
+<LINE>shall give you direction.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>My lord, I am for you, though it cost me ten</LINE>
+<LINE>nights' watchings.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And I, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>And you too, gentle Hero?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>I will do any modest office, my lord, to help my</LINE>
+<LINE>cousin to a good husband.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>And Benedick is not the unhopefullest husband that</LINE>
+<LINE>I know. Thus far can I praise him; he is of a noble</LINE>
+<LINE>strain, of approved valour and confirmed honesty. I</LINE>
+<LINE>will teach you how to humour your cousin, that she</LINE>
+<LINE>shall fall in love with Benedick; and I, with your</LINE>
+<LINE>two helps, will so practise on Benedick that, in</LINE>
+<LINE>despite of his quick wit and his queasy stomach, he</LINE>
+<LINE>shall fall in love with Beatrice. If we can do this,</LINE>
+<LINE>Cupid is no longer an archer: his glory shall be</LINE>
+<LINE>ours, for we are the only love-gods. Go in with me,</LINE>
+<LINE>and I will tell you my drift.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE II. The same.</TITLE>
+<STAGEDIR>Enter DON JOHN and BORACHIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>It is so; the Count Claudio shall marry the</LINE>
+<LINE>daughter of Leonato.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Yea, my lord; but I can cross it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Any bar, any cross, any impediment will be</LINE>
+<LINE>medicinable to me: I am sick in displeasure to him,</LINE>
+<LINE>and whatsoever comes athwart his affection ranges</LINE>
+<LINE>evenly with mine. How canst thou cross this marriage?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Not honestly, my lord; but so covertly that no</LINE>
+<LINE>dishonesty shall appear in me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Show me briefly how.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>I think I told your lordship a year since, how much</LINE>
+<LINE>I am in the favour of Margaret, the waiting</LINE>
+<LINE>gentlewoman to Hero.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I remember.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>I can, at any unseasonable instant of the night,</LINE>
+<LINE>appoint her to look out at her lady's chamber window.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>What life is in that, to be the death of this marriage?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>The poison of that lies in you to temper. Go you to</LINE>
+<LINE>the prince your brother; spare not to tell him that</LINE>
+<LINE>he hath wronged his honour in marrying the renowned</LINE>
+<LINE>Claudio--whose estimation do you mightily hold</LINE>
+<LINE>up--to a contaminated stale, such a one as Hero.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>What proof shall I make of that?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Proof enough to misuse the prince, to vex Claudio,</LINE>
+<LINE>to undo Hero and kill Leonato. Look you for any</LINE>
+<LINE>other issue?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Only to despite them, I will endeavour any thing.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Go, then; find me a meet hour to draw Don Pedro and</LINE>
+<LINE>the Count Claudio alone: tell them that you know</LINE>
+<LINE>that Hero loves me; intend a kind of zeal both to the</LINE>
+<LINE>prince and Claudio, as,--in love of your brother's</LINE>
+<LINE>honour, who hath made this match, and his friend's</LINE>
+<LINE>reputation, who is thus like to be cozened with the</LINE>
+<LINE>semblance of a maid,--that you have discovered</LINE>
+<LINE>thus. They will scarcely believe this without trial:</LINE>
+<LINE>offer them instances; which shall bear no less</LINE>
+<LINE>likelihood than to see me at her chamber-window,</LINE>
+<LINE>hear me call Margaret Hero, hear Margaret term me</LINE>
+<LINE>Claudio; and bring them to see this the very night</LINE>
+<LINE>before the intended wedding,--for in the meantime I</LINE>
+<LINE>will so fashion the matter that Hero shall be</LINE>
+<LINE>absent,--and there shall appear such seeming truth</LINE>
+<LINE>of Hero's disloyalty that jealousy shall be called</LINE>
+<LINE>assurance and all the preparation overthrown.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Grow this to what adverse issue it can, I will put</LINE>
+<LINE>it in practise. Be cunning in the working this, and</LINE>
+<LINE>thy fee is a thousand ducats.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Be you constant in the accusation, and my cunning</LINE>
+<LINE>shall not shame me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I will presently go learn their day of marriage.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE III. LEONATO'S orchard.</TITLE>
+<STAGEDIR>Enter BENEDICK</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Boy!</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter Boy</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>Boy</SPEAKER>
+<LINE>Signior?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>In my chamber-window lies a book: bring it hither</LINE>
+<LINE>to me in the orchard.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Boy</SPEAKER>
+<LINE>I am here already, sir.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I know that; but I would have thee hence, and here again.</LINE>
+<STAGEDIR>Exit Boy</STAGEDIR>
+<LINE>I do much wonder that one man, seeing how much</LINE>
+<LINE>another man is a fool when he dedicates his</LINE>
+<LINE>behaviors to love, will, after he hath laughed at</LINE>
+<LINE>such shallow follies in others, become the argument</LINE>
+<LINE>of his own scorn by failing in love: and such a man</LINE>
+<LINE>is Claudio. I have known when there was no music</LINE>
+<LINE>with him but the drum and the fife; and now had he</LINE>
+<LINE>rather hear the tabour and the pipe: I have known</LINE>
+<LINE>when he would have walked ten mile a-foot to see a</LINE>
+<LINE>good armour; and now will he lie ten nights awake,</LINE>
+<LINE>carving the fashion of a new doublet. He was wont to</LINE>
+<LINE>speak plain and to the purpose, like an honest man</LINE>
+<LINE>and a soldier; and now is he turned orthography; his</LINE>
+<LINE>words are a very fantastical banquet, just so many</LINE>
+<LINE>strange dishes. May I be so converted and see with</LINE>
+<LINE>these eyes? I cannot tell; I think not: I will not</LINE>
+<LINE>be sworn, but love may transform me to an oyster; but</LINE>
+<LINE>I'll take my oath on it, till he have made an oyster</LINE>
+<LINE>of me, he shall never make me such a fool. One woman</LINE>
+<LINE>is fair, yet I am well; another is wise, yet I am</LINE>
+<LINE>well; another virtuous, yet I am well; but till all</LINE>
+<LINE>graces be in one woman, one woman shall not come in</LINE>
+<LINE>my grace. Rich she shall be, that's certain; wise,</LINE>
+<LINE>or I'll none; virtuous, or I'll never cheapen her;</LINE>
+<LINE>fair, or I'll never look on her; mild, or come not</LINE>
+<LINE>near me; noble, or not I for an angel; of good</LINE>
+<LINE>discourse, an excellent musician, and her hair shall</LINE>
+<LINE>be of what colour it please God. Ha! the prince and</LINE>
+<LINE>Monsieur Love! I will hide me in the arbour.</LINE>
+</SPEECH>
+
+<STAGEDIR>Withdraws</STAGEDIR>
+<STAGEDIR>Enter DON PEDRO, CLAUDIO, and LEONATO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Come, shall we hear this music?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Yea, my good lord. How still the evening is,</LINE>
+<LINE>As hush'd on purpose to grace harmony!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>See you where Benedick hath hid himself?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>O, very well, my lord: the music ended,</LINE>
+<LINE>We'll fit the kid-fox with a pennyworth.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter BALTHASAR with Music</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Come, Balthasar, we'll hear that song again.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>O, good my lord, tax not so bad a voice</LINE>
+<LINE>To slander music any more than once.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>It is the witness still of excellency</LINE>
+<LINE>To put a strange face on his own perfection.</LINE>
+<LINE>I pray thee, sing, and let me woo no more.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>Because you talk of wooing, I will sing;</LINE>
+<LINE>Since many a wooer doth commence his suit</LINE>
+<LINE>To her he thinks not worthy, yet he wooes,</LINE>
+<LINE>Yet will he swear he loves.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Now, pray thee, come;</LINE>
+<LINE>Or, if thou wilt hold longer argument,</LINE>
+<LINE>Do it in notes.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>Note this before my notes;</LINE>
+<LINE>There's not a note of mine that's worth the noting.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Why, these are very crotchets that he speaks;</LINE>
+<LINE>Note, notes, forsooth, and nothing.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Air</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Now, divine air! now is his soul ravished! Is it</LINE>
+<LINE>not strange that sheeps' guts should hale souls out</LINE>
+<LINE>of men's bodies? Well, a horn for my money, when</LINE>
+<LINE>all's done.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>The Song</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>Sigh no more, ladies, sigh no more,</LINE>
+<LINE>Men were deceivers ever,</LINE>
+<LINE>One foot in sea and one on shore,</LINE>
+<LINE>To one thing constant never:</LINE>
+<LINE>Then sigh not so, but let them go,</LINE>
+<LINE>And be you blithe and bonny,</LINE>
+<LINE>Converting all your sounds of woe</LINE>
+<LINE>Into Hey nonny, nonny.</LINE>
+<LINE>Sing no more ditties, sing no moe,</LINE>
+<LINE>Of dumps so dull and heavy;</LINE>
+<LINE>The fraud of men was ever so,</LINE>
+<LINE>Since summer first was leafy:</LINE>
+<LINE>Then sigh not so, &amp;c.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>By my troth, a good song.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>And an ill singer, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Ha, no, no, faith; thou singest well enough for a shift.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>An he had been a dog that should have howled thus,</LINE>
+<LINE>they would have hanged him: and I pray God his bad</LINE>
+<LINE>voice bode no mischief. I had as lief have heard the</LINE>
+<LINE>night-raven, come what plague could have come after</LINE>
+<LINE>it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Yea, marry, dost thou hear, Balthasar? I pray thee,</LINE>
+<LINE>get us some excellent music; for to-morrow night we</LINE>
+<LINE>would have it at the Lady Hero's chamber-window.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BALTHASAR</SPEAKER>
+<LINE>The best I can, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Do so: farewell.</LINE>
+<STAGEDIR>Exit BALTHASAR</STAGEDIR>
+<LINE>Come hither, Leonato. What was it you told me of</LINE>
+<LINE>to-day, that your niece Beatrice was in love with</LINE>
+<LINE>Signior Benedick?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>O, ay: stalk on. stalk on; the fowl sits. I did</LINE>
+<LINE>never think that lady would have loved any man.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>No, nor I neither; but most wonderful that she</LINE>
+<LINE>should so dote on Signior Benedick, whom she hath in</LINE>
+<LINE>all outward behaviors seemed ever to abhor.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Is't possible? Sits the wind in that corner?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>By my troth, my lord, I cannot tell what to think</LINE>
+<LINE>of it but that she loves him with an enraged</LINE>
+<LINE>affection: it is past the infinite of thought.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>May be she doth but counterfeit.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Faith, like enough.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>O God, counterfeit! There was never counterfeit of</LINE>
+<LINE>passion came so near the life of passion as she</LINE>
+<LINE>discovers it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Why, what effects of passion shows she?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Bait the hook well; this fish will bite.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>What effects, my lord? She will sit you, you heard</LINE>
+<LINE>my daughter tell you how.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>She did, indeed.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>How, how, pray you? You amaze me: I would have I</LINE>
+<LINE>thought her spirit had been invincible against all</LINE>
+<LINE>assaults of affection.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I would have sworn it had, my lord; especially</LINE>
+<LINE>against Benedick.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I should think this a gull, but that the</LINE>
+<LINE>white-bearded fellow speaks it: knavery cannot,</LINE>
+<LINE>sure, hide himself in such reverence.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>He hath ta'en the infection: hold it up.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Hath she made her affection known to Benedick?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>No; and swears she never will: that's her torment.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>'Tis true, indeed; so your daughter says: 'Shall</LINE>
+<LINE>I,' says she, 'that have so oft encountered him</LINE>
+<LINE>with scorn, write to him that I love him?'</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>This says she now when she is beginning to write to</LINE>
+<LINE>him; for she'll be up twenty times a night, and</LINE>
+<LINE>there will she sit in her smock till she have writ a</LINE>
+<LINE>sheet of paper: my daughter tells us all.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Now you talk of a sheet of paper, I remember a</LINE>
+<LINE>pretty jest your daughter told us of.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>O, when she had writ it and was reading it over, she</LINE>
+<LINE>found Benedick and Beatrice between the sheet?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>That.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>O, she tore the letter into a thousand halfpence;</LINE>
+<LINE>railed at herself, that she should be so immodest</LINE>
+<LINE>to write to one that she knew would flout her; 'I</LINE>
+<LINE>measure him,' says she, 'by my own spirit; for I</LINE>
+<LINE>should flout him, if he writ to me; yea, though I</LINE>
+<LINE>love him, I should.'</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Then down upon her knees she falls, weeps, sobs,</LINE>
+<LINE>beats her heart, tears her hair, prays, curses; 'O</LINE>
+<LINE>sweet Benedick! God give me patience!'</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>She doth indeed; my daughter says so: and the</LINE>
+<LINE>ecstasy hath so much overborne her that my daughter</LINE>
+<LINE>is sometime afeared she will do a desperate outrage</LINE>
+<LINE>to herself: it is very true.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>It were good that Benedick knew of it by some</LINE>
+<LINE>other, if she will not discover it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>To what end? He would make but a sport of it and</LINE>
+<LINE>torment the poor lady worse.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>An he should, it were an alms to hang him. She's an</LINE>
+<LINE>excellent sweet lady; and, out of all suspicion,</LINE>
+<LINE>she is virtuous.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And she is exceeding wise.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>In every thing but in loving Benedick.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>O, my lord, wisdom and blood combating in so tender</LINE>
+<LINE>a body, we have ten proofs to one that blood hath</LINE>
+<LINE>the victory. I am sorry for her, as I have just</LINE>
+<LINE>cause, being her uncle and her guardian.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I would she had bestowed this dotage on me: I would</LINE>
+<LINE>have daffed all other respects and made her half</LINE>
+<LINE>myself. I pray you, tell Benedick of it, and hear</LINE>
+<LINE>what a' will say.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Were it good, think you?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Hero thinks surely she will die; for she says she</LINE>
+<LINE>will die, if he love her not, and she will die, ere</LINE>
+<LINE>she make her love known, and she will die, if he woo</LINE>
+<LINE>her, rather than she will bate one breath of her</LINE>
+<LINE>accustomed crossness.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>She doth well: if she should make tender of her</LINE>
+<LINE>love, 'tis very possible he'll scorn it; for the</LINE>
+<LINE>man, as you know all, hath a contemptible spirit.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>He is a very proper man.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>He hath indeed a good outward happiness.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Before God! and, in my mind, very wise.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>He doth indeed show some sparks that are like wit.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And I take him to be valiant.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>As Hector, I assure you: and in the managing of</LINE>
+<LINE>quarrels you may say he is wise; for either he</LINE>
+<LINE>avoids them with great discretion, or undertakes</LINE>
+<LINE>them with a most Christian-like fear.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>If he do fear God, a' must necessarily keep peace:</LINE>
+<LINE>if he break the peace, he ought to enter into a</LINE>
+<LINE>quarrel with fear and trembling.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>And so will he do; for the man doth fear God,</LINE>
+<LINE>howsoever it seems not in him by some large jests</LINE>
+<LINE>he will make. Well I am sorry for your niece. Shall</LINE>
+<LINE>we go seek Benedick, and tell him of her love?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Never tell him, my lord: let her wear it out with</LINE>
+<LINE>good counsel.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Nay, that's impossible: she may wear her heart out first.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Well, we will hear further of it by your daughter:</LINE>
+<LINE>let it cool the while. I love Benedick well; and I</LINE>
+<LINE>could wish he would modestly examine himself, to see</LINE>
+<LINE>how much he is unworthy so good a lady.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>My lord, will you walk? dinner is ready.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If he do not dote on her upon this, I will never</LINE>
+<LINE>trust my expectation.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Let there be the same net spread for her; and that</LINE>
+<LINE>must your daughter and her gentlewomen carry. The</LINE>
+<LINE>sport will be, when they hold one an opinion of</LINE>
+<LINE>another's dotage, and no such matter: that's the</LINE>
+<LINE>scene that I would see, which will be merely a</LINE>
+<LINE>dumb-show. Let us send her to call him in to dinner.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt DON PEDRO, CLAUDIO, and LEONATO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE><STAGEDIR>Coming forward</STAGEDIR> This can be no trick: the</LINE>
+<LINE>conference was sadly borne. They have the truth of</LINE>
+<LINE>this from Hero. They seem to pity the lady: it</LINE>
+<LINE>seems her affections have their full bent. Love me!</LINE>
+<LINE>why, it must be requited. I hear how I am censured:</LINE>
+<LINE>they say I will bear myself proudly, if I perceive</LINE>
+<LINE>the love come from her; they say too that she will</LINE>
+<LINE>rather die than give any sign of affection. I did</LINE>
+<LINE>never think to marry: I must not seem proud: happy</LINE>
+<LINE>are they that hear their detractions and can put</LINE>
+<LINE>them to mending. They say the lady is fair; 'tis a</LINE>
+<LINE>truth, I can bear them witness; and virtuous; 'tis</LINE>
+<LINE>so, I cannot reprove it; and wise, but for loving</LINE>
+<LINE>me; by my troth, it is no addition to her wit, nor</LINE>
+<LINE>no great argument of her folly, for I will be</LINE>
+<LINE>horribly in love with her. I may chance have some</LINE>
+<LINE>odd quirks and remnants of wit broken on me,</LINE>
+<LINE>because I have railed so long against marriage: but</LINE>
+<LINE>doth not the appetite alter? a man loves the meat</LINE>
+<LINE>in his youth that he cannot endure in his age.</LINE>
+<LINE>Shall quips and sentences and these paper bullets of</LINE>
+<LINE>the brain awe a man from the career of his humour?</LINE>
+<LINE>No, the world must be peopled. When I said I would</LINE>
+<LINE>die a bachelor, I did not think I should live till I</LINE>
+<LINE>were married. Here comes Beatrice. By this day!</LINE>
+<LINE>she's a fair lady: I do spy some marks of love in</LINE>
+<LINE>her.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter BEATRICE</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Against my will I am sent to bid you come in to dinner.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Fair Beatrice, I thank you for your pains.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I took no more pains for those thanks than you take</LINE>
+<LINE>pains to thank me: if it had been painful, I would</LINE>
+<LINE>not have come.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>You take pleasure then in the message?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Yea, just so much as you may take upon a knife's</LINE>
+<LINE>point and choke a daw withal. You have no stomach,</LINE>
+<LINE>signior: fare you well.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Ha! 'Against my will I am sent to bid you come in</LINE>
+<LINE>to dinner;' there's a double meaning in that 'I took</LINE>
+<LINE>no more pains for those thanks than you took pains</LINE>
+<LINE>to thank me.' that's as much as to say, Any pains</LINE>
+<LINE>that I take for you is as easy as thanks. If I do</LINE>
+<LINE>not take pity of her, I am a villain; if I do not</LINE>
+<LINE>love her, I am a Jew. I will go get her picture.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+</SCENE>
+
+</ACT>
+
+<ACT><TITLE>ACT III</TITLE>
+
+<SCENE><TITLE>SCENE I. LEONATO'S garden.</TITLE>
+<STAGEDIR>Enter HERO, MARGARET, and URSULA</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Good Margaret, run thee to the parlor;</LINE>
+<LINE>There shalt thou find my cousin Beatrice</LINE>
+<LINE>Proposing with the prince and Claudio:</LINE>
+<LINE>Whisper her ear and tell her, I and Ursula</LINE>
+<LINE>Walk in the orchard and our whole discourse</LINE>
+<LINE>Is all of her; say that thou overheard'st us;</LINE>
+<LINE>And bid her steal into the pleached bower,</LINE>
+<LINE>Where honeysuckles, ripen'd by the sun,</LINE>
+<LINE>Forbid the sun to enter, like favourites,</LINE>
+<LINE>Made proud by princes, that advance their pride</LINE>
+<LINE>Against that power that bred it: there will she hide her,</LINE>
+<LINE>To listen our purpose. This is thy office;</LINE>
+<LINE>Bear thee well in it and leave us alone.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>I'll make her come, I warrant you, presently.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Now, Ursula, when Beatrice doth come,</LINE>
+<LINE>As we do trace this alley up and down,</LINE>
+<LINE>Our talk must only be of Benedick.</LINE>
+<LINE>When I do name him, let it be thy part</LINE>
+<LINE>To praise him more than ever man did merit:</LINE>
+<LINE>My talk to thee must be how Benedick</LINE>
+<LINE>Is sick in love with Beatrice. Of this matter</LINE>
+<LINE>Is little Cupid's crafty arrow made,</LINE>
+<LINE>That only wounds by hearsay.</LINE>
+<STAGEDIR>Enter BEATRICE, behind</STAGEDIR>
+<LINE>Now begin;</LINE>
+<LINE>For look where Beatrice, like a lapwing, runs</LINE>
+<LINE>Close by the ground, to hear our conference.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>The pleasant'st angling is to see the fish</LINE>
+<LINE>Cut with her golden oars the silver stream,</LINE>
+<LINE>And greedily devour the treacherous bait:</LINE>
+<LINE>So angle we for Beatrice; who even now</LINE>
+<LINE>Is couched in the woodbine coverture.</LINE>
+<LINE>Fear you not my part of the dialogue.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Then go we near her, that her ear lose nothing</LINE>
+<LINE>Of the false sweet bait that we lay for it.</LINE>
+<STAGEDIR>Approaching the bower</STAGEDIR>
+<LINE>No, truly, Ursula, she is too disdainful;</LINE>
+<LINE>I know her spirits are as coy and wild</LINE>
+<LINE>As haggerds of the rock.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>But are you sure</LINE>
+<LINE>That Benedick loves Beatrice so entirely?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>So says the prince and my new-trothed lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>And did they bid you tell her of it, madam?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>They did entreat me to acquaint her of it;</LINE>
+<LINE>But I persuaded them, if they loved Benedick,</LINE>
+<LINE>To wish him wrestle with affection,</LINE>
+<LINE>And never to let Beatrice know of it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>Why did you so? Doth not the gentleman</LINE>
+<LINE>Deserve as full as fortunate a bed</LINE>
+<LINE>As ever Beatrice shall couch upon?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>O god of love! I know he doth deserve</LINE>
+<LINE>As much as may be yielded to a man:</LINE>
+<LINE>But Nature never framed a woman's heart</LINE>
+<LINE>Of prouder stuff than that of Beatrice;</LINE>
+<LINE>Disdain and scorn ride sparkling in her eyes,</LINE>
+<LINE>Misprising what they look on, and her wit</LINE>
+<LINE>Values itself so highly that to her</LINE>
+<LINE>All matter else seems weak: she cannot love,</LINE>
+<LINE>Nor take no shape nor project of affection,</LINE>
+<LINE>She is so self-endeared.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>Sure, I think so;</LINE>
+<LINE>And therefore certainly it were not good</LINE>
+<LINE>She knew his love, lest she make sport at it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Why, you speak truth. I never yet saw man,</LINE>
+<LINE>How wise, how noble, young, how rarely featured,</LINE>
+<LINE>But she would spell him backward: if fair-faced,</LINE>
+<LINE>She would swear the gentleman should be her sister;</LINE>
+<LINE>If black, why, Nature, drawing of an antique,</LINE>
+<LINE>Made a foul blot; if tall, a lance ill-headed;</LINE>
+<LINE>If low, an agate very vilely cut;</LINE>
+<LINE>If speaking, why, a vane blown with all winds;</LINE>
+<LINE>If silent, why, a block moved with none.</LINE>
+<LINE>So turns she every man the wrong side out</LINE>
+<LINE>And never gives to truth and virtue that</LINE>
+<LINE>Which simpleness and merit purchaseth.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>Sure, sure, such carping is not commendable.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>No, not to be so odd and from all fashions</LINE>
+<LINE>As Beatrice is, cannot be commendable:</LINE>
+<LINE>But who dare tell her so? If I should speak,</LINE>
+<LINE>She would mock me into air; O, she would laugh me</LINE>
+<LINE>Out of myself, press me to death with wit.</LINE>
+<LINE>Therefore let Benedick, like cover'd fire,</LINE>
+<LINE>Consume away in sighs, waste inwardly:</LINE>
+<LINE>It were a better death than die with mocks,</LINE>
+<LINE>Which is as bad as die with tickling.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>Yet tell her of it: hear what she will say.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>No; rather I will go to Benedick</LINE>
+<LINE>And counsel him to fight against his passion.</LINE>
+<LINE>And, truly, I'll devise some honest slanders</LINE>
+<LINE>To stain my cousin with: one doth not know</LINE>
+<LINE>How much an ill word may empoison liking.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>O, do not do your cousin such a wrong.</LINE>
+<LINE>She cannot be so much without true judgment--</LINE>
+<LINE>Having so swift and excellent a wit</LINE>
+<LINE>As she is prized to have--as to refuse</LINE>
+<LINE>So rare a gentleman as Signior Benedick.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>He is the only man of Italy.</LINE>
+<LINE>Always excepted my dear Claudio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>I pray you, be not angry with me, madam,</LINE>
+<LINE>Speaking my fancy: Signior Benedick,</LINE>
+<LINE>For shape, for bearing, argument and valour,</LINE>
+<LINE>Goes foremost in report through Italy.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Indeed, he hath an excellent good name.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>His excellence did earn it, ere he had it.</LINE>
+<LINE>When are you married, madam?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Why, every day, to-morrow. Come, go in:</LINE>
+<LINE>I'll show thee some attires, and have thy counsel</LINE>
+<LINE>Which is the best to furnish me to-morrow.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>She's limed, I warrant you: we have caught her, madam.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>If it proves so, then loving goes by haps:</LINE>
+<LINE>Some Cupid kills with arrows, some with traps.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt HERO and URSULA</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE><STAGEDIR>Coming forward</STAGEDIR></LINE>
+<LINE>What fire is in mine ears? Can this be true?</LINE>
+<LINE>Stand I condemn'd for pride and scorn so much?</LINE>
+<LINE>Contempt, farewell! and maiden pride, adieu!</LINE>
+<LINE>No glory lives behind the back of such.</LINE>
+<LINE>And, Benedick, love on; I will requite thee,</LINE>
+<LINE>Taming my wild heart to thy loving hand:</LINE>
+<LINE>If thou dost love, my kindness shall incite thee</LINE>
+<LINE>To bind our loves up in a holy band;</LINE>
+<LINE>For others say thou dost deserve, and I</LINE>
+<LINE>Believe it better than reportingly.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE II. A room in LEONATO'S house</TITLE>
+<STAGEDIR>Enter DON PEDRO, CLAUDIO, BENEDICK, and LEONATO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I do but stay till your marriage be consummate, and</LINE>
+<LINE>then go I toward Arragon.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I'll bring you thither, my lord, if you'll</LINE>
+<LINE>vouchsafe me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Nay, that would be as great a soil in the new gloss</LINE>
+<LINE>of your marriage as to show a child his new coat</LINE>
+<LINE>and forbid him to wear it. I will only be bold</LINE>
+<LINE>with Benedick for his company; for, from the crown</LINE>
+<LINE>of his head to the sole of his foot, he is all</LINE>
+<LINE>mirth: he hath twice or thrice cut Cupid's</LINE>
+<LINE>bow-string and the little hangman dare not shoot at</LINE>
+<LINE>him; he hath a heart as sound as a bell and his</LINE>
+<LINE>tongue is the clapper, for what his heart thinks his</LINE>
+<LINE>tongue speaks.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Gallants, I am not as I have been.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>So say I methinks you are sadder.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I hope he be in love.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Hang him, truant! there's no true drop of blood in</LINE>
+<LINE>him, to be truly touched with love: if he be sad,</LINE>
+<LINE>he wants money.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I have the toothache.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Draw it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Hang it!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>You must hang it first, and draw it afterwards.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>What! sigh for the toothache?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Where is but a humour or a worm.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Well, every one can master a grief but he that has</LINE>
+<LINE>it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Yet say I, he is in love.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>There is no appearance of fancy in him, unless it be</LINE>
+<LINE>a fancy that he hath to strange disguises; as, to be</LINE>
+<LINE>a Dutchman today, a Frenchman to-morrow, or in the</LINE>
+<LINE>shape of two countries at once, as, a German from</LINE>
+<LINE>the waist downward, all slops, and a Spaniard from</LINE>
+<LINE>the hip upward, no doublet. Unless he have a fancy</LINE>
+<LINE>to this foolery, as it appears he hath, he is no</LINE>
+<LINE>fool for fancy, as you would have it appear he is.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If he be not in love with some woman, there is no</LINE>
+<LINE>believing old signs: a' brushes his hat o'</LINE>
+<LINE>mornings; what should that bode?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Hath any man seen him at the barber's?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>No, but the barber's man hath been seen with him,</LINE>
+<LINE>and the old ornament of his cheek hath already</LINE>
+<LINE>stuffed tennis-balls.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Indeed, he looks younger than he did, by the loss of a beard.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Nay, a' rubs himself with civet: can you smell him</LINE>
+<LINE>out by that?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>That's as much as to say, the sweet youth's in love.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>The greatest note of it is his melancholy.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And when was he wont to wash his face?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Yea, or to paint himself? for the which, I hear</LINE>
+<LINE>what they say of him.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Nay, but his jesting spirit; which is now crept into</LINE>
+<LINE>a lute-string and now governed by stops.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Indeed, that tells a heavy tale for him: conclude,</LINE>
+<LINE>conclude he is in love.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Nay, but I know who loves him.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>That would I know too: I warrant, one that knows him not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Yes, and his ill conditions; and, in despite of</LINE>
+<LINE>all, dies for him.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>She shall be buried with her face upwards.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Yet is this no charm for the toothache. Old</LINE>
+<LINE>signior, walk aside with me: I have studied eight</LINE>
+<LINE>or nine wise words to speak to you, which these</LINE>
+<LINE>hobby-horses must not hear.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt BENEDICK and LEONATO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>For my life, to break with him about Beatrice.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>'Tis even so. Hero and Margaret have by this</LINE>
+<LINE>played their parts with Beatrice; and then the two</LINE>
+<LINE>bears will not bite one another when they meet.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter DON JOHN</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>My lord and brother, God save you!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Good den, brother.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>If your leisure served, I would speak with you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>In private?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>If it please you: yet Count Claudio may hear; for</LINE>
+<LINE>what I would speak of concerns him.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>What's the matter?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE><STAGEDIR>To CLAUDIO</STAGEDIR> Means your lordship to be married</LINE>
+<LINE>to-morrow?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>You know he does.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I know not that, when he knows what I know.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If there be any impediment, I pray you discover it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>You may think I love you not: let that appear</LINE>
+<LINE>hereafter, and aim better at me by that I now will</LINE>
+<LINE>manifest. For my brother, I think he holds you</LINE>
+<LINE>well, and in dearness of heart hath holp to effect</LINE>
+<LINE>your ensuing marriage;--surely suit ill spent and</LINE>
+<LINE>labour ill bestowed.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Why, what's the matter?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I came hither to tell you; and, circumstances</LINE>
+<LINE>shortened, for she has been too long a talking of,</LINE>
+<LINE>the lady is disloyal.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Who, Hero?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Even she; Leonato's Hero, your Hero, every man's Hero:</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Disloyal?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>The word is too good to paint out her wickedness; I</LINE>
+<LINE>could say she were worse: think you of a worse</LINE>
+<LINE>title, and I will fit her to it. Wonder not till</LINE>
+<LINE>further warrant: go but with me to-night, you shall</LINE>
+<LINE>see her chamber-window entered, even the night</LINE>
+<LINE>before her wedding-day: if you love her then,</LINE>
+<LINE>to-morrow wed her; but it would better fit your honour</LINE>
+<LINE>to change your mind.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>May this be so?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I will not think it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>If you dare not trust that you see, confess not</LINE>
+<LINE>that you know: if you will follow me, I will show</LINE>
+<LINE>you enough; and when you have seen more and heard</LINE>
+<LINE>more, proceed accordingly.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If I see any thing to-night why I should not marry</LINE>
+<LINE>her to-morrow in the congregation, where I should</LINE>
+<LINE>wed, there will I shame her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>And, as I wooed for thee to obtain her, I will join</LINE>
+<LINE>with thee to disgrace her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>I will disparage her no farther till you are my</LINE>
+<LINE>witnesses: bear it coldly but till midnight, and</LINE>
+<LINE>let the issue show itself.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>O day untowardly turned!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>O mischief strangely thwarting!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>O plague right well prevented! so will you say when</LINE>
+<LINE>you have seen the sequel.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE III. A street.</TITLE>
+<STAGEDIR>Enter DOGBERRY and VERGES with the Watch</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Are you good men and true?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Yea, or else it were pity but they should suffer</LINE>
+<LINE>salvation, body and soul.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Nay, that were a punishment too good for them, if</LINE>
+<LINE>they should have any allegiance in them, being</LINE>
+<LINE>chosen for the prince's watch.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Well, give them their charge, neighbour Dogberry.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>First, who think you the most desertless man to be</LINE>
+<LINE>constable?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>First Watchman</SPEAKER>
+<LINE>Hugh Otecake, sir, or George Seacole; for they can</LINE>
+<LINE>write and read.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Come hither, neighbour Seacole. God hath blessed</LINE>
+<LINE>you with a good name: to be a well-favoured man is</LINE>
+<LINE>the gift of fortune; but to write and read comes by nature.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Second Watchman</SPEAKER>
+<LINE>Both which, master constable,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>You have: I knew it would be your answer. Well,</LINE>
+<LINE>for your favour, sir, why, give God thanks, and make</LINE>
+<LINE>no boast of it; and for your writing and reading,</LINE>
+<LINE>let that appear when there is no need of such</LINE>
+<LINE>vanity. You are thought here to be the most</LINE>
+<LINE>senseless and fit man for the constable of the</LINE>
+<LINE>watch; therefore bear you the lantern. This is your</LINE>
+<LINE>charge: you shall comprehend all vagrom men; you are</LINE>
+<LINE>to bid any man stand, in the prince's name.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Second Watchman</SPEAKER>
+<LINE>How if a' will not stand?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Why, then, take no note of him, but let him go; and</LINE>
+<LINE>presently call the rest of the watch together and</LINE>
+<LINE>thank God you are rid of a knave.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>If he will not stand when he is bidden, he is none</LINE>
+<LINE>of the prince's subjects.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>True, and they are to meddle with none but the</LINE>
+<LINE>prince's subjects. You shall also make no noise in</LINE>
+<LINE>the streets; for, for the watch to babble and to</LINE>
+<LINE>talk is most tolerable and not to be endured.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE>We will rather sleep than talk: we know what</LINE>
+<LINE>belongs to a watch.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Why, you speak like an ancient and most quiet</LINE>
+<LINE>watchman; for I cannot see how sleeping should</LINE>
+<LINE>offend: only, have a care that your bills be not</LINE>
+<LINE>stolen. Well, you are to call at all the</LINE>
+<LINE>ale-houses, and bid those that are drunk get them to bed.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE>How if they will not?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Why, then, let them alone till they are sober: if</LINE>
+<LINE>they make you not then the better answer, you may</LINE>
+<LINE>say they are not the men you took them for.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE>Well, sir.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>If you meet a thief, you may suspect him, by virtue</LINE>
+<LINE>of your office, to be no true man; and, for such</LINE>
+<LINE>kind of men, the less you meddle or make with them,</LINE>
+<LINE>why the more is for your honesty.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE>If we know him to be a thief, shall we not lay</LINE>
+<LINE>hands on him?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Truly, by your office, you may; but I think they</LINE>
+<LINE>that touch pitch will be defiled: the most peaceable</LINE>
+<LINE>way for you, if you do take a thief, is to let him</LINE>
+<LINE>show himself what he is and steal out of your company.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>You have been always called a merciful man, partner.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Truly, I would not hang a dog by my will, much more</LINE>
+<LINE>a man who hath any honesty in him.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>If you hear a child cry in the night, you must call</LINE>
+<LINE>to the nurse and bid her still it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE>How if the nurse be asleep and will not hear us?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Why, then, depart in peace, and let the child wake</LINE>
+<LINE>her with crying; for the ewe that will not hear her</LINE>
+<LINE>lamb when it baes will never answer a calf when he bleats.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>'Tis very true.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>This is the end of the charge:--you, constable, are</LINE>
+<LINE>to present the prince's own person: if you meet the</LINE>
+<LINE>prince in the night, you may stay him.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Nay, by'r our lady, that I think a' cannot.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Five shillings to one on't, with any man that knows</LINE>
+<LINE>the statutes, he may stay him: marry, not without</LINE>
+<LINE>the prince be willing; for, indeed, the watch ought</LINE>
+<LINE>to offend no man; and it is an offence to stay a</LINE>
+<LINE>man against his will.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>By'r lady, I think it be so.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Ha, ha, ha! Well, masters, good night: an there be</LINE>
+<LINE>any matter of weight chances, call up me: keep your</LINE>
+<LINE>fellows' counsels and your own; and good night.</LINE>
+<LINE>Come, neighbour.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE>Well, masters, we hear our charge: let us go sit here</LINE>
+<LINE>upon the church-bench till two, and then all to bed.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>One word more, honest neighbours. I pray you watch</LINE>
+<LINE>about Signior Leonato's door; for the wedding being</LINE>
+<LINE>there to-morrow, there is a great coil to-night.</LINE>
+<LINE>Adieu: be vigitant, I beseech you.</LINE>
+</SPEECH>
+
+<STAGEDIR>Exeunt DOGBERRY and VERGES</STAGEDIR>
+<STAGEDIR>Enter BORACHIO and CONRADE</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>What Conrade!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE><STAGEDIR>Aside</STAGEDIR> Peace! stir not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Conrade, I say!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Here, man; I am at thy elbow.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Mass, and my elbow itched; I thought there would a</LINE>
+<LINE>scab follow.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>I will owe thee an answer for that: and now forward</LINE>
+<LINE>with thy tale.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Stand thee close, then, under this pent-house, for</LINE>
+<LINE>it drizzles rain; and I will, like a true drunkard,</LINE>
+<LINE>utter all to thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE><STAGEDIR>Aside</STAGEDIR> Some treason, masters: yet stand close.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Therefore know I have earned of Don John a thousand ducats.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Is it possible that any villany should be so dear?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Thou shouldst rather ask if it were possible any</LINE>
+<LINE>villany should be so rich; for when rich villains</LINE>
+<LINE>have need of poor ones, poor ones may make what</LINE>
+<LINE>price they will.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>I wonder at it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>That shows thou art unconfirmed. Thou knowest that</LINE>
+<LINE>the fashion of a doublet, or a hat, or a cloak, is</LINE>
+<LINE>nothing to a man.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Yes, it is apparel.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>I mean, the fashion.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Yes, the fashion is the fashion.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Tush! I may as well say the fool's the fool. But</LINE>
+<LINE>seest thou not what a deformed thief this fashion</LINE>
+<LINE>is?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE><STAGEDIR>Aside</STAGEDIR> I know that Deformed; a' has been a vile</LINE>
+<LINE>thief this seven year; a' goes up and down like a</LINE>
+<LINE>gentleman: I remember his name.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Didst thou not hear somebody?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>No; 'twas the vane on the house.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Seest thou not, I say, what a deformed thief this</LINE>
+<LINE>fashion is? how giddily a' turns about all the hot</LINE>
+<LINE>bloods between fourteen and five-and-thirty?</LINE>
+<LINE>sometimes fashioning them like Pharaoh's soldiers</LINE>
+<LINE>in the reeky painting, sometime like god Bel's</LINE>
+<LINE>priests in the old church-window, sometime like the</LINE>
+<LINE>shaven Hercules in the smirched worm-eaten tapestry,</LINE>
+<LINE>where his codpiece seems as massy as his club?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>All this I see; and I see that the fashion wears</LINE>
+<LINE>out more apparel than the man. But art not thou</LINE>
+<LINE>thyself giddy with the fashion too, that thou hast</LINE>
+<LINE>shifted out of thy tale into telling me of the fashion?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Not so, neither: but know that I have to-night</LINE>
+<LINE>wooed Margaret, the Lady Hero's gentlewoman, by the</LINE>
+<LINE>name of Hero: she leans me out at her mistress'</LINE>
+<LINE>chamber-window, bids me a thousand times good</LINE>
+<LINE>night,--I tell this tale vilely:--I should first</LINE>
+<LINE>tell thee how the prince, Claudio and my master,</LINE>
+<LINE>planted and placed and possessed by my master Don</LINE>
+<LINE>John, saw afar off in the orchard this amiable encounter.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>And thought they Margaret was Hero?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Two of them did, the prince and Claudio; but the</LINE>
+<LINE>devil my master knew she was Margaret; and partly</LINE>
+<LINE>by his oaths, which first possessed them, partly by</LINE>
+<LINE>the dark night, which did deceive them, but chiefly</LINE>
+<LINE>by my villany, which did confirm any slander that</LINE>
+<LINE>Don John had made, away went Claudio enraged; swore</LINE>
+<LINE>he would meet her, as he was appointed, next morning</LINE>
+<LINE>at the temple, and there, before the whole</LINE>
+<LINE>congregation, shame her with what he saw o'er night</LINE>
+<LINE>and send her home again without a husband.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>First Watchman</SPEAKER>
+<LINE>We charge you, in the prince's name, stand!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Second Watchman</SPEAKER>
+<LINE>Call up the right master constable. We have here</LINE>
+<LINE>recovered the most dangerous piece of lechery that</LINE>
+<LINE>ever was known in the commonwealth.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>First Watchman</SPEAKER>
+<LINE>And one Deformed is one of them: I know him; a'</LINE>
+<LINE>wears a lock.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Masters, masters,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Second Watchman</SPEAKER>
+<LINE>You'll be made bring Deformed forth, I warrant you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Masters,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>First Watchman</SPEAKER>
+<LINE>Never speak: we charge you let us obey you to go with us.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>We are like to prove a goodly commodity, being taken</LINE>
+<LINE>up of these men's bills.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>A commodity in question, I warrant you. Come, we'll obey you.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE IV. HERO's apartment.</TITLE>
+<STAGEDIR>Enter HERO, MARGARET, and URSULA</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Good Ursula, wake my cousin Beatrice, and desire</LINE>
+<LINE>her to rise.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>I will, lady.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>And bid her come hither.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>Well.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Troth, I think your other rabato were better.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>No, pray thee, good Meg, I'll wear this.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>By my troth, 's not so good; and I warrant your</LINE>
+<LINE>cousin will say so.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>My cousin's a fool, and thou art another: I'll wear</LINE>
+<LINE>none but this.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>I like the new tire within excellently, if the hair</LINE>
+<LINE>were a thought browner; and your gown's a most rare</LINE>
+<LINE>fashion, i' faith. I saw the Duchess of Milan's</LINE>
+<LINE>gown that they praise so.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>O, that exceeds, they say.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>By my troth, 's but a night-gown in respect of</LINE>
+<LINE>yours: cloth o' gold, and cuts, and laced with</LINE>
+<LINE>silver, set with pearls, down sleeves, side sleeves,</LINE>
+<LINE>and skirts, round underborne with a bluish tinsel:</LINE>
+<LINE>but for a fine, quaint, graceful and excellent</LINE>
+<LINE>fashion, yours is worth ten on 't.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>God give me joy to wear it! for my heart is</LINE>
+<LINE>exceeding heavy.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>'Twill be heavier soon by the weight of a man.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Fie upon thee! art not ashamed?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Of what, lady? of speaking honourably? Is not</LINE>
+<LINE>marriage honourable in a beggar? Is not your lord</LINE>
+<LINE>honourable without marriage? I think you would have</LINE>
+<LINE>me say, 'saving your reverence, a husband:' and bad</LINE>
+<LINE>thinking do not wrest true speaking, I'll offend</LINE>
+<LINE>nobody: is there any harm in 'the heavier for a</LINE>
+<LINE>husband'? None, I think, and it be the right husband</LINE>
+<LINE>and the right wife; otherwise 'tis light, and not</LINE>
+<LINE>heavy: ask my Lady Beatrice else; here she comes.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter BEATRICE</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Good morrow, coz.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Good morrow, sweet Hero.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Why how now? do you speak in the sick tune?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I am out of all other tune, methinks.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Clap's into 'Light o' love;' that goes without a</LINE>
+<LINE>burden: do you sing it, and I'll dance it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Ye light o' love, with your heels! then, if your</LINE>
+<LINE>husband have stables enough, you'll see he shall</LINE>
+<LINE>lack no barns.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>O illegitimate construction! I scorn that with my heels.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>'Tis almost five o'clock, cousin; tis time you were</LINE>
+<LINE>ready. By my troth, I am exceeding ill: heigh-ho!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>For a hawk, a horse, or a husband?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>For the letter that begins them all, H.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Well, and you be not turned Turk, there's no more</LINE>
+<LINE>sailing by the star.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>What means the fool, trow?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Nothing I; but God send every one their heart's desire!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>These gloves the count sent me; they are an</LINE>
+<LINE>excellent perfume.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I am stuffed, cousin; I cannot smell.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>A maid, and stuffed! there's goodly catching of cold.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>O, God help me! God help me! how long have you</LINE>
+<LINE>professed apprehension?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Even since you left it. Doth not my wit become me rarely?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>It is not seen enough, you should wear it in your</LINE>
+<LINE>cap. By my troth, I am sick.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Get you some of this distilled Carduus Benedictus,</LINE>
+<LINE>and lay it to your heart: it is the only thing for a qualm.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>There thou prickest her with a thistle.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Benedictus! why Benedictus? you have some moral in</LINE>
+<LINE>this Benedictus.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Moral! no, by my troth, I have no moral meaning; I</LINE>
+<LINE>meant, plain holy-thistle. You may think perchance</LINE>
+<LINE>that I think you are in love: nay, by'r lady, I am</LINE>
+<LINE>not such a fool to think what I list, nor I list</LINE>
+<LINE>not to think what I can, nor indeed I cannot think,</LINE>
+<LINE>if I would think my heart out of thinking, that you</LINE>
+<LINE>are in love or that you will be in love or that you</LINE>
+<LINE>can be in love. Yet Benedick was such another, and</LINE>
+<LINE>now is he become a man: he swore he would never</LINE>
+<LINE>marry, and yet now, in despite of his heart, he eats</LINE>
+<LINE>his meat without grudging: and how you may be</LINE>
+<LINE>converted I know not, but methinks you look with</LINE>
+<LINE>your eyes as other women do.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>What pace is this that thy tongue keeps?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Not a false gallop.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Re-enter URSULA</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>Madam, withdraw: the prince, the count, Signior</LINE>
+<LINE>Benedick, Don John, and all the gallants of the</LINE>
+<LINE>town, are come to fetch you to church.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Help to dress me, good coz, good Meg, good Ursula.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE V. Another room in LEONATO'S house.</TITLE>
+<STAGEDIR>Enter LEONATO, with DOGBERRY and VERGES</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>What would you with me, honest neighbour?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Marry, sir, I would have some confidence with you</LINE>
+<LINE>that decerns you nearly.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Brief, I pray you; for you see it is a busy time with me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Marry, this it is, sir.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Yes, in truth it is, sir.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>What is it, my good friends?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Goodman Verges, sir, speaks a little off the</LINE>
+<LINE>matter: an old man, sir, and his wits are not so</LINE>
+<LINE>blunt as, God help, I would desire they were; but,</LINE>
+<LINE>in faith, honest as the skin between his brows.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Yes, I thank God I am as honest as any man living</LINE>
+<LINE>that is an old man and no honester than I.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Comparisons are odorous: palabras, neighbour Verges.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Neighbours, you are tedious.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>It pleases your worship to say so, but we are the</LINE>
+<LINE>poor duke's officers; but truly, for mine own part,</LINE>
+<LINE>if I were as tedious as a king, I could find it in</LINE>
+<LINE>my heart to bestow it all of your worship.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>All thy tediousness on me, ah?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Yea, an 'twere a thousand pound more than 'tis; for</LINE>
+<LINE>I hear as good exclamation on your worship as of any</LINE>
+<LINE>man in the city; and though I be but a poor man, I</LINE>
+<LINE>am glad to hear it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>And so am I.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I would fain know what you have to say.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Marry, sir, our watch to-night, excepting your</LINE>
+<LINE>worship's presence, ha' ta'en a couple of as arrant</LINE>
+<LINE>knaves as any in Messina.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>A good old man, sir; he will be talking: as they</LINE>
+<LINE>say, when the age is in, the wit is out: God help</LINE>
+<LINE>us! it is a world to see. Well said, i' faith,</LINE>
+<LINE>neighbour Verges: well, God's a good man; an two men</LINE>
+<LINE>ride of a horse, one must ride behind. An honest</LINE>
+<LINE>soul, i' faith, sir; by my troth he is, as ever</LINE>
+<LINE>broke bread; but God is to be worshipped; all men</LINE>
+<LINE>are not alike; alas, good neighbour!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Indeed, neighbour, he comes too short of you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Gifts that God gives.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I must leave you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>One word, sir: our watch, sir, have indeed</LINE>
+<LINE>comprehended two aspicious persons, and we would</LINE>
+<LINE>have them this morning examined before your worship.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Take their examination yourself and bring it me: I</LINE>
+<LINE>am now in great haste, as it may appear unto you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>It shall be suffigance.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Drink some wine ere you go: fare you well.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter a Messenger</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>My lord, they stay for you to give your daughter to</LINE>
+<LINE>her husband.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I'll wait upon them: I am ready.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt LEONATO and Messenger</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Go, good partner, go, get you to Francis Seacole;</LINE>
+<LINE>bid him bring his pen and inkhorn to the gaol: we</LINE>
+<LINE>are now to examination these men.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>And we must do it wisely.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>We will spare for no wit, I warrant you; here's</LINE>
+<LINE>that shall drive some of them to a non-come: only</LINE>
+<LINE>get the learned writer to set down our</LINE>
+<LINE>excommunication and meet me at the gaol.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+</ACT>
+
+<ACT><TITLE>ACT IV</TITLE>
+
+<SCENE><TITLE>SCENE I. A church.</TITLE>
+<STAGEDIR>Enter DON PEDRO, DON JOHN, LEONATO, FRIAR FRANCIS,
+CLAUDIO, BENEDICK, HERO, BEATRICE, and Attendants</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Come, Friar Francis, be brief; only to the plain</LINE>
+<LINE>form of marriage, and you shall recount their</LINE>
+<LINE>particular duties afterwards.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>You come hither, my lord, to marry this lady.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>No.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>To be married to her: friar, you come to marry her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Lady, you come hither to be married to this count.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>I do.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>If either of you know any inward impediment why you</LINE>
+<LINE>should not be conjoined, charge you, on your souls,</LINE>
+<LINE>to utter it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Know you any, Hero?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>None, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Know you any, count?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I dare make his answer, none.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>O, what men dare do! what men may do! what men daily</LINE>
+<LINE>do, not knowing what they do!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>How now! interjections? Why, then, some be of</LINE>
+<LINE>laughing, as, ah, ha, he!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Stand thee by, friar. Father, by your leave:</LINE>
+<LINE>Will you with free and unconstrained soul</LINE>
+<LINE>Give me this maid, your daughter?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>As freely, son, as God did give her me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And what have I to give you back, whose worth</LINE>
+<LINE>May counterpoise this rich and precious gift?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Nothing, unless you render her again.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Sweet prince, you learn me noble thankfulness.</LINE>
+<LINE>There, Leonato, take her back again:</LINE>
+<LINE>Give not this rotten orange to your friend;</LINE>
+<LINE>She's but the sign and semblance of her honour.</LINE>
+<LINE>Behold how like a maid she blushes here!</LINE>
+<LINE>O, what authority and show of truth</LINE>
+<LINE>Can cunning sin cover itself withal!</LINE>
+<LINE>Comes not that blood as modest evidence</LINE>
+<LINE>To witness simple virtue? Would you not swear,</LINE>
+<LINE>All you that see her, that she were a maid,</LINE>
+<LINE>By these exterior shows? But she is none:</LINE>
+<LINE>She knows the heat of a luxurious bed;</LINE>
+<LINE>Her blush is guiltiness, not modesty.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>What do you mean, my lord?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Not to be married,</LINE>
+<LINE>Not to knit my soul to an approved wanton.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Dear my lord, if you, in your own proof,</LINE>
+<LINE>Have vanquish'd the resistance of her youth,</LINE>
+<LINE>And made defeat of her virginity,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I know what you would say: if I have known her,</LINE>
+<LINE>You will say she did embrace me as a husband,</LINE>
+<LINE>And so extenuate the 'forehand sin:</LINE>
+<LINE>No, Leonato,</LINE>
+<LINE>I never tempted her with word too large;</LINE>
+<LINE>But, as a brother to his sister, show'd</LINE>
+<LINE>Bashful sincerity and comely love.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>And seem'd I ever otherwise to you?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Out on thee! Seeming! I will write against it:</LINE>
+<LINE>You seem to me as Dian in her orb,</LINE>
+<LINE>As chaste as is the bud ere it be blown;</LINE>
+<LINE>But you are more intemperate in your blood</LINE>
+<LINE>Than Venus, or those pamper'd animals</LINE>
+<LINE>That rage in savage sensuality.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Is my lord well, that he doth speak so wide?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Sweet prince, why speak not you?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>What should I speak?</LINE>
+<LINE>I stand dishonour'd, that have gone about</LINE>
+<LINE>To link my dear friend to a common stale.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Are these things spoken, or do I but dream?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Sir, they are spoken, and these things are true.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>This looks not like a nuptial.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>True! O God!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Leonato, stand I here?</LINE>
+<LINE>Is this the prince? is this the prince's brother?</LINE>
+<LINE>Is this face Hero's? are our eyes our own?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>All this is so: but what of this, my lord?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Let me but move one question to your daughter;</LINE>
+<LINE>And, by that fatherly and kindly power</LINE>
+<LINE>That you have in her, bid her answer truly.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I charge thee do so, as thou art my child.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>O, God defend me! how am I beset!</LINE>
+<LINE>What kind of catechising call you this?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>To make you answer truly to your name.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Is it not Hero? Who can blot that name</LINE>
+<LINE>With any just reproach?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Marry, that can Hero;</LINE>
+<LINE>Hero itself can blot out Hero's virtue.</LINE>
+<LINE>What man was he talk'd with you yesternight</LINE>
+<LINE>Out at your window betwixt twelve and one?</LINE>
+<LINE>Now, if you are a maid, answer to this.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>I talk'd with no man at that hour, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Why, then are you no maiden. Leonato,</LINE>
+<LINE>I am sorry you must hear: upon mine honour,</LINE>
+<LINE>Myself, my brother and this grieved count</LINE>
+<LINE>Did see her, hear her, at that hour last night</LINE>
+<LINE>Talk with a ruffian at her chamber-window</LINE>
+<LINE>Who hath indeed, most like a liberal villain,</LINE>
+<LINE>Confess'd the vile encounters they have had</LINE>
+<LINE>A thousand times in secret.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Fie, fie! they are not to be named, my lord,</LINE>
+<LINE>Not to be spoke of;</LINE>
+<LINE>There is not chastity enough in language</LINE>
+<LINE>Without offence to utter them. Thus, pretty lady,</LINE>
+<LINE>I am sorry for thy much misgovernment.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>O Hero, what a Hero hadst thou been,</LINE>
+<LINE>If half thy outward graces had been placed</LINE>
+<LINE>About thy thoughts and counsels of thy heart!</LINE>
+<LINE>But fare thee well, most foul, most fair! farewell,</LINE>
+<LINE>Thou pure impiety and impious purity!</LINE>
+<LINE>For thee I'll lock up all the gates of love,</LINE>
+<LINE>And on my eyelids shall conjecture hang,</LINE>
+<LINE>To turn all beauty into thoughts of harm,</LINE>
+<LINE>And never shall it more be gracious.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Hath no man's dagger here a point for me?</LINE>
+</SPEECH>
+
+
+<STAGEDIR>HERO swoons</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Why, how now, cousin! wherefore sink you down?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON JOHN</SPEAKER>
+<LINE>Come, let us go. These things, come thus to light,</LINE>
+<LINE>Smother her spirits up.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt DON PEDRO, DON JOHN, and CLAUDIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>How doth the lady?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Dead, I think. Help, uncle!</LINE>
+<LINE>Hero! why, Hero! Uncle! Signior Benedick! Friar!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>O Fate! take not away thy heavy hand.</LINE>
+<LINE>Death is the fairest cover for her shame</LINE>
+<LINE>That may be wish'd for.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>How now, cousin Hero!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Have comfort, lady.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Dost thou look up?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Yea, wherefore should she not?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Wherefore! Why, doth not every earthly thing</LINE>
+<LINE>Cry shame upon her? Could she here deny</LINE>
+<LINE>The story that is printed in her blood?</LINE>
+<LINE>Do not live, Hero; do not ope thine eyes:</LINE>
+<LINE>For, did I think thou wouldst not quickly die,</LINE>
+<LINE>Thought I thy spirits were stronger than thy shames,</LINE>
+<LINE>Myself would, on the rearward of reproaches,</LINE>
+<LINE>Strike at thy life. Grieved I, I had but one?</LINE>
+<LINE>Chid I for that at frugal nature's frame?</LINE>
+<LINE>O, one too much by thee! Why had I one?</LINE>
+<LINE>Why ever wast thou lovely in my eyes?</LINE>
+<LINE>Why had I not with charitable hand</LINE>
+<LINE>Took up a beggar's issue at my gates,</LINE>
+<LINE>Who smirch'd thus and mired with infamy,</LINE>
+<LINE>I might have said 'No part of it is mine;</LINE>
+<LINE>This shame derives itself from unknown loins'?</LINE>
+<LINE>But mine and mine I loved and mine I praised</LINE>
+<LINE>And mine that I was proud on, mine so much</LINE>
+<LINE>That I myself was to myself not mine,</LINE>
+<LINE>Valuing of her,--why, she, O, she is fallen</LINE>
+<LINE>Into a pit of ink, that the wide sea</LINE>
+<LINE>Hath drops too few to wash her clean again</LINE>
+<LINE>And salt too little which may season give</LINE>
+<LINE>To her foul-tainted flesh!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Sir, sir, be patient.</LINE>
+<LINE>For my part, I am so attired in wonder,</LINE>
+<LINE>I know not what to say.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>O, on my soul, my cousin is belied!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Lady, were you her bedfellow last night?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>No, truly not; although, until last night,</LINE>
+<LINE>I have this twelvemonth been her bedfellow.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Confirm'd, confirm'd! O, that is stronger made</LINE>
+<LINE>Which was before barr'd up with ribs of iron!</LINE>
+<LINE>Would the two princes lie, and Claudio lie,</LINE>
+<LINE>Who loved her so, that, speaking of her foulness,</LINE>
+<LINE>Wash'd it with tears? Hence from her! let her die.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Hear me a little; for I have only been</LINE>
+<LINE>Silent so long and given way unto</LINE>
+<LINE>This course of fortune</LINE>
+<LINE>By noting of the lady. I have mark'd</LINE>
+<LINE>A thousand blushing apparitions</LINE>
+<LINE>To start into her face, a thousand innocent shames</LINE>
+<LINE>In angel whiteness beat away those blushes;</LINE>
+<LINE>And in her eye there hath appear'd a fire,</LINE>
+<LINE>To burn the errors that these princes hold</LINE>
+<LINE>Against her maiden truth. Call me a fool;</LINE>
+<LINE>Trust not my reading nor my observations,</LINE>
+<LINE>Which with experimental seal doth warrant</LINE>
+<LINE>The tenor of my book; trust not my age,</LINE>
+<LINE>My reverence, calling, nor divinity,</LINE>
+<LINE>If this sweet lady lie not guiltless here</LINE>
+<LINE>Under some biting error.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Friar, it cannot be.</LINE>
+<LINE>Thou seest that all the grace that she hath left</LINE>
+<LINE>Is that she will not add to her damnation</LINE>
+<LINE>A sin of perjury; she not denies it:</LINE>
+<LINE>Why seek'st thou then to cover with excuse</LINE>
+<LINE>That which appears in proper nakedness?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Lady, what man is he you are accused of?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>They know that do accuse me; I know none:</LINE>
+<LINE>If I know more of any man alive</LINE>
+<LINE>Than that which maiden modesty doth warrant,</LINE>
+<LINE>Let all my sins lack mercy! O my father,</LINE>
+<LINE>Prove you that any man with me conversed</LINE>
+<LINE>At hours unmeet, or that I yesternight</LINE>
+<LINE>Maintain'd the change of words with any creature,</LINE>
+<LINE>Refuse me, hate me, torture me to death!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>There is some strange misprision in the princes.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Two of them have the very bent of honour;</LINE>
+<LINE>And if their wisdoms be misled in this,</LINE>
+<LINE>The practise of it lives in John the bastard,</LINE>
+<LINE>Whose spirits toil in frame of villanies.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I know not. If they speak but truth of her,</LINE>
+<LINE>These hands shall tear her; if they wrong her honour,</LINE>
+<LINE>The proudest of them shall well hear of it.</LINE>
+<LINE>Time hath not yet so dried this blood of mine,</LINE>
+<LINE>Nor age so eat up my invention,</LINE>
+<LINE>Nor fortune made such havoc of my means,</LINE>
+<LINE>Nor my bad life reft me so much of friends,</LINE>
+<LINE>But they shall find, awaked in such a kind,</LINE>
+<LINE>Both strength of limb and policy of mind,</LINE>
+<LINE>Ability in means and choice of friends,</LINE>
+<LINE>To quit me of them throughly.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Pause awhile,</LINE>
+<LINE>And let my counsel sway you in this case.</LINE>
+<LINE>Your daughter here the princes left for dead:</LINE>
+<LINE>Let her awhile be secretly kept in,</LINE>
+<LINE>And publish it that she is dead indeed;</LINE>
+<LINE>Maintain a mourning ostentation</LINE>
+<LINE>And on your family's old monument</LINE>
+<LINE>Hang mournful epitaphs and do all rites</LINE>
+<LINE>That appertain unto a burial.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>What shall become of this? what will this do?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Marry, this well carried shall on her behalf</LINE>
+<LINE>Change slander to remorse; that is some good:</LINE>
+<LINE>But not for that dream I on this strange course,</LINE>
+<LINE>But on this travail look for greater birth.</LINE>
+<LINE>She dying, as it must so be maintain'd,</LINE>
+<LINE>Upon the instant that she was accused,</LINE>
+<LINE>Shall be lamented, pitied and excused</LINE>
+<LINE>Of every hearer: for it so falls out</LINE>
+<LINE>That what we have we prize not to the worth</LINE>
+<LINE>Whiles we enjoy it, but being lack'd and lost,</LINE>
+<LINE>Why, then we rack the value, then we find</LINE>
+<LINE>The virtue that possession would not show us</LINE>
+<LINE>Whiles it was ours. So will it fare with Claudio:</LINE>
+<LINE>When he shall hear she died upon his words,</LINE>
+<LINE>The idea of her life shall sweetly creep</LINE>
+<LINE>Into his study of imagination,</LINE>
+<LINE>And every lovely organ of her life</LINE>
+<LINE>Shall come apparell'd in more precious habit,</LINE>
+<LINE>More moving-delicate and full of life,</LINE>
+<LINE>Into the eye and prospect of his soul,</LINE>
+<LINE>Than when she lived indeed; then shall he mourn,</LINE>
+<LINE>If ever love had interest in his liver,</LINE>
+<LINE>And wish he had not so accused her,</LINE>
+<LINE>No, though he thought his accusation true.</LINE>
+<LINE>Let this be so, and doubt not but success</LINE>
+<LINE>Will fashion the event in better shape</LINE>
+<LINE>Than I can lay it down in likelihood.</LINE>
+<LINE>But if all aim but this be levell'd false,</LINE>
+<LINE>The supposition of the lady's death</LINE>
+<LINE>Will quench the wonder of her infamy:</LINE>
+<LINE>And if it sort not well, you may conceal her,</LINE>
+<LINE>As best befits her wounded reputation,</LINE>
+<LINE>In some reclusive and religious life,</LINE>
+<LINE>Out of all eyes, tongues, minds and injuries.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Signior Leonato, let the friar advise you:</LINE>
+<LINE>And though you know my inwardness and love</LINE>
+<LINE>Is very much unto the prince and Claudio,</LINE>
+<LINE>Yet, by mine honour, I will deal in this</LINE>
+<LINE>As secretly and justly as your soul</LINE>
+<LINE>Should with your body.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Being that I flow in grief,</LINE>
+<LINE>The smallest twine may lead me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>'Tis well consented: presently away;</LINE>
+<LINE>For to strange sores strangely they strain the cure.</LINE>
+<LINE>Come, lady, die to live: this wedding-day</LINE>
+<LINE>Perhaps is but prolong'd: have patience and endure.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt all but BENEDICK and BEATRICE</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Lady Beatrice, have you wept all this while?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Yea, and I will weep a while longer.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I will not desire that.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>You have no reason; I do it freely.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Surely I do believe your fair cousin is wronged.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Ah, how much might the man deserve of me that would right her!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Is there any way to show such friendship?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>A very even way, but no such friend.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>May a man do it?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>It is a man's office, but not yours.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I do love nothing in the world so well as you: is</LINE>
+<LINE>not that strange?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>As strange as the thing I know not. It were as</LINE>
+<LINE>possible for me to say I loved nothing so well as</LINE>
+<LINE>you: but believe me not; and yet I lie not; I</LINE>
+<LINE>confess nothing, nor I deny nothing. I am sorry for my cousin.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>By my sword, Beatrice, thou lovest me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Do not swear, and eat it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I will swear by it that you love me; and I will make</LINE>
+<LINE>him eat it that says I love not you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Will you not eat your word?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>With no sauce that can be devised to it. I protest</LINE>
+<LINE>I love thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Why, then, God forgive me!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>What offence, sweet Beatrice?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>You have stayed me in a happy hour: I was about to</LINE>
+<LINE>protest I loved you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>And do it with all thy heart.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I love you with so much of my heart that none is</LINE>
+<LINE>left to protest.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Come, bid me do any thing for thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Kill Claudio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Ha! not for the wide world.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>You kill me to deny it. Farewell.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Tarry, sweet Beatrice.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I am gone, though I am here: there is no love in</LINE>
+<LINE>you: nay, I pray you, let me go.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Beatrice,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>In faith, I will go.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>We'll be friends first.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>You dare easier be friends with me than fight with mine enemy.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Is Claudio thine enemy?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Is he not approved in the height a villain, that</LINE>
+<LINE>hath slandered, scorned, dishonoured my kinswoman? O</LINE>
+<LINE>that I were a man! What, bear her in hand until they</LINE>
+<LINE>come to take hands; and then, with public</LINE>
+<LINE>accusation, uncovered slander, unmitigated rancour,</LINE>
+<LINE>--O God, that I were a man! I would eat his heart</LINE>
+<LINE>in the market-place.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Hear me, Beatrice,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Talk with a man out at a window! A proper saying!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Nay, but, Beatrice,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Sweet Hero! She is wronged, she is slandered, she is undone.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Beat--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Princes and counties! Surely, a princely testimony,</LINE>
+<LINE>a goodly count, Count Comfect; a sweet gallant,</LINE>
+<LINE>surely! O that I were a man for his sake! or that I</LINE>
+<LINE>had any friend would be a man for my sake! But</LINE>
+<LINE>manhood is melted into courtesies, valour into</LINE>
+<LINE>compliment, and men are only turned into tongue, and</LINE>
+<LINE>trim ones too: he is now as valiant as Hercules</LINE>
+<LINE>that only tells a lie and swears it. I cannot be a</LINE>
+<LINE>man with wishing, therefore I will die a woman with grieving.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Tarry, good Beatrice. By this hand, I love thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Use it for my love some other way than swearing by it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Think you in your soul the Count Claudio hath wronged Hero?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Yea, as sure as I have a thought or a soul.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Enough, I am engaged; I will challenge him. I will</LINE>
+<LINE>kiss your hand, and so I leave you. By this hand,</LINE>
+<LINE>Claudio shall render me a dear account. As you</LINE>
+<LINE>hear of me, so think of me. Go, comfort your</LINE>
+<LINE>cousin: I must say she is dead: and so, farewell.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE II. A prison.</TITLE>
+<STAGEDIR>Enter DOGBERRY, VERGES, and Sexton, in gowns; and
+the Watch, with CONRADE and BORACHIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Is our whole dissembly appeared?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>O, a stool and a cushion for the sexton.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Sexton</SPEAKER>
+<LINE>Which be the malefactors?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Marry, that am I and my partner.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Nay, that's certain; we have the exhibition to examine.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Sexton</SPEAKER>
+<LINE>But which are the offenders that are to be</LINE>
+<LINE>examined? let them come before master constable.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Yea, marry, let them come before me. What is your</LINE>
+<LINE>name, friend?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Borachio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Pray, write down, Borachio. Yours, sirrah?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>I am a gentleman, sir, and my name is Conrade.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Write down, master gentleman Conrade. Masters, do</LINE>
+<LINE>you serve God?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Yea, sir, we hope.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Write down, that they hope they serve God: and</LINE>
+<LINE>write God first; for God defend but God should go</LINE>
+<LINE>before such villains! Masters, it is proved already</LINE>
+<LINE>that you are little better than false knaves; and it</LINE>
+<LINE>will go near to be thought so shortly. How answer</LINE>
+<LINE>you for yourselves?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Marry, sir, we say we are none.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>A marvellous witty fellow, I assure you: but I</LINE>
+<LINE>will go about with him. Come you hither, sirrah; a</LINE>
+<LINE>word in your ear: sir, I say to you, it is thought</LINE>
+<LINE>you are false knaves.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Sir, I say to you we are none.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Well, stand aside. 'Fore God, they are both in a</LINE>
+<LINE>tale. Have you writ down, that they are none?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Sexton</SPEAKER>
+<LINE>Master constable, you go not the way to examine:</LINE>
+<LINE>you must call forth the watch that are their accusers.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Yea, marry, that's the eftest way. Let the watch</LINE>
+<LINE>come forth. Masters, I charge you, in the prince's</LINE>
+<LINE>name, accuse these men.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>First Watchman</SPEAKER>
+<LINE>This man said, sir, that Don John, the prince's</LINE>
+<LINE>brother, was a villain.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Write down Prince John a villain. Why, this is flat</LINE>
+<LINE>perjury, to call a prince's brother villain.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Master constable,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Pray thee, fellow, peace: I do not like thy look,</LINE>
+<LINE>I promise thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Sexton</SPEAKER>
+<LINE>What heard you him say else?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Second Watchman</SPEAKER>
+<LINE>Marry, that he had received a thousand ducats of</LINE>
+<LINE>Don John for accusing the Lady Hero wrongfully.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Flat burglary as ever was committed.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Yea, by mass, that it is.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Sexton</SPEAKER>
+<LINE>What else, fellow?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>First Watchman</SPEAKER>
+<LINE>And that Count Claudio did mean, upon his words, to</LINE>
+<LINE>disgrace Hero before the whole assembly. and not marry her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>O villain! thou wilt be condemned into everlasting</LINE>
+<LINE>redemption for this.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Sexton</SPEAKER>
+<LINE>What else?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Watchman</SPEAKER>
+<LINE>This is all.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Sexton</SPEAKER>
+<LINE>And this is more, masters, than you can deny.</LINE>
+<LINE>Prince John is this morning secretly stolen away;</LINE>
+<LINE>Hero was in this manner accused, in this very manner</LINE>
+<LINE>refused, and upon the grief of this suddenly died.</LINE>
+<LINE>Master constable, let these men be bound, and</LINE>
+<LINE>brought to Leonato's: I will go before and show</LINE>
+<LINE>him their examination.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Come, let them be opinioned.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Let them be in the hands--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Off, coxcomb!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>God's my life, where's the sexton? let him write</LINE>
+<LINE>down the prince's officer coxcomb. Come, bind them.</LINE>
+<LINE>Thou naughty varlet!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CONRADE</SPEAKER>
+<LINE>Away! you are an ass, you are an ass.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Dost thou not suspect my place? dost thou not</LINE>
+<LINE>suspect my years? O that he were here to write me</LINE>
+<LINE>down an ass! But, masters, remember that I am an</LINE>
+<LINE>ass; though it be not written down, yet forget not</LINE>
+<LINE>that I am an ass. No, thou villain, thou art full of</LINE>
+<LINE>piety, as shall be proved upon thee by good witness.</LINE>
+<LINE>I am a wise fellow, and, which is more, an officer,</LINE>
+<LINE>and, which is more, a householder, and, which is</LINE>
+<LINE>more, as pretty a piece of flesh as any is in</LINE>
+<LINE>Messina, and one that knows the law, go to; and a</LINE>
+<LINE>rich fellow enough, go to; and a fellow that hath</LINE>
+<LINE>had losses, and one that hath two gowns and every</LINE>
+<LINE>thing handsome about him. Bring him away. O that</LINE>
+<LINE>I had been writ down an ass!</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+</ACT>
+
+<ACT><TITLE>ACT V</TITLE>
+
+<SCENE><TITLE>SCENE I. Before LEONATO'S house.</TITLE>
+<STAGEDIR>Enter LEONATO and ANTONIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>If you go on thus, you will kill yourself:</LINE>
+<LINE>And 'tis not wisdom thus to second grief</LINE>
+<LINE>Against yourself.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I pray thee, cease thy counsel,</LINE>
+<LINE>Which falls into mine ears as profitless</LINE>
+<LINE>As water in a sieve: give not me counsel;</LINE>
+<LINE>Nor let no comforter delight mine ear</LINE>
+<LINE>But such a one whose wrongs do suit with mine.</LINE>
+<LINE>Bring me a father that so loved his child,</LINE>
+<LINE>Whose joy of her is overwhelm'd like mine,</LINE>
+<LINE>And bid him speak of patience;</LINE>
+<LINE>Measure his woe the length and breadth of mine</LINE>
+<LINE>And let it answer every strain for strain,</LINE>
+<LINE>As thus for thus and such a grief for such,</LINE>
+<LINE>In every lineament, branch, shape, and form:</LINE>
+<LINE>If such a one will smile and stroke his beard,</LINE>
+<LINE>Bid sorrow wag, cry 'hem!' when he should groan,</LINE>
+<LINE>Patch grief with proverbs, make misfortune drunk</LINE>
+<LINE>With candle-wasters; bring him yet to me,</LINE>
+<LINE>And I of him will gather patience.</LINE>
+<LINE>But there is no such man: for, brother, men</LINE>
+<LINE>Can counsel and speak comfort to that grief</LINE>
+<LINE>Which they themselves not feel; but, tasting it,</LINE>
+<LINE>Their counsel turns to passion, which before</LINE>
+<LINE>Would give preceptial medicine to rage,</LINE>
+<LINE>Fetter strong madness in a silken thread,</LINE>
+<LINE>Charm ache with air and agony with words:</LINE>
+<LINE>No, no; 'tis all men's office to speak patience</LINE>
+<LINE>To those that wring under the load of sorrow,</LINE>
+<LINE>But no man's virtue nor sufficiency</LINE>
+<LINE>To be so moral when he shall endure</LINE>
+<LINE>The like himself. Therefore give me no counsel:</LINE>
+<LINE>My griefs cry louder than advertisement.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Therein do men from children nothing differ.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I pray thee, peace. I will be flesh and blood;</LINE>
+<LINE>For there was never yet philosopher</LINE>
+<LINE>That could endure the toothache patiently,</LINE>
+<LINE>However they have writ the style of gods</LINE>
+<LINE>And made a push at chance and sufferance.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Yet bend not all the harm upon yourself;</LINE>
+<LINE>Make those that do offend you suffer too.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>There thou speak'st reason: nay, I will do so.</LINE>
+<LINE>My soul doth tell me Hero is belied;</LINE>
+<LINE>And that shall Claudio know; so shall the prince</LINE>
+<LINE>And all of them that thus dishonour her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Here comes the prince and Claudio hastily.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter DON PEDRO and CLAUDIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Good den, good den.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Good day to both of you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Hear you. my lords,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>We have some haste, Leonato.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Some haste, my lord! well, fare you well, my lord:</LINE>
+<LINE>Are you so hasty now? well, all is one.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Nay, do not quarrel with us, good old man.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>If he could right himself with quarreling,</LINE>
+<LINE>Some of us would lie low.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Who wrongs him?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Marry, thou dost wrong me; thou dissembler, thou:--</LINE>
+<LINE>Nay, never lay thy hand upon thy sword;</LINE>
+<LINE>I fear thee not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Marry, beshrew my hand,</LINE>
+<LINE>If it should give your age such cause of fear:</LINE>
+<LINE>In faith, my hand meant nothing to my sword.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Tush, tush, man; never fleer and jest at me:</LINE>
+<LINE>I speak not like a dotard nor a fool,</LINE>
+<LINE>As under privilege of age to brag</LINE>
+<LINE>What I have done being young, or what would do</LINE>
+<LINE>Were I not old. Know, Claudio, to thy head,</LINE>
+<LINE>Thou hast so wrong'd mine innocent child and me</LINE>
+<LINE>That I am forced to lay my reverence by</LINE>
+<LINE>And, with grey hairs and bruise of many days,</LINE>
+<LINE>Do challenge thee to trial of a man.</LINE>
+<LINE>I say thou hast belied mine innocent child;</LINE>
+<LINE>Thy slander hath gone through and through her heart,</LINE>
+<LINE>And she lies buried with her ancestors;</LINE>
+<LINE>O, in a tomb where never scandal slept,</LINE>
+<LINE>Save this of hers, framed by thy villany!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>My villany?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Thine, Claudio; thine, I say.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>You say not right, old man.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>My lord, my lord,</LINE>
+<LINE>I'll prove it on his body, if he dare,</LINE>
+<LINE>Despite his nice fence and his active practise,</LINE>
+<LINE>His May of youth and bloom of lustihood.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Away! I will not have to do with you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Canst thou so daff me? Thou hast kill'd my child:</LINE>
+<LINE>If thou kill'st me, boy, thou shalt kill a man.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>He shall kill two of us, and men indeed:</LINE>
+<LINE>But that's no matter; let him kill one first;</LINE>
+<LINE>Win me and wear me; let him answer me.</LINE>
+<LINE>Come, follow me, boy; come, sir boy, come, follow me:</LINE>
+<LINE>Sir boy, I'll whip you from your foining fence;</LINE>
+<LINE>Nay, as I am a gentleman, I will.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Brother,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Content yourself. God knows I loved my niece;</LINE>
+<LINE>And she is dead, slander'd to death by villains,</LINE>
+<LINE>That dare as well answer a man indeed</LINE>
+<LINE>As I dare take a serpent by the tongue:</LINE>
+<LINE>Boys, apes, braggarts, Jacks, milksops!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Brother Antony,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Hold you content. What, man! I know them, yea,</LINE>
+<LINE>And what they weigh, even to the utmost scruple,--</LINE>
+<LINE>Scrambling, out-facing, fashion-monging boys,</LINE>
+<LINE>That lie and cog and flout, deprave and slander,</LINE>
+<LINE>Go anticly, show outward hideousness,</LINE>
+<LINE>And speak off half a dozen dangerous words,</LINE>
+<LINE>How they might hurt their enemies, if they durst;</LINE>
+<LINE>And this is all.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>But, brother Antony,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Come, 'tis no matter:</LINE>
+<LINE>Do not you meddle; let me deal in this.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Gentlemen both, we will not wake your patience.</LINE>
+<LINE>My heart is sorry for your daughter's death:</LINE>
+<LINE>But, on my honour, she was charged with nothing</LINE>
+<LINE>But what was true and very full of proof.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>My lord, my lord,--</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I will not hear you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>No? Come, brother; away! I will be heard.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>And shall, or some of us will smart for it.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt LEONATO and ANTONIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>See, see; here comes the man we went to seek.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter BENEDICK</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Now, signior, what news?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Good day, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Welcome, signior: you are almost come to part</LINE>
+<LINE>almost a fray.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>We had like to have had our two noses snapped off</LINE>
+<LINE>with two old men without teeth.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Leonato and his brother. What thinkest thou? Had</LINE>
+<LINE>we fought, I doubt we should have been too young for them.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>In a false quarrel there is no true valour. I came</LINE>
+<LINE>to seek you both.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>We have been up and down to seek thee; for we are</LINE>
+<LINE>high-proof melancholy and would fain have it beaten</LINE>
+<LINE>away. Wilt thou use thy wit?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>It is in my scabbard: shall I draw it?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Dost thou wear thy wit by thy side?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Never any did so, though very many have been beside</LINE>
+<LINE>their wit. I will bid thee draw, as we do the</LINE>
+<LINE>minstrels; draw, to pleasure us.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>As I am an honest man, he looks pale. Art thou</LINE>
+<LINE>sick, or angry?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>What, courage, man! What though care killed a cat,</LINE>
+<LINE>thou hast mettle enough in thee to kill care.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Sir, I shall meet your wit in the career, and you</LINE>
+<LINE>charge it against me. I pray you choose another subject.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Nay, then, give him another staff: this last was</LINE>
+<LINE>broke cross.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>By this light, he changes more and more: I think</LINE>
+<LINE>he be angry indeed.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>If he be, he knows how to turn his girdle.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Shall I speak a word in your ear?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>God bless me from a challenge!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE><STAGEDIR>Aside to CLAUDIO</STAGEDIR> You are a villain; I jest not:</LINE>
+<LINE>I will make it good how you dare, with what you</LINE>
+<LINE>dare, and when you dare. Do me right, or I will</LINE>
+<LINE>protest your cowardice. You have killed a sweet</LINE>
+<LINE>lady, and her death shall fall heavy on you. Let me</LINE>
+<LINE>hear from you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Well, I will meet you, so I may have good cheer.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>What, a feast, a feast?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I' faith, I thank him; he hath bid me to a calf's</LINE>
+<LINE>head and a capon; the which if I do not carve most</LINE>
+<LINE>curiously, say my knife's naught. Shall I not find</LINE>
+<LINE>a woodcock too?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Sir, your wit ambles well; it goes easily.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>I'll tell thee how Beatrice praised thy wit the</LINE>
+<LINE>other day. I said, thou hadst a fine wit: 'True,'</LINE>
+<LINE>said she, 'a fine little one.' 'No,' said I, 'a</LINE>
+<LINE>great wit:' 'Right,' says she, 'a great gross one.'</LINE>
+<LINE>'Nay,' said I, 'a good wit:' 'Just,' said she, 'it</LINE>
+<LINE>hurts nobody.' 'Nay,' said I, 'the gentleman</LINE>
+<LINE>is wise:' 'Certain,' said she, 'a wise gentleman.'</LINE>
+<LINE>'Nay,' said I, 'he hath the tongues:' 'That I</LINE>
+<LINE>believe,' said she, 'for he swore a thing to me on</LINE>
+<LINE>Monday night, which he forswore on Tuesday morning;</LINE>
+<LINE>there's a double tongue; there's two tongues.' Thus</LINE>
+<LINE>did she, an hour together, transshape thy particular</LINE>
+<LINE>virtues: yet at last she concluded with a sigh, thou</LINE>
+<LINE>wast the properest man in Italy.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>For the which she wept heartily and said she cared</LINE>
+<LINE>not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Yea, that she did: but yet, for all that, an if she</LINE>
+<LINE>did not hate him deadly, she would love him dearly:</LINE>
+<LINE>the old man's daughter told us all.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>All, all; and, moreover, God saw him when he was</LINE>
+<LINE>hid in the garden.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>But when shall we set the savage bull's horns on</LINE>
+<LINE>the sensible Benedick's head?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Yea, and text underneath, 'Here dwells Benedick the</LINE>
+<LINE>married man'?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Fare you well, boy: you know my mind. I will leave</LINE>
+<LINE>you now to your gossip-like humour: you break jests</LINE>
+<LINE>as braggarts do their blades, which God be thanked,</LINE>
+<LINE>hurt not. My lord, for your many courtesies I thank</LINE>
+<LINE>you: I must discontinue your company: your brother</LINE>
+<LINE>the bastard is fled from Messina: you have among</LINE>
+<LINE>you killed a sweet and innocent lady. For my Lord</LINE>
+<LINE>Lackbeard there, he and I shall meet: and, till</LINE>
+<LINE>then, peace be with him.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>He is in earnest.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>In most profound earnest; and, I'll warrant you, for</LINE>
+<LINE>the love of Beatrice.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>And hath challenged thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Most sincerely.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>What a pretty thing man is when he goes in his</LINE>
+<LINE>doublet and hose and leaves off his wit!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>He is then a giant to an ape; but then is an ape a</LINE>
+<LINE>doctor to such a man.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>But, soft you, let me be: pluck up, my heart, and</LINE>
+<LINE>be sad. Did he not say, my brother was fled?</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter DOGBERRY, VERGES, and the Watch, with CONRADE
+and BORACHIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Come you, sir: if justice cannot tame you, she</LINE>
+<LINE>shall ne'er weigh more reasons in her balance: nay,</LINE>
+<LINE>an you be a cursing hypocrite once, you must be looked to.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>How now? two of my brother's men bound! Borachio</LINE>
+<LINE>one!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Hearken after their offence, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Officers, what offence have these men done?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Marry, sir, they have committed false report;</LINE>
+<LINE>moreover, they have spoken untruths; secondarily,</LINE>
+<LINE>they are slanders; sixth and lastly, they have</LINE>
+<LINE>belied a lady; thirdly, they have verified unjust</LINE>
+<LINE>things; and, to conclude, they are lying knaves.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>First, I ask thee what they have done; thirdly, I</LINE>
+<LINE>ask thee what's their offence; sixth and lastly, why</LINE>
+<LINE>they are committed; and, to conclude, what you lay</LINE>
+<LINE>to their charge.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Rightly reasoned, and in his own division: and, by</LINE>
+<LINE>my troth, there's one meaning well suited.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Who have you offended, masters, that you are thus</LINE>
+<LINE>bound to your answer? this learned constable is</LINE>
+<LINE>too cunning to be understood: what's your offence?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Sweet prince, let me go no farther to mine answer:</LINE>
+<LINE>do you hear me, and let this count kill me. I have</LINE>
+<LINE>deceived even your very eyes: what your wisdoms</LINE>
+<LINE>could not discover, these shallow fools have brought</LINE>
+<LINE>to light: who in the night overheard me confessing</LINE>
+<LINE>to this man how Don John your brother incensed me</LINE>
+<LINE>to slander the Lady Hero, how you were brought into</LINE>
+<LINE>the orchard and saw me court Margaret in Hero's</LINE>
+<LINE>garments, how you disgraced her, when you should</LINE>
+<LINE>marry her: my villany they have upon record; which</LINE>
+<LINE>I had rather seal with my death than repeat over</LINE>
+<LINE>to my shame. The lady is dead upon mine and my</LINE>
+<LINE>master's false accusation; and, briefly, I desire</LINE>
+<LINE>nothing but the reward of a villain.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Runs not this speech like iron through your blood?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I have drunk poison whiles he utter'd it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>But did my brother set thee on to this?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Yea, and paid me richly for the practise of it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>He is composed and framed of treachery:</LINE>
+<LINE>And fled he is upon this villany.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Sweet Hero! now thy image doth appear</LINE>
+<LINE>In the rare semblance that I loved it first.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Come, bring away the plaintiffs: by this time our</LINE>
+<LINE>sexton hath reformed Signior Leonato of the matter:</LINE>
+<LINE>and, masters, do not forget to specify, when time</LINE>
+<LINE>and place shall serve, that I am an ass.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>VERGES</SPEAKER>
+<LINE>Here, here comes master Signior Leonato, and the</LINE>
+<LINE>Sexton too.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Re-enter LEONATO and ANTONIO, with the Sexton</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Which is the villain? let me see his eyes,</LINE>
+<LINE>That, when I note another man like him,</LINE>
+<LINE>I may avoid him: which of these is he?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>If you would know your wronger, look on me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Art thou the slave that with thy breath hast kill'd</LINE>
+<LINE>Mine innocent child?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>Yea, even I alone.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>No, not so, villain; thou beliest thyself:</LINE>
+<LINE>Here stand a pair of honourable men;</LINE>
+<LINE>A third is fled, that had a hand in it.</LINE>
+<LINE>I thank you, princes, for my daughter's death:</LINE>
+<LINE>Record it with your high and worthy deeds:</LINE>
+<LINE>'Twas bravely done, if you bethink you of it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I know not how to pray your patience;</LINE>
+<LINE>Yet I must speak. Choose your revenge yourself;</LINE>
+<LINE>Impose me to what penance your invention</LINE>
+<LINE>Can lay upon my sin: yet sinn'd I not</LINE>
+<LINE>But in mistaking.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>By my soul, nor I:</LINE>
+<LINE>And yet, to satisfy this good old man,</LINE>
+<LINE>I would bend under any heavy weight</LINE>
+<LINE>That he'll enjoin me to.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I cannot bid you bid my daughter live;</LINE>
+<LINE>That were impossible: but, I pray you both,</LINE>
+<LINE>Possess the people in Messina here</LINE>
+<LINE>How innocent she died; and if your love</LINE>
+<LINE>Can labour ought in sad invention,</LINE>
+<LINE>Hang her an epitaph upon her tomb</LINE>
+<LINE>And sing it to her bones, sing it to-night:</LINE>
+<LINE>To-morrow morning come you to my house,</LINE>
+<LINE>And since you could not be my son-in-law,</LINE>
+<LINE>Be yet my nephew: my brother hath a daughter,</LINE>
+<LINE>Almost the copy of my child that's dead,</LINE>
+<LINE>And she alone is heir to both of us:</LINE>
+<LINE>Give her the right you should have given her cousin,</LINE>
+<LINE>And so dies my revenge.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>O noble sir,</LINE>
+<LINE>Your over-kindness doth wring tears from me!</LINE>
+<LINE>I do embrace your offer; and dispose</LINE>
+<LINE>For henceforth of poor Claudio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>To-morrow then I will expect your coming;</LINE>
+<LINE>To-night I take my leave. This naughty man</LINE>
+<LINE>Shall face to face be brought to Margaret,</LINE>
+<LINE>Who I believe was pack'd in all this wrong,</LINE>
+<LINE>Hired to it by your brother.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BORACHIO</SPEAKER>
+<LINE>No, by my soul, she was not,</LINE>
+<LINE>Nor knew not what she did when she spoke to me,</LINE>
+<LINE>But always hath been just and virtuous</LINE>
+<LINE>In any thing that I do know by her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Moreover, sir, which indeed is not under white and</LINE>
+<LINE>black, this plaintiff here, the offender, did call</LINE>
+<LINE>me ass: I beseech you, let it be remembered in his</LINE>
+<LINE>punishment. And also, the watch heard them talk of</LINE>
+<LINE>one Deformed: they say be wears a key in his ear and</LINE>
+<LINE>a lock hanging by it, and borrows money in God's</LINE>
+<LINE>name, the which he hath used so long and never paid</LINE>
+<LINE>that now men grow hard-hearted and will lend nothing</LINE>
+<LINE>for God's sake: pray you, examine him upon that point.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>I thank thee for thy care and honest pains.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>Your worship speaks like a most thankful and</LINE>
+<LINE>reverend youth; and I praise God for you.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>There's for thy pains.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>God save the foundation!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Go, I discharge thee of thy prisoner, and I thank thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DOGBERRY</SPEAKER>
+<LINE>I leave an arrant knave with your worship; which I</LINE>
+<LINE>beseech your worship to correct yourself, for the</LINE>
+<LINE>example of others. God keep your worship! I wish</LINE>
+<LINE>your worship well; God restore you to health! I</LINE>
+<LINE>humbly give you leave to depart; and if a merry</LINE>
+<LINE>meeting may be wished, God prohibit it! Come, neighbour.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt DOGBERRY and VERGES</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Until to-morrow morning, lords, farewell.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Farewell, my lords: we look for you to-morrow.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>We will not fail.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>To-night I'll mourn with Hero.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE><STAGEDIR>To the Watch</STAGEDIR> Bring you these fellows on. We'll</LINE>
+<LINE>talk with Margaret,</LINE>
+<LINE>How her acquaintance grew with this lewd fellow.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt, severally</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE II. LEONATO'S garden.</TITLE>
+<STAGEDIR>Enter BENEDICK and MARGARET, meeting</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Pray thee, sweet Mistress Margaret, deserve well at</LINE>
+<LINE>my hands by helping me to the speech of Beatrice.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Will you then write me a sonnet in praise of my beauty?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>In so high a style, Margaret, that no man living</LINE>
+<LINE>shall come over it; for, in most comely truth, thou</LINE>
+<LINE>deservest it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>To have no man come over me! why, shall I always</LINE>
+<LINE>keep below stairs?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Thy wit is as quick as the greyhound's mouth; it catches.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>And yours as blunt as the fencer's foils, which hit,</LINE>
+<LINE>but hurt not.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>A most manly wit, Margaret; it will not hurt a</LINE>
+<LINE>woman: and so, I pray thee, call Beatrice: I give</LINE>
+<LINE>thee the bucklers.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Give us the swords; we have bucklers of our own.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>If you use them, Margaret, you must put in the</LINE>
+<LINE>pikes with a vice; and they are dangerous weapons for maids.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>MARGARET</SPEAKER>
+<LINE>Well, I will call Beatrice to you, who I think hath legs.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>And therefore will come.</LINE>
+<STAGEDIR>Exit MARGARET</STAGEDIR>
+<STAGEDIR>Sings</STAGEDIR>
+<LINE>The god of love,</LINE>
+<LINE>That sits above,</LINE>
+<LINE>And knows me, and knows me,</LINE>
+<LINE>How pitiful I deserve,--</LINE>
+<LINE>I mean in singing; but in loving, Leander the good</LINE>
+<LINE>swimmer, Troilus the first employer of panders, and</LINE>
+<LINE>a whole bookful of these quondam carpet-mangers,</LINE>
+<LINE>whose names yet run smoothly in the even road of a</LINE>
+<LINE>blank verse, why, they were never so truly turned</LINE>
+<LINE>over and over as my poor self in love. Marry, I</LINE>
+<LINE>cannot show it in rhyme; I have tried: I can find</LINE>
+<LINE>out no rhyme to 'lady' but 'baby,' an innocent</LINE>
+<LINE>rhyme; for 'scorn,' 'horn,' a hard rhyme; for,</LINE>
+<LINE>'school,' 'fool,' a babbling rhyme; very ominous</LINE>
+<LINE>endings: no, I was not born under a rhyming planet,</LINE>
+<LINE>nor I cannot woo in festival terms.</LINE>
+<STAGEDIR>Enter BEATRICE</STAGEDIR>
+<LINE>Sweet Beatrice, wouldst thou come when I called thee?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Yea, signior, and depart when you bid me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>O, stay but till then!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>'Then' is spoken; fare you well now: and yet, ere</LINE>
+<LINE>I go, let me go with that I came; which is, with</LINE>
+<LINE>knowing what hath passed between you and Claudio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Only foul words; and thereupon I will kiss thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Foul words is but foul wind, and foul wind is but</LINE>
+<LINE>foul breath, and foul breath is noisome; therefore I</LINE>
+<LINE>will depart unkissed.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Thou hast frighted the word out of his right sense,</LINE>
+<LINE>so forcible is thy wit. But I must tell thee</LINE>
+<LINE>plainly, Claudio undergoes my challenge; and either</LINE>
+<LINE>I must shortly hear from him, or I will subscribe</LINE>
+<LINE>him a coward. And, I pray thee now, tell me for</LINE>
+<LINE>which of my bad parts didst thou first fall in love with me?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>For them all together; which maintained so politic</LINE>
+<LINE>a state of evil that they will not admit any good</LINE>
+<LINE>part to intermingle with them. But for which of my</LINE>
+<LINE>good parts did you first suffer love for me?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Suffer love! a good epithet! I do suffer love</LINE>
+<LINE>indeed, for I love thee against my will.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>In spite of your heart, I think; alas, poor heart!</LINE>
+<LINE>If you spite it for my sake, I will spite it for</LINE>
+<LINE>yours; for I will never love that which my friend hates.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Thou and I are too wise to woo peaceably.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>It appears not in this confession: there's not one</LINE>
+<LINE>wise man among twenty that will praise himself.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>An old, an old instance, Beatrice, that lived in</LINE>
+<LINE>the lime of good neighbours. If a man do not erect</LINE>
+<LINE>in this age his own tomb ere he dies, he shall live</LINE>
+<LINE>no longer in monument than the bell rings and the</LINE>
+<LINE>widow weeps.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>And how long is that, think you?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Question: why, an hour in clamour and a quarter in</LINE>
+<LINE>rheum: therefore is it most expedient for the</LINE>
+<LINE>wise, if Don Worm, his conscience, find no</LINE>
+<LINE>impediment to the contrary, to be the trumpet of his</LINE>
+<LINE>own virtues, as I am to myself. So much for</LINE>
+<LINE>praising myself, who, I myself will bear witness, is</LINE>
+<LINE>praiseworthy: and now tell me, how doth your cousin?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Very ill.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>And how do you?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Very ill too.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Serve God, love me and mend. There will I leave</LINE>
+<LINE>you too, for here comes one in haste.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter URSULA</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>URSULA</SPEAKER>
+<LINE>Madam, you must come to your uncle. Yonder's old</LINE>
+<LINE>coil at home: it is proved my Lady Hero hath been</LINE>
+<LINE>falsely accused, the prince and Claudio mightily</LINE>
+<LINE>abused; and Don John is the author of all, who is</LINE>
+<LINE>fed and gone. Will you come presently?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Will you go hear this news, signior?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I will live in thy heart, die in thy lap, and be</LINE>
+<LINE>buried in thy eyes; and moreover I will go with</LINE>
+<LINE>thee to thy uncle's.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE III. A church.</TITLE>
+<STAGEDIR>Enter DON PEDRO, CLAUDIO, and three or four
+with tapers</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Is this the monument of Leonato?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>Lord</SPEAKER>
+<LINE>It is, my lord.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE><STAGEDIR>Reading out of a scroll</STAGEDIR></LINE>
+<LINE>Done to death by slanderous tongues</LINE>
+<LINE>Was the Hero that here lies:</LINE>
+<LINE>Death, in guerdon of her wrongs,</LINE>
+<LINE>Gives her fame which never dies.</LINE>
+<LINE>So the life that died with shame</LINE>
+<LINE>Lives in death with glorious fame.</LINE>
+<LINE>Hang thou there upon the tomb,</LINE>
+<LINE>Praising her when I am dumb.</LINE>
+<LINE>Now, music, sound, and sing your solemn hymn.</LINE>
+<SUBHEAD>SONG.</SUBHEAD>
+<LINE>Pardon, goddess of the night,</LINE>
+<LINE>Those that slew thy virgin knight;</LINE>
+<LINE>For the which, with songs of woe,</LINE>
+<LINE>Round about her tomb they go.</LINE>
+<LINE>Midnight, assist our moan;</LINE>
+<LINE>Help us to sigh and groan,</LINE>
+<LINE>Heavily, heavily:</LINE>
+<LINE>Graves, yawn and yield your dead,</LINE>
+<LINE>Till death be uttered,</LINE>
+<LINE>Heavily, heavily.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Now, unto thy bones good night!</LINE>
+<LINE>Yearly will I do this rite.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Good morrow, masters; put your torches out:</LINE>
+<LINE>The wolves have prey'd; and look, the gentle day,</LINE>
+<LINE>Before the wheels of Phoebus, round about</LINE>
+<LINE>Dapples the drowsy east with spots of grey.</LINE>
+<LINE>Thanks to you all, and leave us: fare you well.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Good morrow, masters: each his several way.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Come, let us hence, and put on other weeds;</LINE>
+<LINE>And then to Leonato's we will go.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And Hymen now with luckier issue speed's</LINE>
+<LINE>Than this for whom we render'd up this woe.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+
+<SCENE><TITLE>SCENE IV. A room in LEONATO'S house.</TITLE>
+<STAGEDIR>Enter LEONATO, ANTONIO, BENEDICK, BEATRICE,
+MARGARET, URSULA, FRIAR FRANCIS, and HERO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>Did I not tell you she was innocent?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>So are the prince and Claudio, who accused her</LINE>
+<LINE>Upon the error that you heard debated:</LINE>
+<LINE>But Margaret was in some fault for this,</LINE>
+<LINE>Although against her will, as it appears</LINE>
+<LINE>In the true course of all the question.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Well, I am glad that all things sort so well.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>And so am I, being else by faith enforced</LINE>
+<LINE>To call young Claudio to a reckoning for it.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Well, daughter, and you gentle-women all,</LINE>
+<LINE>Withdraw into a chamber by yourselves,</LINE>
+<LINE>And when I send for you, come hither mask'd.</LINE>
+<STAGEDIR>Exeunt Ladies</STAGEDIR>
+<LINE>The prince and Claudio promised by this hour</LINE>
+<LINE>To visit me. You know your office, brother:</LINE>
+<LINE>You must be father to your brother's daughter</LINE>
+<LINE>And give her to young Claudio.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>Which I will do with confirm'd countenance.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Friar, I must entreat your pains, I think.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>To do what, signior?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>To bind me, or undo me; one of them.</LINE>
+<LINE>Signior Leonato, truth it is, good signior,</LINE>
+<LINE>Your niece regards me with an eye of favour.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>That eye my daughter lent her: 'tis most true.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>And I do with an eye of love requite her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>The sight whereof I think you had from me,</LINE>
+<LINE>From Claudio and the prince: but what's your will?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Your answer, sir, is enigmatical:</LINE>
+<LINE>But, for my will, my will is your good will</LINE>
+<LINE>May stand with ours, this day to be conjoin'd</LINE>
+<LINE>In the state of honourable marriage:</LINE>
+<LINE>In which, good friar, I shall desire your help.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>My heart is with your liking.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>And my help.</LINE>
+<LINE>Here comes the prince and Claudio.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter DON PEDRO and CLAUDIO, and two or
+three others</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Good morrow to this fair assembly.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Good morrow, prince; good morrow, Claudio:</LINE>
+<LINE>We here attend you. Are you yet determined</LINE>
+<LINE>To-day to marry with my brother's daughter?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I'll hold my mind, were she an Ethiope.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Call her forth, brother; here's the friar ready.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Exit ANTONIO</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>Good morrow, Benedick. Why, what's the matter,</LINE>
+<LINE>That you have such a February face,</LINE>
+<LINE>So full of frost, of storm and cloudiness?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I think he thinks upon the savage bull.</LINE>
+<LINE>Tush, fear not, man; we'll tip thy horns with gold</LINE>
+<LINE>And all Europa shall rejoice at thee,</LINE>
+<LINE>As once Europa did at lusty Jove,</LINE>
+<LINE>When he would play the noble beast in love.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Bull Jove, sir, had an amiable low;</LINE>
+<LINE>And some such strange bull leap'd your father's cow,</LINE>
+<LINE>And got a calf in that same noble feat</LINE>
+<LINE>Much like to you, for you have just his bleat.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>For this I owe you: here comes other reckonings.</LINE>
+<STAGEDIR>Re-enter ANTONIO, with the Ladies masked</STAGEDIR>
+<LINE>Which is the lady I must seize upon?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>ANTONIO</SPEAKER>
+<LINE>This same is she, and I do give you her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Why, then she's mine. Sweet, let me see your face.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>No, that you shall not, till you take her hand</LINE>
+<LINE>Before this friar and swear to marry her.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Give me your hand: before this holy friar,</LINE>
+<LINE>I am your husband, if you like of me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>And when I lived, I was your other wife:</LINE>
+<STAGEDIR>Unmasking</STAGEDIR>
+<LINE>And when you loved, you were my other husband.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>Another Hero!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>Nothing certainer:</LINE>
+<LINE>One Hero died defiled, but I do live,</LINE>
+<LINE>And surely as I live, I am a maid.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>The former Hero! Hero that is dead!</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>She died, my lord, but whiles her slander lived.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>FRIAR FRANCIS</SPEAKER>
+<LINE>All this amazement can I qualify:</LINE>
+<LINE>When after that the holy rites are ended,</LINE>
+<LINE>I'll tell you largely of fair Hero's death:</LINE>
+<LINE>Meantime let wonder seem familiar,</LINE>
+<LINE>And to the chapel let us presently.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Soft and fair, friar. Which is Beatrice?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE><STAGEDIR>Unmasking</STAGEDIR> I answer to that name. What is your will?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Do not you love me?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Why, no; no more than reason.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Why, then your uncle and the prince and Claudio</LINE>
+<LINE>Have been deceived; they swore you did.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Do not you love me?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Troth, no; no more than reason.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>Why, then my cousin Margaret and Ursula</LINE>
+<LINE>Are much deceived; for they did swear you did.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>They swore that you were almost sick for me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>They swore that you were well-nigh dead for me.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>'Tis no such matter. Then you do not love me?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>No, truly, but in friendly recompense.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>Come, cousin, I am sure you love the gentleman.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>And I'll be sworn upon't that he loves her;</LINE>
+<LINE>For here's a paper written in his hand,</LINE>
+<LINE>A halting sonnet of his own pure brain,</LINE>
+<LINE>Fashion'd to Beatrice.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>HERO</SPEAKER>
+<LINE>And here's another</LINE>
+<LINE>Writ in my cousin's hand, stolen from her pocket,</LINE>
+<LINE>Containing her affection unto Benedick.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>A miracle! here's our own hands against our hearts.</LINE>
+<LINE>Come, I will have thee; but, by this light, I take</LINE>
+<LINE>thee for pity.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BEATRICE</SPEAKER>
+<LINE>I would not deny you; but, by this good day, I yield</LINE>
+<LINE>upon great persuasion; and partly to save your life,</LINE>
+<LINE>for I was told you were in a consumption.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Peace! I will stop your mouth.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Kissing her</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>DON PEDRO</SPEAKER>
+<LINE>How dost thou, Benedick, the married man?</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>I'll tell thee what, prince; a college of</LINE>
+<LINE>wit-crackers cannot flout me out of my humour. Dost</LINE>
+<LINE>thou think I care for a satire or an epigram? No:</LINE>
+<LINE>if a man will be beaten with brains, a' shall wear</LINE>
+<LINE>nothing handsome about him. In brief, since I do</LINE>
+<LINE>purpose to marry, I will think nothing to any</LINE>
+<LINE>purpose that the world can say against it; and</LINE>
+<LINE>therefore never flout at me for what I have said</LINE>
+<LINE>against it; for man is a giddy thing, and this is my</LINE>
+<LINE>conclusion. For thy part, Claudio, I did think to</LINE>
+<LINE>have beaten thee, but in that thou art like to be my</LINE>
+<LINE>kinsman, live unbruised and love my cousin.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>CLAUDIO</SPEAKER>
+<LINE>I had well hoped thou wouldst have denied Beatrice,</LINE>
+<LINE>that I might have cudgelled thee out of thy single</LINE>
+<LINE>life, to make thee a double-dealer; which, out of</LINE>
+<LINE>question, thou wilt be, if my cousin do not look</LINE>
+<LINE>exceedingly narrowly to thee.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Come, come, we are friends: let's have a dance ere</LINE>
+<LINE>we are married, that we may lighten our own hearts</LINE>
+<LINE>and our wives' heels.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>LEONATO</SPEAKER>
+<LINE>We'll have dancing afterward.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>First, of my word; therefore play, music. Prince,</LINE>
+<LINE>thou art sad; get thee a wife, get thee a wife:</LINE>
+<LINE>there is no staff more reverend than one tipped with horn.</LINE>
+</SPEECH>
+
+
+<STAGEDIR>Enter a Messenger</STAGEDIR>
+
+<SPEECH>
+<SPEAKER>Messenger</SPEAKER>
+<LINE>My lord, your brother John is ta'en in flight,</LINE>
+<LINE>And brought with armed men back to Messina.</LINE>
+</SPEECH>
+
+<SPEECH>
+<SPEAKER>BENEDICK</SPEAKER>
+<LINE>Think not on him till to-morrow:</LINE>
+<LINE>I'll devise thee brave punishments for him.</LINE>
+<LINE>Strike up, pipers.</LINE>
+</SPEECH>
+
+<STAGEDIR>Dance</STAGEDIR>
+<STAGEDIR>Exeunt</STAGEDIR>
+</SCENE>
+</ACT>
+</PLAY>
diff --git a/jni/ruby/test/rexml/data/namespaces.xml b/jni/ruby/test/rexml/data/namespaces.xml
new file mode 100644
index 0000000..e8e4df8
--- /dev/null
+++ b/jni/ruby/test/rexml/data/namespaces.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<foo:a xmlns:foo="http://fooNamespace/">
+ <b>
+ <c>Hello</c>
+ </b>
+
+ <foo:d>
+ <foo:e>Hey</foo:e>
+ </foo:d>
+
+ <bar:f xmlns:bar="http://barNamespace/">
+ <bar:g>Hey2</bar:g>
+ </bar:f>
+
+ <alias:x xmlns:alias="http://fooNamespace/">
+ <alias:y>Hey3</alias:y>
+ </alias:x>
+</foo:a>
diff --git a/jni/ruby/test/rexml/data/nitf.xml b/jni/ruby/test/rexml/data/nitf.xml
new file mode 100644
index 0000000..269d99e
--- /dev/null
+++ b/jni/ruby/test/rexml/data/nitf.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nitf>
+
+ <!-- Example of markup of URLs (at the bottom of the story) -->
+
+ <head>
+ <meta name="ap-cycle" content="AP"/>
+ <meta name="ap-online-code" content="1700"/>
+ <meta name="ap-company" content="CO:Media Metrix Inc;TS:MMXI;IG:SVC;"/>
+ <meta name="ap-routing" content="ENTITLEMENTS,pfONLINE,pf1700"/>
+ <meta name="ap-format" content="bx"/>
+ <meta name="ap-category" content="f"/>
+ <meta name="ap-selector" content="-----"/>
+ <meta name="ap-transref" content="V0347"/>
+ <docdata>
+ <doc-id regsrc="AP" id-string="D76UIMO80"/>
+ <urgency ed-urg="7"/>
+ <date.issue norm="20000911T185842Z"/>
+ <du-key key="Napster Traffic"/>
+ <doc.copyright holder="(AP)"/>
+ </docdata>
+ </head>
+ <body>
+ <body.head>
+ <hedline>
+ <hl1>Use of Napster Quadruples</hl1>
+ </hedline>
+ <byline>By PETER SVENSSON
+ <byttl>AP Business Writer</byttl>
+ </byline>
+ <distributor>The Associated Press</distributor>
+ <dateline>
+ <location>NEW YORK</location>
+ </dateline>
+ </body.head>
+ <body.content>
+ <block>
+ <p>Despite the uncertain legality of the Napster online music-sharing service, the number of people
+using it more than quadrupled in just five months, Media Metrix said Monday.</p>
+ <p>That made Napster the fastest-growing software application ever recorded by the Internet research
+company.</p>
+ <p>From 1.1 million home users in the United States in February, the first month Media Metrix
+tracked the application, Napster use rocketed to 4.9 million users in July.</p>
+ <p>That represents 6 percent of U.S. home PC users who have modems, said Media Metrix, which pays
+people to install monitoring software on their computers.</p>
+ <p>It estimates total usage from a panel of about 50,000 people in the United States.</p>
+ <p>Napster was also used at work by 887,000 people in July, Media Metrix said.</p>
+ <p>Napster Inc. has been sued by the recording industry for allegedly enabling copyright
+infringement. The federal government weighed in on the case Friday, saying the service is not protected
+under a key copyright law, as the San Mateo, Calif., company claims.</p>
+ <p>Bruce Ryon, head of Media Metrix&apos;s New Media Group, said Napster was used by &quot;the full spectrum of PC users, not just the youth with time on their hands and a passion for music.&quot;</p>
+ <p>The Napster program allows users to copy digital music files from the hard drives of other
+users over the Internet.</p>
+ <p>Napster Inc. said last week that 28 million people had downloaded its program. It does not reveal
+its own figures for how many people actually use the software.</p>
+ <p>Because the program connects to the company&apos;s computers over the Internet every time
+it is run, Napster Inc. can track usage exactly.</p>
+ <p>__</p>
+ <p>On the Net:</p>
+ <p><a href="http://www.napster.com">
+http://www.napster.com</a></p>
+ <p><a href="http://www.mediametrix.com">
+http://www.mediametrix.com</a></p>
+ </block>
+ </body.content>
+ </body>
+</nitf>
diff --git a/jni/ruby/test/rexml/data/numbers.xml b/jni/ruby/test/rexml/data/numbers.xml
new file mode 100644
index 0000000..a1791cd
--- /dev/null
+++ b/jni/ruby/test/rexml/data/numbers.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+
+<numbers>
+ <set>
+ <nr>3</nr>
+ <nr>24</nr>
+ <nr>55</nr>
+ <nr>11</nr>
+ <nr>2</nr>
+ <nr>-3</nr>
+ </set>
+ <set>
+ <nr value="66"/>
+ <nr value="123"/>
+ <nr value="55"/>
+ <nr value="9999"/>
+ </set>
+</numbers>
diff --git a/jni/ruby/test/rexml/data/ofbiz-issues-full-177.xml b/jni/ruby/test/rexml/data/ofbiz-issues-full-177.xml
new file mode 100644
index 0000000..bfff771
--- /dev/null
+++ b/jni/ruby/test/rexml/data/ofbiz-issues-full-177.xml
@@ -0,0 +1,13971 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE issuezilla [
+ <!-- This DTD describes a structure that contains one or more -->
+ <!-- IssueZilla issues. IssueZilla is a derivative of BugZilla -->
+ <!-- (http://bugzilla.mozilla.org) developed for use with SourceCast -->
+ <!-- $Id: issuezilla.dtd 34364 2003-11-18 00:30:16Z kmaples $ -->
+
+ <!ELEMENT issuezilla (issue+)>
+
+ <!-- Attributes of the root element 'issuezilla': -->
+ <!-- -->
+ <!-- version : Current version of IssueZilla. -->
+ <!-- urlbase : Full url to source installation of issueZilla. -->
+ <!-- maintainer : Email address of person responsible for -->
+ <!-- installation usually 'webmaster@foo.com' -->
+ <!-- exporter : Email address of the individual performing the -->
+ <!-- XML export. -->
+ <!-- charset : The originating charset for this issue. -->
+ <!-- dtd_version : Version of this DTD; the cvs revision number. -->
+
+ <!ATTLIST issuezilla version CDATA #REQUIRED>
+ <!ATTLIST issuezilla urlbase CDATA #REQUIRED>
+ <!ATTLIST issuezilla maintainer CDATA #REQUIRED>
+ <!ATTLIST issuezilla exporter CDATA #IMPLIED>
+ <!ATTLIST issuezilla charset CDATA #REQUIRED>
+
+ <!-- Attributes of the Helm project from whence this export came: -->
+
+ <!ATTLIST issuezilla project_domain CDATA #IMPLIED>
+ <!ATTLIST issuezilla project_name CDATA #IMPLIED>
+ <!ATTLIST issuezilla project_id CDATA #IMPLIED>
+
+ <!-- Current cvs revision: $Revision: 1.6 $ -->
+ <!-- Revision above should match hardcoded value below (hard-coded -->
+ <!-- to facilitate development): -->
+ <!ATTLIST issuezilla dtd_version CDATA #FIXED "Revision: 1.2">
+
+ <!-- Elements comprising an issue. -->
+
+ <!ELEMENT issue ( issue_id, (issue_status, priority, resolution,
+ component, version, rep_platform, assigned_to,
+ delta_ts, subcomponent, reporter, target_milestone?, issue_type,
+ creation_ts, qa_contact?, status_whiteboard?, issue_file_loc?,
+ votes?, op_sys, short_desc, keywords*, dependson*, blocks*,
+ is_duplicate?, has_duplicates*, cc*, long_desc+, attachment*,
+ activity*
+ )?
+ ) >
+
+ <!-- Attribute(s) of the 'issue' element: -->
+ <!-- -->
+ <!-- status_code : Error code if issue requested is missing, or user -->
+ <!-- performing the operation does not have the -->
+ <!-- necessary role. Uses HTTP codes as defined in -->
+ <!-- RFC 2616; will always return a three-digit code. -->
+ <!-- status_message : String corresponding to status_code above. -->
+
+ <!ATTLIST issue status_code (200|401|404) #REQUIRED>
+ <!ATTLIST issue status_message (OK|Unauthorized|NotFound) #REQUIRED>
+
+ <!-- Elements of issue and their definitions: -->
+ <!-- -->
+ <!-- issue_id : ID of this issue (unique key). -->
+ <!-- issue_status : Current status of this issue. -->
+ <!-- priority : Priority (severity) assigned to issue. -->
+ <!-- resolution : The issue's resolution, if any -->
+ <!-- component : Product against which issue is reported. -->
+ <!-- version : Version associated with component. -->
+ <!-- platform : Platform issue reported against (e.g. linux, etc.).-->
+ <!-- assigned_to : Email of person issue currently assigned to. -->
+ <!-- delta_ts : Last modified timestamp ('yyyy-mm-dd hh:mm:ss'). -->
+ <!-- -->
+ <!-- subcomponent : Component of component issue reported against. -->
+ <!-- reporter : Email of initial issue reporter. -->
+ <!-- target_milestone : Milestone for this issue's resolution. -->
+ <!-- issue_type : Nature of issue, e.g. defect, task, etc. -->
+ <!-- creation_ts : Issue creation timestamp ('yyyy-mm-dd hh:mm:ss').-->
+ <!-- qa_contact : Email of the QA contact for this issue. -->
+ <!-- status_whiteboard: Free text 'whiteboard' for issue comments. -->
+ <!-- issue_file_loc : URL related to issue -->
+ <!-- votes : current votes for issu -->
+ <!-- op_sys : Operating system issue reported against. -->
+ <!-- short_desc : Short description of issue. -->
+
+ <!ELEMENT issue_id (#PCDATA)>
+ <!ELEMENT issue_status (#PCDATA)>
+ <!ELEMENT priority (#PCDATA)>
+ <!ELEMENT resolution (#PCDATA)>
+ <!ELEMENT component (#PCDATA)>
+ <!ELEMENT version (#PCDATA)>
+ <!ELEMENT rep_platform (#PCDATA)>
+ <!ELEMENT assigned_to (#PCDATA)>
+ <!ELEMENT delta_ts (#PCDATA)>
+ <!ELEMENT subcomponent (#PCDATA)>
+ <!ELEMENT reporter (#PCDATA)>
+ <!ELEMENT target_milestone (#PCDATA)>
+ <!ELEMENT issue_type (#PCDATA)>
+ <!ELEMENT creation_ts (#PCDATA)>
+ <!ELEMENT qa_contact (#PCDATA)>
+ <!ELEMENT status_whiteboard (#PCDATA)>
+ <!ELEMENT issue_file_loc (#PCDATA)>
+ <!ELEMENT votes (#PCDATA)>
+ <!ELEMENT op_sys (#PCDATA)>
+ <!ELEMENT short_desc (#PCDATA)>
+
+ <!-- Data from the longdescs table for this issue id. Essentially -->
+ <!-- the log of additional comments. -->
+ <!-- -->
+ <!-- who : Email of person posting long_desc. -->
+ <!-- issue_when : Timestamp when long_desc added ('yyy-mm-dd hh:mm') -->
+ <!-- thetext : Free text that comprises the long desc. -->
+
+ <!ELEMENT long_desc (who, issue_when, thetext)>
+
+ <!ELEMENT who (#PCDATA)>
+ <!ELEMENT issue_when (#PCDATA)>
+ <!ELEMENT thetext (#PCDATA)>
+
+ <!-- 'optional' elements.
+ keywords : List of keywords for this issue.
+ dependson : List of local issue IDs that depend on this one.
+ blocks : List of local issue IDs blocked by this one.
+ is_duplicate : The issue which this issue was closed as a
+ duplicate of.
+ has_duplicates : Other issues which were closed as a duplicate
+ of this issue.
+ cc : List of email addresses of interested parties.
+
+ NOTE: elements of 'blocks', 'dependson', 'is_duplicate' and
+ 'has_duplicates' are defined elsewhere in this document. -->
+
+ <!ELEMENT keywords (#PCDATA)>
+ <!ELEMENT dependson (issue_id, who, when)>
+ <!ELEMENT blocks (issue_id, who, when)>
+ <!ELEMENT is_duplicate (issue_id, who, when)>
+ <!ELEMENT has_duplicates (issue_id, who, when)>
+ <!ELEMENT cc (#PCDATA)>
+
+ <!-- Data pertaining to attachments. NOTE - some of these fields -->
+ <!-- are currently unimplemented (ispatch, filename, etc.). -->
+
+ <!ELEMENT attachment (mimetype, attachid, date, desc, ispatch*, filename,
+ submitter_id, submitting_username, data, attachment_iz_url)>
+
+ <!-- encoding : How the inline attachment is encoded. -->
+
+ <!ATTLIST attachment encoding CDATA #FIXED "Base64" >
+
+ <!-- mimetype : Mime type for the attachment. -->
+ <!-- attachid : A unique id for this attachment. -->
+ <!-- date : Timestamp of when added 'yyyy-mm-dd hh:mm' -->
+ <!-- desc : Short description for attachment. -->
+ <!-- ispatch : Whether attachment is a patch file. -->
+ <!-- filename : Filename of attachment. -->
+ <!-- submitter_id : Issuezilla ID of attachement submitter. -->
+ <!-- submitting_username : username of attachement submitter. -->
+ <!-- data : Encoded attachment. -->
+ <!-- attachment_iz_url : URL to attachment in iz. -->
+
+ <!ELEMENT mimetype (#PCDATA)>
+ <!ELEMENT attachid (#PCDATA)>
+ <!ELEMENT date (#PCDATA)>
+ <!ELEMENT desc (#PCDATA)>
+ <!ELEMENT ispatch (#PCDATA)>
+ <!ELEMENT filename (#PCDATA)>
+ <!ELEMENT submitter_id (#PCDATA)>
+ <!ELEMENT submitting_username (#PCDATA)>
+ <!ELEMENT data (#PCDATA)>
+ <!ELEMENT attachment_iz_url (#PCDATA)>
+
+ <!-- Data pertaining to the issue's activity record. -->
+
+ <!ELEMENT activity (user, when, field_name, field_desc, oldvalue,
+ newvalue)>
+
+ <!-- user : user who performed the action -->
+ <!-- when : date the described change was made -->
+ <!-- field_name : name of db field (in fielddefs) -->
+ <!-- field_desc : description of the database field -->
+ <!-- oldvalue : value changed from -->
+ <!-- newvalue : value changed to -->
+
+ <!ELEMENT user (#PCDATA)>
+ <!ELEMENT when (#PCDATA)>
+ <!ELEMENT field_name (#PCDATA)>
+ <!ELEMENT field_desc (#PCDATA)>
+ <!ELEMENT oldvalue (#PCDATA)>
+ <!ELEMENT newvalue (#PCDATA)>
+
+
+]>
+<issuezilla exporter="guest" charset="UTF-8" version="2.11" urlbase="https://ofbiz.dev.java.net/issues/" maintainer="owner@ofbiz.dev.java.net" project_domain="dev.java.net" project_name="ofbiz" project_id="792" dtd_version="Revision: 1.2">
+<issue status_code="200" status_message="OK">
+ <issue_id>1</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Documentation</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20030820210924</delta_ts>
+ <subcomponent>website</subcomponent>
+ <reporter>ajzeneski</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-08-13 22:35:10</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Website missing</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-08-13 22:35:10</issue_when>
+ <thetext>Website is missing from CVS.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-08-19 12:09:17</issue_when>
+ <thetext>Website starting to move</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-08-19 12:11:57</issue_when>
+ <thetext>Changed milestone</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-08-20 21:09:24</issue_when>
+ <thetext>Website has been imported into CVS</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2003-08-19 12:09:17</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>STARTED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2003-08-19 12:09:17</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>unspecified</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2003-08-19 12:11:57</when>
+ <field_name>target_milestone</field_name>
+ <field_desc>Target Milestone</field_desc>
+ <oldvalue>milestone 1</oldvalue>
+ <newvalue>not determined</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2003-08-20 21:09:24</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>STARTED</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2003-08-20 21:09:24</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>2</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20030824004146</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>ajzeneski</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-08-23 14:25:20</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Missing FTL</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-08-23 14:25:20</issue_when>
+ <thetext>Missing FTL file:
+
+Error: File not found: /feature/EditFeatureCategoryFeatures.ftl</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-08-24 00:41:46</issue_when>
+ <thetext>Converted JSP to FTL, made some small corrections.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-08-24 00:41:46</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-08-24 00:41:46</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>3</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20030828103031</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>ajzeneski</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-08-23 14:27:14</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Edit Promo Bug</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-08-23 14:27:14</issue_when>
+ <thetext>When editing a promo rule; the drop down for the current condition contains only
+the existing condition (many times).</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-08-28 10:30:31</issue_when>
+ <thetext>#list directive had a type. Fix in CVS.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-08-28 10:30:31</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-08-28 10:30:31</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>4</issue_id>
+ <issue_status>VERIFIED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20030828103057</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>ajzeneski</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-08-23 14:30:52</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Price Rule Bug</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-08-23 14:30:52</issue_when>
+ <thetext>When editing a price rule; changing the is &quot;sale price&quot; flag sets properly,
+however always displays &quot;no&quot; as the selected choice.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-08-28 10:20:59</issue_when>
+ <thetext>Fixed conditional on the no button, now appears correctly.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-08-28 10:30:57</issue_when>
+ <thetext>Done</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-08-28 10:20:59</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-08-28 10:20:59</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-08-28 10:30:57</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>RESOLVED</oldvalue>
+ <newvalue>VERIFIED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>5</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20030823144138</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>ajzeneski</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-08-23 14:41:38</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Relationship missing button</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-08-23 14:41:38</issue_when>
+ <thetext>View/Edit party relationships page first is still JSP and second is missing the
+links to communication event(s).</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>6</issue_id>
+ <issue_status>CLOSED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20030918144334</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>robdawson</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-09-04 02:09:57</creation_ts>
+ <qa_contact>jonesde</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Entity Engine ConnectionFactory and DBCPConnectionFactory issues with Oracle</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>robdawson</who>
+ <issue_when>2003-09-04 02:09:57</issue_when>
+ <thetext>When using the Inline JDBC Functionality of the Entity Engine to access an Oracle
+database the Entity Engine gives SQL no suitable driver exceptions.
+
+The fix for this involves changing:
+ loader.loadClass(driverClassName);
+to
+ Class clazz = loader.loadClass(driverClassName);
+ clazz.newInstance();
+in both the ConnectionFactory and DBCPConnectionFactory classes.
+
+There will also need to be the appropriate exception handling.
+
+Please contact me if this requires any clarification.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-09-04 22:15:06</issue_when>
+ <thetext>The message you are getting means that the JDBC drivers for Oracle cannot be
+found on the classpath. Contact the users mailing list if you need further help
+with this issue.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2003-09-04 22:17:28</issue_when>
+ <thetext>Sorry; DBCP support is currently outdated and not used due to the fact that
+there is no transaction support. It is recommended you use the JOTM/XAPool
+connections. This issue will be addressed during a refactor of this class in the
+coming months.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-09-18 14:43:34</issue_when>
+ <thetext>
+This issue has been resolved with code changes similar to those recommended.
+
+HOWEVER: There is one caveat with this: I would NOT recommend running using
+either of these blocks of code. Without a transaction aware connection pool
+performance will be severely affected or operations may be performed outside of
+transactions. So, I wouldn&apos;t use straight JDBC or DBCP right now. Hopefully in
+the future DBCP will support XADataSources, then it may be an option.
+
+Later,
+-David Jones
+</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2003-09-04 22:15:06</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2003-09-04 22:15:06</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-09-18 14:43:34</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>RESOLVED</oldvalue>
+ <newvalue>CLOSED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-09-18 14:43:34</when>
+ <field_name>qa_contact</field_name>
+ <field_desc>QA Contact</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>jonesde</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>7</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040304194045</delta_ts>
+ <subcomponent>WebTools</subcomponent>
+ <reporter>fzhu_genshare</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-09-21 07:35:08</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc>http://localhost:8080/webtools/control/view/ModelGroupWriter?savetofile=true</issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>&quot;Save Entity Group XML to File&quot; error</short_desc>
+ <keywords></keywords>
+ <has_duplicates>
+ <issue_id>8</issue_id>
+ <who>jonesde</who>
+ <when>2003-11-04 01:04:02</when>
+ </has_duplicates>
+ <long_desc>
+ <who>fzhu_genshare</who>
+ <issue_when>2003-09-21 07:35:08</issue_when>
+ <thetext>If click &quot;Save Entity Group XML to File&quot; on the webtools, the new page opens and
+the following error message appears:
+
+org.apache.jasper.JasperException: Unable to compile class for JSP
+
+An error occurred at line: -1 in the jsp file: null
+
+Generated servlet error:
+[javac] Since fork is true, ignoring compiler setting.
+[javac] Compiling 1 source file
+[javac] Since fork is true, ignoring compiler setting.
+[javac] C:\DOCUME~1\ADMINI~1\LOCALS~1
+\Temp\Jetty_0_0_0_0_8080__webtools\entity\ModelGroupWriter_jsp.java:72:
+cannot resolve symbol
+[javac] symbol : variable entityGroupResourceHandler
+[javac] location: class org.ofbiz.entity.model.ModelGroupReader
+[javac] ResourceHandler resourceHandler =
+modelGroupReader.entityGroupResourceHandler;
+[javac] ^
+[javac] 1 error</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-11-04 01:04:02</issue_when>
+ <thetext>*** Issue 8 has been marked as a duplicate of this issue. ***</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-04 19:40:45</issue_when>
+ <thetext>lower priority</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-04 19:40:45</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P4</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>8</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>DUPLICATE</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20031104010402</delta_ts>
+ <subcomponent>WebTools</subcomponent>
+ <reporter>fzhu_genshare</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-09-21 07:40:53</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc>http://localhost:8080/webtools/control/view/ModelGroupWriter</issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>&quot;Generate Entity Group XML&quot; error</short_desc>
+ <keywords></keywords>
+ <is_duplicate>
+ <issue_id>7</issue_id>
+ <who>jonesde</who>
+ <when>2003-11-04 01:04:02</when>
+ </is_duplicate>
+ <long_desc>
+ <who>fzhu_genshare</who>
+ <issue_when>2003-09-21 07:40:53</issue_when>
+ <thetext>If clicked &quot;Generate Entity Group XML&quot; on webtools, an error message occurs:
+
+org.apache.jasper.JasperException: Unable to compile class for JSP
+
+An error occurred at line: -1 in the jsp file: null
+
+Generated servlet error:
+[javac] Since fork is true, ignoring compiler setting.
+[javac] Compiling 1 source file
+[javac] Since fork is true, ignoring compiler setting.
+[javac] C:\DOCUME~1\ADMINI~1\LOCALS~1
+\Temp\Jetty_0_0_0_0_8080__webtools\entity\ModelGroupWriter_jsp.java:72:
+cannot resolve symbol
+[javac] symbol : variable entityGroupResourceHandler
+[javac] location: class org.ofbiz.entity.model.ModelGroupReader
+[javac] ResourceHandler resourceHandler =
+modelGroupReader.entityGroupResourceHandler;
+[javac] ^
+[javac] 1 error
+
+It seems the class ResourceHandler is missing. The same error result in several
+links of webtools concerning entity engine xml export broken.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-11-04 01:04:02</issue_when>
+ <thetext>This issue is a duplicate of #7, they may be different links but they hit the same
+request and code.
+
+*** This issue has been marked as a duplicate of 7 ***</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-11-04 01:04:02</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-11-04 01:04:02</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>DUPLICATE</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>9</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>WONTFIX</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040223211140</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>byersa</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-10-28 09:27:31</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>GenericEntity.getBytes fails with HSQLDB</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>byersa</who>
+ <issue_when>2003-10-28 09:27:31</issue_when>
+ <thetext>getBytes internally casts to ByteWrapper which causes a ClassCastException.
+Casting directly to byte[] seems to work.
+
+Don&apos;t know if it fails with other dbs.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-23 21:11:40</issue_when>
+ <thetext>This does not appear to be something that can be fixed in OFBiz, it appears to be a bug in HSQLDB, and
+only in certain versions. Trying it with an earlier version it worked, but various other things didn&apos;t. We
+may just have to be patient until HSQLDB has another stable release.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-23 21:11:40</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-23 21:11:40</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>WONTFIX</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>10</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P5</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040630193003</delta_ts>
+ <subcomponent>Service</subcomponent>
+ <reporter>byersa</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2003-10-31 08:53:59</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>missing or incorrect service parameter message</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>byersa</who>
+ <issue_when>2003-10-31 08:53:59</issue_when>
+ <thetext>If possible, I think it would be very helpful to developers if the message that
+informs on invalid service parameter conditions would clearly indicate whether
+it is in the IN or OUT mode that the problem is occurring.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 12:17:42</issue_when>
+ <thetext>changed to enhancement</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:49:18</issue_when>
+ <thetext>reassigned</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-06-30 19:30:03</issue_when>
+ <thetext>The ServiceValidationException has been updated to support missing and extra fields as well as
+knowing which &quot;mode&quot; (IN/OUT) the error occured in.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:17:42</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>DEFECT</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:17:42</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P5</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:49:17</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:30:03</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:30:03</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>11</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>PC</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20031104002629</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>cyf</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-11-03 22:26:18</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>Windows 2000</op_sys>
+ <short_desc>&quot;response.sendRedirect&quot; in jsp could not take effect</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>cyf</who>
+ <issue_when>2003-11-03 22:26:18</issue_when>
+ <thetext>I have write a test.jsp code(like):
+.....
+hello world!
+&lt;%response.sendRedirect(&quot;control/main&quot;);%&gt;
+....
+
+define :
+in controller.xml:
+&lt;request-map uri=&quot;test&quot; edit=&quot;true&quot;&gt;
+ &lt;response name=&quot;success&quot; type=&quot;view&quot; value=&quot;test&quot;/&gt;
+ &lt;response name=&quot;error&quot; type=&quot;view&quot; value=&quot;error&quot;/&gt;
+&lt;/request-map&gt;
+.....
+&lt;view-map name=&quot;test&quot; type=&quot;region&quot;/&gt;
+
+in region also configued
+
+when I test the page :
+it&apos;s appear &quot;hello world!&quot;,but didn&apos;t redirect.
+
+When I use this code as jsp page in normal web app,redirect can take effect.
+
+I think this is a bug of in region process.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-11-04 00:26:29</issue_when>
+ <thetext>
+If I understand your complaint correctly it is because the way you organized
+things the response output stream is committed before the sendRedirect is
+called. There is absolutely nothing that can be done about this.
+
+If you want to send a redirect you should do so in an event or a non-region view
+(and non-jpublish for that matter). Those composite view tools are meant for
+generating views and involve templates that may cause the response to be
+committed before you wrapped view is even called.
+
+BTW, this may be more appropriate as a question on the users list, not as a defect
+bug report.
+
+Later,
+-David Jones</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-11-04 00:26:29</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-11-04 00:26:29</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>12</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040223213513</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>quake_wang</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-11-04 01:39:16</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>entity cache is not synchronized with DB</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>quake_wang</who>
+ <issue_when>2003-11-04 01:39:16</issue_when>
+ <thetext>Quake,
+
+It might make sense to have an issue created on the ofbiz.dev.java.net
+site that covers this problem, since it may take some time to resolve.
+Could you create one there? It could just include text from messages
+send back and forth in this email thread.
+
+Thanks,
+-David
+
+On Nov 3, 2003, at 6:57 PM, Quake_Wang wrote:
+
+&gt; The immutable flag can solve some cache synchronize issues, it will
+&gt; help for debugging. But, please consider this scenario:
+&gt;
+&gt; Service A:
+&gt; OK, now base on policy: &quot;NEVER read from the cache if you are planning
+&gt; on updating&quot;, we directly read from DB,
+&gt; order = findByPK (&quot;Order&quot;, 10000);
+&gt; order.set(&quot;status&quot;, &quot;DEF&quot;);
+&gt; order.store();
+&gt;
+&gt;
+&gt; Service B:
+&gt; order = findByPKCache(&quot;Order&quot;, 10000);
+&gt; just query, do not change any fields, entity engine will put the order
+&gt; 10000 with &quot;DEF&quot; status in cache, after some other operations (not on
+&gt; the order generic value) throw exception, rolled back service A and B,
+&gt; then PKCache is not synchronized with DB too, the immutable flag can
+&gt; not help any......
+&gt;
+&gt; You wrote a service using cache for better performance, and someone
+&gt; may assembly your service in same transaction (group service, ECA
+&gt; service), the synchronizing may broken, it&apos;s really hard to find such
+&gt; problem.
+&gt;
+&gt; Any ideas?
+&gt;
+&gt; Quake
+&gt;
+&gt; -----Original Message-----
+&gt; From: David Jones [mailto:jonesde@ofbiz.org]
+&gt; Sent: Monday, November 03, 2003 11:05 PM
+&gt; To: dev@ofbiz.dev.java.net
+&gt; Subject: Re: [OFBiz] Dev - Important - [Bug] PKCache is not
+&gt; synchronized
+&gt; with DB
+&gt;
+&gt;
+&gt;
+&gt; Because of the possibility that this is happening and causing
+&gt; previously unexplained problems, I decided to go ahead and implement
+&gt; the immutable feature on the GenericEntity. This is now in CVS, but may
+&gt; cause problems with current code (and will most likely in many cases).
+&gt; Do disable this just comment out the line near the beginning of the set
+&gt; method in GenericEntity.java. It is around line 248 in the current
+&gt; revision of the file.
+&gt;
+&gt; I fixed a few places that had this problem, including createCustomer in
+&gt; ecommerce and the userLogin service. Creating a new customer and going
+&gt; through the checkout process works fine now, but variations on the way
+&gt; I did it may break.
+&gt;
+&gt; If we have too many problems with this, I&apos;m okay with disabling it
+&gt; temporarily, but it would be nice to go this direction in the future.
+&gt; It will help resolve the possibility that strange things happen because
+&gt; things are being changed in the cache when they shouldn&apos;t be and the
+&gt; cache is out of sync with the database, ie is dirty.
+&gt;
+&gt; Feel free to report bugs along these lines, or better yet send
+&gt; patches...
+&gt;
+&gt; Later,
+&gt; -David
+&gt;
+&gt;
+&gt; On Nov 3, 2003, at 5:24 AM, David Jones wrote:
+&gt;
+&gt;&gt;
+&gt;&gt; Quake,
+&gt;&gt;
+&gt;&gt; Yes, I know this is a problem, although I had forgotten about it since
+&gt;&gt; it hasn&apos;t come up for a long time.
+&gt;&gt;
+&gt;&gt; Our official answer on this, related to the use of the cache in
+&gt;&gt; general is: NEVER read from the cache if you are planning on updating
+&gt;&gt; something. It&apos;s tempting to change the GenericValue object to have an
+&gt;&gt; unchangeable flag that is set for all versions that go into the
+&gt;&gt; cache....
+&gt;&gt;
+&gt;&gt; I agree with you, there doesn&apos;t seem to be an easy way to
+&gt;&gt; automatically clear the cache on a rollback. In most cases the
+&gt;&gt; rollback doesn&apos;t even go through the Entity Engine, it is done in
+&gt;&gt; external code. So, the Entity Engine never gets a notification of the
+&gt;&gt; rollback, and if it did it would have to remember every operation that
+&gt;&gt; happened during that transaction so it could clear those dirty cache
+&gt;&gt; entries.
+&gt;&gt;
+&gt;&gt; Thus the policy: never use the cache to read when you are planning on
+&gt;&gt; writing to the database. And yeah, maybe I should throw some code in
+&gt;&gt; there to enforce this... Any thoughts from anyone on that?
+&gt;&gt;
+&gt;&gt; Later,
+&gt;&gt; -David
+&gt;&gt;
+&gt;&gt;
+&gt;&gt;
+&gt;&gt; On Nov 3, 2003, at 4:45 AM, Quake_Wang wrote:
+&gt;&gt;
+&gt;&gt;&gt; Found a bug in the PKCache, below is the scenario
+&gt;&gt;&gt;
+&gt;&gt;&gt; Service A:
+&gt;&gt;&gt; 1. find an order with the order No. 10000, dummy code:
+&gt;&gt;&gt; order = findByPKCache(&quot;Order&quot;, 10000);
+&gt;&gt;&gt; assume we get an order with the status &quot;ABC&quot;;
+&gt;&gt;&gt;
+&gt;&gt;&gt; 2. set the order status to &quot;DEF&quot; and store, dummy code:
+&gt;&gt;&gt; order.set(&quot;status&quot;, &quot;DEF&quot;);
+&gt;&gt;&gt; order.store();
+&gt;&gt;&gt;
+&gt;&gt;&gt; Service B:
+&gt;&gt;&gt; 1. find order 10000 again, dummy code:
+&gt;&gt;&gt; order = findByPKCache(&quot;Order&quot;, 10000);
+&gt;&gt;&gt; If the service A and B are in the same transaction (ex, group service
+&gt;&gt;&gt; or ECA service), entity engine will get the order with &quot;DEF&quot; status
+&gt;&gt;&gt; and put it in the PK cache.
+&gt;&gt;&gt;
+&gt;&gt;&gt; 2. some other operations, throw exception
+&gt;&gt;&gt; Service A and B are rolled back, as the result, the PKCache is not
+&gt;&gt;&gt; synchronized with DB:
+&gt;&gt;&gt; DB: order 10000, status &quot;ABC&quot;
+&gt;&gt;&gt; PKCache: order 10000, status &quot;DEF&quot;
+&gt;&gt;&gt;
+&gt;&gt;&gt; I spent some time to trace this bug, but can not find an easy way to
+&gt;&gt;&gt; fix it, just simply change the findByPKCache to findByPK in service
+&gt;&gt;&gt; B.
+&gt;&gt;&gt; I&apos;m sending out this email in hopes of helping who may encounter the
+&gt;&gt;&gt; same problem and receiving a better fix method.
+&gt;&gt;&gt;
+&gt;&gt;&gt; Regards
+&gt;&gt;&gt; Quake
+&gt;&gt;&gt;
+&gt;&gt;&gt; ---------------------------------------------------------------------
+&gt;&gt;&gt; To unsubscribe, e-mail: dev-unsubscribe@ofbiz.dev.java.net
+&gt;&gt;&gt; For additional commands, e-mail: dev-help@ofbiz.dev.java.net
+&gt;&gt;
+&gt;&gt;
+&gt;&gt; ---------------------------------------------------------------------
+&gt;&gt; To unsubscribe, e-mail: dev-unsubscribe@ofbiz.dev.java.net
+&gt;&gt; For additional commands, e-mail: dev-help@ofbiz.dev.java.net
+&gt;
+&gt;
+&gt; ---------------------------------------------------------------------
+&gt; To unsubscribe, e-mail: dev-unsubscribe@ofbiz.dev.java.net
+&gt; For additional commands, e-mail: dev-help@ofbiz.dev.java.net
+&gt;
+&gt;
+&gt; ---------------------------------------------------------------------
+&gt; To unsubscribe, e-mail: dev-unsubscribe@ofbiz.dev.java.net
+&gt; For additional commands, e-mail: dev-help@ofbiz.dev.java.net
+
+
+---------------------------------------------------------------------
+To unsubscribe, e-mail: dev-unsubscribe@ofbiz.dev.java.net
+For additional commands, e-mail: dev-help@ofbiz.dev.java.net</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-23 21:35:13</issue_when>
+ <thetext>
+Just a note on this. To fix it there need to be ThreadLocal caches that are maintained by a class that
+implements the Synchronization interface for transactions. The idea would be that cache reads look in
+the ThreadLocal cache first, then in the global cache, cache writes put everything in the ThreadLocal
+cache and when the transaction is committed all ThreadLocal cache entries go into the global cache.
+-David</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>13</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20031112065558</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>byersa</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2003-11-12 06:15:27</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>setNonPKFields</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>byersa</who>
+ <issue_when>2003-11-12 06:15:27</issue_when>
+ <thetext>I had a situation in which I used setNonPKFields to populate an entity that had
+createdData and lastModifiedDate from parameters thinking that it would be a
+sort of parameter map. My intention was to then add the individual fields to a
+service input, but it turns out that instead of converting the date string to
+DateTime format, it kept them as strings and the service complained. I guess the
+GenericValue only converts datatypes during the persistence phase? If that is
+the case, it would be useful if it converted up loading.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2003-11-12 06:55:58</issue_when>
+ <thetext>This is really not the responsibility of the GenericValue object (or really the
+GenericEntity object). This is difficult to handle as a defect report and should
+be sent to the dev mailing for discussion of the best way to go about what you
+are trying to do. The email message should perhaps include more detail about the
+overall goal you are trying to accomplish. Note that the GenericEntity, at the
+moment, does not enforce types going in, but may be changed to do that and throw
+an exception if the type is wrong. -David</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-11-12 06:55:58</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2003-11-12 06:55:58</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>14</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>Macintosh</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040107162053</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-01-07 16:20:53</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Order Summary pages don&apos;t show free shipping promo</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-01-07 16:20:53</issue_when>
+ <thetext>Here is a description from Si Chen who reported the problem:
+
+One thing that I noticed that is anomalous, though, is that the order review
+pages still show an estimate for shipping charges, even though the customer
+should get free shipping. It seems that the checkout review pages are using
+org.ofbiz.order.shoppingcart.shipping.ShippingEvents.getShipEstimate, while on
+actual checkout it is using
+org.ofbiz.order.order.OrderReadHelper.getShippingTotal.
+
+The former is not taking into account shipping adjustments.</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>15</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040210064750</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>nowpulse</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-10 01:36:16</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Order Stats showing incorrect YTD info</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>nowpulse</who>
+ <issue_when>2004-02-10 01:36:16</issue_when>
+ <thetext>The YTD info is incorrect since the Java month starts at 0 instead of 1.
+
+ofbiz/components/order/webapp/ordermgr/WEB-INF/actions/order/orderstats.bsh
+On Line 71 the 1 should be replaced with a 0
+
+cal.set(Calendar.MONTH, 0);
+
+thank you,
+sterling</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-10 06:47:50</issue_when>
+ <thetext>Fixed as recommended.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-10 06:47:50</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-10 06:47:50</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>16</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20040527233859</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-02-24 02:56:29</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Limit Categories Shown in Drop Downs</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-24 02:56:29</issue_when>
+ <thetext>Add &quot;show in drop-down&quot; option ProductCategory, filter by it in all combo style category drop downs,
+and change existing category drop-downs to be combo-boxes, include those on the EditProduct,
+EditProductCategories, EditCategory, EditCategoryProducts, EditPromoRules pages.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-27 23:38:59</issue_when>
+ <thetext>Some progress has been made on this, but something still needs to be done with the form tool, etc to
+limit the drop downs.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 11:59:21</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-27 23:38:59</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P1</oldvalue>
+ <newvalue>P4</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>17</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20040318120731</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-02-24 02:58:45</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Filter Promotion Products to exclude discontinued</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-24 02:58:45</issue_when>
+ <thetext>Add code to the ProductPromoWorker to (perhaps optionally) filter products included by the
+salesDiscontinuationDate, and of course make sure the category member from/thru dates are being
+honored.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-25 21:00:44</issue_when>
+ <thetext>Now implemented.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-25 21:00:44</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-25 21:00:44</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:07:31</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>18</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20040318120740</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-24 03:01:49</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Back ordered, or insufficient quantities not handled right</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-24 03:01:49</issue_when>
+ <thetext>It appears that when a product is back-ordered, or reserved in excess of the AvailableToPromise the
+calculations and setting of values are messed up.
+
+Here is a report on this from Bryce Ewing on Feb 5th, 2004:
+
+=================================================
+I have found something that appears to be a bug (but I am not totally sure), I will explain this in two
+cases below:
+
+case 1: buy 1 item of product1 with no inventory
+ - inventory item created with 0 on hand, and -1 available to promise
+ - order item inventory res created with quantity of 1 and quantity not available of 1
+
+case 2: buy 2 items of product2 with 1 item in inventory
+ - inventory item updated with 1 on hand, and -1 available to promise
+ - order item inventory res created with quantity of 2 and quantity not available of 0
+
+For case 1 the system is doing exactly what I would expect, but in case 2 I would have expected that
+the order item inventory res record would have had a quantity not available of 1 rather than 0.
+
+Am I mistaken in this expectation, or is this infact a bug?
+=================================================
+
+Here is a comment on some apparently related findings by Jacopo Cappellato:
+
+=================================================
+I&apos;m studying the simple method InventoryServices.reserveProductInventory
+that implements the inventory reservation of on order/order item, and I&apos;ve
+got a question about this issue.
+What is the meaning of the quantityNotAvailable field in
+OrderItemInventoryRes entity? Where and how is it used (or intended to be
+used for)? I&apos;ve noticed that this quantity is considered in the pick list
+reports...
+
+Probably the quantityNotAvailable field should contain the quantity reserved
+that caused inventory item&apos;s atp to be less than zero: if so, why in the
+reserveProductInventory method the quantityNotAvailable is set only in line
+498 and not also after line 458? And in which way, once set, could this
+quantity return to zero?
+=================================================</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-24 18:35:45</issue_when>
+ <thetext>After a patch from Bryce Ewing it appears to be fixed.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-24 18:35:45</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-24 18:35:45</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:07:40</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>19</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120748</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-24 06:44:51</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Patch for bug in the Inventory Receive function</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 06:44:51</issue_when>
+ <thetext>In Facility manager, when you receive a purchase order delivered with more than
+one shipments and you select to receive only one shipment the proposed
+quantities are the quantities of the original purchase order and not the
+quantities of the shipment.
+In my patches I tried to change the minimum amount of code and the
+result is that the code is not well written... obviously feel free to change
+my code.
+PS: I submitted this patches to the dev list in january 2004.
+PPS: the two patches are in Unified Output Format
+
+
+Index: ofbiz/components/product/webapp/facility/inventory/receiveInventory.ftl
+===================================================================
+RCS
+file: /cvs/ofbiz/components/product/webapp/facility/inventory/receiveInventory.f
+tl,v
+retrieving revision 1.5
+diff -u -r1.5 receiveInventory.ftl
+--- ofbiz/components/product/webapp/facility/inventory/receiveInventory.ftl
+ 17 Dec 2003 22:46:36 -0000 1.5
++++ ofbiz/components/product/webapp/facility/inventory/receiveInventory.ftl
+ 14 Jan 2004 11:05:06 -0000
+@@ -312,6 +312,9 @@
+ &lt;/tr&gt;
+ &lt;#list purchaseOrderItems as orderItem&gt;
+ &lt;#assign defaultQuantity = orderItem.quantity - receivedQuantities
+[orderItem.orderItemSeqId]?double&gt;
++ &lt;#if shipment?has_content&gt;
++ &lt;#assign defaultQuantity = shippedQuantities
+[orderItem.orderItemSeqId]?double - receivedQuantities
+[orderItem.orderItemSeqId]?double&gt;
++ &lt;/#if&gt;
+ &lt;#if 0 &lt; defaultQuantity&gt;
+ &lt;#assign orderItemType = orderItem.getRelatedOne(&quot;OrderItemType&quot;)&gt;
+ &lt;input type=&quot;hidden&quot; name=&quot;orderId_o_${rowCount}&quot;
+value=&quot;${orderItem.orderId}&quot;&gt;
+
+
+
+
+
+
+
+Index: ofbiz/components/product/webapp/facility/WEB-
+INF/actions/inventory/receiveInventory.bsh
+===================================================================
+RCS file: /cvs/ofbiz/components/product/webapp/facility/WEB-
+INF/actions/inventory/receiveInventory.bsh,v
+retrieving revision 1.4
+diff -u -r1.4 receiveInventory.bsh
+--- ofbiz/components/product/webapp/facility/WEB-
+INF/actions/inventory/receiveInventory.bsh 25 Aug 2003 15:28:00 -0000
+ 1.4
++++ ofbiz/components/product/webapp/facility/WEB-
+INF/actions/inventory/receiveInventory.bsh 14 Jan 2004 11:08:14 -0000
+@@ -69,6 +69,7 @@
+ shipment = delegator.findByPrimaryKey(&quot;Shipment&quot;, UtilMisc.toMap
+(&quot;shipmentId&quot;, shipmentId));
+ }
+
++shippedQuantities = new HashMap();
+ purchaseOrderItems = null;
+ if (purchaseOrder != null) {
+ if (product != null) {
+@@ -80,14 +81,18 @@
+ exprs = new ArrayList();
+ while (issueIter.hasNext()) {
+ issuance = issueIter.next();
+- exprs.add(new EntityExpr(&quot;orderItemSeqId&quot;, EntityOperator.EQUALS,
+issuance.getString(&quot;orderItemSeqId&quot;)));
++ exprs.add(new EntityExpr(&quot;orderItemSeqId&quot;, EntityOperator.EQUALS,
+issuance.getString(&quot;orderItemSeqId&quot;)));
++ double issuanceQty = issuance.getDouble(&quot;quantity&quot;).doubleValue();
++ if (shippedQuantities.containsKey(issuance.getString
+(&quot;orderItemSeqId&quot;))) {
++ issuanceQty += ((Double)shippedQuantities.get
+(issuance.getString(&quot;orderItemSeqId&quot;))).doubleValue();
++ }
++ shippedQuantities.put(issuance.getString(&quot;orderItemSeqId&quot;),
+issuanceQty);
+ }
+ purchaseOrderItems = EntityUtil.filterByOr(orderItems, exprs);
+ } else {
+ purchaseOrderItems = purchaseOrder.getRelated(&quot;OrderItem&quot;);
+ }
+ }
+-
+ receivedQuantities = new HashMap();
+ if (purchaseOrderItems != null &amp;&amp; purchaseOrderItems.size() &gt; 0) {
+ context.put(&quot;firstOrderItem&quot;, EntityUtil.getFirst(purchaseOrderItems));
+@@ -100,7 +105,13 @@
+ if (receipts != null &amp;&amp; receipts.size() &gt; 0) {
+ recIter = receipts.iterator();
+ while (recIter.hasNext()) {
+- rec = recIter.next();
++ rec = recIter.next();
++ if (shipment != null) {
++ if (rec.getString(&quot;shipmentId&quot;) == null ||
++ !rec.getString(&quot;shipmentId&quot;).equals(shipment.getString
+(&quot;shipmentId&quot;))) {
++ continue;
++ }
++ }
+ accepted = rec.getDouble(&quot;quantityAccepted&quot;);
+ rejected = rec.getDouble(&quot;quantityRejected&quot;);
+ if (accepted != null)
+@@ -140,6 +151,7 @@
+ context.put(&quot;product&quot;, product);
+ context.put(&quot;shipments&quot;, shipments);
+ context.put(&quot;shipment&quot;, shipment);
++context.put(&quot;shippedQuantities&quot;, shippedQuantities);
+ context.put(&quot;purchaseOrderItems&quot;, purchaseOrderItems);
+ context.put(&quot;receivedQuantities&quot;, receivedQuantities);
+ context.put(&quot;rejectReasons&quot;, rejectReasons);</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 06:55:45</issue_when>
+ <thetext>Created an attachment (id=1)
+Patch files in UOF for receiveInventory,bsh and receiveInventory.ftl
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 06:35:22</issue_when>
+ <thetext>The changes are in CVS. Thanks Jacopo!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>1</attachid>
+ <date>2004-02-24 06:55:45</date>
+ <desc>Patch files in UOF for receiveInventory,bsh and receiveInventory.ftl</desc>
+ <ispatch>1</ispatch>
+ <filename>receiveInventory.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/1/receiveInventory.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-24 06:55:45</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=1)
+Patch files in UOF for receiveInventory,bsh and receiveInventory.ftl
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 06:35:22</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 06:35:22</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:07:48</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>20</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120758</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-24 07:36:49</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>List All Visits link in the Party Profile page returns an error with 0 visits</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 07:36:50</issue_when>
+ <thetext>List All Visits link in the Party Profile page returns an error with 0 visits
+(see attached patch file) due to wrong bsh code.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 07:38:20</issue_when>
+ <thetext>Created an attachment (id=2)
+Patch for bug in uof
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 06:39:57</issue_when>
+ <thetext>The changes are in CVS. Thanks Jacopo!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>2</attachid>
+ <date>2004-02-24 07:38:20</date>
+ <desc>Patch for bug in uof</desc>
+ <ispatch>1</ispatch>
+ <filename>showvisits.bsh.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/2/showvisits.bsh.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-24 07:38:20</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=2)
+Patch for bug in uof
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 06:39:57</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 06:39:57</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:07:58</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>21</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120807</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-24 07:40:56</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>findparty page returns JavaScript errors when clicking on the LookupParty link and when the lookup fields are hidden</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 07:40:56</issue_when>
+ <thetext>findparty page returns JavaScript errors when clicking on the LookupParty link
+and when the lookup fields are hidden. The attached patch should fix the
+problem.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 07:41:29</issue_when>
+ <thetext>Created an attachment (id=3)
+Patch for bug in uof
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 06:44:01</issue_when>
+ <thetext>Your changes are in CVS. Thanks Jacopo!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>3</attachid>
+ <date>2004-02-24 07:41:29</date>
+ <desc>Patch for bug in uof</desc>
+ <ispatch>1</ispatch>
+ <filename>findparty.ftl.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/3/findparty.ftl.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-24 07:41:29</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=3)
+Patch for bug in uof
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 06:44:01</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 06:44:01</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:08:07</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>22</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040318115951</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-24 08:52:07</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Errors in order entry when &quot;Payment Already Received&quot; type is selected.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 08:52:08</issue_when>
+ <thetext>In Order Entry:
+when you finalize an order and, in the &quot;Order Entry Payment
+Settings&quot; page, you select the &quot;Payment Already Received&quot; type, in the
+following page you get JavaScript errors when you click over
+the links (stating that the object &quot;document.billsetupform&quot; doesn&apos;t exist);
+and so you cannot submit the order.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-26 07:08:20</issue_when>
+ <thetext>
+This has been an issue for a while now. It&apos;s good to have it in the tracking system since it may not get
+fixed right away. Andy is more aware of what is happening there than I am, so he may have more
+feedback on it. -David</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-26 07:08:20</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-26 07:08:20</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P2</oldvalue>
+ <newvalue>P3</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 11:59:51</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>23</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040318120006</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-24 08:56:40</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Inventory receive ignores the inventory item id field.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 08:56:40</issue_when>
+ <thetext>In Facility Manager -&gt; Inventory receive:
+it always creates a new inventory item even if the &quot;inventory item id&quot; optional
+field is filled with an existing inventory item id.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:38:11</issue_when>
+ <thetext>reassigned</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:38:11</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:00:06</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>24</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040318120024</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-24 09:01:59</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Receive return: it always creates serialized inventory items.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-24 09:01:59</issue_when>
+ <thetext>In Facility Manager --&gt;Receive return
+
+it always creates a serialized inventory item (even if &apos;non-serialized&apos; is
+selected) and so the received quantities are lost (if more than one).</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:38:55</issue_when>
+ <thetext>reassigned</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:38:55</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:00:24</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>25</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040318120814</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-25 14:58:53</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Permission Checks missing in various part of Accounting Manager</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-25 14:58:53</issue_when>
+ <thetext>Various pages in the Accounting Manager have no view permission checking, including the billing
+account, invoice, etc pages.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jaz</who>
+ <issue_when>2004-02-25 15:36:03</issue_when>
+ <thetext>I&apos;m not involved in this project.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-25 16:13:54</issue_when>
+ <thetext>
+Fixed assigned to mistake.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 10:48:28</issue_when>
+ <thetext>All components have a default VIEW permission based on the component configuration. Each page no
+longer needs to have the added code as long as require auth is set in the controller.xml file. The
+checkLogin event will make sure the user has the default permission for the specific component.
+Additional permissions are only necessary when not using the default permission.</thetext>
+ </long_desc>
+ <activity>
+ <user>jaz</user>
+ <when>2004-02-25 15:36:03</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>jaz</oldvalue>
+ <newvalue>jonesde</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-25 16:13:54</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>jonesde</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 10:48:28</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 10:48:28</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:08:14</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>26</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040308115822</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-25 21:11:36</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Billing Account Errors</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-25 21:11:36</issue_when>
+ <thetext>This issue was reported by Si Chen, Olivier Heintz, and some others.
+
+The following is some text from an email fro Si Chen on Jan 26, 2004; the attached patch is also from
+this email.
+
+=======================================
+Hello. After some work we have tracked down the problem with using
+billingAccount in the order manager and were able to fix it. The
+problem is stated in the order manager&apos;s billsettings.ftl, it was
+passing in &quot;EXT_BILLACT|10000&quot; for the checkoutPaymentId instead of
+separate checkoutPaymentId and billingAccountId. Olivier had suggested
+a good patch earlier which parses this form of checkoutPaymentId in the
+CheckOutEvents.java. We took a different approach and used code from
+ecommerce to pass both checkoutPaymentId and billingAccountId. We also
+fixed CheckOutEvents.java to get the billingAccountId from the request
+and associate it with the cart. Both are shown in the attached patch.
+
+One last problem: in ecommerce, in checkoutpayment.ftl, gift cards,
+credit cards, and billing accounts are all using &quot;amount_&lt;account_id&gt;&quot;
+as the name of the input fields. As a result, when the
+toggleBillingAccount javascript function tries to toggle the billing
+account selected for entering the amount to pay, it gets confused and
+cannot do this. As a result, when you use the multi-page rather than
+express checkout in ecommerce, it does not work properly. I&apos;ll be happy
+to elaborate on this if you need me to.
+=======================================</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-25 21:14:01</issue_when>
+ <thetext>Created an attachment (id=4)
+Patch to fix BillingAccount issues
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 11:58:22</issue_when>
+ <thetext>Applied patch; fixed JS in ecommerce </thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>4</attachid>
+ <date>2004-02-25 21:14:01</date>
+ <desc>Patch to fix BillingAccount issues</desc>
+ <ispatch>1</ispatch>
+ <filename>BillingAccountError.patch</filename>
+ <submitter_id>3</submitter_id>
+ <submitting_username>jonesde</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/4/BillingAccountError.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-25 21:14:01</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=4)
+Patch to fix BillingAccount issues
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 11:58:22</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 11:58:22</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 11:58:22</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>27</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120822</delta_ts>
+ <subcomponent>WorkEffort</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-26 03:20:00</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Patch for missing pagedef file for popup template that causes lookup error (due to i18n changes)</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 03:20:00</issue_when>
+ <thetext>The lookup link in New Event -&gt; Parties page is broken.
+The popup template doesn&apos;t run the envsetup script that loads the localized
+labels.
+
+PATCH: add a file named &quot;popup.xml&quot; to the template folder containing the
+following lines:
+
+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+&lt;page&gt;
+ &lt;template-action name=&quot;/includes/envsetup.bsh&quot;/&gt;
+&lt;/page&gt;</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 07:16:55</issue_when>
+ <thetext>
+I have added the popup.xml file to CVS. Thanks for sending it over Jacopo.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:16:55</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:16:55</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:08:22</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>28</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040806161040</delta_ts>
+ <subcomponent>WorkEffort</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-26 03:32:11</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Added lookup link for parties in month view</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 03:32:11</issue_when>
+ <thetext>Added lookup link for parties in month view.
+All the stuff was already in place... I just added the link.
+However I think this is a very useful enhancement.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 03:32:44</issue_when>
+ <thetext>Created an attachment (id=5)
+Patch in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-03 08:10:20</issue_when>
+ <thetext>Changed issue type as per email from Jacopo.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-08-06 16:10:40</issue_when>
+ <thetext>Thanks Jacopo, this is now in CVS.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>5</attachid>
+ <date>2004-02-26 03:32:44</date>
+ <desc>Patch in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>month.ftl.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/5/month.ftl.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-26 03:32:44</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=5)
+Patch in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-03 08:10:20</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>ENHANCEMENT</oldvalue>
+ <newvalue>PATCH</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:00:44</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-06 16:10:40</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-06 16:10:40</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>29</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120831</delta_ts>
+ <subcomponent>WorkEffort</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-26 03:56:48</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>When creating a new event from calendar, start and end date were not filled properly in the form</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 03:56:48</issue_when>
+ <thetext>When creating a new event from calendar, start and end date were not filled
+properly in the event form.
+The two patches (one for the event.ftl file and one for the event.bsh file)
+will solve this problem.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 03:59:24</issue_when>
+ <thetext>Created an attachment (id=6)
+Patch for event.bsh in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 03:59:58</issue_when>
+ <thetext>Created an attachment (id=7)
+Patch for event.ftl file in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 07:28:02</issue_when>
+ <thetext>Your changes are in CVS, thanks Jacopo!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>6</attachid>
+ <date>2004-02-26 03:59:24</date>
+ <desc>Patch for event.bsh in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>event.bsh.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/6/event.bsh.patch</attachment_iz_url>
+ </attachment>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>7</attachid>
+ <date>2004-02-26 03:59:58</date>
+ <desc>Patch for event.ftl file in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>event.ftl.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/7/event.ftl.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-26 03:59:24</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=6)
+Patch for event.bsh in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-26 03:59:58</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=7)
+Patch for event.ftl file in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:28:02</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:28:02</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:08:31</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>30</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P5</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040308175050</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jackhung</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-26 04:17:49</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Fail to approval order</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jackhung</who>
+ <issue_when>2004-02-26 04:17:49</issue_when>
+ <thetext>19285437[ ControlServlet.java:133:DEBUG] [[[updateactivityassign] Servlet
+Starting, doing setup- total:0.0,since last(Begin):0.0]]
+19285438[ ControlServlet.java:182:DEBUG] [[[updateactivityassign] Setup
+done, doing Event(s) and View(s)- total:0.0,since last([updateactivityas...):0.0]]
+19285440[ RequestHandler.java:200:INFO ] [Processing Request]:
+updateactivityassign
+19285744[ WfProcessMgrImpl.java:106:INFO ] [WfProcessMgr.init] : Create
+process manager (org.ofbiz.order[20030730144901] / ProcessOrder[20030730144901])
+19285890[ BshUtil.java:75 :ERROR] BSH Evaluation error.
+Sourced file: &lt;Inline eval of: approvalCode.equals(&quot;0&quot;); &gt; : Attempt to resolve
+method: equals() on undefined variable or class name: approvalCode : at Line: 1
+: in file: &lt;Inline eval of: approvalCode.equals(&quot;0&quot;); &gt; : approvalCode .equals (
+&quot;0&quot; )
+
+ at bsh.UtilEvalError.toEvalError(UtilEvalError.java:82)
+ at bsh.UtilEvalError.toEvalError(UtilEvalError.java:87)
+ at bsh.BSHMethodInvocation.eval(BSHMethodInvocation.java:79)
+ at bsh.BSHPrimaryExpression.eval(BSHPrimaryExpression.java:69)
+ at bsh.Interpreter.evalParsedScript(Interpreter.java:1104)
+ at bsh.Interpreter.eval(Interpreter.java:590)
+ at bsh.Interpreter.eval(Interpreter.java:616)
+ at bsh.Interpreter.eval(Interpreter.java:606)
+ at org.ofbiz.base.util.BshUtil.eval(BshUtil.java:64)
+ at
+org.ofbiz.workflow.impl.WfExecutionObjectImpl.evalBshCondition(WfExecutionObjectImpl.java:718)
+ at
+org.ofbiz.workflow.impl.WfProcessImpl.getTransFrom(WfProcessImpl.java:477)
+ at org.ofbiz.workflow.impl.WfProcessImpl.queueNext(WfProcessImpl.java:298)
+ at
+org.ofbiz.workflow.impl.WfProcessImpl.activityComplete(WfProcessImpl.java:286)
+ at org.ofbiz.workflow.impl.WfActivityImpl.complete(WfActivityImpl.java:408)
+ at
+org.ofbiz.workflow.impl.WfAssignmentImpl.complete(WfAssignmentImpl.java:175)
+ at
+org.ofbiz.workflow.client.WorkflowClient.complete(WorkflowClient.java:266)
+ at
+org.ofbiz.workflow.client.WorkflowServices.completeAssignment(WorkflowServices.java:418)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 12:08:46</issue_when>
+ <thetext>I cannot duplicate this problem; please provide more information or we will have to close this has no
+longer an issue.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:50:50</issue_when>
+ <thetext>reassigned</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:08:46</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>STARTED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:08:46</when>
+ <field_name>op_sys</field_name>
+ <field_desc>OS/Version</field_desc>
+ <oldvalue>Linux</oldvalue>
+ <newvalue>All</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:08:46</when>
+ <field_name>rep_platform</field_name>
+ <field_desc>Platform</field_desc>
+ <oldvalue>PC</oldvalue>
+ <newvalue>All</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:08:46</when>
+ <field_name>short_desc</field_name>
+ <field_desc>Summary</field_desc>
+ <oldvalue>Fail to approval order </oldvalue>
+ <newvalue>Fail to approval order</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:50:50</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:50:50</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>STARTED</oldvalue>
+ <newvalue>NEW</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:50:50</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P2</oldvalue>
+ <newvalue>P5</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>31</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120101</delta_ts>
+ <subcomponent>WorkEffort</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-26 05:39:36</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Error message when you try to create new contact information under an event</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 05:39:36</issue_when>
+ <thetext>An error message is returned when you try to create new contact information
+under an event: Cannot locate service by name (createWorkEffortContactMech)
+
+This is the complete message:
+
+ERROR: Could not complete the Create an email address for WorkEffort process
+[problem invoking the [createWorkEffortContactMech] service with the map named
+[context] containing [{locale=en_US, infoString=foo@bar.com, userLogin=
+[GenericEntity:UserLogin][partyId,admin(java.lang.String)][disabledDateTime,null
+()][passwordHint,null()][createdTxStamp,null()][successiveFailedLogins,null()]
+[enabled,Y(java.lang.String)][userLoginId,admin(java.lang.String)]
+[currentPassword,ofbiz(java.lang.String)][lastUpdatedTxStamp,null()]
+[lastUpdatedStamp,null()][createdStamp,null()], workEffortId=10030,
+contactMechTypeId=EMAIL_ADDRESS}]: Cannot locate service by name
+(createWorkEffortContactMech)]</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:01:01</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>32</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040409054533</delta_ts>
+ <subcomponent>WorkEffort</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-26 05:50:01</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Errors that prevent adding a new phase to a project.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 05:50:01</issue_when>
+ <thetext>It&apos;s impossible to add a new phase to a project. Even if you fill all the
+fields of the new phase form when you press the &apos;create&apos; button you get an
+error in the system log (however no error messages are shown in the html page).
+
+This is the complete error message:
+
+1160388[ ServiceDispatcher.java:309:ERROR] Service Error [createPhase]: ERROR:
+Could not complete the Create Project Phase process [problem creating the newEnt
+ity value: while inserting: [GenericEntity:WorkEffortAssoc][createdTxStamp,2004-
+02-26 14:44:26.096(java.sql.Timestamp)][workEffortIdTo,10034(java.lang.String)][
+workEffortIdFrom,10031(java.lang.String)][workEffortAssocTypeId,WORK_EFF_BREAKDO
+WN(java.lang.String)][lastUpdatedTxStamp,2004-02-26 14:44:26.096(java.sql.Timest
+amp)][createdStamp,2004-02-26 14:44:26.106(java.sql.Timestamp)][lastUpdatedStamp
+,2004-02-26 14:44:26.106(java.sql.Timestamp)] (SQL Exception while executing the
+ following:INSERT INTO WORK_EFFORT_ASSOC (WORK_EFFORT_ID_FROM, WORK_EFFORT_ID_TO
+, WORK_EFFORT_ASSOC_TYPE_ID, SEQUENCE_NUM, FROM_DATE, THRU_DATE, LAST_UPDATED_ST
+AMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, CREATED_TX_STAMP) VALUES (?, ?, ?, ?,
+ ?, ?, ?, ?, ?, ?) ([-5005] (at 124): Missing non-NULL value))]</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-04-09 05:45:33</issue_when>
+ <thetext>David Jones has fixed this problem in date 2004-04-08 (see the
+thread &quot;createphase ERROR in OFBiz 3.0 and OFBiz CVS versions&quot; in the dev-list).
+</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:01:18</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-04-09 05:45:33</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-04-09 05:45:33</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>33</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P4</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20050209023142</delta_ts>
+ <subcomponent>WorkEffort</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-26 06:00:26</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>When you create a new request and you don&apos;t fill correctly all the fields no error messages are shown</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 06:00:26</issue_when>
+ <thetext>When you create a new request and you don&apos;t fill correctly all the fields no
+error messages are shown.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2005-02-09 02:31:42</issue_when>
+ <thetext>Fixed after the refactoring to screen widget.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:01:30</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2005-02-09 02:31:42</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2005-02-09 02:31:42</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>34</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P5</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120838</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-26 06:11:14</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Little JavaScript in &quot;find returns&quot; when lookup fields are hidden</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 06:11:14</issue_when>
+ <thetext>Little JavaScript in &quot;find returns&quot; when lookup fields are hidden
+
+Patch for findReturns.ftl is attached.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 06:11:49</issue_when>
+ <thetext>Created an attachment (id=8)
+Patch for findReturn.ftl in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 11:18:49</issue_when>
+ <thetext>Your changes are in CVS. Thanks for sending that over.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>8</attachid>
+ <date>2004-02-26 06:11:49</date>
+ <desc>Patch for findReturn.ftl in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>findReturn.ftl.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/8/findReturn.ftl.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-26 06:11:49</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=8)
+Patch for findReturn.ftl in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 11:18:49</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 11:18:49</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:08:38</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>35</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040308103928</delta_ts>
+ <subcomponent>E-Commerce</subcomponent>
+ <reporter>razorb</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-26 06:18:48</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc>http://localhost:8080/ecommerce/control/product/~category_id=100/~product_id=GZ-1006</issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Single variant combo doesn&apos;t select properly</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>razorb</who>
+ <issue_when>2004-02-26 06:18:48</issue_when>
+ <thetext>Using the drop down menu to select a variantion i.e. LGPL does not set the
+selection on the menu. Using the swatch/images does correctly set the menu
+selection and allows adding to the basket.
+
+I suspect a little JS error somewhere for single variant items.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-26 06:52:49</issue_when>
+ <thetext>
+I just reproduced this against the latest code base, appears to still be an issue with single feature type
+virtual products.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 09:54:51</issue_when>
+ <thetext>Updated since this is a CVS bug and updated status</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 10:39:28</issue_when>
+ <thetext>fixed JS bugs in productdetail</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-26 06:52:48</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-26 06:52:48</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P1</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 09:54:51</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>STARTED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 09:54:51</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 10:39:28</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>STARTED</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 10:39:28</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>36</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120846</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-26 23:17:51</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Facility Manager: in &apos;inventory receive&apos; the type of order is not checked and it&apos;s possible to receive a sales order if you enter its id in the &apos;purchase order id&apos; field</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-26 23:17:51</issue_when>
+ <thetext>Facility Manager: in &apos;inventory receive&apos; the type of order is not checked and
+it&apos;s possible to receive a sales order if you enter its id in the &apos;purchase
+order id&apos; field.
+
+This is the patch in UOF for the receiveInventory.bsh file (pay attention that
+some days ago I&apos;ve submitted a patch for the same file against the same CVS
+release):
+
+Index: ofbiz/components/product/webapp/facility/WEB-
+INF/actions/inventory/receiveInventory.bsh
+===================================================================
+RCS file: /cvs/ofbiz/components/product/webapp/facility/WEB-
+INF/actions/inventory/receiveInventory.bsh,v
+retrieving revision 1.4
+diff -u -r1.4 receiveInventory.bsh
+--- ofbiz/components/product/webapp/facility/WEB-
+INF/actions/inventory/receiveInventory.bsh 25 Aug 2003 15:28:00 -0000
+ 1.4
++++ ofbiz/components/product/webapp/facility/WEB-
+INF/actions/inventory/receiveInventory.bsh 27 Feb 2004 07:17:08 -0000
+@@ -43,6 +43,9 @@
+ purchaseOrder = null;
+ if (purchaseOrderId != null &amp;&amp; purchaseOrderId.length() &gt; 0) {
+ purchaseOrder = delegator.findByPrimaryKey(&quot;OrderHeader&quot;, UtilMisc.toMap
+(&quot;orderId&quot;, purchaseOrderId));
++ if (purchaseOrder != null &amp;&amp; !&quot;PURCHASE_ORDER&quot;.equals
+(purchaseOrder.getString(&quot;orderTypeId&quot;))) {
++ purchaseOrder = null;
++ }
+ }
+
+ product = null;</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-02-28 11:58:13</issue_when>
+ <thetext>The patch is in CVS.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-28 11:58:13</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-02-28 11:58:13</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:08:46</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>37</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120856</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-27 02:05:25</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Facility Manager: error message when trying to confirm stock moves needed.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-27 02:05:25</issue_when>
+ <thetext>In the Facility Manager, int the PickList -&gt; Stock Moves page, if I try to
+confirm the &quot;stock moves needed&quot; proposed by the system I get the following
+error:
+
+Error calling event: org.ofbiz.content.webapp.event.EventHandlerException: No
+rows to process
+
+And the movements are not performed.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 17:22:23</issue_when>
+ <thetext>This is caused by some code that was added to display warning messages that wiped out the rowCount
+variable. The warning message loop now uses a variable names messageCount.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 17:22:23</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 17:22:23</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:08:56</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>38</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120905</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-27 02:23:36</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Facility Manager: in Edit shipment -&gt; items tab, a wrong link to EditProduct page caused a controller error.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-27 02:23:36</issue_when>
+ <thetext>Facility Manager: in Edit shipment -&gt; items tab, a wrong link to EditProduct
+page caused a controller error.
+
+Patch for EditShipmentItems.ftl file is attached.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-27 02:24:16</issue_when>
+ <thetext>Created an attachment (id=9)
+Patch for EditShipmentItems.ftl in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 07:38:50</issue_when>
+ <thetext>Your changes are in CVS. Thanks for the patch Jacopo.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>9</attachid>
+ <date>2004-02-27 02:24:16</date>
+ <desc>Patch for EditShipmentItems.ftl in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>EditShipmentItems.ftl.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/9/EditShipmentItems.ftl.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-27 02:24:16</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=9)
+Patch for EditShipmentItems.ftl in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:38:50</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:38:50</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:09:05</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>39</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P2</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120141</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-27 04:05:34</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>SapDB problem when displaying order task list assigned to user role</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-27 04:05:34</issue_when>
+ <thetext>SapDB problem when displaying order task list assigned to user role:
+if more than one order is assigned to user role, in &quot;Order List&quot; page the
+orders are shown 5 times!
+If only one order is assigned to the user role, the order is shown well (1
+time).
+
+See picture attached.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-27 04:07:00</issue_when>
+ <thetext>Created an attachment (id=10)
+Bug example showing two orders waiting approval duplicated 5 times.
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>image/gif</mimetype>
+ <attachid>10</attachid>
+ <date>2004-02-27 04:07:00</date>
+ <desc>Bug example showing two orders waiting approval duplicated 5 times.</desc>
+ <ispatch></ispatch>
+ <filename>order_list_bug.GIF</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/10/order_list_bug.GIF</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-27 04:07:00</when>
+ <field_name>attachments.thedata</field_name>
+ <field_desc>Attachment Data</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=10)
+Bug example showing two orders waiting approval duplicated 5 times.
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:01:41</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>40</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20050209023003</delta_ts>
+ <subcomponent>WorkEffort</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-27 07:27:15</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Party lookup with empty fields returns only one person repeated many times (= number of persons).</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-27 07:27:15</issue_when>
+ <thetext>If, in Party lookup, you perform a lookup with empty fields (all records),
+returns only one person repeated many times (= number of persons).
+I suspect that the problem is in the &quot;lookupParty&quot; simple method in common
+component.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2005-02-09 02:30:03</issue_when>
+ <thetext>Fixed changing the way the lookup is implemented (see Jira issue OFBIZ-114).</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:01:50</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2005-02-09 02:30:03</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2005-02-09 02:30:03</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>41</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120912</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-27 08:53:04</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Facility Manager: in Edit shipment -&gt; items tab, a wrong link to EditProduct caused a controller error (same as issue #38)</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-27 08:53:04</issue_when>
+ <thetext>Facility Manager: in View shipment tab, a wrong link to EditProduct
+page caused a controller error (same as issue #38).
+
+Patch for ViewShipmentItemInfo.ftl file is attached.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-27 08:53:50</issue_when>
+ <thetext>Created an attachment (id=11)
+Patch for file ViewShipmentItemInfo.ftl in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 07:41:15</issue_when>
+ <thetext>Your changes are now in CVS. Thanks for sending those over Jacopo.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>11</attachid>
+ <date>2004-02-27 08:53:50</date>
+ <desc>Patch for file ViewShipmentItemInfo.ftl in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>ViewShipmentItemInfo.ftl.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/11/ViewShipmentItemInfo.ftl.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-27 08:53:50</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=11)
+Patch for file ViewShipmentItemInfo.ftl in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:41:15</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:41:15</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:09:12</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>42</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P4</priority>
+ <resolution>WONTFIX</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040630193328</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-28 22:34:42</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In shipment, the following error message appears: &quot;Applet Package Weight Reader notinited&quot;</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-28 22:34:42</issue_when>
+ <thetext>In shipment, the following error message appears: &quot;Applet Package Weight Reader
+notinited&quot;.
+I think the problem is caused by the fact that the class ShipmentScaleApplet is
+not built in the ant task.
+Was this done intentionally? (I think this applet tries to connect to a scale
+device connected to a serial port and so probably this could cause some
+problems...).</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:39:52</issue_when>
+ <thetext>reassigned; I will comment out the applet code until this is finished</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-06-30 19:33:28</issue_when>
+ <thetext>The applet is going to be replaced with a think client shipment station UI</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:39:52</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:01:59</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:33:28</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:33:28</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>WONTFIX</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>43</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120921</delta_ts>
+ <subcomponent>E-Commerce</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-02-28 22:46:49</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In &quot;Create Return&quot;, select-all checkbox doesn&apos;t work due to wrong form name.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-28 22:46:49</issue_when>
+ <thetext>In &quot;Create Return&quot;, select-all checkbox doesn&apos;t work due to wrong form name.
+
+Patch attached for requestreturn.ftl in Unified Output Format.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-28 22:47:40</issue_when>
+ <thetext>Created an attachment (id=12)
+Patch attached for requestreturn.ftl in UOF.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 07:48:30</issue_when>
+ <thetext>Your changes are in CVS. Thanks for fixing that.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>12</attachid>
+ <date>2004-02-28 22:47:39</date>
+ <desc>Patch attached for requestreturn.ftl in UOF.</desc>
+ <ispatch>1</ispatch>
+ <filename>requestreturn.ftl.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/12/requestreturn.ftl.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-02-28 22:47:40</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=12)
+Patch attached for requestreturn.ftl in UOF.
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:48:30</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 07:48:30</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:09:21</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>44</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040229030013</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-02-29 03:00:13</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Packages: ShipmentPackage&apos;s weight automatically updated when ShipmentPackageContent entries are added/deleted (weight taken from the Product entity).</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-29 03:00:13</issue_when>
+ <thetext>Packages: ShipmentPackage&apos;s weight automatically updated when
+ShipmentPackageContent entries are added/deleted (weight taken from the Product
+entity).</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>45</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040301155709</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-02-29 06:17:33</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In orderview page, if you select the &quot;QuickShip order link&quot; you get a controller error.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-02-29 06:17:33</issue_when>
+ <thetext>In orderview page (/ordermgr/control/orderview), if you select the &quot;QuickShip
+order link&quot; you get a controller error:
+
+org.ofbiz.content.webapp.control.RequestHandlerException: Unknown request
+[quickShipOrderMultiFacility]; this request does not exist or cannot be called
+directly.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 12:11:07</issue_when>
+ <thetext>I&apos;ll get on this...</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-01 15:57:09</issue_when>
+ <thetext>Changed the default behavior for this; also added request-map and service defs for the quick ship from
+multi facilites.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 12:11:07</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>STARTED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 15:57:09</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>STARTED</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-01 15:57:09</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>46</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040806152631</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-02 05:13:01</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Patch for multiple inventory facility reservation</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-02 05:13:01</issue_when>
+ <thetext>Patch for multiple inventory facility reservation:
+the intent of this is to perform reservation only if all items are available in
+one facility: that, I think, is the declared behaviour, and what is checked
+with the isInventoryAvailable method.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-02 05:13:49</issue_when>
+ <thetext>Created an attachment (id=13)
+Patch for ProductStoreWorker.java in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-08-06 15:26:31</issue_when>
+ <thetext>Thanks Jacopo, your patch is now in CVS.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>13</attachid>
+ <date>2004-03-02 05:13:49</date>
+ <desc>Patch for ProductStoreWorker.java in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>ProductStoreWorker.java.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/13/ProductStoreWorker.java.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-02 05:13:49</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=13)
+Patch for ProductStoreWorker.java in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:02:10</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-06 15:26:31</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-06 15:26:31</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>47</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040323221326</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-02 06:06:51</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Facility -&gt; Shipment: patch for JavaScript error introduced during i18n process.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-02 06:06:51</issue_when>
+ <thetext>Facility -&gt; Shipment: patch for JavaScript error introduced during i18n process.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-02 06:07:51</issue_when>
+ <thetext>Created an attachment (id=14)
+Patch for AddItemsFromOrder.ftl file in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:42:45</issue_when>
+ <thetext>reassigned</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-23 22:13:26</issue_when>
+ <thetext>This bug has been fixed by the code for the new feature regarding shipment
+plans.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>14</attachid>
+ <date>2004-03-02 06:07:51</date>
+ <desc>Patch for AddItemsFromOrder.ftl file in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>AddItemsFromOrder.ftl.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/14/AddItemsFromOrder.ftl.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-02 06:07:51</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=14)
+Patch for AddItemsFromOrder.ftl file in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:42:45</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:02:18</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-23 22:13:26</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-23 22:13:26</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>48</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P2</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120228</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-02 06:28:37</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Facility -&gt; Shipment -&gt; Items: delete shipment item link returns a constraint violation error.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-02 06:28:37</issue_when>
+ <thetext>Facility -&gt; Shipment -&gt; Items: delete shipment item link returns a constraint
+violation error:
+
+The following errors occurred:
+ERROR: Could not complete the Delete ShipmentItem process [problem removing the
+lookedUpValue value: Exception while deleting the following entity:
+[GenericEntity:ShipmentItem][shipmentId,10010(java.lang.String)]
+[shipmentItemSeqId,00001(java.lang.String)] (SQL Exception while executing the
+following:DELETE FROM SHIPMENT_ITEM WHERE SHIPMENT_ID=? AND
+SHIPMENT_ITEM_SEQ_ID=? (Integrity constraint violation ITEM_ISS_SHITM table:
+ITEM_ISSUANCE in statement [DELETE FROM SHIPMENT_ITEM WHERE SHIPMENT_ID=? AND
+SHIPMENT_ITEM_SEQ_ID=?]))]
+
+In my opinion, the delete shipment item link should perform something like this
+(instead of barely trying to remove the ShipmentItem entry): it should remove
+the item issuances, recreate the reservations and then remove the shipment item.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:02:28</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>49</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040528001027</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-03 03:11:18</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Facility -&gt; Receive PO: at now it&apos;s possible to receive multiple times the same shipment items. This is a partial patch for this problem.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-03 03:11:18</issue_when>
+ <thetext>Facility -&gt; Receive PO: at now it&apos;s possible to receive multiple times the same
+shipment items. This is a partial patch for this problem. With the attached
+patch the shipment already delivered (or cancelled) are not shown (and so the
+user cannot receive them again).
+In order to completely fix this issue the best solution is, in my opinion, to
+automatically update the shipment status to &quot;delivered&quot; when the user receives
+an item: this could be done modifying the service
+group &quot;receiveInventoryProduct&quot; by adding to it the &quot;updateShipment&quot; service
+(to update the shipment status). However I&apos;m going to add this comments to a
+new &quot;DEFECT&quot; issue.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-03 03:12:11</issue_when>
+ <thetext>Created an attachment (id=15)
+Patch for file receiveInventory.bsh in UOF.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-28 00:10:27</issue_when>
+ <thetext>This is now in CVS, thanks for sending it over Jacopo.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>15</attachid>
+ <date>2004-03-03 03:12:11</date>
+ <desc>Patch for file receiveInventory.bsh in UOF.</desc>
+ <ispatch>1</ispatch>
+ <filename>receiveInventory.bsh.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/15/receiveInventory.bsh.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-03 03:12:11</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=15)
+Patch for file receiveInventory.bsh in UOF.
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:02:37</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-28 00:10:27</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-28 00:10:27</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>50</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040528000924</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-03 03:15:23</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Facility -&gt; Receive PO: at now it&apos;s possible to receive multiple times the same shipment items.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-03 03:15:24</issue_when>
+ <thetext>Facility -&gt; Receive PO: at now it&apos;s possible to receive multiple times the same
+shipment items.
+This problem was partially fixed by the patch attached to issue #49.
+With that patch the shipment already delivered (or cancelled) are not shown
+(and so the user cannot receive them again).
+In order to completely fix this issue the best solution is, in my opinion, to
+automatically update the shipment status to &quot;delivered&quot; when the user receives
+an item: this could be done modifying the service
+group &quot;receiveInventoryProduct&quot; by adding to it the &quot;updateShipment&quot; service
+(to update the shipment status). However I&apos;m going to add this comments to a
+new &quot;DEFECT&quot; issue.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-28 00:09:24</issue_when>
+ <thetext>The patch is in, now just need to update the shipment status as mentioned in the description.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:02:49</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>51</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>REMIND</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040811004716</delta_ts>
+ <subcomponent>Startup</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-03 06:37:30</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Several SapDB error messages are generated at startup when entities are created.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-03 06:37:30</issue_when>
+ <thetext>Several SapDB error messages are generated at startup when entities are created.
+The messages appear during the indices&apos; generation phase, not for all entities
+(maybe 5-10%); here is an example:
+
+147042[ DatabaseUtil.java:319:ERROR] Could not create foreign key indices
+for entity &quot;EftAccount&quot;: SQL Exception while executing the following:
+CREATE INDEX EFTACCT_PMETH ON EFT_ACCOUNT (PAYMENT_METHOD_ID)
+Error was: com.sap.dbtech.jdbc.exceptions.DatabaseException: [-7055] (at 44):
+Column(s) already indexed:EFTACCT_PMETH
+SQL Exception while executing the following:
+CREATE INDEX EFTACCT_PADDR ON EFT_ACCOUNT (CONTACT_MECH_ID)
+Error was: com.sap.dbtech.jdbc.exceptions.DatabaseException: [-7055] (at 44):
+Column(s) already indexed:EFTACCT_PADDR</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-08-11 00:47:16</issue_when>
+ <thetext>I&apos;ve created a new issue (OFBIZ-1) for this in Jira; in it you will find a lot
+of details.
+</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:03:00</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-08-11 00:47:16</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-08-11 00:47:16</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>REMIND</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>52</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040308121509</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-04 03:14:08</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Billing accounts selectable by users during order entry are not correctly filtered out.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-04 03:14:08</issue_when>
+ <thetext>Billing accounts selectable by users as payment methods during order entry are
+not correctly filtered out: removed billing accounts are also shown (i.e.
+billing accounts with thru date &lt; now).</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 12:15:09</issue_when>
+ <thetext>Billing accounts are now filtered by date so expired accounts no longer show on order manager order
+entry and the ecommerce checkout pages</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:15:09</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:15:09</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 12:15:09</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>53</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040528010450</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-05 06:01:05</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In Shipment: it&apos;s possible to issue 0 quantities from AddItemsFormOrder page.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-05 06:01:05</issue_when>
+ <thetext>In Shipment: it&apos;s possible to issue 0 quantities from AddItemsFormOrder page
+(an ItemIssuance record is created with 0 quantity).
+I think this should be avoided by submitting only quantities &gt; 0.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-28 01:04:50</issue_when>
+ <thetext>Now checks to see if quantity is empty or 0 when issuing OIIRs to the shipment.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:03:13</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-28 01:04:50</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-28 01:04:50</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>54</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040630124239</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-05 06:20:02</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In Shipment: if you enter a non numeric value (such as null quantity) in the issue field the program returns a bad error.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-05 06:20:02</issue_when>
+ <thetext>In Shipment: if you enter a non numeric value (such as null quantity) in the
+issue field the program returns a bad error.
+See also issue#53.
+I&apos;m running SapDB and after this error I need to restart OFBiz because in every
+page I receive SQL errors like the following:
+
+Target exception: org.ofbiz.entity.GenericDataSourceException: SQL Exception
+occurred on commit (Cannot commit a transactional connection: See JDBC 2.0
+Optional Package Specification section 7.1 (p25))
+BSF info: ..\ofbiz\components\content\webapp\content\WEB-
+INF\actions\includes\envsetup.bsh at line: 0 column: 0)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-06-30 12:42:39</issue_when>
+ <thetext>Applied patch which fixes a bug in the service multi event handler</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:03:23</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 12:42:39</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 12:42:39</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>55</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040323221851</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-05 06:25:34</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In shipment, it&apos;s possible to issue to shipment items with negative reservations (atp &lt; 0); as a consequence of this, also the inventory items&apos; quantity on hand can be negative.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-05 06:25:34</issue_when>
+ <thetext>I&apos;ve noticed that, in shipment, it&apos;s possible to issue to shipment items with
+negative reservations (atp &lt; 0); as a consequence of this, also the inventory
+items&apos; quantity on hand can be negative.
+In my opinion, in this situation at least a warning message should be
+provided.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-23 22:18:51</issue_when>
+ <thetext>In &quot;Order Items&quot; page a new column has been added that tells users if they are
+issuing negative reservations and the default issuance value is also
+decremented by this quantity.
+However is still possible to override this value and force an issuance for a
+negative reservation.
+
+</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:03:32</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-23 22:18:51</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-23 22:18:51</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>56</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>WONTFIX</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>PC</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040308173638</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>vivalinux</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-08 14:07:32</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc>http://&lt;ofbiz_root_url&gt;/partymgr/control/findparty</issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Selecting &quot;Show All Records&quot; displays all names as &quot;(Not selected)&quot;</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>vivalinux</who>
+ <issue_when>2004-03-08 14:07:32</issue_when>
+ <thetext>The findparty.bsh constructs a dynamic view tailored to the search fields
+submited by the lookupparty form. Since selecting all does not give a hint about
+which fields the client is interested in, the findparty.bsh fails to include any
+of the name (firstName, lastName, groupName) fields.
+
+The can be corrected with the patch that follows or handling the select all
+records as yet another case. I think, I&apos;ll side with simplicity, and sacrify
+efficiency ....
+
+Hope this helps.
+
+
+Index: components/party/webapp/partymgr/WEB-INF/actions/party/findparty.bsh
+===================================================================
+RCS file:
+/cvs/ofbiz/components/party/webapp/partymgr/WEB-INF/actions/party/findparty.bsh,v
+retrieving revision 1.13
+diff -u -r1.13 findparty.bsh
+--- components/party/webapp/partymgr/WEB-INF/actions/party/findparty.bsh 27 Jan
+2004 19:44:36 -0000 1.13
++++ components/party/webapp/partymgr/WEB-INF/actions/party/findparty.bsh 8 Mar
+2004 22:06:24 -0000
+@@ -100,8 +100,15 @@
+
+ // default view settings
+ dynamicView.addMemberEntity(&quot;PT&quot;, &quot;Party&quot;);
++ dynamicView.addMemberEntity(&quot;PG&quot;, &quot;PartyGroup&quot;);
++ dynamicView.addMemberEntity(&quot;PP&quot;, &quot;Person&quot;);
+ dynamicView.addAlias(&quot;PT&quot;, &quot;partyId&quot;);
+ dynamicView.addAlias(&quot;PT&quot;, &quot;partyTypeId&quot;);
++ dynamicView.addAlias(&quot;PG&quot;, &quot;groupName&quot;);
++ dynamicView.addAlias(&quot;PP&quot;, &quot;firstName&quot;);
++ dynamicView.addAlias(&quot;PP&quot;, &quot;lastName&quot;);
++ dynamicView.addViewLink(&quot;PT&quot;,&quot;PG&quot;,true,UtilMisc.toList(new
+ModelKeyMap(&quot;partyId&quot;, &quot;partyId&quot;)));
++ dynamicView.addViewLink(&quot;PT&quot;,&quot;PP&quot;,true,UtilMisc.toList(new
+ModelKeyMap(&quot;partyId&quot;, &quot;partyId&quot;)));
+ dynamicView.addRelation(&quot;one-nofk&quot;, &quot;&quot;, &quot;PartyType&quot;,
+ModelKeyMap.makeKeyMapList(&quot;partyTypeId&quot;));
+ dynamicView.addRelation(&quot;many&quot;, &quot;&quot;, &quot;UserLogin&quot;,
+ModelKeyMap.makeKeyMapList(&quot;partyId&quot;));
+
+@@ -143,11 +150,6 @@
+ if (groupName != null &amp;&amp; groupName.length() &gt; 0) {
+ paramList = paramList + &quot;&amp;groupName=&quot; + groupName;
+
+- // modify the dynamic view
+- dynamicView.addMemberEntity(&quot;PG&quot;, &quot;PartyGroup&quot;);
+- dynamicView.addAlias(&quot;PG&quot;, &quot;groupName&quot;);
+- dynamicView.addViewLink(&quot;PT&quot;, &quot;PG&quot;, Boolean.FALSE,
+ModelKeyMap.makeKeyMapList(&quot;partyId&quot;));
+-
+ // add the expr
+ andExprs.add(new EntityExpr(&quot;groupName&quot;, true, EntityOperator.LIKE,
+&quot;%&quot;+groupName+&quot;%&quot;, true));
+ }
+@@ -160,14 +162,6 @@
+ firstName = request.getParameter(&quot;firstName&quot;);
+ lastName = request.getParameter(&quot;lastName&quot;);
+
+- // modify the dynamic view
+- if ((firstName != null &amp;&amp; firstName.length() &gt; 0) || (lastName != null &amp;&amp;
+lastName.length() &gt; 0)) {
+- dynamicView.addMemberEntity(&quot;PE&quot;, &quot;Person&quot;);
+- dynamicView.addAlias(&quot;PE&quot;, &quot;firstName&quot;);
+- dynamicView.addAlias(&quot;PE&quot;, &quot;lastName&quot;);
+- dynamicView.addViewLink(&quot;PT&quot;, &quot;PE&quot;, Boolean.FALSE,
+ModelKeyMap.makeKeyMapList(&quot;partyId&quot;));
+- }
+-
+ // filter on firstName
+ if (firstName != null &amp;&amp; firstName.length() &gt; 0) {
+ paramList = paramList + &quot;&amp;firstName=&quot; + firstName;
+@@ -319,6 +313,12 @@
+ List orderBy = new ArrayList();
+ fieldsToSelect.add(&quot;partyId&quot;);
+ fieldsToSelect.add(&quot;partyTypeId&quot;);
++ fieldsToSelect.add(&quot;firstName&quot;);
++ fieldsToSelect.add(&quot;lastName&quot;);
++ fieldsToSelect.add(&quot;groupName&quot;);
++ orderBy.add(&quot;-partyTypeId&quot;);
++ orderBy.add(&quot;lastName&quot;);
++ orderBy.add(&quot;groupName&quot;);
+
+ // UserLogin
+ if (userLoginId != null &amp;&amp; userLoginId.length() &gt; 0) {
+@@ -327,14 +327,11 @@
+
+ // Person
+ if ((firstName != null &amp;&amp; firstName.length() &gt; 0) || (lastName != null
+&amp;&amp; lastName.length() &gt; 0)) {
+- fieldsToSelect.add(&quot;firstName&quot;);
+- fieldsToSelect.add(&quot;lastName&quot;);
+ orderBy.add(&quot;lastName&quot;);
+ orderBy.add(&quot;firstName&quot;);
+ }
+ // PartyGroup
+ if (groupName != null &amp;&amp; groupName.length() &gt; 0) {
+- fieldsToSelect.add(&quot;groupName&quot;);
+ orderBy.add(&quot;groupName&quot;);
+ }
+ // RoleType
+Index: components/party/webapp/partymgr/party/findparty.ftl
+===================================================================
+RCS file: /cvs/ofbiz/components/party/webapp/partymgr/party/findparty.ftl,v
+retrieving revision 1.10
+diff -u -r1.10 findparty.ftl
+--- components/party/webapp/partymgr/party/findparty.ftl 1 Mar 2004 14:43:27
+-0000 1.10
++++ components/party/webapp/partymgr/party/findparty.ftl 8 Mar 2004 22:06:24 -0000
+@@ -212,13 +212,12 @@
+ &lt;/table&gt;
+ &lt;input type=&quot;image&quot; src=&quot;/images/spacer.gif&quot;
+onClick=&quot;javascript:document.lookupparty.submit();&quot;&gt;
+ &lt;/form&gt;
+-&lt;#if requestParameters.hideFields?default(&quot;N&quot;) != &quot;Y&quot;&gt;
+ &lt;script language=&quot;JavaScript&quot;&gt;
+ &lt;!--//
+ document.lookupparty.partyId.focus();
+ //--&gt;
+ &lt;/script&gt;
+-&lt;/#if&gt;
++
+ &lt;#if partyList?exists&gt;
+ &lt;br&gt;
+ &lt;table border=0 width=&apos;100%&apos; cellspacing=&apos;0&apos; cellpadding=&apos;0&apos;
+class=&apos;boxoutside&apos;&gt;
+@@ -292,21 +291,13 @@
+ &lt;/td&gt;
+ &lt;td&gt;
+ &lt;div class=&quot;tabletext&quot;&gt;
+- &lt;#if partyRow.containsKey(&quot;lastName&quot;)&gt;
+ &lt;#if partyRow.lastName?has_content&gt;
+ ${partyRow.lastName}&lt;#if
+partyRow.firstName?has_content&gt;, ${partyRow.firstName}&lt;/#if&gt;
+- &lt;#else&gt;
+- (No Name Found)
+- &lt;/#if&gt;
+- &lt;#elseif partyRow.containsKey(&quot;groupName&quot;)&gt;
+- &lt;#if partyRow.groupName?has_content&gt;
++ &lt;#elseif partyRow.groupName?has_content&gt;
+ ${partyRow.groupName}
+ &lt;#else&gt;
+- (No Name Found)
++ (Not Selected)
+ &lt;/#if&gt;
+- &lt;#else&gt;
+- (Not Selected)
+- &lt;/#if&gt;
+ &lt;/td&gt;
+ &lt;#if extInfo?default(&quot;&quot;) == &quot;P&quot;&gt;
+ &lt;td&gt;&lt;div
+class=&quot;tabletext&quot;&gt;${partyRow.postalCode?if_exists}&lt;/div&gt;&lt;/td&gt;</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:36:38</issue_when>
+ <thetext>This page has been changed from using the left joins in the dynamic view. Using left joins performs too
+poorly for actual use. This patch has been rejected; a new task will be opened for fixing the problem of
+the name not being displayed.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:36:38</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:36:38</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>WONTFIX</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>57</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040308174641</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>ajzeneski</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-08 17:36:18</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Find Party - Show all does not show name</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:36:18</issue_when>
+ <thetext>The name is not displayed when selecting show all in the party manager.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-03-08 17:46:41</issue_when>
+ <thetext>reassigned</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-08 17:46:41</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>58</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040308185722</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>vivalinux</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-08 18:57:22</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Inherited hidden fields overide child fields when they have use-when</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>vivalinux</who>
+ <issue_when>2004-03-08 18:57:22</issue_when>
+ <thetext>In working with inherited forms I noticed that hidden fields are hard
+to override if the overriding field has a use-when attribute:
+
+ Since the HtmlFormRenderer gives precedence to hidden fields, and new
+fields with &quot;use-when&quot; are only placed in front of existing fields
+but not merged with, the resulting behavior is that hidden fields of
+parent forms always get rendered even when the use-when condition is
+true. This causes the hidden field to take precedence over the user
+input when the form is submitted.
+
+ Here is a posible solution:
+
+Index: components/content/src/org/ofbiz/content/widget/form/ModelForm.java
+===================================================================
+RCS file:
+/usr/local/cvsrepos/newriverbiz/components/content/src/org/ofbiz/content/widget/form/ModelForm.java,v
+retrieving revision 1.1.1.4
+diff -u -r1.1.1.4 ModelForm.java
+--- components/content/src/org/ofbiz/content/widget/form/ModelForm.java
+2004/01/08 14:19:55 1.1.1.4
++++ components/content/src/org/ofbiz/content/widget/form/ModelForm.java
+2004/01/29 20:23:20
+@@ -277,15 +277,25 @@
+ public ModelFormField addUpdateField(ModelFormField modelFormField) {
+ if (!modelFormField.isUseWhenEmpty()) {
+ // is a conditional field, add to the List but don&apos;t worry about
+the Map
+- //for adding to list, see if there is another field with that name
+in the list and if so, put it before that one
++ //for adding to list, see if there is another field with that name
++ //in the list and if so, put it before that one
+ boolean inserted = false;
+ for (int i = 0; i &lt; this.fieldList.size(); i++) {
+ ModelFormField curField = (ModelFormField) this.fieldList.get(i);
++ // BUG FIX: parent hidden fields are left and rendered even when they
++ // are overriden with fields that have a use-when
+ if (curField.getName() != null &amp;&amp;
+curField.getName().equals(modelFormField.getName())) {
+- this.fieldList.add(i, modelFormField);
+- inserted = true;
+- break;
++ if ((curField.getFieldInfo().getFieldType() !=
+ModelFormField.FieldInfo.HIDDEN) &amp;&amp;
++ (curField.getFieldInfo().getFieldType() != ModelFormField.FieldInfo.IGNORED)) {
++ this.fieldList.add(i, modelFormField);
++ inserted = true;
++ break;
++ } else {
++ curField.mergeOverrideModelFormField(modelFormField);
++ break;
++ }
+ }
++ //End of BUG fix
+ }
+ if (!inserted) {
+ this.fieldList.add(modelFormField);</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>59</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P1</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040318120354</delta_ts>
+ <subcomponent>Workflow</subcomponent>
+ <reporter>mbennani</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-09 06:47:17</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Serious Problem in Sub-flow Implementation: in-Memory Requester Used!!</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>mbennani</who>
+ <issue_when>2004-03-09 06:47:17</issue_when>
+ <thetext>I am running ofbiz almost v.3 :-) (image from late 2003 from cvs).
+(subflow implementation did not change since 1.2 anyways, I believe)
+
+Quick Description:
+------------------
+Subflows are using the in-memory requester object which
+does not persist over server re-starts. This is serious one guys.
+
+More Description (Scenario):
+---------------------------
+I have a main process with 3 steps(step1,step2-subflow,step3). A subflow
+process being the second step. This second step calls another process that I
+call sub. sub has 2 simple steps (substep1,substep2).
+
+Now, in normal time (i.e no server restart), it all works fine and my main
+process runs fine, calls the subflow, subflow runs, the call returns and my
+step3 of the main process runs successfully and main completes fine.
+
+The problem arises when say I run step 1 of main process, subflow gets
+initiated, substep1 is executed, and before substep2 is executed, I stop the
+server (faking a server problem..., connection lost...whatever).
+When I restart the server, the subflow resumes at substep2 which executes fine,
+and eventually completes the subflow process. But, step-subflow activity in the
+workeffort table still shows WF_RUNNING. Therefore, my main process is hanging
+there and will never move along. The waitForResult() is not executed anymore
+after server restart.
+This is happening in WfActivitySubFlowImplementation on runService.
+
+Even More Details, code wise, if you want to look at it:
+-------------------------------------------------------
+In a normal scenario (no server stopped), the runService of
+WfActivitySubFlowImplementation eventually calls the runSync in WorkflowEngine
+and then waitForResult. I put a system out message in the waiter class and I
+can see my message being displayed in the console forever (&quot;waiting...&quot;), until
+the subflow process finishes. And that&apos;s ok, that&apos;s what we want. When the
+subflow process finishes, the call is returned back to the runService in
+WfActivitySubFlowImplementation() and the setComplete is called, which changes
+the status of my step2-subflow activity in the main process to WF_COMPLETED to
+advance the main process. It also calls setResults() to propagate the results
+from the subflow up to the main process.
+
+BUT, now if I stop my server during that wait, and restart it the call never
+reachs the setComplete nor the setResults, making therefore my main process
+unusable anymore.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:03:54</when>
+ <field_name>op_sys</field_name>
+ <field_desc>OS/Version</field_desc>
+ <oldvalue>Windows 2000</oldvalue>
+ <newvalue>All</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:03:54</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>60</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040707152609</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 07:09:04</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Replace simplistic caching with complex reverse dependency resolver</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:09:04</issue_when>
+ <thetext>This patch removes all simple caching from GenericDelegator, and replaces
+it with a stand-alone class, that handles all entity cache management. It
+uses new features in the entity model, that keeps track of view entities
+that are referenced by regular entities. As entities are updated, all
+cached lists of views or condition caches are cleared.
+
+With this patch applied, view entities can be cached, without worry that
+they will become stale. Which then allows very complex views to be
+created, that emulate complex code patterns, while still allowing for
+caching. This can speed up code tremendously.
+
+Additionally, since cache clearing now works, it almost always makes more
+sense to use the cache variation of the methods.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:14:24</issue_when>
+ <thetext>Created an attachment (id=16)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 15:44:46</issue_when>
+ <thetext>This patch is on hold, until some other patches are committed. Some of them
+need to have bugs filed, which I am working on. Plus, this patch needs to be
+updated to the latest code base(not a big task, done locally).</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-07 15:26:09</issue_when>
+ <thetext>This is now in cvs.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>16</attachid>
+ <date>2004-03-09 07:14:24</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>EntityCache.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/16/EntityCache.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:14:24</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=16)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 15:44:46</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>STARTED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:01</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-07-07 15:26:09</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>STARTED</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-07-07 15:26:09</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>61</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422222023</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 07:18:26</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allow for easier extension of this class.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:18:26</issue_when>
+ <thetext>This patch makes a protected writeValues() method, which allows sub-classes
+to redirect the output to other places, as needed.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:18:49</issue_when>
+ <thetext>Created an attachment (id=17)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-22 22:20:23</issue_when>
+ <thetext>Applied the patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>17</attachid>
+ <date>2004-03-09 07:18:49</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>EntitySaxReader-extension.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/17/EntitySaxReader-extension.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:18:49</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=17)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:20:23</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:20:23</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>62</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422223418</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 07:34:07</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allow for UPPER() and LOWER() sorting</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:34:07</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:34:26</issue_when>
+ <thetext>Created an attachment (id=18)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-22 22:34:18</issue_when>
+ <thetext>Applied patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>18</attachid>
+ <date>2004-03-09 07:34:26</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>EntityUtil-UPPER_LOWER.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/18/EntityUtil-UPPER_LOWER.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:34:26</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=18)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:34:18</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:34:18</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>63</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429160209</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 07:36:19</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add support for turning off schemas completely</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:36:19</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:36:42</issue_when>
+ <thetext>Created an attachment (id=19)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:02:09</issue_when>
+ <thetext>Applied this patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>19</attachid>
+ <date>2004-03-09 07:36:42</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>use-schemas.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/19/use-schemas.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:36:42</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=19)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:02:09</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:02:09</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>64</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422222810</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 07:43:55</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Make the code look like the rest of the file.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:43:55</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:44:21</issue_when>
+ <thetext>Created an attachment (id=20)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-22 22:28:10</issue_when>
+ <thetext>Applied patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>20</attachid>
+ <date>2004-03-09 07:44:21</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>SqlJdbcUtil-Cast.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/20/SqlJdbcUtil-Cast.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:44:21</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=20)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:28:10</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:28:10</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>65</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429163146</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 07:46:14</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add java.util.Date support.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:46:14</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:46:39</issue_when>
+ <thetext>Created an attachment (id=21)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:31:46</issue_when>
+ <thetext>Applied the patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>21</attachid>
+ <date>2004-03-09 07:46:39</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>SqlJdbcUtil-Java_Util_Date.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/21/SqlJdbcUtil-Java_Util_Date.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:46:39</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=21)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:03</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:31:45</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:31:45</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>66</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422221943</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 07:47:46</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Use UtilValidate.isNotEmpty.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:47:46</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:48:06</issue_when>
+ <thetext>Created an attachment (id=22)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-22 22:19:43</issue_when>
+ <thetext>Applied patch, which uses UtilValidate.isNotEmpty in SqlJdbcUtil.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>22</attachid>
+ <date>2004-03-09 07:48:06</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>SqlJdbcUtil-UtilValidate.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/22/SqlJdbcUtil-UtilValidate.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:48:06</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=22)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:19:43</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:19:43</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>67</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429161604</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 07:49:20</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add support for &lt;constraint&gt; on relations and views.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:49:20</issue_when>
+ <thetext>When defining a view or relation, you can now constrain the returned
+values to those that match an EntityCondition. This is really helpful
+for complex views.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:49:42</issue_when>
+ <thetext>Created an attachment (id=23)
+The patch
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>23</attachid>
+ <date>2004-03-09 07:49:42</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>model-constraint.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/23/model-constraint.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:49:42</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=23)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:04</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>68</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040423074926</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 07:51:20</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Use a NumberFormat for casting number objects to strings.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:51:20</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:51:40</issue_when>
+ <thetext>Created an attachment (id=24)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-23 07:49:26</issue_when>
+ <thetext>Applied patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>24</attachid>
+ <date>2004-03-09 07:51:40</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>GenericEntity-NumberFormat.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/24/GenericEntity-NumberFormat.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:51:40</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=24)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-23 07:49:26</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-23 07:49:26</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>69</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040423075223</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 07:52:20</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add addToXmlElement method</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:52:20</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:52:42</issue_when>
+ <thetext>Created an attachment (id=25)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-23 07:52:23</issue_when>
+ <thetext>Applied patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>25</attachid>
+ <date>2004-03-09 07:52:42</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>GenericEntity-addToXmlElement.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/25/GenericEntity-addToXmlElement.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:52:42</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=25)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-23 07:52:23</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-23 07:52:23</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>70</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422221512</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-09 07:54:40</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Fix modelEntity dereferencing</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:54:40</issue_when>
+ <thetext>When GenericEntities have been deserialized, modelEntity will be null.
+getModelEntity() should be called, to repopulate it.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:54:57</issue_when>
+ <thetext>Created an attachment (id=26)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-22 22:15:12</issue_when>
+ <thetext>Applied patch, plus fixed it in a few other areas.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>26</attachid>
+ <date>2004-03-09 07:54:57</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>GenericEntity-getModelEntity.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/26/GenericEntity-getModelEntity.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:54:57</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=26)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:15:12</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:15:12</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>71</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501214447</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 07:56:46</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Factor out the code</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:56:46</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 07:57:40</issue_when>
+ <thetext>Created an attachment (id=27)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:51</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 06:06:43</issue_when>
+ <thetext>Not adding this patch, breaks the Server Stats Since Start page in WebTools, and not totally sure the
+cleanup is worth it yet.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-05-01 21:44:47</issue_when>
+ <thetext>I&apos;ll fix that page then, and submit a revised patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>27</attachid>
+ <date>2004-03-09 07:57:40</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>ServerHitBin-factor.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/27/ServerHitBin-factor.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 07:57:40</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=27)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:02</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:51</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:07:05</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P2</oldvalue>
+ <newvalue>P4</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>72</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422223139</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 08:04:05</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add a matches(condition) helper method.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:04:05</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:04:31</issue_when>
+ <thetext>Created an attachment (id=28)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-22 22:31:39</issue_when>
+ <thetext>Applied patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>28</attachid>
+ <date>2004-03-09 08:04:31</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>GenericEntity-matches-condition.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/28/GenericEntity-matches-condition.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:04:31</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=28)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:31:39</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:31:39</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>73</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422221835</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-09 08:05:30</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Fix deserializing of these objects.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:05:30</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:05:51</issue_when>
+ <thetext>Created an attachment (id=29)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-22 22:18:35</issue_when>
+ <thetext>Applied patch, which set delegatorName to &quot;default&quot; when it is null.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>29</attachid>
+ <date>2004-03-09 08:05:50</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>GenericDelegator-fix-serialization.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/29/GenericDelegator-fix-serialization.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:05:51</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=29)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:18:35</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:18:35</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>74</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P2</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429200642</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 08:08:00</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Fix all fetchers of shopping cart to use a static method</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:08:00</issue_when>
+ <thetext>This patch fixes all code that fetches the ShoppingCart from the session,
+to use a static method instead. It also adds the beginning of named
+ShoppingCart support.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:08:22</issue_when>
+ <thetext>Created an attachment (id=30)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:51</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 20:05:04</issue_when>
+ <thetext>This patch depends on EntityCache, which isn&apos;t ready for inclussion yet,
+reducing priority.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 20:06:42</issue_when>
+ <thetext>Oops, modified wrong issue.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>30</attachid>
+ <date>2004-03-09 08:08:22</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>ShoppingCart-getShoppingCart.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/30/ShoppingCart-getShoppingCart.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:08:22</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=30)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:03</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:51</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 20:05:04</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P2</oldvalue>
+ <newvalue>P3</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 20:06:41</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>75</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429171641</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 08:09:21</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allow code to detect whether a cacheline has expired.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:09:21</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:09:41</issue_when>
+ <thetext>Created an attachment (id=31)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:57:47</issue_when>
+ <thetext>Changing priority to P2, as I need these applied before I can commit my entity
+cache work.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-29 17:16:41</issue_when>
+ <thetext>Now in CVS, thanks Adam.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>31</attachid>
+ <date>2004-03-09 08:09:41</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-hasExpired.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/31/UtilCache-hasExpired.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:09:41</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=31)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:04</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:57:47</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:16:41</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:16:41</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>76</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429171643</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 08:10:31</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allow code to get the values stored in the cache.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:10:31</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:10:58</issue_when>
+ <thetext>Created an attachment (id=32)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:57:47</issue_when>
+ <thetext>Changing priority to P2, as I need these applied before I can commit my entity
+cache work.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-29 17:16:43</issue_when>
+ <thetext>Now in CVS, thanks Adam.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>32</attachid>
+ <date>2004-03-09 08:10:58</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-getCacheLineValues.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/32/UtilCache-getCacheLineValues.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:10:58</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=32)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:05</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:57:47</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:16:43</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:16:43</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>77</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429171648</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 08:11:52</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allow registering of listeners for the various events on caches.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:11:52</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:12:13</issue_when>
+ <thetext>Created an attachment (id=33)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:57:48</issue_when>
+ <thetext>Changing priority to P2, as I need these applied before I can commit my entity
+cache work.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-29 17:16:48</issue_when>
+ <thetext>Now in CVS, thanks Adam.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>33</attachid>
+ <date>2004-03-09 08:12:12</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-listeners.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/33/UtilCache-listeners.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:12:13</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=33)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:05</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:57:48</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:16:48</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:16:48</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>78</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040423080723</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-09 08:19:59</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Some additional methods to manipulate the embedded cache.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:19:59</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:20:20</issue_when>
+ <thetext>Created an attachment (id=34)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-23 08:07:23</issue_when>
+ <thetext>Add some more methods to GenericValue to manipulate the cache.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>34</attachid>
+ <date>2004-03-09 08:20:20</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>GenericValue-EmbeddedCache.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/34/GenericValue-EmbeddedCache.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:20:20</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=34)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-23 08:07:23</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-23 08:07:23</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>79</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429200727</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 08:21:27</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>methods to fetch the list of available catalogs.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:21:27</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:21:46</issue_when>
+ <thetext>Created an attachment (id=35)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:52</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 20:07:27</issue_when>
+ <thetext>This patch depends on EntityCache, which isn&apos;t ready for inclussion yet,
+reducing priority.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>35</attachid>
+ <date>2004-03-09 08:21:46</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>CatalogWorker-getCatalogsAvailable.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/35/CatalogWorker-getCatalogsAvailable.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:21:46</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=35)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:05</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:52</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 20:07:27</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P2</oldvalue>
+ <newvalue>P3</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>80</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422222535</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-09 08:22:53</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Create a separate makeIndexName helper method.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:22:53</issue_when>
+ <thetext>This patch also truncates the index name, based on the clipping value.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:23:14</issue_when>
+ <thetext>Created an attachment (id=36)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-22 22:25:35</issue_when>
+ <thetext>Applied patch.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>36</attachid>
+ <date>2004-03-09 08:23:14</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>DatabaseUtil-makeIndexName.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/36/DatabaseUtil-makeIndexName.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:23:14</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=36)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:25:35</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-22 22:25:35</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>81</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429172408</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 08:23:52</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add support for writing elements.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:23:52</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:24:13</issue_when>
+ <thetext>Created an attachment (id=37)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:57:47</issue_when>
+ <thetext>Changing priority to P2, as I need these applied before I can commit my entity
+cache work.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-29 17:24:08</issue_when>
+ <thetext>Now in CVS, thanks Adam.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>37</attachid>
+ <date>2004-03-09 08:24:12</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilXml-writeElement.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/37/UtilXml-writeElement.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:24:13</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=37)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:03</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:57:47</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:24:08</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:24:08</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>82</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P2</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429195752</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 08:25:06</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Services/etc for manipulating PartyAttributes.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:25:06</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-09 08:25:27</issue_when>
+ <thetext>Created an attachment (id=38)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:52</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>38</attachid>
+ <date>2004-03-09 08:25:27</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>PartyAttributes.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/38/PartyAttributes.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-09 08:25:27</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=38)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:16:04</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>FEATURE</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:52</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>83</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P5</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040309095844</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-09 09:58:44</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Problems displaying the Euro symbol in order confirmation by email with MS Outlook</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-09 09:58:44</issue_when>
+ <thetext>Problems displaying the Euro symbol in order confirmation by email with MS
+Outlook: instead of the beautyful Euro symbol you see a &apos;?&apos;.
+Ok, maybe I should create an issue to the Microsoft issue tracker... however
+I&apos;ve reported this for two reasons:
+1) other users reported this some time ago in the mailing lists
+2) maybe, in general, for the currency symbols in ftl templates we should use
+the html entity references (i.e. &amp;euro; for the Euro symbol): this will solve
+all the display problems with virtually any client software.</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>84</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040309101202</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-09 10:12:02</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In shipment, it&apos;s possible to issue to shipment items with negative reservations (atp &lt; 0); as a consequence of this, also the inventory items&apos; quantity on hand can be negative.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-09 10:12:02</issue_when>
+ <thetext>In shipment, it&apos;s possible to issue to shipment items with negative
+reservations (atp &lt; 0); as a consequence of this, also the inventory items&apos;
+quantity on hand can be negative.
+In fact, when the issuance is performed, the (negative) reservation is deleted
+and a new issuance is created and the inventory&apos;s qoh is decremented.
+In my opinion, in this situation a warning message should be provided.
+Also, David Jones has suggested to create a report of all negative QOH
+InventoryItems and add a link for each one to get a physical inventory variance
+started.</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>85</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040730212116</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-03-11 08:14:35</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>The showvisits page in the Party Manager is very slow when there are many visits</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-11 08:14:35</issue_when>
+ <thetext>The showvisits page is not coded to efficiently display a subset of the visits when there are many active.
+When there are tens of thousands the current code doesn&apos;t work very efficiently.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 21:21:16</issue_when>
+ <thetext>The code for visit display utilized the entity list iterator and pulls a subset for each page. The query
+fields are indexed (as of now) so depending on the JDBC driver being used this should be quite efficient.
+If you experience further problems, send more information to the DEV list.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:04:04</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 21:21:16</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 21:21:16</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>86</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040312164734</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-12 16:45:26</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Error Message (esp Map) Improvements from Quake Wang</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-12 16:45:26</issue_when>
+ <thetext>A description from an email from Quake:
+
+================================
+David,
+ The attachment is my patch. The main changes are:
+
+1. Now the event and the service invoke will use the same message fields in SimpleMethod.
+2. Add the errorMessageMap and successMessageMap for the message handling
+
+ I only change the envsetup.bsh and errormsg.ftl in the ecommerce component, please use the
+ecommerce to review the backward compatiblity.
+
+Regards
+Quake
+=============================</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-12 16:47:34</issue_when>
+ <thetext>Created an attachment (id=39)
+Error Message patch from Quake Wang
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>39</attachid>
+ <date>2004-03-12 16:47:34</date>
+ <desc>Error Message patch from Quake Wang</desc>
+ <ispatch>1</ispatch>
+ <filename>quake.errormessages.patch</filename>
+ <submitter_id>3</submitter_id>
+ <submitting_username>jonesde</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/39/quake.errormessages.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-12 16:47:34</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=39)
+Error Message patch from Quake Wang
+</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>87</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040312165745</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-12 16:56:52</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>bsf.jar in base is old</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-12 16:56:52</issue_when>
+ <thetext>bsf is no longer part of IBM. Instead, it has moved to jakarta.
+
+Attached patch changes imports, etc. It can&apos;t change the bsf.jar in
+base/lib/scripting/, however.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-03-12 16:57:45</issue_when>
+ <thetext>Created an attachment (id=40)
+The patch.
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>40</attachid>
+ <date>2004-03-12 16:57:45</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>IBM-Jakarta-BSF.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/40/IBM-Jakarta-BSF.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-03-12 16:57:45</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=40)
+The patch.
+</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>88</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040312165737</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-12 16:57:37</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Promotion/other adjustments not as expected when cancelling order items</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-12 16:57:37</issue_when>
+ <thetext>When order items are cancelled some order level adjustments are not changed, like order level
+promotion adjustments. Because of this order totals can easily become negative. The question is what
+to do about it?
+
+Andy wrote this at one point in an email:
+
+============================
+I was aware of this; the only order level adjustments which are currently re-calculated are shipping and
+tax, order level promotions are not done at this time so we will need to add this in. In order to do this
+promotion evaluation will need to be able to be done without a shopping cart.
+============================
+
+I&apos;m not sure I totally agree with this, I think short of that it would still be good to at least pro-rate these
+order adjustments like we do with tax and shipping.
+
+Or would it be better on any order change to just cancel the order and create a new one with full re-
+calculations, including promotions, tax, shipping, etc, etc? If that is done should we apply promotions,
+etc as if it was the original order date, or using today&apos;s date? (could be a big difference based on
+promotions that come and go over time)</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>89</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040312171315</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-12 17:10:18</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In product detail show variant prices (from Brian Johnson)</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-12 17:10:18</issue_when>
+ <thetext>Here is a description from an email from Brian Johnson:
+
+===================================
+I&apos;ve changed productdetail to better handle variants with different prices. When you first bring-up the
+page it says &quot;make selections&quot;, then when you select the features from the drop-downs it&apos;ll show the
+price for the product with those features.
+--
+Brian Johnson
+===================================</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-12 17:13:15</issue_when>
+ <thetext>Created an attachment (id=41)
+Show prices for variants along with drop down, patch from Brian Johnson
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>41</attachid>
+ <date>2004-03-12 17:13:15</date>
+ <desc>Show prices for variants along with drop down, patch from Brian Johnson</desc>
+ <ispatch>1</ispatch>
+ <filename>productdetail-variantprices.patch</filename>
+ <submitter_id>3</submitter_id>
+ <submitting_username>jonesde</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/41/productdetail-variantprices.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-12 17:11:11</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>DEFECT</oldvalue>
+ <newvalue>PATCH</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-12 17:13:15</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=41)
+Show prices for variants along with drop down, patch from Brian Johnson
+</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>90</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040312172159</delta_ts>
+ <subcomponent>E-Commerce</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-12 17:21:59</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Web/order/etc marketing reports like Core Metrics</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-12 17:21:59</issue_when>
+ <thetext>The feature here is to create a standard set of reports commonly used to keep tabs on a web content
+and/or ecommerce site. These reports would be similar to what a company like Core Metrics offers.
+
+Would include reports for a given time period such as for things like hits, visits/sessions, orders,
+tracking codes, percent of customers that order, orders per customer, pages/products/etc viewed,
+searches done, referring sites &amp; search strings, etc, etc, etc.</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>91</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040315011245</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-15 01:12:45</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Shipment: potential problem when issuing negative reservations (quantityNotAvailable could be lost)</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-15 01:12:45</issue_when>
+ <thetext>This is a potential problem when you issue negative reservation to a shipment.
+Here is the scenario:
+
+1) inventory item 9000 for product WG-1111 with atp/qoh = 10/10
+2) two orders are received for WG-1111: the first for 8 items, the second
+for 5 items
+3) two reservations are made: the first for 8 items and the second for 5
+items but with quantityNotAvailable = 3
+4) the inventory item 9000 is now: atp/qoh = -3/10
+5) only the second order is issued to shipment: the reservation record is
+deleted and the qoh is decremented
+6) the inventory item 9000 is now: atp/qoh = -3/5 but the reservation for
+the first order (for quantity = 8) has quantityNotAvailable = 0 (ERROR: it
+should be 3)
+
+A possible patch: if we issue a negative reservation we should try to assign
+the quantityNotAvailable to another reservation in the same inventory item.
+
+In order to do this, David Jones has proposed to do a re-reservation whenever a
+reservation is issued to a shipment, considering that (like in this scenario)
+shipments may occur in a different order than reservations.
+
+In fact, when new inventory is received a re-reservation is already done for
+all OIIR that are associated to inventory items (of the same product) that have
+negative ATP.
+In a similar way, when we issue a OIIR, we could do a re-reservation for all
+the OIIR associated to the inventory item.</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>92</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20040416115022</delta_ts>
+ <subcomponent>E-Commerce</subcomponent>
+ <reporter>holivier</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-16 06:35:41</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Simple Update for localization</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 06:35:41</issue_when>
+ <thetext>In the ecommerce component
+2 UiLabels change (the # is now in the label)
+and some french UiLabel correction</thetext>
+ </long_desc>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 06:38:00</issue_when>
+ <thetext>Created an attachment (id=42)
+the Ecommerce UiLabel patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-16 11:50:22</issue_when>
+ <thetext>Patch is in CVS. Thanks for doing these Olivier.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>42</attachid>
+ <date>2004-03-16 06:38:00</date>
+ <desc>the Ecommerce UiLabel patch</desc>
+ <ispatch>1</ispatch>
+ <filename>EcommerceUiLabel.update.patch</filename>
+ <submitter_id>165</submitter_id>
+ <submitting_username>holivier</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/42/EcommerceUiLabel.update.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>holivier</user>
+ <when>2004-03-16 06:38:00</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=42)
+the Ecommerce UiLabel patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:04:15</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-16 11:50:22</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-16 11:50:22</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>93</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20040416115029</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>holivier</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-16 08:00:55</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Simple Update for localization</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 08:00:55</issue_when>
+ <thetext>Order component, some french UiLabels correction</thetext>
+ </long_desc>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 08:02:07</issue_when>
+ <thetext>Created an attachment (id=43)
+The OrderUilabel properties patch file
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-16 11:50:29</issue_when>
+ <thetext>Patch is in CVS. Thanks for doing these Olivier.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>43</attachid>
+ <date>2004-03-16 08:02:07</date>
+ <desc>The OrderUilabel properties patch file</desc>
+ <ispatch>1</ispatch>
+ <filename>OrderUilabelCorrection.patch</filename>
+ <submitter_id>165</submitter_id>
+ <submitting_username>holivier</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/43/OrderUilabelCorrection.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>holivier</user>
+ <when>2004-03-16 08:02:07</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=43)
+The OrderUilabel properties patch file
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:04:26</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-16 11:50:29</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-16 11:50:29</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>94</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20040416115037</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>holivier</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-16 09:28:27</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Simple Update for localization</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 09:28:27</issue_when>
+ <thetext>simple french correction &amp; translation in the Party and workeffort components</thetext>
+ </long_desc>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 09:29:26</issue_when>
+ <thetext>Created an attachment (id=44)
+the PartyUiLabel correction patch file
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 09:30:03</issue_when>
+ <thetext>Created an attachment (id=45)
+the workeffortUilabel correction patch file
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-16 11:50:37</issue_when>
+ <thetext>Patch is in CVS. Thanks for doing these Olivier.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>44</attachid>
+ <date>2004-03-16 09:29:26</date>
+ <desc>the PartyUiLabel correction patch file</desc>
+ <ispatch>1</ispatch>
+ <filename>PartyUiLabelCorrection040316.patch</filename>
+ <submitter_id>165</submitter_id>
+ <submitting_username>holivier</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/44/PartyUiLabelCorrection040316.patch</attachment_iz_url>
+ </attachment>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>45</attachid>
+ <date>2004-03-16 09:30:03</date>
+ <desc>the workeffortUilabel correction patch file</desc>
+ <ispatch>1</ispatch>
+ <filename>WorkeffortUiLabelCorrection040316.patch</filename>
+ <submitter_id>165</submitter_id>
+ <submitting_username>holivier</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/45/WorkeffortUiLabelCorrection040316.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>holivier</user>
+ <when>2004-03-16 09:29:26</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=44)
+the PartyUiLabel correction patch file
+</newvalue>
+ </activity>
+ <activity>
+ <user>holivier</user>
+ <when>2004-03-16 09:30:03</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=45)
+the workeffortUilabel correction patch file
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:04:35</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-16 11:50:37</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-16 11:50:37</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>95</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20040416115047</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>holivier</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-16 09:32:59</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Simple Update for localization</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 09:32:59</issue_when>
+ <thetext>Product components internationalization and the UiLabel files associated</thetext>
+ </long_desc>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 09:36:08</issue_when>
+ <thetext>Created an attachment (id=46)
+the patch file for produt bsh, xml, ftl and properties
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-16 11:50:47</issue_when>
+ <thetext>Patch is in CVS. Thanks for doing these Olivier.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>46</attachid>
+ <date>2004-03-16 09:36:08</date>
+ <desc>the patch file for produt bsh, xml, ftl and properties</desc>
+ <ispatch>1</ispatch>
+ <filename>ProductUiLabelMigration040316.patch</filename>
+ <submitter_id>165</submitter_id>
+ <submitting_username>holivier</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/46/ProductUiLabelMigration040316.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>holivier</user>
+ <when>2004-03-16 09:36:08</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=46)
+the patch file for produt bsh, xml, ftl and properties
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:04:46</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-16 11:50:47</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-16 11:50:47</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>96</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jonesde</assigned_to>
+ <delta_ts>20040730204543</delta_ts>
+ <subcomponent>Security Extensions</subcomponent>
+ <reporter>holivier</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-16 13:30:49</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Typo error correction in emailPassword (bcc in place of cc)</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 13:30:49</issue_when>
+ <thetext>just a simple typo correction.In the emailPassword method when reading and
+populated the service fields they was an error.
+ serviceContext.put(&quot;sendBcc&quot;, productStoreEmail.get(&quot;ccAddress&quot;));
+I have change cc to bcc</thetext>
+ </long_desc>
+ <long_desc>
+ <who>holivier</who>
+ <issue_when>2004-03-16 13:31:26</issue_when>
+ <thetext>Created an attachment (id=47)
+the patch file
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 20:45:43</issue_when>
+ <thetext>this has already been fixed in cvs.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>47</attachid>
+ <date>2004-03-16 13:31:26</date>
+ <desc>the patch file</desc>
+ <ispatch>1</ispatch>
+ <filename>EmailPassword040316.patch</filename>
+ <submitter_id>165</submitter_id>
+ <submitting_username>holivier</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/47/EmailPassword040316.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>holivier</user>
+ <when>2004-03-16 13:31:26</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=47)
+the patch file
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-03-18 12:04:54</when>
+ <field_name>version</field_name>
+ <field_desc>Version</field_desc>
+ <oldvalue>Pre3.0.0</oldvalue>
+ <newvalue>CVS</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 20:45:43</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 20:45:43</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>97</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040527234819</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-18 06:59:56</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Error when creating an invoice with billing account payment type.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-18 06:59:56</issue_when>
+ <thetext>The following error is generated when you try to create an invoice for an order
+with payment type == billing account:
+
+----------------------------------------
+803495[ InvoiceServices.java:583:ERROR] Problems storing invoice items
+org.ofbiz.entity.GenericEntityException: Exception occurred in storeAll (while i
+nserting: [GenericEntity:InvoiceContactMech][createdTxStamp,2004-03-18 15:29:46.
+156(java.sql.Timestamp)][invoiceId,10010(java.lang.String)][contactMechPurposeTy
+peId,BILLING_LOCATION(java.lang.String)][lastUpdatedTxStamp,2004-03-18 15:29:46.
+156(java.sql.Timestamp)][contactMechId,null()][createdStamp,2004-03-18 15:29:46.
+977(java.sql.Timestamp)][lastUpdatedStamp,2004-03-18 15:29:46.977(java.sql.Times
+tamp)] (SQL Exception while executing the following:INSERT INTO INVOICE_CONTACT_
+MECH (INVOICE_ID, CONTACT_MECH_PURPOSE_TYPE_ID, CONTACT_MECH_ID, LAST_UPDATED_ST
+AMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, CREATED_TX_STAMP) VALUES (?, ?, ?, ?,
+ ?, ?, ?) ([-5005] (at 83): Missing non-NULL value)))
+----------------------------------------
+
+I think I&apos;ve tracked down the source of this problem:
+the field contactMechId for the billing account entry in BillingAccount entity
+is empty.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-05-26 10:12:53</issue_when>
+ <thetext>The error happens when no Billing Contact Mechanism is specified in the billing
+account.
+In order to fix this issue the input field &quot;Billing Contact Mech&quot; in the
+EditBillingAccount page should be marked as mandatory.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-27 23:48:19</issue_when>
+ <thetext>Fixed by changing the code to not try to create that record when the BillingAccount.contactMechId is
+empty.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-27 23:48:18</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-27 23:48:18</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>98</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040322173551</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-03-19 07:41:21</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>New Shipment Plan feature for the shipment process.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-19 07:41:21</issue_when>
+ <thetext>New Shipment Plan feature for the shipment process.
+
+In order to support some of the tasks in the manufacturing industry there was
+the need of some simple shipment planning functionality: we need to assign a
+group of customer order items to a scheduled shipment (but without actually
+picking and issuing the items from warehouse).
+
+The patches in the attached zip file add this functionality to the shipment ui.
+This is the list of changes:
+
+NEW FILES (new page, within the shipment ui, for ShipmentPlan management)
+
+product/webapp/facility/shipment/EditShipmentPlan.ftl
+product/webapp/facility/WEB-INF/actions/shipment/EditShipmentPlan.bsh
+product/webapp/facility/WEB-INF/pagedefs/shipment/EditShipmentPlan.xml
+
+PATCHES
+product/webapp/facility/shipment/ShipmentTabBar.ftl (added new link to
+EditShipmentPlan page)
+
+product/webapp/facility/shipment/ShipmentForms.xml (some new forms for the
+EditShipmentPlan page)
+
+product/webapp/facility/WEB-INF/controller.xml (controller definitions for the
+EditShipmentPlan page)
+
+product/webapp/facility/shipment/AddItemsFromOrder.ftl (added some info, also
+fixed issues #47 and #55)
+product/webapp/facility/WEB-INF/actions/shipment/AddItemsFromOrder.bsh (added
+code to get reservations also from shipment plan)
+
+product/servicedef/services_shipment.xml (definitions for two new services to
+add/delete entries in the Shipment Plan)
+product/scripts/org/ofbiz/shipment/shipment/ShipmentSerices.xml (implementation
+of the two new services)
+
+product/entitydef/entitygroup.xml (declaration of the new entity ShipmentPlan)
+product/entitydef/entitymodel_shipment.xml (definition of the new entity
+ShipmentPlan)
+
+product/config/ProductUiLabels.properties (some new labels)
+product/config/ProductUiLabels_fr.properties (some new labels in ENGLISH)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-19 07:42:41</issue_when>
+ <thetext>Created an attachment (id=48)
+The patches and new files
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-03-22 17:35:51</issue_when>
+ <thetext>This patch is now in place. Thanks for sending it in Jacopo. BTW, for future patches a consolidated
+patch file is nice.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>application/x-compressed</mimetype>
+ <attachid>48</attachid>
+ <date>2004-03-19 07:42:41</date>
+ <desc>The patches and new files</desc>
+ <ispatch></ispatch>
+ <filename>ShipmentPlan.zip</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/48/ShipmentPlan.zip</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-19 07:42:41</when>
+ <field_name>attachments.thedata</field_name>
+ <field_desc>Attachment Data</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=48)
+The patches and new files
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-22 17:35:51</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-03-22 17:35:51</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>99</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P4</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040806160304</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-03-28 01:13:10</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>New html report that shows inventory availability by product</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-28 01:13:10</issue_when>
+ <thetext>This is a new html report (patches are in the attached zip file) for the
+Facility Manager Application that shows info about stock levels and
+reservations from a product point of view (instead of inventory item).
+The action code is very simple (the bsh action calls a service to
+compute atp and qoh per product); the report is formatted by the form widget
+stuff.
+It is not internationalized.
+In my local setup I&apos;ve added a link to it from the Inventory Item page but fell
+free to modify it and put it where you want.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-03-28 01:14:46</issue_when>
+ <thetext>Created an attachment (id=49)
+Files and patches for the new report.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-08-06 16:03:04</issue_when>
+ <thetext>Okay, this is in CVS, and I added a link to it from the facility inventory items page. Thanks for sending
+this over.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>application/x-compressed</mimetype>
+ <attachid>49</attachid>
+ <date>2004-03-28 01:14:46</date>
+ <desc>Files and patches for the new report.</desc>
+ <ispatch></ispatch>
+ <filename>inventory_report.zip</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/49/inventory_report.zip</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-03-28 01:14:46</when>
+ <field_name>attachments.thedata</field_name>
+ <field_desc>Attachment Data</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=49)
+Files and patches for the new report.
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-06 16:03:04</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-06 16:03:04</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>100</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>REL 3.0.0</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040403232241</delta_ts>
+ <subcomponent>E-Commerce</subcomponent>
+ <reporter>dlouzado</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-03-28 13:50:10</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>crash on Shipping Information page</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>dlouzado</who>
+ <issue_when>2004-03-28 13:50:10</issue_when>
+ <thetext>I was logged as &quot;admin&quot; user and buying something on the test store on ofbiz
+instance running on a standalone machine (no connection with the internet).
+
+I was filling the shipping information and when I clicked &apos;continue&apos; the
+following exception was thrown:
+
+
+&quot;org.ofbiz.base.util.GeneralException: JPublish execution error (BeanShell
+script error: Sourced file:
+C:\jtools\ofbiz\components\ecommerce\webapp\ecommerce\WEB-INF\actions\order\optionsettings.bsh
+: Error in method invocation: Attempt to pass void argument (position 0) to
+method: getContactMechByType : at Line: 65 : in file:
+C:\jtools\ofbiz\components\ecommerce\webapp\ecommerce\WEB-INF\actions\order\optionsettings.bsh
+: ContactHelper .getContactMechByType ( party , &quot;EMAIL_ADDRESS&quot; , false )
+BSF info:
+C:\jtools\ofbiz\components\ecommerce\webapp\ecommerce\WEB-INF\actions\order\optionsettings.bsh
+at line: 0 column: 0)&quot;</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-03 23:22:41</issue_when>
+ <thetext>Fixed so that &quot;party&quot; would exist where it is referenced there.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-03 23:22:41</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-03 23:22:41</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>101</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P5</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040730152858</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>eckardjf</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-04-02 09:44:46</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>https Website Settings Ignored</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>eckardjf</who>
+ <issue_when>2004-04-02 09:44:46</issue_when>
+ <thetext>org.ofbiz.content.webapp.control.RequestHandler.doRequest() checks to see if a request should be
+secure, but is not - and will redirect to a secure url if necessary. However, when rewriting the url to
+make it secure, it does not check the website settings for the enableHttps, httpsHost and httpsPort. It
+uses the global defaults from url.properties instead, which means the website settings have no effect.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 15:28:58</issue_when>
+ <thetext>this has now been corrected in CVS.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:43:50</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 15:28:58</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 15:28:58</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>102</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P4</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>REL 3.0.0</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040403233556</delta_ts>
+ <subcomponent>Entity Extensions</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-03 23:35:56</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Easy way to disabling Entity Sync oriented information</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-03 23:35:56</issue_when>
+ <thetext>Need an easy way to disable the saving of Entity Sync oriented information such as the entity remove
+information. May not want to disable the auto tx and individual timestamps because they are used in
+other code that takes advantage of the fact that they are always present (though we still need to get rid
+of the last updated and created timestamps that were added to various entities).</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>103</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040405070601</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>bryce_nz</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-04-05 00:05:28</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Nasty little error in PaymentGatewayServices</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>bryce_nz</who>
+ <issue_when>2004-04-05 00:05:28</issue_when>
+ <thetext>In PaymentGatewayServices.refundPayment there is a section of code for creating
+the payment for the refund, then at line 1255 there is the following:
+
+ Map result = ServiceUtil.returnSuccess();
+ result.put(&quot;paymentId&quot;, &quot;10000&quot;);
+ return result;
+
+Obviously the payment id being returned shouldn&apos;t be hardcoded in, should read:
+
+ Map result = ServiceUtil.returnSuccess();
+ result.put(&quot;paymentId&quot;, paymentId);
+ return result;</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 07:06:01</issue_when>
+ <thetext>This has been corrected in CVS, thank you.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 07:05:36</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 07:06:01</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 07:06:01</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>104</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405134511</delta_ts>
+ <subcomponent>Workflow</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-05 13:22:20</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Job Definition</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 13:22:20</issue_when>
+ <thetext>This request might fit to the section &quot;Service Engine Tools&quot; on the page
+&quot;http://ofbiz.org/feature-list.html&quot;.
+
+Would you like to support the JDF
+standard?
+http://en.wikipedia.org/wiki/Job_Definition_Format</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 13:45:11</issue_when>
+ <thetext>Not sure what this is supposed to be requesting. This issue contains no specific feature request.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 13:45:11</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 13:45:11</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>105</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405134741</delta_ts>
+ <subcomponent>Service</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-05 13:28:41</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Support for dynamic Service Configuration</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 13:28:41</issue_when>
+ <thetext>Would you like to use a service configurator for your server?
+-
+http://www.cs.wustl.edu/~schmidt/ACE-papers.html
+-
+http://java.sun.com/j2se/1.5.0/docs/guide/jmx/tutorial/tutorialTOC.html
+
+Can this
+request solved with JMX?
+http://en.wikipedia.org/wiki/Java_Management_eXtensions</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 13:47:41</issue_when>
+ <thetext>This issue contains no specific feature request. Not sure what you are requesting, so there is no way to
+determine if JMX will solve the problem. </thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 13:47:41</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 13:47:41</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>106</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405134845</delta_ts>
+ <subcomponent>WebTools</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-05 13:42:12</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Portlet Interface</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 13:42:12</issue_when>
+ <thetext>Would you like to integrate components with web portals?
+
+-
+http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-portlet2.html
+-
+http://www.javaworld.net/javaworld/jw-11-2003/jw-1114-jsfredux.html
+-
+http://en.wikipedia.org/wiki/Portlet</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 13:48:45</issue_when>
+ <thetext>Sure we would love to integrate with web portals, however this issue contains no specific feature
+request.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 13:48:45</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 13:48:45</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>107</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405135106</delta_ts>
+ <subcomponent>WebTools</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-05 13:47:12</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Vector graphics and XForms</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 13:47:12</issue_when>
+ <thetext>Will the application support .svgz or .x3dz files that contain XForms?
+
+-
+http://en.wikipedia.org/wiki/Scalable_Vector_Graphics
+-
+http://en.wikipedia.org/wiki/X3D
+- http://en.wikipedia.org/wiki/XForms
+-
+http://xml.apache.org/batik/
+
+See also the request &quot;XForms&quot; in the discussion forum by
+Britton LaRoche from &quot;August 28, 2003 12:38:36 AM
+GMT&quot;.
+https://ofbiz.dev.java.net/servlets/ProjectForumMessageView?forumID=282&amp;messageID=1065</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 13:51:06</issue_when>
+ <thetext>This issue contains no specific feature request. No information was provided regarding how support for
+these file types is desired and where the desired functionality is needed. </thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 13:51:06</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 13:51:06</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>108</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405140048</delta_ts>
+ <subcomponent>Rules</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-05 13:51:31</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Integration with Knowledge Representation</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 13:51:31</issue_when>
+ <thetext>What is the current state of your support for this technology?
+-
+http://en.wikipedia.org/wiki/Knowledge_representation
+-
+http://en.wikipedia.org/wiki/Expert_system</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 14:00:48</issue_when>
+ <thetext>This issue is not a valid feature request, questions of this sort should be discussed on the mailing lists.
+In addition, do expect readers to follow links and read articles to get your point across it is highly
+unlikely that people will spent the time to research something for you. Your best bet is to read the
+articles and post a question asking something specific, then your chances of getting an intelligent
+response is greater.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:00:48</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:00:48</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>109</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405140138</delta_ts>
+ <subcomponent>Service</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-05 13:52:57</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Open Services Gateway</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 13:52:57</issue_when>
+ <thetext>Would you like to cooperate with these service APIs so that your software can be used over these
+interfaces?
+http://www.osgi.org/about/faqs.asp
+http://en.wikipedia.org/wiki/OSGi</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 14:01:37</issue_when>
+ <thetext>This issue contains no specific feature request.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:01:37</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:01:37</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>110</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405140228</delta_ts>
+ <subcomponent>Entity</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-05 13:56:20</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Content Storage API</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 13:56:20</issue_when>
+ <thetext>Would you like to support the programming interface &quot;Content
+Repository&quot;?
+http://jcp.org/en/jsr/detail?id=170
+
+This topic can be perhaps combined
+with requests for CVS and Subversion integration.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 14:02:28</issue_when>
+ <thetext>This issue contains no specific feature request; please see issue #108 for more suggestions.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:02:28</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:02:28</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>111</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405140305</delta_ts>
+ <subcomponent>Workflow</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-05 14:02:16</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Workflow Management</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 14:02:17</issue_when>
+ <thetext>How do you think about workflow modelling patterns?
+Which of them are supported by your
+software? (Is cooperation with other standards like BPML besides WfMC XPDL
+possible?)
+http://tmitwww.tm.tue.nl/research/patterns/</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 14:03:05</issue_when>
+ <thetext>see issue #108</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:03:05</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:03:05</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>112</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040405141257</delta_ts>
+ <subcomponent>Entity Extensions</subcomponent>
+ <reporter>elfring</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-05 14:07:40</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>relationships and object mapping</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>elfring</who>
+ <issue_when>2004-04-05 14:07:40</issue_when>
+ <thetext>I&apos;ve got the impression from the discussion about the topic &quot;entity-engine: thoughts on extends
+(IS A) pattern and relationships&quot; that there might be something useful from other information
+sources.
+
+1. CORBA: Relationship Service
+Specification
+http://www.omg.org/technology/documents/formal/relationship_service.htm
+
+2.
+ObJectRelationalBridge
+http://db.apache.org/ojb/
+
+3. alternative open source
+implementation for JDO
+http://speedo.objectweb.org/
+
+4. Citations from
+CiteSeer
+http://citeseer.ist.psu.edu/cis?q=object+and+relational+and+mapping+and+database
+
+5.
+a detailed Wiki
+http://en.wikipedia.org/wiki/Object_database
+
+Would you like to reuse
+anything from those implementations and experiences?
+
+How does your use of the Java Open
+Transaction Manager (http://jotm.objectweb.org/) fit into this picture?</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-05 14:12:57</issue_when>
+ <thetext>No specific enhancement requested.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:12:57</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-04-05 14:12:57</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>113</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>WORKSFORME</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040406101817</delta_ts>
+ <subcomponent>WebTools</subcomponent>
+ <reporter>ajzeneski</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-04-06 07:00:29</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>EditEntity.jsp displays wrong field length</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-04-06 07:00:29</issue_when>
+ <thetext>Reported by prozelit@yahoo.com:
+
+On EditEntity.jsp, there is a fields table. It is
+supposed to display the length of each field, but it
+looks like it displays the length of the field name
+instead. It seems to me there is a bug at line 347.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-06 10:18:17</issue_when>
+ <thetext>
+This is the expected behavior. The length in parenthesis there is meant to show you the length of the
+column name so that you can tell if it is within the required length of the database, we generally
+recommend (and always use) equal or less than 30 characters.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-06 10:18:17</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-06 10:18:17</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>WORKSFORME</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>114</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>PC</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040406100126</delta_ts>
+ <subcomponent>WorkEffort</subcomponent>
+ <reporter>vivalinux</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-04-06 10:01:26</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc>http://&lt;ofbiz_URL&gt;/workeffort/control/month?start=1083297600000</issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Daylight savings and the calendar</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>vivalinux</who>
+ <issue_when>2004-04-06 10:01:26</issue_when>
+ <thetext>On the months when daylight savings takes place, or stops taking place
+(April/September in the US), the way the start of the period is computed in the
+WorkEffortServices.getWorkEffortsByPeriod yields one extra hour after dayligh
+savings kicks in.
+The reason for this is that the absolute time is interpreted differently once
+daylight savings is in place.
+So I suggest using the attached patch which relies on UtilDateTime.getDayStart
+to get the period start/end.</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>115</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040730150719</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>lykins</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-06 13:56:51</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Product to Order Lookup Link</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>lykins</who>
+ <issue_when>2004-04-06 13:56:51</issue_when>
+ <thetext>Something that should bring great benefit is the ability to link the product to
+the orders from the product maintenance screen. It would be very beneficial to
+know what purchase order the product came from. I noticed that there are links
+to almost all other relations besides this.
+
+Thanks,
+Patrick</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 15:07:19</issue_when>
+ <thetext>good idea; now in CVS</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 15:07:19</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 15:07:19</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>116</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040417174533</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-04-17 00:19:59</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Implementation of getProductVariant service.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-04-17 00:19:59</issue_when>
+ <thetext>Implementation of getProductVariant service.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-04-17 00:20:48</issue_when>
+ <thetext>Created an attachment (id=50)
+Patch for the java implementation
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-04-17 00:21:21</issue_when>
+ <thetext>Created an attachment (id=51)
+Patch for the service definition.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-17 17:45:33</issue_when>
+ <thetext>It&apos;s in. Thanks Jacopo.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>50</attachid>
+ <date>2004-04-17 00:20:48</date>
+ <desc>Patch for the java implementation</desc>
+ <ispatch>1</ispatch>
+ <filename>ProductServices.java.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/50/ProductServices.java.patch</attachment_iz_url>
+ </attachment>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>51</attachid>
+ <date>2004-04-17 00:21:21</date>
+ <desc>Patch for the service definition.</desc>
+ <ispatch>1</ispatch>
+ <filename>services.xml.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/51/services.xml.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-04-17 00:20:48</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=50)
+Patch for the java implementation
+</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-04-17 00:21:21</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=51)
+Patch for the service definition.
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-17 17:45:33</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-17 17:45:33</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>117</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040422201817</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>colt_nz</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-04-20 14:45:56</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>editPayment has wrongly named fields</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>colt_nz</who>
+ <issue_when>2004-04-20 14:45:56</issue_when>
+ <thetext>/accounting/webapp/accounting/payment/editPayment.ftl
+
+Input fields partyIdTo and partyIdFrom are round the wrong way. ie switch &apos;em.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-22 20:18:17</issue_when>
+ <thetext>
+Thanks for reporting this, the fix is in CVS. Looks like it was perhaps an issue with the i18n...</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-22 20:18:17</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-22 20:18:17</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>118</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P4</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040819001212</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>jonesde</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-04-23 21:32:35</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Make QuickAddVariants Easier to Use</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-23 21:32:35</issue_when>
+ <thetext>Make QuickAddVariants Easier to Use:
+
+1. Change to multi-form
+2. Allow enter pattern to use to pre-populate the various IDs</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-08-19 00:12:11</issue_when>
+ <thetext>Some time ago I did this and you put all the stuff in CVS, so now it&apos;s done.</thetext>
+ </long_desc>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-08-19 00:12:10</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-08-19 00:12:10</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>119</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jacopo</assigned_to>
+ <delta_ts>20040717041328</delta_ts>
+ <subcomponent>Manufacturing</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-24 00:15:36</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Refactoring of the BOMEvent class that manages all the related to bill of materials editing.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-04-24 00:15:36</issue_when>
+ <thetext>Refactoring of the BOMEvent class that manages all the related to bill of
+materials editing.
+
+Three new (simple) services should be created (create, update, delete).
+An sECA rule should be added to the create and update services to update the
+low level code.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-07-17 04:13:28</issue_when>
+ <thetext>Ok, the BOMEvent class has gone away; I have implemented some simple services
+to add/update/remove/copy boms (performing all the needed checks for loops and
+updating the product&apos;s llc).
+</thetext>
+ </long_desc>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-17 04:13:28</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-17 04:13:28</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>120</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jacopo</assigned_to>
+ <delta_ts>20040424001903</delta_ts>
+ <subcomponent>Manufacturing</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-24 00:19:03</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>searchDuplicateAncenstor service should resolve also virtual to variant products to search for loops.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-04-24 00:19:03</issue_when>
+ <thetext>searchDuplicateAncenstor service should resolve also virtual to variant
+products to search for loops.</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>121</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jacopo</assigned_to>
+ <delta_ts>20040717041557</delta_ts>
+ <subcomponent>Manufacturing</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-24 00:24:11</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Refactoring of the Bill of Material editor&apos;s ui: migration to the forms widget.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-04-24 00:24:11</issue_when>
+ <thetext>Refactoring of the Bill of Material editor&apos;s ui: migration to the forms widget.
+
+This will also include:
+- i18n and l10n issues
+- management of all the relavant fields that are missing
+- lookup fields
+- fields validation</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-07-17 04:15:57</issue_when>
+ <thetext>I have not used the form widget (maybe in the future, together with some more
+clean ups) here, but most of these points has been fixed.</thetext>
+ </long_desc>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-17 04:15:57</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-17 04:15:57</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>122</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jacopo</assigned_to>
+ <delta_ts>20040424002537</delta_ts>
+ <subcomponent>Manufacturing</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-24 00:25:37</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>updateLowLevelCode service - it should also update the low level code for variant products </short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-04-24 00:25:37</issue_when>
+ <thetext>updateLowLevelCode service - it should also update the low level code for
+variant products</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>123</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501062848</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:05:20</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add methods to check against classes, instead of only objects.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:05:20</issue_when>
+ <thetext>This patch adds new versions of interfaceOf, isOrSubOf, and instanceOf, that
+check against a class, instead of an Object.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:06:57</issue_when>
+ <thetext>Created an attachment (id=52)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:52</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 06:28:48</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>52</attachid>
+ <date>2004-04-29 16:06:57</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>ObjectType-check-against-class.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/52/ObjectType-check-against-class.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:06:57</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=52)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:15:14</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>DEFECT</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:52</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:28:48</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:28:48</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>124</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501062855</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:08:48</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Handle is-* operators when value1 is null</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:08:48</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:09:11</issue_when>
+ <thetext>Created an attachment (id=53)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:53</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 06:28:55</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>53</attachid>
+ <date>2004-04-29 16:09:11</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>ObjectType-is-compare-value1-null.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/53/ObjectType-is-compare-value1-null.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:09:11</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=53)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:15:14</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>DEFECT</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:53</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:28:55</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:28:55</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>125</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501062903</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:10:04</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Convert classnames to full names.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:10:04</issue_when>
+ <thetext>See $summary.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:10:21</issue_when>
+ <thetext>Created an attachment (id=54)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:48</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 06:29:03</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>54</attachid>
+ <date>2004-04-29 16:10:21</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>ObjectType-convert-classname-to-full.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/54/ObjectType-convert-classname-to-full.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:10:21</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=54)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:15:13</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>DEFECT</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:48</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:29:03</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:29:03</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>126</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501062243</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:13:25</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add some helper methods for objects implementing their own equals, compareTo, and hashCode</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:13:25</issue_when>
+ <thetext>Name: UtilObject.patch
+Prune: 1
+Description: Add some helper methods for objects implementing their own equals,
+compareTo, and hashCode.
+DiffStat:
+ base/src/base/org/ofbiz/base/util/UtilObject.java | 35 ++++++++++++++++++++++
+ 1 files changed, 35 insertions(+)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:13:45</issue_when>
+ <thetext>Created an attachment (id=55)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:49</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 06:22:43</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>55</attachid>
+ <date>2004-04-29 16:13:45</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilObject.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/55/UtilObject.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:13:45</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=55)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:15:13</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>DEFECT</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:49</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:22:43</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:22:43</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>127</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501055554</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:18:14</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Simplify the top-level build.xml</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:18:14</issue_when>
+ <thetext>Name: simpler-build.xml.patch
+Prune: 1
+Description: Simplifies the top-level build.xml
+ This patch makes use of ant 1.6 features, to automate calling of sub
+ build.xmls. It also adds a &apos;test&apos; target; note, however, that the
+ listed sub build.xmls don&apos;t actually have that test target yet.
+DiffStat:
+ build.xml | 494 ++++----------------------------------------------------------
+ 1 files changed, 32 insertions(+), 462 deletions(-)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:18:43</issue_when>
+ <thetext>Created an attachment (id=56)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:50</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 05:55:54</issue_when>
+ <thetext>This is now in, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>56</attachid>
+ <date>2004-04-29 16:18:43</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>simpler-build.xml.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/56/simpler-build.xml.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:18:43</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=56)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:19:44</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>DEFECT</oldvalue>
+ <newvalue>ENHANCEMENT</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:50</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 05:55:54</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 05:55:54</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>128</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501051820</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:20:39</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add support for formatting time intervals with human words</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:20:39</issue_when>
+ <thetext>Name: UtilDateTime-time-interval.patch
+Prune: 1
+Description: Add support for formatting time intervals with human words
+DiffStat:
+ base/config/DateTimeLabels.properties | 10 ++
+ base/src/base/org/ofbiz/base/util/UtilDateTime.java | 90 ++++++++++++++++++++
+ 2 files changed, 100 insertions(+)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:20:57</issue_when>
+ <thetext>Created an attachment (id=57)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:51</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 05:18:20</issue_when>
+ <thetext>These are now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>57</attachid>
+ <date>2004-04-29 16:20:57</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilDateTime-time-interval.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/57/UtilDateTime-time-interval.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:20:58</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=57)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:51</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 05:18:20</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 05:18:20</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>129</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429172414</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:22:10</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add more childElementList and firstChildElement variants.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:22:10</issue_when>
+ <thetext>Name: UtilXml-childElementList-firstChildElement.patch
+Prune: 1
+Depends: UtilXml-writeElement.patch
+Description: Add more childElementList and firstChildElement variants.
+DiffStat:
+ base/src/base/org/ofbiz/base/component/ComponentLoaderConfig.java
+ | 2
+ base/src/base/org/ofbiz/base/util/UtilXml.java
+ | 80 ++++++++++
+ components/content/src/org/ofbiz/content/widget/form/ModelFormField.java
+ | 4
+ components/entity/src/org/ofbiz/entity/model/ModelViewEntity.java
+ | 2
+ components/minilang/src/org/ofbiz/minilang/SimpleMethod.java
+ | 2
+ components/minilang/src/org/ofbiz/minilang/method/callops/CallClassMethod.java
+ | 2
+ components/minilang/src/org/ofbiz/minilang/method/callops/CallObjectMethod.java
+ | 2
+ components/minilang/src/org/ofbiz/minilang/method/callops/CreateObject.java
+ | 2
+ components/minilang/src/org/ofbiz/minilang/method/conditional/CombinedCondition.java
+| 2
+ components/minilang/src/org/ofbiz/minilang/method/conditional/MasterIf.java
+ | 2
+ components/minilang/src/org/ofbiz/minilang/method/otherops/Calculate.java
+ | 4
+ components/minilang/src/org/ofbiz/minilang/method/otherops/Log.java
+ | 2
+ components/minilang/src/org/ofbiz/minilang/operation/MakeInString.java
+ | 2
+ components/minilang/src/org/ofbiz/minilang/operation/SimpleMapProcess.java
+ | 2
+ 14 files changed, 95 insertions(+), 15 deletions(-)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:22:50</issue_when>
+ <thetext>Created an attachment (id=58)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:57:47</issue_when>
+ <thetext>Changing priority to P2, as I need these applied before I can commit my entity
+cache work.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-29 17:24:14</issue_when>
+ <thetext>Now in CVS, thanks Adam.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>58</attachid>
+ <date>2004-04-29 16:22:50</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilXml-childElementList-firstChildElement.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/58/UtilXml-childElementList-firstChildElement.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:22:50</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=58)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:57:47</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:24:14</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:24:14</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>130</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429174623</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:24:36</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>put now returns the previous value of the object, if it exists</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:24:36</issue_when>
+ <thetext>Name: UtilCache-put-returns-object.patch
+Prune: 1
+Depends: UtilCache-listeners.patch
+Description: put now returns the previous value of the object, if it exists
+DiffStat:
+ base/src/base/org/ofbiz/base/util/UtilCache.java | 16 ++++++++++------
+ 1 files changed, 10 insertions(+), 6 deletions(-)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:24:58</issue_when>
+ <thetext>Created an attachment (id=59)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:57:48</issue_when>
+ <thetext>Changing priority to P2, as I need these applied before I can commit my entity
+cache work.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-29 17:46:23</issue_when>
+ <thetext>Now in CVS, thanks Adam.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>59</attachid>
+ <date>2004-04-29 16:24:58</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-put-returns-object.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/59/UtilCache-put-returns-object.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:24:58</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=59)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:57:48</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:46:23</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:46:23</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>131</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040429174619</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 16:55:31</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add getCacheLineKeys method.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:55:31</issue_when>
+ <thetext>Name: UtilCache-getCacheLineKeys.patch
+Prune: 1
+Depends: UtilCache-put-returns-object.patch
+Description: Add getCacheLineKeys method.
+DiffStat:
+ base/src/base/org/ofbiz/base/util/UtilCache.java | 4 ++++
+ 1 files changed, 4 insertions(+)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:55:56</issue_when>
+ <thetext>Created an attachment (id=60)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 16:57:48</issue_when>
+ <thetext>Changing priority to P2, as I need these applied before I can commit my entity
+cache work.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-04-29 17:46:19</issue_when>
+ <thetext>Now in CVS, thanks Adam.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>60</attachid>
+ <date>2004-04-29 16:55:56</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-getCacheLineKeys.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/60/UtilCache-getCacheLineKeys.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:55:56</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=60)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 16:57:48</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:46:19</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-04-29 17:46:19</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>132</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501051751</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 19:39:13</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add entrySet(), which functions like Map.entrySet().</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:39:13</issue_when>
+ <thetext>Name: UtilCache-entrySet.patch
+Prune: 1
+Description: Add entrySet(), which functions like Map.entrySet().
+DiffStat:
+ base/src/base/org/ofbiz/base/util/UtilCache.java | 4 ++++
+ 1 files changed, 4 insertions(+)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:41:28</issue_when>
+ <thetext>Created an attachment (id=61)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:53</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 05:17:51</issue_when>
+ <thetext>These are now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>61</attachid>
+ <date>2004-04-29 19:41:27</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-entrySet.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/61/UtilCache-entrySet.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:41:28</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=61)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:53</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 05:17:51</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 05:17:51</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>133</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501042628</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 19:42:19</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Add a new constructor.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:42:19</issue_when>
+ <thetext>Name: UtilCache-new-constructor.patch
+Prune: 1
+Depends: UtilCache-entrySet.patch
+Description: Add a new constructor.
+DiffStat:
+ base/src/base/org/ofbiz/base/util/UtilCache.java | 13 +++++++++++++
+ 1 files changed, 13 insertions(+)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:42:45</issue_when>
+ <thetext>Created an attachment (id=62)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:48</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-30 10:42:15</issue_when>
+ <thetext>Changing these issuses, as they are needed by EntityCache.patch.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 04:26:28</issue_when>
+ <thetext>These are now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>62</attachid>
+ <date>2004-04-29 19:42:45</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-new-constructor.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/62/UtilCache-new-constructor.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:42:45</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=62)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:48</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-30 10:42:15</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P2</oldvalue>
+ <newvalue>P1</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 04:26:28</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 04:26:28</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>134</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501062248</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 19:43:49</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Change utilCacheTable to a WeakHashMap.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:43:49</issue_when>
+ <thetext>Name: UtilCache-WeakHashMap.patch
+Prune: 1
+Depends: UtilCache-new-constructor.patch
+Description: Change utilCacheTable to a WeakHashMap.
+DiffStat:
+ base/src/base/org/ofbiz/base/util/UtilCache.java | 3 ++-
+ 1 files changed, 2 insertions(+), 1 deletion(-)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:44:39</issue_when>
+ <thetext>Created an attachment (id=63)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:49</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 06:22:48</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>63</attachid>
+ <date>2004-04-29 19:44:39</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-WeakHashMap.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/63/UtilCache-WeakHashMap.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:44:39</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=63)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:49</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:22:48</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:22:48</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>135</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P1</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501042633</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 19:45:22</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Methods that clear a cache by name or pattern.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:45:22</issue_when>
+ <thetext>Name: UtilCache-clearCache-helpers.patch
+Prune: 1
+Depends: UtilCache-WeakHashMap.patch
+Description: Methods that clear a cache by name or pattern.
+DiffStat:
+ base/src/base/org/ofbiz/base/util/UtilCache.java | 22 ++++++++++++++++++++++
+ 1 files changed, 22 insertions(+)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:45:43</issue_when>
+ <thetext>Created an attachment (id=64)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:49</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-30 10:42:16</issue_when>
+ <thetext>Changing these issuses, as they are needed by EntityCache.patch.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 04:26:33</issue_when>
+ <thetext>These are now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>64</attachid>
+ <date>2004-04-29 19:45:43</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-clearCache-helpers.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/64/UtilCache-clearCache-helpers.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:45:43</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=64)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:49</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-30 10:42:16</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P2</oldvalue>
+ <newvalue>P1</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 04:26:33</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 04:26:33</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>136</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040501064335</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-04-29 19:46:54</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Place the loaded document object in a UtilCache instance.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:46:54</issue_when>
+ <thetext>Name: ResourceLoader-document-in-cache.patch
+Prune: 1
+Depends: UtilCache-clearCache-helpers.patch
+Description: Place the loaded document object in a UtilCache instance.
+DiffStat:
+ base/src/base/org/ofbiz/base/config/ResourceLoader.java | 11 ++++-------
+ 1 files changed, 4 insertions(+), 7 deletions(-)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:47:17</issue_when>
+ <thetext>Created an attachment (id=65)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-04-29 19:57:50</issue_when>
+ <thetext>Changing priority, so that someone else can see the ones I want applied next.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-01 06:43:35</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>65</attachid>
+ <date>2004-04-29 19:47:17</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>ResourceLoader-document-in-cache.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/65/ResourceLoader-document-in-cache.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:47:17</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=65)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>doogie</user>
+ <when>2004-04-29 19:57:50</when>
+ <field_name>priority</field_name>
+ <field_desc>Priority</field_desc>
+ <oldvalue>P3</oldvalue>
+ <newvalue>P2</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:43:35</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-01 06:43:35</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>137</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040527233355</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-05-03 14:48:41</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>createInvoiceForOrder not get correct fields for terms</short_desc>
+ <keywords></keywords>
+ <has_duplicates>
+ <issue_id>141</issue_id>
+ <who>jacopo</who>
+ <when>2004-05-26 10:48:21</when>
+ </has_duplicates>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-05-03 14:48:41</issue_when>
+ <thetext>InvoiceServices.createInvoiceForOrder is not getting correct fields for terms.
+It is not automatically generating a key for InvoiceTerm entity and is also
+looking for &quot;termType&quot; from BillingAccount when the field is actually &quot;termTypeId.&quot;</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-05-03 14:50:09</issue_when>
+ <thetext>Created an attachment (id=66)
+Patch fixes defect and allows invoices to be created correctly
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-05-04 00:50:17</issue_when>
+ <thetext>See also issue #97</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-05-26 10:48:20</issue_when>
+ <thetext>*** Issue 141 has been marked as a duplicate of this issue. ***</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-05-27 23:33:55</issue_when>
+ <thetext>It&apos;s now in CVS, thanks to both Si Chen and Jacopo Cappellato for reporting/fixing this.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>66</attachid>
+ <date>2004-05-03 14:50:09</date>
+ <desc>Patch fixes defect and allows invoices to be created correctly</desc>
+ <ispatch>1</ispatch>
+ <filename>InvoiceServices.java.patch</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/66/InvoiceServices.java.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-05-03 14:50:09</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=66)
+Patch fixes defect and allows invoices to be created correctly
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-03 23:03:12</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-27 23:33:55</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-05-27 23:33:55</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>138</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>WORKSFORME</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040713022331</delta_ts>
+ <subcomponent>Manufacturing</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-05-04 17:45:50</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>Windows XP</op_sys>
+ <short_desc>Null delegator in FindProductRun</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-05-04 17:45:50</issue_when>
+ <thetext>I am getting a
+4269781[ VisitHandler.java:130:ERROR] Could not find delegator with
+delegatorName [null] in session, not creating Visit entity
+
+Is this a Windows problem or a bug?
+
+Thanks,
+
+Si Chen</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-05-04 17:46:33</issue_when>
+ <thetext>Created an attachment (id=67)
+error log from running FindProductRun in Manufacturing
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-07-13 02:23:31</issue_when>
+ <thetext>I couldn&apos;t recreate this error.
+However, from the log it seems something related to the Visit stuff.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>67</attachid>
+ <date>2004-05-04 17:46:33</date>
+ <desc>error log from running FindProductRun in Manufacturing</desc>
+ <ispatch></ispatch>
+ <filename>findproductrun.log</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/67/findproductrun.log</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-05-04 17:46:33</when>
+ <field_name>attachments.thedata</field_name>
+ <field_desc>Attachment Data</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=67)
+error log from running FindProductRun in Manufacturing
+</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-13 02:23:31</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-13 02:23:31</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>WORKSFORME</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>139</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>david</assigned_to>
+ <delta_ts>20040509064322</delta_ts>
+ <subcomponent>E-Commerce</subcomponent>
+ <reporter>arukala</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-05-09 06:43:22</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Reading UiLable Properties in Email templates</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>arukala</who>
+ <issue_when>2004-05-09 06:43:22</issue_when>
+ <thetext>Hallo David,
+
+We are looking for your suggestions. on how should i call the UiLabel
+Properties in eCommerce Email Templates. we are eager to see this feature in
+email Confirmation after placing an order for customers?. suggest us how to?</thetext>
+ </long_desc>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>140</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>jacopo</assigned_to>
+ <delta_ts>20040518040000</delta_ts>
+ <subcomponent>Manufacturing</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-05-11 08:26:52</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Remove all the checks against the isVariant field.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-05-11 08:26:52</issue_when>
+ <thetext>Remove all the checks against the isVariant field: the best way to see if a
+product is variant is to check the ProductAssoc entity.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-05-18 04:00:00</issue_when>
+ <thetext>Committed with the last changes a few days ago.</thetext>
+ </long_desc>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-05-18 04:00:00</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-05-18 04:00:00</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>141</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>DUPLICATE</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040526104821</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-05-26 09:01:42</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Patch for bug in invoice creation when terms are specified for a billing account.</short_desc>
+ <keywords></keywords>
+ <is_duplicate>
+ <issue_id>137</issue_id>
+ <who>jacopo</who>
+ <when>2004-05-26 10:48:21</when>
+ </is_duplicate>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-05-26 09:01:42</issue_when>
+ <thetext>Patch for bug in invoice creation when terms are specified for billing account:
+A wrong field name was used.
+Also a non-null field was not set.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-05-26 09:02:49</issue_when>
+ <thetext>Created an attachment (id=68)
+The patch file in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-05-26 10:48:21</issue_when>
+ <thetext>Ok, Si Chen had already fixed this!!!
+
+*** This issue has been marked as a duplicate of 137 ***</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>68</attachid>
+ <date>2004-05-26 09:02:49</date>
+ <desc>The patch file in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>InvoiceServices.java.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/68/InvoiceServices.java.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-05-26 09:02:49</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=68)
+The patch file in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-05-26 10:48:21</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-05-26 10:48:21</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>DUPLICATE</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>142</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>PC</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040630194530</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>jackhung</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-06-15 03:33:20</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>Linux</op_sys>
+ <short_desc>Party Profile [New Communication] throws exception</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jackhung</who>
+ <issue_when>2004-06-15 03:33:20</issue_when>
+ <thetext>Error: The application script threw an exception: Sourced file:
+/mnt/hda6/jackwork/ofbiz3/workspace/ofbiz/components/party/webapp/partymgr/WEB-INF/actions/communication/editCommunication.bsh
+: null : at Line: 47 : in file:
+/mnt/hda6/jackwork/ofbiz3/workspace/ofbiz/components/party/webapp/partymgr/WEB-INF/actions/communication/editCommunication.bsh
+: party .getRelatedOne ( &quot;Person&quot; ) Target exception:
+java.lang.NullPointerException: Null Pointer in Method Invocation BSF info:
+/mnt/hda6/jackwork/ofbiz3/workspace/ofbiz/components/party/webapp/partymgr/WEB-INF/actions/communication/editCommunication.bsh
+at line: 0 column: 0
+
+
+URL: https://jhpc:18443/partymgr/control/viewCommunicationEvent?partyIdFrom=ltdadmin
+
+partyId = request.getParameter(&quot;party_id&quot;);
+...
+party = delegator.findByPrimaryKey(&quot;Party&quot;, UtilMisc.toMap(&quot;partyId&quot;, partyId));
+context.put(&quot;party&quot;, party);
+context.put(&quot;lookupPerson&quot;, party.getRelatedOne(&quot;Person&quot;)); &lt;---</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-06-30 19:45:30</issue_when>
+ <thetext>This has been fixed in CVS</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-06-15 03:39:27</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:45:30</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:45:30</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>143</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040630194322</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>FEATURE</issue_type>
+ <creation_ts>2004-06-17 10:51:48</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allows orders to have shipBeforeDate and shipAfterDate</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-06-17 10:51:48</issue_when>
+ <thetext>Changed ShoppingCart.java and CheckoutHelper.java to allow ship before and
+after dates to be set using the OrderShipmentPreference entity.
+OrderServices.java is modified as well to allow these dates to go on the order
+confirmation email.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-06-17 10:53:16</issue_when>
+ <thetext>Created an attachment (id=69)
+Patches ShoppingCart.java, CheckoutHelper.java, OrderServices.java,
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>69</attachid>
+ <date>2004-06-17 10:53:16</date>
+ <desc>Patches ShoppingCart.java, CheckoutHelper.java, OrderServices.java,</desc>
+ <ispatch>1</ispatch>
+ <filename>order_dating_patch.txt</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/69/order_dating_patch.txt</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-06-17 10:53:16</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=69)
+Patches ShoppingCart.java, CheckoutHelper.java, OrderServices.java,
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:43:22</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:43:22</when>
+ <field_name>short_desc</field_name>
+ <field_desc>Summary</field_desc>
+ <oldvalue>Allows orders to have shipBeforeDate and shipAfterDate </oldvalue>
+ <newvalue>Allows orders to have shipBeforeDate and shipAfterDate</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>144</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>WORKSFORME</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>PC</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040630194017</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>jackhung</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-06-18 02:57:10</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>Linux</op_sys>
+ <short_desc>https connection broke Mozilla 1.3 and 1.4</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jackhung</who>
+ <issue_when>2004-06-18 02:57:10</issue_when>
+ <thetext>I&apos;m getting a strange problem lately (2 days). I&apos;m at the ecommerce&apos;s main page
+and try to [Login] (url
+https://www.myhost.net:8443/ecommerce/control/checkLogin/main). The
+certification stuff comes up and I accepted it. On one of my system using
+Mozilla 1.3, the browser popup a dialog saying:
+ &quot;www.myhost.net has received an incorrect or unexpected message. Error Code:
+-12229&quot;
+For all subsequent attempt, the browser popup a dialog saying:
+ &quot;The Document contains no data&quot;
+It appears that the browser is not sending any request to the server on
+subsequent attempt.
+
+On another system using Mozilla 1.4, i did not get the &quot;unexpected message&quot;, but
+still got the &quot;no data&quot; one.
+
+I have an Crozilla 1.3a browser around (from the Mozilla SVG project). I tried
+it and it works!!
+
+As a matter of fact, i&apos;m having the same problem with any of the secure
+components (webtools, partymgr, ...)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-06-30 19:40:17</issue_when>
+ <thetext>I cannot duplicate this issue with FireFox. It could be a bug in that version of mozilla. Is this still an
+issue w/ the 1.5-1.7?</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:40:17</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:40:17</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>WORKSFORME</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>145</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040619093839</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-06-18 09:57:41</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Fixes duplicated PartyContactMechPurposes when using updatePartyContactMech</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-06-18 09:57:41</issue_when>
+ <thetext>Fixes duplicated PartyContactMechPurposes when using updatePartyContactMech to
+move a party&apos;s contact information from an old contactMechId to a
+newContactMechId by checking to make sure the PartyContactMechPurpose does not
+already exist.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-06-18 09:58:34</issue_when>
+ <thetext>Created an attachment (id=70)
+Patches PartyContactMechServices.xml
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-06-19 09:38:39</issue_when>
+ <thetext>It&apos;s in CVS now. Thanks Si.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>70</attachid>
+ <date>2004-06-18 09:58:34</date>
+ <desc>Patches PartyContactMechServices.xml</desc>
+ <ispatch>1</ispatch>
+ <filename>PartyContactMechServices.patch</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/70/PartyContactMechServices.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-06-18 09:58:34</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=70)
+Patches PartyContactMechServices.xml
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-06-19 09:38:39</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-06-19 09:38:39</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>146</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040622094557</delta_ts>
+ <subcomponent>Party</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-06-21 17:56:29</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Passes partyId into updatePostalAddress from updatepartyPostalAddress</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-06-21 17:56:29</issue_when>
+ <thetext>Passes partyId into updatePostalAddress from updatePartyPostalAddress, to
+preserve it for updateCreditCard later.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-06-21 17:57:02</issue_when>
+ <thetext>Created an attachment (id=71)
+Passes partyId from updatePartyPostalAddress to updatePostalAddress
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-06-22 09:45:57</issue_when>
+ <thetext>It&apos;s in. Thanks Si Chen.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>71</attachid>
+ <date>2004-06-21 17:57:02</date>
+ <desc>Passes partyId from updatePartyPostalAddress to updatePostalAddress</desc>
+ <ispatch>1</ispatch>
+ <filename>PartyContactMechServices.patch</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/71/PartyContactMechServices.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-06-21 17:57:02</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=71)
+Passes partyId from updatePartyPostalAddress to updatePostalAddress
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-06-22 09:45:57</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-06-22 09:45:57</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>147</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040623161518</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-06-22 15:58:48</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Minor additions to calculateProductPrice</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-06-22 15:58:48</issue_when>
+ <thetext>Implements a &quot;Percent of Default Price&quot; price action for price rules and checks
+both default and list prices when there is a &quot;Promotional Price Override&quot; price
+action.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-06-22 15:59:22</issue_when>
+ <thetext>Created an attachment (id=72)
+minor additions to calculateProductPrice
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-06-23 16:15:18</issue_when>
+ <thetext>Okay, this patch is in. Thanks for working on this and sending it over Si.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>72</attachid>
+ <date>2004-06-22 15:59:22</date>
+ <desc>minor additions to calculateProductPrice</desc>
+ <ispatch>1</ispatch>
+ <filename>calculateProductPrice.patch</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/72/calculateProductPrice.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-06-22 15:59:22</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=72)
+minor additions to calculateProductPrice
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-06-23 16:15:18</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-06-23 16:15:18</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>148</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>WORKSFORME</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040730233418</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-06-23 05:42:27</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Duplicate invoices for the same order under some cicumstances.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-23 05:42:27</issue_when>
+ <thetext>With &quot;Offline Payments&quot;, from the order detail page it is possible to
+receive payments: when the payments cover the order&apos;s grand total an invoice
+is generated; also when the shipment to which the order is issued changes
+its status to PACKED an invoice is created: this can cause the creation
+of two different invoices for the same order.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 21:11:52</issue_when>
+ <thetext>I cannot duplicate this problem; I attempted an offline order, received the payment - no invoice was
+created; issued a quick ship a single invoice was created. Please provide more details.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-07-30 23:34:18</issue_when>
+ <thetext>I cannot believe... I have tried again and the problem is gone away.
+However I&apos;m totally sure (99%) that this was an issue...
+</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-06-23 16:16:30</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-30 23:34:18</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-30 23:34:18</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>WORKSFORME</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>149</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040818085225</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-06-26 01:51:16</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Patch for bug that happens when you try to cancel an order item.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-26 01:51:16</issue_when>
+ <thetext>In revision 1.30 of the services.xml file for the order component, the require-
+new-transaction=&quot;true&quot; was added to the resetGrandTotal service: this causes
+problems when you cancel an order item because the resetGrandTotal is one of
+the services that are triggered and it seems to hang up the process waiting for
+a transaction (at least this happens with SAP-DB).
+
+The attached patch removes the require-new-transaction=&quot;true&quot; parameter and
+this solves the problem; however I don&apos;t know if this could cause other
+problems in different parts of the system.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-26 01:51:58</issue_when>
+ <thetext>Created an attachment (id=73)
+Patch in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-08-18 08:52:25</issue_when>
+ <thetext>The patch has been applied.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>73</attachid>
+ <date>2004-06-26 01:51:58</date>
+ <desc>Patch in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>services.xml.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/73/services.xml.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-06-26 01:51:58</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=73)
+Patch in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-08-18 08:52:25</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-08-18 08:52:25</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>150</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040630190556</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-06-26 01:56:29</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>When you receive a purchase order the order lines are declared completed (also multiple times) even if it&apos;s not true.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-26 01:56:29</issue_when>
+ <thetext>The attached patch fixes two bugs that caused the problem.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-26 01:56:53</issue_when>
+ <thetext>Created an attachment (id=74)
+Tha patch in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-06-30 19:05:56</issue_when>
+ <thetext>patch applied to CVS</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>74</attachid>
+ <date>2004-06-26 01:56:53</date>
+ <desc>Tha patch in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>OrderSimpleMethods.xml.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/74/OrderSimpleMethods.xml.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-06-26 01:56:53</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=74)
+Tha patch in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:05:56</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:05:56</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>151</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040630224402</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-06-26 02:01:14</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In order entry, if no party is selected, during the checkout process some errors are generated: this patch fix the problem by disabling the checkout link if the party is missing.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-26 02:01:14</issue_when>
+ <thetext>In order entry, if no party is selected, during the checkout process some
+errors are generated: this patch fix the problem by disabling the checkout link
+if the party is missing.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-26 02:01:41</issue_when>
+ <thetext>Created an attachment (id=75)
+Patch in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-06-30 19:19:39</issue_when>
+ <thetext>I cannot duplicate this issue; is it still a problem?</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-30 22:44:02</issue_when>
+ <thetext>Hmmm, now it seems to work for me, too.
+I&apos;m wondering if something is changed during the last 10 days... I was quite
+sure about this problem.
+By the way, I&apos;m going to declare resolved this issue, thanks.
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>75</attachid>
+ <date>2004-06-26 02:01:41</date>
+ <desc>Patch in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>noparty.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/75/noparty.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-06-26 02:01:41</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=75)
+Patch in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-06-30 22:44:02</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-06-30 22:44:02</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>152</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P4</priority>
+ <resolution>LATER</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040630192805</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>jacopo</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-06-26 02:13:57</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Shipping location is missing in purchase orders: this causes problems when you try to cancel an order item.</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-26 02:13:57</issue_when>
+ <thetext>In purchase order the shipping location address is not set and this causes
+problems when you try to cancel order items (the shipping location is used is
+some service that are triggered in this situation, such as the calcTax service
+etc...).
+In my opinion the best solution is to add a shipping location address to the
+purchase order.
+The address could be that of the facility that will receive the po.
+
+I have also attached a patch (not a good one, so probably you shouldn&apos;t apply
+it) that fix this problem by allowing, during order entry, to select one of the
+addresses of the facility that is linked to the productStore for which the
+purchase order is taken: so in the checkinits page the store selection should
+be enabled for purchase orders too.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-06-26 02:14:21</issue_when>
+ <thetext>Created an attachment (id=76)
+Patch in UOF
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-06-30 19:28:05</issue_when>
+ <thetext>Sounds like there are more issues here. First off most likely tax should not be calculated for purchase
+orders; nor should the same shipping charges which apply to sales orders should not apply to purchase
+orders.
+
+I will see what I can do when working on some additional PO enhancements.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>76</attachid>
+ <date>2004-06-26 02:14:20</date>
+ <desc>Patch in UOF</desc>
+ <ispatch>1</ispatch>
+ <filename>shipping_address.patch</filename>
+ <submitter_id>194</submitter_id>
+ <submitting_username>jacopo</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/76/shipping_address.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-06-26 02:14:22</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=76)
+Patch in UOF
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:28:05</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-06-30 19:28:05</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>LATER</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>153</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P5</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040707011403</delta_ts>
+ <subcomponent>Service</subcomponent>
+ <reporter>eckardjf</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-07-05 12:19:00</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>purgeOldJobs service leaves artifacts</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>eckardjf</who>
+ <issue_when>2004-07-05 12:19:00</issue_when>
+ <thetext>The purgeOldJobs service will remove qualifying JobSandbox entities, but will not remove the related
+RuntimeData, RecurrenceInfo and RecurrenceRule entities (even if they are not referenced by an
+existing WorkEffort).</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:14:03</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>154</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040707150844</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-07-06 12:49:26</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Unknown import</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-06 12:49:26</issue_when>
+ <thetext>Region.java imports com.sun.rsasign.r. When compiling with jikes, this class is
+not available. Removing the import, and the class compiles fine, so it&apos;s
+obviously not needed.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-06 12:49:58</issue_when>
+ <thetext>Created an attachment (id=77)
+Remove the bad import
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-07 15:08:44</issue_when>
+ <thetext>This import has now been removed, not even sure why it was there in the first place... Thanks Adam.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>77</attachid>
+ <date>2004-07-06 12:49:58</date>
+ <desc>Remove the bad import</desc>
+ <ispatch>1</ispatch>
+ <filename>Region.java.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/77/Region.java.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-07-06 12:49:58</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=77)
+Remove the bad import
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 15:08:44</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 15:08:44</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>155</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040707011249</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-07-06 14:27:26</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>UtilCache does not allow for null keys</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-06 14:27:26</issue_when>
+ <thetext>UtilCache doesn&apos;t allow null keys to be stored. This is sub-optimal, and
+actually causes problems with my enhanced entity cache code. Attached patch fixes.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-06 14:28:17</issue_when>
+ <thetext>Created an attachment (id=78)
+Allow for null keys
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-07 01:12:49</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>78</attachid>
+ <date>2004-07-06 14:28:17</date>
+ <desc>Allow for null keys</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-null-key.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/78/UtilCache-null-key.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-07-06 14:28:17</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=78)
+Allow for null keys
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:12:49</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:12:49</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>156</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040707011312</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-07-06 14:30:09</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allow specifying the expire time on put in UtilCache</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-06 14:30:09</issue_when>
+ <thetext>Add a new put method, that takes an expireTime parameter.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-06 14:30:31</issue_when>
+ <thetext>Created an attachment (id=79)
+The patch.
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-07 01:13:12</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>79</attachid>
+ <date>2004-07-06 14:30:31</date>
+ <desc>The patch.</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-put-takes-expireTime.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/79/UtilCache-put-takes-expireTime.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-07-06 14:30:31</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=79)
+The patch.
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:13:12</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:13:12</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>157</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040707011225</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-07-06 14:35:33</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>In UtilCache, allow for a list of property bases to be specified</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-06 14:35:33</issue_when>
+ <thetext>When a UtilCache instance is created, it currently will only look for a single
+matching property value, by name. This patch allows for a list of base property
+names to be specified, and the first one found will be used.
+
+I make use of this in my entity cache code. For instance:
+==
+Cache.entity-list.${delegator-name}.${entity-name}.expireTime=1800000
+Cache.entity-list.${delegator-name}.${entity-name}.maxSize=500
+
+# high count, low access
+# 5 minute cache time
+Cache.entity-list.${delegator-name}.PartyAttribute=300000
+==</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-06 14:35:48</issue_when>
+ <thetext>Created an attachment (id=80)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-07 01:12:25</issue_when>
+ <thetext>This is now in CVS, thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>80</attachid>
+ <date>2004-07-06 14:35:48</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>UtilCache-wildcard-config.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/80/UtilCache-wildcard-config.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-07-06 14:35:48</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=80)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:12:25</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:12:25</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>158</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040707011838</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-07-06 17:08:44</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>distinct attribute for getProductFeatures is not working</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-07-06 17:08:44</issue_when>
+ <thetext>distinct attribute for getProductFeatures service does not work because it is
+trying to use ProductFeatureType rather than ProductFeatureTypeId in
+ProductServices.prodGetFeatures</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-07-06 17:16:51</issue_when>
+ <thetext>Created an attachment (id=81)
+Corrects productFeatureType to productFeatureTypeId
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-07 01:18:38</issue_when>
+ <thetext>It&apos;s in CVS now, thanks Si!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>81</attachid>
+ <date>2004-07-06 17:16:51</date>
+ <desc>Corrects productFeatureType to productFeatureTypeId</desc>
+ <ispatch>1</ispatch>
+ <filename>ProductServices_1.patch</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/81/ProductServices_1.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-07-06 17:16:51</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=81)
+Corrects productFeatureType to productFeatureTypeId
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:18:38</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:18:38</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>159</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040707012137</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-07-06 17:26:05</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Adds productFeatureApplTypeId field to getProductFeatureSet service</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-07-06 17:26:05</issue_when>
+ <thetext>Adds productFeatureApplTypeId as optional field to getProductFeatureSet service.
+ If none is specified, it will still search for &quot;SELECTABLE_FEATURE&quot;s (to be
+compatible with existing code), but if one is specified, it will use that
+productFeatureApplTypeId, so the same service can now be used to search for
+STANDARD, DISTINGUISHING, OPTIONAL features.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-07-06 17:27:49</issue_when>
+ <thetext>Created an attachment (id=82)
+Extends getProductFeatures with optional productFeatureApplTypeId field
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-07 01:21:37</issue_when>
+ <thetext>It&apos;s in CVS now, thanks Si!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>82</attachid>
+ <date>2004-07-06 17:27:49</date>
+ <desc>Extends getProductFeatures with optional productFeatureApplTypeId field</desc>
+ <ispatch>1</ispatch>
+ <filename>ProductServices_2.patch</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/82/ProductServices_2.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-07-06 17:27:49</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=82)
+Extends getProductFeatures with optional productFeatureApplTypeId field
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:21:37</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 01:21:37</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>160</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040707144701</delta_ts>
+ <subcomponent>Entity Extensions</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-07-07 07:27:45</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>implement condition versions of functions in entityext</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-07 07:27:45</issue_when>
+ <thetext>I had sent an earlier version of this patch to the mailing list. I&apos;m now adding
+it as an issue, as I have for all the other patches.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-07 07:28:22</issue_when>
+ <thetext>Created an attachment (id=83)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-07 14:47:01</issue_when>
+ <thetext>This is now in CVS. Thanks Adam!</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>83</attachid>
+ <date>2004-07-07 07:28:22</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>cache-entityext.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/83/cache-entityext.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-07-07 07:28:22</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=83)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 14:47:01</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-07 14:47:01</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>161</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040708153407</delta_ts>
+ <subcomponent>WebTools</subcomponent>
+ <reporter>doogie</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-07-08 11:51:01</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>NPE in FindUtilCacheElements.jsp</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-08 11:51:01</issue_when>
+ <thetext>Missed one NPE when key is null(calls key.toString()).</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-08 11:51:22</issue_when>
+ <thetext>Created an attachment (id=84)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-08 15:34:07</issue_when>
+ <thetext>Thanks Adam, it is now in CVS.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>84</attachid>
+ <date>2004-07-08 11:51:22</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>FindUtilCacheElements.jsp.patch</filename>
+ <submitter_id>270</submitter_id>
+ <submitting_username>doogie</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/84/FindUtilCacheElements.jsp.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>doogie</user>
+ <when>2004-07-08 11:51:22</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=84)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-08 15:34:07</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-08 15:34:07</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>162</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>WORKSFORME</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040713170819</delta_ts>
+ <subcomponent>Workflow</subcomponent>
+ <reporter>ktippetts</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-07-12 09:32:42</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>Linux</op_sys>
+ <short_desc>NoSuchMethodError in Entity Data Maintenance</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ktippetts</who>
+ <issue_when>2004-07-12 09:32:42</issue_when>
+ <thetext>Getting this error when trying to view entity data in Webtools | Entity Data
+Maintenance:
+
+&lt;snip&gt;
+Exception: java.lang.NoSuchMethodError
+Message:
+org.ofbiz.entity.condition.EntityFieldMap.&lt;init&gt;(Ljava/util/Map;Lorg/ofbiz/entity/condition/EntityOperator;)V
+----- stack trace ----------------------------------------
+java.lang.NoSuchMethodError:
+org.ofbiz.entity.condition.EntityFieldMap.&lt;init&gt;(Ljava/util/Map;Lorg/ofbiz/entity/condition/EntityOperator;)V
+org.apache.jsp.entity.FindGeneric_jsp._jspService(FindGeneric_jsp.java:159)
+&lt;snip&gt;
+
+To duplicate Using CVS code as of 09Jul2004:
+* Webtools &gt; Entity Data Maintenance
+* Click &apos;All&apos; on any entity.
+
+Possible Solution:
+* in FindGeneric.jsp (line 94) change 2nd parameter to EntityJoinOperator.AND</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-07-12 20:41:10</issue_when>
+ <thetext>This works fine from a clean compile, but if have partial old code and partial new code in a build it may
+have problems. Try an &quot;ant clean&quot; and then an &quot;ant&quot;.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>doogie</who>
+ <issue_when>2004-07-13 17:08:19</issue_when>
+ <thetext>The signature of the constructor changed. It used to take an EntityOperator.
+However, the code only accepted an EntityJoinOperator. Passing in an
+EntityComparisonOperator would have caused it to fail.
+
+Also, running ant clean will *not* fix the problem. The parsed/compiled jsp
+pages need to be cleaned out.</thetext>
+ </long_desc>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-12 20:41:10</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-12 20:41:10</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>WORKSFORME</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>163</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Documentation</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040730150851</delta_ts>
+ <subcomponent>website</subcomponent>
+ <reporter>michaelrempel</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>TASK</issue_type>
+ <creation_ts>2004-07-13 11:11:43</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>cant get csv instructions</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>michaelrempel</who>
+ <issue_when>2004-07-13 11:11:43</issue_when>
+ <thetext>When I pull up csv instructions for either the wincsv or command line, I get a
+logon request screen, even though I am logged on.
+
+Thx
+
+Michael Rempel</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 15:08:51</issue_when>
+ <thetext>This is a java.net related issue not OFBiz; please contact the Java.Net team for help.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 15:08:51</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 15:08:51</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>164</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040730204811</delta_ts>
+ <subcomponent>Security</subcomponent>
+ <reporter>adrianc</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-07-14 12:24:23</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>SecurityData.xml File</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>adrianc</who>
+ <issue_when>2004-07-14 12:24:23</issue_when>
+ <thetext>From mailing list:
+
+On Jul 14, 2004, at 12:03 PM, Adrian Crum wrote:
+
+&gt; I was just wondering...
+&gt;
+&gt; Why is the security data for ALL components in the SecurityData.xml file? This
+file looks monolithic. Shouldn&apos;t each component load its own security data?
+&gt;
+&gt; -Adrian
+
+
+Adrian,
+
+Yes, good point, monolithic is a good word for this. Ideally it should be split
+up, I think that&apos;s a good idea.
+
+If anyone wants to work on this please do... In the mean time it might be a good
+idea to create an issue for it.
+
+-David</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 20:48:11</issue_when>
+ <thetext>applied patch file(s); now in CVS</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 20:48:11</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 20:48:11</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>165</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P4</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040730065021</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>pgoron</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-07-15 06:34:37</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allow user to specify default value for desired delivery date and comment fields in order entry GUI</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-07-15 06:34:37</issue_when>
+ <thetext>This patch allows user to define default desired delivery date and default
+comment during quickaddform filling in OrderEntry page. Thus, users are not
+obliged any more to reinput these informations for each order item.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-07-15 06:35:35</issue_when>
+ <thetext>Created an attachment (id=85)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-07-30 06:50:21</issue_when>
+ <thetext>The patch is in CVS.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>85</attachid>
+ <date>2004-07-15 06:35:35</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>order20040707.patch</filename>
+ <submitter_id>392</submitter_id>
+ <submitting_username>pgoron</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/85/order20040707.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>pgoron</user>
+ <when>2004-07-15 06:35:35</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=85)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-15 16:16:54</when>
+ <field_name>assigned_to</field_name>
+ <field_desc>Assigned To</field_desc>
+ <oldvalue>issues@ofbiz</oldvalue>
+ <newvalue>ajzeneski</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-07-15 16:16:54</when>
+ <field_name>issue_type</field_name>
+ <field_desc>Issue Type</field_desc>
+ <oldvalue>ENHANCEMENT</oldvalue>
+ <newvalue>PATCH</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-30 06:50:21</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-30 06:50:21</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>166</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040730115233</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>pgoron</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-07-21 02:46:56</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Allow user to associate parties to an order in OrderEntry website</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-07-21 02:46:56</issue_when>
+ <thetext>Hi,
+
+Currently, I did not found any way to associate additional parties to an order.
+That&apos;s why I&apos;ve written this enhancement patch.
+
+
+This patch adds a new step during order entry in which user can associate
+additional party&amp;role to order. Then, these informations are stored in OrderRole
+entity.
+
+
+List of modifications :
+- I&apos;ve written a new view in which user can select a party and party&apos;s role to
+associate to order
++ webapp/ordermgr/entry/SetAdditionalParty.ftl
++ webapp/ordermgr/WEB-INF/actions/entry/SetAdditionalParty.bsh
++ webapp/ordermgr/WEB-INF/pagedefs/entry/SetAdditionalParty.xml
++ webapp/ordermgr/entry/AdditionalPartyListing.ftl
++ webapp/ordermgr/WEB-INF/actions/entry/AdditionalPartyListing.bsh
++ webapp/ordermgr/entry/PartySettingsForm.xml
+M webapp/ordermgr/WEB-INF/controller.xml
+
+- I&apos;ve included list of additional parties associated to order in confimorder view
+M webapp/ordermgr/entry/confirmorder.ftl
+M webapp/ordermgr/WEB-INF/pagedefs/entry/confirmorder.xml
+
+- I&apos;ve modified these files so that my view is shown before order confirmation
+M src/org/ofbiz/order/shoppingcart/CheckOutEvents.java
+M webapp/ordermgr/entry/showcart.ftl
+
+- I&apos;ve modified storeOrder service and ShoppingCart to take in account
+additional parties.
+M servicedef/services.xml
+M src/org/ofbiz/order/order/OrderServices.java
+M src/org/ofbiz/order/shoppingcart/ShoppingCartEvents.java
+M src/org/ofbiz/order/shoppingcart/ShoppingCart.java
+
+- I&apos;ve copied some files from content module to be able to use GenericLookup.
++ src/org/ofbiz/order/lookupParams.java
++ webapp/ordermgr/lookup/FieldLookupForms.xml
++ webapp/ordermgr/lookup/GenericLookup.ftl
++ webapp/ordermgr/templates/lookup.ftl
++ webapp/ordermgr/templates/lookup.xml
++ webapp/ordermgr/WEB-INF/actions/includes/findprepLk.bsh
++ webapp/ordermgr/WEB-INF/actions/includes/formprep.bsh
++ webapp/ordermgr/WEB-INF/actions/includes/pagelistprepLk.bsh
++ webapp/ordermgr/WEB-INF/pagedefs/lookup/GenericLookup.xml</thetext>
+ </long_desc>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-07-21 02:47:42</issue_when>
+ <thetext>Created an attachment (id=86)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-07-22 00:38:39</issue_when>
+ <thetext>I&apos;ve found some minor mistakes. I am correcting them and I will send a new patch
+as soon as possible.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-07-22 01:29:07</issue_when>
+ <thetext>Created an attachment (id=87)
+The corrected patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jacopo</who>
+ <issue_when>2004-07-30 11:52:33</issue_when>
+ <thetext>The patches are in CVS.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>86</attachid>
+ <date>2004-07-21 02:47:42</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>order20040721.patch</filename>
+ <submitter_id>392</submitter_id>
+ <submitting_username>pgoron</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/86/order20040721.patch</attachment_iz_url>
+ </attachment>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>87</attachid>
+ <date>2004-07-22 01:29:07</date>
+ <desc>The corrected patch</desc>
+ <ispatch>1</ispatch>
+ <filename>order20040722.patch</filename>
+ <submitter_id>392</submitter_id>
+ <submitting_username>pgoron</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/87/order20040722.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>pgoron</user>
+ <when>2004-07-21 02:47:42</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=86)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>pgoron</user>
+ <when>2004-07-22 01:29:07</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=87)
+The corrected patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-30 11:52:33</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jacopo</user>
+ <when>2004-07-30 11:52:33</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>167</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040730145814</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-07-28 17:15:09</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Fixes a couple of other getNextSeqId methods</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-07-28 17:15:09</issue_when>
+ <thetext>getNextSeqId now returns a string(), so .toString() is no longer needed and will
+cause runtime exceptions.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-07-28 17:19:29</issue_when>
+ <thetext>Created an attachment (id=88)
+Changes getNextSeqId usage to PaymentGatewayServices and GenericAsyncEngine
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 14:58:14</issue_when>
+ <thetext>patch applied</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>88</attachid>
+ <date>2004-07-28 17:19:29</date>
+ <desc>Changes getNextSeqId usage to PaymentGatewayServices and GenericAsyncEngine</desc>
+ <ispatch>1</ispatch>
+ <filename>changes.txt</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/88/changes.txt</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-07-28 17:19:29</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=88)
+Changes getNextSeqId usage to PaymentGatewayServices and GenericAsyncEngine
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 14:58:14</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 14:58:14</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>168</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040730144804</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>pgoron</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-07-29 02:43:55</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>FreeMarker localization issues</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-07-29 02:43:55</issue_when>
+ <thetext>Hi,
+
+This patch resolves some localization issues. Currently, FreeMarker formats
+number according to the machine&apos;s locale. My patch forces FreeMarker to use the
+user session&apos;s locale.
+
+It must be applied in ofbiz/components/content/src/org/ofbiz/content/webapp/ftl
+folder.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-07-29 02:45:19</issue_when>
+ <thetext>Created an attachment (id=89)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 14:48:04</issue_when>
+ <thetext>This has been fixed in CVS</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>89</attachid>
+ <date>2004-07-29 02:45:19</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>locale-freemaker-200040727.patch</filename>
+ <submitter_id>392</submitter_id>
+ <submitting_username>pgoron</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/89/locale-freemaker-200040727.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>pgoron</user>
+ <when>2004-07-29 02:45:19</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=89)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 14:48:04</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 14:48:04</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>169</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>PC</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040730220642</delta_ts>
+ <subcomponent>E-Commerce</subcomponent>
+ <reporter>amheiss</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-07-30 09:53:11</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc>https://localhost:8443/ecommerce/control/finalizeOrder</issue_file_loc>
+ <votes></votes>
+ <op_sys>Linux</op_sys>
+ <short_desc>Party/Userlogin issues</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>amheiss</who>
+ <issue_when>2004-07-30 09:53:11</issue_when>
+ <thetext>Application will not allow run of createPartyPostalAddress (due to null user
+login?). Sequence of events from july 30th, 11am build: goto ecommerce,
+login, then logout. attempt to use anonymous checkout after this, and shipping
+address feilds prepopulated with previous logins settings. After adjusting and
+trying to save, get error &apos;User authorization is required for this service:
+createPartyPostalAddress &apos; .
+
+Debug output from shipsettings.bsh:
+Debug.log(&quot;party ID : &quot; + partyId); ---&gt; output admin ( i logged in as admin,
+then logged out)
+Debug.log(&quot;ul: &quot; + session.getAttribute(&quot;userLogin&quot;)); ---&gt; (null)</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-07-30 22:06:42</issue_when>
+ <thetext>Yes, if there is an autoUserLogin attribute available that will break anonymous checkout. In theory, the
+anonymous checkout links should not be displayed if there is an auto-userlogin. However, I have added
+some code to clear this when entering the anonymous checkout process.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 22:06:42</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-07-30 22:06:42</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>170</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040804093020</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>ENHANCEMENT</issue_type>
+ <creation_ts>2004-08-03 17:02:41</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Optionally duplicates ProductContent when duplicating a Product</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-08-03 17:02:41</issue_when>
+ <thetext>Will now optionally duplicate ProductContent entries when duplicating a Product</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-08-03 17:03:19</issue_when>
+ <thetext>Created an attachment (id=90)
+Patches services.xml, EditProduct.ftl, and ProductServices.xml
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-08-04 09:30:20</issue_when>
+ <thetext>Thanks Si. This is now in CVS. I also did a content remove option, and fixed an issue I noticed with the
+product IDs (GoodIdentification). BTW, this is from a slightly earlier version of OFBiz than the current
+CVS as this form has been moved to the file EditProductDupForm.ftl.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>90</attachid>
+ <date>2004-08-03 17:03:19</date>
+ <desc>Patches services.xml, EditProduct.ftl, and ProductServices.xml</desc>
+ <ispatch>1</ispatch>
+ <filename>duplicateProduct.patch</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/90/duplicateProduct.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-08-03 17:03:19</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=90)
+Patches services.xml, EditProduct.ftl, and ProductServices.xml
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-04 09:30:20</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-04 09:30:20</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>171</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P2</priority>
+ <resolution>FIXED</resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040804091929</delta_ts>
+ <subcomponent>Product</subcomponent>
+ <reporter>pgoron</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-08-04 00:59:53</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Comparaison bug in InventoryServices.xml minilang script</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-08-04 00:59:53</issue_when>
+ <thetext>Some if-compare element forget to declare type used to compare fields. By
+default, string comparison is used whereas Double is required.
+
+
+Applying patch :
+cd /ofbiz/components/product/script/org/ofbiz/product/inventory
+patch -p0 &lt; InventoryServices.xml.patch</thetext>
+ </long_desc>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-08-04 01:01:13</issue_when>
+ <thetext>Created an attachment (id=91)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-08-04 09:19:29</issue_when>
+ <thetext>Thanks, it&apos;s now in CVS.</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>91</attachid>
+ <date>2004-08-04 01:01:13</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>InventoryServices.xml.patch</filename>
+ <submitter_id>392</submitter_id>
+ <submitting_username>pgoron</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/91/InventoryServices.xml.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>pgoron</user>
+ <when>2004-08-04 01:01:13</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=91)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-04 09:19:29</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-04 09:19:29</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>172</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P2</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040819033400</delta_ts>
+ <subcomponent>Order</subcomponent>
+ <reporter>pgoron</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-08-05 07:09:49</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Bug in shippableWeight calculation from OrderReadHelper.getShippableWeight method</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-08-05 07:09:49</issue_when>
+ <thetext>Hi,
+
+The purpose of this patch is to solve a bug in
+OrderReadHelper.getShippableWeight method. The algorithm of shippable weight
+calculation don&apos;t care of order item&apos;s quantity. So there is an inconsistency
+with ShoppingCart.getShippableWeight method when order is modified after his
+creation (for example, by RecalcShippingTotal service).
+
+Applying patch:
+cd ofbiz/components/order/src/org/ofbiz/order/order
+patch -p0 &lt; OrderReadHelper-20040805.patch
+
+Peter Goron</thetext>
+ </long_desc>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-08-05 07:10:36</issue_when>
+ <thetext>Created an attachment (id=92)
+The patch
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>pgoron</who>
+ <issue_when>2004-08-19 03:34:00</issue_when>
+ <thetext>Created an attachment (id=96)
+An up to date patch
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>92</attachid>
+ <date>2004-08-05 07:10:36</date>
+ <desc>The patch</desc>
+ <ispatch>1</ispatch>
+ <filename>OrderReadHelper-20040805.patch</filename>
+ <submitter_id>392</submitter_id>
+ <submitting_username>pgoron</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/92/OrderReadHelper-20040805.patch</attachment_iz_url>
+ </attachment>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>96</attachid>
+ <date>2004-08-19 03:34:00</date>
+ <desc>An up to date patch</desc>
+ <ispatch>1</ispatch>
+ <filename>OrderReadHelper-200408019.patch</filename>
+ <submitter_id>392</submitter_id>
+ <submitting_username>pgoron</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/96/OrderReadHelper-200408019.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>pgoron</user>
+ <when>2004-08-05 07:10:36</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=92)
+The patch
+</newvalue>
+ </activity>
+ <activity>
+ <user>pgoron</user>
+ <when>2004-08-19 03:34:00</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=96)
+An up to date patch
+</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>173</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>INVALID</resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>PC</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040809165336</delta_ts>
+ <subcomponent>Startup</subcomponent>
+ <reporter>mn</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-08-09 13:38:08</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>Linux</op_sys>
+ <short_desc>typo in base/config/debug.properties</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>mn</who>
+ <issue_when>2004-08-09 13:38:08</issue_when>
+ <thetext>Index: base/config/debug.properties
+===================================================================
+RCS file: /cvs/ofbiz/base/config/debug.properties,v
+retrieving revision 1.10
+diff -u -r1.10 debug.properties
+--- base/config/debug.properties 9 Jun 2004 18:12:59 -0000 1.10
++++ base/config/debug.properties 9 Aug 2004 19:32:33 -0000
+@@ -7,7 +7,7 @@
+ pack.exception=true
+
+ # These top level switches are used before calling Log4J, or if Log4J is not used
+-print.verbose=ftrue
++print.verbose=true
+ print.timing=true
+ print.info=true
+ print.important=true</thetext>
+ </long_desc>
+ <long_desc>
+ <who>mn</who>
+ <issue_when>2004-08-09 13:40:09</issue_when>
+ <thetext>Created an attachment (id=93)
+corerct typo for print.verbose property
+</thetext>
+ </long_desc>
+ <long_desc>
+ <who>jonesde</who>
+ <issue_when>2004-08-09 16:25:29</issue_when>
+ <thetext>In this case &quot;ftrue&quot; is not a misspelling of &quot;true&quot;, we add the &quot;f&quot; before &quot;true&quot; so that it is easier to switch
+between true and false than it would be to type out &quot;true&quot; and &quot;false&quot;. In other words, anything but
+&quot;true&quot; is false.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>mn</who>
+ <issue_when>2004-08-09 16:53:36</issue_when>
+ <thetext>What about placing comment into debug.properties about this decision?</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>93</attachid>
+ <date>2004-08-09 13:40:09</date>
+ <desc>corerct typo for print.verbose property</desc>
+ <ispatch>1</ispatch>
+ <filename>debug.properties.patch</filename>
+ <submitter_id>377</submitter_id>
+ <submitting_username>mn</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/93/debug.properties.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>mn</user>
+ <when>2004-08-09 13:40:09</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=93)
+corerct typo for print.verbose property
+</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-09 16:25:29</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>jonesde</user>
+ <when>2004-08-09 16:25:29</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>INVALID</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>174</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040810161004</delta_ts>
+ <subcomponent>Accounting</subcomponent>
+ <reporter>amheiss</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>PATCH</issue_type>
+ <creation_ts>2004-08-10 16:08:36</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>retry failed auths fix</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>amheiss</who>
+ <issue_when>2004-08-10 16:08:36</issue_when>
+ <thetext>retryFailedAuths service was creating superfluous retryFailedOrderAuth
+services. I&apos;ll see if i can attach the patch to check on order status before
+dispatching retryFailedOrderAuth service. If not, patch will be to dev list
+soon enough.</thetext>
+ </long_desc>
+ <long_desc>
+ <who>amheiss</who>
+ <issue_when>2004-08-10 16:10:04</issue_when>
+ <thetext>Created an attachment (id=94)
+Patch for retryFailedAuth fix
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>94</attachid>
+ <date>2004-08-10 16:10:04</date>
+ <desc>Patch for retryFailedAuth fix</desc>
+ <ispatch>1</ispatch>
+ <filename>retryAuth.patch</filename>
+ <submitter_id>336</submitter_id>
+ <submitting_username>amheiss</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/94/retryAuth.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>amheiss</user>
+ <when>2004-08-10 16:10:04</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=94)
+Patch for retryFailedAuth fix
+</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>175</issue_id>
+ <issue_status>RESOLVED</issue_status>
+ <priority>P3</priority>
+ <resolution>FIXED</resolution>
+ <component>foo</component>
+ <version>unspecified</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>ajzeneski</assigned_to>
+ <delta_ts>20040817213241</delta_ts>
+ <subcomponent>bar</subcomponent>
+ <reporter>ajzeneski</reporter>
+ <target_milestone>milestone 1</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-08-17 21:31:38</creation_ts>
+ <qa_contact>ajzeneski</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>bar</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-08-17 21:31:38</issue_when>
+ <thetext>foo</thetext>
+ </long_desc>
+ <long_desc>
+ <who>ajzeneski</who>
+ <issue_when>2004-08-17 21:32:41</issue_when>
+ <thetext>.</thetext>
+ </long_desc>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-08-17 21:32:41</when>
+ <field_name>issue_status</field_name>
+ <field_desc>Status</field_desc>
+ <oldvalue>NEW</oldvalue>
+ <newvalue>RESOLVED</newvalue>
+ </activity>
+ <activity>
+ <user>ajzeneski</user>
+ <when>2004-08-17 21:32:41</when>
+ <field_name>resolution</field_name>
+ <field_desc>Resolution</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>FIXED</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>176</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Components</component>
+ <version>CVS</version>
+ <rep_platform>All</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040818174622</delta_ts>
+ <subcomponent>Content</subcomponent>
+ <reporter>sichen</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>DEFECT</issue_type>
+ <creation_ts>2004-08-18 17:45:44</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>All</op_sys>
+ <short_desc>Possible bug in renderContentAsText</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-08-18 17:45:44</issue_when>
+ <thetext>It seems that renderContentAsText is still trying to do a findByAnd on
+SubContentDataResourceView with a fromDate as the orderBy, whereas
+renderContentAsTextCache no longer does (it passes a null.) We got an
+exception from this in ProductContentWrapper and made this change to fix it.
+Is this a defect?</thetext>
+ </long_desc>
+ <long_desc>
+ <who>sichen</who>
+ <issue_when>2004-08-18 17:46:22</issue_when>
+ <thetext>Created an attachment (id=95)
+Changes findByAnd of SubContentDataResourceView
+</thetext>
+ </long_desc>
+ <attachment encoding='Base64'>
+ <mimetype>text/plain</mimetype>
+ <attachid>95</attachid>
+ <date>2004-08-18 17:46:22</date>
+ <desc>Changes findByAnd of SubContentDataResourceView</desc>
+ <ispatch>1</ispatch>
+ <filename>ContentWorker.patch</filename>
+ <submitter_id>178</submitter_id>
+ <submitting_username>sichen</submitting_username>
+ <data></data>
+ <attachment_iz_url>https://ofbiz.dev.java.net/nonav/issues/showattachment.cgi/95/ContentWorker.patch</attachment_iz_url>
+ </attachment>
+ <activity>
+ <user>sichen</user>
+ <when>2004-08-18 17:46:22</when>
+ <field_name>attachments.ispatch</field_name>
+ <field_desc>Attachment is Patch</field_desc>
+ <oldvalue></oldvalue>
+ <newvalue>Created an attachment (id=95)
+Changes findByAnd of SubContentDataResourceView
+</newvalue>
+ </activity>
+</issue>
+<issue status_code="200" status_message="OK">
+ <issue_id>177</issue_id>
+ <issue_status>NEW</issue_status>
+ <priority>P3</priority>
+ <resolution></resolution>
+ <component>Base</component>
+ <version>CVS</version>
+ <rep_platform>Other</rep_platform>
+ <assigned_to>issues@ofbiz</assigned_to>
+ <delta_ts>20040910075014</delta_ts>
+ <subcomponent>Base</subcomponent>
+ <reporter>lrawley</reporter>
+ <target_milestone>not determined</target_milestone>
+ <issue_type>TASK</issue_type>
+ <creation_ts>2004-09-10 07:50:14</creation_ts>
+ <qa_contact>issues@ofbiz</qa_contact>
+ <status_whiteboard></status_whiteboard>
+ <issue_file_loc></issue_file_loc>
+ <votes></votes>
+ <op_sys>Windows XP</op_sys>
+ <short_desc>HTTP Header needs Expires</short_desc>
+ <keywords></keywords>
+ <long_desc>
+ <who>lrawley</who>
+ <issue_when>2004-09-10 07:50:14</issue_when>
+ <thetext>Using Internet Explorer 6.0 with settings for automatically obtaining newer
+versions of page.
+
+Images are cached and if changed are not refreshed on visits to the same page.
+Pages get cached with login page because the first time a user requested they
+were prompted to login first and then redirected to the page.
+
+This behavior is resolved if set to obtain newer versions on every visit,
+however, this causes performance issues. So, automatically is the desired
+browser setting.
+
+I would like to be able to set the EXPIRE in the HTTP header. I&apos;m not sure
+where this change needs to occur. Can you assist? Is this available in 3.0?
+I am using 2.1.1.
+
+Please contact me at lrawley@tla.com.
+
+Lisa Rawley</thetext>
+ </long_desc>
+</issue>
+</issuezilla>
diff --git a/jni/ruby/test/rexml/data/pi.xml b/jni/ruby/test/rexml/data/pi.xml
new file mode 100644
index 0000000..980bbf9
--- /dev/null
+++ b/jni/ruby/test/rexml/data/pi.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<foo>
+ <?cheese is tasty?>
+ <bar>
+ <baz/>
+ <cheese/>
+ <baz/>
+ <?toast is tasty?>
+ <cheese/>
+ <baz/>
+ </bar>
+ <?cheese is gooey?>
+</foo>
diff --git a/jni/ruby/test/rexml/data/pi2.xml b/jni/ruby/test/rexml/data/pi2.xml
new file mode 100644
index 0000000..46bce45
--- /dev/null
+++ b/jni/ruby/test/rexml/data/pi2.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<a>
+ <b>foo</b>
+ <?toc order-by="x"?>
+ <c>bar</c>
+</a>
diff --git a/jni/ruby/test/rexml/data/project.xml b/jni/ruby/test/rexml/data/project.xml
new file mode 100644
index 0000000..a02582f
--- /dev/null
+++ b/jni/ruby/test/rexml/data/project.xml
@@ -0,0 +1 @@
+<Project id="17" Name="dave test" Deprecated="false"><Creator User="etools" Date="10/24/00 10:54 AM"></Creator><LastModifier User="Default" Date="Fri Jun 22 14:01:54 PDT 2001"></LastModifier><Description>tool testing</Description><Purpose> </Purpose><Datasets><link name="Test data 1" idref="18"></link><link name="veg plot data" idref="21"></link></Datasets></Project>
diff --git a/jni/ruby/test/rexml/data/simple.xml b/jni/ruby/test/rexml/data/simple.xml
new file mode 100644
index 0000000..3cff71a
--- /dev/null
+++ b/jni/ruby/test/rexml/data/simple.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" ?>
+<root><a>a</a><b>b</b><c><d>d</d></c></root>
diff --git a/jni/ruby/test/rexml/data/stream_accents.xml b/jni/ruby/test/rexml/data/stream_accents.xml
new file mode 100644
index 0000000..6def54e
--- /dev/null
+++ b/jni/ruby/test/rexml/data/stream_accents.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<g>
+<f a="" />
+</g>
diff --git a/jni/ruby/test/rexml/data/t63-1.xml b/jni/ruby/test/rexml/data/t63-1.xml
new file mode 100644
index 0000000..f8d0c54
--- /dev/null
+++ b/jni/ruby/test/rexml/data/t63-1.xml
Binary files differ
diff --git a/jni/ruby/test/rexml/data/t63-2.svg b/jni/ruby/test/rexml/data/t63-2.svg
new file mode 100644
index 0000000..706c0e9
--- /dev/null
+++ b/jni/ruby/test/rexml/data/t63-2.svg
@@ -0,0 +1,2828 @@
+<?xml version="1.0" encoding="UTF-16"?>
+<svg height="647px" width="1024px" viewBox="-800000 -505508 1600000 1011016" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:rgr="http://www.abbnm.com/ranger">
+ <defs>
+ <font id="pedDef" horiz-adv-x="1470">
+ <font-face font-family="PED2" units-per-em="2048" panose-1="0 0 0 0 0 0 0 0 0 0" ascent="1536" descent="-348" alphabetic="0"/>
+ <missing-glyph horiz-adv-x="1024"/>
+ <glyph unicode="!" glyph-name="down-arrow" horiz-adv-x="848" d="M14 819L424 0L834 819L526 614V1434H322V614L14 819Z"/>
+ <glyph unicode="!" glyph-name="right-arrow" horiz-adv-x="1460" d="M625 410L1444 819L625 1229L829 922H10V717H829L625 410Z"/>
+ <glyph unicode="!" glyph-name="left-arrow" horiz-adv-x="1460" d="M836 1229L16 819L836 410L631 717H1450V922H631L836 1229Z"/>
+ <glyph unicode="!" glyph-name="up-arrow" horiz-adv-x="848" d="M834 614L424 1434L14 614L322 819V0H526V819L834 614Z"/>
+ <glyph unicode="!" glyph-name="ne-arrow" horiz-adv-x="1118" d="M809 360L1098 1229L229 940L592 868L12 289L158 143L737 723L809 360Z"/>
+ <glyph unicode="!" glyph-name="se-arrow" horiz-adv-x="1118" d="M229 494L1098 205L809 1073L737 711L158 1290L12 1145L592 565L229 494Z"/>
+ <glyph unicode="!" glyph-name="nw-arrow" horiz-adv-x="1118" d="M889 940L20 1229L309 360L381 723L961 143L1106 289L526 868L889 940Z"/>
+ <glyph unicode="!" glyph-name="sw-arrow" horiz-adv-x="1118" d="M309 1073L20 205L889 494L526 565L1106 1145L961 1290L381 711L309 1073Z"/>
+ <glyph unicode="%" glyph-name="vert-bar" horiz-adv-x="2355" d="M1229 -410H1126V1843H1229V-410Z"/>
+ <glyph unicode="%" glyph-name="sw-corner" horiz-adv-x="2355" d="M1126 1843V614H2355V717H1229V1843H1126Z"/>
+ <glyph unicode="%" glyph-name="horiz-bar" horiz-adv-x="2355" d="M0 614H2355V717H0V614Z"/>
+ <glyph unicode="%" glyph-name="right-t" horiz-adv-x="2355" d="M1229 614H2355V717H1229V1843H1126V-410H1229V614Z"/>
+ <!-- The following glyphs reside in the private use area of the Unicode standard -->
+ <glyph unicode="" glyph-name="thin-rt-arrow" horiz-adv-x="1808" d="M1741 666L1024 307L1229 614H0V717H1229L1024 1024L1741 666Z"/>
+ <glyph unicode="" glyph-name="ground" horiz-adv-x="1128" d="M463 -51H666V-51V51H463V-51V-51ZM819 307V205H309V307H819ZM973 563V461H156V563H973ZM2 717H1126V819H614V1434H514V819H2V717Z"/>
+ <glyph unicode="" glyph-name="ground-right" horiz-adv-x="1509" d="M1491 666V868V868H1389V666H1491V666ZM1133 1022H1235V512H1133V1022ZM877 1176H979V358H877V1176ZM723 205V1329H621V817H6V717H621V205H723Z"/>
+ </font>
+ <g id="symbol639">
+ <title>SYMBOL 640</title>
+ <g stroke="none" transform=" translate(27, -27)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">OFF</text>
+ </g>
+ </g>
+ <g id="symbol641">
+ <title>SYMBOL 642</title>
+ <g stroke="none">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">NORMAL</text>
+ </g>
+ </g>
+ <g id="symbol642">
+ <title>SYMBOL 643</title>
+ <g stroke="none" transform=" translate(27, -27)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">ARMED</text>
+ </g>
+ </g>
+ <g id="symbol643">
+ <title>SYMBOL 644</title>
+ <g stroke="none">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">ALARM</text>
+ </g>
+ </g>
+ <g id="symbol645">
+ <title>SYMBOL 646</title>
+ <g stroke="none">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">FAILED</text>
+ </g>
+ </g>
+ <g id="symbol651">
+ <title>SYMBOL 652</title>
+ <g stroke="none" transform=" translate(27, -27)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">ON</text>
+ </g>
+ </g>
+ <g id="symbol659">
+ <title>SYMBOL 660</title>
+ <g stroke="none" transform=" translate(27, -27)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">AUTO</text>
+ </g>
+ </g>
+ <g id="symbol674">
+ <title>SYMBOL 675</title>
+ <g stroke="none">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">MARGINAL</text>
+ </g>
+ </g>
+ <g id="symbol834">
+ <title>SYMBOL 835</title>
+ <g stroke="none" transform=" translate(0, 0)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">TRIPPED</text>
+ </g>
+ </g>
+ <g id="symbol921">
+ <title>SYMBOL 922</title>
+ <g stroke="none">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">MANUAL</text>
+ </g>
+ </g>
+ <g id="symbol1679">
+ <title>Select</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">S</text>
+ </g>
+ </g>
+ <g id="symbol1680">
+ <title>Unack</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">U</text>
+ </g>
+ </g>
+ <g id="symbol1681">
+ <title>Alarm</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">A</text>
+ </g>
+ </g>
+ <g id="symbol1682">
+ <title>Deact</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">D</text>
+ </g>
+ </g>
+ <g id="symbol1683">
+ <title>Alinh</title>
+ <g stroke="none" transform=" translate(-1790, 6666)">
+ <text font-size="24357" font-family="PED2, Times New Roman" xml:space="preserve">I</text>
+ </g>
+ </g>
+ <g id="symbol1684">
+ <title>Telem</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">E</text>
+ </g>
+ </g>
+ <g id="symbol1685">
+ <title>Manual</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">M</text>
+ </g>
+ </g>
+ <g id="symbol1686">
+ <title>TagInfo</title>
+ <g stroke="none" transform=" translate(-1751, 2911)">
+ <text font-size="9955" font-family="PED2, Times New Roman" xml:space="preserve">I</text>
+ </g>
+ <g fill="none" stroke-width="1000" style=" stroke-dasharray: 16000 0 ;">
+ <polygon points="-5833,-335 -509,5833 4659,-335 -509,-6326 -5833,-335 ">
+ </polygon>
+ </g>
+ </g>
+ <g id="symbol1687">
+ <title>TagCtl</title>
+ <g fill="none" stroke-width="1000" style=" stroke-dasharray: 16000 0 ;">
+ <polygon points="-5000,81 323,6249 5493,81 323,-5909 -5000,81 ">
+ </polygon>
+ </g>
+ <g stroke="none" transform=" translate(-3418, 3327)">
+ <text font-size="9955" font-family="PED2, Arial" xml:space="preserve">N</text>
+ </g>
+ </g>
+ <g id="symbol1688">
+ <title>TagCls</title>
+ <g fill="none" stroke-width="1000" style=" stroke-dasharray: 16000 0 ;">
+ <polygon points="-5000,81 323,6249 5493,81 323,-5909 -5000,81 ">
+ </polygon>
+ </g>
+ <g stroke="none" transform=" translate(-3418, 3327)">
+ <text font-size="9955" font-family="PED2, Arial" xml:space="preserve">H</text>
+ </g>
+ </g>
+ <g id="symbol1689">
+ <title>TagOpn</title>
+ <g stroke="none" transform=" translate(-3418, 3327)">
+ <text font-size="9955" font-family="PED2, Arial" xml:space="preserve">O</text>
+ </g>
+ <g fill="none" stroke-width="1000" style=" stroke-dasharray: 16000 0 ;">
+ <polygon points="-5000,81 323,6249 5493,81 323,-5909 -5000,81 ">
+ </polygon>
+ </g>
+ </g>
+ <g id="symbol1690">
+ <title>RDV</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">R</text>
+ </g>
+ </g>
+ <g id="symbol1691">
+ <title>Gmerr</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">G</text>
+ </g>
+ </g>
+ <g id="symbol1692">
+ <title>Limovr</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">O</text>
+ </g>
+ </g>
+ <g id="symbol1693">
+ <title>Limit1</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">1</text>
+ </g>
+ </g>
+ <g id="symbol1694">
+ <title>Kimit2</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">2</text>
+ </g>
+ </g>
+ <g id="symbol1695">
+ <title>Limit3</title>
+ <g stroke="none" transform=" translate(-8456, 6666)">
+ <text font-size="24357" font-family="PED2, Arial" xml:space="preserve">3</text>
+ </g>
+ </g>
+ </defs>
+ <rgr:update display="A118TH-SO1">
+ </rgr:update>
+ <rgr:timestamp date="10301" time="57116000">
+ </rgr:timestamp>
+ <rect id="background" x="-800000" y="-505508" height="1011016" width="1600000" fill="#000000">
+ </rect>
+ <g id="layer32">
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -313333)">
+ <text font-size="18018" font-family="PED2, Arial" xml:space="preserve">03/16/06 ADDED DFK 6 -- K. POPE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -353333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">01/05/05 FIXED DFK'S -- K. POPE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -373333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">10/14/04 CHANGED ACRONYM FOR REG CONTROL TO MANUAL / AUTO -- O. WAHLSTROM</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -473333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">12/24/03 DISPLAY BUILT AND DATA ADDED -- K. POPE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -453333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">01/06/04 DISPLAY CORRECTED PER R. MCCORMICK -- N. FISHER</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -433333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">03/18/04 DISPLAY UPDATED/DFK'S FIXED/APPENDED SYMBOLS MOVED -- T. TURNER</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -413333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">03/29/04 REVIEWED -- O. WAHLSTROM</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -393333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">05/11/04 RTU BOX FIXED AND DISPLAY UPDATED -- K. POPE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-780000, -333333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">10/18/05 REMOVED BUS VOLT MAXIMUM ALARM -- K. POPE</text>
+ </g>
+ </g>
+ </g>
+ <g id="layer1">
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-179999, -466666)">
+ <text font-size="30029" font-family="PED2, Arial" xml:space="preserve">118TH SOUTH (118TH-SO)</text>
+ </g>
+ </g>
+ <g>
+ <g fill="none" stroke-width="1000" style=" stroke-dasharray: 16000 0 ;" stroke="#e1d3b3">
+ <rect x="-753333" y="-500000" height="46666" width="1506666">
+ </rect>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(481450, -466666)">
+ <text font-size="25825" font-family="PED2, Arial" xml:space="preserve">Salt Lake County, Utah</text>
+ </g>
+ </g>
+ <g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#e1d3b3">
+ <rect x="-753333" y="-26666" height="520791" width="1506666">
+ </rect>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-600000, 180000)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">REG 1 AUTOMATIC (C)</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-600000, 73333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">To control regulator, right click on the tap position</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-600000, 100000)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">indication and select the regulation option.</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(19999, -226666)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 TEMPERATURE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(19999, -199999)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 NITROGEN CYL LOW</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(19999, -253333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 LOW OIL LEVEL</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(19999, -173333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 NITROGEN PRESSURE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(20000, -146666)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 PRESSURE FAULT</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(20000, -119999)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 LTC CONTROL LOSS</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -93333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">RELAY LOSS OF AC</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -119999)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">STATION SERVICE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(20000, -279999)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 LTC FAIL</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(20000, -306666)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 WINDING TEMP TRIP</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(20000, -333333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 LOCKOUT</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -253333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">CONTROL SYSTEM FAIL</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -279999)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">UNDER FREQ TRIP RELAY</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -306666)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">CB LOW SPRING CHARGE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -333333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">RELAY FAILURE</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -146666)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">CAP 1 VOLT CONTROL (C)</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -173333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">HMI PLC STALLED</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -199999)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">XFMR 1 LTC FILTER</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-406666, -226666)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">CONTROL SYSTEM NON CRITICAL</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-86666, -406666)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">Station Alarms</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-140000, 20000)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">Regulator 1 Control and Tap Position</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-600000, 213333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">REG 1 TAP POSITION</text>
+ </g>
+ </g>
+ <g transform="translate(-260000, 180000)" id="elm139">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol921" stroke="#ff0000" fill="#ff0000" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol659" stroke="#00ffff" fill="#00ffff" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(77484, -6019) scale(0.678085)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(91920, -6019) scale(0.678085)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="REG 1 AUTOMATIC">
+ </rgr:ranger>
+ </g>
+ <g>
+ <g stroke="none" fill="#ba8864">
+ <polygon points="-59607,187500 -59607,212499 59607,212499 59607,187500 -59607,187500 ">
+ </polygon>
+ </g>
+ <g stroke="none" fill="#ffedb5">
+ <polygon points="63333,183333 -63333,183333 -59607,187500 59607,187500 63333,183333 ">
+ </polygon>
+ </g>
+ <g stroke="none" fill="#96825a">
+ <polyline points="63333,216666 59607,212499 59607,187500 63333,183333 ">
+ </polyline>
+ </g>
+ <g stroke="none" fill="#ffedb5">
+ <polygon points="-63333,183333 -59607,187500 -59607,212499 -63333,216666 -63333,183333 ">
+ </polygon>
+ </g>
+ <g stroke="none" fill="#96825a">
+ <polyline points="-63333,216666 -59607,212500 59607,212500 63333,216666 ">
+ </polyline>
+ </g>
+ </g>
+ <g id="elm141">
+ <g id="appended" transform="translate(-266371, 214890)">
+ <g transform="translate(33332, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(43242, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <g stroke="none" fill="#00ffff" transform=" translate(-266371, 214890)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">- 12 </text>
+ </g>
+ <rgr:ranger type="analog" presentation="numeric" leftJust="false" pos=" " neg="-" substation="118TH.SO" point="REG 1 TAP POSITION">
+ </rgr:ranger>
+ </g>
+ <g id="elm142">
+ <g id="appended" transform="translate(-366666, 400000)">
+ <g transform="translate(66365, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(76274, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <g stroke="none" fill="#00ffff" transform=" translate(-366666, 400000)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">- 1234.6 </text>
+ </g>
+ <rgr:ranger type="analog" presentation="numeric" leftJust="false" pos=" " neg="-" substation="118TH.SO" point="REG 1 A.PH 12KV BV">
+ </rgr:ranger>
+ </g>
+ <g id="elm143">
+ <g id="appended" transform="translate(-199999, 400000)">
+ <g transform="translate(66365, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(76274, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <g stroke="none" fill="#00ffff" transform=" translate(-199999, 400000)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">- 1234.6 </text>
+ </g>
+ <rgr:ranger type="analog" presentation="numeric" leftJust="false" pos=" " neg="-" substation="118TH.SO" point="REG 1 B.PH 12KV BV">
+ </rgr:ranger>
+ </g>
+ <g id="elm144">
+ <g id="appended" transform="translate(-39999, 400000)">
+ <g transform="translate(66365, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(76274, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <g stroke="none" fill="#00ffff" transform=" translate(-39999, 400000)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">- 1234.6 </text>
+ </g>
+ <rgr:ranger type="analog" presentation="numeric" leftJust="false" pos=" " neg="-" substation="118TH.SO" point="REG 1 C.PH 12KV BV">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -119999)" id="elm145">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 LTC CONTRL LOSS">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -146666)" id="elm146">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 PRESSURE FAULT">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -173333)" id="elm147">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 NITROGEN PRESS.">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -199999)" id="elm148">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 NITROGEN CYL LO">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -226666)" id="elm149">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 TEMP">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -253333)" id="elm150">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 LOW OIL LEVEL">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -333333)" id="elm151">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 LOCKOUT">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -306666)" id="elm152">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 WINDING TEMP">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(306666, -279999)" id="elm153">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 LTC FAIL">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-120000, -93333)" id="elm154">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="RELAY LOSS OF AC">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119999, -119999)" id="elm155">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="STATION SERVICE">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119704, -146666)" id="elm156">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol639" stroke="#ff0000" fill="#ff0000" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol651" stroke="#00ffff" fill="#00ffff" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(44470, -6033) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(58885, -6033) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="CAP 1 VOLT CTRL">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119999, -173333)" id="elm157">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="HMI PLC STALLED">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119999, -199999)" id="elm158">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="XFMR 1 LTC FILTER">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119999, -226666)" id="elm159">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="CTRL SYSTEM NON CRIT">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119999, -253333)" id="elm160">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="CTRL SYSTEM FAIL">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119999, -279999)" id="elm161">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="UNDER FREQ TRIP RELAY">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119999, -306666)" id="elm162">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="CB LOW SPRING CHARGE">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-119999, -333333)" id="elm163">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(80479, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(94893, -6005) scale(0.677063)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="RELAY FAIL">
+ </rgr:ranger>
+ </g>
+ <g id="elm164">
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 49201)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">16</text>
+ </g>
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 99717)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">12</text>
+ </g>
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 150234)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">8</text>
+ </g>
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 200750)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">4</text>
+ </g>
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 251267)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">0</text>
+ </g>
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 301783)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">-4</text>
+ </g>
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 352300)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">-8</text>
+ </g>
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 402816)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">-12</text>
+ </g>
+ <g stroke="none" fill="#bfbfbf" transform=" translate(476402, 453333)">
+ <text font-size="29514" font-family="PED2, Arial" xml:space="preserve">-16</text>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="414435,445954 414435,41822 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,41822 414435,41822 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,92339 414435,92339 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,142855 414435,142855 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,193372 414435,193372 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,243888 414435,243888 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,294405 414435,294405 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,344921 414435,344921 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,395438 414435,395438 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="462930,445954 414435,445954 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,54451 414435,54451 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,67081 414435,67081 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,79710 414435,79710 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,104968 414435,104968 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,117597 414435,117597 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,130226 414435,130226 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,155484 414435,155484 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,168113 414435,168113 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,180743 414435,180743 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,206001 414435,206001 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,218630 414435,218630 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,231259 414435,231259 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,256517 414435,256517 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,269146 414435,269146 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,281776 414435,281776 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,307034 414435,307034 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,319663 414435,319663 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,332292 414435,332292 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,357550 414435,357550 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,370179 414435,370179 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,382808 414435,382808 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,408067 414435,408067 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,420696 414435,420696 ">
+ </polyline>
+ </g>
+ <g fill="none" stroke-width="2000" style=" stroke-dasharray: 16000 0 ;" stroke="#bfbfbf">
+ <polyline points="438683,433325 414435,433325 ">
+ </polyline>
+ </g>
+ <rgr:ranger type="analog" presentation="meter" min="-16.000000" max="16.000000" propagationDir="0" displayDir="1" substation="118TH.SO" point="REG 1 TAP POSITION">
+ </rgr:ranger>
+ <g fill="none" stroke="#ff0000" stroke-width="2000">
+ <polyline points="0,0 0,0 0,0 0,0 0,0">
+ </polyline>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-599290, 400000)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">REG 1 12KV BUS VOLTS</text>
+ </g>
+ </g>
+ <g id="elm166">
+ <g id="appended" transform="translate(-26371, 208223)">
+ <g transform="translate(33332, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(43242, -6005) scale(0.677048)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <g stroke="none" fill="#1a1a1a" transform=" translate(-26371, 208223)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">- 12 </text>
+ </g>
+ <rgr:ranger type="analog" presentation="numeric" leftJust="false" pos=" " neg="-" substation="118TH.SO" point="REG 1 TAP POSITION">
+ </rgr:ranger>
+ </g>
+ <g transform="translate(-506666, -353333)" id="elm167">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol834" stroke="#ff0000" fill="#ff0000" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol642" stroke="#00ffff" fill="#00ffff" visibility="hidden">
+ </use>
+ </g>
+ <g id="appended">
+ <g transform="translate(83490, -6019) scale(0.678085)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ <g transform="translate(97926, -6019) scale(0.678085)">
+ <use visibility="hidden" xlink:href="#symbol1679" stroke="#ffea00" fill="#ffea00">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1680" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1681" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1682" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1683" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1684" stroke="#377800" fill="#377800">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1685" stroke="#00ffff" fill="#00ffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1686" stroke="#ffffff" fill="#ffffff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1687" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1688" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1689" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1690" stroke="#5555ff" fill="#5555ff">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1691" stroke="#fe00fe" fill="#fe00fe">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1692" stroke="#ff6e05" fill="#ff6e05">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1693" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1694" stroke="#ff0000" fill="#ff0000">
+ </use>
+ <use visibility="hidden" xlink:href="#symbol1695" stroke="#ff0000" fill="#ff0000">
+ </use>
+ </g>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="118TH.SO" point="ENTRY">
+ </rgr:ranger>
+ </g>
+ <g>
+ <g stroke="none" fill="#ffffff" transform=" translate(-740000, -393333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">RTU STATUS:</text>
+ </g>
+ </g>
+ <g transform="translate(-506666, -393333)" id="elm169">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol639" stroke="#ff0000" fill="#ff0000" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol651" stroke="#00ffff" fill="#00ffff" visibility="hidden">
+ </use>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="RTUERR.SE" point="481">
+ </rgr:ranger>
+ </g>
+ <g>
+ <g stroke="none" fill="#ffffff" transform=" translate(-740000, -373333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">RTU COMMUNICATIONS:</text>
+ </g>
+ </g>
+ <g>
+ <g stroke="none" fill="#eeeeee" transform=" translate(-740000, -353333)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">ENTRY</text>
+ </g>
+ </g>
+ <g>
+ <g fill="none" stroke-width="1000" style=" stroke-dasharray: 16000 0 ;" stroke="#e1d3b3">
+ <rect x="-753333" y="-446666" height="106666" width="339999">
+ </rect>
+ </g>
+ </g>
+ <g>
+ <g fill="none" stroke-width="1000" style=" stroke-dasharray: 16000 0 ;" stroke="#e1d3b3">
+ <polyline points="-413333,-419999 -753333,-419999 ">
+ </polyline>
+ </g>
+ </g>
+ <g transform="translate(-506666, -373333)" id="elm174">
+ <g id="status" transform="translate(0, 0)">
+ <use xlink:href="#symbol645" stroke="#ff0000" fill="#ff0000" visibility="inherit">
+ </use>
+ <use xlink:href="#symbol641" stroke="#00ffff" fill="#00ffff" visibility="hidden">
+ </use>
+ <use xlink:href="#symbol674" stroke="#ffea00" fill="#ffea00" visibility="hidden">
+ </use>
+ <use xlink:href="#symbol643" stroke="#ff0000" fill="#ff0000" visibility="hidden">
+ </use>
+ </g>
+ <rgr:ranger type="discrete" pos="+" neg="-" presentation="symbolic" substation="RTUERR.COM" point="481">
+ </rgr:ranger>
+ </g>
+ <g>
+ <g stroke="none" fill="#e1d3b3" transform=" translate(-626666, -426666)">
+ <text font-size="18017" font-family="PED2, Arial" xml:space="preserve">RTU - 481</text>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/jni/ruby/test/rexml/data/t75.xml b/jni/ruby/test/rexml/data/t75.xml
new file mode 100644
index 0000000..0911fb1
--- /dev/null
+++ b/jni/ruby/test/rexml/data/t75.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="ISO-8859-1"?><?pos="3"?>
+<!-- generated by hnb 1.9.17 (http://hnb.sourceforge.net) -->
+
+<!DOCTYPE tree[
+ <!ELEMENT tree (node*)>
+ <!ELEMENT data (#PCDATA)> <!-- (max 4096 bytes long) -->
+ <!ELEMENT node (data?,node*)>
+ <!ATTLIST node done (yes|no) #IMPLIED
+ type CDATA #IMPLIED
+ >]>
+
+<tree>
+<node done="no" type="todo"><data>Next_Actions</data>
+ <node done="no" type="todo"><data>@class</data></node>
+ <node done="no" type="todo"><data>@agenda</data>
+ <node done="yes" type="todo"><data>this is something I&apos;d like to do</data></node>
+ </node>
+ <node done="no" type="todo"><data>@dorm</data>
+ <node done="no" type="todo"><data>clean room</data></node>
+ </node>
+ <node done="no" type="todo"><data>@computer</data>
+ <node done="no" type="todo"><data>Write general makefile for cs projects</data></node>
+ <node done="no" type="todo"><data>Set up bash podder</data></node>
+ </node>
+ <node done="no" type="todo"><data>@errands</data>
+ <node done="no" type="todo"><data>Purchase geo lab book</data></node>
+ </node>
+ <node done="no" type="todo"><data>@dublin</data></node>
+</node>
+<node><data>projects</data></node>
+</tree>
diff --git a/jni/ruby/test/rexml/data/test/tests.xml b/jni/ruby/test/rexml/data/test/tests.xml
new file mode 100644
index 0000000..cf03b42
--- /dev/null
+++ b/jni/ruby/test/rexml/data/test/tests.xml
@@ -0,0 +1,683 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tests xmlns:var="http://jaxen.org/test-harness/var">
+ <!-- test for jaxen-24 -->
+ <document url="xml/jaxen24.xml">
+ <context select="/body/div">
+ <test select="preceding::*[1]" count="1"/>
+ <valueOf select="local-name(preceding::*[1])">span</valueOf>
+ </context>
+ <!-- jaxen-58 -->
+ <context select="/">
+ <test select="//preceding::x" count="0"/>
+ <test select="//following::x" count="0"/>
+ <test select="/descendant::*/preceding::x" count="0"/>
+ <test select="/descendant::node()/preceding::x" count="0"/>
+ </context>
+ </document>
+
+ <!-- test for jaxen-3 -->
+ <document url="xml/simple.xml">
+ <context select="/">
+ <valueOf select="string()">abd</valueOf>
+ </context>
+ <context select="/root">
+ <valueOf select="string()">abd</valueOf>
+ </context>
+ <context select="/root/a">
+ <valueOf select="string()">a</valueOf>
+ </context>
+ <context select="/root/c">
+ <valueOf select="string()">d</valueOf>
+ </context>
+ </document>
+
+
+ <!-- test for jaxen-3 -->
+ <document url="xml/jaxen3.xml">
+ <context select="/">
+ <test select="/Configuration/hostname/attrlist/hostname[. = 'CE-A'] " count="1"/>
+ </context>
+ </document>
+
+ <!-- parser test cases all of which should fail-->
+ <document url="xml/numbers.xml">
+ <context select="/">
+ <!-- repeated xpaths, jaxen-35 -->
+ <test exception="true" select="/numbers numbers" count="0"/>
+ <!-- invalid xpath, jaxen-34 -->
+ <test exception="true" select="/a/b[c > d]efg" count="0"/>
+ <!-- invalid xpath, jaxen-27 -->
+ <test exception="true" select="/inv/child::" count="0"/>
+ <!-- invalid xpath, jaxen-26 -->
+ <!--
+
+ <test exception="true" select="/invoice/@test[abcd" count="0"/>
+ <test exception="true" select="/invoice/@test[abcd > x" count="0"/>
+
+ <test exception="true" select="string-length('a" count="0"/>
+ <test exception="true" select="/descendant::()" count="0"/>
+ <test exception="true" select="(1 + 1" count="0"/>
+
+ -->
+ </context>
+ </document>
+
+
+ <!-- test cases for the use of underscores in names -->
+ <document url="xml/underscore.xml">
+ <context select="/">
+ <test select="/root/@a" count="1"/>
+ <test select="/root/@_a" count="1"/>
+ <test select="/root/b" count="1"/>
+ <test select="/root/_b" count="1"/>
+ <valueOf select="/root/@a">1</valueOf>
+ <valueOf select="/root/@_a">2</valueOf>
+ <valueOf select="/root/b">1</valueOf>
+ <valueOf select="/root/_b">2</valueOf>
+ </context>
+ </document>
+
+ <!-- test cases for the use of = with nodesets -->
+ <document url="xml/web.xml">
+ <context select="/">
+ <valueOf select="/web-app/servlet/servlet-name = 'file'">true</valueOf>
+ <valueOf select="/web-app/servlet/servlet-name = 'snoop'">true</valueOf>
+ </context>
+ </document>
+
+ <document url="xml/numbers.xml">
+ <context select="/">
+ <valueOf select="/numbers/set/nr = '-3'">true</valueOf>
+ <valueOf select="/numbers/set/nr = -3">true</valueOf>
+ <valueOf select="/numbers/set/nr = 24">true</valueOf>
+ <valueOf select="/numbers/set/nr/@value = '9999'">true</valueOf>
+ <valueOf select="/numbers/set/nr/@value = 9999.0">true</valueOf>
+ <valueOf select="/numbers/set/nr/@value = 66">true</valueOf>
+ </context>
+ </document>
+
+ <!-- test basic math... -->
+ <document url="xml/numbers.xml">
+ <context select="/">
+ <valueOf select="(8 * 2 + 1) = 17">true</valueOf>
+ <valueOf select="(1 + 8 * 2) = 17">true</valueOf>
+ <valueOf select="(7 - 3 + 1) = 5">true</valueOf>
+ <valueOf select="(8 - 4 + 5 - 6) = 3">true</valueOf>
+ <!-- left-assoc tests, comments show WRONG evaluation -->
+ <!-- 3 - 2 - 1 != 2 -->
+ <valueOf select="3 - 2 - 1">0</valueOf>
+ <!-- 8 div 4 div 2 != 4 -->
+ <valueOf select="8 div 4 div 2">1</valueOf>
+ <!-- 3 mod 5 mod 7 != 1 -->
+ <valueOf select="3 mod 7 mod 5">3</valueOf>
+ <!-- 1=(2=2) is true-->
+ <valueOf select="1 = 2 = 2">false</valueOf>
+ <!-- 2!=(3!=1) => 2!=1 => true, (2!=3)!=1 => 1!=1 => false -->
+ <valueOf select="2 != 3 != 1">false</valueOf>
+ <!-- 3 > (2 > 1) is true -->
+ <valueOf select="3 &gt; 2 &gt; 1">false</valueOf>
+ <!-- 3 >= (2 >= 2) is true -->
+ <valueOf select="3 &gt;= 2 &gt;= 2">false</valueOf>
+ <!-- 1 < (2 < 3) is false -->
+ <valueOf select="1 &lt; 2 &lt; 3">true</valueOf>
+ <!-- 0 <= (2 <= 3) is true -->
+ <valueOf select="2 &lt;= 2 &lt;= 3">true</valueOf>
+ </context>
+ </document>
+
+ <!-- test cases for preceding axis with different node types -->
+ <document url="xml/pi2.xml">
+ <context select="/a/c">
+ <test select="//processing-instruction()" count="1"/>
+ <test select="preceding-sibling::*" count="1"/>
+ <test select="preceding-sibling::node()" count="5"/>
+ <test select="preceding-sibling::*[1]" count="1"/>
+ <test select="preceding-sibling::processing-instruction()" count="1"/>
+ <valueOf select="preceding-sibling::processing-instruction()">order-by="x"</valueOf>
+ <valueOf select="preceding-sibling::*[1]">foo</valueOf>
+ <valueOf select="preceding-sibling::node()[2]">order-by="x"</valueOf>
+ </context>
+ </document>
+
+ <document url="xml/id.xml">
+ <context select="/"
+ var:foobar="foobar"
+ var:foo="foo">
+ <valueOf select="$foobar">foobar</valueOf>
+ <test select="/foo[@id=$foobar]" count="1"/>
+ <test select="/foo[@id='$foobar']" count="0"/>
+ <test select="/foo[concat($foo, 'bar')=@id]" count="1"/>
+ <test select="CD_Library/artist[@name=$artist]" count="0"/>
+ </context>
+ </document>
+
+ <document url="xml/id.xml">
+ <context select="/">
+ <!-- attributes have a parent: their element -->
+ <test select="/foo/@id/parent::foo" count="1"/>
+ </context>
+ <!-- attributes can also be used as context nodes -->
+ <context select="/foo/@id">
+ <test select="parent::foo" count="1"/>
+ </context>
+ </document>
+
+ <document url="xml/pi.xml">
+ <context select="/">
+ <test select="//processing-instruction()" count="3"/>
+ <test select="//processing-instruction('cheese')" count="2"/>
+ <test select="//processing-instruction('toast')" count="1">
+ <valueOf select="string()">is tasty</valueOf>
+ </test>
+ </context>
+
+ </document>
+
+ <!-- test evaluate() extension function -->
+ <document url="xml/evaluate.xml">
+ <context select="/">
+ <test select="evaluate('//jumps/*')" count="3"/>
+ <test select="evaluate('//jumps/object/dog')" count="1"/>
+ <test select="evaluate('//jumps/object')/evaluate" count="0"/>
+ <test select="evaluate('//jumps/object')/dog" count="1"/>
+ <test select="evaluate('//jumps/*')/dog" count="1"/>
+ <test select="//metatest[ evaluate(@select) = . ]" count="1"/>
+ </context>
+ </document>
+
+ <document url="xml/numbers.xml">
+ <context select="/numbers/set[1]">
+ <test select="*[-3 = .]" count="1"/>
+ <valueOf select="54 &lt; *">true</valueOf>
+ <valueOf select="55 &lt;= *">true</valueOf>
+ <valueOf select="69 &lt; *">false</valueOf>
+ <valueOf select="-2 &gt; *">true</valueOf>
+ <valueOf select="-3 &gt;= *">true</valueOf>
+ <valueOf select="-4 &gt;= *">false</valueOf>
+ </context>
+ <!-- TODO
+ This context should work, but needs a fixed version of saxpath to parse the right-hand side
+ of the greater-than expression.
+ <context select="/numbers/set[2]">
+ <valueOf select="1 &gt; nr/@value">false</valueOf>
+ <valueOf select="55 &gt; nr/@value">false</valueOf>
+ <valueOf select="55 &gt;= nr/@value">true</valueOf>
+ <valueOf select="1000000 &gt; nr/@value">true</valueOf>
+ </context>
+ -->
+ </document>
+
+
+ <!-- test sibling axes -->
+ <document url="xml/axis.xml">
+
+ <context select="/root">
+ <test select="preceding-sibling::*" count="0"/>
+ </context>
+
+ <context select="/root/a/a.3">
+ <test select="preceding::*" count="2"/>
+ </context>
+
+ <context select="/root/a/a.3">
+ <test select="preceding-sibling::*" count="2"/>
+ </context>
+
+ <context select="/">
+ <valueOf select="name(/root/a/a.3/preceding-sibling::*[1])">a.2</valueOf>
+ <valueOf select="name(/root/a/a.3/preceding-sibling::*[2])">a.1</valueOf>
+ </context>
+
+ <context select="/">
+ <valueOf select="name(/root/a/a.3/following-sibling::*[1])">a.4</valueOf>
+ <valueOf select="name(/root/a/a.3/following-sibling::*[2])">a.5</valueOf>
+ </context>
+
+ </document>
+
+
+ <document url="xml/web.xml">
+
+ <context select="/">
+ <valueOf select="/web-app/servlet[1]/servlet-name">snoop</valueOf>
+ <valueOf select="/web-app/servlet[1]/servlet-name/text()">snoop</valueOf>
+ <valueOf select="/web-app/servlet[2]/servlet-name">file</valueOf>
+ <valueOf select="/web-app/servlet[2]/servlet-name/text()">file</valueOf>
+ </context>
+
+ <context select="/web-app/servlet[1]">
+ <valueOf select="servlet-name">snoop</valueOf>
+ <valueOf select="servlet-name/text()">snoop</valueOf>
+ </context>
+
+ <context select="/web-app/servlet[2]/servlet-name">
+ <test select="preceding::*" count="3"/>
+ </context>
+
+ <context select="/web-app/servlet[2]/servlet-name">
+ <test select="following::*" count="13"/>
+ </context>
+
+ </document>
+
+
+ <!-- test name -->
+
+ <document url="xml/web.xml">
+ <context select="/">
+
+ <test select="*" count="1">
+ <valueOf select="name()">web-app</valueOf>
+ </test>
+
+ <!-- NOTE that the child::node() tests only work if the
+ XML document does not comments or PIs
+ -->
+
+ <test select="./*" count="1">
+ <valueOf select="name()">web-app</valueOf>
+ </test>
+ <test select="child::*" count="1">
+ <valueOf select="name()">web-app</valueOf>
+ </test>
+ <test select="/*" count="1">
+ <valueOf select="name()">web-app</valueOf>
+ </test>
+ <test select="/child::node()" count="1">
+ <valueOf select="name(.)">web-app</valueOf>
+ </test>
+ <test select="child::node()" count="1">
+ <valueOf select="name(.)">web-app</valueOf>
+ </test>
+
+ <!-- empty names -->
+
+ <valueOf select="name()"></valueOf>
+ <valueOf select="name(.)"></valueOf>
+ <valueOf select="name(parent::*)"></valueOf>
+ <valueOf select="name(/)"></valueOf>
+ <valueOf select="name(/.)"></valueOf>
+ <valueOf select="name(/self::node())"></valueOf>
+
+ <!-- name of root elemet -->
+ <valueOf select="name(node())">web-app</valueOf>
+ <valueOf select="name(/node())">web-app</valueOf>
+ <valueOf select="name(/*)">web-app</valueOf>
+ <valueOf select="name(/child::*)">web-app</valueOf>
+ <valueOf select="name(/child::node())">web-app</valueOf>
+ <valueOf select="name(/child::node())">web-app</valueOf>
+ <valueOf select="name(child::node())">web-app</valueOf>
+ <valueOf select="name(./*)">web-app</valueOf>
+ <valueOf select="name(*)">web-app</valueOf>
+
+ </context>
+
+ <context select="/*">
+ <!-- empty names -->
+ <valueOf select="name(..)"></valueOf>
+ <valueOf select="name(parent::node())"></valueOf>
+ <valueOf select="name(parent::*)"></valueOf>
+
+ <!-- name of root elemet -->
+ <valueOf select="name()">web-app</valueOf>
+ <valueOf select="name(.)">web-app</valueOf>
+ <valueOf select="name(../*)">web-app</valueOf>
+ <valueOf select="name(../child::node())">web-app</valueOf>
+ </context>
+ </document>
+
+
+
+ <!-- test predicates -->
+
+ <document url="xml/nitf.xml">
+ <context select="/nitf/head/docdata">
+ <test select="doc-id[@regsrc='AP' and @id-string='D76UIMO80']" count="1"/>
+ </context>
+ <context select="/nitf/head">
+ <test select="meta[@name='ap-cycle']" count="1"/>
+ <test select="meta[@content='AP']" count="1"/>
+ <test select="meta[@name and @content]" count="8"/>
+ <test select="meta[@name='ap-cycle' and @content='AP']" count="1"/>
+ <test select="meta[@name != 'ap-cycle']" count="7"/>
+ </context>
+ <context select="/">
+ <test select="/nitf/head/meta[@name='ap-cycle']" count="1"/>
+ <test select="/nitf/head/meta[@content='AP']" count="1"/>
+ <test select="/nitf/head/meta[@name and @content]" count="8"/>
+ <test select="/nitf/head/meta[@name='ap-cycle' and @content='AP']" count="1"/>
+ <test select="/nitf/head/meta[@name != 'ap-cycle']" count="7"/>
+ </context>
+ </document>
+
+
+ <document url="xml/moreover.xml">
+ <context select="/">
+ <test select="/child::node()" count="1"/>
+ <test select="/*" count="1"/>
+
+ <test select="/*/article" count="20"/>
+ <test select="//*" count="221"/>
+ <test select="//*[local-name()='article']" count="20"/>
+ <test select="//article" count="20"/>
+ <test select="/*/*[@code]" count="20"/>
+
+ <test select="/moreovernews/article[@code='13563275']" count="1"/>
+
+ <test select="/moreovernews/article[@code='13563275']">
+ <valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
+ </test>
+ <test select="/*/article[@code='13563275']">
+ <valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
+ </test>
+ <test select="//article[@code='13563275']">
+ <valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
+ </test>
+ <test select="//*[@code='13563275']">
+ <valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
+ </test>
+ <test select="/child::node()/child::node()[@code='13563275']">
+ <valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
+ </test>
+ <test select="/*/*[@code='13563275']">
+ <valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
+ </test>
+ </context>
+ </document>
+
+
+
+ <!-- test other node types-->
+
+ <document url="xml/contents.xml">
+ <context select="/">
+ <test select="processing-instruction()" count="3"/>
+ <test select="/processing-instruction()" count="3"/>
+ <test select="/comment()" count="1"/>
+ <test select="comment()" count="1"/>
+ <test select="/child::node()/comment()" count="2"/>
+ <test select="/*/comment()" count="2"/>
+ <test select="//comment()" count="3"/>
+ </context>
+ </document>
+
+
+
+ <!-- test positioning -->
+
+ <document url="xml/fibo.xml">
+ <context select="/">
+ <test select="/*/fibonacci[position() &lt; 10]" count="9"/>
+ <valueOf select="sum(//fibonacci)">196417</valueOf>
+ <valueOf select="sum(//fibonacci/@index)">325</valueOf>
+ <valueOf select="/*/fibonacci[2]">1</valueOf>
+ <valueOf select="/*/fibonacci[ count(/*/fibonacci) ]">75025</valueOf>
+ <valueOf select="/*/fibonacci[ count(/*/fibonacci) - 1 ]">46368</valueOf>
+ </context>
+ </document>
+
+
+ <!-- test number functions -->
+
+ <!-- test Axes -->
+
+ <document url="xml/web.xml">
+ <context select="/">
+ <test select="descendant-or-self::*" count="19"/>
+ <test select="descendant::*" count="19"/>
+ <test select="/descendant::*" count="19"/>
+ <test select="/descendant-or-self::*" count="19"/>
+ <test select="/descendant::servlet" count="2"/>
+ <test select="/descendant-or-self::servlet" count="2"/>
+ <test select="descendant-or-self::servlet" count="2"/>
+ <test select="descendant::servlet" count="2"/>
+ <test select="/*/servlet" count="2"/>
+ <valueOf select="count(/*/servlet)">2</valueOf>
+ <test select="//servlet" count="2"/>
+ <valueOf select="count(//servlet)">2</valueOf>
+ </context>
+ <context select="/web-app">
+ <test select="/descendant::servlet" count="2"/>
+ <test select="/descendant-or-self::servlet" count="2"/>
+ <test select="descendant-or-self::servlet" count="2"/>
+ <test select="descendant::servlet" count="2"/>
+ </context>
+ </document>
+
+ <document url="xml/much_ado.xml">
+ <context select="/">
+ <test select="/descendant::ACT" count="5"/>
+ <test select="descendant::ACT" count="5"/>
+ <valueOf select="/PLAY/TITLE">Much Ado about Nothing</valueOf>
+ <valueOf select="2+2">4</valueOf>
+ <valueOf select="5 * 4 + 1">21</valueOf>
+ <valueOf select="count(descendant::ACT)">5</valueOf>
+ <valueOf select="10 + count(descendant::ACT) * 5">35</valueOf>
+ <valueOf select="(10 + count(descendant::ACT)) * 5">75</valueOf>
+ </context>
+ <context select="/PLAY/ACT[2]/SCENE[1]">
+ <test select="/descendant::ACT" count="5"/>
+ <test select="../../descendant::ACT" count="5"/>
+ <test select="/PLAY/ACT[2]/SCENE[1]/descendant::SPEAKER" count="141"/>
+ <test select="descendant::SPEAKER" count="141"/>
+ <valueOf select="count(descendant::*)+1">646</valueOf>
+ <valueOf select="count(descendant::SPEAKER)+1">142</valueOf>
+ <valueOf select="count(ancestor::*)">2</valueOf>
+ <valueOf select="count(ancestor::PLAY)">1</valueOf>
+ <valueOf select="count(ancestor-or-self::*)">3</valueOf>
+ <valueOf select="count(ancestor-or-self::PLAY)">1</valueOf>
+ <valueOf select="5+count(ancestor::*)-1">6</valueOf>
+ </context>
+ <context select="/">
+ <!-- Test correct predicate application -->
+ <valueOf select="count(/PLAY/ACT/SCENE[1])">5</valueOf>
+ </context>
+ </document>
+
+ <!-- test axis node ordering -->
+ <document url="xml/web.xml">
+ <context select="/">
+ <!-- Reported as Jira issue JAXEN-24 -->
+ <test select="//servlet-mapping/preceding::*[1][name()='description']" count="1"/>
+ <test select="/web-app/servlet//description/following::*[1][name()='servlet-mapping']" count="1"/>
+ <test select="/web-app/servlet//description/following::*[2][name()='servlet-name']" count="1"/>
+ </context>
+ </document>
+
+ <!-- test document function -->
+ <document url="xml/text.xml">
+ <context select="/">
+ <test select="document('xml/web.xml')" count="1">
+ <valueOf select="/web-app/servlet[1]/servlet-name">snoop</valueOf>
+ <valueOf select="/web-app/servlet[1]/servlet-name/text()">snoop</valueOf>
+ </test>
+ <valueOf select="document('xml/web.xml')/web-app/servlet[1]/servlet-name">snoop</valueOf>
+ </context>
+ <!-- Test to check if the context changes when an extension function is used.
+ First test is an example, second is the actual test.
+ -->
+ <context select="/foo/bar/cheese[1]">
+ <valueOf select="concat(./@id,'foo',@id)">3foo3</valueOf>
+ <valueOf select="concat(./@id,document('xml/web.xml')/web-app/servlet[1]/servlet-name,./@id)">3snoop3</valueOf>
+ </context>
+ </document>
+
+ <document url="xml/message.xml">
+ <context select="/">
+ <valueOf select="/message/body/data/items/item[name/text()='parentinfo']/value">Pruefgebiete</valueOf>
+ <valueOf select="document('xml/message.xml')/message/body/data/items/item[name/text()='parentinfo']/value">Pruefgebiete</valueOf>
+ </context>
+ </document>
+
+ <document url="xml/simple.xml">
+
+ <!-- test behaviour of AbsoluteLocationPath -->
+ <context select="/root/a">
+ <valueOf select="concat( ., /root/b )">ab</valueOf>
+ <valueOf select="concat( ../b, . )">ba</valueOf>
+ <valueOf select="concat( /root/b, . )">ba</valueOf>
+ <valueOf select="concat( /root/c/d, ../b )">db</valueOf>
+ </context>
+
+ <!-- test the translate() function -->
+ <context select="/">
+ <valueOf select="translate( '', '', '' )"></valueOf>
+ <valueOf select="translate( 'abcd', '', '' )">abcd</valueOf>
+ <valueOf select="translate( 'abcd', 'abcd', 'abcd' )">abcd</valueOf>
+ <valueOf select="translate( 'abcd', 'dcba', 'dcba' )">abcd</valueOf>
+ <valueOf select="translate( 'abcd', 'abcd', 'dcba' )">dcba</valueOf>
+ <valueOf select="translate( 'abcd', 'abcd', 'ab' )">ab</valueOf>
+ <valueOf select="translate( 'abcd', 'cdab', 'cd' )">cd</valueOf>
+ <valueOf select="translate( 'abcd', 'acbd', 'xy' )">xy</valueOf>
+ <valueOf select="translate( 'abcd', 'abcdb', 'abcdb' )">abcd</valueOf>
+ <valueOf select="translate( 'abcd', 'abcd', 'abcdb' )">abcd</valueOf>
+ </context>
+
+ <context select="/">
+ <valueOf select="substring('12345', 1.5, 2.6)">234</valueOf>
+ <valueOf select="substring('12345', 0, 3)">12</valueOf>
+ <valueOf select="substring('12345', 0 div 0, 3)"></valueOf>
+ <valueOf select="substring('12345', 1, 0 div 0)"></valueOf>
+ <valueOf select="substring('12345', -42, 1 div 0)">12345</valueOf>
+ <valueOf select="substring('12345', -1 div 0, 1 div 0)"></valueOf>
+ <valueOf select="substring('12345', 3)">345</valueOf>
+ <valueOf select="substring('12345',1,15)">12345</valueOf>
+ </context>
+
+ <!-- Some tests for the normalize-space() function -->
+
+ <context select="/">
+ <valueOf select="normalize-space(' abc ')">abc</valueOf>
+ <valueOf select="normalize-space(' a b c ')">a b c</valueOf>
+ <valueOf select="normalize-space(' a &#x0d; b &#x0a; c ')">a b c</valueOf>
+ <!-- Next test case addresses issue JAXEN-22 -->
+ <valueOf select="normalize-space(' ')"></valueOf>
+ <!-- Next test case addresses issue JAXEN-29 -->
+ <valueOf select="normalize-space('')"></valueOf>
+ </context>
+ </document>
+
+
+
+ <!-- test cases for String extension functions -->
+ <document url="xml/web.xml">
+ <context select="/web-app/servlet[1]">
+ <valueOf select="upper-case( servlet-class )">SNOOPSERVLET</valueOf>
+ <valueOf select="lower-case( servlet-class )">snoopservlet</valueOf>
+ <valueOf select="upper-case( servlet-class, 'fr' )">SNOOPSERVLET</valueOf>
+ <valueOf select="upper-case( servlet-class, 'fr-CA' )">SNOOPSERVLET</valueOf>
+ <valueOf select="upper-case( servlet-class, 'es-ES-Traditional_WIN' )">SNOOPSERVLET</valueOf>
+ <valueOf select="ends-with( servlet-class, 'Servlet' )">true</valueOf>
+ <valueOf select="ends-with( servlet-class, 'S' )">false</valueOf>
+ </context>
+ </document>
+
+ <!-- test cases for the lang() function -->
+ <document url="xml/lang.xml">
+ <context select="/">
+ <test select="/e1/e2[lang('hr')]" count="0"/>
+ <test select="/e1/e2/e3[lang('en')]" count="1"/>
+ <test select="/e1/e2/e3[lang('en-US')]" count="1"/>
+ <test select="/e1/e2/e3[lang('en-GB')]" count="0"/>
+ <test select="/e1/e2/e3[lang('hu')]" count="2"/>
+ <test select="/e1/e2/e3[lang('hu-HU')]" count="0"/>
+ <test select="/e1/e2/e3[lang('es')]" count="1"/>
+ <test select="/e1/e2/e3[lang('es-BR')]" count="0"/>
+ </context>
+ </document>
+
+ <!-- test namespace -->
+ <document url="xml/namespaces.xml">
+ <context select="/"
+ xmlns:foo="http://fooNamespace/"
+ xmlns:voo="http://fooNamespace/"
+ xmlns:bar="http://barNamespace/"
+ xmlns:alias="http://fooNamespace/">
+ <test select="/*" count="1"/>
+ <test select="/foo:a" count="1"/>
+ <test select="/foo:a/b" count="1"/>
+ <test select="/voo:a/b/c" count="1"/>
+ <test select="/voo:a/bar:f" count="1"/>
+ <test select="/*[namespace-uri()='http://fooNamespace/' and local-name()='a']" count="1"/>
+ <test select="/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']" count="1"/>
+ <test select="/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']/*[local-name()='y' and namespace-uri()='http://fooNamespace/']" count="1"/>
+ </context>
+ <!-- the prefix here and in the document have no relation; it's their
+ namespace-uri binding that counts -->
+ <context select="/" xmlns:foo="http://somethingElse/">
+ <test select="/foo:a/b/c" count="0"/>
+ </context>
+
+ <context select="/"
+ xmlns:foo="http://fooNamespace/"
+ xmlns:bar="http://barNamespace/"
+ xmlns:alias="http://fooNamespace/">
+ <valueOf select="/foo:a/b/c">Hello</valueOf>
+ <valueOf select="/foo:a/foo:d/foo:e">Hey</valueOf>
+ <valueOf select="/foo:a/alias:x/alias:y">Hey3</valueOf>
+ <valueOf select="/foo:a/foo:x/foo:y">Hey3</valueOf>
+ <valueOf select="/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']/*[local-name()='y' and namespace-uri()='http://fooNamespace/']">Hey3</valueOf>
+ </context>
+
+ </document>
+
+ <document url="xml/defaultNamespace.xml">
+ <context select="/">
+ <!-- NOTE: /a/b/c selects elements in no namespace only! -->
+ <test select="/a/b/c" count="0"/>
+ <!--
+ The following test uses an unbound prefix 'x' and should throw an exception.
+ Addresses issue JAXEN-18.
+ Turns out this isn't really tested as the test didn't fail when the exception wasn't thrown.
+ <test select="/x:a/x:b/x:c" count="0" exception="true"/>
+ -->
+ </context>
+ <context select="/"
+ xmlns:dummy="http://dummyNamespace/">
+ <test select="/dummy:a/dummy:b/dummy:c" count="1"/>
+ </context>
+ </document>
+ <document url="xml/text.xml">
+ <context select="/">
+ <test select="/foo/bar/text()" count="3"/>
+ <valueOf select="normalize-space(/foo/bar/text())">baz</valueOf>
+ </context>
+ </document>
+
+ <document url="xml/testNamespaces.xml">
+ <context select="/">
+ <!-- the root is not an element, so no namespaces -->
+ <test select="namespace::*" count="0" debug="off"/>
+ <test select="/namespace::*" count="0" debug="off"/>
+ <test select="/Template/Application1/namespace::*" count="3" debug="off"/>
+ <test select="/Template/Application2/namespace::*" count="3" debug="off"/>
+
+ <test select="//namespace::*" count="25" debug="off"/>
+ </context>
+
+ <!--
+ <context select="/Template/Application1">
+ <test select="namespace::*" count="3" debug="off"/>
+ <test select="/namespace::*" count="0" debug="off"/>
+ <test select="/Template/Application1/namespace::*" count="3" debug="off"/>
+ <test select="/Template/Application2/namespace::*" count="3" debug="off"/>
+ <test select="//namespace::*" count="25" debug="off"/>
+ <test select="//namespace::xplt" count="8" debug="off"/>
+ <test xmlns:somethingelse="http://www.xxxx.com/"
+ select="//namespace::somethingelse" count="0" debug="off"/>
+ </context>
+ -->
+ </document>
+
+ <document url="xml/testNamespaces.xml">
+ <context select="/">
+ <!-- namespace nodes have their element as their parent -->
+ <test select="/Template/namespace::xml/parent::Template" count="1"/>
+ </context>
+ <!-- namespace nodes can also be used as context nodes -->
+ <context select="/Template/namespace::xml">
+ <test select="parent::Template" count="1"/>
+ </context>
+ </document>
+
+</tests>
diff --git a/jni/ruby/test/rexml/data/test/tests.xsl b/jni/ruby/test/rexml/data/test/tests.xsl
new file mode 100644
index 0000000..3ecd648
--- /dev/null
+++ b/jni/ruby/test/rexml/data/test/tests.xsl
@@ -0,0 +1,369 @@
+<stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:var="http://jaxen.org/test-harness/var">
+<!-- this is what I used to generate XPathTestBase. After generating I fixed the illegal strings (its quicker
+than fixing the xsl for that few errors) and reformatted the code. Its unlikely this code will be needed
+again, its just in cvs for completeness -->
+ <output method="text"/>
+ <template match="/">
+ <text>
+ /*
+ * $Header: /home/projects/jaxen/scm/jaxen/src/java/test/org/jaxen/XPathTestBase.java,v 1.32 2005/06/15 23:52:40 bewins Exp $
+ * $Revision: 1.32 $
+ * $Date: 2005/06/15 23:52:40 $
+ *
+ * ====================================================================
+ *
+ * Copyright (C) 2000-2002 bob mcwhirter &amp; James Strachan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions, and the disclaimer that follows
+ * these conditions in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. The name "Jaxen" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact license@jaxen.org.
+ *
+ * 4. Products derived from this software may not be called "Jaxen", nor
+ * may "Jaxen" appear in their name, without prior written permission
+ * from the Jaxen Project Management (pm@jaxen.org).
+ *
+ * In addition, we request (but do not require) that you include in the
+ * end-user documentation provided with the redistribution and/or in the
+ * software itself an acknowledgement equivalent to the following:
+ * "This product includes software developed by the
+ * Jaxen Project (http://www.jaxen.org/)."
+ * Alternatively, the acknowledgment may be graphical using the logos
+ * available at http://www.jaxen.org/
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * ====================================================================
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Jaxen Project and was originally
+ * created by bob mcwhirter &lt;bob@werken.com> and
+ * James Strachan &lt;jstrachan@apache.org>. For more information on the
+ * Jaxen Project, please see &lt;http://www.jaxen.org/>.
+ *
+ * $Id: XPathTestBase.java,v 1.32 2005/06/15 23:52:40 bewins Exp $
+ */
+
+
+package org.jaxen;
+
+import junit.framework.TestCase;
+import org.jaxen.function.StringFunction;
+import org.jaxen.saxpath.helpers.XPathReaderFactory;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class XPathTestBase extends TestCase
+{
+ protected static String VAR_URI = "http://jaxen.org/test-harness/var";
+ protected static String TESTS_XML = "xml/test/tests.xml";
+
+ protected static boolean verbose = true;
+ protected static boolean debug = true;
+ private ContextSupport contextSupport;
+
+ public XPathTestBase(String name)
+ {
+ super( name );
+ }
+
+ public void setUp() throws ParserConfigurationException
+ {
+ this.contextSupport = null;
+ System.setProperty( XPathReaderFactory.DRIVER_PROPERTY,
+ "" );
+ log( "-----------------------------" );
+ }
+
+ public void log(String text)
+ {
+ log( verbose,
+ text );
+ }
+
+ public void log(boolean actualVerbose,
+ String text)
+ {
+ if ( ! actualVerbose )
+ {
+ return;
+ }
+
+ System.out.println( text );
+ }
+
+ protected void assertCountXPath(int expectedSize, Object context, String xpathStr) throws JaxenException {
+ try
+ {
+ assertCountXPath2(expectedSize, context, xpathStr);
+ }
+ catch (UnsupportedAxisException e)
+ {
+ log ( debug,
+ " ## SKIPPED -- Unsupported Axis" );
+ }
+ }
+
+ protected Object assertCountXPath2(int expectedSize, Object context, String xpathStr) throws JaxenException {
+ log ( debug,
+ " Select :: " + xpathStr );
+ BaseXPath xpath = new BaseXPath( xpathStr );
+ List results = xpath.selectNodes( getContext( context ) );
+ log ( debug,
+ " Expected Size :: " + expectedSize );
+ log ( debug,
+ " Result Size :: " + results.size() );
+
+ if ( expectedSize != results.size() )
+ {
+ log ( debug,
+ " ## FAILED" );
+ log ( debug,
+ " ## xpath: " + xpath + " = " + xpath.debug() );
+
+ Iterator resultIter = results.iterator();
+
+ while ( resultIter.hasNext() )
+ {
+ log ( debug,
+ " --> " + resultIter.next() );
+ }
+ }
+ assertEquals( xpathStr,
+ expectedSize,
+ results.size() );
+ if (expectedSize > 0) {
+ return results.get(0);
+ }
+ return null;
+ }
+
+ protected void assertInvalidXPath(Object context, String xpathStr) throws JaxenException {
+ try
+ {
+ log ( debug,
+ " Select :: " + xpathStr );
+ BaseXPath xpath = new BaseXPath( xpathStr );
+ List results = xpath.selectNodes( getContext( context ) );
+ log ( debug,
+ " Result Size :: " + results.size() );
+ fail("An exception was expected.");
+ }
+ catch (UnsupportedAxisException e)
+ {
+ log ( debug,
+ " ## SKIPPED -- Unsupported Axis" );
+ }
+ catch (JaxenException e) {
+ log (debug, " Caught expected exception "+e.getMessage());
+ }
+ }
+
+ protected void assertValueOfXPath(String expected, Object context, String xpathStr) throws JaxenException {
+ try
+ {
+ BaseXPath xpath = new BaseXPath( xpathStr );
+ Object node = xpath.evaluate( getContext( context ) );
+
+ String result = StringFunction.evaluate( node,
+ getNavigator() );
+
+ log ( debug,
+ " Select :: " + xpathStr );
+ log ( debug,
+ " Expected :: " + expected );
+ log ( debug,
+ " Result :: " + result );
+
+ if ( ! expected.equals( result ) )
+ {
+ log ( debug,
+ " ## FAILED" );
+ log ( debug,
+ " ## xpath: " + xpath + " = " + xpath.debug() );
+ }
+
+ assertEquals( xpathStr,
+ expected,
+ result );
+ }
+ catch (UnsupportedAxisException e)
+ {
+ log ( debug,
+ " ## SKIPPED -- Unsupported Axis " );
+
+ }
+ }
+ protected Context getContext(Object contextNode)
+ {
+ Context context = new Context( getContextSupport() );
+
+ List list = new ArrayList( 1 );
+ list.add( contextNode );
+ context.setNodeSet( list );
+
+ return context;
+ }
+
+ public ContextSupport getContextSupport()
+ {
+ if ( this.contextSupport == null )
+ {
+ this.contextSupport = new ContextSupport( new SimpleNamespaceContext(),
+ XPathFunctionContext.getInstance(),
+ new SimpleVariableContext(),
+ getNavigator() );
+ }
+
+ return this.contextSupport;
+ }
+
+ public abstract Navigator getNavigator();
+
+ public abstract Object getDocument(String url) throws Exception;
+
+ public void testGetNodeType() throws FunctionCallException, UnsupportedAxisException
+ {
+ Navigator nav = getNavigator();
+ Object document = nav.getDocument("xml/testNamespaces.xml");
+ int count = 0;
+ Iterator descendantOrSelfAxisIterator = nav.getDescendantOrSelfAxisIterator(document);
+ while (descendantOrSelfAxisIterator.hasNext()) {
+ Object node = descendantOrSelfAxisIterator.next();
+ Iterator namespaceAxisIterator = nav.getNamespaceAxisIterator(node);
+ while (namespaceAxisIterator.hasNext()) {
+ count++;
+ assertEquals("Node type mismatch", Pattern.NAMESPACE_NODE, nav.getNodeType(namespaceAxisIterator.next()));
+ }
+ }
+ assertEquals(25, count);
+ }
+
+ </text>
+ <apply-templates select="node()|@*"/>
+ <text>
+}
+ </text>
+ </template>
+ <template match="context">
+ <text>
+ public void test</text><value-of select="generate-id()"/><text>() throws JaxenException {
+ Navigator nav = getNavigator();
+ String url = "</text><value-of select="../@url"/><text>";
+ log( "Document [" + url + "]" );
+ Object document = nav.getDocument(url);
+
+ XPath contextpath = new BaseXPath("</text><value-of select="@select"/><text>", nav);
+ log( "Initial Context :: " + contextpath );
+ List list = contextpath.selectNodes(document);
+ </text>
+ <if test="count(namespace::*) > count(../namespace::*)">
+ <text>
+ SimpleNamespaceContext nsContext = new SimpleNamespaceContext();</text>
+ <for-each select="namespace::*[local-name() != 'var' and local-name() != 'xml']">
+ <text>
+ nsContext.addNamespace( "</text><value-of select="local-name()"/><text>", "</text><value-of select="."/><text>" );</text>
+ </for-each>
+ <text>
+ getContextSupport().setNamespaceContext( nsContext );</text>
+ </if>
+ <if test="@*[namespace-uri() = 'http://jaxen.org/test-harness/var']">
+ <text>
+ SimpleVariableContext varContext = new SimpleVariableContext();</text>
+ <for-each select="@*[namespace-uri() = 'http://jaxen.org/test-harness/var']">
+ <text>
+ varContext.setVariableValue(null, "</text><value-of select="local-name()"/><text>", "</text><value-of select="."/><text>" );</text>
+ </for-each>
+ <text>
+ getContextSupport().setVariableContext( varContext );</text>
+ </if>
+ <text>
+ Iterator iter = list.iterator();
+ while (iter.hasNext()) {
+ Object context = iter.next();</text>
+ <apply-templates select="node()|@*"/>
+ <text>
+ }
+ }</text>
+ </template>
+ <template match="test[@exception]">
+ <text>
+ assertInvalidXPath(context, "</text><value-of select='@select'/><text>");</text>
+ </template>
+ <template match="test[valueOf]">
+ <choose>
+ <when test="@count">
+ <text>
+ try
+ {
+ Object result = assertCountXPath2(</text><value-of select="@count"/><text>, context, "</text><value-of select="@select"/><text>");</text>
+ <for-each select="valueOf">
+ <text>
+ assertValueOfXPath("</text><value-of select="."/><text>", result, "</text><value-of select="@select"/><text>");</text>
+ </for-each>
+ <text>
+ }
+ catch (UnsupportedAxisException e)
+ {
+ log ( debug, " ## SKIPPED -- Unsupported Axis" );
+ }</text>
+ </when>
+ <otherwise>
+ <text>
+ try
+ {
+ BaseXPath xpath = new BaseXPath( "</text><value-of select="@select"/><text>" );
+ List results = xpath.selectNodes( getContext( context ) );
+ Object result = results.get(0);</text>
+ <for-each select="valueOf">
+ <text>
+ assertValueOfXPath("</text><value-of select="."/><text>", result, "</text><value-of select="@select"/><text>");</text>
+ </for-each>
+ <text>
+ }
+ catch (UnsupportedAxisException e)
+ {
+ log ( debug, " ## SKIPPED -- Unsupported Axis" );
+ }</text>
+ </otherwise>
+ </choose>
+ </template>
+ <template match="test">
+ <text>
+ assertCountXPath(</text><value-of select="@count"/><text>, context, "</text><value-of select="@select"/><text>");</text>
+ </template>
+ <template match="valueOf">
+ <text>
+ assertValueOfXPath("</text><value-of select="."/>", context, "<value-of select="@select"/><text>");</text>
+ </template>
+ <template match="comment()"><text>
+ /*</text><value-of select="."/><text>
+ */</text>
+ </template>
+ <template match="node()|@*"><apply-templates select="node()|@*"/></template>
+</stylesheet> \ No newline at end of file
diff --git a/jni/ruby/test/rexml/data/testNamespaces.xml b/jni/ruby/test/rexml/data/testNamespaces.xml
new file mode 100644
index 0000000..50f7c93
--- /dev/null
+++ b/jni/ruby/test/rexml/data/testNamespaces.xml
@@ -0,0 +1,22 @@
+<Template>
+ <Application1 xmlns:xplt="http://www.xxxx.com/"
+ xmlns:xpl="http://www.xxxx.com/"
+ version="3.0"
+ randomAttribute="foo"
+ >
+ <xpl:insertText/>
+ <xplt:anyElement>
+ <Name/>
+ </xplt:anyElement>
+ </Application1>
+
+ <Application2 xmlns:xplt="http://www.xxxx.com/"
+ xmlns:xpl="http://www.xxxx.com/"
+ version="3.0"
+ >
+ <xpl:insertText/>
+ <xplt:anyElement>
+ <Name/>
+ </xplt:anyElement>
+ </Application2>
+</Template>
diff --git a/jni/ruby/test/rexml/data/testsrc.xml b/jni/ruby/test/rexml/data/testsrc.xml
new file mode 100644
index 0000000..9c7a782
--- /dev/null
+++ b/jni/ruby/test/rexml/data/testsrc.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
+ "http://my.netscape.com/publish/formats/rss-0.91.dtd">
+<rss version="0.91">
+ <channel>
+ <title>xmlhack</title>
+ <link>http://www.xmlhack.com</link>
+ <description>Developer news from the XML community</description>
+ <language>en-us</language>
+ <copyright>Copyright 1999-2001, xmlhack team.</copyright>
+ <managingEditor>editor@xmlhack.com</managingEditor>
+ <webMaster>webmaster@xmlhack.com</webMaster>
+ <image>
+ <title>xmlhack</title>
+ <url>http://www.xmlhack.com/images/mynetscape88.gif</url>
+ <link>http://www.xmlhack.com</link>
+ <width>88</width>
+ <height>31</height>
+ <description>News, opinions, tips and issues concerning XML development</description>
+ </image>
+<item x='0'>
+<title>Experimental non-XML syntax for RELAX NG</title>
+<link>http://www.xmlhack.com/read.php?item=1343</link>
+<description>
+ James Clark has announced the release of an experimental non-XML syntax for RELAX
+ NG and a Java translator implementation that converts
+ instances of the syntax into RELAX NG's XML syntax.
+</description>
+<category>Schemas</category>
+</item>
+<item x='1'>
+<title>Long-awaited entity-resolver Java classes finally released</title>
+<link>http://www.xmlhack.com/read.php?item=1342</link>
+<description>Norman Walsh has
+ announced the release of SAX entityResolver() and JAXP
+ URIResolver() Java
+ classes he wrote to implement the OASIS XML Catalogs
+ Committee Specification (in addition to the TR9401 and
+ Apache XCatalogs specifications).
+</description>
+<category>SGML/XML</category>
+<category>Java</category>
+</item>
+<item x='3'>
+
+<title>Beepcore-C framework released</title>
+<link>http://www.xmlhack.com/read.php?item=1341</link>
+<description>Invisible Worlds have announced the publication of Beepcore-C, an implementation of the BEEP framework written in C.</description>
+<category>Protocols</category>
+<category>C++</category>
+</item>
+<item>
+<title>SVG and XSL-FO by example</title>
+<link>http://www.xmlhack.com/read.php?item=1340</link>
+<description>Jirka Jirat has announced the
+addition of an XSL-FO and SVG examples repository to the Zvon developer
+reference site.</description>
+<category>SVG</category>
+<category>XSL-FO</category>
+</item>
+
+
+</channel>
+</rss>
diff --git a/jni/ruby/test/rexml/data/text.xml b/jni/ruby/test/rexml/data/text.xml
new file mode 100644
index 0000000..e1865c7
--- /dev/null
+++ b/jni/ruby/test/rexml/data/text.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<foo>
+ <bar>
+ baz
+ <cheese id="3"/>
+ baz
+ <cheese/>
+ baz
+ </bar>
+</foo>
diff --git a/jni/ruby/test/rexml/data/ticket_61.xml b/jni/ruby/test/rexml/data/ticket_61.xml
new file mode 100644
index 0000000..4df1cc9
--- /dev/null
+++ b/jni/ruby/test/rexml/data/ticket_61.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<HTML>
+<body onload="RunOnLoads()" ms_positioning="FlowLayout"><form id="CollectiveForm" name="CollectiveForm" action="Matter_List.aspx" method="post" defaultbutton="btSearch"><input type="hidden" name="LinkID"/> <input type="hidden" name="Param"/><input type="hidden" name="__EVENTTARGET"/> <input type="hidden" name="__EVENTARGUMENT"/><input type="hidden" value="dDwtMjA3ODMxNDk3NDt0PDtsPGk8MD47aTwxPjs+O2w8dDxAPC9BSU1TL3N0eWxlc2hlZXRzL0RhdGFDZXJ0U3R5bGUuYXNweDs+Ozs+O3Q8cDxsPGRlZmF1bHRCdXR0b247PjtsPGJ0U2VhcmNoOz4+O2w8aTwxPjtpPDM+O2k8ND47aTw4PjtpPDExPjtpPDEzPjtpPDE1Pjs+O2w8dDxAPC9BSU1TL2ltYWdlcy9oZWFkZXItYWltc2xvZ28uZ2lmOy9BSU1TL2ltYWdlcy9oZWFkZXItYmtnZC5naWY7Pjs7Pjt0PEA8L0FJTVMvaW1hZ2VzL2hlYWRlci1yaWdodGVuZC5naWY7Oz47Oz47dDw7bDxpPDI+Oz47bDx0PDtsPGk8MT47aTwyPjs+O2w8dDxwPDtwPGw8b25wcm9wZXJ0eWNoYW5nZTs+O2w8Q29sbGVjdGl2ZVZhbGlkYXRpb25TdW1tYXJ5X09uUHJvcGVydHlDaGFuZ2UoKTs+Pj47Oz47dDxAPC9BSU1TL2ltYWdlcy9hbGVydExlZnRUb3AuZ2lmOy9BSU1TL2ltYWdlcy9hbGVydEhvcml6b250YWwuZ2lmOy9BSU1TL2ltYWdlcy9hbGVydC5naWY7L0FJTVMvaW1hZ2VzL2FsZXJ0SG9yaXpvbnRhbC5naWY7L0FJTVMvaW1hZ2VzL2FsZXJ0UmlnaHRUb3AuZ2lmOy9BSU1TL2ltYWdlcy9hbGVydExlZnRWZXJ0aWNhbC5naWY7L0FJTVMvaW1hZ2VzL2FsZXJ0UmlnaHRWZXJ0aWNhbC5naWY7L0FJTVMvaW1hZ2VzL2FsZXJ0TGVmdEJvdHRvbS5naWY7L0FJTVMvaW1hZ2VzL2FsZXJ0SG9yaXpvbnRhbC5naWY7L0FJTVMvaW1hZ2VzL2FsZXJ0UmlnaHRCb3R0b20uZ2lmOz47Oz47Pj47Pj47dDw7bDxpPDE+O2k8Mz47aTw3Pjs+O2w8dDxwPHA8bDxUZXh0O2luRXJyb3JTdGF0ZTs+O2w8WTtvPGY+Oz4+O3A8bDxvbmNoYW5nZTtjdXJyZW50dmFsdWU7PjtsPEluZGljYXRlQ2hhbmdlZCh0aGlzLCcnKVw7O1k7Pj4+Ozs+O3Q8cDxwPGw8VGV4dDtpbkVycm9yU3RhdGU7PjtsPE9wZW47bzxmPjs+PjtwPGw8b25jaGFuZ2U7Y3VycmVudHZhbHVlOz47bDxJbmRpY2F0ZUNoYW5nZWQodGhpcywnJylcOztPcGVuOz4+Pjs7Pjt0PHA8cDxsPFRvb2xUaXA7PjtsPFxlOz4+Oz47Oz47Pj47dDw7bDxpPDE+O2k8Mz47aTw1PjtpPDY+O2k8Nz47PjtsPHQ8cDxwPGw8aW5FcnJvclN0YXRlO1RleHQ7PjtsPG88Zj47XGU7Pj47cDxsPG9uY2hhbmdlO2N1cnJlbnR2YWx1ZTs+O2w8SW5kaWNhdGVDaGFuZ2VkKHRoaXMsJycpXDs7XGU7Pj4+Ozs+O3Q8cDxwPGw8aW5FcnJvclN0YXRlO1RleHQ7PjtsPG88Zj47XGU7Pj47cDxsPG9uY2hhbmdlO2N1cnJlbnR2YWx1ZTs+O2w8SW5kaWNhdGVDaGFuZ2VkKHRoaXMsJycpXDs7XGU7Pj4+Ozs+O3Q8cDxwPGw8aW5FcnJvclN0YXRlO1RleHQ7PjtsPG88Zj47XGU7Pj47cDxsPG9uY2hhbmdlO2N1cnJlbnR2YWx1ZTs+O2w8SW5kaWNhdGVDaGFuZ2VkKHRoaXMsJycpXDs7XGU7Pj4+Ozs+O3Q8QDxQcm9mZXNzaW9uYWwgaW4gQ2hhcmdlOz47Oz47dDxwPHA8bDxpbkVycm9yU3RhdGU7VGV4dDs+O2w8bzxmPjtcZTs+PjtwPGw8b25jaGFuZ2U7Y3VycmVudHZhbHVlOz47bDxJbmRpY2F0ZUNoYW5nZWQodGhpcywnJylcOztcZTs+Pj47Oz47Pj47dDxAMDxwPHA8bDxEYXRhS2V5cztQYWdpbmdWaWV3O1RvdGFsUmVjb3JkcztQYWdlQ291bnQ7UGFnZVNpemU7U29ydERpcmVjdGlvbjtVbmlxdWVJZDtTb3J0RXhwcmVzc2lvbjtDdXJyZW50UGFnZTtfIUl0ZW1Db3VudDtfIURhdGFTb3VyY2VJdGVtQ291bnQ7TWF4UGFnZU1vZGU7PjtsPGw8PjtPcGVuO2k8MD47aTwwPjtpPDIwPjtpPDE+O2MxNjA4M2Y4LTkwZjgtNDQ1Ni04YzM0LTc5ZTFhNjg2ZDhlMjttYXR0ZXJfbm87aTwxPjtpPC0xPjtpPC0xPjtEYXRhQ2VydC5XZWIuQ29udHJvbHMuTWF4UGFnZU1vZGUsIERhdGFDZXJ0LldlYiwgVmVyc2lvbj0xLjYuMi4xNCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsPE5PUk1BTD47Pj47PjtAMDw7OztAMDxwPGw8SGVhZGVyVGV4dDs+O2w8UHJvZmVzc2lvbmFsIGluIENoYXJnZTs+Pjs7Ozs+O0AwPHA8bDxWaXNpYmxlOz47bDxvPHQ+Oz4+Ozs7Oz47Oz47Ozs7Ozs7Ozs+Ozs+O3Q8QDwvQUlNUy9pbWFnZXMvZm9vdGVyLWJrZ2QuZ2lmOy9BSU1TL2ltYWdlcy9mb290ZXItYmtnZC5naWY7MjAwNjs7Pjs7Pjs+Pjs+Pjs+2T4SCaJMpmBYZKv3iwMGME7j8M0=" name="__VIEWSTATE"/> <input id="PageGuid" type="hidden" value="659179a1-bdcc-41f3-8f27-0495ae9356fd" name="PageGuid"/> <table style="BORDER-COLLAPSE: collapse" height="100%" cellSpacing="0" cellPadding="0" width="100%" border="0"><tbody ><tr ><td ><table cellSpacing="0" cellPadding="0" width="100%" border="0"><tbody ><tr ><td vAlign="top" align="left" width="325"><br /></td><td align="right" background="/AIMS/images/header-bkgd.gif"><table style="DISPLAY: inline" cellSpacing="0" cellPadding="0" border="0"><tbody ><tr ><td ><img src="/Aims/images/nav-leftend.gif"/></td><td background="/Aims/images/nav-center.gif"><span ><table id="NavControl__ctl0_group_1" style="Z-INDEX: 999" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl0_group_1', null, null, 0);" cellSpacing="0" cellPadding="0" border="0"><tbody ><tr ></tr></tbody></table></span><span ><table style="LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px"><tbody ><tr ><td ><br /></td></tr></tbody></table></span><span ><table class="MenuGroup" id="NavControl__ctl1_group_1" style="Z-INDEX: 999" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_1', null, null, 250);" cellSpacing="0" cellPadding="0" border="0"><tbody ><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_1" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_1', 'NavControl__ctl1_group_2', 'belowleft', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '10', '', 'MyWork/Inbox.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_1', 'NavControl__ctl1_group_1', 'NavControl__ctl1_group_2', 250, null);">MY WORK</td><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_6" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_6', 'NavControl__ctl1_group_7', 'belowleft', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '1', '', 'Matter/Matter_List.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_6', 'NavControl__ctl1_group_1', 'NavControl__ctl1_group_7', 250, null);">MATTER</td><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_7" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_7', 'NavControl__ctl1_group_8', 'belowleft', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '2', '', 'Administrator/Admin_Home.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_7', 'NavControl__ctl1_group_1', 'NavControl__ctl1_group_8', 250, null);">ADMINISTRATOR</td><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_20" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_20', 'NavControl__ctl1_group_21', 'belowleft', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '15', '', 'MyWork/AIE.aspx?ClearForm=true'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_20', 'NavControl__ctl1_group_1', 'NavControl__ctl1_group_21', 250, null);">AIE</td><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_21" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_21', 'NavControl__ctl1_group_22', 'belowleft', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '16', '', 'Reports/ReportsFrontPage.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_21', 'NavControl__ctl1_group_1', 'NavControl__ctl1_group_22', 250, null);">REPORTS</td><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_22" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_22', 'NavControl__ctl1_group_23', 'belowleft', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '17', '', 'javascript:window.open(&quot;[appRoot]/Documentation/PubAIMS_3_02/AIMS_Master_Page.htm&quot;)'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_22', 'NavControl__ctl1_group_1', 'NavControl__ctl1_group_23', 250, null);">HELP</td><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_23" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_23', 'NavControl__ctl1_group_24', 'belowleft', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '34', '', 'Logon.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_23', 'NavControl__ctl1_group_1', 'NavControl__ctl1_group_24', 250, null);">LOGOFF</td></tr></tbody></table></span><span ><table class="MenuGroup" id="NavControl__ctl1_group_2" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_2')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_2', 'NavControl__ctl1_item_1', 'NavControl__ctl1_group_1', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_2" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_2', 'NavControl__ctl1_group_3', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '11', '', 'MyWork/Inbox.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_2', 'NavControl__ctl1_group_2', 'NavControl__ctl1_group_3', 250, null);">INBOX</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_3" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_3', 'NavControl__ctl1_group_4', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '12', '', 'MyWork/Coordinator.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_3', 'NavControl__ctl1_group_2', 'NavControl__ctl1_group_4', 250, null);">COORDINATOR</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_4" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_4', 'NavControl__ctl1_group_5', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '13', '', 'MyWork/ErrorManagement.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_4', 'NavControl__ctl1_group_2', 'NavControl__ctl1_group_5', 250, null);">ERROR MANAGER</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_5" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_5', 'NavControl__ctl1_group_6', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '14', '', 'MyWork/Invoice_Search.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_5', 'NavControl__ctl1_group_2', 'NavControl__ctl1_group_6', 250, null);">SEARCH</td></tr></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_3" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_3')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_3', 'NavControl__ctl1_item_2', 'NavControl__ctl1_group_2', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_4" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_4')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_4', 'NavControl__ctl1_item_3', 'NavControl__ctl1_group_2', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_5" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_5')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_5', 'NavControl__ctl1_item_4', 'NavControl__ctl1_group_2', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_6" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_6')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_6', 'NavControl__ctl1_item_5', 'NavControl__ctl1_group_2', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_7" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_7')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_7', 'NavControl__ctl1_item_6', 'NavControl__ctl1_group_1', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_8" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_8')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_8', 'NavControl__ctl1_item_7', 'NavControl__ctl1_group_1', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_8" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_8', 'NavControl__ctl1_group_9', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '19', '', 'Administrator/GeneralSettings_View.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_8', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_9', 250, null);">GENERAL SETTINGS</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_9" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_9', 'NavControl__ctl1_group_10', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '30', '', 'Integration/Log_View.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_9', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_10', 250, null);">INTEGRATION LOG</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_10" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_10', 'NavControl__ctl1_group_11', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '20', '', 'Administrator/Lookup_List.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_10', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_11', 250, null);">LOOKUPS</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_11" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_11', 'NavControl__ctl1_group_12', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '37', '', 'NotificationLog/Log_View.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_11', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_12', 250, null);">NOTIFICATION LOG</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_12" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_12', 'NavControl__ctl1_group_13', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '7', '', 'Administrator/MatterType_List.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_12', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_13', 250, null);">MATTER TYPES</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_13" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_13', 'NavControl__ctl1_group_14', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '6', '', 'Professional/Prof_List.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_13', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_14', 250, null);">PROFESSIONALS</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_14" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_14', 'NavControl__ctl1_group_15', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '9', '', 'Vendor/Vendor_List.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_14', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_15', 250, null);">VENDORS</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_15" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_15', 'NavControl__ctl1_group_16', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '32', '', 'Workflow/WorkflowTitle_List.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_15', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_16', 250, null);">WORKFLOW TITLES</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_16" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_16', 'NavControl__ctl1_group_17', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '36', '', 'Professional/Prof_SubList.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_16', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_17', 250, null);">WORKFLOW SEARCH-REPLACE</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_17" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_17', 'NavControl__ctl1_group_18', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '5', '', 'Workgroup/Workgroup_List.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_17', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_18', 250, null);">WORKGROUPS</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_18" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_18', 'NavControl__ctl1_group_19', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '33', '', 'Administrator/System_View.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_18', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_19', 250, null);">SYSTEM POLICY</td></tr><tr ><td class="MenuItem" onmousemove="return false;" id="NavControl__ctl1_item_19" ondblclick="return false;" onmouseover="this.className='MenuItemOver';if (document.readyState == 'complete') aspnm_itemMsOver('NavControl__ctl1_item_19', 'NavControl__ctl1_group_20', 'rightdown', 0, 0, 250, null);" onclick="aspnm_hideAllGroups();Link_OnClick(this.id, '18', '', 'Timekeeper/TimekeeperClassification_List.aspx'); " onmouseout="this.className='MenuItem';if (document.readyState == 'complete') aspnm_itemMsOut('NavControl__ctl1_item_19', 'NavControl__ctl1_group_8', 'NavControl__ctl1_group_20', 250, null);">TIMEKEEPER CLASSIFICATIONS</td></tr></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_9" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_9')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_9', 'NavControl__ctl1_item_8', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_10" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_10')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_10', 'NavControl__ctl1_item_9', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_11" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_11')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_11', 'NavControl__ctl1_item_10', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_12" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_12')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_12', 'NavControl__ctl1_item_11', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_13" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_13')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_13', 'NavControl__ctl1_item_12', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_14" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_14')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_14', 'NavControl__ctl1_item_13', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_15" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_15')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_15', 'NavControl__ctl1_item_14', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_16" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_16')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_16', 'NavControl__ctl1_item_15', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_17" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_17')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_17', 'NavControl__ctl1_item_16', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_18" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_18')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_18', 'NavControl__ctl1_item_17', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_19" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_19')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_19', 'NavControl__ctl1_item_18', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_20" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_20')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_20', 'NavControl__ctl1_item_19', 'NavControl__ctl1_group_8', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_21" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_21')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_21', 'NavControl__ctl1_item_20', 'NavControl__ctl1_group_1', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_22" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_22')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_22', 'NavControl__ctl1_item_21', 'NavControl__ctl1_group_1', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_23" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_23')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_23', 'NavControl__ctl1_item_22', 'NavControl__ctl1_group_1', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table class="MenuGroup" id="NavControl__ctl1_group_24" onmouseover="aspnm_groupMsOver('NavControl__ctl1_group_24')" style="Z-INDEX: 999; LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px" onmouseout="if (document.readyState == 'complete') aspnm_groupMsOut('NavControl__ctl1_group_24', 'NavControl__ctl1_item_23', 'NavControl__ctl1_group_1', 250, null);" cellSpacing="0" cellPadding="0" border="0"><tbody ></tbody></table><table style="LEFT: 0px; VISIBILITY: hidden; POSITION: absolute; TOP: 0px"><tbody ><tr ><td ><br /></td></tr></tbody></table></span></td><td ><img src="/Aims/images/nav-rightend.gif"/></td></tr></tbody></table></td><td vAlign="top" align="left" width="20"><img height="70" alt="" src="/AIMS/images/header-rightend.gif" width="20" border="0"/></td></tr></tbody></table></td></tr><tr ><td width="100%" height="100%"><table height="100%" width="100%" border="0"><tbody ><tr ><td vAlign="top" align="middle"><table border="0"><tbody ><tr ><td align="left"><center ><div id="CustomValidationSummary" style="DISPLAY: none"><div style="DISPLAY: none"></div><table style="BORDER-COLLAPSE: collapse" cellSpacing="0" cellPadding="0"><tbody ><tr ><td ><img src="/AIMS/images/alertLeftTop.gif"/><br /><br /></td><td ><table style="BORDER-COLLAPSE: collapse" cellSpacing="0" cellPadding="0"><tbody ><tr ><td width="50%" background="/AIMS/images/alertHorizontal.gif"><br /><br /></td><td ><img src="/AIMS/images/alert.gif"/><br /><br /></td><td width="50%" background="/AIMS/images/alertHorizontal.gif"><br /><br /></td></tr></tbody></table><br /><br /></td><td ><img src="/AIMS/images/alertRightTop.gif"/><br /><br /></td></tr><tr ><td background="/AIMS/images/alertLeftVertical.gif"><br /><br /></td><td vAlign="center" align="middle"><div id="ValidationSummaryHeader">Header Text<br /><br /></div><div id="ValidationSummaryItem" style="DISPLAY: none">- Item Text<br /></div><br /><br /></td><td background="/AIMS/images/alertRightVertical.gif"><br /><br /></td></tr><tr ><td ><img src="/AIMS/images/alertLeftBottom.gif"/><br /><br /></td><td background="/AIMS/images/alertHorizontal.gif"><br /><br /></td><td ><img src="/AIMS/images/alertRightBottom.gif"/><br /><br /></td></tr></tbody></table></div></center><input id="Version_FetchMatterVendorSummaryBO" type="hidden" value="0" name="Version_FetchMatterVendorSummaryBO"/><input id="Version_DocumentBO" type="hidden" value="0" name="Version_DocumentBO"/><input id="Version_MatterBO" type="hidden" value="0" name="Version_MatterBO"/><input id="Version_CustomSettingsBO" type="hidden" value="0" name="Version_CustomSettingsBO"/> <table class="Clear"><tbody ><tr ><td class="Title">ListMatters</td></tr></tbody></table><br /><div id="Panel1" style="WIDTH: 400px; HEIGHT: 21px"><span class="DCViewListActive">Open</span><span class="DCViewListActive"> |</span> <a class="DCViewListInactive" id="DCViewLink2" onclick="setView('Closed',this);" href="javascript:__doPostBack('DCViewLink2','')" pagecontext="">Closed</a><span class="DCViewListActive"> |</span> <a class="DCViewListInactive" id="DCViewLink3" onclick="setView('All',this);" href="javascript:__doPostBack('DCViewLink3','')" pagecontext="">All</a> </div></td></tr><tr ><td ><div id="pnOpen" style="DISPLAY: block; WIDTH: 160px"><table class="Normal"><tbody ><tr class="Header"><td class="Header">Open Matters </td><td align="right"><table class="Invisible"><tbody ><tr ><td ><div id="DCImageButton1" title="Add Matter" onclick="DCImageButton1_onclick(this);return true;" anchorclicked="false"><table style="BORDER-COLLAPSE: collapse" cellSpacing="0" cellPadding="0" width="28" border="0"><tbody ><tr ><td width="1%"><img src="/AIMS/images/bt2Left26gray.gif" border="0"/></td><td style="PADDING-RIGHT: 2px; PADDING-LEFT: 4px" vAlign="center" align="middle" width="98%" background="/AIMS/images/bt2Center26gray.gif"><a class="ButtonText" id="imgbtaDCImageButton1" href="#AimsStandardLink"><nobr ><div class="ButtonText" id="dvbtDCImageButton1Text" style="DISPLAY: inline">Add</div></nobr></a></td><td style="PADDING-RIGHT: 4px; PADDING-LEFT: 2px" vAlign="center" align="middle" width="98%" background="/AIMS/images/bt2Center26gray.gif"><img id="imgbtDCImageButton1" src="/AIMS/images/add.gif" border="0"/></td><td width="1%"><img src="/AIMS/images/bt2Right26gray.gif" border="0"/></td></tr></tbody></table></div></td><td ><div id="DCCollapseButton1" title="" onclick="DCCollapseButton1_onclick(this);return true;" anchorclicked="false"><table style="BORDER-COLLAPSE: collapse" cellSpacing="0" cellPadding="0" width="0" border="0"><tbody ><tr ><td width="1%"><img src="/AIMS/images/bt2Left26gray.gif" border="0"/></td><td style="PADDING-RIGHT: 4px; PADDING-LEFT: 4px" vAlign="center" align="middle" width="98%" background="/AIMS/images/bt2Center26gray.gif"><a class="ButtonText" id="imgbtaDCCollapseButton1" href="#AimsStandardLink"><img id="imgbtDCCollapseButton1" src="/AIMS/images/blue-chevron_up.gif" border="0"/></a></td><td width="1%"><img src="/AIMS/images/bt2Right26gray.gif" border="0"/></td></tr></tbody></table></div><input id="SearchState" type="hidden" value="False" name="SearchState"/></td></tr></tbody></table></td></tr></tbody></table></div><div id="panelSearch" style="DISPLAY: block"><table class="Normal" id="Table1"><tbody ><tr class="Report"><td class="Report"><br /></td><td class="Report"><br /></td><td class="Report"><br /></td><td class="Report" style="WIDTH: 287px"><br /></td><td class="Report"><br /></td></tr><tr class="Report"><td class="Report"><br /></td><td class="Report"><br /></td><td class="Report"><br /></td><td class="Report" style="WIDTH: 283px"><br /></td><td class="Report" align="right"><br /></td></tr></tbody></table></div></td></tr></tbody></table></td></tr></tbody></table></td></tr><tr ><td ><table cellSpacing="0" cellPadding="0" width="100%"><tbody ><tr ><td vAlign="top" align="left" background="/AIMS/images/footer-bkgd.gif" colSpan="3"><img height="17" alt="" src="/AIMS/images/footer-bkgd.gif" width="50" border="0"/></td></tr><tr ><td vAlign="top" align="left" colSpan="3"><font face="verdana" color="#666666" size="1"><br /></font></td></tr></tbody></table></td></tr></tbody></table><input id="ViewLinkId" type="hidden" name="ViewLinkId"/><input id="View" type="hidden" value="Default" name="View"/><input id="CausesValidation" type="hidden" name="CausesValidation"/><input id="RenderLocation" type="hidden" value="Server" name="RenderLocation"/> </form></body>
+</HTML>
diff --git a/jni/ruby/test/rexml/data/ticket_68.xml b/jni/ruby/test/rexml/data/ticket_68.xml
new file mode 100644
index 0000000..7ab34b0
--- /dev/null
+++ b/jni/ruby/test/rexml/data/ticket_68.xml
@@ -0,0 +1,590 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Rev 1.05
+ Changed the <gram> element name to <pos>
+ Added the g_gend attribute
+ moved the s_inf element
+-->
+<!-- Rev 1.04
+ Changes:
+ Rename the project "JMdict" and add the g_lang attribute to the
+ <gloss> entity - 08 May 1999
+ Moved the <gram>, <field> and <misc> elements down to be in the
+ <sense> region, as suggested by Chris Czeyka. I have also tidied up
+ some of the "*" as he suggested. - 27 May 2000
+ Added the re_nokanji element - Sep 2003.
+ -->
+<!DOCTYPE JMdict [
+<!ELEMENT JMdict (entry*)>
+<!-- -->
+<!ELEMENT entry (ent_seq, k_ele*, r_ele+, info*, sense+)>
+ <!-- Entries consist of kanji elements, reading elements,
+ general information and sense elements. Each entry must have at
+ least one reading element and one sense element. Others are optional.
+ -->
+<!ELEMENT ent_seq (#PCDATA)>
+ <!-- A unique numeric sequence number for each entry
+ -->
+<!ELEMENT k_ele (keb, ke_inf*, ke_pri*)>
+ <!-- The kanji element, or in its absence, the reading element, is
+ the defining component of each entry.
+ The overwhelming majority of entries will have a single kanji
+ element associated with a word in Japanese. Where there are
+ multiple kanji elements within an entry, they will be orthographical
+ variants of the same word, either using variations in okurigana, or
+ alternative and equivalent kanji. Common "mis-spellings" may be
+ included, provided they are associated with appropriate information
+ fields. Synonyms are not included; they may be indicated in the
+ cross-reference field associated with the sense element.
+ -->
+<!ELEMENT keb (#PCDATA)>
+ <!-- This element will contain a word or short phrase in Japanese
+ which is written using at least one kanji. The valid characters are
+ kanji, kana, related characters such as chouon and kurikaeshi, and
+ in exceptional cases, letters from other alphabets.
+ -->
+<!ELEMENT ke_inf (#PCDATA)>
+ <!-- This is a coded information field related specifically to the
+ orthography of the keb, and will typically indicate some unusual
+ aspect, such as okurigana irregularity.
+ -->
+<!ELEMENT ke_pri (#PCDATA)>
+ <!-- This and the equivalent re_pri field are provided to record
+ information about the relative priority of the entry, and consist
+ of codes indicating the word appears in various references which
+ can be taken as an indication of the frequency with which the word
+ is used. This field is intended for use either by applications which
+ want to concentrate on entries of a particular priority, or to
+ generate subset files.
+ The current values in this field are:
+ - news1/2: appears in the "wordfreq" file compiled by Alexandre Girardi
+ from the Mainichi Shimbun. (See the Monash ftp archive for a copy.)
+ Words in the first 12,000 in that file are marked "news1" and words
+ in the second 12,000 are marked "news2".
+ - ichi1/2: appears in the "Ichimango goi bunruishuu", Senmon Kyouiku
+ Publishing, Tokyo, 1998.
+ - spec1 and spec2: a small number of words use this marker when they
+ are detected as being common, but are not included in other lists.
+ - gai1: common loanwords, based on the wordfreq file.
+ - nfxx: this is an indicator of frequency-of-use ranking in the
+ wordfreq file. "xx" is the number of the set of 500 words in which
+ the entry can be found, with "01" assigned to the first 500, "02"
+ to the second, and so on.
+
+ The reason both the kanji and reading elements are tagged is because
+ on occasions a priority is only associated with a particular
+ kanji/reading pair.
+ -->
+<!-- -->
+<!ELEMENT r_ele (reb, re_nokanji?, re_restr*, re_inf*, re_pri*)>
+ <!-- The reading element typically contains the valid readings
+ of the word(s) in the kanji element using modern kanadzukai.
+ Where there are multiple reading elements, they will typically be
+ alternative readings of the kanji element. In the absence of a
+ kanji element, i.e. in the case of a word or phrase written
+ entirely in kana, these elements will define the entry.
+ -->
+<!ELEMENT reb (#PCDATA)>
+ <!-- this element content is restricted to kana and related
+ characters such as chouon and kurikaeshi. Kana usage will be
+ consistent between the keb and reb elements; e.g. if the keb
+ contains katakana, so too will the reb.
+ -->
+<!ELEMENT re_nokanji (#PCDATA)>
+ <!-- This element, which will usually have a null value, indicates
+ that the reb, while associated with the keb, cannot be regarded
+ as a true reading of the kanji. It is typically used for words
+ such as foreign place names, gairaigo which can be in kanji or
+ katakana, etc.
+ -->
+<!ELEMENT re_restr (#PCDATA)>
+ <!-- This element is used to indicate when the reading only applies
+ to a subset of the keb elements in the entry. In its absence, all
+ readings apply to all kanji elements. The contents of this element
+ must exactly match those of one of the keb elements.
+ -->
+<!ELEMENT re_inf (#PCDATA)>
+ <!-- General coded information pertaining to the specific reading.
+ Typically it will be used to indicate some unusual aspect of
+ the reading. -->
+<!ELEMENT re_pri (#PCDATA)>
+ <!-- See the comment on ke_pri above. -->
+<!-- -->
+<!ELEMENT info (lang*, dial*, links*, bibl*, etym*, audit*)>
+ <!-- general coded information relating to the entry as a whole.-->
+<!ELEMENT lang (#PCDATA)>
+ <!-- For loan-words, the ISO 639 two-letter code for the originating
+ language. -->
+<!ELEMENT dial (#PCDATA)>
+ <!-- For words specifically associated with regional dialects in
+ Japanese, the entity code for that dialect, e.g. ksb for Kansaiben.
+ -->
+<!ELEMENT bibl (bib_tag?, bib_txt?)>
+<!ELEMENT bib_tag (#PCDATA)>
+<!ELEMENT bib_txt (#PCDATA)>
+ <!-- Bibliographic information about the entry. The bib_tag will a
+ coded reference to an entry in an external bibliographic database.
+ The bib_txt field may be used for brief (local) descriptions.-->
+<!ELEMENT etym (#PCDATA)>
+ <!-- This field is used to hold information about the etymology
+ of the entry. -->
+<!ELEMENT links (link_tag, link_desc, link_uri)>
+<!ELEMENT link_tag (#PCDATA)>
+<!ELEMENT link_desc (#PCDATA)>
+<!ELEMENT link_uri (#PCDATA)>
+ <!-- This element holds details of linking information to
+ entries in other electronic repositories. The link_tag will be
+ coded to indicate the type of link (text, image, sound), the
+ link_desc will provided a textual label for the link, and the
+ link_uri contains the actual URI. -->
+<!ELEMENT audit (upd_date, upd_detl)>
+<!ELEMENT upd_date (#PCDATA)>
+<!ELEMENT upd_detl (#PCDATA)>
+ <!-- The audit element will contain the date and other information
+ about updates to the entry. Can be used to record the source of
+ the material. -->
+<!-- -->
+<!ELEMENT sense (stagk*, stagr*, pos*, xref*, ant*, field*, misc*, s_inf*, gloss*, example*)>
+ <!-- The sense element will record the translational equivalent
+ of the Japanese word, plus other related information. Where there
+ are several distinctly different meanings of the word, multiple
+ sense elements will be employed.
+ -->
+<!ELEMENT stagk (#PCDATA)>
+<!ELEMENT stagr (#PCDATA)>
+ <!-- These elements, if present, indicate that the sense is restricted
+ to the lexeme represented by the keb and/or reb. -->
+<!ELEMENT xref (#PCDATA)*>
+ <!-- This element is used to indicate a cross-reference to another
+ entry with a similar or related meaning or sense. The content of
+ this element must exactly match that of a keb or reb element in
+ another entry.
+ -->
+<!ELEMENT ant (#PCDATA)*>
+ <!-- This element is used to indicate another entry which is an
+ antonym of the current entry/sense. The content of this element
+ must exactly match that of a keb or reb element in another entry.
+ -->
+<!ELEMENT pos (#PCDATA)>
+ <!-- Part-of-speech information about the entry/sense. Should use
+ appropriate entity codes.
+ -->
+<!ELEMENT field (#PCDATA)>
+ <!-- Information about the field of application of the entry. When
+ absent, general application is implied. Entity coding for specific
+ fields of application. -->
+<!ELEMENT misc (#PCDATA)>
+ <!-- This element is used for other relevant information about
+ the entry. -->
+<!ELEMENT gloss (#PCDATA | pri)*>
+ <!-- Within each sense will be one or more "glosses", i.e.
+ target-language words or phrases which are equivalents to the
+ Japanese word. This element would normally be present, however it
+ may be omitted in entries which are purely for a cross-reference.
+ -->
+<!ATTLIST gloss g_lang CDATA "en">
+ <!-- The g_lang attribute defines the target language of the
+ gloss. It will be coded using the two-letter language code from
+ the ISO 639 standard. When absent, the value "en" (i.e. English)
+ is the default value. -->
+<!ATTLIST gloss g_gend CDATA #IMPLIED>
+ <!-- The g_gend attribute defines the gender of the gloss (typically
+ a noun in the target language. When absent, the gender is either
+ not relevant or has yet to be provided.
+ -->
+<!ELEMENT pri (#PCDATA)>
+ <!-- These elements highlight particular target-language words which
+ are strongly associated with the Japanese word. The purpose is to
+ establish a set of target-language words which can effectively be
+ used as head-words in a reverse target-language/Japanese relationship.
+ -->
+<!ELEMENT example (#PCDATA)>
+ <!-- The example elements provide for pairs of short Japanese and
+ target-language phrases or sentences which exemplify the usage of the
+ Japanese head-word and the target-language gloss. Words in example
+ fields would typically not be indexed by a dictionary application.
+ -->
+<!ELEMENT s_inf (#PCDATA)>
+ <!-- The sense-information elements provided for additional
+ information to be recorded about a sense. Typical usage would
+ be to indicate such things as level of currency of a sense, the
+ regional variations, etc.
+ -->
+<!-- The following entity codes are used for common elements within the
+various information fields.
+-->
+<!ENTITY MA "martial arts term">
+<!ENTITY X "rude or X-rated term (not displayed in educational software)">
+<!ENTITY abbr "abbreviation">
+<!ENTITY adj "adjective (keiyoushi)">
+<!ENTITY adj-na "adjectival nouns or quasi-adjectives (keiyodoshi)">
+<!ENTITY adj-no "nouns which may take the genitive case particle `no'">
+<!ENTITY adj-pn "pre-noun adjectival (rentaishi)">
+<!ENTITY adj-t "`taru' adjective">
+<!ENTITY adv "adverb (fukushi)">
+<!ENTITY adv-n "adverbial noun">
+<!ENTITY adv-to "adverb taking the `to' particle">
+<!ENTITY arch "archaism">
+<!ENTITY ateji "ateji (phonetic) reading">
+<!ENTITY aux "auxiliary">
+<!ENTITY aux-v "auxiliary verb">
+<!ENTITY aux-adj "auxiliary adjective">
+<!ENTITY Buddh "Buddhist term">
+<!ENTITY chn "children's language">
+<!ENTITY col "colloquialism ">
+<!ENTITY comp "computer terminology">
+<!ENTITY conj "conjunction">
+<!ENTITY derog "derogatory">
+<!ENTITY ek "exclusively kanji">
+<!ENTITY exp "Expressions (phrases, clauses, etc.)">
+<!ENTITY fam "familiar language ">
+<!ENTITY fem "female term or language">
+<!ENTITY food "food term">
+<!ENTITY geom "geometry term">
+<!ENTITY gikun "gikun (meaning) reading">
+<!ENTITY gram "grammatical term">
+<!ENTITY hon "honorific or respectful (sonkeigo) language ">
+<!ENTITY hum "humble (kenjougo) language ">
+<!ENTITY iK "word containing irregular kanji usage">
+<!ENTITY id "idiomatic expression ">
+<!ENTITY ik "word containing irregular kana usage">
+<!ENTITY int "interjection (kandoushi)">
+<!ENTITY io "irregular okurigana usage">
+<!ENTITY iv "irregular verb">
+<!ENTITY ling "linguistics terminology">
+<!ENTITY m-sl "manga slang">
+<!ENTITY male "male term or language">
+<!ENTITY male-sl "male slang">
+<!ENTITY math "mathematics">
+<!ENTITY mil "military">
+<!ENTITY n "noun (common) (futsuumeishi)">
+<!ENTITY n-adv "adverbial noun (fukushitekimeishi)">
+<!ENTITY n-suf "noun, used as a suffix">
+<!ENTITY n-pref "noun, used as a prefix">
+<!ENTITY n-t "noun (temporal) (jisoumeishi)">
+<!ENTITY neg "negative (in a negative sentence, or with negative verb)">
+<!ENTITY neg-v "negative verb (when used with)">
+<!ENTITY num "numeric">
+<!ENTITY oK "word containing out-dated kanji ">
+<!ENTITY obs "obsolete term">
+<!ENTITY obsc "obscure term">
+<!ENTITY ok "out-dated or obsolete kana usage">
+<!ENTITY pol "polite (teineigo) language ">
+<!ENTITY pref "prefix ">
+<!ENTITY prt "particle ">
+<!ENTITY physics "physics terminology">
+<!ENTITY qv "quod vide (see another entry)">
+<!ENTITY rare "rare">
+<!ENTITY sl "slang">
+<!ENTITY suf "suffix ">
+<!ENTITY uK "word usually written using kanji alone ">
+<!ENTITY uk "word usually written using kana alone ">
+<!ENTITY v1 "Ichidan verb">
+<!ENTITY v5 "Godan verb (not completely classified)">
+<!ENTITY v5aru "Godan verb - -aru special class">
+<!ENTITY v5b "Godan verb with `bu' ending">
+<!ENTITY v5g "Godan verb with `gu' ending">
+<!ENTITY v5k "Godan verb with `ku' ending">
+<!ENTITY v5k-s "Godan verb - Iku/Yuku special class">
+<!ENTITY v5m "Godan verb with `mu' ending">
+<!ENTITY v5n "Godan verb with `nu' ending">
+<!ENTITY v5r "Godan verb with `ru' ending">
+<!ENTITY v5r-i "Godan verb with `ru' ending (irregular verb)">
+<!ENTITY v5s "Godan verb with `su' ending">
+<!ENTITY v5t "Godan verb with `tsu' ending">
+<!ENTITY v5u "Godan verb with `u' ending">
+<!ENTITY v5u-s "Godan verb with `u' ending (special class)">
+<!ENTITY v5uru "Godan verb - Uru old class verb (old form of Eru)">
+<!ENTITY vi "intransitive verb ">
+<!ENTITY vk "Kuru verb - special class">
+<!ENTITY vs "noun or participle which takes the aux. verb suru">
+<!ENTITY vs-s "suru verb - special class">
+<!ENTITY vs-i "suru verb - irregular">
+<!ENTITY vz "zuru verb - (alternative form of -jiru verbs)">
+<!ENTITY vt "transitive verb">
+<!ENTITY vulg "vulgar expression or word ">
+<!ENTITY mg "masculine gender">
+<!ENTITY fg "feminine gender">
+<!ENTITY ng "neuter gender">
+]>
+<!-- JMdict created: 2006-09-11 -->
+<JMdict>
+<!-- JMdict Japanese-Multilingual Dictionary file (XML format) -->
+<!-- Using V1.05 of the DTD -->
+<!-- Copyright J.W. Breen (jwb@csse.monash.edu.au) -->
+<entry>
+<ent_seq>1000000</ent_seq>
+<k_ele>
+<keb>ヽ</keb>
+</k_ele>
+<r_ele>
+<reb>くりかえし</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>repetition mark in katakana</gloss>
+<gloss g_lang="de">Wiederholungszeichen für Katakana</gloss>
+<gloss g_lang="de">normalerweise nur in vertikaler Schreibweise verwendet</gloss>
+<gloss g_lang="fr">marque de la répétition dans katakana</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000010</ent_seq>
+<k_ele>
+<keb>ヾ</keb>
+</k_ele>
+<r_ele>
+<reb>くりかえし</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>voiced repetition mark in katakana</gloss>
+<gloss g_lang="de">stimmhaftes Wiederholungszeichen für Katakana</gloss>
+<gloss g_lang="de">normalerweise nur in vertikaler Schreibweise verwendet</gloss>
+<gloss g_lang="fr">marque de la répétition sonore dans katakana</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000020</ent_seq>
+<k_ele>
+<keb>ゝ</keb>
+</k_ele>
+<r_ele>
+<reb>くりかえし</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>repetition mark in hiragana</gloss>
+<gloss g_lang="de">Wiederholungszeichen für Hiragana</gloss>
+<gloss g_lang="de">normalerweise nur in vertikaler Schreibweise verwendet</gloss>
+<gloss g_lang="fr">marque de la répétition dans hiragana</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000030</ent_seq>
+<k_ele>
+<keb>ゞ</keb>
+</k_ele>
+<r_ele>
+<reb>くりかえし</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>voiced repetition mark in hiragana</gloss>
+<gloss g_lang="de">stimmhaftes Wiederholungszeichen für Hiragana</gloss>
+<gloss g_lang="de">normalerweise nur in vertikaler Schreibweise verwendet</gloss>
+<gloss g_lang="fr">marque de la répétition sonore dans hiragana</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000040</ent_seq>
+<k_ele>
+<keb>〃</keb>
+</k_ele>
+<r_ele>
+<reb>おなじく</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>ditto mark</gloss>
+<gloss g_lang="ru">знак "то же самое"</gloss>
+<gloss g_lang="de">Wiederholungszeichen in Tabellen</gloss>
+<gloss g_lang="fr">idem marque</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000050</ent_seq>
+<k_ele>
+<keb>仝</keb>
+</k_ele>
+<r_ele>
+<reb>どうじょう</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>"as above" mark</gloss>
+<gloss g_lang="de">Abkürzung für "siehe oben"</gloss>
+<gloss g_lang="fr">comme au-dessus</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000060</ent_seq>
+<k_ele>
+<keb>々</keb>
+</k_ele>
+<r_ele>
+<reb>くりかえし</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>repetition of kanji (sometimes voiced)</gloss>
+<gloss g_lang="de">Wiederholungszeichen für Kanji</gloss>
+<gloss g_lang="de">(Laut wird durch Wiederholung manchmal stimmhaft)</gloss>
+<gloss g_lang="fr">répétition de kanji(quelquefois a exprimé)</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000070</ent_seq>
+<k_ele>
+<keb>〆</keb>
+</k_ele>
+<r_ele>
+<reb>しめ</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>end or closure mark</gloss>
+<gloss g_lang="de">Zeichen als eine Art Versiegelung über der zugeklebten Lasche auf der Rückseite eines Briefumschlages</gloss>
+<gloss g_lang="fr">fin ou marque de la fermeture</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000080</ent_seq>
+<k_ele>
+<keb>〇</keb>
+</k_ele>
+<k_ele>
+<keb>○</keb>
+<ke_inf>&iK;</ke_inf>
+</k_ele>
+<r_ele>
+<reb>かんすうじゼロ</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<xref>漢数字</xref>
+<xref>ゼロ</xref>
+<gloss>"kanji" zero</gloss>
+<gloss g_lang="de">Kanji-Ziffer für Null</gloss>
+<gloss g_lang="fr">kanji</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000090</ent_seq>
+<k_ele>
+<keb>○</keb>
+</k_ele>
+<k_ele>
+<keb>〇</keb>
+<ke_inf>&iK;</ke_inf>
+</k_ele>
+<r_ele>
+<reb>まる</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>circle (sometimes used for zero)</gloss>
+<gloss g_lang="ru">круг</gloss>
+<gloss g_lang="ru">но́ль</gloss>
+<gloss g_lang="de">Kreis</gloss>
+<gloss g_lang="de">Markierung für "richtig"</gloss>
+<gloss g_lang="de">Maru</gloss>
+<gloss g_lang="de">(ein japan. Schriftfont hat mindestens drei verschiedene Codierungen und Darstellungen für "maru")</gloss>
+<gloss g_lang="fr">entourez</gloss>
+<gloss g_lang="fr">mettez à zéro</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000100</ent_seq>
+<k_ele>
+<keb>ABC順</keb>
+</k_ele>
+<r_ele>
+<reb>エービーシーじゅん</reb>
+</r_ele>
+<r_ele>
+<reb>ええびいしいじゅん</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>alphabetical order</gloss>
+<gloss g_lang="de">alphabetische Ordnung</gloss>
+<gloss g_lang="de">alphabetische Reihenfolge</gloss>
+<gloss g_lang="fr">ordre alphabétique</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000110</ent_seq>
+<k_ele>
+<keb>CDプレーヤー</keb>
+</k_ele>
+<r_ele>
+<reb>シーディープレーヤー</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>CD player</gloss>
+<gloss g_lang="ru">CD плеер</gloss>
+<gloss g_lang="ru">проигрыватель компакт-дисков</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000120</ent_seq>
+<k_ele>
+<keb>Hな映画</keb>
+</k_ele>
+<k_ele>
+<keb>エッチな映画</keb>
+</k_ele>
+<r_ele>
+<reb>エッチなえいが</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>pornographic film</gloss>
+<gloss>salacious film</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000130</ent_seq>
+<k_ele>
+<keb>N響</keb>
+</k_ele>
+<r_ele>
+<reb>エヌきょう</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<misc>&abbr;</misc>
+<gloss>NHK Symphony Orchestra</gloss>
+<gloss g_lang="fr">NHK Symphonie Orchestre(abbr)</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000140</ent_seq>
+<r_ele>
+<reb>Oバック</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>O-back</gloss>
+<gloss>skirt with peek-a-boo hole in rump</gloss>
+<gloss g_lang="fr">O En arrière</gloss>
+<gloss g_lang="fr">contournez avec coucou trou dans croupe</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+<entry>
+<ent_seq>1000150</ent_seq>
+<r_ele>
+<reb>RS232ケーブル</reb>
+</r_ele>
+<sense>
+<pos>&n;</pos>
+<gloss>rs232 cable</gloss>
+<gloss g_lang="fr">les rs232 câblent</gloss>
+<gloss g_lang="fr">(JF2)</gloss>
+</sense>
+</entry>
+</JMdict>
diff --git a/jni/ruby/test/rexml/data/tutorial.xml b/jni/ruby/test/rexml/data/tutorial.xml
new file mode 100644
index 0000000..bf5783d
--- /dev/null
+++ b/jni/ruby/test/rexml/data/tutorial.xml
@@ -0,0 +1,678 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/css"
+href="../../documentation/documentation.css"
+?>
+<?xml-stylesheet type="text/xsl"
+href="../../documentation/documentation.xsl"
+?>
+<!DOCTYPE documentation SYSTEM "http://www.germane-software.com/software/documentation/documentation.dtd">
+<documentation>
+ <head>
+ <title>REXML Tutorial</title>
+
+ <version>$Revision: 1.1.2.1 $</version>
+
+ <date>*2001-296+594</date>
+
+ <home>http://www.germane-software.com/~ser/software/rexml</home>
+
+ <base></base>
+
+ <language>ruby</language>
+
+ <author email="ser@germane-software.com"
+ href="http://www.germane-software.com/~ser">Sean Russell</author>
+ </head>
+
+ <overview>
+ <purpose lang="en">
+ <p>This is a tutorial for using <link
+ href="http://www.germane-software.com/~ser/software/rexml">REXML</link>,
+ a pure Ruby XML processor.</p>
+ </purpose>
+
+ <general>
+ <p>REXML was inspired by the Electric XML library for Java, which
+ features an easy-to-use API, small size, and speed. Hopefully, REXML,
+ designed with the same philosophy, has these same features. I've tried
+ to keep the API as intuitive as possible, and have followed the Ruby
+ methodology for method naming and code flow, rather than mirroring the
+ Java API.</p>
+
+ <p>REXML supports both tree and stream document parsing. Stream parsing
+ is faster (about 1.5 times as fast). However, with stream parsing, you
+ don't get access to features such as XPath.</p>
+
+ <p>The <link href="../doc/index.html">API</link> documentation also
+ contains code snippits to help you learn how to use various methods.
+ This tutorial serves as a starting point and quick guide to using
+ REXML.</p>
+
+ <subsection title="Tree Parsing XML and accessing Elements">
+ <p>We'll start with parsing an XML document</p>
+
+ <example>require "rexml/document"
+file = File.new( "mydoc.xml" )
+doc = REXML::Document.new file</example>
+
+ <p>Line 3 creates a new document and parses the supplied file. You can
+ also do the following</p>
+
+ <example>require "rexml/document"
+include REXML # so that we don't have to prefix everything with REXML::...
+string = &lt;&lt;EOF
+ &lt;mydoc&gt;
+ &lt;someelement attribute="nanoo"&gt;Text, text, text&lt;/someelement&gt;
+ &lt;/mydoc&gt;
+EOF
+doc = Document.new string</example>
+
+ <p>So parsing a string is just as easy as parsing a file. For future
+ examples, I'm going to omit both the <code>require</code> and
+ <code>include</code> lines.</p>
+
+ <p>Once you have a document, you can access elements in that document
+ in a number of ways:</p>
+
+ <list>
+ <item>The <code>Element</code> class itself has
+ <code>each_element_with_attribute</code>, a common way of accessing
+ elements.</item>
+
+ <item>The attribute <code>Element.elements</code> is an
+ <code>Elements</code> class instance which has the <code>each</code>
+ and <code>[]</code> methods for accessing elements. Both methods can
+ be supplied with an XPath for filtering, which makes them very
+ powerful.</item>
+
+ <item>Since <code>Element</code> is a subclass of Parent, you can
+ also access the element's children directly through the Array-like
+ methods <code>Element[], Element.each, Element.find,
+ Element.delete</code>. This is the fastest way of accessing
+ children, but note that, being a true array, XPath searches are not
+ supported, and that all of the element children are contained in
+ this array, not just the Element children.</item>
+ </list>
+
+ <p>Here are a few examples using these methods. First is the source
+ document used in the examples. Save this as mydoc.xml before running
+ any of the examples that require it:</p>
+
+ <example title="The source document">&lt;inventory title="OmniCorp Store #45x10^3"&gt;
+ &lt;section name="health"&gt;
+ &lt;item upc="123456789" stock="12"&gt;
+ &lt;name&gt;Invisibility Cream&lt;/name&gt;
+ &lt;price&gt;14.50&lt;/price&gt;
+ &lt;description&gt;Makes you invisible&lt;/description&gt;
+ &lt;/item&gt;
+ &lt;item upc="445322344" stock="18"&gt;
+ &lt;name&gt;Levitation Salve&lt;/name&gt;
+ &lt;price&gt;23.99&lt;/price&gt;
+ &lt;description&gt;Levitate yourself for up to 3 hours per application&lt;/description&gt;
+ &lt;/item&gt;
+ &lt;/section&gt;
+ &lt;section name="food"&gt;
+ &lt;item upc="485672034" stock="653"&gt;
+ &lt;name&gt;Blork and Freen Instameal&lt;/name&gt;
+ &lt;price&gt;4.95&lt;/price&gt;
+ &lt;description&gt;A tasty meal in a tablet; just add water&lt;/description&gt;
+ &lt;/item&gt;
+ &lt;item upc="132957764" stock="44"&gt;
+ &lt;name&gt;Grob winglets&lt;/name&gt;
+ &lt;price&gt;3.56&lt;/price&gt;
+ &lt;description&gt;Tender winglets of Grob. Just add water&lt;/description&gt;
+ &lt;/item&gt;
+ &lt;/section&gt;
+&lt;/inventory&gt;</example>
+
+ <example title="Accessing Elements">doc = Document.new File.new("mydoc.xml")
+doc.elements.each("inventory/section") { |element| puts element.attributes["name"] }
+# -&gt; health
+# -&gt; food
+doc.elements.each("*/section/item") { |element| puts element.attributes["upc"] }
+# -&gt; 123456789
+# -&gt; 445322344
+# -&gt; 485672034
+# -&gt; 132957764
+root = doc.root
+puts root.attributes["title"]
+# -&gt; OmniCorp Store #45x10^3
+puts root.elements["section/item[@stock='44']"].attributes["upc"]
+# -&gt; 132957764
+puts root.elements["section"].attributes["name"]
+# -&gt; health (returns the first encountered matching element)
+puts root.elements[1].attributes["name"]
+# -&gt; health (returns the FIRST child element)
+root.detect {|node| node.kind_of? Element and node.attributes["name"] == "food" }</example>
+
+ <p>Notice the second-to-last line of code. Element children in REXML
+ are indexed starting at 1, not 0. This is because XPath itself counts
+ elements from 1, and REXML maintains this relationship; IE,
+ <code>root.elements['*[1]'] == root.elements[1]</code>. The last line
+ finds the first child element with the name of "food". As you can see
+ in this example, accessing attributes is also straightforward.</p>
+
+ <p>You can also access xpaths directly via the XPath class.</p>
+
+ <example title="Using XPath"># The invisibility cream is the first &lt;item&gt;
+invisibility = XPath.first( doc, "//item" )
+# Prints out all of the prices
+XPath.each( doc, "//price") { |element| puts element.text }
+# Gets an array of all of the "name" elements in the document.
+names = XPath.match( doc, "//name" ) </example>
+
+ <p>Another way of getting an array of matching nodes is through
+ Element.elements.to_a(). Although this is a method on elements, if
+ passed an XPath it can return an array of arbitrary objects. This is
+ due to the fact that XPath itself can return arbitrary nodes
+ (Attribute nodes, Text nodes, and Element nodes).</p>
+
+ <example title="Using to_a()">all_elements = doc.elements.to_a
+all_children = doc.to_a
+all_upc_strings = doc.elements.to_a( "//item/attribute::upc" )
+all_name_elements = doc.elements.to_a( "//name" )</example>
+ </subsection>
+
+ <subsection title="Text Nodes">
+ <p>REXML attempts to make the common case simple, but this means that
+ the uncommon case can be complicated. This is especially true with
+ Text nodes.</p>
+
+ <p>Text nodes have a lot of behavior, and in the case of internal
+ entities, what you get may be different from what you expect. When
+ REXML reads an XML document, in parses the DTD and creates an internal
+ table of entities. If it finds any of these entities in the document,
+ it replaces them with their values:</p>
+
+ <example title="Entity Replacement">doc = Document.new '&lt;!DOCTYPE foo [
+&lt;!ENTITY ent "replace"&gt;
+]&gt;&lt;a&gt;&amp;ent;&lt;/a&gt;'
+doc.root.text #-&gt; "replace"
+</example>
+
+ <p>When you write the document back out, REXML replaces the values
+ with the entity reference:</p>
+
+ <example>doc.to_s
+# Generates:
+# &lt;!DOCTYPE foo [
+# &lt;!ENTITY ent "replace"&gt;
+# ]&gt;&lt;a&gt;&amp;ent;&lt;/a&gt;</example>
+
+ <p>But there's a problem. What happens if only some of the words are
+ also entity reference values?</p>
+
+ <example>doc = Document.new '&lt;!DOCTYPE foo [
+&lt;!ENTITY ent "replace"&gt;
+]&gt;&lt;a&gt;replace &amp;ent;&lt;/a&gt;'
+doc.root.text #-&gt; "replace replace"
+</example>
+
+ <p>Well, REXML does the only thing it can:</p>
+
+ <example>doc.to_s
+# Generates:
+# &lt;!DOCTYPE foo [
+# &lt;!ENTITY ent "replace"&gt;
+# ]&gt;&lt;a&gt;&amp;ent; &amp;ent;&lt;/a&gt;</example>
+
+ <p>This is probably not what you expect. However, when designing
+ REXML, I had a choice between this behavior, and using immutable text
+ nodes. The problem is that, if you can change the text in a node,
+ REXML can never tell which tokens you want to have replaced with
+ entities. There is a wrinkle: REXML will write what it gets in as long
+ as you don't access the text. This is because REXML does lazy
+ evaluation of entities. Therefore,</p>
+
+ <example title="Lazy Evaluation">doc = Document.new( '&lt;!DOCTYPE foo
+ [ &lt;!ENTITY ent "replace"&gt; ]&gt;&lt;a&gt;replace
+ &amp;ent;&lt;/a&gt;' ) doc.to_s # Generates: # &lt;!DOCTYPE foo [ #
+ &lt;!ENTITY ent "replace"&gt; # ]&gt;&lt;a&gt;<emphasis>replace
+ &amp;ent;</emphasis>&lt;/a&gt; doc.root.text #-&gt; Now accessed,
+ entities have been resolved doc.to_s # Generates: # &lt;!DOCTYPE foo [
+ # &lt;!ENTITY ent "replace"&gt; # ]&gt;&lt;a&gt;<emphasis>&amp;ent;
+ &amp;ent;</emphasis>&lt;/a&gt;</example>
+
+ <p>There is a programmatic solution: <code>:raw</code>. If you set the
+ <code>:raw</code> flag on any Text or Element node, the entities
+ within that node will not be processed. This means that you'll have to
+ deal with entities yourself:</p>
+
+ <example title="Entity Replacement">doc = Document.new('&lt;!DOCTYPE
+ foo [ &lt;!ENTITY ent "replace"&gt; ]&gt;&lt;a&gt;replace
+ &amp;ent;&lt;/a&gt;',<emphasis>{:raw=&gt;:all})</emphasis>
+ doc.root.text #-&gt; "replace &amp;ent;" doc.to_s # Generates: #
+ &lt;!DOCTYPE foo [ # &lt;!ENTITY ent "replace"&gt; #
+ ]&gt;&lt;a&gt;replace &amp;ent;&lt;/a&gt;</example>
+ </subsection>
+
+ <subsection title="Creating XML documents">
+ <p>Again, there are a couple of mechanisms for creating XML documents
+ in REXML. Adding elements by hand is faster than the convenience
+ method, but which you use will probably be a matter of aesthetics.</p>
+
+ <example title="Creating elements">el = someelement.add_element "myel"
+# creates an element named "myel", adds it to "someelement", and returns it
+el2 = el.add_element "another", {"id"=&gt;"10"}
+# does the same, but also sets attribute "id" of el2 to "10"
+el3 = Element.new "blah"
+el1.elements &lt;&lt; el3
+el3.attributes["myid"] = "sean"
+# creates el3 "blah", adds it to el1, then sets attribute "myid" to "sean"</example>
+
+ <p>If you want to add text to an element, you can do it by either
+ creating Text objects and adding them to the element, or by using the
+ convenience method <code>text=</code></p>
+
+ <example title="Adding text">el1 = Element.new "myelement"
+el1.text = "Hello world!"
+# -&gt; &lt;myelement&gt;Hello world!&lt;/myelement&gt;
+el1.add_text "Hello dolly"
+# -&gt; &lt;myelement&gt;Hello world!Hello dolly&lt;/element&gt;
+el1.add Text.new("Goodbye")
+# -&gt; &lt;myelement&gt;Hello world!Hello dollyGoodbye&lt;/element&gt;
+el1 &lt;&lt; Text.new(" cruel world")
+# -&gt; &lt;myelement&gt;Hello world!Hello dollyGoodbye cruel world&lt;/element&gt;</example>
+
+ <p>But note that each of these text objects are still stored as
+ separate objects; <code>el1.text</code> will return "Hello world!";
+ <code>el1[2]</code> will return a Text object with the contents
+ "Goodbye".</p>
+
+ <p>Please be aware that all text nodes in REXML are UTF-8 encoded, and
+ all of your code must reflect this. You may input and output other
+ encodings (UTF-8, UTF-16, ISO-8859-1, and UNILE are all supported,
+ input and output), but within your program, you must pass REXML UTF-8
+ strings.</p>
+
+ <p>I can't emphasize this enough, because people do have problems with
+ this. REXML can't possibly alway guess correctly how your text is
+ encoded, so it always assumes the text is UTF-8. It also does not warn
+ you when you try to add text which isn't properly encoded, for the
+ same reason. You must make sure that you are adding UTF-8 text.
+ &#160;If you're adding standard 7-bit ASCII, which is most common, you
+ don't have to worry. &#160;If you're using ISO-8859-1 text (characters
+ above 0x80), you must convert it to UTF-8 before adding it to an
+ element. &#160;You can do this with the shard:
+ <code>text.unpack("C*").pack("U*")</code>. If you ignore this warning
+ and add 8-bit ASCII characters to your documents, your code may
+ work... or it may not. &#160;In either case, REXML is not at fault.
+ You have been warned.</p>
+
+ <p>One last thing: alternate encoding output support only works from
+ Document.write() and Document.to_s(). If you want to write out other
+ nodes with a particular encoding, you must wrap your output object
+ with Output:</p>
+
+ <example title="Encoded Output">e = Element.new "&lt;a/&gt;"
+e.text = "f\xfcr" # ISO-8859-1 'ü'
+o = ''
+e.write( Output.new( o, "ISO-8859-1" ) )
+</example>
+
+ <p>You can pass Output any of the supported encodings.</p>
+
+ <p>If you want to insert an element between two elements, you can use
+ either the standard Ruby array notation, or
+ <code>Parent.insert_before</code> and
+ <code>Parent.insert_after</code>.</p>
+
+ <example title="Inserts">doc = Document.new "&lt;a&gt;&lt;one/&gt;&lt;three/&gt;&lt;/a&gt;"
+doc.root[1,0] = Element.new "two"
+# -&gt; &lt;a&gt;&lt;one/&gt;&lt;two/&gt;&lt;three/&gt;&lt;/a&gt;
+three = doc.elements["a/three"]
+doc.root.insert_after three, Element.new "four"
+# -&gt; &lt;a&gt;&lt;one/&gt;&lt;two/&gt;&lt;three/&gt;&lt;four/&gt;&lt;/a&gt;
+# A convenience method allows you to insert before/after an XPath:
+doc.root.insert_after( "//one", Element.new("one-five") )
+# -&gt; &lt;a&gt;&lt;one/&gt;&lt;one-five/&gt;&lt;two/&gt;&lt;three/&gt;&lt;four/&gt;&lt;/a&gt;
+# Another convenience method allows you to insert after/before an element:
+four = doc.elements["//four"]
+four.previous_sibling = Element.new("three-five")
+# -&gt; &lt;a&gt;&lt;one/&gt;&lt;one-five/&gt;&lt;two/&gt;&lt;three/&gt;&lt;three-five/&gt;&lt;four/&gt;&lt;/a&gt;</example>
+
+ <p>The <code>raw</code> flag in the <code>Text</code> constructor can
+ be used to tell REXML to leave strings which have entities defined for
+ them alone.</p>
+
+ <example title="Raw text">doc = Document.new( "&lt;?xml version='1.0?&gt;
+&lt;!DOCTYPE foo SYSTEM 'foo.dtd' [
+&lt;!ENTITY % s "Sean"&gt;
+]&gt;
+&lt;a/&gt;"
+t = Text.new( "Sean", false, nil, false )
+doc.root.text = t
+t.to_s # -&gt; &amp;s;
+t = Text.new( "Sean", false, nil, true )
+doc.root.text = t
+t.to_s # -&gt; Sean</example>
+
+ <p>Note that, in all cases, the <code>value()</code> method returns
+ the text with entities expanded, so the <code>raw</code> flag only
+ affects the <code>to_s()</code> method. If the <code>raw</code> is set
+ for a text node, then <code>to_s()</code> will not entities will not
+ normalize (turn into entities) entity values. You can not create raw
+ text nodes that contain illegal XML, so the following will generate a
+ parse error:</p>
+
+ <example>t = Text.new( "&amp;", false, nil, true )</example>
+
+ <p>You can also tell REXML to set the Text children of given elements
+ to raw automatically, on parsing or creating:</p>
+
+ <example title="Automatic raw text handling">doc = REXML::Document.new( source, { :raw =&gt; %w{ tag1 tag2 tag3 } }</example>
+
+ <p>In this example, all tags named "tag1", "tag2", or "tag3" will have
+ any Text children set to raw text. If you want to have all of the text
+ processed as raw text, pass in the :all tag:</p>
+
+ <example title="Raw documents">doc = REXML::Document.new( source, { :raw =&gt; :all })</example>
+ </subsection>
+
+ <subsection title="Writing a tree">
+ <p>There aren't many things that are more simple than writing a REXML
+ tree. Simply pass an object that supports <code>&lt;&lt;( String
+ )</code> to the <code>write</code> method of any object. In Ruby, both
+ IO instances (File) and String instances support &lt;&lt;.</p>
+
+ <example>doc.write $stdout
+output = ""
+doc.write output</example>
+
+ <p>If you want REXML to pretty-print output, pass <code>write()</code>
+ an indent value greater than -1:</p>
+
+ <example title="Write with pretty-printing">doc.write( $stdout, 0 )</example>
+
+ <p>REXML will not, by default, write out the XML declaration unless
+ you specifically ask for them. If a document is read that contains an
+ XML declaration, that declaration <emphasis>will</emphasis> be written
+ faithfully. The other way you can tell REXML to write the declaration
+ is to specifically add the declaration:</p>
+
+ <example title="Adding an XML Declaration to a Document">doc = Document.new
+doc.add_element 'foo'
+doc.to_s #-&gt; &lt;foo/&gt;
+doc &lt;&lt; XMLDecl.new
+doc.to_s #-&gt; &lt;?xml version='1.0'?&gt;&lt;foo/&gt;</example>
+ </subsection>
+
+ <subsection title="Iterating">
+ <p>There are four main methods of iterating over children.
+ <code>Element.each</code>, which iterates over all the children;
+ <code>Element.elements.each</code>, which iterates over just the child
+ Elements; <code>Element.next_element</code> and
+ <code>Element.previous_element</code>, which can be used to fetch the
+ next Element siblings; and <code>Element.next_sibling</code> and
+ <code>Eleemnt.previous_sibling</code>, which fetches the next and
+ previous siblings, regardless of type.</p>
+ </subsection>
+
+ <subsection title="Stream Parsing">
+ <p>REXML stream parsing requires you to supply a Listener class. When
+ REXML encounters events in a document (tag start, text, etc.) it
+ notifies your listener class of the event. You can supply any subset
+ of the methods, but make sure you implement method_missing if you
+ don't implement them all. A StreamListener module has been supplied as
+ a template for you to use.</p>
+
+ <example title="Stream parsing">list = MyListener.new
+source = File.new "mydoc.xml"
+REXML::Document.parse_stream(source, list)</example>
+
+ <p>Stream parsing in REXML is much like SAX, where events are
+ generated when the parser encounters them in the process of parsing
+ the document. When a tag is encountered, the stream listener's
+ <code>tag_start()</code> method is called. When the tag end is
+ encountered, <code>tag_end()</code> is called. When text is
+ encountered, <code>text()</code> is called, and so on, until the end
+ of the stream is reached. One other note: the method
+ <code>entity()</code> is called when an <code>&amp;entity;</code> is
+ encountered in text, and only then.</p>
+
+ <p>Please look at the <link
+ href="../doc/classes/REXML/StreamListener.html">StreamListener
+ API</link> for more information.<footnote>You must generate the API
+ documentation with rdoc or download the API documentation from the
+ REXML website for this documentation.</footnote></p>
+ </subsection>
+
+ <subsection title="Whitespace">
+ <p>By default, REXML respects whitespace in your document. In many
+ applications, you want the parser to compress whitespace in your
+ document. In these cases, you have to tell the parser which elements
+ you want to respect whitespace in by passing a context to the
+ parser:</p>
+
+ <example title="Compressing whitespace">doc = REXML::Document.new( source, { :compress_whitespace =&gt; %w{ tag1 tag2 tag3 } }</example>
+
+ <p>Whitespace for tags "tag1", "tag2", and "tag3" will be compressed;
+ all other tags will have their whitespace respected. Like :raw, you
+ can set :compress_whitespace to :all, and have all elements have their
+ whitespace compressed.</p>
+
+ <p>You may also use the tag <code>:respect_whitespace</code>, which
+ flip-flops the behavior. If you use <code>:respect_whitespace</code>
+ for one or more tags, only those elements will have their whitespace
+ respected; all other tags will have their whitespace compressed.</p>
+ </subsection>
+
+ <subsection title="Automatic Entity Processing">
+ <p>REXML does some automatic processing of entities for your
+ convenience. The processed entities are &amp;, &lt;, &gt;, ", and '.
+ If REXML finds any of these characters in Text or Attribute values, it
+ automatically turns them into entity references when it writes them
+ out. Additionally, when REXML finds any of these entity references in
+ a document source, it converts them to their character equivalents.
+ All other entity references are left unprocessed. If REXML finds an
+ &amp;, &lt;, or &gt; in the document source, it will generate a
+ parsing error.</p>
+
+ <example title="Entity processing">bad_source = "&lt;a&gt;Cats &amp; dogs&lt;/a&gt;"
+good_source = "&lt;a&gt;Cats &amp;amp; &amp;#100;ogs&lt;/a&gt;"
+doc = REXML::Document.new bad_source
+# Generates a parse error
+doc = REXML::Document.new good_source
+puts doc.root.text
+# -&gt; "Cats &amp; &amp;#100;ogs"
+doc.root.write $stdout
+# -&gt; "&lt;a&gt;Cats &amp;amp; &amp;#100;ogs&lt;/a&gt;"
+doc.root.attributes["m"] = "x'y\"z"
+puts doc.root.attributes["m"]
+# -&gt; "x'y\"z"
+doc.root.write $stdout
+# -&gt; "&lt;a m='x&amp;apos;y&amp;quot;z'&gt;Cats &amp;amp; &amp;#100;ogs&lt;/a&gt;"</example>
+ </subsection>
+
+ <subsection title="Namespaces">
+ <p>Namespaces are fully supported in REXML and within the XPath
+ parser. There are a few caveats when using XPath, however:</p>
+
+ <list>
+ <item>If you don't supply a namespace mapping, the default namespace
+ mapping of the context element is used. This has its limitations,
+ but is convenient for most purposes.</item>
+
+ <item>If you need to supply a namespace mapping, you must use the
+ XPath methods <code>each</code>, <code>first</code>, and
+ <code>match</code> and pass them the mapping.</item>
+ </list>
+
+ <example title="Using namespaces">source = "&lt;a xmlns:x='foo' xmlns:y='bar'&gt;&lt;x:b id='1'/&gt;&lt;y:b id='2'/&gt;&lt;/a&gt;"
+doc = Document.new source
+doc.elements["/a/x:b"].attributes["id"] # -&gt; '1'
+XPath.first(doc, "/a/m:b", {"m"=&gt;"bar"}).attributes["id"] # -&gt; '2'
+doc.elements["//x:b"].prefix # -&gt; 'x'
+doc.elements["//x:b"].namespace # -&gt; 'foo'
+XPath.first(doc, "//m:b", {"m"=&gt;"bar"}).prefix # -&gt; 'y'</example>
+ </subsection>
+
+ <subsection title="Pull parsing">
+ <p>The pull parser API is not yet stable. When it settles down, I'll
+ fill in this section. For now, you'll have to bite the bullet and read
+ the <link
+ href="http://www.germane-software.com/software/rexml_doc/classes/REXML/PullParser.html">PullParser</link>
+ API docs. Ignore the PullListener class; it is a private helper
+ class.</p>
+ </subsection>
+
+ <subsection title="SAX2 Stream Parsing">
+ <p>The original REXML stream parsing API is very minimal. This also
+ means that it is fairly fast. For a more complex, more "standard" API,
+ REXML also includes a streaming parser with a SAX2+ API. This API
+ differs from SAX2 in a couple of ways, such as having more filters and
+ multiple notification mechanisms, but the core API is SAX2.</p>
+
+ <p>The two classes in the SAX2 API are <link
+ href="http://www.germane-software.com/software/rexml_doc/classes/REXML/SAX2Parser.html"><code>SAX2Parser</code></link>
+ and <link
+ href="http://www.germane-software.com/software/rexml_doc/classes/REXML/SAX2Listener.html"><code>SAX2Listener</code></link>.
+ You can use the parser in one of five ways, depending on your needs.
+ Three of the ways are useful if you are filtering for a small number
+ of events in the document, such as just printing out the names of all
+ of the elements in a document, or getting all of the text in a
+ document. The other two ways are for more complex processing, where
+ you want to be notified of multiple events. The first three involve
+ Procs, and the last two involve listeners. The listener mechanisms are
+ very similar to the original REXML streaming API, with the addition of
+ filtering options, and are faster than the proc mechanisms.</p>
+
+ <p>An example is worth a thousand words, so we'll just take a look at
+ a small example of each of the mechanisms. The first example involves
+ printing out only the text content of a document.</p>
+
+ <example title="Filtering for Events with Procs">require 'rexml/sax2parser'
+parser = REXML::SAX2Parser.new( File.new( 'documentation.xml' ) )
+parser.listen( :characters ) {|text| puts text }
+parser.parse</example>
+
+ <p>In this example, we tell the parser to call our block for every
+ <code>characters</code> event. "characters" is what SAX2 calls Text
+ nodes. The event is identified by the symbol <code>:characters</code>.
+ There are a number of these events, including
+ <code>:element_start</code>, <code>:end_prefix_mapping</code>, and so
+ on; the events are named after the methods in the
+ <code>SAX2Listener</code> API, so refer to that document for a
+ complete list.</p>
+
+ <p>You can additionally filter for particular elements by passing an
+ array of tag names to the <code>listen</code> method. In further
+ examples, we will not include the <code>require</code> or parser
+ construction lines, as they are the same for all of these
+ examples.</p>
+
+ <example title="Filtering for Events on Particular Elements with Procs">parser.listen( :characters, %w{ changelog todo } ) {|text| puts text }
+parser.parse</example>
+
+ <p>In this example, only the text content of changelog and todo
+ elements will be printed. The array of tag names can also contain
+ regular expressions which the element names will be matched
+ against.</p>
+
+ <p>Finally, as a shortcut, if you do not pass a symbol to the listen
+ method, it will default to <code>:element_start</code></p>
+
+ <example title="Default Events">parser.listen( %w{ item }) do |uri,localname,qname,attributes|
+ puts attributes['version']
+end
+parser.parse</example>
+
+ <p>This example prints the "version" attribute of all "item" elements
+ in the document. Notice that the number of arguments passed to the
+ block is larger than for <code>:text</code>; again, check the
+ SAX2Listener API for a list of what arguments are passed the blocks
+ for a given event.</p>
+
+ <p>The last two mechanisms for parsing use the SAX2Listener API. Like
+ StreamListener, SAX2Listener is a <code>module</code>, so you can
+ <code>include</code> it in your class to give you an adapter. To use
+ the listener model, create a class that implements some of the
+ SAX2Listener methods, or all of them if you don't include the
+ SAX2Listener model. Add them to a parser as you would blocks, and when
+ the parser is run, the methods will be called when events occur.
+ Listeners do not use event symbols, but they can filter on element
+ names.</p>
+
+ <example title="Filtering for Events with Listeners">listener1 = MySAX2Listener.new
+listener2 = MySAX2Listener.new
+parser.listen( listener1 )
+parser.listen( %{ changelog, todo, credits }, listener2 )
+parser.parse</example>
+
+ <p>In the previous example, <code>listener1</code> will be notified of
+ all events that occur, and <code>listener2</code> will only be
+ notified of events that occur in <code>changelog</code>,
+ <code>todo</code>, and <code>credits</code> elements. We also see that
+ multiple listeners can be added to the same parser; multiple blocks
+ can also be added, and listeners and blocks can be mixed together.</p>
+
+ <p>There is, as yet, no mechanism for recursion. Two upcoming features
+ of the SAX2 API will be the ability to filter based on an XPath, and
+ the ability to specify filtering on an elemnt and all of its
+ descendants.</p>
+
+ <p><em>WARNING:</em> The SAX2 API for dealing with doctype (DTD)
+ events almost <em>certainly</em> will change.</p>
+ </subsection>
+
+ <subsection title="Convenience methods">
+ <p>Michael Neumann contributed some convenience functions for nodes,
+ and they are general enough that I've included. Michael's use-case
+ examples follow: <example title="Node convenience functions">#
+ Starting with +root_node+, we recursively look for a node with the
+ given # +tag+, the given +attributes+ (a Hash) and whoose text equals
+ or matches the # +text+ string or regular expression. # # To find the
+ following node: # # &lt;td class='abc'&gt;text&lt;/td&gt; # # We use:
+ # # find_node(root, 'td', {'class' =&gt; 'abc'}, "text") # # Returns
+ +nil+ if no matching node was found. def find_node(root_node, tag,
+ attributes, text) root_node.find_first_recursive {|node| node.name ==
+ tag and attributes.all? {|attr, val| node.attributes[attr] == val} and
+ text === node.text } end # # Extract specific columns (specified by
+ the position of it's corresponding # header column) from a table. # #
+ Given the following table: # # &lt;table&gt; # &lt;tr&gt; #
+ &lt;td&gt;A&lt;/td&gt; # &lt;td&gt;B&lt;/td&gt; #
+ &lt;td&gt;C&lt;/td&gt; # &lt;/tr&gt; # &lt;tr&gt; #
+ &lt;td&gt;A.1&lt;/td&gt; # &lt;td&gt;B.1&lt;/td&gt; #
+ &lt;td&gt;C.1&lt;/td&gt; # &lt;/tr&gt; # &lt;tr&gt; #
+ &lt;td&gt;A.2&lt;/td&gt; # &lt;td&gt;B.2&lt;/td&gt; #
+ &lt;td&gt;C.2&lt;/td&gt; # &lt;/tr&gt; # &lt;/table&gt; # # To extract
+ the first (A) and last (C) column: # # extract_from_table(root_node,
+ ["A", "C"]) # # And you get this as result: # # [ # ["A.1", "C.1"], #
+ ["A.2", "C.2"] # ] # def extract_from_table(root_node, headers) #
+ extract and collect all header nodes header_nodes = headers.collect {
+ |header| find_node(root_node, 'td', {}, header) } raise "some headers
+ not found" if header_nodes.compact.size &lt; headers.size # assert
+ that all headers have the same parent 'header_row', which is the row #
+ in which the header_nodes are contained. 'table' is the surrounding
+ table tag. header_row = header_nodes.first.parent table =
+ header_row.parent raise "different parents" unless header_nodes.all?
+ {|n| n.parent == header_row} # we now iterate over all rows in the
+ table that follows the header_row. # for each row we collect the
+ elements at the same positions as the header_nodes. # this is what we
+ finally return from the method. (header_row.index_in_parent+1 ..
+ table.elements.size).collect do |inx| row = table.elements[inx]
+ header_nodes.collect { |n| row.elements[ n.index_in_parent ].text }
+ end end</example></p>
+ </subsection>
+
+ <subsection title="Conclusion">
+ <p>This isn't everything there is to REXML, but it should be enough to
+ get started. Check the <link href="../doc/index.html">API
+ documentation</link><footnote>You must generate the API documentation
+ with rdoc or download the API documentation from the REXML website for
+ this documentation.</footnote> for particulars and more examples.
+ There are plenty of unit tests in the <code>test/</code> directory,
+ and these are great sources of working examples.</p>
+ </subsection>
+ </general>
+ </overview>
+
+ <credits>
+ <p>Among the people who've contributed to this document are:</p>
+
+ <list>
+ <item><link href="mailto:deicher@sandia.gov">Eichert, Diana</link> (bug
+ fix)</item>
+ </list>
+ </credits>
+</documentation> \ No newline at end of file
diff --git a/jni/ruby/test/rexml/data/underscore.xml b/jni/ruby/test/rexml/data/underscore.xml
new file mode 100644
index 0000000..111446f
--- /dev/null
+++ b/jni/ruby/test/rexml/data/underscore.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" ?>
+<root a="1" _a="2">
+ <b>1</b>
+ <_b>2</_b>
+</root>
+
diff --git a/jni/ruby/test/rexml/data/utf16.xml b/jni/ruby/test/rexml/data/utf16.xml
new file mode 100644
index 0000000..f7e5e90
--- /dev/null
+++ b/jni/ruby/test/rexml/data/utf16.xml
Binary files differ
diff --git a/jni/ruby/test/rexml/data/web.xml b/jni/ruby/test/rexml/data/web.xml
new file mode 100644
index 0000000..972cf5d
--- /dev/null
+++ b/jni/ruby/test/rexml/data/web.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app>
+ <servlet>
+ <servlet-name>snoop</servlet-name>
+ <servlet-class>SnoopServlet</servlet-class>
+ </servlet>
+ <servlet>
+ <servlet-name>file</servlet-name>
+ <servlet-class>ViewFile</servlet-class>
+ <init-param>
+ <param-name>initial</param-name>
+ <param-value>
+ 1000
+ </param-value>
+ <description>
+ The initial value for the counter <!-- optional -->
+ </description>
+ </init-param>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>
+ mv
+ </servlet-name>
+ <url-pattern>
+ *.wm
+ </url-pattern>
+ </servlet-mapping>
+
+ <distributed/>
+
+ <security-role>
+ <role-name>
+ manager
+ </role-name>
+ <role-name>
+ director
+ </role-name>
+ <role-name>
+ president
+ </role-name>
+ </security-role>
+</web-app>
diff --git a/jni/ruby/test/rexml/data/web2.xml b/jni/ruby/test/rexml/data/web2.xml
new file mode 100644
index 0000000..7d479d3
--- /dev/null
+++ b/jni/ruby/test/rexml/data/web2.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<root>
+ <b>
+ <d />
+ </b>
+ <c />
+</root>
diff --git a/jni/ruby/test/rexml/data/working.rss b/jni/ruby/test/rexml/data/working.rss
new file mode 100644
index 0000000..b38c45f
--- /dev/null
+++ b/jni/ruby/test/rexml/data/working.rss
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+
+<rss version="0.92">
+<channel>
+ <title>Paul Duncan</title>
+ <link>http://www.paulduncan.org/</link>
+ <description>Paul Duncan's personal blog.</description>
+</channel>
+
+ <item>
+ <title>Fun With Tense
+</title>
+ <date>Wed Sep 3 19:37:18 2003</date>
+ <creator>paul@paulduncan.org</creator>
+ <link>http://www.paulduncan.org/?id=23</link>
+ <description>
+&lt;pre&gt;
+&amp;lt;richlowe&amp;gt; blah.
+&amp;lt;richlowe&amp;gt; that's what I say.
+&amp;lt;pabs&amp;gt; if i say it too then what happens?
+&amp;lt;richlowe&amp;gt; then &quot;that's what we say&quot;
+&amp;lt;richlowe&amp;gt; duh.
+&amp;lt;pabs&amp;gt; what if i say it, then retract it?
+&amp;lt;richlowe&amp;gt; Did they teach you nothing in school!
+&amp;lt;richlowe&amp;gt; &quot;that's what I say, and he said&quot;
+&amp;lt;pabs&amp;gt; :(
+&amp;lt;pabs&amp;gt; okay what if i say it, retract it, but then plan on saying it
+ again at some point in the indeterminite future?
+&amp;lt;richlowe&amp;gt; &quot;that's what I say, and he has said, and will possibly say
+ again&quot;?
+&amp;lt;pabs&amp;gt; see now that's not concise at all
+&lt;/pre&gt;
+
+ </description>
+ </item>
+
+ <item>
+ <title>We'll Miss You, Michael
+</title>
+ <date>Mon Aug 25 09:52:41 2003</date>
+ <creator>paul@paulduncan.org</creator>
+ <link>http://www.paulduncan.org/?id=22</link>
+ <description>
+&lt;p&gt;
+Perhaps &lt;a href='http://www.dash-dash.org/pix/death.jpg'&gt;the image&lt;/a&gt;
+on &lt;a href='http://www.dash-dash.org/'&gt;his site&lt;/a&gt; was a bit more
+fitting than we realized. I'm frustrated that nothing and noone around
+him could help ease the pain that he was going through. We can take
+comfort in knowing that he felt the decision he made was best for him.
+We'll miss you, Michael.
+&lt;/p&gt;
+
+ </description>
+ </item>
+
+ <item>
+ <title>Horis, Isis, Osiris, Oh My!
+</title>
+ <date>Fri Aug 22 22:39:23 2003</date>
+ <creator>paul@paulduncan.org</creator>
+ <link>http://www.paulduncan.org/?id=21</link>
+ <description>
+&lt;p&gt;
+I started reading one of the several mythology books I checked out
+earlier this week (as part of a personal research project, the results
+of which will be available here eventually). I've only made it through
+about 60 pages of Egyptian mythology so far, but it's interesting stuff.
+One thing that's impressive to me is how the sexes appear to be on much
+more equal footing (as compared to the Judeo/Christian mythology we've
+been innundated with for most of our lives). For example, Isis revives
+her husband Osiris by collecting his body parts, which are scattered
+throughout Egypt. She also saves her son Horus from death and nurtures
+him until he can fend for himself. Really interesting stuff. Anyway,
+I'll have more about the project as I get farther along.
+&lt;/p&gt;
+
+ </description>
+ </item>
+
+ <item>
+ <title>Nature Sucks
+</title>
+ <date>Fri Aug 22 08:33:41 2003</date>
+ <creator>paul@paulduncan.org</creator>
+ <link>http://www.paulduncan.org/?id=20</link>
+ <description>
+&lt;p&gt;
+&lt;a href='http://www.linuxbrit.co.uk/'&gt;Tom&lt;/a&gt; has been having a rough
+couple of weeks. Apparently he's got &lt;a
+href='http://www.ninds.nih.gov/health_and_medical/disorders/bells_doc.htm'&gt;Bell's
+Palsy&lt;/a&gt; (link from ljlane), which, while treatable and rarely
+permanent, still leaves his face partially paralyzed. If you have a few
+minutes, you might consider sending him an &lt;abbr title='Electronic
+Card'&gt;E-Card&lt;/abbr&gt; or an &lt;a
+href='mailto:dr_spock@linuxbrit.co.uk'&gt;email&lt;/a&gt;.
+&lt;/p&gt;
+
+ </description>
+ </item>
+
+ <item>
+ <title>Or Maybe He Does
+</title>
+ <date>Thu Aug 21 23:45:40 2003</date>
+ <creator>paul@paulduncan.org</creator>
+ <link>http://www.paulduncan.org/?id=19</link>
+ <description>
+&lt;p&gt;
+Okay, apparently Ed &lt;em&gt;does&lt;/em&gt; read this page. :-D
+&lt;/p&gt;
+
+ </description>
+ </item>
+
+ <item>
+ <title>Ed Got Married!!
+</title>
+ <date>Thu Aug 21 15:52:34 2003</date>
+ <creator>paul@paulduncan.org</creator>
+ <link>http://www.paulduncan.org/?id=18</link>
+ <description>
+&lt;p&gt;
+Ed got married in the Bahamas! Here's a picture from the honeymoon:
+&lt;/p&gt;
+
+&lt;img src='/files/ed_dolphin.jpg' width='252' height='288'
+ title='Ed on his honeymoon!' alt='Ed on his honeymoon!' /&gt;
+
+&lt;p&gt;
+(Fortunately he doesn't read this site).
+&lt;/p&gt;
+
+&lt;p&gt;
+One other thing. I weigh 186&lt;abbr title='Pounds'&gt;lbs&lt;/abbr&gt; now.
+The &lt;a href='http://www.pablotron.org/weight/'&gt;weight page&lt;/a&gt; has been
+updated.
+&lt;/p&gt;
+
+ </description>
+ </item>
+
+ <item>
+ <title>Gratuitous Douglas Adams Reference, #32427!
+</title>
+ <date>Tue Aug 19 08:25:42 2003</date>
+ <creator>paul@paulduncan.org</creator>
+ <link>http://www.paulduncan.org/?id=17</link>
+ <description>
+&lt;p&gt;
+Just saw this &lt;acronym title='Uniform Resource Locator'&gt;URL&lt;/acronym&gt;
+on &lt;a href='http://www.diveintomark.org/'&gt;Dive Into
+Mark&lt;/a&gt;:
+&lt;/p&gt;
+
+&lt;p&gt;
+&lt;a
+href='http://www.google.com/search?q=answer+to+life+the+universe+and+everything'&gt;http://www.google.com/search?q=answer+to+life+the+universe+and+everything&lt;/a&gt;
+&lt;/p&gt;
+
+&lt;p&gt;
+&lt;a href='http://www.bbc.co.uk/h2g2/guide/'&gt;&lt;acronym title='Hitchhikers
+Guide to the Galaxy'&gt;H2G2&lt;/acronym&gt;&lt;/a&gt; references never get old for me.
+&lt;/p&gt;
+
+ </description>
+ </item>
+
+ <item>
+ <title>Weekend Update, sans Norm McDonald
+</title>
+ <date>Tue Aug 19 08:18:32 2003</date>
+ <creator>paul@paulduncan.org</creator>
+ <link>http://www.paulduncan.org/?id=16</link>
+ <description>
+&lt;p&gt;
+What a nutty weekend. A friend of mine went to the &lt;acronym
+title='Emergency Room'&gt;ER&lt;/acronym&gt; on Friday night, so I spent 4 hours
+down there. It's really frustrating to see that many sick and injured
+people in once place, and not really be able to do anything about it.
+Also, for that much chaos, both the atmosphere and the staff are
+disturbingly calm.
+&lt;/p&gt;
+
+&lt;p&gt;
+I finally got &lt;a href='http://www.raggle.org/'&gt;Raggle&lt;/a&gt; to a point
+worthy of a new release. I'm not going to blather on about the new
+stuff; if you're interested you can &lt;a
+href='http://www.raggle.org/'&gt;check it out&lt;/a&gt; on your own.
+&lt;/p&gt;
+
+&lt;p&gt;
+&lt;a href='http://www.pablotron.org/weight/'&gt;The war against obesity&lt;/a&gt;
+continues! I'm down to 188 pounds now, a total of 75 pounds lost since
+the beginning of March. I've been having trouble losing these last 10
+pounds, so this week is officially &quot;Go to the Gym Every Night or Else&quot;
+week. 2 days down, 5 to go.
+&lt;/p&gt;
+
+ </description>
+ </item>
+
+</rss>
diff --git a/jni/ruby/test/rexml/data/xmlfile-bug.xml b/jni/ruby/test/rexml/data/xmlfile-bug.xml
new file mode 100644
index 0000000..d439668
--- /dev/null
+++ b/jni/ruby/test/rexml/data/xmlfile-bug.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<intranet>
+<position><aktuell datum="01-10-11">Technik</aktuell></position>
+<hauptspalte>
+<headline>Technik</headline>
+Die Technik ist das Rckgrat der meisten Geschftsprozesse bei Home of the Brave. Deshalb sollen hier alle relevanten technischen Ablufe, Daten und Einrichtungen beschrieben werden, damit jeder im Bedarfsfall die ntigen Informationen, Anweisungen und Verhaltensempfehlungen nachlesen und/oder abrufen kann.
+</hauptspalte>
+<nebenspalte>
+ <link ziel="Flash/">Flash</link><umbruch/>
+ Ntzliches von Flashern fr Flasher.<umbruch/>
+ <link neu="ja" ziel="Cvs/">CVS-FAQ</link><umbruch/>
+ FAQ zur Benutzung von CVS bei HOB
+</nebenspalte>
+</intranet>
+
diff --git a/jni/ruby/test/rexml/data/xp.tst b/jni/ruby/test/rexml/data/xp.tst
new file mode 100644
index 0000000..6551372
--- /dev/null
+++ b/jni/ruby/test/rexml/data/xp.tst
@@ -0,0 +1,27 @@
+/
+/rss
+//rss
+/rss/channel
+//link
+//image/*
+//link[2]
+//link[last()]
+rss/channel/link[last()]
+rss/channel/item/link[last()]
+rss/channel/item/link[1]
+rss/channel/item[@x='1']
+rss/channel/item[@x]
+//item[@x]
+//item[normalize-space(@name)='x']
+//*[count(title)=1]
+//*[name()='link']
+//*[starts-with(name(),'li')]
+//*[contains(name(),'y')]
+//*[string-length(name()) = 4]
+//copyright | //title
+/child::rss
+/descendant::*
+//language/parent::*
+/rss/channel/ancestor::*
+//item[position() mod 2 = 0 ]
+//item/ancestor::* \ No newline at end of file
diff --git a/jni/ruby/test/rexml/data/yahoo.xml b/jni/ruby/test/rexml/data/yahoo.xml
new file mode 100644
index 0000000..b63a944
--- /dev/null
+++ b/jni/ruby/test/rexml/data/yahoo.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<rss version="2.0">
+<channel>
+<title><![CDATA[Yahoo! News Search Results for market]]></title>
+<link>http://news.search.yahoo.com/search/news?p=market&amp;ei=UTF-8</link>
+<description><![CDATA[Yahoo! News Search Results for market]]></description>
+<language>en-us</language>
+<copyright>Copyright (c) 2004 Yahoo! Inc. All rights reserved.</copyright>
+<lastBuildDate>Sun, 03 Sep 2006 16:34:54 GMT</lastBuildDate>
+<ttl>5</ttl>
+<image>
+<title>Yahoo! News</title>
+<width>142</width>
+<height>18</height>
+<link>http://news.search.yahoo.com/news</link>
+<url>http://us.i1.yimg.com/us.yimg.com/i/us/nws/th/main_142.gif</url>
+</image>
+<incremental xmlns="http://purl.org/syndication/history/1.0">false</incremental><item>
+<title><![CDATA[Toyota increases share in US auto market (Manila Bulletin)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=13ck33lrk/*http%3A//www.mb.com.ph/archive_pages.php?url=http://www.mb.com.ph/issues/2006/09/04/BSNS2006090473427.html</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=13ck33lrk/*http%3A//www.mb.com.ph/archive_pages.php?url=http://www.mb.com.ph/issues/2006/09/04/BSNS2006090473427.html</guid>
+<pubDate>Sun, 03 Sep 2006 16:21:30 GMT</pubDate>
+<description><![CDATA[DETROIT (AP) — With a model lineup that consumers love, Toyota Motor Corp. continued to take market share from other automakers last month, posting an industry-best 17 percent increase in sales.]]></description>
+</item><item>
+<title><![CDATA[ANALYSIS - North Korea finds market for missiles shrinking (Reuters via Yahoo! Asia News)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=11ibfq78e/*http%3A//asia.news.yahoo.com/060903/3/2pd1j.html</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=11ibfq78e/*http%3A//asia.news.yahoo.com/060903/3/2pd1j.html</guid>
+<pubDate>Sun, 03 Sep 2006 08:37:33 GMT</pubDate>
+<description><![CDATA[ SEOUL (Reuters) - The missile market is not what it used to be for North Korea, with fewer buyers for a key export product and a loss in prestige when its top-end rocket fizzled after a short test flight, U.S. officials and experts said.]]></description>
+</item><item>
+<title><![CDATA[Broward labor market's a solid performer (Miami Herald)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=13671c2t8/*http%3A//www.miami.com/mld/miamiherald/business/15427113.htm?source=rss&amp;channel=miamiherald_business</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=13671c2t8/*http%3A//www.miami.com/mld/miamiherald/business/15427113.htm?source=rss&amp;channel=miamiherald_business</guid>
+<pubDate>Sun, 03 Sep 2006 07:39:53 GMT</pubDate>
+<description><![CDATA[In looking at South Florida's labor market, it's a tale of two cities, with Fort Lauderdale coming out on top, by far, according to an annual Labor Day report by Florida International University's Bruce Nissen.]]></description>
+</item><item>
+<title><![CDATA[NID to help Jharkhand e-market crafts (Yahoo! India News)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=11hg1j19f/*http%3A//in.news.yahoo.com/060903/43/6780a.html</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=11hg1j19f/*http%3A//in.news.yahoo.com/060903/43/6780a.html</guid>
+<pubDate>Sun, 03 Sep 2006 10:02:54 GMT</pubDate>
+<description><![CDATA[Ranchi, Sep 3 (IANS) Jharkhand will e-market its indigenous products with the help of National Institute of Design (NID), Ahmedabad.]]></description>
+</item><item>
+<title><![CDATA[Reinsurance market goes high-tech to predict disasters (The Herald-Tribune)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=12j43rdgp/*http%3A//www.heraldtribune.com/apps/pbcs.dll/article?AID=/20060903/NEWS/609030458</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=12j43rdgp/*http%3A//www.heraldtribune.com/apps/pbcs.dll/article?AID=/20060903/NEWS/609030458</guid>
+<pubDate>Sun, 03 Sep 2006 10:34:36 GMT</pubDate>
+<description><![CDATA[Hurricane outlooks are closely watched at such places as the Lloyd's of London insurance market building.]]></description>
+</item><item>
+<title><![CDATA[Government warned against going into property market (Sunday Business Post)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=12pru5k6v/*http%3A//www.sbpost.ie/breakingnews/breaking_story.asp?j=4126020&amp;p=4yz6x35&amp;n=4126112&amp;x=</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=12pru5k6v/*http%3A//www.sbpost.ie/breakingnews/breaking_story.asp?j=4126020&amp;p=4yz6x35&amp;n=4126112&amp;x=</guid>
+<pubDate>Sun, 03 Sep 2006 09:31:34 GMT</pubDate>
+<description><![CDATA[A new report is expected to warn the Government not to intervene in the property market. The report by IIB Bank, which is due to be published tomorrow, will argue that it doesn't make sense for the Government to try and stop speculators buying new homes for profit.]]></description>
+</item><item>
+<title><![CDATA[Farmers Market gets shoulder-to-shoulder crowds on Saturdays (Great Falls Tribune)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=12pfls0n1/*http%3A//www.greatfallstribune.com/apps/pbcs.dll/article?AID=/20060903/NEWS01/609030306</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=12pfls0n1/*http%3A//www.greatfallstribune.com/apps/pbcs.dll/article?AID=/20060903/NEWS01/609030306</guid>
+<pubDate>Sun, 03 Sep 2006 11:12:36 GMT</pubDate>
+<description><![CDATA[The produce is bountiful at the Great Falls Farmers Market and so are the customers, vendors say. "Business is booming," said Heather Sands of Little Creek Nursery in Fort Shaw.]]></description>
+</item><item>
+<title><![CDATA[City Hall takes Plan B for SF market rehab (Sun Star)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=138rfnhvi/*http%3A//www.sunstar.com.ph/static/pam/2006/09/03/news/city.hall.takes.plan.b.for.sf.market.rehab.html</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=138rfnhvi/*http%3A//www.sunstar.com.ph/static/pam/2006/09/03/news/city.hall.takes.plan.b.for.sf.market.rehab.html</guid>
+<pubDate>Sun, 03 Sep 2006 08:18:46 GMT</pubDate>
+<description><![CDATA[CITY OF SAN FERNANDO -- The City Government here shelved an earlier proposal of constructing the old public market through a bond flotation scheme and instead took Plan B, which is the rehabilitation of the market's wet section.]]></description>
+</item><item>
+<title><![CDATA[Market watch (AG Weekly)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=12ahobgi6/*http%3A//www.agweekly.com/articles/2006/09/02/news/markets/markets01.txt</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=12ahobgi6/*http%3A//www.agweekly.com/articles/2006/09/02/news/markets/markets01.txt</guid>
+<pubDate>Sun, 03 Sep 2006 04:20:37 GMT</pubDate>
+<description><![CDATA[The Market Sentiment Index can be an invaluable tool in forecasting important price tops and bottoms in the market. The index was developed by Market Vane, a California-based research company, in the 1960s and is still published by a variety of reliable sources.]]></description>
+</item><item>
+<title><![CDATA[Catholic War Vets Ladies Club sets flea market (Times Leader)]]></title>
+<link>http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=1383flil1/*http%3A//www.timesleader.com/mld/timesleader/living/15431391.htm?source=rss&amp;channel=timesleader_living</link>
+<guid isPermaLink="false">http://us.rd.yahoo.com/dailynews/rss/search/market/SIG=1383flil1/*http%3A//www.timesleader.com/mld/timesleader/living/15431391.htm?source=rss&amp;channel=timesleader_living</guid>
+<pubDate>Sun, 03 Sep 2006 07:07:50 GMT</pubDate>
+<description><![CDATA[Catholic War Veterans, Post 274, Ashley, Ladies Club will hold a flea market from 7 a.m. until 2 p.m. Sept. 10, Sept. 24 and Oct. 1 on the grounds of the Catholic War Veterans. The event will take place rain or shine. For more information, vendor information or to reserve a table, call Marsha Panetta at 823-6232 or Dorothy Liberaski at 474-9969. From left, members of the planning committee are ]]></description>
+</item></channel>
+</rss>
+<!-- s8.news.dcn.yahoo.com uncompressed/chunked Sun Sep 3 09:34:54 PDT 2006 -->
diff --git a/jni/ruby/test/rexml/listener.rb b/jni/ruby/test/rexml/listener.rb
new file mode 100644
index 0000000..81c9e88
--- /dev/null
+++ b/jni/ruby/test/rexml/listener.rb
@@ -0,0 +1,50 @@
+class Listener
+ attr_reader :ts, :te
+ attr_reader :normalize
+ def initialize
+ @ts = false
+ @te = false
+ end
+ def tag_start name, attrs
+ @ts = true if name=="subsection" and attrs["title"]=="Namespaces"
+ end
+ def tag_end name
+ @te = true if name=="documentation"
+ end
+ def text text
+ @normalize = text
+ #text.tr! "\n", ' '
+ #puts "text #{text[0..10]}..."
+ end
+ def instruction name, instruction
+ #puts "instruction"
+ end
+ def comment comment
+ #puts "comment #{comment[0..10]}..."
+ end
+ def doctype name, pub_sys, long_name, uri
+ #puts "doctype #{name}"
+ end
+ def attlistdecl content
+ #puts "attlistdecl"
+ end
+ def elementdecl content
+ #puts "elementdecl"
+ end
+ def entitydecl content
+ #puts "entitydecl"
+ end
+ def notationdecl content
+ #puts "notationdecl"
+ end
+ def entity content
+ #puts "entity"
+ end
+ def cdata content
+ #puts "cdata"
+ end
+ def xmldecl version, encoding, standalone
+ #puts "xmldecl #{version}"
+ end
+end
+
diff --git a/jni/ruby/test/rexml/parse/test_document_type_declaration.rb b/jni/ruby/test/rexml/parse/test_document_type_declaration.rb
new file mode 100644
index 0000000..59fe561
--- /dev/null
+++ b/jni/ruby/test/rexml/parse/test_document_type_declaration.rb
@@ -0,0 +1,49 @@
+require "test/unit"
+require "rexml/document"
+
+module REXMLTests
+ class TestParseDocumentTypeDeclaration < Test::Unit::TestCase
+ private
+ def xml(internal_subset)
+ <<-XML
+<!DOCTYPE r SYSTEM "urn:x-rexml:test" [
+#{internal_subset}
+]>
+<r/>
+ XML
+ end
+
+ def parse(internal_subset)
+ REXML::Document.new(xml(internal_subset)).doctype
+ end
+
+ class TestMixed < self
+ def test_entity_element
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!ENTITY entity-name "entity content">
+<!ELEMENT element-name EMPTY>
+ INTERNAL_SUBSET
+ assert_equal([REXML::Entity, REXML::ElementDecl],
+ doctype.children.collect(&:class))
+ end
+
+ def test_attlist_entity
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!ATTLIST attribute-list-name attribute-name CDATA #REQUIRED>
+<!ENTITY entity-name "entity content">
+ INTERNAL_SUBSET
+ assert_equal([REXML::AttlistDecl, REXML::Entity],
+ doctype.children.collect(&:class))
+ end
+
+ def test_notation_attlist
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION notation-name SYSTEM "system-literal">
+<!ATTLIST attribute-list-name attribute-name CDATA #REQUIRED>
+ INTERNAL_SUBSET
+ assert_equal([REXML::NotationDecl, REXML::AttlistDecl],
+ doctype.children.collect(&:class))
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/parse/test_notation_declaration.rb b/jni/ruby/test/rexml/parse/test_notation_declaration.rb
new file mode 100644
index 0000000..0a4e737
--- /dev/null
+++ b/jni/ruby/test/rexml/parse/test_notation_declaration.rb
@@ -0,0 +1,99 @@
+require 'test/unit'
+require 'rexml/document'
+
+module REXMLTests
+ class TestParseNotationDeclaration < Test::Unit::TestCase
+ private
+ def xml(internal_subset)
+ <<-XML
+<!DOCTYPE r SYSTEM "urn:x-henrikmartensson:test" [
+#{internal_subset}
+]>
+<r/>
+ XML
+ end
+
+ def parse(internal_subset)
+ REXML::Document.new(xml(internal_subset)).doctype
+ end
+
+ class TestCommon < self
+ def test_name
+ doctype = parse("<!NOTATION name PUBLIC 'urn:public-id'>")
+ assert_equal("name", doctype.notation("name").name)
+ end
+ end
+
+ class TestExternalID < self
+ class TestSystem < self
+ def test_single_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name SYSTEM 'system-literal'>
+ INTERNAL_SUBSET
+ assert_equal("system-literal", doctype.notation("name").system)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name SYSTEM "system-literal">
+ INTERNAL_SUBSET
+ assert_equal("system-literal", doctype.notation("name").system)
+ end
+ end
+
+ class TestPublic < self
+ class TestPublicIDLiteral < self
+ def test_single_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'public-id-literal' "system-literal">
+ INTERNAL_SUBSET
+ assert_equal("public-id-literal", doctype.notation("name").public)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC "public-id-literal" "system-literal">
+ INTERNAL_SUBSET
+ assert_equal("public-id-literal", doctype.notation("name").public)
+ end
+ end
+
+ class TestSystemLiteral < self
+ def test_single_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC "public-id-literal" 'system-literal'>
+ INTERNAL_SUBSET
+ assert_equal("system-literal", doctype.notation("name").system)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC "public-id-literal" "system-literal">
+ INTERNAL_SUBSET
+ assert_equal("system-literal", doctype.notation("name").system)
+ end
+ end
+ end
+
+ class TestMixed < self
+ def test_system_public
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION system-name SYSTEM "system-literal">
+<!NOTATION public-name PUBLIC "public-id-literal" 'system-literal'>
+ INTERNAL_SUBSET
+ assert_equal(["system-name", "public-name"],
+ doctype.notations.collect(&:name))
+ end
+
+ def test_public_system
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION public-name PUBLIC "public-id-literal" 'system-literal'>
+<!NOTATION system-name SYSTEM "system-literal">
+ INTERNAL_SUBSET
+ assert_equal(["public-name", "system-name"],
+ doctype.notations.collect(&:name))
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/parser/test_sax2.rb b/jni/ruby/test/rexml/parser/test_sax2.rb
new file mode 100644
index 0000000..dba5eab
--- /dev/null
+++ b/jni/ruby/test/rexml/parser/test_sax2.rb
@@ -0,0 +1,202 @@
+require "test/unit"
+require "rexml/parsers/sax2parser"
+require "rexml/sax2listener"
+
+module REXMLTests
+class TestSAX2Parser < Test::Unit::TestCase
+ class TestDocumentTypeDeclaration < self
+ private
+ def xml(internal_subset)
+ <<-XML
+<!DOCTYPE r SYSTEM "urn:x-henrikmartensson:test" [
+#{internal_subset}
+]>
+<r/>
+ XML
+ end
+
+ class TestEntityDeclaration < self
+ class Listener
+ include REXML::SAX2Listener
+ attr_reader :entity_declarations
+ def initialize
+ @entity_declarations = []
+ end
+
+ def entitydecl(declaration)
+ super
+ @entity_declarations << declaration
+ end
+ end
+
+ private
+ def parse(internal_subset)
+ listener = Listener.new
+ parser = REXML::Parsers::SAX2Parser.new(xml(internal_subset))
+ parser.listen(listener)
+ parser.parse
+ listener.entity_declarations
+ end
+
+ class TestGeneralEntity < self
+ class TestValue < self
+ def test_double_quote
+ assert_equal([["name", "value"]], parse(<<-INTERNAL_SUBSET))
+<!ENTITY name "value">
+ INTERNAL_SUBSET
+ end
+
+ def test_single_quote
+ assert_equal([["name", "value"]], parse(<<-INTERNAL_SUBSET))
+<!ENTITY name 'value'>
+ INTERNAL_SUBSET
+ end
+ end
+
+ class TestExternlID < self
+ class TestSystem < self
+ def test_with_ndata
+ declaration = [
+ "name",
+ "SYSTEM", "system-literal",
+ "NDATA", "ndata-name",
+ ]
+ assert_equal([declaration],
+ parse(<<-INTERNAL_SUBSET))
+<!ENTITY name SYSTEM "system-literal" NDATA ndata-name>
+ INTERNAL_SUBSET
+ end
+
+ def test_without_ndata
+ declaration = [
+ "name",
+ "SYSTEM", "system-literal",
+ ]
+ assert_equal([declaration],
+ parse(<<-INTERNAL_SUBSET))
+<!ENTITY name SYSTEM "system-literal">
+ INTERNAL_SUBSET
+ end
+ end
+
+ class TestPublic < self
+ def test_with_ndata
+ declaration = [
+ "name",
+ "PUBLIC", "public-literal", "system-literal",
+ "NDATA", "ndata-name",
+ ]
+ assert_equal([declaration],
+ parse(<<-INTERNAL_SUBSET))
+<!ENTITY name PUBLIC "public-literal" "system-literal" NDATA ndata-name>
+ INTERNAL_SUBSET
+ end
+
+ def test_without_ndata
+ declaration = [
+ "name",
+ "PUBLIC", "public-literal", "system-literal",
+ ]
+ assert_equal([declaration], parse(<<-INTERNAL_SUBSET))
+<!ENTITY name PUBLIC "public-literal" "system-literal">
+ INTERNAL_SUBSET
+ end
+ end
+ end
+ end
+
+ class TestParameterEntity < self
+ class TestValue < self
+ def test_double_quote
+ assert_equal([["%", "name", "value"]], parse(<<-INTERNAL_SUBSET))
+<!ENTITY % name "value">
+ INTERNAL_SUBSET
+ end
+
+ def test_single_quote
+ assert_equal([["%", "name", "value"]], parse(<<-INTERNAL_SUBSET))
+<!ENTITY % name 'value'>
+ INTERNAL_SUBSET
+ end
+ end
+
+ class TestExternlID < self
+ def test_system
+ declaration = [
+ "%",
+ "name",
+ "SYSTEM", "system-literal",
+ ]
+ assert_equal([declaration],
+ parse(<<-INTERNAL_SUBSET))
+<!ENTITY % name SYSTEM "system-literal">
+ INTERNAL_SUBSET
+ end
+
+ def test_public
+ declaration = [
+ "%",
+ "name",
+ "PUBLIC", "public-literal", "system-literal",
+ ]
+ assert_equal([declaration], parse(<<-INTERNAL_SUBSET))
+<!ENTITY % name PUBLIC "public-literal" "system-literal">
+ INTERNAL_SUBSET
+ end
+ end
+ end
+ end
+
+ class TestNotationDeclaration < self
+ class Listener
+ include REXML::SAX2Listener
+ attr_reader :notation_declarations
+ def initialize
+ @notation_declarations = []
+ end
+
+ def notationdecl(*declaration)
+ super
+ @notation_declarations << declaration
+ end
+ end
+
+ private
+ def parse(internal_subset)
+ listener = Listener.new
+ parser = REXML::Parsers::SAX2Parser.new(xml(internal_subset))
+ parser.listen(listener)
+ parser.parse
+ listener.notation_declarations
+ end
+
+ class TestExternlID < self
+ def test_system
+ declaration = ["name", "SYSTEM", nil, "system-literal"]
+ assert_equal([declaration],
+ parse(<<-INTERNAL_SUBSET))
+<!NOTATION name SYSTEM "system-literal">
+ INTERNAL_SUBSET
+ end
+
+ def test_public
+ declaration = ["name", "PUBLIC", "public-literal", "system-literal"]
+ assert_equal([declaration], parse(<<-INTERNAL_SUBSET))
+<!NOTATION name PUBLIC "public-literal" "system-literal">
+ INTERNAL_SUBSET
+ end
+ end
+
+ class TestPublicID < self
+ def test_literal
+ declaration = ["name", "PUBLIC", "public-literal", nil]
+ assert_equal([declaration],
+ parse(<<-INTERNAL_SUBSET))
+<!NOTATION name PUBLIC "public-literal">
+ INTERNAL_SUBSET
+ end
+ end
+ end
+ end
+end
+end
diff --git a/jni/ruby/test/rexml/parser/test_tree.rb b/jni/ruby/test/rexml/parser/test_tree.rb
new file mode 100644
index 0000000..fa010f6
--- /dev/null
+++ b/jni/ruby/test/rexml/parser/test_tree.rb
@@ -0,0 +1,42 @@
+require "test/unit"
+require "rexml/document"
+require "rexml/parsers/treeparser"
+
+module REXMLTests
+class TestTreeParser < Test::Unit::TestCase
+ class TestInvalid < self
+ def test_unmatched_close_tag
+ xml = "<root></not-root>"
+ exception = assert_raise(REXML::ParseException) do
+ parse(xml)
+ end
+ assert_equal(<<-MESSAGE, exception.to_s)
+Missing end tag for 'root' (got "not-root")
+Line: 1
+Position: #{xml.bytesize}
+Last 80 unconsumed characters:
+ MESSAGE
+ end
+
+ def test_no_close_tag
+ xml = "<root>"
+ exception = assert_raise(REXML::ParseException) do
+ parse(xml)
+ end
+ assert_equal(<<-MESSAGE, exception.to_s)
+No close tag for /root
+Line: 1
+Position: #{xml.bytesize}
+Last 80 unconsumed characters:
+ MESSAGE
+ end
+
+ private
+ def parse(xml)
+ document = REXML::Document.new
+ parser = REXML::Parsers::TreeParser.new(xml, document)
+ parser.parse
+ end
+ end
+end
+end
diff --git a/jni/ruby/test/rexml/parser/test_ultra_light.rb b/jni/ruby/test/rexml/parser/test_ultra_light.rb
new file mode 100644
index 0000000..4960fc1
--- /dev/null
+++ b/jni/ruby/test/rexml/parser/test_ultra_light.rb
@@ -0,0 +1,69 @@
+require "test/unit"
+require "rexml/parsers/ultralightparser"
+
+module REXMLTests
+class TestUltraLightParser < Test::Unit::TestCase
+ class TestDocumentTypeDeclaration < self
+ def test_entity_declaration
+ assert_equal([
+ [
+ :start_doctype,
+ :parent,
+ "root",
+ "SYSTEM",
+ "urn:x-test",
+ nil,
+ [:entitydecl, "name", "value"]
+ ],
+ [:text, "\n"],
+ [:start_element, :parent, "root", {}],
+ [:text, "\n"],
+ ],
+ parse(<<-INTERNAL_SUBSET))
+<!ENTITY name "value">
+ INTERNAL_SUBSET
+ end
+
+ private
+ def xml(internal_subset)
+ <<-XML
+<!DOCTYPE root SYSTEM "urn:x-test" [
+#{internal_subset}
+]>
+<root/>
+ XML
+ end
+
+ def parse(internal_subset)
+ parser = REXML::Parsers::UltraLightParser.new(xml(internal_subset))
+ normalize(parser.parse)
+ end
+
+ def normalize(root)
+ root.collect do |child|
+ normalize_child(child)
+ end
+ end
+
+ def normalize_child(child)
+ tag = child.first
+ case tag
+ when :start_doctype
+ normalized_parent = :parent
+ normalized_doctype = child.dup
+ normalized_doctype[1] = normalized_parent
+ normalized_doctype
+ when :start_element
+ tag, parent, name, attributes, *children = child
+ normalized_parent = :parent
+ normalized_children = children.collect do |sub_child|
+ normalize_child(sub_child)
+ end
+ [tag, normalized_parent, name, attributes, *normalized_children]
+ else
+ child
+ end
+ end
+ end
+end
+end
diff --git a/jni/ruby/test/rexml/rexml_test_utils.rb b/jni/ruby/test/rexml/rexml_test_utils.rb
new file mode 100644
index 0000000..57fe65c
--- /dev/null
+++ b/jni/ruby/test/rexml/rexml_test_utils.rb
@@ -0,0 +1,6 @@
+require 'test/unit'
+module REXMLTestUtils
+ def fixture_path(*components)
+ File.join(File.dirname(__FILE__), "data", *components)
+ end
+end
diff --git a/jni/ruby/test/rexml/test_attributes.rb b/jni/ruby/test/rexml/test_attributes.rb
new file mode 100644
index 0000000..aea1d88
--- /dev/null
+++ b/jni/ruby/test/rexml/test_attributes.rb
@@ -0,0 +1,222 @@
+require 'test/unit/testcase'
+require 'rexml/document'
+
+module REXMLTests
+ class AttributesTester < Test::Unit::TestCase
+ include REXML
+ def test_accessor
+ doc = Document.new("<a xmlns:foo='a' xmlns:bar='b' foo:att='1' bar:att='2' att='3'/>")
+ assert_equal '3', doc.root.attributes['att']
+ assert_equal '2', doc.root.attributes['bar:att']
+ doc.root.attributes['att'] = 5
+ assert_equal '5', doc.root.attributes['att']
+ end
+
+ def test_each_attribute
+ doc = Document.new('<a x="1" y="2"/>')
+ doc.root.attributes.each_attribute {|attr|
+ if attr.expanded_name == 'x'
+ assert_equal '1', attr.value
+ elsif attr.expanded_name == 'y'
+ assert_equal '2', attr.value
+ else
+ assert_fail "No such attribute!!"
+ end
+ }
+ end
+
+ def test_each
+ doc = Document.new('<a x="1" y="2"/>')
+ doc.root.attributes.each {|name, value|
+ if name == 'x'
+ assert_equal '1', value
+ elsif name == 'y'
+ assert_equal '2', value
+ else
+ assert_fail "No such attribute!!"
+ end
+ }
+ end
+
+ def test_get_attribute
+ doc = Document.new('<a xmlns:x="a" x:foo="1" foo="2" bar="3"/>')
+ assert_equal '2', doc.root.attributes.get_attribute("foo").value
+ assert_equal '1', doc.root.attributes.get_attribute("x:foo").value
+ end
+
+ def test_size
+ doc = Document.new("<a xmlns:foo='a' x='1' y='2' foo:x='3'/>")
+ assert_equal 4, doc.root.attributes.length
+ end
+
+ def test_setter
+ doc = Document.new("<a xmlns:x='a' x:foo='1' foo='3'/>")
+ doc.root.attributes['y:foo'] = '2'
+ assert_equal '2', doc.root.attributes['y:foo']
+ doc.root.attributes['foo'] = '4'
+ assert_equal '4', doc.root.attributes['foo']
+ doc.root.attributes['x:foo'] = nil
+ assert_equal 3, doc.root.attributes.size
+ end
+
+ def test_delete
+ doc = Document.new("<a xmlns:y='a' xmlns:x='b' xmlns:z='c' y:foo='0' x:foo='1' foo='3' z:foo='4'/>")
+ doc.root.attributes.delete 'foo'
+ assert_equal 6, doc.root.attributes.size
+ assert_equal '1', doc.root.attributes['x:foo']
+
+ doc.root.attributes.delete 'x:foo'
+ assert_equal 5, doc.root.attributes.size
+
+ attr = doc.root.attributes.get_attribute('y:foo')
+ doc.root.attributes.delete attr
+ assert_equal 4, doc.root.attributes.size
+
+ assert_equal '4', doc.root.attributes['z:foo']
+ end
+
+ def test_prefixes
+ doc = Document.new("<a xmlns='foo' xmlns:x='bar' xmlns:y='twee' z='glorp' x:k='gru'/>")
+ prefixes = doc.root.attributes.prefixes
+ assert_equal 2, prefixes.size
+ assert_equal 0, (prefixes - ['x', 'y']).size
+ end
+
+ # Contributed by Mike Stok
+ def test_values_with_apostrophes
+ doc = Document.new(%q#<tag h1="1'2'" h2='1"2'/>#)
+ s = doc.to_s
+ assert(s =~ /h1='1&apos;2&apos;'/)
+ assert(s =~ /h2='1"2'/)
+ end
+
+ # Submitted by Kou
+ def test_namespace_conflict
+ assert_raise( ParseException,
+ "Declaring two attributes with the same namespace should be an error" ) do
+ REXML::Document.new <<-XML
+ <x xmlns:n1="http://www.w3.org"
+ xmlns:n2="http://www.w3.org" >
+ <bad n1:a="1" n2:a="2" />
+ </x>
+ XML
+ end
+
+ REXML::Document.new("<a xmlns:a='a' xmlns:b='a'></a>")
+ end
+
+ # Submitted by Kou
+ def test_attribute_deletion
+ e = REXML::Element.new
+ e.add_namespace("a", "http://a/")
+ e.add_namespace("b", "http://b/")
+ e.add_attributes({"c" => "cc", "a:c" => "cC", "b:c" => "CC"})
+
+ e.attributes.delete("c")
+ assert_nil(e.attributes.get_attribute("c"))
+
+ before_size = e.attributes.size
+ e.attributes.delete("c")
+ assert_nil(e.attributes.get_attribute("c"))
+ assert_equal(before_size, e.attributes.size)
+
+ e.attributes.delete(e.attributes.get_attribute("a:c"))
+ assert_nil(e.attributes.get_attribute("a:c"))
+
+ e.attributes.delete("b:c")
+ assert_nil(e.attributes.get_attribute("b:c"))
+
+ before_size = e.attributes.size
+ e.attributes.delete(e.attributes.get_attribute("b:c"))
+ assert_nil(e.attributes.get_attribute("b:c"))
+ assert_equal(before_size, e.attributes.size)
+
+ before_size = e.attributes.size
+ e.attributes.delete("c")
+ assert_nil(e.attributes.get_attribute("c"))
+ assert_equal(before_size, e.attributes.size)
+
+ e.add_attribute("c", "cc")
+
+ e.attributes.delete(e.attributes.get_attribute("c"))
+ assert_nil(e.attributes.get_attribute("c"))
+ end
+
+ # Submitted by Kou
+ def test_element_usage
+ attr = Attribute.new("name", "value")
+ elem = Element.new("elem")
+ a = Attribute.new(attr, elem)
+ assert_equal(elem, a.element)
+ end
+
+ def attr_test(attr_name,attr_value)
+ a1 = REXML::Attribute.new(attr_name,attr_value)
+
+ s1 = a1.value
+ s2 = a1.value
+
+ #p s1
+ #p s2
+ assert_equal(s1,s2)
+
+ a2 = REXML::Attribute.new(attr_name,attr_value)
+
+ a2.to_s # NB invocation of to_s
+ s1 = a2.value
+ s2 = a2.value
+
+ #p s1
+ #p s2
+ assert_equal(s1,s2)
+ end
+
+ def test_amp_attributes
+ attr_test('name','value with &amp; ampersand only')
+ end
+
+ def test_amp_and_lf_attributes
+ attr_test('name','value with LF &#x000a; &amp; ampersand')
+ end
+
+ def test_quoting
+ d = Document.new(%q{<a x='1' y="2"/>})
+ assert_equal( %q{<a x='1' y='2'/>}, d.to_s )
+ d.root.context[:attribute_quote] = :quote
+ assert_equal( %q{<a x="1" y="2"/>}, d.to_s )
+
+ d = Document.new(%q{<a x='1' y="2"><b z='3'/></a>})
+ assert_equal( %q{<a x='1' y='2'><b z='3'/></a>}, d.to_s )
+ d.root.context[:attribute_quote] = :quote
+ assert_equal( %q{<a x="1" y="2"><b z="3"/></a>}, d.to_s )
+ end
+
+ def test_ticket_127
+ doc = Document.new
+ doc.add_element 'a', { 'v' => 'x & y' }
+ assert doc.to_s.index(';')
+ end
+
+ def test_to_a_with_namespaces
+ document = Document.new(<<-XML)
+<root
+ xmlns:ns1="http://example.org/ns1"
+ xmlns:ns2="http://example.org/ns2">
+ <child
+ ns1:attribute="ns1"
+ ns2:attribute="ns2"
+ attribute="no-ns"
+ other-attribute="other-value"/>
+</root>
+XML
+ child = document.root.elements["child"]
+ assert_equal([
+ "attribute='no-ns'",
+ "ns1:attribute='ns1'",
+ "ns2:attribute='ns2'",
+ "other-attribute='other-value'",
+ ],
+ child.attributes.to_a.collect(&:to_string).sort)
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_attributes_mixin.rb b/jni/ruby/test/rexml/test_attributes_mixin.rb
new file mode 100644
index 0000000..17cca4e
--- /dev/null
+++ b/jni/ruby/test/rexml/test_attributes_mixin.rb
@@ -0,0 +1,31 @@
+require 'test/unit'
+require 'rexml/document'
+
+module REXMLTests
+ class TestAttributes < Test::Unit::TestCase
+ def setup
+ @ns_a = "urn:x-test:a"
+ @ns_b = "urn:x-test:b"
+ element_string = <<-"XMLEND"
+ <test xmlns:a="#{@ns_a}"
+ xmlns:b="#{@ns_b}"
+ a = "1"
+ b = '2'
+ a:c = "3"
+ a:d = '4'
+ a:e = "5"
+ b:f = "6"/>
+ XMLEND
+ @attributes = REXML::Document.new(element_string).root.attributes
+ end
+
+ def test_get_attribute_ns
+ assert_equal("1", @attributes.get_attribute_ns("", "a").value)
+ assert_equal("2", @attributes.get_attribute_ns("", "b").value)
+ assert_equal("3", @attributes.get_attribute_ns(@ns_a, "c").value)
+ assert_equal("4", @attributes.get_attribute_ns(@ns_a, "d").value)
+ assert_equal("5", @attributes.get_attribute_ns(@ns_a, "e").value)
+ assert_equal("6", @attributes.get_attribute_ns(@ns_b, "f").value)
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_changing_encoding.rb b/jni/ruby/test/rexml/test_changing_encoding.rb
new file mode 100644
index 0000000..5a085e2
--- /dev/null
+++ b/jni/ruby/test/rexml/test_changing_encoding.rb
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+require 'rexml/encoding'
+
+module REXMLTests
+ class ChangingEncodings < Test::Unit::TestCase
+ def initialize a
+ @u = 'テスト ほげ ふが 美しい'
+ @e = @u.encode("EUC-JP")
+ @f = Foo.new
+ super
+ end
+
+ class Foo
+ include REXML::Encoding
+ end
+
+ # Note that these tests must be executed in order for the third one to
+ # actually test anything.
+ def test_0_euc
+ @f.encoding = 'EUC-JP'
+ assert_equal( @u, @f.decode(@e) )
+ # This doesn't happen anymore, for some reason
+ #assert_raises( Iconv::IllegalSequence, "Decoding unicode should fail" ) {
+ # @f.decode(@u) == @u
+ #}
+ end
+
+ def test_1_utf
+ @f.encoding = 'UTF-8'
+ assert_not_equal( @u, @f.decode( @e ) )
+ assert_equal( @u, @f.decode( @u ) )
+ end
+
+ def test_2_euc
+ @f.encoding = 'EUC-JP'
+ assert_equal( @u, @f.decode(@e) )
+ # This doesn't happen anymore, for some reason
+ #assert_raises( Iconv::IllegalSequence, "Decoding unicode should fail" ) {
+ # @f.decode(@u) == @u
+ #}
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_comment.rb b/jni/ruby/test/rexml/test_comment.rb
new file mode 100644
index 0000000..d5e78a5
--- /dev/null
+++ b/jni/ruby/test/rexml/test_comment.rb
@@ -0,0 +1,25 @@
+require "test/unit/testcase"
+
+require 'rexml/document'
+
+module REXMLTests
+ class CommentTester < Test::Unit::TestCase
+ # Bug #5278
+ def test_hyphen_end_line_in_doctype
+ xml = <<-XML
+<?xml version="1.0"?>
+<!DOCTYPE root [
+<!-- comment end with hyphen -
+ here -->
+]>
+<root/>
+ XML
+ document = REXML::Document.new(xml)
+ comments = document.doctype.children.find_all do |child|
+ child.is_a?(REXML::Comment)
+ end
+ assert_equal([" comment end with hyphen -\n here "],
+ comments.collect(&:to_s))
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_contrib.rb b/jni/ruby/test/rexml/test_contrib.rb
new file mode 100644
index 0000000..fa84ebe
--- /dev/null
+++ b/jni/ruby/test/rexml/test_contrib.rb
@@ -0,0 +1,584 @@
+# coding: binary
+
+require_relative "rexml_test_utils"
+
+require "rexml/document"
+require "rexml/parseexception"
+require "rexml/formatters/default"
+
+module REXMLTests
+ class ContribTester < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML
+
+ XML_STRING_01 = <<DELIMITER
+<?xml version="1.0" encoding="UTF-8"?>
+<biblio>
+ <entry type="Book">
+ <author>Thomas, David; Hunt, Andrew</author>
+ <language>english</language>
+ <publisher>Addison-Wesley</publisher>
+ <title>Programming Ruby. The Pragmatic Programmer's Guide</title>
+ <year>2000</year>
+ </entry>
+ <entry type="Book">
+ <author>Blammo, Blah</author>
+ <language>english</language>
+ <publisher>Hubbabubba</publisher>
+ <title>Foozboozer's Life</title>
+ <type>Book</type>
+ <year>2002</year>
+ </entry>
+</biblio>
+DELIMITER
+
+ XML_STRING_02 = <<DELIMITER
+<biblio>
+ <entry type="Book">
+ <language>english</language>
+ <publisher>Addison-Wesley</publisher>
+ <title>Programming Ruby. The Pragmatic Programmer's Guide</title>
+ <type>Book</type>
+ <year>2000</year>
+ </entry>
+ <entry type="Book">
+ <author>Blammo, Blah</author>
+ <language>english</language>
+ <publisher>Hubbabubba</publisher>
+ <title>Foozboozer's Life</title>
+ <type>Book</type>
+ <year>2002</year>
+ </entry>
+</biblio>
+DELIMITER
+
+ # Tobias Reif <tobiasreif@pinkjuice.com>
+ def test_bad_doctype_Tobias
+ source = <<-EOF
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+ "http://www.w3.org/TR/SVG/DTD/svg10.dtd"
+ [
+ <!-- <!ENTITY % fast-slow "0 0 .5 1">-->
+ <!--<!ENTITY % slow-fast ".5 0 1 1">-->
+ <!ENTITY hover_ani
+ '<animateTransform attributeName="transform"
+ type="scale" restart="whenNotActive" values="1;0.96"
+ dur="0.5s" calcMode="spline" keySplines="0 0 .5 1"
+ fill="freeze" begin="mouseover"/>
+ <animateTransform attributeName="transform"
+ type="scale" restart="whenNotActive" values="0.96;1"
+ dur="0.5s" calcMode="spline" keySplines=".5 0 1 1"
+ fill="freeze" begin="mouseover+0.5s"/>'
+ >
+ ]
+ >
+ EOF
+ doc = REXML::Document.new source
+ doc.write(out="")
+ assert(out[/>\'>/] != nil, "Couldn't find >'>")
+ assert(out[/\]>/] != nil, "Couldn't find ]>")
+ end
+
+ # Peter Verhage
+ def test_namespace_Peter
+ source = <<-EOF
+ <?xml version="1.0"?>
+ <config:myprog-config xmlns:config="http://someurl/program/version">
+ <!-- main options -->
+ <config:main>
+ <config:parameter name="name" value="value"/>
+ </config:main>
+ </config:myprog-config>
+ EOF
+ doc = REXML::Document.new source
+ assert_equal "myprog-config", doc.root.name
+ count = 0
+ REXML::XPath.each(doc, "x:myprog-config/x:main/x:parameter",
+ {"x"=>"http://someurl/program/version"}) { |element|
+ assert_equal "name", element.attributes["name"]
+ count += 1;
+ }
+ assert_equal 1, count
+ assert_equal "myprog-config", doc.elements["config:myprog-config"].name
+ end
+
+ # Tobias Reif <tobiasreif@pinkjuice.com>
+ def test_complex_xpath_Tobias
+ source = <<-EOF
+ <root>
+ <foo>
+ <bar style="baz"/>
+ <blah style="baz"/>
+ <blam style="baz"/>
+ </foo>
+ <wax>
+ <fudge>
+ <noodle/>
+ </fudge>
+ </wax>
+ </root>
+ EOF
+ # elements that have child elements
+ # but not grandchildren
+ # and not children that don't have a style attribute
+ # and not children that have a unique style attribute
+ complex_path = "*[* "+
+ "and not(*/node()) "+
+ "and not(*[not(@style)]) "+
+ "and not(*/@style != */@style)]"
+ doc = REXML::Document.new source
+ results = REXML::XPath.match( doc.root, complex_path )
+ assert(results)
+ assert_equal 1, results.size
+ assert_equal "foo", results[0].name
+ end
+
+ # "Chris Morris" <chrismo@charter.net>
+ def test_extra_newline_on_read_Chris
+ text = 'test text'
+ e = REXML::Element.new('Test')
+ e.add_text(text)
+ REXML::Formatters::Default.new.write(e,out="")
+
+ doc = REXML::Document.new(out)
+ outtext = doc.root.text
+
+ assert_equal(text, outtext)
+ end
+
+ # Tobias Reif <tobiasreif@pinkjuice.com>
+ def test_other_xpath_Tobias
+ schema = <<-DELIM
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ elementFormDefault="qualified">
+ <xs:element name="rect">
+ <xs:complexType>
+ <xs:attribute name="width" type="xs:byte" use="required"/>
+ <xs:attribute name="height" type="xs:byte" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="svg">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="rect"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:schema>
+ DELIM
+
+ doc = REXML::Document.new schema
+
+ result = REXML::XPath.first(doc.root, 'xs:element[descendant::xs:element[@ref]]')
+ assert result
+ assert_equal "svg", result.attributes['name']
+ result = REXML::XPath.first(doc, 'element[descendant::element[@ref]]')
+ assert_nil result
+ end
+
+ #this first test succeeds, to check if stuff is set up correctly
+ def test_xpath_01_TobiasReif
+ doc = Document.new XML_STRING_01.dup
+ desired_result = Document.new '<author>Thomas, David; Hunt, Andrew</author>'
+ xpath = '//author'
+ result = XPath.first(doc, xpath)
+ assert_equal desired_result.to_s, result.to_s
+ end
+
+ def test_xpath_whitespace_TobiasReif
+ # same as above, with whitespace in XPath
+ doc = Document.new(XML_STRING_01.dup)
+ desired_result = Document.new('<author>Thomas, David; Hunt, Andrew</author>')
+ xpath = "\/\/author\n \n"
+ result = XPath.first(doc, xpath)
+ failure_message = "\n[[[TR: AFAIK, whitespace should be allowed]]]\n"
+ assert_equal(desired_result.to_s, result.to_s, failure_message)
+ end
+
+ def test_xpath_02_TobiasReif
+ doc = Document.new XML_STRING_01.dup
+ desired_result = Document.new '<author>Thomas, David; Hunt, Andrew</author>'
+ # Could that quirky
+ # Programmer',&quot;'&quot;,'s
+ # be handled automatically, somehow?
+ # Or is there a simpler way? (the below XPath should match the author element above,
+ # AFAIK; I tested it inside an XSLT)
+ xpath = %q{/biblio/entry[
+ title/text()=concat('Programming Ruby. The Pragmatic Programmer',"'",'s Guide')
+ and
+ year='2000'
+ ]/author}
+ result = XPath.first(doc, xpath)
+ failure_message = "\nHow to handle the apos inside the string inside the XPath?\nXPath = #{xpath}\n"
+ assert_equal desired_result.to_s, result.to_s, failure_message
+ end
+
+ def test_xpath_03_TobiasReif
+ doc = Document.new XML_STRING_02.dup
+ desired_result_string = "<entry type='Book'>
+ <language>english</language>
+ <publisher>Addison-Wesley</publisher>
+ <title>Programming Ruby. The Pragmatic Programmer's Guide</title>
+ <type>Book</type>
+ <year>2000</year>
+ </entry>"
+ Document.new desired_result_string
+ xpath = "/biblio/entry[not(author)]"
+ result = XPath.first(doc, xpath)
+ assert_equal desired_result_string, result.to_s
+ end
+
+ def test_umlaut
+ koln_iso = "K\xf6ln"
+ koln_utf = "K\xc3\xb6ln"
+ source_iso = "<?xml version='1.0' encoding='ISO-8859-1'?><test>#{koln_iso}</test>"
+ source_utf = "<?xml version='1.0' encoding='UTF-8'?><test>#{koln_utf}</test>"
+
+ if String.method_defined? :encode
+ koln_iso.force_encoding('iso-8859-1')
+ koln_utf.force_encoding('utf-8')
+ source_iso.force_encoding('iso-8859-1')
+ source_utf.force_encoding('utf-8')
+ end
+
+ doc = REXML::Document.new(source_iso)
+ assert_equal('ISO-8859-1', doc.xml_decl.encoding)
+ assert_equal(koln_utf, doc.root.text)
+ doc.write(out="")
+ assert_equal(source_iso, out )
+ doc.xml_decl.encoding = 'UTF-8'
+ doc.write(out="")
+ assert_equal(source_utf, out)
+
+ doc = Document.new <<-EOF
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<intranet>
+<position><aktuell datum="01-10-11">Technik</aktuell></position>
+<hauptspalte>
+<headline>Technik</headline>
+Die Technik ist das R\xFCckgrat der meisten Gesch\xFCftsprozesse bei Home of the Brave. Deshalb sollen hier alle relevanten technischen Abl\xFCufe, Daten und Einrichtungen beschrieben werden, damit jeder im Bedarfsfall die n\xFCtigen Informationen, Anweisungen und Verhaltensempfehlungen nachlesen und/oder abrufen kann.
+</hauptspalte>
+<nebenspalte>
+ <link ziel="Flash/">Flash</link><umbruch/>
+ N\xFCtzliches von Flashern f\xFCr Flasher.<umbruch/>
+ <link neu="ja" ziel="Cvs/">CVS-FAQ</link><umbruch/>
+ FAQ zur Benutzung von CVS bei HOB
+</nebenspalte>
+</intranet>
+EOF
+ tn = XPath.first(doc, "//nebenspalte/text()[2]")
+ expected_iso = "N\xFCtzliches von Flashern f\xFCr Flasher."
+ expected_utf = expected_iso.unpack('C*').pack('U*')
+ expected_iso.force_encoding(::Encoding::ISO_8859_1)
+ expected_utf.force_encoding(::Encoding::UTF_8)
+ assert_equal(expected_utf, tn.to_s.strip)
+ f = REXML::Formatters::Default.new
+ f.write( tn, Output.new(o = "", "ISO-8859-1") )
+ assert_equal(expected_iso, o.strip)
+
+ doc = File.open(fixture_path('xmlfile-bug.xml')) {|file| Document.new file }
+ tn = XPath.first(doc, "//nebenspalte/text()[2]")
+ assert_equal(expected_utf, tn.to_s.strip)
+ f.write( tn, Output.new(o = "", "ISO-8859-1") )
+ assert_equal(expected_iso, o.strip)
+ end
+
+ def test_element_cloning_namespace_Chris
+ aDoc = REXML::Document.new '<h1 tpl:content="title" xmlns:tpl="1">Dummy title</h1>'
+
+ anElement = anElement = aDoc.elements[1]
+ elementAttrPrefix = anElement.attributes.get_attribute('content').prefix
+
+ aClone = anElement.clone
+ cloneAttrPrefix = aClone.attributes.get_attribute('content').prefix
+
+ assert_equal( elementAttrPrefix , cloneAttrPrefix )
+ end
+
+ def test_namespaces_in_attlist_tobias
+ in_string = File.open(fixture_path('foo.xml'), 'r') do |file|
+ file.read
+ end
+
+ doc = Document.new in_string
+
+ assert_nil XPath.first(doc,'//leg')
+ assert_equal 'http://www.foo.com/human', doc.root.elements[1].namespace
+ assert_equal 'human leg',
+ XPath.first(doc, '//x:leg/text()', {'x'=>'http://www.foo.com/human'}).to_s
+ end
+
+ # Alun ap Rhisiart
+ def test_less_than_in_element_content
+ doc = File.open(fixture_path('ProductionSupport.xml')) do |source|
+ REXML::Document.new source
+ end
+ h = Hash.new
+ doc.elements.each("//CommonError") { |el|
+ h[el.elements['Key'].text] = 'okay'
+ }
+ assert(h.include?('MotorInsuranceContract(Object)>>#error:'))
+ end
+
+ # XPaths provided by Thomas Sawyer
+ def test_various_xpath
+ #@doc = REXML::Document.new('<r a="1"><p><c b="2"/></p></r>')
+ doc = REXML::Document.new('<r a="1"><p><c b="2">3</c></p></r>')
+
+ [['/r', REXML::Element],
+ ['/r/p/c', REXML::Element],
+ ['/r/attribute::a', Attribute],
+ ['/r/@a', Attribute],
+ ['/r/attribute::*', Attribute],
+ ['/r/@*', Attribute],
+ ['/r/p/c/attribute::b', Attribute],
+ ['/r/p/c/@b', Attribute],
+ ['/r/p/c/attribute::*', Attribute],
+ ['/r/p/c/@*', Attribute],
+ ['//c/attribute::b', Attribute],
+ ['//c/@b', Attribute],
+ ['//c/attribute::*', Attribute],
+ ['//c/@*', Attribute],
+ ['.//node()', REXML::Node ],
+ ['.//node()[@a]', REXML::Element ],
+ ['.//node()[@a="1"]', REXML::Element ],
+ ['.//node()[@b]', REXML::Element ], # no show, why?
+ ['.//node()[@b="2"]', REXML::Element ]
+ ].each do |xpath,kind|
+ begin
+ REXML::XPath.each( doc, xpath ) do |what|
+ assert_kind_of( kind, what, "\n\nWrong type (#{what.class}) returned for #{xpath} (expected #{kind.name})\n\n" )
+ end
+ rescue Exception
+ puts "PATH WAS: #{xpath}"
+ raise
+ end
+ end
+
+ [
+ ['/r', 'attribute::a', Attribute ],
+ ['/r', '@a', Attribute ],
+ ['/r', 'attribute::*', Attribute ],
+ ['/r', '@*', Attribute ],
+ ['/r/p/c', 'attribute::b', Attribute ],
+ ['/r/p/c', '@b', Attribute ],
+ ['/r/p/c', 'attribute::*', Attribute ],
+ ['/r/p/c', '@*', Attribute ]
+ ].each do |nodepath, xpath, kind|
+ begin
+ context = REXML::XPath.first(doc, nodepath)
+ REXML::XPath.each( context, xpath ) do |what|
+ assert_kind_of kind, what, "Wrong type (#{what.class}) returned for #{xpath} (expected #{kind.name})\n"
+ end
+ rescue Exception
+ puts "PATH WAS: #{xpath}"
+ raise
+ end
+ end
+ end
+
+ def test_entities_Holden_Glova
+ document = <<-EOL
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE rubynet [
+ <!ENTITY rbconfig.MAJOR "1">
+ <!ENTITY rbconfig.MINOR "7">
+ <!ENTITY rbconfig.TEENY "2">
+ <!ENTITY rbconfig.ruby_version "&rbconfig.MAJOR;.&rbconfig.MINOR;">
+ <!ENTITY rbconfig.arch "i386-freebsd5">
+ <!ENTITY rbconfig.prefix "/usr/local">
+ <!ENTITY rbconfig.libdir "&rbconfig.prefix;/lib">
+ <!ENTITY rbconfig.includedir "&rbconfig.prefix;/include">
+ <!ENTITY rbconfig.sitedir "&rbconfig.prefix;/lib/ruby/site_ruby">
+ <!ENTITY rbconfig.sitelibdir "&rbconfig.sitedir;/&rbconfig.ruby_version;">
+ <!ENTITY rbconfig.sitearchdir "&rbconfig.sitelibdir;/&rbconfig.arch;">
+ ]>
+ <rubynet>
+ <pkg version="version1.0">
+ <files>
+ <file>
+ <filename>uga.rb</filename>
+ <mode>0444</mode>
+ <path>&rbconfig.libdir;/rexml</path>
+ <content encoding="xml">... the file here</content>
+ </file>
+ <file>
+ <filename>booga.h</filename>
+ <mode>0444</mode>
+ <path>&rbconfig.includedir;</path>
+ <content encoding="xml">... the file here</content>
+ </file>
+ <file>
+ <filename>foo.so</filename>
+ <mode>0555</mode>
+ <path>&rbconfig.sitearchdir;/rexml</path>
+ <content encoding="mime64">Li4uIHRoZSBmaWxlIGhlcmU=\n</content>
+ </file>
+ </files>
+ </pkg>
+ </rubynet>
+ EOL
+
+ file_xpath = '/rubynet/pkg/files/file'
+
+ root = REXML::Document.new(document)
+
+ root.elements.each(file_xpath) do |metadata|
+ text = metadata.elements['path'].get_text.value
+ assert text !~ /&rbconfig/, "'#{text}' failed"
+ end
+
+ #Error occurred in test_package_file_opens(TC_PackageInstall):
+ # ArgumentError:
+ #illegal access mode &rbconfig.prefix;/lib/rexml
+ #
+ #[synack@Evergreen] src $ ruby --version
+ #ruby 1.6.7 (2002-03-01) [i686-linux-gnu]
+ #
+ #It looks like it expanded the first entity, but didn't reparse it for more
+ #entities. possible bug - or have I mucked this up?
+ end
+
+ def test_whitespace_after_xml_decl
+ Document.new <<EOL
+<?xml version='1.0'?>
+ <blo>
+ <wak>
+ </wak>
+</blo>
+EOL
+ end
+
+ def test_external_entity
+ xp = '//channel/title'
+
+ %w{working.rss broken.rss}.each do |path|
+ File.open(File.join(fixture_path(path))) do |file|
+ doc = REXML::Document.new file.readlines.join('')
+
+ # check to make sure everything is kosher
+ assert_equal( doc.root.class, REXML::Element )
+ assert_equal( doc.root.elements.class, REXML::Elements )
+
+ # get the title of the feed
+ assert( doc.root.elements[xp].kind_of?( REXML::Element ) )
+ end
+ end
+ end
+
+ def test_maintain_dtd
+ src = %q{<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE ivattacks SYSTEM "../../ivacm.dtd" [
+<!ENTITY % extern-packages SYSTEM "../../ivpackages.dtd">
+<!ENTITY % extern-packages SYSTEM "../../common-declarations.dtd">
+%extern-packages;
+%extern-common;
+]>}
+ doc = Document.new( src )
+ doc.write( out="" )
+ src = src.tr('"', "'")
+ out = out.tr('"', "'")
+ assert_equal( src, out )
+ end
+
+ def test_text_nodes_nomatch
+ source = "<root><child>test</child></root>"
+ d = REXML::Document.new( source )
+ r = REXML::XPath.match( d, %q{/root/child[text()="no-test"]} )
+ assert_equal( 0, r.size )
+ end
+
+ def test_raw_Terje_Elde
+ f = REXML::Formatters::Default.new
+ txt = 'abc&#248;def'
+ a = Text.new( txt,false,nil,true )
+ f.write(a,out="")
+ assert_equal( txt, out )
+
+ txt = '<sean><russell>abc&#248;def</russell></sean>'
+ a = Document.new( txt, { :raw => ["russell"] } )
+ f.write(a,out="")
+ assert_equal( txt, out )
+ end
+
+ def test_indenting_error
+ a=Element.new("test1")
+ b=Element.new("test2")
+ c=Element.new("test3")
+ b << c
+ a << b
+
+ REXML::Formatters::Pretty.new.write(a,"")
+ end
+
+ def test_pos
+ require 'tempfile'
+ Tempfile.create("tidal") {|testfile|
+ testdata = %Q{<calibration>
+<section name="parameters">
+<param name="barpress">760</param>
+<param name="hertz">50</param>
+</section>
+</calibration>
+}
+
+ testfile.puts testdata
+ testfile.rewind
+ assert_nothing_raised do
+ REXML::Document.new(testfile)
+ end
+ }
+ end
+
+ def test_deep_clone
+ a = Document.new( '<?xml version="1.0"?><!DOCTYPE html PUBLIC
+ "-//W3C//DTD
+ XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html
+ xmlns="http:///www.w3.org/1999/xhtml"></html>' )
+ b = a.deep_clone
+ assert_equal a.to_s, b.to_s
+ end
+
+ def test_double_escaping
+ data = '<title>AT&amp;T</title>'
+ xml = "<description><![CDATA[#{data}]]></description>"
+
+ doc = REXML::Document.new(xml)
+ description = doc.find {|e| e.name=="description"}
+ assert_equal data, description.text
+ end
+
+ def test_ticket_12
+ cfg = "<element><anotherelement><child1>a</child1><child2>b</child2></anotherelement></element>"
+
+ config = REXML::Document.new( cfg )
+
+ assert_equal( "a", config.elements[ "//child1" ].text )
+ end
+
+=begin
+ # This is a silly test, and is low priority
+ def test_namespace_serialization_tobi_reif
+ doc = Document.new '<doc xmlns:b="http://www.foo.foo">
+ <b:p/>
+ </doc>'
+ ns = 'http://www.foo.foo'
+ ns_declaration={'f'=>ns}
+ returned = XPath.match(doc,'//f:p',ns_declaration)
+ # passes:
+ assert( (returned[0].namespace==ns), 'namespace should be '+ns)
+ serialized = returned.to_s
+ serialized_and_parsed = Document.new(serialized)
+ puts 'serialized: '+serialized
+ # ... currently brings <b:p/>
+ # prefix b is undeclared (!)
+ assert( (serialized_and_parsed.namespace==ns),
+ 'namespace should still be '+ns.inspect+
+ ' and not '+serialized_and_parsed.namespace.inspect)
+ # ... currently results in a failure:
+ # 'namespace should still be "http://www.foo.foo" and not ""'
+ end
+=end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_core.rb b/jni/ruby/test/rexml/test_core.rb
new file mode 100644
index 0000000..97aada9
--- /dev/null
+++ b/jni/ruby/test/rexml/test_core.rb
@@ -0,0 +1,1467 @@
+# coding: binary
+
+require_relative "rexml_test_utils"
+
+require "rexml/document"
+require "rexml/parseexception"
+require "rexml/output"
+require "rexml/source"
+require "rexml/formatters/pretty"
+require "rexml/undefinednamespaceexception"
+
+require_relative "listener"
+
+module REXMLTests
+ class Tester < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML
+ def setup
+ @xsa_source = <<-EOL
+ <?xml version="1.0"?>
+ <?xsl stylesheet="blah.xsl"?>
+ <!-- The first line tests the XMLDecl, the second tests PI.
+ The next line tests DocType. This line tests comments. -->
+ <!DOCTYPE xsa PUBLIC
+ "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML"
+ "http://www.garshol.priv.no/download/xsa/xsa.dtd">
+
+ <xsa>
+ <vendor id="blah">
+ <name>Lars Marius Garshol</name>
+ <email>larsga@garshol.priv.no</email>
+ <url>http://www.stud.ifi.uio.no/~lmariusg/</url>
+ </vendor>
+ </xsa>
+ EOL
+ end
+
+ def test_bad_markup
+ [
+ "<pkg='version'> foo </pkg>",
+ '<0/>',
+ '<a>&</a>',
+ '<a>&a</a>',
+# '<a>&a;</a>', # FIXME
+ '<a a="<"/>',
+ '<a 3="<"/>',
+ '<a a="1" a="2"/>',
+ '<a><!-- -- --></a>',
+ '<a><!-- ---></a>',
+ '<a>&#x00;</a>',
+ '<a>&#0;</a>',
+ "<a a='&#0;' />",
+ "<a>\f</a>",
+ "<a a='\f' />",
+ "<a>\000</a>",
+# FIXME '<a' + [65535].pack('U') + ' />',
+ '<a>&#xfffe;</a>',
+ '<a>&#65535;</a>',
+# FIXME '<a' + [0x0371].pack('U') + ' />',
+# FIXME '<a a' + [0x0371].pack('U') + '="" />',
+ ].each do |src|
+ assert_raise( ParseException, %Q{Parse #{src.inspect} should have failed!} ) do
+ Document.new(src)
+ end
+ end
+ end
+
+ def test_attribute
+ # Testing constructors
+ #a = Attribute.new "hello", "dolly"
+ #b = Attribute.new a
+ #d = Document.new( "<a hello='dolly' href='blah'/>" )
+ #c = d[0].attributes.get_attribute( "hello" )
+
+ #assert_equal a, b
+ #for attr in [ a, b, c]
+ # assert_equal "hello", attr.name
+ # assert_equal "dolly", attr.value
+ #end
+
+ # This because of a reported bug in attribute handling in 1.0a8
+ source = '<a att="A">blah</a>'
+ doc = Document.new source
+ doc.elements.each do |a|
+ a.attributes['att'] << 'B'
+ assert_equal "AB", a.attributes['att']
+ a.attributes['att'] = 'C'
+ assert_equal "C", a.attributes['att']
+ end
+
+ # Bryan Murphy <murphybryanp@yahoo.com>
+ text = "this is a {target[@name='test']/@value} test"
+ source = <<-EOL
+ <?xml version="1.0"?>
+ <doc search="#{text}"/>
+ EOL
+
+ xml = Document.new source
+ value = xml.root.attributes["search"]
+ assert_equal text, value.to_s
+
+ e = Element.new "test"
+ e.add_attributes({ "name1" => "test1", "name4" => "test4" })
+ e.add_attributes([["name3","test3"], ["name2","test2"]])
+ assert_equal "test1", e.attributes["name1"]
+ assert_equal "test2", e.attributes["name2"]
+ assert_equal "test3", e.attributes["name3"]
+ assert_equal "test4", e.attributes["name4"]
+
+ # ensure that the attributes come out in sorted order
+ assert_equal %w(<test
+ name1='test1'
+ name2='test2'
+ name3='test3'
+ name4='test4'/>).join(' '), e.to_s
+ end
+
+ def test_cdata
+ test = "The quick brown fox jumped
+ & < & < \" '
+ over the lazy dog."
+
+ source = "<a><![CDATA[#{test}]]></a>"
+ d = REXML::Document.new( source )
+
+ # Test constructors
+ cdata = d[0][0]
+ assert_equal test, cdata.value
+ end
+
+ def test_comment
+ string = "This is a new comment!"
+ source = "<!--#{string}-->"
+ comment = Comment.new string
+ REXML::Formatters::Default.new.write( comment, out = "" )
+ assert_equal(source, out)
+
+ comment2 = Comment.new comment
+ assert_equal(comment, comment2)
+
+ assert_raise(ParseException) {
+ REXML::Document.new("<d><!- foo --></d>")
+ }
+ assert_raise(ParseException) {
+ REXML::Document.new("<d><!-- foo -></d>")
+ }
+ end
+
+ def test_whitespace
+ doc = Document.new "<root-element><first-element/></root-element>"
+ assert_equal 1, doc.root.size
+ assert_equal 1, doc.root.elements.size
+ doc = Document.new "<root-element>
+ <first-element/>
+ </root-element>"
+ assert_equal 3, doc.root.size
+ assert_equal 1, doc.root.elements.size
+
+ text = " This is text
+ with a lot of whitespace "
+ source = "<a>#{text}<b>#{text}</b><c>#{text}</c>#{text}</a>"
+
+ doc = Document.new( source, {
+ :respect_whitespace => %w{ a c }
+ } )
+ assert_equal text, doc.elements["//c"].text
+ string = ""
+ doc.root.each { |n| string << n.to_s if n.kind_of? Text }
+ assert_equal text+text, string
+
+ string =" lots of blank
+ space"
+ doc.root.add_element("d").add_element("c").text = string
+ doc.root.add_element("e").text = string
+ assert_equal string, doc.elements["/a/d/c"].text
+ assert string != doc.elements["/a/e"].text, "Text wasn't properly compressed"
+
+ doc = Document.new source, { :respect_whitespace => :all }
+ doc.root.add_element("d").text = string
+ assert_equal text, doc.root.text
+ nxt = ""
+ doc.root.each { |n| nxt << n.to_s if n.kind_of? Text }
+ assert_equal text+text, nxt
+ assert_equal text, doc.root.elements["b"].text
+ assert_equal text, doc.root.elements["c"].text
+ assert_equal string, doc.root.elements["d"].text
+ end
+
+ # This isn't complete. We need to check declarations and comments
+ def test_doctype
+ string = "something"
+ correct = "<!DOCTYPE something>"
+ doc = DocType.new(string)
+ assert_equal(string, doc.name)
+ doc.write(out="")
+ assert_equal(correct, out)
+
+ doc2 = DocType.new(doc)
+ assert_equal(doc.name, doc2.name)
+ assert_equal(doc.external_id, doc2.external_id)
+
+ correct = '<!DOCTYPE xsa PUBLIC "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML" "http://www.garshol.priv.no/download/xsa/xsa.dtd">'
+
+ one_line_source = '<!DOCTYPE xsa PUBLIC "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML" "http://www.garshol.priv.no/download/xsa/xsa.dtd"><a/>'
+ doc = Document.new( one_line_source )
+ doc = doc[0]
+ assert(doc)
+ doc.write(test="")
+ assert_equal(correct, test)
+
+ multi_line_source = '<!DOCTYPE xsa PUBLIC
+ "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML"
+ "http://www.garshol.priv.no/download/xsa/xsa.dtd">
+ <a/>'
+ d = Document.new( multi_line_source )
+ doc = d[0]
+ assert(doc)
+ doc.write(test="")
+ assert_equal(correct, test)
+
+ odd_space_source = ' <!DOCTYPE
+ xsa PUBLIC "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML"
+ "http://www.garshol.priv.no/download/xsa/xsa.dtd"> <a/>'
+ d = Document.new( odd_space_source )
+ dt = d.doctype
+ dt.write(test="")
+ assert_equal(correct, test)
+
+ # OK, the BIG doctype test, numba wun
+ doc = File.open(fixture_path("doctype_test.xml")) do |docin|
+ Document.new(docin)
+ end
+ doc.write(test="")
+ assert_equal(31, doc.doctype.size)
+ end
+
+ def test_document
+ # Testing cloning
+ source = "<element/>"
+ doc = Document.new source
+
+ # Testing Root
+ assert_equal doc.root.name.to_s, "element"
+
+ # Testing String source
+ source = @xsa_source
+ doc = Document.new source
+ assert_instance_of XMLDecl, doc.xml_decl
+ assert_instance_of DocType, doc.doctype
+ assert_equal doc.version, "1.0"
+
+ doc = File.open(fixture_path("dash.xml")) {|s| Document.new s }
+ assert_equal "content-2", doc.elements["//content-2"].name
+ end
+
+ def test_instruction
+ target = "use"
+ content = "ruby"
+ source = "<?#{target} #{content}?>"
+
+ instruction = Instruction.new target, content
+ instruction2 = Instruction.new instruction
+ assert_equal(instruction, instruction2)
+ REXML::Formatters::Default.new.write( instruction, out = "" )
+ assert_equal(source, out)
+
+ d = Document.new( source )
+ instruction2 = d[0]
+ assert_equal(instruction.to_s, instruction2.to_s)
+
+ assert_raise(ParseException) {
+ REXML::Document.new("<d><?foo bar></d>")
+ }
+ end
+
+ def test_parent
+ parent = Parent.new
+ begin
+ parent << "Something"
+ rescue Exception
+ parent << Comment.new("Some comment")
+ assert parent.size == 1, "size of parent should be 1"
+ else
+ assert_fail "should have gotten an exception trying to add a "+ "String to a Parent"
+ end
+
+ source = "<a><one/><three/><five/></a>"
+ doc = Document.new source
+ three = doc.root.elements["three"]
+ doc.root.insert_before( three, Element.new("two") )
+ nxt = doc.root.elements["one"]
+ string = ""
+ while nxt
+ string << nxt.name
+ nxt = nxt.next_sibling
+ end
+ assert_equal "onetwothreefive", string
+
+
+ doc.root.insert_after( three, Element.new("four") )
+ string = ""
+ doc.root.each { |element| string << element.name }
+ assert_equal "onetwothreefourfive", string
+
+ string = ""
+ nxt = doc.root.elements["five"]
+ while nxt
+ string << nxt.name
+ nxt = nxt.previous_sibling
+ end
+ assert_equal "fivefourthreetwoone", string
+
+ doc.insert_after "//two", Element.new("two-and-half")
+ string = doc.root.elements.collect {|x| x.name}.join
+ assert_equal "onetwotwo-and-halfthreefourfive", string
+ doc.elements["/a/five"].insert_before "../four", Element.new("three-and-half")
+ string = doc.root.elements.collect {|x| x.name}.join
+ assert_equal "onetwotwo-and-halfthreethree-and-halffourfive", string
+
+ doc.elements["/a/five"].previous_sibling = Element.new("four-and-half")
+ string = doc.root.elements.collect {|x| x.name}.join
+ assert_equal "onetwotwo-and-halfthreethree-and-halffourfour-and-halffive", string
+ doc.elements["/a/one"].next_sibling = Element.new("one-and-half")
+ string = doc.root.elements.collect {|x| x.name}.join
+ assert_equal "oneone-and-halftwotwo-and-halfthreethree-and-halffourfour-and-halffive", string
+
+ doc = Document.new "<a><one/><three/></a>"
+ doc.root[1,0] = Element.new "two"
+ string = ""
+ doc.root.each { |el| string << el.name }
+ assert_equal "onetwothree", string
+ end
+
+ # The Source classes are tested extensively throughout the test suite
+ def test_source
+ # Testing string source
+ source = @xsa_source
+ doc = Document.new source
+ assert_equal doc.root.name.to_s, "xsa"
+
+ # Testing IO source
+ doc = File.open(fixture_path("project.xml")) {|f| Document.new f }
+ assert_equal doc.root.name.to_s, "Project"
+ end
+
+ def test_text
+ f = REXML::Formatters::Default.new
+ string = "Some text"
+ text = Text.new(string)
+ assert_equal(string, text.to_s)
+ text2 = Text.new(text)
+ assert_equal(text, text2)
+ #testing substitution
+ string = "0 < ( 1 & 1 )"
+ correct = "0 &lt; ( 1 &amp; 1 )"
+ text = Text.new(string, true)
+ f.write(text,out="")
+ assert_equal(correct, out)
+
+ string = "Cats &amp; dogs"
+ text = Text.new(string, false, nil, true)
+ assert_equal(string, text.to_s)
+
+ string2 = "<a>#{string}</a>"
+ doc = Document.new( string2, {
+ :raw => %w{ a b }
+ } )
+ f.write(doc,out="")
+ assert_equal(string2, out)
+ b = doc.root.add_element( "b" )
+ b.text = string
+ assert_equal(string, b.get_text.to_s)
+
+ c = doc.root.add_element("c")
+ c.text = string
+ assert_equal("Cats &amp;amp; dogs", c.get_text.to_s)
+
+ # test all
+ string = "<a>&amp;<b>&lt;</b><c>&gt;<d>&quot;</d></c></a>"
+ doc = Document.new(string, { :raw => :all })
+ assert_equal( "&amp;", doc.elements["/a"][0].to_s )
+ assert_equal( "&", doc.elements["/a"].text )
+ assert_equal( "&lt;", doc.elements["/a/b"][0].to_s )
+ assert_equal( "<", doc.elements["/a/b"].text )
+ assert_equal( "&gt;", doc.elements["/a/c"][0].to_s )
+ assert_equal( ">", doc.elements["/a/c"].text )
+ assert_equal( '&quot;', doc.elements["//d"][0].to_s )
+ assert_equal( '"', doc.elements["//d"].text )
+
+ # test some other stuff
+ doc = Document.new('<a><b/></a>')
+ doc.root.text = 'Sean'
+ assert_equal( '<a><b/>Sean</a>', doc.to_s )
+ doc.root.text = 'Elliott'
+ assert_equal( '<a><b/>Elliott</a>', doc.to_s )
+ doc.root.add_element( 'c' )
+ assert_equal( '<a><b/>Elliott<c/></a>', doc.to_s )
+ doc.root.text = 'Russell'
+ assert_equal( '<a><b/>Russell<c/></a>', doc.to_s )
+ doc.root.text = nil
+ assert_equal( '<a><b/><c/></a>', doc.to_s )
+ end
+
+ def test_text_frozen
+ string = "Frozen".freeze
+ text = Text.new(string)
+ assert_equal(string, text.to_s)
+ end
+
+ def test_xmldecl
+ source = "<?xml version='1.0'?>"
+ # test args
+ # test no args
+ decl2 = XMLDecl.new
+ assert_equal source, decl2.to_s
+ # test XMLDecl
+ decl2 = XMLDecl.new "1.0"
+ assert_equal source, decl2.to_s
+ end
+
+ def test_xmldecl_utf_16be_encoding_name
+ assert_equal("<?xml version='1.0' encoding='UTF-16'?>",
+ XMLDecl.new("1.0", "UTF-16").to_s)
+ end
+
+ def each_test( element, xpath, num_children )
+ count = 0
+ element.each_element( xpath ) { |child|
+ count += 1
+ yield child if block_given?
+ }
+ assert_equal num_children, count
+ end
+
+ # This is the biggest test, as the number of permutations of xpath are
+ # enormous.
+ def test_element_access
+ # Testing each_element
+ doc = File.open(fixture_path("project.xml")) {|f| Document.new f }
+
+ each_test( doc, "/", 1 ) { |child|
+ assert_equal doc.name, child.name
+ }
+ each_test(doc, ".", 1) { |child| assert_equal doc, child }
+ each_test(doc.root, "..", 1) { |child| assert_equal doc, child }
+ each_test(doc.root, "*", 5)
+ each_test(doc, "Project/Datasets", 1) { |child|
+ assert_equal "Datasets", child.name
+ }
+ each_test(doc, "Project/Datasets/link", 2 )
+ each_test(doc.root, "/Project/Description", 1) {|child|
+ assert_equal "Description", child.name
+ }
+ each_test(doc.root, "./Description",1 ) { |child|
+ assert_equal "Description",child.name
+ }
+ each_test(doc.root, "../Project",1 ) { |child|
+ assert_equal doc.root, child
+ }
+ #each_test(doc,".../link",2) {|child| assert_equal "link",child.name.to_s}
+
+ # test get_element
+ first = doc.elements[ "Project" ]
+ assert_equal doc.root, first
+ second = doc.elements[ "Project" ].elements[1]
+ third = doc.elements[ "Project/Creator" ]
+ assert_equal second, third
+ fourth = doc.elements[ "Project/Datasets/link[@idref='18']" ]
+ assert_equal "Test data 1", fourth.attributes["name"]
+
+ # Testing each_predicate
+ each_test( doc, "Project/Datasets/link[@idref='18']", 1 ) { |child|
+ assert_equal "Test data 1", child.attributes["name"]
+ }
+
+ # testing next/previous_element
+ creator = doc.elements["//Creator"]
+ lm = creator.next_element
+ assert_equal "LastModifier", lm.name
+ assert_equal "Creator", lm.previous_element.name
+ end
+
+ def test_child
+ sean = Element.new "Sean"
+ rubbell = Element.new "Rubbell"
+ elliott = sean.add_element "Elliott"
+ sean << rubbell
+ assert_equal elliott, rubbell.previous_sibling
+ assert_equal rubbell, elliott.next_sibling
+
+ russell = Element.new "Russell"
+ rubbell.replace_with russell
+ assert_equal elliott, russell.previous_sibling
+ assert_equal russell, elliott.next_sibling
+
+ assert_nil russell.document
+ assert_equal sean, russell.root
+ end
+
+ # Most of this class is tested elsewhere. Here are the methods which
+ # aren't used in any other class
+ def test_element
+ sean = Element.new "Sean"
+ string = "1) He's a great guy!"
+ sean.text = string
+ russell = Element.new "Russell"
+ sean << russell
+
+ russell.attributes["email"] = "ser@germane-software.com"
+ assert_equal russell.attributes["email"], "ser@germane-software.com"
+ russell.attributes["webpage"] = "http://www.germane-software.com/~ser"
+
+ assert sean.has_text?, "element should have text"
+ assert_equal sean.text, string
+ assert sean.has_elements?, "element should have one element"
+ string = "2) What a stud!"
+ sean.add_text string
+ sean.text = "3) Super programmer!"
+ sean.text = nil
+ assert sean.has_text?, "element should still have text"
+ assert_equal sean.text, string
+
+ russell.delete_attribute "email"
+ assert_nil russell.attributes["email"]
+ russell.attributes.delete "webpage"
+ assert !russell.has_attributes?, "element should have no attributes"
+ end
+
+ def test_no_format
+ source = "<a><b><c>blah</c><d/></b></a>"
+ out = ""
+ doc = Document.new( source )
+ doc.write(out)
+ assert_equal(source, out)
+ end
+
+ def test_namespace
+ source = <<-EOF
+ <x xmlns:foo="http://www.bar.com/schema">
+ </x>
+ EOF
+ doc = Document.new(source)
+ assert_equal("http://www.bar.com/schema", doc.root.namespace( "foo" ))
+ source = <<-EOF
+ <!-- bar namespace is "someuri" -->
+ <foo:bar xmlns="default" xmlns:foo="someuri">
+ <!-- a namespace is "default" -->
+ <a/>
+ <!-- foo:b namespace is "someuri" -->
+ <foo:b>
+ <!-- c namespace is "default" -->
+ <c/>
+ </foo:b>
+ <!-- d namespace is "notdefault" -->
+ <d xmlns="notdefault">
+ <!-- e namespace is "notdefault" -->
+ <e/>
+ <f xmlns="">
+ <g/>
+ </f>
+ </d>
+ </foo:bar>
+ EOF
+ doc = Document.new source
+ assert_equal "someuri", doc.root.namespace
+ assert_equal "default", doc.root.elements[1].namespace
+ assert_equal "someuri", doc.root.elements[2].namespace
+ assert_equal "notdefault", doc.root.elements[ 3 ].namespace
+
+ # Testing namespaces in attributes
+ source = <<-EOF
+ <a xmlns:b="uri">
+ <b b:a="x" a="y"/>
+ <c xmlns="foo">
+ </c>
+ </a>
+ EOF
+ doc = Document.new source
+ b = doc.root.elements["b"]
+ assert_equal "x", b.attributes["b:a"]
+ assert_equal "y", b.attributes["a"]
+
+ doc = Document.new
+ doc.add_element "sean:blah"
+ doc.root.text = "Some text"
+ out = ""
+ doc.write(out)
+ assert_equal "<sean:blah>Some text</sean:blah>", out
+ end
+
+
+ def test_add_namespace
+ e = Element.new 'a'
+ e.add_namespace 'someuri'
+ e.add_namespace 'foo', 'otheruri'
+ e.add_namespace 'xmlns:bar', 'thirduri'
+ assert_equal 'someuri', e.attributes['xmlns']
+ assert_equal 'otheruri', e.attributes['xmlns:foo']
+ assert_equal 'thirduri', e.attributes['xmlns:bar']
+ end
+
+
+ def test_big_documentation
+ d = File.open(fixture_path("documentation.xml")) {|f| Document.new f }
+ assert_equal "Sean Russell", d.elements["documentation/head/author"].text.tr("\n\t", " ").squeeze(" ")
+ out = ""
+ d.write out
+ end
+
+ def test_tutorial
+ doc = File.open(fixture_path("tutorial.xml")) {|f| Document.new f }
+ out = ""
+ doc.write out
+ end
+
+ def test_stream
+ c = Listener.new
+ File.open(fixture_path("documentation.xml")) do |f|
+ Document.parse_stream( f, c )
+ end
+ assert(c.ts, "Stream parsing apparantly didn't parse the whole file")
+ assert(c.te, "Stream parsing dropped end tag for documentation")
+
+ Document.parse_stream("<a.b> <c/> </a.b>", c)
+
+ Document.parse_stream("<a>&lt;&gt;&amp;</a>", c)
+ assert_equal('<>&', c.normalize)
+ end
+
+ def test_line
+ f = File.new(fixture_path("bad.xml"))
+ Document.new f
+ assert_fail "There should have been an error"
+ rescue Exception
+ # We should get here
+ assert($!.line == 5, "Should have been an error on line 5, "+
+ "but was reported as being on line #{$!.line}" )
+ ensure
+ f.close if f
+ end
+
+ def test_substitution
+ val = "a'b\"c"
+ el = Element.new("a")
+ el.attributes["x"] = val
+ REXML::Formatters::Default.new.write(el, out="")
+
+ nel = Document.new( out)
+ assert_equal( val, nel.root.attributes["x"] )
+ end
+
+ def test_exception
+ source = SourceFactory.create_from "<a/>"
+ p = ParseException.new( "dummy message", source )
+ begin
+ raise "dummy"
+ rescue Exception
+ p.continued_exception = $!
+ end
+ end
+
+ def test_bad_content
+ in_gt = '<root-el>content>content</root-el>'
+ in_lt = '<root-el>content<content</root-el>'
+
+ # This is OK
+ tree_gt = Document.new in_gt
+ assert_equal "content>content", tree_gt.elements[1].text
+ # This isn't
+ begin
+ Document.new in_lt
+ assert_fail "Should have gotten a parse error"
+ rescue ParseException
+ end
+ end
+
+ def test_iso_8859_1_output_function
+ out = ""
+ output = Output.new( out )
+ koln_iso_8859_1 = "K\xF6ln"
+ koln_utf8 = "K\xc3\xb6ln"
+ source = Source.new( koln_iso_8859_1, 'iso-8859-1' )
+ results = source.scan(/.*/)[0]
+ koln_utf8.force_encoding('UTF-8') if koln_utf8.respond_to?(:force_encoding)
+ assert_equal koln_utf8, results
+ output << results
+ if koln_iso_8859_1.respond_to?(:force_encoding)
+ koln_iso_8859_1.force_encoding('ISO-8859-1')
+ end
+ assert_equal koln_iso_8859_1, out
+ end
+
+ def test_attributes_each
+ doc = Document.new("<a xmlns:a='foo'><b x='1' y='2' z='3' a:x='4'/></a>")
+ count = 0
+ doc.root.elements[1].attributes.each {|k,v| count += 1 }
+ assert_equal 4, count
+ end
+
+ def test_delete_namespace
+ doc = Document.new "<a xmlns='1' xmlns:x='2'/>"
+ doc.root.delete_namespace
+ doc.root.delete_namespace 'x'
+ assert_equal "<a/>", doc.to_s
+ end
+
+ def test_each_element_with_attribute
+ doc = Document.new "<a><b id='1'/><c id='2'/><d id='1'/><e/></a>"
+ arry = []
+ block = proc { |e|
+ assert arry.include?(e.name)
+ arry.delete e.name
+ }
+ # Yields b, c, d
+ arry = %w{b c d}
+ doc.root.each_element_with_attribute( 'id', &block )
+ assert_equal 0, arry.size
+ # Yields b, d
+ arry = %w{b d}
+ doc.root.each_element_with_attribute( 'id', '1', &block )
+ assert_equal 0, arry.size
+ # Yields b
+ arry = ['b']
+ doc.root.each_element_with_attribute( 'id', '1', 1, &block )
+ assert_equal 0, arry.size
+ # Yields d
+ arry = ['d']
+ doc.root.each_element_with_attribute( 'id', '1', 0, 'd', &block )
+ assert_equal 0, arry.size
+ end
+ def test_each_element_with_text
+ doc = Document.new '<a><b>b</b><c>b</c><d>d</d><e/></a>'
+ arry = []
+ block = proc { |e|
+ assert arry.include?(e.name)
+ arry.delete e.name
+ }
+ # Yields b, c, d
+ arry = %w{b c d}
+ doc.root.each_element_with_text(&block)
+ assert_equal 0, arry.size
+ # Yields b, d
+ arry = %w{b c}
+ doc.root.each_element_with_text( 'b', &block )
+ assert_equal 0, arry.size
+ # Yields b
+ arry = ['b']
+ doc.root.each_element_with_text( 'b', 1, &block )
+ assert_equal 0, arry.size
+ # Yields d
+ arry = ['d']
+ doc.root.each_element_with_text( nil, 0, 'd', &block )
+ assert_equal 0, arry.size
+ end
+
+ def test_element_parse_stream
+ s = Source.new( "<a>some text</a>" )
+ l = Listener.new
+ class << l
+ def tag_start name, attributes
+ raise "Didn't find proper tag name" unless 'a'==name
+ end
+ end
+
+ Document::parse_stream(s, l)
+ end
+
+ def test_deep_clone
+ a = Document.new( '<?xml version="1"?><a x="y"><b>text</b>text<c><d><e>text</e></d></c></a>' )
+ b = a.deep_clone
+ assert_equal a.to_s, b.to_s
+
+ a = Document.new( '<a>some &lt; text <b> more &gt; text </b> &gt; </a>' )
+ b = a.deep_clone
+ assert_equal a.to_s, b.to_s
+ c = Document.new( b.to_s )
+ assert_equal a.to_s, c.to_s
+ end
+
+ def test_whitespace_before_root
+ a = <<EOL
+<?xml version='1.0'?>
+ <blo>
+ <wak>
+ </wak>
+ </blo>
+EOL
+ d = Document.new(a)
+ b = ""
+ d.write( b )
+ assert_equal a,b
+ end
+
+ def test_entities
+ a = Document.new( '<a>&#101;&#x65;&#252;</a>' )
+ assert_equal('eeü'.force_encoding("UTF-8"), a.root.text)
+ end
+
+ def test_element_decl
+ element_decl = Source.new("<!DOCTYPE foo [
+<!ELEMENT bar (#PCDATA)>
+]>")
+ doc = Document.new( element_decl )
+ d = doc[0]
+ assert_equal("<!ELEMENT bar (#PCDATA)>", d.to_s.split(/\n/)[1].strip)
+ end
+
+ def test_attlist_decl
+ doc = Document.new <<-EOL
+ <!DOCTYPE blah [
+ <!ATTLIST blah
+ xmlns CDATA "foo">
+ <!ATTLIST a
+ bar CDATA "gobble"
+ xmlns:one CDATA "two"
+ >
+ ]>
+ <a xmlns:three='xxx' three='yyy'><one:b/><three:c/></a>
+ EOL
+ assert_equal 'gobble', doc.root.attributes['bar']
+ assert_equal 'xxx', doc.root.elements[2].namespace
+ assert_equal 'two', doc.root.elements[1].namespace
+ assert_equal 'foo', doc.root.namespace
+
+ doc = Document.new <<-EOL
+ <?xml version="1.0"?>
+ <!DOCTYPE schema SYSTEM "XMLSchema.dtd" [
+ <!ENTITY % p ''>
+ <!ENTITY % s ''>
+ <!ATTLIST schema
+ xmlns:svg CDATA #FIXED "http://www.w3.org/2000/svg"
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink"
+ xmlns:xml CDATA #FIXED "http://www.w3.org/XML/1998/namespace"
+ >]>
+ <schema/>
+ EOL
+ prefixes = doc.root.prefixes.sort
+ correct = ['svg', 'xlink', 'xml']
+ assert_equal correct, prefixes
+ end
+
+ def test_attlist_write
+ doc = File.open(fixture_path("foo.xml")) {|file| Document.new file }
+ out = ''
+ doc.write(out)
+ end
+
+ def test_more_namespaces
+ assert_raise( REXML::UndefinedNamespaceException,
+ %Q{Should have gotten an Undefined Namespace error} ) {
+ Document.new("<r><p><n:c/></p></r>")
+ }
+ doc2 = Document.new("<r xmlns:n='1'><p><n:c/></p></r>")
+ es = XPath.match(doc2, '//c')
+ assert_equal 0, es.size
+ es = XPath.match(doc2, '//n:c')
+ assert_equal 1, es.size
+ doc2.root.add_namespace('m', '2')
+ doc2.root.add_element("m:o")
+ es = XPath.match(doc2, './/o')
+ assert_equal 0, es.size
+ es = XPath.match(doc2, '//n:c')
+ assert_equal 1, es.size
+ end
+
+ def test_ticket_51
+ doc = REXML::Document.new <<-EOL
+ <test xmlns='1' xmlns:x='1'>
+ <a>X</a>
+ <x:a>Y</x:a>
+
+ <b xmlns='2'>
+ <a>Z</a>
+ </b>
+ </test>
+ EOL
+
+ # The most common case. People not caring about the namespaces much.
+ assert_equal( "XY", XPath.match( doc, "/test/a/text()" ).join )
+ assert_equal( "XY", XPath.match( doc, "/test/x:a/text()" ).join )
+ # Surprising? I don't think so, if you believe my definition of the "common case"
+ assert_equal( "XYZ", XPath.match( doc, "//a/text()" ).join )
+
+ # These are the uncommon cases. Namespaces are actually important, so we define our own
+ # mappings, and pass them in.
+ assert_equal( "XY", XPath.match( doc, "/f:test/f:a/text()", { "f" => "1" } ).join )
+ # The namespaces are defined, and override the original mappings
+ assert_equal( "", XPath.match( doc, "/test/a/text()", { "f" => "1" } ).join )
+ assert_equal( "", XPath.match( doc, "/x:test/x:a/text()", { "f" => "1" } ).join )
+ assert_equal( "", XPath.match( doc, "//a/text()", { "f" => "1" } ).join )
+ end
+
+ def test_processing_instruction
+ d = Document.new("<a><?foo bar?><?foo2 bar2?><b><?foo3 bar3?></b><?foo4 bar4?></a>")
+ assert_equal 4, XPath.match(d, '//processing-instruction()' ).size
+ match = XPath.match(d, "//processing-instruction('foo3')" )
+ assert_equal 1, match.size
+ assert_equal 'bar3', match[0].content
+ end
+
+ def test_oses_with_bad_EOLs
+ Document.new("\n\n\n<?xml version='1.0'?>\n\n\n<a/>\n\n")
+ end
+
+ # Contributed (with patch to fix bug) by Kouhei
+ def test_ignore_whitespace
+ source = "<a> <b/> abc <![CDATA[def]]> </a>"
+
+ context_all = {:ignore_whitespace_nodes => :all}
+ context_a = {:ignore_whitespace_nodes => %(a)}
+ context_b = {:ignore_whitespace_nodes => %(b)}
+
+ tests = [[[" abc ", "def"], context_all],
+ [[" abc ", "def"], context_a],
+ [[" ", " abc ", "def", " "], context_b]]
+
+ tests.each do |test|
+ assert_equal(test[0], Document.new(source, test[1]).root.texts.collect{|x|
+ x.to_s})
+ end
+ end
+
+ def test_0xD_in_preface
+ doc = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\x0D<opml version=\"1.0\">\x0D</opml>"
+ doc = Document.new doc
+ end
+
+ def test_hyphens_in_doctype
+ doc = REXML::Document.new <<-EOQ
+ <?xml version="1.0"?>
+ <!DOCTYPE a-b-c>
+ <a-b-c>
+ <a/>
+ </a-b-c>
+ EOQ
+
+ assert_equal('a-b-c', doc.doctype.name)
+ end
+
+ def test_accents
+ docs = [
+ %Q{<?xml version="1.0" encoding="ISO-8859-1"?>
+<gnuPod>
+<files>
+ <file id="57" artist="Coralie Cl\357\277\275ent" />
+</files>
+</gnuPod>},
+ '<?xml version="1.0" encoding="ISO-8859-1"?>
+<gnuPod>
+<files>
+ <file id="71" album="Astrakan Caf" />
+</files>
+</gnuPod>',
+ %Q{<?xml version="1.0" encoding="ISO-8859-1"?>
+<gnuPod>
+<files>
+ <file id="71" album="Astrakan Caf\357\277\275eria" />
+</files>
+</gnuPod>},
+ %Q{<?xml version="1.0" encoding="ISO-8859-1"?>
+<gnuPod>
+<files>
+ <file id="71" album="Astrakan Caf\357\277\275" />
+</files>
+</gnuPod>} ]
+ docs.each_with_index { |d,i|
+ begin
+ REXML::Document.new(d)
+ rescue
+ puts "#{i} => #{docs[i]}"
+ raise
+ end
+ }
+ end
+
+ def test_replace_text
+ e = REXML::Element.new( "a" )
+ e.add_text( "foo" )
+ assert_equal( "<a>foo</a>", e.to_s )
+ e[0].value = "bar"
+ assert_equal( "<a>bar</a>", e.to_s )
+ e[0].value = "<"
+ assert_equal( "<a>&lt;</a>", e.to_s )
+ assert_equal( "<", e[0].value )
+ end
+
+
+ def test_write_doctype
+ ## XML Document and Declaration
+ document = REXML::Document.new
+ xmldecl = REXML::XMLDecl.new("1.0", "UTF-8")
+ document.add(xmldecl)
+ s = ""
+ document.write(s)
+
+ ## XML Doctype
+ str = '<!DOCTYPE foo "bar">'
+ source = REXML::Source.new(str)
+ doctype = REXML::DocType.new(source)
+ document.add(doctype)
+ document.write(s)
+
+ ## Element
+ element = REXML::Element.new("hoge")
+ document.add(element)
+
+ document.write(s)
+ end
+
+ def test_write_cdata
+ src = "<a>A</a>"
+ doc = REXML::Document.new( src )
+ out = ""
+ doc.write( out )
+ assert_equal( src, out )
+
+ src = "<a><![CDATA[A]]></a>"
+ doc = REXML::Document.new( src )
+ out = ""
+ doc.write( out )
+ assert_equal( src, out )
+ end
+
+ def test_namespace_attributes
+ source = <<-EOL
+ <a xmlns:x="1">
+ <x:b x:n="foo"/>
+ </a>
+ EOL
+ d = Document.new( source )
+ assert_equal( 'foo', REXML::XPath.first(d.root, "//x:b/@x:n").value )
+ assert_equal( nil, REXML::XPath.first(d.root, "//x:b/@x:n", {}))
+ end
+
+ def test_null_element_name
+ a = REXML::Document.new
+ assert_raise( RuntimeError ) {
+ a.add_element( nil )
+ }
+ end
+
+ def test_text_raw
+ # From the REXML tutorial
+ # (http://www.germane-software.com/software/rexml/test/data/tutorial.html)
+ doc = Document.new <<-EOL
+ <?xml version="1.0"?>
+ <!DOCTYPE schema SYSTEM "XMLSchema.dtd" [
+ <!ENTITY % s 'Sean'>
+ ]>
+ <a/>
+ EOL
+ a = doc.root
+
+ # This makes sure that RAW text nodes don't have their entity strings
+ # replaced
+ t = Text.new "Sean", false, nil, true
+ a.text = t
+ assert_equal( "Sean", t.to_s )
+ assert_equal( "Sean", t.value )
+
+ # This makes sure that they do
+ t = Text.new "Sean", false, nil, false
+ a.text = t
+ assert_equal( "&s;", t.to_s )
+ assert_equal( "Sean", t.value )
+
+ t = Text.new "&s;", false, nil, true
+ a.text = t
+ assert_equal( "&s;", t.to_s )
+ assert_equal( "Sean", t.value )
+
+ t = Text.new "&s;", false, nil, true
+ a.text = t
+ assert_equal( "&s;", t.to_s )
+ assert_equal( "Sean", t.value )
+
+ # Ticket #44
+ t = REXML::Text.new( "&amp;", false, nil, true )
+ assert_equal( "&amp;", t.to_s )
+
+ t = REXML::Text.new("&amp;", false, false)
+ assert_equal( "&amp;amp;", t.to_s )
+ end
+
+ def test_to_xpath
+ doc = REXML::Document.new( %q{<tag1>
+ <tag2 name="tag2"/>
+ <tag2 name="tag2"/>
+ </tag1>})
+ names = %w{ /tag1/tag2[1] /tag1/tag2[2] }
+ doc.root.elements.each_with_index {|el, i|
+ assert_equal( names[i], el.xpath )
+ }
+ end
+
+ def test_transitive
+ doc = REXML::Document.new( "<a/>")
+ s = ""
+ doc.write( s, 0, true )
+ end
+
+ # This is issue #40
+ def test_replace_with
+ old = '<doc>old<foo/>old</doc>'
+ d = REXML::Document.new(old).root
+ new = REXML::Text.new('new',true,nil,true)
+ child = d.children[2]
+ child.replace_with(new)
+ assert_equal( new, d.children[2] )
+ end
+
+ def test_repeated_writes
+ a = IO.read(fixture_path("iso8859-1.xml"))
+ f = REXML::Formatters::Pretty.new
+
+ xmldoc = REXML::Document.new( a )
+ a_andre = xmldoc.elements['//image'].attributes['caption']
+
+ f.write(xmldoc,b="")
+
+ xmldoc = REXML::Document.new(b)
+ b_andre = xmldoc.elements['//image'].attributes['caption']
+ assert_equal( a_andre, b_andre )
+
+ f.write(xmldoc,c="")
+
+ xmldoc = REXML::Document.new(c)
+ c_andre = xmldoc.elements['//image'].attributes['caption']
+ assert_equal( b_andre, c_andre )
+
+ o = Output.new(d="","UTF-8")
+ f.write(xmldoc,o)
+ assert_not_equal( c, d )
+ end
+
+ def test_pretty_format_long_text_finite
+ n = 1_000_000
+ long_text = 'aaaa ' * n
+ xml = "<doc>#{long_text}</doc>"
+ formatter = REXML::Formatters::Pretty.new
+ document = nil
+ begin
+ document = REXML::Document.new(xml)
+ rescue REXML::ParseException
+ skip_message = "skip this test because we can't check Pretty#wrap " +
+ "works without #<SystemStackError: stack level too deep> on " +
+ "small memory system. #<RegexpError: failed to allocate memory> " +
+ "will be raised on the system. See also [ruby-dev:42599]."
+ return skip_message
+ end
+ output = ""
+ formatter.write(document, output)
+ assert_equal("<doc>\n" +
+ ((" " + (" aaaa" * 15) + "\n") * (n / 15)) +
+ " " + ("aaaa " * (n % 15)) + "\n" +
+ "</doc>",
+ output)
+ end
+
+ def test_pretty_format_deep_indent
+ n = 6
+ elements = ""
+ n.times do |i|
+ elements << "<element#{i}>"
+ elements << "element#{i} " * 5
+ end
+ (n - 1).downto(0) do |i|
+ elements << "</element#{i}>"
+ end
+ xml = "<doc>#{elements}</doc>"
+ document = REXML::Document.new(xml)
+ formatter = REXML::Formatters::Pretty.new
+ formatter.width = 20
+ output = ""
+ formatter.write(document, output)
+ assert_equal(<<-XML.strip, output)
+<doc>
+ <element0>
+ element0
+ element0
+ element0
+ element0
+ element0\s
+ <element1>
+ element1
+ element1
+ element1
+ element1
+ element1\s
+ <element2>
+ element2
+ element2
+ element2
+ element2
+ element2\s
+ <element3>
+ element3
+ element3
+ element3
+ element3
+ element3\s
+ <element4>
+ element4
+ element4
+ element4
+ element4
+ element4
+ \s
+ <element5>
+ element5 element5 element5 element5 element5\s
+ </element5>
+ </element4>
+ </element3>
+ </element2>
+ </element1>
+ </element0>
+</doc>
+ XML
+ end
+
+ def test_ticket_58
+ doc = REXML::Document.new
+ doc << REXML::XMLDecl.default
+ doc << REXML::Element.new("a")
+
+ str = ""
+ doc.write(str)
+
+ assert_equal("<a/>", str)
+
+ doc = REXML::Document.new
+ doc << REXML::XMLDecl.new("1.0", "UTF-8")
+ doc << REXML::Element.new("a")
+
+ str = ""
+ doc.write(str)
+
+ assert_equal("<?xml version='1.0' encoding='UTF-8'?><a/>", str)
+ end
+
+ # Incomplete tags should generate an error
+ def test_ticket_53
+ assert_raise( REXML::ParseException ) {
+ REXML::Document.new( "<a><b></a>" )
+ }
+ assert_raise( REXML::ParseException ) {
+ REXML::Document.new( "<a><b>" )
+ }
+ assert_raise( REXML::ParseException ) {
+ REXML::Document.new( "<a><b/>" )
+ }
+ end
+
+ def test_ticket_52
+ source = "<!-- this is a single line comment -->"
+ d = REXML::Document.new(source)
+ d.write(k="")
+ assert_equal( source, k )
+
+ source = "<a><!-- Comment --></a>"
+ target = "<a>\n <!-- Comment -->\n</a>"
+ d = REXML::Document.new(source)
+ REXML::Formatters::Pretty.new(4).write(d,k="")
+ assert_equal( target, k )
+ end
+
+ def test_ticket_76
+ src = "<div>at&t"
+ assert_raise( ParseException, %Q{"#{src}" is invalid XML} ) {
+ REXML::Document.new(src)
+ }
+ end
+
+ def test_ticket_21
+ src = "<foo bar=value/>"
+ assert_raise( ParseException, "invalid XML should be caught" ) {
+ Document.new(src)
+ }
+ begin
+ Document.new(src)
+ rescue
+ assert_match( /missing attribute quote/, $!.message )
+ end
+ end
+
+ def test_ticket_63
+ File.open(fixture_path("t63-1.xml")) {|f| Document.new(f) }
+ end
+
+ def test_ticket_75
+ d = File.open(fixture_path("t75.xml")) {|f| REXML::Document.new(f) }
+ assert_equal("tree", d.root.name)
+ end
+
+ def test_ticket_48_part_II
+ f = REXML::Formatters::Pretty.new
+ #- rexml sanity check (bugs in ruby 1.8.4, ruby 1.8.6)
+ xmldoc = Document.new("<test/>")
+ xmldoc << XMLDecl.new(XMLDecl::DEFAULT_VERSION, "UTF-8")
+ content = ['61c3a927223c3e26'].pack("H*")
+ content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
+ #- is some UTF-8 text but just to make sure my editor won't magically convert..
+ xmldoc.root.add_attribute('attr', content)
+ f.write(xmldoc,out=[])
+
+ xmldoc = REXML::Document.new(out.join)
+ sanity1 = xmldoc.root.attributes['attr']
+ f.write(xmldoc,out=[])
+
+ xmldoc = REXML::Document.new(out.join)
+ sanity2 = xmldoc.root.attributes['attr']
+ f.write(xmldoc,out=[])
+
+ assert_equal( sanity1, sanity2 )
+ end
+
+ def test_ticket_88
+ doc = REXML::Document.new("<?xml version=\"1.0\" encoding=\"shift_jis\"?>")
+ assert_equal("<?xml version='1.0' encoding='SHIFT_JIS'?>", doc.to_s)
+ doc = REXML::Document.new("<?xml version = \"1.0\" encoding = \"shift_jis\"?>")
+ assert_equal("<?xml version='1.0' encoding='SHIFT_JIS'?>", doc.to_s)
+ end
+
+ def test_ticket_85
+ xml = <<ENDXML
+<foo>
+ <bar>
+ <bob name='jimmy'/>
+ </bar>
+</foo>
+ENDXML
+
+ yml = "<foo>
+ <bar>
+ <bob name='jimmy'/>
+ </bar>
+</foo>"
+
+ # The pretty printer ignores all whitespace, anyway so output1 == output2
+ f = REXML::Formatters::Pretty.new( 2 )
+ d = Document.new( xml, :ignore_whitespace_nodes=>:all )
+ f.write( d, output1="" )
+
+ d = Document.new( xml )
+ f.write( d, output2="" )
+
+ # Output directives should override whitespace directives.
+ assert_equal( output1, output2 )
+
+ # The base case.
+ d = Document.new(yml)
+ f.write( d, output3="" )
+
+ assert_equal( output3.strip, output2.strip )
+
+ d = Document.new(yml)
+ f.write( d, output4="" )
+
+ assert_equal( output3.strip, output4.strip )
+ end
+
+ def test_ticket_91
+ source="<root>
+ <bah something='1' somethingelse='bah'>
+ <something>great</something>
+ </bah>
+ </root>"
+ expected="<root>
+ <bah something='1' somethingelse='bah'>
+ <something>great</something>
+ </bah>
+ <bah/>
+</root>"
+ d = Document.new( source )
+ d.root.add_element( "bah" )
+ p=REXML::Formatters::Pretty.new(2)
+ p.compact = true # Don't add whitespace to text nodes unless necessary
+ p.write(d,out="")
+ assert_equal( expected, out )
+ end
+
+ def test_ticket_95
+ testd = REXML::Document.new "<a><b><c/><c/><c/></b></a>"
+ testd.write(out1="")
+ testd.elements["//c[2]"].xpath
+ testd.write(out2="")
+ assert_equal(out1,out2)
+ end
+
+ def test_ticket_102
+ doc = REXML::Document.new '<doc xmlns="ns"><item name="foo"/></doc>'
+ assert_equal( "foo", doc.root.elements["item"].attribute("name","ns").to_s )
+ assert_equal( "item", doc.root.elements["item[@name='foo']"].name )
+ end
+
+ def test_ticket_14
+ # Per .2.5 Node Tests of XPath spec
+ assert_raise( REXML::UndefinedNamespaceException,
+ %Q{Should have gotten an Undefined Namespace error} ) {
+ Document.new("<a><n:b/></a>")
+ }
+ end
+
+ # 5.7 Text Nodes
+ # Character data is grouped into text nodes. As much character data as
+ # possible is grouped into each text node: a text node never has an
+ # immediately following or preceding sibling that is a text node. The
+ # string-value of a text node is the character data. A text node always has
+ # at least one character of data.
+ def test_ticket_105
+ d = Document.new("<a/>")
+ d.root.add_text( "a" )
+ d.root.add_text( "b" )
+ assert_equal( 1, d.root.children.size )
+ end
+
+ # phantom namespace same as default namespace
+ def test_ticket_121
+ doc = REXML::Document.new(
+ '<doc xmlns="ns" xmlns:phantom="ns"><item name="foo">text</item></doc>'
+ )
+ assert_equal 'text', doc.text( "/doc/item[@name='foo']" )
+ assert_equal "name='foo'",
+ doc.root.elements["item"].attribute("name", "ns").inspect
+ assert_equal "<item name='foo'>text</item>",
+ doc.root.elements["item[@name='foo']"].to_s
+ end
+
+ def test_ticket_135
+ bean_element = REXML::Element.new("bean")
+ textToAdd = "(&#38;(|(memberof=CN=somegroupabcdefgh,OU=OUsucks,DC=hookemhorns,DC=com)(mail=*someco.com))(acct=%u)(!(extraparameter:2.2.222.222222.2.2.222:=2)))"
+ bean_element.add_element("prop", {"key"=> "filter"}).add_text(textToAdd)
+ doc = REXML::Document.new
+ doc.add_element(bean_element)
+
+ REXML::Formatters::Pretty.new(3).write( doc, out = "" )
+
+ assert_equal "<bean>\n <prop key='filter'>\n (&amp;#38;(|(memberof=CN=somegroupabcdefgh,OU=OUsucks,DC=hookemhorns,DC=com)(mail=*someco.com))(acct=%u)(!(extraparameter:2.2.222.222222.2.2.222:=2)))\n </prop>\n</bean>", out
+ end
+
+ def test_ticket_138
+ doc = REXML::Document.new(
+ '<svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" ' +
+ 'inkscape:version="0.44" version="1.0"/>'
+ )
+ expected = {
+ "inkscape" => attribute("xmlns:inkscape",
+ "http://www.inkscape.org/namespaces/inkscape"),
+ "version" => {
+ "inkscape" => attribute("inkscape:version", "0.44"),
+ "" => attribute("version", "1.0"),
+ },
+ }
+ assert_equal(expected, doc.root.attributes)
+ assert_equal(expected, REXML::Document.new(doc.root.to_s).root.attributes)
+ end
+
+ def test_empty_doc
+ assert(REXML::Document.new('').children.empty?)
+ end
+
+ private
+ def attribute(name, value)
+ REXML::Attribute.new(name, value)
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_doctype.rb b/jni/ruby/test/rexml/test_doctype.rb
new file mode 100644
index 0000000..0b3ea11
--- /dev/null
+++ b/jni/ruby/test/rexml/test_doctype.rb
@@ -0,0 +1,106 @@
+require 'test/unit'
+require 'rexml/document'
+
+module REXMLTests
+ class TestDocTypeAccessor < Test::Unit::TestCase
+
+ def setup
+ @sysid = "urn:x-test:sysid1"
+ @notid1 = "urn:x-test:notation1"
+ @notid2 = "urn:x-test:notation2"
+ document_string1 = <<-"XMLEND"
+ <!DOCTYPE r SYSTEM "#{@sysid}" [
+ <!NOTATION n1 SYSTEM "#{@notid1}">
+ <!NOTATION n2 SYSTEM "#{@notid2}">
+ ]>
+ <r/>
+ XMLEND
+ @doctype1 = REXML::Document.new(document_string1).doctype
+
+ @pubid = "TEST_ID"
+ document_string2 = <<-"XMLEND"
+ <!DOCTYPE r PUBLIC "#{@pubid}">
+ <r/>
+ XMLEND
+ @doctype2 = REXML::Document.new(document_string2).doctype
+
+ document_string3 = <<-"XMLEND"
+ <!DOCTYPE r PUBLIC "#{@pubid}" "#{@sysid}">
+ <r/>
+ XMLEND
+ @doctype3 = REXML::Document.new(document_string3).doctype
+
+ end
+
+ def test_public
+ assert_equal(nil, @doctype1.public)
+ assert_equal(@pubid, @doctype2.public)
+ assert_equal(@pubid, @doctype3.public)
+ end
+
+ def test_system
+ assert_equal(@sysid, @doctype1.system)
+ assert_equal(nil, @doctype2.system)
+ assert_equal(@sysid, @doctype3.system)
+ end
+
+ def test_notation
+ assert_equal(@notid1, @doctype1.notation("n1").system)
+ assert_equal(@notid2, @doctype1.notation("n2").system)
+ end
+
+ def test_notations
+ notations = @doctype1.notations
+ assert_equal(2, notations.length)
+ assert_equal(@notid1, find_notation(notations, "n1").system)
+ assert_equal(@notid2, find_notation(notations, "n2").system)
+ end
+
+ def find_notation(notations, name)
+ notations.find { |notation|
+ name == notation.name
+ }
+ end
+
+ end
+
+ class TestNotationDeclPublic < Test::Unit::TestCase
+ def setup
+ @name = "vrml"
+ @id = "VRML 1.0"
+ @uri = "http://www.web3d.org/"
+ end
+
+ def test_to_s
+ assert_equal("<!NOTATION #{@name} PUBLIC \"#{@id}\">",
+ decl(@id, nil).to_s)
+ end
+
+ def test_to_s_with_uri
+ assert_equal("<!NOTATION #{@name} PUBLIC \"#{@id}\" \"#{@uri}\">",
+ decl(@id, @uri).to_s)
+ end
+
+ private
+ def decl(id, uri)
+ REXML::NotationDecl.new(@name, "PUBLIC", id, uri)
+ end
+ end
+
+ class TestNotationDeclSystem < Test::Unit::TestCase
+ def setup
+ @name = "gif"
+ @id = "gif viewer"
+ end
+
+ def test_to_s
+ assert_equal("<!NOTATION #{@name} SYSTEM \"#{@id}\">",
+ decl(@id).to_s)
+ end
+
+ private
+ def decl(id)
+ REXML::NotationDecl.new(@name, "SYSTEM", id, nil)
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_document.rb b/jni/ruby/test/rexml/test_document.rb
new file mode 100644
index 0000000..46cd6f7
--- /dev/null
+++ b/jni/ruby/test/rexml/test_document.rb
@@ -0,0 +1,415 @@
+# -*- coding: utf-8 -*-
+
+require "rexml/document"
+require "test/unit"
+
+module REXMLTests
+ class TestDocument < Test::Unit::TestCase
+ def test_version_attributes_to_s
+ doc = REXML::Document.new(<<-eoxml)
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+ <svg id="svg2"
+ xmlns:sodipodi="foo"
+ xmlns:inkscape="bar"
+ sodipodi:version="0.32"
+ inkscape:version="0.44.1"
+ >
+ </svg>
+ eoxml
+
+ string = doc.to_s
+ assert_match('xmlns:sodipodi', string)
+ assert_match('xmlns:inkscape', string)
+ assert_match('sodipodi:version', string)
+ assert_match('inkscape:version', string)
+ end
+
+ def test_new
+ doc = REXML::Document.new(<<EOF)
+<?xml version="1.0" encoding="UTF-8"?>
+<message>Hello world!</message>
+EOF
+ assert_equal("Hello world!", doc.root.children.first.value)
+ end
+
+ class EntityExpansionLimitTest < Test::Unit::TestCase
+ def setup
+ @default_entity_expansion_limit = REXML::Security.entity_expansion_limit
+ end
+
+ def teardown
+ REXML::Security.entity_expansion_limit = @default_entity_expansion_limit
+ end
+
+ class GeneralEntityTest < self
+ def test_have_value
+ xml = <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE member [
+ <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
+ <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
+ <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
+ <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
+ <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
+ <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
+ <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
+]>
+<member>
+&a;
+</member>
+EOF
+
+ doc = REXML::Document.new(xml)
+ assert_raise(RuntimeError) do
+ doc.root.children.first.value
+ end
+ REXML::Security.entity_expansion_limit = 100
+ assert_equal(100, REXML::Security.entity_expansion_limit)
+ doc = REXML::Document.new(xml)
+ assert_raise(RuntimeError) do
+ doc.root.children.first.value
+ end
+ assert_equal(101, doc.entity_expansion_count)
+ end
+
+ def test_empty_value
+ xml = <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE member [
+ <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
+ <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
+ <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
+ <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
+ <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
+ <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
+ <!ENTITY g "">
+]>
+<member>
+&a;
+</member>
+EOF
+
+ doc = REXML::Document.new(xml)
+ assert_raise(RuntimeError) do
+ doc.root.children.first.value
+ end
+ REXML::Security.entity_expansion_limit = 100
+ assert_equal(100, REXML::Security.entity_expansion_limit)
+ doc = REXML::Document.new(xml)
+ assert_raise(RuntimeError) do
+ doc.root.children.first.value
+ end
+ assert_equal(101, doc.entity_expansion_count)
+ end
+
+ def test_with_default_entity
+ xml = <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE member [
+ <!ENTITY a "a">
+ <!ENTITY a2 "&a; &a;">
+]>
+<member>
+&a;
+&a2;
+&lt;
+</member>
+EOF
+
+ REXML::Security.entity_expansion_limit = 4
+ doc = REXML::Document.new(xml)
+ assert_equal("\na\na a\n<\n", doc.root.children.first.value)
+ REXML::Security.entity_expansion_limit = 3
+ doc = REXML::Document.new(xml)
+ assert_raise(RuntimeError) do
+ doc.root.children.first.value
+ end
+ end
+ end
+
+ class ParameterEntityTest < self
+ def test_have_value
+ xml = <<EOF
+<!DOCTYPE root [
+ <!ENTITY % a "BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.BOOM.">
+ <!ENTITY % b "%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;">
+ <!ENTITY % c "%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;">
+ <!ENTITY % d "%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;">
+ <!ENTITY % e "%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;">
+ <!ENTITY % f "%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;">
+ <!ENTITY % g "%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;">
+ <!ENTITY test "test %g;">
+]>
+<cd></cd>
+EOF
+
+ assert_raise(REXML::ParseException) do
+ REXML::Document.new(xml)
+ end
+ REXML::Security.entity_expansion_limit = 100
+ assert_equal(100, REXML::Security.entity_expansion_limit)
+ assert_raise(REXML::ParseException) do
+ REXML::Document.new(xml)
+ end
+ end
+
+ def test_empty_value
+ xml = <<EOF
+<!DOCTYPE root [
+ <!ENTITY % a "">
+ <!ENTITY % b "%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;%a;">
+ <!ENTITY % c "%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;%b;">
+ <!ENTITY % d "%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;%c;">
+ <!ENTITY % e "%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;">
+ <!ENTITY % f "%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;%e;">
+ <!ENTITY % g "%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;">
+ <!ENTITY test "test %g;">
+]>
+<cd></cd>
+EOF
+
+ assert_raise(REXML::ParseException) do
+ REXML::Document.new(xml)
+ end
+ REXML::Security.entity_expansion_limit = 100
+ assert_equal(100, REXML::Security.entity_expansion_limit)
+ assert_raise(REXML::ParseException) do
+ REXML::Document.new(xml)
+ end
+ end
+ end
+ end
+
+ def test_tag_in_cdata_with_not_ascii_only_but_ascii8bit_encoding_source
+ tag = "<b>...</b>"
+ message = "こんにちは、世界!" # Hello world! in Japanese
+ xml = <<EOX
+<?xml version="1.0" encoding="UTF-8"?>
+<message><![CDATA[#{tag}#{message}]]></message>
+EOX
+ xml.force_encoding(Encoding::ASCII_8BIT)
+ doc = REXML::Document.new(xml)
+ assert_equal("#{tag}#{message}", doc.root.children.first.value)
+ end
+
+ def test_xml_declaration_standalone
+ bug2539 = '[ruby-core:27345]'
+ doc = REXML::Document.new('<?xml version="1.0" standalone="no" ?>')
+ assert_equal('no', doc.stand_alone?, bug2539)
+ doc = REXML::Document.new('<?xml version="1.0" standalone= "no" ?>')
+ assert_equal('no', doc.stand_alone?, bug2539)
+ doc = REXML::Document.new('<?xml version="1.0" standalone= "no" ?>')
+ assert_equal('no', doc.stand_alone?, bug2539)
+ end
+
+ class WriteTest < Test::Unit::TestCase
+ def setup
+ @document = REXML::Document.new(<<-EOX)
+<?xml version="1.0" encoding="UTF-8"?>
+<message>Hello world!</message>
+EOX
+ end
+
+ class ArgumentsTest < self
+ def test_output
+ output = ""
+ @document.write(output)
+ assert_equal(<<-EOX, output)
+<?xml version='1.0' encoding='UTF-8'?>
+<message>Hello world!</message>
+EOX
+ end
+
+ def test_indent
+ output = ""
+ indent = 2
+ @document.write(output, indent)
+ assert_equal(<<-EOX.chomp, output)
+<?xml version='1.0' encoding='UTF-8'?>
+<message>
+ Hello world!
+</message>
+EOX
+ end
+
+ def test_transitive
+ output = ""
+ indent = 2
+ transitive = true
+ @document.write(output, indent, transitive)
+ assert_equal(<<-EOX, output)
+<?xml version='1.0' encoding='UTF-8'?>
+<message
+>Hello world!</message
+>
+EOX
+ end
+
+ def test_ie_hack
+ output = ""
+ indent = -1
+ transitive = false
+ ie_hack = true
+ document = REXML::Document.new("<empty/>")
+ document.write(output, indent, transitive, ie_hack)
+ assert_equal("<empty />", output)
+ end
+
+ def test_encoding
+ output = ""
+ indent = -1
+ transitive = false
+ ie_hack = false
+ encoding = "Windows-31J"
+
+ @document.xml_decl.encoding = "Shift_JIS"
+ japanese_text = "こんにちは"
+ @document.root.text = japanese_text
+ @document.write(output, indent, transitive, ie_hack, encoding)
+ assert_equal(<<-EOX.encode(encoding), output)
+<?xml version='1.0' encoding='SHIFT_JIS'?>
+<message>#{japanese_text}</message>
+EOX
+ end
+ end
+
+ class OptionsTest < self
+ def test_output
+ output = ""
+ @document.write(:output => output)
+ assert_equal(<<-EOX, output)
+<?xml version='1.0' encoding='UTF-8'?>
+<message>Hello world!</message>
+EOX
+ end
+
+ def test_indent
+ output = ""
+ @document.write(:output => output, :indent => 2)
+ assert_equal(<<-EOX.chomp, output)
+<?xml version='1.0' encoding='UTF-8'?>
+<message>
+ Hello world!
+</message>
+EOX
+ end
+
+ def test_transitive
+ output = ""
+ @document.write(:output => output, :indent => 2, :transitive => true)
+ assert_equal(<<-EOX, output)
+<?xml version='1.0' encoding='UTF-8'?>
+<message
+>Hello world!</message
+>
+EOX
+ end
+
+ def test_ie_hack
+ output = ""
+ document = REXML::Document.new("<empty/>")
+ document.write(:output => output, :ie_hack => true)
+ assert_equal("<empty />", output)
+ end
+
+ def test_encoding
+ output = ""
+ encoding = "Windows-31J"
+ @document.xml_decl.encoding = "Shift_JIS"
+ japanese_text = "こんにちは"
+ @document.root.text = japanese_text
+ @document.write(:output => output, :encoding => encoding)
+ assert_equal(<<-EOX.encode(encoding), output)
+<?xml version='1.0' encoding='SHIFT_JIS'?>
+<message>#{japanese_text}</message>
+EOX
+ end
+ end
+ end
+
+ class BomTest < Test::Unit::TestCase
+ class HaveEncodingTest < self
+ def test_utf_8
+ xml = <<-EOX.force_encoding("ASCII-8BIT")
+<?xml version="1.0" encoding="UTF-8"?>
+<message>Hello world!</message>
+EOX
+ bom = "\ufeff".force_encoding("ASCII-8BIT")
+ document = REXML::Document.new(bom + xml)
+ assert_equal("UTF-8", document.encoding)
+ end
+
+ def test_utf_16le
+ xml = <<-EOX.encode("UTF-16LE").force_encoding("ASCII-8BIT")
+<?xml version="1.0" encoding="UTF-16"?>
+<message>Hello world!</message>
+EOX
+ bom = "\ufeff".encode("UTF-16LE").force_encoding("ASCII-8BIT")
+ document = REXML::Document.new(bom + xml)
+ assert_equal("UTF-16", document.encoding)
+ end
+
+ def test_utf_16be
+ xml = <<-EOX.encode("UTF-16BE").force_encoding("ASCII-8BIT")
+<?xml version="1.0" encoding="UTF-16"?>
+<message>Hello world!</message>
+EOX
+ bom = "\ufeff".encode("UTF-16BE").force_encoding("ASCII-8BIT")
+ document = REXML::Document.new(bom + xml)
+ assert_equal("UTF-16", document.encoding)
+ end
+ end
+
+ class NoEncodingTest < self
+ def test_utf_8
+ xml = <<-EOX.force_encoding("ASCII-8BIT")
+<?xml version="1.0"?>
+<message>Hello world!</message>
+EOX
+ bom = "\ufeff".force_encoding("ASCII-8BIT")
+ document = REXML::Document.new(bom + xml)
+ assert_equal("UTF-8", document.encoding)
+ end
+
+ def test_utf_16le
+ xml = <<-EOX.encode("UTF-16LE").force_encoding("ASCII-8BIT")
+<?xml version="1.0"?>
+<message>Hello world!</message>
+EOX
+ bom = "\ufeff".encode("UTF-16LE").force_encoding("ASCII-8BIT")
+ document = REXML::Document.new(bom + xml)
+ assert_equal("UTF-16", document.encoding)
+ end
+
+ def test_utf_16be
+ xml = <<-EOX.encode("UTF-16BE").force_encoding("ASCII-8BIT")
+<?xml version="1.0"?>
+<message>Hello world!</message>
+EOX
+ bom = "\ufeff".encode("UTF-16BE").force_encoding("ASCII-8BIT")
+ document = REXML::Document.new(bom + xml)
+ assert_equal("UTF-16", document.encoding)
+ end
+ end
+
+ class WriteTest < self
+ def test_utf_16
+ xml = <<-EOX.encode("UTF-16LE").force_encoding("ASCII-8BIT")
+<?xml version="1.0"?>
+<message>Hello world!</message>
+EOX
+ bom = "\ufeff".encode("UTF-16LE").force_encoding("ASCII-8BIT")
+ document = REXML::Document.new(bom + xml)
+
+ actual_xml = ""
+ document.write(actual_xml)
+ expected_xml = <<-EOX.encode("UTF-16BE")
+\ufeff<?xml version='1.0' encoding='UTF-16'?>
+<message>Hello world!</message>
+EOX
+ assert_equal(expected_xml, actual_xml)
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_elements.rb b/jni/ruby/test/rexml/test_elements.rb
new file mode 100644
index 0000000..f253a75
--- /dev/null
+++ b/jni/ruby/test/rexml/test_elements.rb
@@ -0,0 +1,118 @@
+require 'test/unit/testcase'
+require 'rexml/document'
+
+module REXMLTests
+ class ElementsTester < Test::Unit::TestCase
+ include REXML
+ def test_accessor
+ doc = Document.new '<a><b/><c id="1"/><c id="2"/><d/></a>'
+ assert_equal 'b', doc.root.elements[1].name
+ assert_equal '1', doc.root.elements['c'].attributes['id']
+ assert_equal '2', doc.root.elements[2,'c'].attributes['id']
+ end
+
+ def test_indexing
+ doc = Document.new '<a/>'
+ doc.root.elements[10] = Element.new('b')
+ assert_equal 'b', doc.root.elements[1].name
+ doc.root.elements[1] = Element.new('c')
+ assert_equal 'c', doc.root.elements[1].name
+ doc.root.elements['c'] = Element.new('d')
+ assert_equal 'd', doc.root.elements[1].name
+ end
+
+ def test_delete
+ doc = Document.new '<a><b/><c/><c id="1"/></a>'
+ block = proc { |str|
+ out = ''
+ doc.write out
+ assert_equal str, out
+ }
+ b = doc.root.elements[1]
+ doc.root.elements.delete b
+ block.call( "<a><c/><c id='1'/></a>" )
+ doc.elements.delete("a/c[@id='1']")
+ block.call( '<a><c/></a>' )
+ doc.root.elements.delete 1
+ block.call( '<a/>' )
+ end
+
+ def test_delete_all
+ doc = Document.new '<a><c/><c/><c/><c/></a>'
+ deleted = doc.elements.delete_all 'a/c'
+ assert_equal 4, deleted.size
+ end
+
+ def test_ticket_36
+ doc = Document.new( "<a xmlns:xi='foo'><b><xi:c id='1'/></b><xi:c id='2'/></a>" )
+
+ deleted = doc.root.elements.delete_all( "xi:c" )
+ assert_equal( 1, deleted.size )
+
+ doc = Document.new( "<a xmlns:xi='foo'><b><xi:c id='1'/></b><xi:c id='2'/></a>" )
+ deleted = doc.root.elements.delete_all( "//xi:c" )
+ assert_equal( 2, deleted.size )
+ end
+
+ def test_add
+ a = Element.new 'a'
+ a.elements.add Element.new('b')
+ assert_equal 'b', a.elements[1].name
+ a.elements.add 'c'
+ assert_equal 'c', a.elements[2].name
+ end
+
+ def test_size
+ doc = Document.new '<a>sean<b/>elliott<b/>russell<b/></a>'
+ assert_equal 6, doc.root.size
+ assert_equal 3, doc.root.elements.size
+ end
+
+ def test_each
+ doc = Document.new '<a><b/><c/><d/>sean<b/><c/><d/></a>'
+ count = 0
+ block = proc {|e| count += 1}
+ doc.root.elements.each(&block)
+ assert_equal 6, count
+ count = 0
+ doc.root.elements.each('b', &block)
+ assert_equal 2, count
+ count = 0
+ doc.root.elements.each('child::node()', &block)
+ assert_equal 6, count
+ count = 0
+ XPath.each(doc.root, 'child::node()', &block)
+ assert_equal 7, count
+ end
+
+ def test_each_with_frozen_condition
+ doc = Document.new('<books><book name="Ruby"/><book name="XML"/></books>')
+ names = []
+ doc.root.elements.each('book'.freeze) do |element|
+ names << element.attributes["name"]
+ end
+ assert_equal(["Ruby", "XML"], names)
+ end
+
+ def test_to_a
+ doc = Document.new '<a>sean<b/>elliott<c/></a>'
+ assert_equal 2, doc.root.elements.to_a.size
+ assert_equal 2, doc.root.elements.to_a("child::node()").size
+ assert_equal 4, XPath.match(doc.root, "child::node()").size
+ end
+
+ def test_collect
+ doc = Document.new( "<a><b id='1'/><b id='2'/></a>" )
+ r = doc.elements.collect( "/a/b" ) { |e| e.attributes["id"].to_i }
+ assert_equal( [1,2], r )
+ end
+
+ def test_inject
+ doc = Document.new( "<a><b id='1'/><b id='2'/></a>" )
+ r = doc.elements.inject( "/a/b", 3 ) { |s, e|
+ s + e.attributes["id"].to_i
+ }
+ assert_equal 6, r
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_encoding.rb b/jni/ruby/test/rexml/test_encoding.rb
new file mode 100644
index 0000000..058fb97
--- /dev/null
+++ b/jni/ruby/test/rexml/test_encoding.rb
@@ -0,0 +1,107 @@
+# coding: binary
+
+require_relative "rexml_test_utils"
+
+require 'rexml/source'
+require 'rexml/document'
+
+module REXMLTests
+ class EncodingTester < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML
+
+ def setup
+ @encoded_root = "<a><b>\346</b></a>"
+ @encoded = "<?xml version='1.0' encoding='ISO-8859-3'?>"+
+ @encoded_root
+ @not_encoded = "<a><b>ĉ</b></a>"
+ end
+
+ # Given an encoded document, try to write out to that encoding
+ def test_encoded_in_encoded_out
+ doc = Document.new( @encoded )
+ doc.write( out="" )
+ out.force_encoding(::Encoding::ASCII_8BIT)
+ assert_equal( @encoded, out )
+ end
+
+ # Given an encoded document, try to change the encoding and write it out
+ def test_encoded_in_change_out
+ doc = Document.new( @encoded )
+ doc.xml_decl.encoding = "UTF-8"
+ assert_equal("UTF-8", doc.encoding)
+ REXML::Formatters::Default.new.write( doc.root, out="" )
+ out.force_encoding(::Encoding::ASCII_8BIT)
+ assert_equal( @not_encoded, out )
+ char = XPath.first( doc, "/a/b/text()" ).to_s
+ char.force_encoding(::Encoding::ASCII_8BIT)
+ assert_equal( "ĉ", char )
+ end
+
+ # * Given an encoded document, try to write it to a different encoding
+ def test_encoded_in_different_out
+ doc = Document.new( @encoded )
+ REXML::Formatters::Default.new.write( doc.root, Output.new( out="", "UTF-8" ) )
+ out.force_encoding(::Encoding::ASCII_8BIT)
+ assert_equal( @not_encoded, out )
+ end
+
+ # * Given a non-encoded document, change the encoding
+ def test_in_change_out
+ doc = Document.new( @not_encoded )
+ doc.xml_decl.encoding = "ISO-8859-3"
+ assert_equal("ISO-8859-3", doc.encoding)
+ doc.write( out="" )
+ out.force_encoding(::Encoding::ASCII_8BIT)
+ assert_equal( @encoded, out )
+ end
+
+ # * Given a non-encoded document, write to a different encoding
+ def test_in_different_out
+ doc = Document.new( @not_encoded )
+ doc.write( Output.new( out="", "ISO-8859-3" ) )
+ out.force_encoding(::Encoding::ASCII_8BIT)
+ assert_equal( "<?xml version='1.0'?>#{@encoded_root}", out )
+ end
+
+ # * Given an encoded document, accessing text and attribute nodes
+ # should provide UTF-8 text.
+ def test_in_different_access
+ doc = Document.new <<-EOL
+ <?xml version='1.0' encoding='ISO-8859-1'?>
+ <a a="\xFF">\xFF</a>
+ EOL
+ expect = "\303\277"
+ expect.force_encoding(::Encoding::UTF_8)
+ assert_equal( expect, doc.elements['a'].attributes['a'] )
+ assert_equal( expect, doc.elements['a'].text )
+ end
+
+
+ def test_ticket_89
+ doc = Document.new <<-EOL
+ <?xml version="1.0" encoding="CP-1252" ?>
+ <xml><foo></foo></xml>
+ EOL
+
+ REXML::Document.new doc
+ end
+
+ def test_parse_utf16
+ utf16 = File.open(fixture_path("utf16.xml")) do |f|
+ REXML::Document.new(f)
+ end
+ assert_equal("UTF-16", utf16.encoding)
+ assert( utf16[0].kind_of?(REXML::XMLDecl))
+ end
+
+ def test_parse_utf16_with_utf8_default_internal
+ EnvUtil.with_default_internal("UTF-8") do
+ utf16 = File.open(fixture_path("utf16.xml")) do |f|
+ REXML::Document.new(f)
+ end
+ assert_equal("UTF-16", utf16.encoding)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_entity.rb b/jni/ruby/test/rexml/test_entity.rb
new file mode 100644
index 0000000..7d16dc8
--- /dev/null
+++ b/jni/ruby/test/rexml/test_entity.rb
@@ -0,0 +1,205 @@
+require "test/unit/testcase"
+
+require 'rexml/document'
+require 'rexml/entity'
+require 'rexml/source'
+
+module REXMLTests
+ class EntityTester < Test::Unit::TestCase
+ def test_parse_general_decl
+ simple = "<!ENTITY foo 'bar'>"
+ simple =~ /#{REXML::Entity::GEDECL}/
+ assert $&
+ assert_equal simple, $&
+
+ REXML::Entity::ENTITYDECL =~ simple
+ assert REXML::Entity::matches?(simple)
+ match = REXML::Entity::ENTITYDECL.match(simple)
+ assert_equal 'foo', match[1]
+ assert_equal "'bar'", match[2]
+
+ simple = '<!ENTITY Pub-Status
+ "This is a pre-release of the specification.">'
+ assert REXML::Entity::matches?(simple)
+ match = REXML::Entity::ENTITYDECL.match(simple)
+ assert_equal 'Pub-Status', match[1]
+ assert_equal '"This is a pre-release of the specification."', match[2]
+
+ txt = '"This is a
+ pre-release of <the> specification."'
+ simple = "<!ENTITY Pub-Status
+ #{txt}>"
+ assert REXML::Entity::matches?(simple)
+ match = REXML::Entity::ENTITYDECL.match(simple)
+ assert_equal 'Pub-Status', match[1]
+ assert_equal txt, match[2]
+ end
+
+ def test_parse_external_decl
+ zero = '<!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml" >'
+ one = '<!ENTITY open-hatch
+ SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">'
+ two = '<!ENTITY open-hatch
+ PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN"
+ "http://www.textuality.com/boilerplate/OpenHatch.xml">'
+ three = '<!ENTITY hatch-pic
+ SYSTEM "../grafix/OpenHatch.gif"
+ NDATA gif >'
+ assert REXML::Entity::matches?(zero)
+ assert REXML::Entity::matches?(one)
+ assert REXML::Entity::matches?(two)
+ assert REXML::Entity::matches?(three)
+ end
+
+ def test_parse_entity
+ one = %q{<!ENTITY % YN '"Yes"'>}
+ two = %q{<!ENTITY WhatHeSaid "He said %YN;">}
+ assert REXML::Entity::matches?(one)
+ assert REXML::Entity::matches?(two)
+ end
+
+ def test_constructor
+ one = [ %q{<!ENTITY % YN '"Yes"'>},
+ %q{<!ENTITY % YN2 "Yes">},
+ %q{<!ENTITY WhatHeSaid "He said %YN;">},
+ '<!ENTITY open-hatch
+ SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">',
+ '<!ENTITY open-hatch2
+ PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN"
+ "http://www.textuality.com/boilerplate/OpenHatch.xml">',
+ '<!ENTITY hatch-pic
+ SYSTEM "../grafix/OpenHatch.gif"
+ NDATA gif>' ]
+ source = %q{<!DOCTYPE foo [
+ <!ENTITY % YN '"Yes"'>
+ <!ENTITY % YN2 "Yes">
+ <!ENTITY WhatHeSaid "He said %YN;">
+ <!ENTITY open-hatch
+ SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ <!ENTITY open-hatch2
+ PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN"
+ "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ <!ENTITY hatch-pic
+ SYSTEM "../grafix/OpenHatch.gif"
+ NDATA gif>
+ ]>}
+
+ d = REXML::Document.new( source )
+ dt = d.doctype
+ c = 0
+ dt.each do |child|
+ if child.kind_of? REXML::Entity
+ str = one[c].tr("\r\n\t", ' ').squeeze(" ")
+ assert_equal str, child.to_s
+ c+=1
+ end
+ end
+ end
+
+ def test_replace_entities
+ source = "<!DOCTYPE blah [\n<!ENTITY foo \"bar\">\n]><a>&foo;</a>"
+ doc = REXML::Document.new(source)
+ assert_equal 'bar', doc.root.text
+ out = ''
+ doc.write out
+ assert_equal source, out
+ end
+
+ def test_entity_string_limit
+ template = '<!DOCTYPE bomb [ <!ENTITY a "^" > ]> <bomb>$</bomb>'
+ len = 5120 # 5k per entity
+ template.sub!(/\^/, "B" * len)
+
+ # 10k is OK
+ entities = '&a;' * 2 # 5k entity * 2 = 10k
+ xmldoc = REXML::Document.new(template.sub(/\$/, entities))
+ assert_equal(len * 2, xmldoc.root.text.bytesize)
+
+ # above 10k explodes
+ entities = '&a;' * 3 # 5k entity * 2 = 15k
+ xmldoc = REXML::Document.new(template.sub(/\$/, entities))
+ assert_raises(RuntimeError) do
+ xmldoc.root.text
+ end
+ end
+
+ def test_entity_string_limit_for_parameter_entity
+ template = '<!DOCTYPE bomb [ <!ENTITY % a "^" > <!ENTITY bomb "$" > ]><root/>'
+ len = 5120 # 5k per entity
+ template.sub!(/\^/, "B" * len)
+
+ # 10k is OK
+ entities = '%a;' * 2 # 5k entity * 2 = 10k
+ REXML::Document.new(template.sub(/\$/, entities))
+
+ # above 10k explodes
+ entities = '%a;' * 3 # 5k entity * 2 = 15k
+ assert_raises(REXML::ParseException) do
+ REXML::Document.new(template.sub(/\$/, entities))
+ end
+ end
+
+ def test_raw
+ source = '<!DOCTYPE foo [
+<!ENTITY ent "replace">
+]><a>replace &ent;</a>'
+ doc = REXML::Document.new( source, {:raw=>:all})
+ assert_equal('replace &ent;', doc.root.get_text.to_s)
+ assert_equal(source, doc.to_s)
+ end
+
+ def test_lazy_evaluation
+ source = '<!DOCTYPE foo [
+<!ENTITY ent "replace">
+]><a>replace &ent;</a>'
+ doc = REXML::Document.new( source )
+ assert_equal(source, doc.to_s)
+ assert_equal("replace replace", doc.root.text)
+ assert_equal(source, doc.to_s)
+ end
+
+ # Contributed (not only test, but bug fix!!) by Kouhei Sutou
+ def test_entity_replacement
+ source = %q{<!DOCTYPE foo [
+ <!ENTITY % YN '"Yes"'>
+ <!ENTITY WhatHeSaid "He said %YN;">]>
+ <a>&WhatHeSaid;</a>}
+
+ d = REXML::Document.new( source )
+ dt = d.doctype
+ assert_equal( '"Yes"', dt.entities[ "YN" ].value )
+ assert_equal( 'He said "Yes"', dt.entities[ "WhatHeSaid" ].value )
+ assert_equal( 'He said "Yes"', d.elements[1].text )
+ end
+
+ # More unit tests from Kouhei. I looove users who give me unit tests.
+ def test_entity_insertions
+ assert_equal("&amp;", REXML::Text.new("&amp;", false, nil, true).to_s)
+ #assert_equal("&", REXML::Text.new("&amp;", false, false).to_s)
+ end
+
+ def test_single_pass_unnormalization # ticket 123
+ assert_equal '&amp;&', REXML::Text::unnormalize('&#38;amp;&amp;')
+ end
+
+ def test_entity_filter
+ document = REXML::Document.new(<<-XML)
+<!DOCTYPE root [
+<!ENTITY copy "(c)">
+<!ENTITY release-year "2013">
+]>
+<root/>
+XML
+ respect_whitespace = false
+ parent = document.root
+ raw = false
+ entity_filter = ["copy"]
+ assert_equal("(c) &release-year;",
+ REXML::Text.new("(c) 2013",
+ respect_whitespace,
+ parent,
+ raw,
+ entity_filter).to_s)
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_functions.rb b/jni/ruby/test/rexml/test_functions.rb
new file mode 100644
index 0000000..bf501ff
--- /dev/null
+++ b/jni/ruby/test/rexml/test_functions.rb
@@ -0,0 +1,224 @@
+require "test/unit/testcase"
+
+require "rexml/document"
+
+module REXMLTests
+ class FunctionsTester < Test::Unit::TestCase
+ include REXML
+ def test_functions
+ # trivial text() test
+ # confuse-a-function
+ source = "<a>more <b id='1'/><b id='2'>dumb</b><b id='3'/><c/> text</a>"
+ doc = Document.new source
+ res = ""
+ XPath::each(doc.root, "text()") {|val| res << val.to_s}
+ assert_equal "more text", res
+
+ res = XPath::first(doc.root, "b[last()]")
+ assert_equal '3', res.attributes['id']
+ res = XPath::first(doc.root, "b[position()=2]")
+ assert_equal '2', res.attributes['id']
+ res = XPath::first(doc.root, "*[name()='c']")
+ assert_equal "c", res.name
+ end
+
+ # Contributed by Mike Stok
+ def test_starts_with
+ source = <<-EOF
+ <foo>
+ <a href="mailto:a@b.c">a@b.c</a>
+ <a href="http://www.foo.com">http://www.foo.com</a>
+ </foo>
+ EOF
+ doc = Document.new source
+ mailtos = doc.elements.to_a("//a[starts-with(@href, 'mailto:')]")
+ assert_equal 1, mailtos.size
+ assert_equal "mailto:a@b.c", mailtos[0].attributes['href']
+
+ ailtos = doc.elements.to_a("//a[starts-with(@href, 'ailto:')]")
+ assert_equal 0, ailtos.size
+ end
+
+ def test_string_length
+ doc = Document.new <<-EOF
+ <AAA>
+ <Q/>
+ <SSSS/>
+ <BB/>
+ <CCC/>
+ <DDDDDDDD/>
+ <EEEE/>
+ </AAA>
+ EOF
+ assert doc, "create doc"
+
+ set = doc.elements.to_a("//*[string-length(name()) = 3]")
+ assert_equal 2, set.size, "nodes with names length = 3"
+
+ set = doc.elements.to_a("//*[string-length(name()) < 3]")
+ assert_equal 2, set.size, "nodes with names length < 3"
+
+ set = doc.elements.to_a("//*[string-length(name()) > 3]")
+ assert_equal 3, set.size, "nodes with names length > 3"
+ end
+
+ # Test provided by Mike Stok
+ def test_contains
+ source = <<-EOF
+ <foo>
+ <a href="mailto:a@b.c">a@b.c</a>
+ <a href="http://www.foo.com">http://www.foo.com</a>
+ </foo>
+ EOF
+ doc = Document.new source
+
+ [['o', 2], ['foo', 1], ['bar', 0]].each { |test|
+ search, expected = test
+ set = doc.elements.to_a("//a[contains(@href, '#{search}')]")
+ assert_equal expected, set.size
+ }
+ end
+
+ # Mike Stok and Sean Russell
+ def test_substring
+ # examples from http://www.w3.org/TR/xpath#function-substring
+ doc = Document.new('<test string="12345" />')
+
+ #puts XPath.first(d, 'node()[0 + 1]')
+ #d = Document.new("<a b='1'/>")
+ #puts XPath.first(d, 'a[0 mod 0]')
+ [ [1.5, 2.6, '234'],
+ [0, 3, '12'],
+ [0, '0 div 0', ''],
+ [1, '0 div 0', ''],
+ ['-42', '1 div 0', '12345'],
+ ['-1 div 0', '1 div 0', '']
+ ].each { |start, length, expected|
+ set = doc.elements.to_a("//test[substring(@string, #{start}, #{length}) = '#{expected}']")
+ assert_equal 1, set.size, "#{start}, #{length}, '#{expected}'"
+ }
+ end
+
+ def test_substring_angrez
+ testString = REXML::Functions::substring_after("helloworld","hello")
+ assert_equal( 'world', testString )
+ end
+
+ def test_translate
+ source = <<-EOF
+ <doc>
+ <case name='w3c one' result='BAr' /> <!-- w3c -->
+ <case name='w3c two' result='AAA' /> <!-- w3c -->
+ <case name='alchemy' result="gold" /> <!-- mike -->
+ <case name='vbxml one' result='A Space Odyssey' />
+ <case name='vbxml two' result='AbCdEf' />
+ </doc>
+ EOF
+
+ doc = Document.new(source)
+
+ [ ['bar', 'abc', 'ABC', 'w3c one'],
+ ['--aaa--','abc-','ABC', 'w3c two'],
+ ['lead', 'dear language', 'doll groover', 'alchemy'],
+ ['A Space Odissei', 'i', 'y', 'vbxml one'],
+ ['abcdefg', 'aceg', 'ACE', 'vbxml two'],
+ ].each { |arg1, arg2, arg3, name|
+ translate = "translate('#{arg1}', '#{arg2}', '#{arg3}')"
+ set = doc.elements.to_a("//case[@result = #{translate}]")
+ assert_equal 1, set.size, translate
+ assert_equal name, set[0].attributes['name']
+ }
+ end
+
+ def test_name
+ d = REXML::Document.new("<a xmlns:x='foo'><b/><x:b/></a>")
+ assert_equal 1, d.root.elements.to_a('*[name() = "b"]').size
+ assert_equal 1, d.elements.to_a('//*[name() = "x:b"]').size
+ end
+
+ def test_local_name
+ d = REXML::Document.new("<a xmlns:x='foo'><b/><x:b/></a>")
+ assert_equal 2, d.root.elements.to_a('*[local_name() = "b"]').size
+ assert_equal 2, d.elements.to_a('//*[local_name() = "b"]').size
+ end
+
+ def test_substring2
+ doc = Document.new('<test string="12345" />')
+ assert_equal(1,doc.elements.to_a("//test[substring(@string,2)='2345']").size)
+ end
+
+ # Submitted by Kouhei
+ def test_floor_ceiling_round
+ source = "<a><b id='1'/><b id='2'/><b id='3'/></a>"
+ doc = REXML::Document.new(source)
+
+ id_1 = doc.elements["/a/b[@id='1']"]
+ id_2 = doc.elements["/a/b[@id='2']"]
+ id_3 = doc.elements["/a/b[@id='3']"]
+
+ good = {
+ "floor" => [[], [id_1], [id_2], [id_3]],
+ "ceiling" => [[id_1], [id_2], [id_3], []],
+ "round" => [[id_1], [id_2], [id_3], []]
+ }
+ good.each do |key, value|
+ (0..3).each do |i|
+ xpath = "//b[number(@id) = #{key}(#{i+0.5})]"
+ assert_equal(value[i], REXML::XPath.match(doc, xpath))
+ end
+ end
+
+ good["round"] = [[], [id_1], [id_2], [id_3]]
+ good.each do |key, value|
+ (0..3).each do |i|
+ xpath = "//b[number(@id) = #{key}(#{i+0.4})]"
+ assert_equal(value[i], REXML::XPath.match(doc, xpath))
+ end
+ end
+ end
+
+ # Submitted by Kou
+ def test_lang
+ d = Document.new(<<-XML)
+ <a xml:lang="en">
+ <b xml:lang="ja">
+ <c xml:lang="fr"/>
+ <d/>
+ <e xml:lang="ja-JP"/>
+ <f xml:lang="en-US"/>
+ </b>
+ </a>
+ XML
+
+ assert_equal(1, d.elements.to_a("//*[lang('fr')]").size)
+ assert_equal(3, d.elements.to_a("//*[lang('ja')]").size)
+ assert_equal(2, d.elements.to_a("//*[lang('en')]").size)
+ assert_equal(1, d.elements.to_a("//*[lang('en-us')]").size)
+
+ d = Document.new(<<-XML)
+ <root>
+ <para xml:lang="en"/>
+ <div xml:lang="en"><para/></div>
+ <para xml:lang="EN"/>
+ <para xml:lang="en-us"/>
+ </root>
+ XML
+
+ assert_equal(5, d.elements.to_a("//*[lang('en')]").size)
+ end
+
+ def test_ticket_60
+ document = REXML::Document.new("<a><b>A</b><b>1</b></a>")
+ assert_equal( "A", REXML::XPath.first(document, '//b[.="A"]').text )
+ assert_equal( "1", REXML::XPath.first(document, '//b[.="1"]').text )
+ end
+
+ def test_normalize_space
+ source = "<a><!--COMMENT A--><b><!-- COMMENT A --></b></a>"
+ doc = REXML::Document.new(source)
+ predicate = "string(.)=normalize_space('\nCOMMENT \n A \n\n ')"
+ m = REXML::XPath.match(doc, "//comment()[#{predicate}]")
+ assert_equal( [REXML::Comment.new("COMMENT A")], m )
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_functions_number.rb b/jni/ruby/test/rexml/test_functions_number.rb
new file mode 100644
index 0000000..0b5ec92
--- /dev/null
+++ b/jni/ruby/test/rexml/test_functions_number.rb
@@ -0,0 +1,34 @@
+require 'rexml/document'
+require 'test/unit'
+require 'rexml/functions'
+
+module REXMLTests
+ class TC_Rexml_Functions_Number < Test::Unit::TestCase
+
+ def test_functions_number_int
+ telem = REXML::Element.new("elem")
+ telem.text="9"
+ assert_equal(9, REXML::Functions::number(telem))
+ end
+ def test_functions_number_float
+ telem = REXML::Element.new("elem")
+ telem.text="10.4"
+ assert_equal(10.4, REXML::Functions::number(telem))
+ end
+ def test_functions_number_negative_int
+ telem = REXML::Element.new("elem")
+ telem.text="-9"
+ assert_equal(-9, REXML::Functions::number(telem))
+ end
+ def test_functions_number_negative_float
+ telem = REXML::Element.new("elem")
+ telem.text="-9.13"
+ assert_equal(-9.13, REXML::Functions::number(telem))
+ end
+ #def test_functions_number_scientific_notation
+ # telem = REXML::Element.new("elem")
+ # telem.text="9.13E12"
+ # assert_equal(9.13E12, REXML::Functions::number(telem))
+ #end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_jaxen.rb b/jni/ruby/test/rexml/test_jaxen.rb
new file mode 100644
index 0000000..1eec73f
--- /dev/null
+++ b/jni/ruby/test/rexml/test_jaxen.rb
@@ -0,0 +1,129 @@
+require_relative 'rexml_test_utils'
+
+require "rexml/document"
+require "rexml/xpath"
+
+# Harness to test REXML's capabilities against the test suite from Jaxen
+# ryan.a.cox@gmail.com
+
+module REXMLTests
+ class JaxenTester < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML
+
+ def test_axis ; test("axis") ; end
+ def test_basic ; test("basic") ; end
+ def test_basicupdate ; test("basicupdate") ; end
+ def test_contents ; test("contents") ; end
+ def test_defaultNamespace ; test("defaultNamespace") ; end
+ def test_fibo ; test("fibo") ; end
+ def test_id ; test("id") ; end
+ def test_jaxen24 ; test("jaxen24") ; end
+ def test_lang ; test("lang") ; end
+ def test_message ; test("message") ; end
+ def test_moreover ; test("moreover") ; end
+ def test_much_ado ; test("much_ado") ; end
+ def test_namespaces ; test("namespaces") ; end
+ def test_nitf ; test("nitf") ; end
+ def test_numbers ; test("numbers") ; end
+ def test_pi ; test("pi") ; end
+ def test_pi2 ; test("pi2") ; end
+ def test_simple ; test("simple") ; end
+ def test_testNamespaces ; test("testNamespaces") ; end
+ def test_text ; test("text") ; end
+ def test_underscore ; test("underscore") ; end
+ def test_web ; test("web") ; end
+ def test_web2 ; test("web2") ; end
+
+ private
+ def test( fname )
+# Dir.entries( xml_dir ).each { |fname|
+# if fname =~ /\.xml$/
+ doc = File.open(fixture_path(fname+".xml")) do |file|
+ Document.new(file)
+ end
+ XPath.each( doc, "/tests/document" ) {|e| handleDocument(e)}
+# end
+# }
+ end
+
+ # processes a tests/document/context node
+ def handleContext( testDoc, ctxElement)
+ testCtx = XPath.match( testDoc, ctxElement.attributes["select"] )[0]
+ namespaces = {}
+ if testCtx.class == Element
+ testCtx.prefixes.each { |pre| handleNamespace( testCtx, pre, namespaces ) }
+ end
+ variables = {}
+ XPath.each( ctxElement, "@*[namespace-uri() = 'http://jaxen.org/test-harness/var']") { |attrib| handleVariable(testCtx, variables, attrib) }
+ XPath.each( ctxElement, "valueOf") { |e| handleValueOf(testCtx, variables, namespaces, e) }
+ XPath.each( ctxElement, "test[not(@exception) or (@exception != 'true') ]") { |e| handleNominalTest(testCtx,variables, namespaces, e) }
+ XPath.each( ctxElement, "test[@exception = 'true']") { |e| handleExceptionalTest(testCtx,variables, namespaces, e) }
+ end
+
+ # processes a tests/document/context/valueOf or tests/document/context/test/valueOf node
+ def handleValueOf(ctx,variables, namespaces, valueOfElement)
+ expected = valueOfElement.text
+ got = XPath.match( ctx, valueOfElement.attributes["select"], namespaces, variables )[0]
+ assert_true( (got.nil? && expected.nil?) || !got.nil? )
+ case got.class
+ when Element
+ assert_equal( got.class, Element )
+ when Attribute, Text, Comment, TrueClass, FalseClass
+ assert_equal( expected, got.to_s )
+ when Instruction
+ assert_equal( expected, got.content )
+ when Fixnum
+ assert_equal( exected.to_f, got )
+ when String
+ # normalize values for comparison
+ got = "" if got == nil or got == ""
+ expected = "" if expected == nil or expected == ""
+ assert_equal( expected, got )
+ else
+ assert_fail( "Wassup?" )
+ end
+ end
+
+
+ # processes a tests/document/context/test node ( where @exception is false or doesn't exist )
+ def handleNominalTest(ctx, variables, namespaces, testElement)
+ expected = testElement.attributes["count"]
+ got = XPath.match( ctx, testElement.attributes["select"], namespaces, variables )
+ # might be a test with no count attribute, but nested valueOf elements
+ assert( expected == got.size.to_s ) if !expected.nil?
+
+ XPath.each( testElement, "valueOf") { |e|
+ handleValueOf(got, variables, namespaces, e)
+ }
+ end
+
+ # processes a tests/document/context/test node ( where @exception is true )
+ def handleExceptionalTest(ctx, variables, namespaces, testElement)
+ assert_raise( Exception ) {
+ XPath.match( ctx, testElement.attributes["select"], namespaces, variables )
+ }
+ end
+
+ # processes a tests/document node
+ def handleDocument(docElement)
+ puts "- Processing document: #{docElement.attributes['url']}"
+ testFile = File.new( docElement.attributes["url"] )
+ testDoc = Document.new testFile
+ XPath.each( docElement, "context") { |e| handleContext(testDoc, e) }
+ end
+
+ # processes a variable definition in a namespace like <test var:foo="bar">
+ def handleVariable( ctx, variables, attrib )
+ puts "--- Found attribute: #{attrib.name}"
+ variables[attrib.name] = attrib.value
+ end
+
+ # processes a namespace definition like <test xmlns:foo="fiz:bang:bam">
+ def handleNamespace( ctx, prefix, namespaces )
+ puts "--- Found namespace: #{prefix}"
+ namespaces[prefix] = ctx.namespaces[prefix]
+ end
+
+ end
+end
diff --git a/jni/ruby/test/rexml/test_light.rb b/jni/ruby/test/rexml/test_light.rb
new file mode 100644
index 0000000..b775eb7
--- /dev/null
+++ b/jni/ruby/test/rexml/test_light.rb
@@ -0,0 +1,106 @@
+require_relative "rexml_test_utils"
+require "rexml/light/node"
+require "rexml/parsers/lightparser"
+
+module REXMLTests
+ class LightTester < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML::Light
+
+ def test_parse_large
+ xml_string = fixture_path("documentation.xml")
+ parser = REXML::Parsers::LightParser.new(xml_string)
+ tag, content = parser.parse
+ assert_equal([:document, :text], [tag, content.first])
+ end
+
+ # FIXME INCOMPLETE
+ # This is because the light API is not yet ready to be used to produce
+ # trees.
+=begin
+ def test_add_element
+ doc = Node.new
+ foo = doc.add_element( 'foo' )
+ assert_equal( "foo", foo.name )
+ end
+
+ def test_add_attribute
+ foo = Node.new( "a" )
+ foo["attr"] = "bar"
+ assert_equal( "bar", foo["attr"] )
+ end
+
+ def test_write_document
+ r = make_small_document
+ assert_equal( "<a><b/><c/></a>", r.to_s )
+ end
+
+ def test_add_attribute_under_namespace
+ foo = Node.new("a")
+ foo["attr", "a"] = "1"
+ foo["attr", "b"] = "2"
+ foo["attr"] = "3"
+ assert_equal( '1', foo['attr', 'a'] )
+ assert_equal( '2', foo['attr', 'b'] )
+ assert_equal( '3', foo['attr'] )
+ end
+
+ def test_change_namespace_of_element
+ foo = Node.new
+ assert_equal( '', foo.namespace )
+ foo.namespace = 'a'
+ assert_equal( 'a', foo.namespace )
+ end
+
+ def test_access_child_elements
+ foo = make_small_document
+ assert_equal( 1, foo.size )
+ a = foo[0]
+ assert_equal( 2, a.size )
+ assert_equal( 'b', a[0].name )
+ assert_equal( 'c', a[1].name )
+ end
+
+ def test_itterate_over_children
+ foo = make_small_document
+ ctr = 0
+ foo[0].each { ctr += 1 }
+ assert_equal( 2, ctr )
+ end
+
+ def test_add_text
+ foo = Node.new( "a" )
+ foo.add_text( "Sean" )
+ sean = foo[0]
+ assert( sean.node_type == :text )
+ end
+
+ def test_add_instruction
+ foo = Node.new( "a" )
+ foo.add_instruction( "target", "value" )
+ assert( foo[0].node_type == :processing_instruction )
+ end
+
+ def test_add_comment
+ foo = Node.new( "a" )
+ foo.add_comment( "target", "value" )
+ assert( foo[0].node_type == :comment )
+ end
+
+ def test_get_root
+ foo = Node.new( 'a' )
+ 10.times { foo = foo.add_element('b') }
+ assert_equals( 'b', foo.name )
+ assert_equals( 'a', foo.root.name )
+ end
+
+ def make_small_document
+ r = Node.new
+ a = r.add_element( "a" )
+ a.add_element( 'b' )
+ a.add_element( 'c' )
+ r
+ end
+=end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_lightparser.rb b/jni/ruby/test/rexml/test_lightparser.rb
new file mode 100644
index 0000000..f8212dc
--- /dev/null
+++ b/jni/ruby/test/rexml/test_lightparser.rb
@@ -0,0 +1,15 @@
+require_relative 'rexml_test_utils'
+require 'rexml/parsers/lightparser'
+
+module REXMLTests
+ class LightParserTester < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML
+ def test_parsing
+ File.open(fixture_path("documentation.xml")) do |f|
+ parser = REXML::Parsers::LightParser.new( f )
+ parser.parse
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_listener.rb b/jni/ruby/test/rexml/test_listener.rb
new file mode 100644
index 0000000..309e827
--- /dev/null
+++ b/jni/ruby/test/rexml/test_listener.rb
@@ -0,0 +1,130 @@
+# coding: binary
+
+require_relative 'rexml_test_utils'
+
+require 'rexml/document'
+require 'rexml/streamlistener'
+
+module REXMLTests
+ class BaseTester < Test::Unit::TestCase
+ include REXMLTestUtils
+ def test_empty
+ return unless defined? @listener
+ # Empty.
+ t1 = %Q{<string></string>}
+ assert_equal( "", @listener.parse( t1 ),
+ "Empty" )
+ end
+
+ def test_space
+ return unless defined? @listener
+ # Space.
+ t2 = %Q{<string> </string>}
+ assert_equal( " ", @listener.parse( t2 ),
+ "Space" )
+ end
+
+ def test_whitespace
+ return unless defined? @listener
+ # Whitespaces.
+ t3 = %Q{<string>RE\n \t \n \t XML</string>}
+ assert_equal( "RE\n \t \n \t XML", @listener.parse( t3 ),
+ "Whitespaces" )
+ end
+
+ def test_leading_trailing_whitespace
+ return unless defined? @listener
+ # Leading and trailing whitespaces.
+ t4 = %Q{<string> REXML </string>}
+ assert_equal( " REXML ", @listener.parse( t4 ),
+ "Leading and trailing whitespaces" )
+ end
+
+ def test_entity_reference
+ return unless defined? @listener
+ # Entity reference.
+ t5 = %Q{<string>&lt;&gt;&amp;lt;&amp;gt;</string>}
+ assert_equal( "<>&lt;&gt;", @listener.parse( t5 ),
+ "Entity reference" )
+ end
+
+ def test_character_reference
+ return unless defined? @listener
+ # Character reference.
+ t6 = %Q{<string>&#xd;</string>}
+ assert_equal( "\r", @listener.parse( t6 ),
+ "Character reference." )
+ end
+
+ def test_cr
+ return unless defined? @listener
+ # CR.
+ t7 = %Q{<string> \r\n \r \n </string>}
+ assert_equal( " \n \n \n ".unpack("C*").inspect,
+ @listener.parse( t7 ).unpack("C*").inspect, "CR" )
+ end
+
+ # The accent bug, and the code that exhibits the bug, was contributed by
+ # Guilhem Vellut
+ class AccentListener
+ def tag_start(name,attributes)
+ #p name
+ #p attributes
+ end
+ def tag_end(name)
+ #p "/"+name
+ end
+ def xmldecl(a,b,c)
+ #puts "#{a} #{b} #{c}"
+ end
+ def text(tx)
+ #p tx
+ end
+ end
+
+ def test_accents
+ source = %[<?xml version="1.0" encoding="ISO-8859-1"?>
+<g>
+<f a="\xE9" />
+</g>]
+ doc = REXML::Document.new( source )
+ a = doc.elements['/g/f'].attribute('a')
+ if a.value.respond_to? :force_encoding
+ a.value.force_encoding('binary')
+ end
+ assert_equal( "\xC3\xA9", a.value)
+ doc = File::open(fixture_path("stream_accents.xml")) do |f|
+ REXML::Document.parse_stream(f, AccentListener::new)
+ end
+ end
+ end
+
+ class MyREXMLListener
+ include REXML::StreamListener
+
+ def initialize
+ @text = nil
+ end
+
+ def parse( stringOrReadable )
+ @text = ""
+ REXML::Document.parse_stream( stringOrReadable, self )
+ @text
+ end
+
+ def text( text )
+ @text << text
+ end
+ end
+
+ class REXMLTester < BaseTester
+ def setup
+ @listener = MyREXMLListener.new
+ end
+
+ def test_character_reference_2
+ t6 = %Q{<string>&#xd;</string>}
+ assert_equal( t6.strip, REXML::Document.new(t6).to_s )
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_martin_fowler.rb b/jni/ruby/test/rexml/test_martin_fowler.rb
new file mode 100644
index 0000000..26db110
--- /dev/null
+++ b/jni/ruby/test/rexml/test_martin_fowler.rb
@@ -0,0 +1,39 @@
+require 'test/unit'
+require 'rexml/document'
+
+module REXMLTests
+ class OrderTester < Test::Unit::TestCase
+ DOC = <<END
+<paper>
+<title>Remove this element and figs order differently</title>
+<figure src="fig1"/>
+<figure src="fig2"/>
+<p>Para of text</p>
+<p>Remove this and figs order differently</p>
+<section>
+<figure src="fig3"/>
+</section>
+<figure src="fig4"/>
+</paper>
+END
+
+ def initialize n
+ @doc = REXML::Document.new(DOC)
+ @figs = REXML::XPath.match(@doc,'//figure')
+ @names = @figs.collect {|f| f.attributes['src']}
+ super
+ end
+ def test_fig1
+ assert_equal 'fig1', @figs[0].attributes['src']
+ end
+ def test_fig2
+ assert_equal 'fig2', @figs[1].attributes['src']
+ end
+ def test_fig3
+ assert_equal 'fig3', @figs[2].attributes['src']
+ end
+ def test_fig4
+ assert_equal 'fig4', @figs[3].attributes['src']
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_namespace.rb b/jni/ruby/test/rexml/test_namespace.rb
new file mode 100644
index 0000000..f5cd294
--- /dev/null
+++ b/jni/ruby/test/rexml/test_namespace.rb
@@ -0,0 +1,40 @@
+require_relative "rexml_test_utils"
+
+require "rexml/document"
+
+module REXMLTests
+ class TestNamespace < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML
+
+ def setup
+ @xsa_source = <<-EOL
+ <?xml version="1.0"?>
+ <?xsl stylesheet="blah.xsl"?>
+ <!-- The first line tests the XMLDecl, the second tests PI.
+ The next line tests DocType. This line tests comments. -->
+ <!DOCTYPE xsa PUBLIC
+ "-//LM Garshol//DTD XML Software Autoupdate 1.0//EN//XML"
+ "http://www.garshol.priv.no/download/xsa/xsa.dtd">
+
+ <xsa>
+ <vendor id="blah">
+ <name>Lars Marius Garshol</name>
+ <email>larsga@garshol.priv.no</email>
+ <url>http://www.stud.ifi.uio.no/~lmariusg/</url>
+ </vendor>
+ </xsa>
+ EOL
+ end
+
+ def test_xml_namespace
+ xml = <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns:xml="http://www.w3.org/XML/1998/namespace" />
+XML
+ document = Document.new(xml)
+ assert_equal("http://www.w3.org/XML/1998/namespace",
+ document.root.namespace("xml"))
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_order.rb b/jni/ruby/test/rexml/test_order.rb
new file mode 100644
index 0000000..0e84961
--- /dev/null
+++ b/jni/ruby/test/rexml/test_order.rb
@@ -0,0 +1,109 @@
+require_relative 'rexml_test_utils'
+require 'rexml/document'
+begin
+ require 'zlib'
+rescue LoadError
+end
+
+module REXMLTests
+ class OrderTester < Test::Unit::TestCase
+ include REXMLTestUtils
+
+ TESTDOC = <<END
+<a>
+ <b/>
+ <x id='1'/>
+ <c/>
+ <d>
+ <x id='2'/>
+ </d>
+ <x id='3'/>
+</a>
+END
+
+ def setup
+ @doc = REXML::Document.new(TESTDOC)
+ @items = REXML::XPath.match(@doc,'//x')
+ end
+ def test_first_element
+ assert_equal '1', @items[0].attributes['id']
+ end
+ def test_second_element
+ assert_equal '2', @items[1].attributes['id']
+ end
+ def test_third_element
+ assert_equal '3', @items[2].attributes['id']
+ end
+ def test_order
+ d = REXML::Document.new( "<a><x id='1'/><x id='2'/><x id='3'/>
+ <x id='4'/><x id='5'/></a>" )
+ items = REXML::XPath.match( d, '//x' )
+ assert_equal( %w{1 2 3 4 5}, items.collect{|e| e.attributes['id']} )
+ d = REXML::Document.new( "<a>
+ <x><z><y id='1'/><y id='2'/></z><y id='3'/></x>
+ <x><y id='4'/></x></a>" )
+ items = REXML::XPath.match( d, '//y' )
+ assert_equal( %w{1 2 3 4}, items.collect{|e| e.attributes['id']} )
+ end
+ # Provided by Tom Talbott
+ def test_more_ordering
+ doc = Zlib::GzipReader.open(fixture_path('LostineRiver.kml.gz'), encoding: 'utf-8') do |f|
+ REXML::Document.new(f)
+ end
+ actual = [
+ "Head south from Phinney Ave N",
+ "Turn left at N 36th St",
+ "Turn right at Fremont Ave N",
+ "Continue on 4th Ave N",
+ "Turn left at Westlake Ave N",
+ "Bear right at 9th Ave N",
+ "Turn left at Mercer St",
+ "Take the I-5 ramp",
+ "Take the I-5 S ramp",
+ "Take the I-90 E exit #164 to Bellevue/Spokane/4th Ave S.",
+ "Take the I-90 E ramp to Bellevue/Spokane",
+ "Take exit #137 to Wanapum Dam/Richland",
+ "Bear right at WA-26",
+ "Bear right and head toward WA-243",
+ "Continue on WA-243",
+ "Bear right at WA-24",
+ "Continue on WA-240",
+ "Turn right at WA-240 E",
+ "Take the I-182 W ramp to Yakima (I-82)/Pendleton",
+ "Take the I-82 E ramp to Umatilla/Pendleton",
+ "Take the I-84 E ramp to Pendleton",
+ "Take the OR-82 exit #261 to La Grande/Elgin",
+ "Turn right at Island Ave",
+ "Continue on W 1st St",
+ "Turn left at N McAlister Rd",
+ "Bear right at OR-82",
+ "Continue on Wallowa Lake Hwy",
+ "Continue on OR-82",
+ "Continue on Ruckman Ave",
+ "Continue on OR-82",
+ "Continue on S 8th Ave",
+ "Turn right at Albany St",
+ "Continue on OR-82",
+ "Continue on Wallowa Lake Hwy",
+ "Continue on N Madison St",
+ "Bear left at W 1st St",
+ "Continue on Wallowa Lake Hwy",
+ "Continue on Water St",
+ "Bear right at Lostine River Rd",
+ "Bear right and head toward Lostine River Rd",
+ "Turn right at Lostine River Rd",
+ "Continue on NF-8210",
+ "Turn right and head toward NF-8210",
+ "Turn right at NF-8210",
+ "",
+ "Route"
+ ]
+ count = 0
+ REXML::XPath.each( doc, "//Placemark") { |element|
+ n = element.elements["name"].text.squeeze(" ")
+ assert_equal( actual[count], n ) unless n =~ /Arrive at/
+ count += 1
+ }
+ end if defined?(Zlib::GzipReader)
+ end
+end
diff --git a/jni/ruby/test/rexml/test_preceding_sibling.rb b/jni/ruby/test/rexml/test_preceding_sibling.rb
new file mode 100644
index 0000000..54fdd5b
--- /dev/null
+++ b/jni/ruby/test/rexml/test_preceding_sibling.rb
@@ -0,0 +1,40 @@
+# ISSUE 32
+require 'test/unit'
+require 'rexml/document'
+
+module REXMLTests
+ # daz - for report by Dan Kohn in:
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/156328
+ class XPathTesterDd < Test::Unit::TestCase
+ include REXML
+
+ def setup
+ @@docDd = Document.new(<<-EOS, :ignore_whitespace_nodes => :all)
+ <a>
+ <b x='ab01A'>
+ <c y='abc01A'>Success</c>
+ </b>
+ <b x='ab02A' y='ab02B'>
+ <c>abc02C</c>
+ </b>
+ </a>
+ EOS
+ end
+
+ def test_Dd_preceding_sibling_children
+ arr = []
+ XPath.each(@@docDd, "//b[@x='ab02A']/preceding-sibling::b/child::*") do |cell|
+ arr << cell.texts.join
+ end
+ assert_equal( 'Success', arr.join )
+ end
+
+ def test_Dd_preceding_sibling_all
+ arr = []
+ XPath.each(@@docDd, "//b[@x='ab02A']/preceding-sibling::*") do |cell|
+ arr << cell.to_s
+ end
+ assert_equal( "<b x='ab01A'><c y='abc01A'>Success</c></b>", arr.join )
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_pullparser.rb b/jni/ruby/test/rexml/test_pullparser.rb
new file mode 100644
index 0000000..db336a1
--- /dev/null
+++ b/jni/ruby/test/rexml/test_pullparser.rb
@@ -0,0 +1,102 @@
+require "test/unit/testcase"
+
+require 'rexml/parsers/pullparser'
+
+module REXMLTests
+ class PullParserTester < Test::Unit::TestCase
+ include REXML
+ def test_basics
+ source = '<?xml version="1.0"?>
+ <!DOCTYPE blah>
+ <a>foo &lt;<b attribute="value">bar</b> nooo</a>'
+ parser = REXML::Parsers::PullParser.new(source)
+ res = { :text=>0 }
+ until parser.empty?
+ results = parser.pull
+ res[ :xmldecl ] = true if results.xmldecl?
+ res[ :doctype ] = true if results.doctype?
+ res[ :a ] = true if results.start_element? and results[0] == 'a'
+ if results.start_element? and results[0] == 'b'
+ res[ :b ] = true
+ assert_equal 'value', results[1]['attribute']
+ end
+ res[ :text ] += 1 if results.text?
+ end
+ [ :xmldecl, :doctype, :a, :b ].each { |tag|
+ assert res[tag] , "#{tag} wasn't processed"
+ }
+ assert_equal 4, res[ :text ]
+ rescue ParseException
+ puts $!
+ end
+
+ def test_bad_document
+ source = "<a><b></a>"
+ parser = REXML::Parsers::PullParser.new(source)
+ assert_raise(ParseException, "Parsing should have failed") {
+ parser.pull while parser.has_next?
+ }
+ end
+
+ def test_entity_replacement
+ source = '<!DOCTYPE foo [
+ <!ENTITY la "1234">
+ <!ENTITY lala "--&la;--">
+ <!ENTITY lalal "&la;&la;">
+ ]><a><la>&la;</la><lala>&lala;</lala></a>'
+ pp = REXML::Parsers::PullParser.new( source )
+ el_name = ''
+ while pp.has_next?
+ event = pp.pull
+ case event.event_type
+ when :start_element
+ el_name = event[0]
+ when :text
+ case el_name
+ when 'la'
+ assert_equal('1234', event[1])
+ when 'lala'
+ assert_equal('--1234--', event[1])
+ end
+ end
+ end
+ end
+
+ def test_peek_unshift
+ source = "<a><b/></a>"
+ REXML::Parsers::PullParser.new(source)
+ # FINISH ME!
+ end
+
+ def test_inspect
+ xml = '<a id="1"><b id="2">Hey</b></a>'
+ parser = Parsers::PullParser.new( xml )
+ while parser.has_next?
+ pull_event = parser.pull
+ if pull_event.start_element?
+ peek = parser.peek()
+ peek.inspect
+ end
+ end
+ end
+
+ def test_peek
+ xml = '<a id="1"><b id="2">Hey</b></a>'
+ parser = Parsers::PullParser.new( xml )
+ names = %w{ a b }
+ while parser.has_next?
+ pull_event = parser.pull
+ if pull_event.start_element?
+ assert_equal( :start_element, pull_event.event_type )
+ assert_equal( names.shift, pull_event[0] )
+ if names[0] == 'b'
+ peek = parser.peek()
+ assert_equal( :start_element, peek.event_type )
+ assert_equal( names[0], peek[0] )
+ end
+ end
+ end
+ assert_equal( 0, names.length )
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_rexml_issuezilla.rb b/jni/ruby/test/rexml/test_rexml_issuezilla.rb
new file mode 100644
index 0000000..bb63466
--- /dev/null
+++ b/jni/ruby/test/rexml/test_rexml_issuezilla.rb
@@ -0,0 +1,18 @@
+require_relative 'rexml_test_utils'
+require 'rexml/document'
+
+module REXMLTests
+ class TestIssuezillaParsing < Test::Unit::TestCase
+ include REXMLTestUtils
+ def test_rexml
+ doc = File.open(fixture_path("ofbiz-issues-full-177.xml")) do |f|
+ REXML::Document.new(f)
+ end
+ ctr = 1
+ doc.root.each_element('//issue') do |issue|
+ assert_equal( ctr, issue.elements['issue_id'].text.to_i )
+ ctr += 1
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_sax.rb b/jni/ruby/test/rexml/test_sax.rb
new file mode 100644
index 0000000..2d37ec9
--- /dev/null
+++ b/jni/ruby/test/rexml/test_sax.rb
@@ -0,0 +1,286 @@
+require_relative "rexml_test_utils"
+require 'rexml/sax2listener'
+require 'rexml/parsers/sax2parser'
+require 'rexml/document'
+
+module REXMLTests
+ class SAX2Tester < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML
+ def test_characters
+ d = Document.new( "<A>@blah@</A>" )
+ txt = d.root.text
+ p = Parsers::SAX2Parser.new "<A>@blah@</A>"
+ p.listen(:characters) {|x| assert_equal txt, x}
+ p.listen(:characters, ["A"]) {|x| assert_equal txt,x}
+ p.parse
+ end
+
+ def test_entity_replacement
+ source = '<!DOCTYPE foo [
+ <!ENTITY la "1234">
+ <!ENTITY lala "--&la;--">
+ <!ENTITY lalal "&la;&la;">
+ ]><a><la>&la;</la><lala>&lala;</lala></a>'
+ sax = Parsers::SAX2Parser.new( source )
+ results = []
+ sax.listen(:characters) {|x| results << x }
+ sax.parse
+ assert_equal 2, results.size
+ assert_equal '1234', results[0]
+ assert_equal '--1234--', results[1]
+ end
+
+ def test_sax2
+ File.open(fixture_path("documentation.xml")) do |f|
+ parser = Parsers::SAX2Parser.new( f )
+ # Listen to all events on the following elements
+ count = 0
+ blok = proc { |uri,localname,qname,attributes|
+ assert %w{ bugs todo }.include?(localname),
+ "Mismatched name; we got '#{qname}'\nArgs were:\n\tURI: #{uri}\n\tLOCALNAME: #{localname}\n\tQNAME: #{qname}\n\tATTRIBUTES: #{attributes.inspect}\n\tSELF=#{blok}"
+ count += 1
+ }
+
+ start_document = 0
+ end_document = 0
+ parser.listen( :start_document ) { start_document += 1 }
+ parser.listen( :end_document ) { end_document += 1 }
+ parser.listen( :start_element, %w{ changelog bugs todo }, &blok )
+ # Listen to all events on the following elements. Synonymous with
+ # listen( :start_element, %w{ ... } )
+ parser.listen( %w{ changelog bugs todo }, &blok )
+ # Listen for all start element events
+ parser.listen( :start_element ) { |uri,localname,qname,attributes|
+ }
+ listener = MySAX2Listener.new
+ # Listen for all events
+ parser.listen( listener )
+ # Listen for all events on the given elements. Does not include children
+ # events. Regular expressions work as well!
+ parser.listen( %w{ /change/ bugs todo }, listener )
+ # Test the deafening method
+ blok = proc { |uri,localname,qname,attributes|
+ assert_fail "This listener should have been deafened!"
+ }
+ parser.listen( %w{ changelog }, &blok )
+ parser.deafen( &blok )
+
+ tc = 0
+ parser.listen( :characters, %w{version} ) {|text|
+ assert(text=~/@ANT_VERSION@/, "version was '#{text}'")
+ tc += 1
+ }
+
+ begin
+ parser.parse
+ rescue => exception
+ if exception.kind_of? Test::Unit::AssertionFailedError
+ raise exception
+ end
+ puts $!
+ puts exception.backtrace
+ end
+ assert_equal 2, count
+ assert_equal 1, tc
+ assert_equal 1, start_document
+ assert_equal 1, end_document
+ end
+ end
+
+ # used by test_simple_doctype_listener
+ # submitted by Jeff Barczewski
+ class SimpleDoctypeListener
+ include REXML::SAX2Listener
+ attr_reader :name, :pub_sys, :long_name, :uri
+
+ def initialize
+ @name = @pub_sys = @long_name = @uri = nil
+ end
+
+ def doctype(name, pub_sys, long_name, uri)
+ @name = name
+ @pub_sys = pub_sys
+ @long_name = long_name
+ @uri = uri
+ end
+ end
+
+ # test simple non-entity doctype in sax listener
+ # submitted by Jeff Barczewski
+ def test_simple_doctype_listener
+ xml = <<-END
+ <?xml version="1.0"?>
+ <!DOCTYPE greeting PUBLIC "Hello Greeting DTD" "http://foo/hello.dtd">
+ <greeting>Hello, world!</greeting>
+ END
+ parser = Parsers::SAX2Parser.new(xml)
+ dtl = SimpleDoctypeListener.new
+ parser.listen(dtl)
+ tname = nil
+ tpub_sys = nil
+ tlong_name = nil
+ turi = nil
+ parser.listen(:doctype) do |name, pub_sys, long_name, uri|
+ tname = name
+ tpub_sys = pub_sys
+ tlong_name = long_name
+ turi = uri
+ end
+ parser.parse
+ assert_equal 'greeting', tname, 'simple doctype block listener failed - incorrect name'
+ assert_equal 'PUBLIC', tpub_sys, 'simple doctype block listener failed - incorrect pub_sys'
+ assert_equal 'Hello Greeting DTD', tlong_name, 'simple doctype block listener failed - incorrect long_name'
+ assert_equal 'http://foo/hello.dtd', turi, 'simple doctype block listener failed - incorrect uri'
+ assert_equal 'greeting', dtl.name, 'simple doctype listener failed - incorrect name'
+ assert_equal 'PUBLIC', dtl.pub_sys, 'simple doctype listener failed - incorrect pub_sys'
+ assert_equal 'Hello Greeting DTD', dtl.long_name, 'simple doctype listener failed - incorrect long_name'
+ assert_equal 'http://foo/hello.dtd', dtl.uri, 'simple doctype listener failed - incorrect uri'
+ end
+
+ # test doctype with missing name, should throw ParseException
+ # submitted by Jeff Barczewseki
+ def test_doctype_with_mising_name_throws_exception
+ xml = <<-END
+ <?xml version="1.0"?>
+ <!DOCTYPE >
+ <greeting>Hello, world!</greeting>
+ END
+ parser = Parsers::SAX2Parser.new(xml)
+ assert_raise(REXML::ParseException, 'doctype missing name did not throw ParseException') do
+ parser.parse
+ end
+ end
+
+
+ class KouListener
+ include REXML::SAX2Listener
+ attr_accessor :sdoc, :edoc
+ attr_reader :selem, :decl, :pi
+ def initialize
+ @sdoc = @edoc = @selem = false
+ @decl = 0
+ @pi = 0
+ end
+ def start_document
+ @sdoc = true
+ end
+ def end_document
+ @edoc = true
+ end
+ def xmldecl( *arg )
+ @decl += 1
+ end
+ def processing_instruction( *arg )
+ @pi += 1
+ end
+ def start_element( *arg )
+ @selem = true
+ end
+ end
+
+ # Submitted by Kou
+ def test_begin_end_document
+ parser = Parsers::SAX2Parser.new("<a/>")
+
+ kl = KouListener.new
+ parser.listen(kl)
+ sd = false
+ ed = false
+ parser.listen(:start_document) { sd = true }
+ parser.listen(:end_document) { ed = true }
+
+ parser.parse
+ assert( sd, ':start_document block failed' )
+ assert( ed, ':end_document block failed' )
+ assert( kl.sdoc, ':start_document listener failed' )
+ assert( kl.edoc, ':end_document listener failed' )
+ end
+
+ # Submitted by Kou
+ def test_listen_before_start
+ # FIXME: the following comment should be a test for validity. (The xml declaration
+ # is invalid).
+ #parser = Parsers::SAX2Parser.new( "<?xml ?><?pi?><a><?pi?></a>")
+ parser = Parsers::SAX2Parser.new( "<?xml version='1.0'?><?pi?><a><?pi?></a>")
+ k1 = KouListener.new
+ parser.listen( k1 )
+ xmldecl = false
+ pi = 0
+ parser.listen( :xmldecl ) { xmldecl = true }
+ parser.listen( :processing_instruction ) { pi += 1 }
+
+ parser.parse
+
+ assert( xmldecl, ':xmldecl failed' )
+ assert_equal( 2, pi, ':processing_instruction failed' )
+ assert( k1.decl, 'Listener for xmldecl failed' )
+ assert_equal( 2, k1.pi, 'Listener for processing instruction failed' )
+ end
+
+
+ def test_socket
+ require 'socket'
+
+ TCPServer.open('127.0.0.1', 0) do |server|
+ TCPSocket.open('127.0.0.1', server.addr[1]) do |socket|
+ ok = false
+ session = server.accept
+ begin
+ session << '<foo>'
+ parser = REXML::Parsers::SAX2Parser.new(socket)
+ Fiber.new do
+ parser.listen(:start_element) do
+ ok = true
+ Fiber.yield
+ end
+ parser.parse
+ end.resume
+ assert(ok)
+ ensure
+ session.close
+ end
+ end
+ end
+ end
+
+ def test_char_ref_sax2()
+ parser = REXML::Parsers::SAX2Parser.new('<ABC>&#252;</ABC>')
+ result = nil
+ parser.listen(:characters) {|text| result = text.unpack('U*')}
+ parser.parse()
+ assert_equal(1, result.size)
+ assert_equal(252, result[0])
+ end
+
+
+ def test_char_ref_dom()
+ doc = REXML::Document.new('<ABC>&#252;</ABC>')
+ result = doc.root.text.unpack('U*')
+ assert_equal(1, result.size)
+ assert_equal(252, result[0])
+ end
+
+ class Ticket68
+ include REXML::SAX2Listener
+ end
+ def test_ticket_68
+ File.open(fixture_path('ticket_68.xml')) do |f|
+ parser = REXML::Parsers::SAX2Parser.new(f)
+ parser.listen( Ticket68.new )
+ begin
+ parser.parse
+ rescue
+ p parser.source.position
+ p parser.source.current_line
+ puts $!.backtrace.join("\n")
+ flunk $!.message
+ end
+ end
+ end
+ end
+
+ class MySAX2Listener
+ include REXML::SAX2Listener
+ end
+end
diff --git a/jni/ruby/test/rexml/test_stream.rb b/jni/ruby/test/rexml/test_stream.rb
new file mode 100644
index 0000000..3164d91
--- /dev/null
+++ b/jni/ruby/test/rexml/test_stream.rb
@@ -0,0 +1,129 @@
+require "test/unit/testcase"
+require "rexml/document"
+require 'rexml/streamlistener'
+require 'stringio'
+
+module REXMLTests
+ class MyListener
+ include REXML::StreamListener
+ end
+
+
+ class StreamTester < Test::Unit::TestCase
+ # Submitted by Han Holl
+ def test_listener
+ data = %Q{<session1 user="han" password="rootWeiler" />\n<session2 user="han" password="rootWeiler" />}
+
+ b = RequestReader.new( data )
+ b = RequestReader.new( data )
+ end
+
+ def test_ticket_49
+ source = StringIO.new( <<-EOL )
+ <!DOCTYPE foo [
+ <!ENTITY ent "replace">
+ ]>
+ <a>&ent;</a>
+ EOL
+ REXML::Document.parse_stream(source, MyListener.new)
+ end
+
+ def test_ticket_10
+ source = StringIO.new( <<-EOL )
+ <!DOCTYPE foo [
+ <!ENTITY ent "replace">
+ <!ATTLIST a
+ xmlns:human CDATA #FIXED "http://www.foo.com/human">
+ <!ELEMENT bar (#PCDATA)>
+ <!NOTATION n1 PUBLIC "-//HM//NOTATION TEST1//EN" 'urn:x-henrikmartensson.org:test5'>
+ ]>
+ <a/>
+ EOL
+ listener = MyListener.new
+ class << listener
+ attr_accessor :events
+ def entitydecl( content )
+ @events[ :entitydecl ] = true
+ end
+ def attlistdecl( element_name, attributes, raw_content )
+ @events[ :attlistdecl ] = true
+ end
+ def elementdecl( content )
+ @events[ :elementdecl ] = true
+ end
+ def notationdecl( content )
+ @events[ :notationdecl ] = true
+ end
+ end
+ listener.events = {}
+
+ REXML::Document.parse_stream( source, listener )
+
+ assert( listener.events[:entitydecl] )
+ assert( listener.events[:attlistdecl] )
+ assert( listener.events[:elementdecl] )
+ assert( listener.events[:notationdecl] )
+ end
+
+ def test_entity
+ listener = MyListener.new
+ class << listener
+ attr_accessor :entities
+ def entity(content)
+ @entities << content
+ end
+ end
+ listener.entities = []
+
+ source = StringIO.new(<<-XML)
+<!DOCTYPE root [
+<!ENTITY % ISOLat2
+ SYSTEM "http://www.xml.com/iso/isolat2-xml.entities" >
+%ISOLat2;
+]>
+<root/>
+ XML
+ REXML::Document.parse_stream(source, listener)
+
+ assert_equal(["ISOLat2"], listener.entities)
+ end
+ end
+
+
+ # For test_listener
+ class RequestReader
+ attr_reader :doc
+ def initialize(io)
+ @stack = []
+ @doc = nil
+ catch(:fini) do
+ REXML::Document.parse_stream(io, self)
+ raise IOError
+ end
+ end
+ def tag_start(name, args)
+ if @doc
+ @stack.push(REXML::Element.new(name, @stack.last))
+ else
+ @doc = REXML::Document.new("<#{name}/>")
+ @stack.push(@doc.root)
+ end
+ args.each do |attr,val|
+ @stack.last.add_attribute(attr, val)
+ end
+ end
+ def tag_end(name, *args)
+ @stack.pop
+ throw(:fini) if @stack.empty?
+ end
+ def text(str)
+ @stack.last.text = str
+ end
+ def comment(str)
+ end
+ def doctype( name, pub_sys, long_name, uri )
+ end
+ def doctype_end
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_text.rb b/jni/ruby/test/rexml/test_text.rb
new file mode 100644
index 0000000..52ee11e
--- /dev/null
+++ b/jni/ruby/test/rexml/test_text.rb
@@ -0,0 +1,21 @@
+require "rexml/text"
+
+module REXMLTests
+ class TextTester < Test::Unit::TestCase
+ include REXML
+
+ def test_shift_operator_chain
+ text = Text.new("original\r\n")
+ text << "append1\r\n" << "append2\r\n"
+ assert_equal("original\nappend1\nappend2\n", text.to_s)
+ end
+
+ def test_shift_operator_cache
+ text = Text.new("original\r\n")
+ text << "append1\r\n" << "append2\r\n"
+ assert_equal("original\nappend1\nappend2\n", text.to_s)
+ text << "append3\r\n" << "append4\r\n"
+ assert_equal("original\nappend1\nappend2\nappend3\nappend4\n", text.to_s)
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_ticket_80.rb b/jni/ruby/test/rexml/test_ticket_80.rb
new file mode 100644
index 0000000..ffeece5
--- /dev/null
+++ b/jni/ruby/test/rexml/test_ticket_80.rb
@@ -0,0 +1,58 @@
+#------------------------------------------------------------------------------
+# file: rexml_test.rb
+# desc: test's REXML's XML/XPath implementation
+# auth: Philip J Grabner <grabner>at<uberdev>dot<org>
+# date: 2006/08/17
+# copy: (C) CopyLoose 2006 Bib Development Team <bib-devel>at<uberdev>dot<org>
+#------------------------------------------------------------------------------
+
+require 'test/unit'
+require 'rexml/document'
+
+module REXMLTests
+ class Ticket80 < Test::Unit::TestCase
+
+ @@xmlstr = '<?xml version="1.0"?>
+<root xmlns="urn:some-xml-ns" xmlns:other="urn:some-other-xml-ns">
+ <l1-foo>
+ <l2 value="foo-01"/>
+ <l2 value="foo-02"/>
+ <l2 value="foo-03"/>
+ </l1-foo>
+ <other:l1>
+ <l2 value="no-show"/>
+ </other:l1>
+ <l1-bar>
+ <l2 value="bar-01"/>
+ <l2 value="bar-02"/>
+ </l1-bar>
+</root>'
+
+ #----------------------------------------------------------------------------
+ def test_xpathNamespacedChildWildcard
+ # tests the "prefix:*" node test syntax
+ out = Array.new
+ REXML::XPath.each( REXML::Document.new(@@xmlstr),
+ '/ns:root/ns:*/ns:l2/@value',
+ { 'ns' => 'urn:some-xml-ns' } ) do |node| out.push node.value ; end
+ chk = [ 'foo-01', 'foo-02', 'foo-03', 'bar-01', 'bar-02' ]
+ assert_equal chk, out
+ end
+
+ #----------------------------------------------------------------------------
+ def test_xpathNamespacedChildWildcardWorkaround
+ # tests a workaround for the "prefix:*" node test syntax
+ out = Array.new
+ REXML::XPath.each( REXML::Document.new(@@xmlstr),
+ '/ns:root/*[namespace-uri()="urn:some-xml-ns"]/ns:l2/@value',
+ { 'ns' => 'urn:some-xml-ns' } ) do |node| out.push node.value ; end
+ chk = [ 'foo-01', 'foo-02', 'foo-03', 'bar-01', 'bar-02' ]
+ assert_equal chk, out
+ end
+
+ end
+end
+
+#------------------------------------------------------------------------------
+# end of rexml_test.rb
+#------------------------------------------------------------------------------
diff --git a/jni/ruby/test/rexml/test_validation_rng.rb b/jni/ruby/test/rexml/test_validation_rng.rb
new file mode 100644
index 0000000..8989fe2
--- /dev/null
+++ b/jni/ruby/test/rexml/test_validation_rng.rb
@@ -0,0 +1,792 @@
+require "test/unit/testcase"
+
+require "rexml/document"
+require "rexml/validation/relaxng"
+
+module REXMLTests
+ class RNGValidation < Test::Unit::TestCase
+ include REXML
+
+ def test_validate
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <element name="C">
+ <attribute name="X"/>
+ <zeroOrMore>
+ <element name="E">
+ <empty/>
+ </element>
+ </zeroOrMore>
+ </element>
+ <element name="D">
+ <empty/>
+ </element>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ no_error( validator, %q{<A><B><C X="x"><E/><E/></C><D/></B></A>} )
+ error( validator, %q{<A><B><D/><C X="x"/></B></A>} )
+ end
+
+
+ def test_sequence
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <element name="C">
+ <empty/>
+ </element>
+ <element name="D">
+ <empty/>
+ </element>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B><C/><C/><D/></B></A>} )
+ error( validator, %q{<A><B><D/><C/></B></A>} )
+ error( validator, %q{<A><C/><D/></A>} )
+ no_error( validator, %q{<A><B><C/><D/></B></A>} )
+ end
+
+
+ def test_choice
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <choice>
+ <element name="C">
+ <empty/>
+ </element>
+ <element name="D">
+ <empty/>
+ </element>
+ </choice>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B><C/><D/></B></A>} )
+ no_error( validator, %q{<A><B><D/></B></A>} )
+ no_error( validator, %q{<A><B><C/></B></A>} )
+ end
+
+ def test_optional
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <optional>
+ <element name="C">
+ <empty/>
+ </element>
+ </optional>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ no_error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><B><C/></B></A>} )
+ error( validator, %q{<A><B><D/></B></A>} )
+ error( validator, %q{<A><B><C/><C/></B></A>} )
+ end
+
+ def test_zero_or_more
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <zeroOrMore>
+ <element name="C">
+ <empty/>
+ </element>
+ </zeroOrMore>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+ no_error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><B><C/></B></A>} )
+ no_error( validator, %q{<A><B><C/><C/><C/></B></A>} )
+ error( validator, %q{<A><B><D/></B></A>} )
+ error( validator, %q{<A></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <zeroOrMore>
+ <element name="C">
+ <empty/>
+ </element>
+ <element name="D">
+ <empty/>
+ </element>
+ </zeroOrMore>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ no_error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><B><C/><D/></B></A>} )
+ no_error( validator, %q{<A><B><C/><D/><C/><D/></B></A>} )
+ error( validator, %q{<A><B><D/></B></A>} )
+ end
+
+ def test_one_or_more
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <oneOrMore>
+ <element name="C">
+ <empty/>
+ </element>
+ </oneOrMore>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><B><C/></B></A>} )
+ no_error( validator, %q{<A><B><C/><C/><C/></B></A>} )
+ error( validator, %q{<A><B><D/></B></A>} )
+ error( validator, %q{<A></A>} )
+ end
+
+ def test_attribute
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <attribute name="X"/>
+ <attribute name="Y"/>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A/>} )
+ error( validator, %q{<A X=""/>} )
+ no_error( validator, %q{<A X="1" Y="1"/>} )
+ end
+
+ def test_choice_attributes
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <choice>
+ <attribute name="X"/>
+ <attribute name="Y"/>
+ </choice>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A X="1" Y="1"/>} )
+ error( validator, %q{<A/>} )
+ no_error( validator, %q{<A X="1"/>})
+ no_error( validator, %q{<A Y="1"/>} )
+ end
+
+ def test_choice_attribute_element
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <choice>
+ <attribute name="X"/>
+ <element name="B"/>
+ </choice>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A X="1"><B/></A>} )
+ error( validator, %q{<A/>} )
+ no_error( validator, %q{<A X="1"/>})
+ no_error( validator, %q{<A><B/></A>} )
+ end
+
+ def test_empty
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <empty/>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A>Text</A>} )
+ no_error( validator, %q{<A/>})
+ end
+
+ def test_text_val
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <text/>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A>Text</A>} )
+ error( validator, %q{<A/>})
+ end
+
+ def test_choice_text
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <choice>
+ <element name="B"/>
+ <text/>
+ </choice>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/>Text</A>} )
+ error( validator, %q{<A>Text<B/></A>} )
+ no_error( validator, %q{<A>Text</A>} )
+ no_error( validator, %q{<A><B/></A>} )
+ end
+
+ def test_group
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <choice>
+ <element name="B"/>
+ <group>
+ <element name="C"/>
+ <element name="D"/>
+ </group>
+ </choice>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/><C/></A>} )
+ error( validator, %q{<A><C/></A>} )
+ no_error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><C/><D/></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B"/>
+ <group>
+ <element name="C"/>
+ <element name="D"/>
+ </group>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/><C/></A>} )
+ error( validator, %q{<A><B/><D/></A>} )
+ error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><B/><C/><D/></A>} )
+ end
+
+ def test_value
+ # Values as text nodes
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <value>VaLuE</value>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B>X</B></A>} )
+ error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><B>VaLuE</B></A>} )
+
+ # Values as text nodes, via choice
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <choice>
+ <value>Option 1</value>
+ <value>Option 2</value>
+ </choice>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A><B>XYZ</B></A>} )
+ no_error( validator, %q{<A><B>Option 1</B></A>} )
+ no_error( validator, %q{<A><B>Option 2</B></A>} )
+
+ # Attribute values
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <attribute name="B">
+ <value>VaLuE</value>
+ </attribute>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A/>} )
+ error( validator, %q{<A B=""/>} )
+ error( validator, %q{<A B="Lala"/>} )
+ no_error( validator, %q{<A B="VaLuE"/>} )
+
+ # Attribute values via choice
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <attribute name="B">
+ <choice>
+ <value>Option 1</value>
+ <value>Option 2</value>
+ </choice>
+ </attribute>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A B=""/>} )
+ error( validator, %q{<A B="Value"/>} )
+ no_error( validator, %q{<A B="Option 1"></A>} )
+ no_error( validator, %q{<A B="Option 2"/>} )
+ end
+
+ def test_interleave
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <interleave>
+ <element name="C"/>
+ <element name="D"/>
+ <element name="E"/>
+ </interleave>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B><C/></B></A>} )
+ error( validator, %q{<A><B><C/><D/><C/></B></A>} )
+ no_error( validator, %q{<A><B><C/><D/><E/></B></A>} )
+ no_error( validator, %q{<A><B><E/><D/><C/></B></A>} )
+ no_error( validator, %q{<A><B><D/><C/><E/></B></A>} )
+ no_error( validator, %q{<A><B><E/><C/><D/></B></A>} )
+ error( validator, %q{<A><B><E/><C/><D/><C/></B></A>} )
+ end
+
+ def test_mixed
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<element name="A" xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="B">
+ <mixed>
+ <element name="D"/>
+ </mixed>
+ </element>
+</element>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ no_error( validator, %q{<A><B>Text<D/></B></A>} )
+ no_error( validator, %q{<A><B><D/>Text</B></A>} )
+ end
+
+ def test_ref_sequence
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <ref name="B"/>
+ <ref name="B"/>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B">
+ <attribute name="X"/>
+ </element>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ no_error( validator, %q{<A><B X=''/><B X=''/></A>} )
+ error( validator, %q{<A><B X=''/></A>} )
+ end
+
+ def test_ref_choice
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <choice>
+ <ref name="B"/>
+ </choice>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B"/>
+ <element name="C"/>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><D/></A>} )
+ error( validator, %q{<A><B/><C/></A>} )
+ no_error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><C/></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <ref name="B"/>
+ </element>
+ </start>
+
+ <define name="B">
+ <choice>
+ <element name="B"/>
+ <element name="C"/>
+ </choice>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><D/></A>} )
+ error( validator, %q{<A><B/><C/></A>} )
+ no_error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><C/></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <choice>
+ <ref name="B"/>
+ <element name="D"/>
+ </choice>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B"/>
+ <element name="C"/>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/><C/></A>} )
+ no_error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><C/></A>} )
+ no_error( validator, %q{<A><D/></A>} )
+ end
+
+
+ def test_ref_zero_plus
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <zeroOrMore>
+ <ref name="B"/>
+ </zeroOrMore>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B">
+ <attribute name="X"/>
+ </element>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A/>} )
+ no_error( validator, %q{<A><B X=''/></A>} )
+ no_error( validator, %q{<A><B X=''/><B X=''/><B X=''/></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <ref name="B"/>
+ </element>
+ </start>
+
+ <define name="B">
+ <zeroOrMore>
+ <element name="B">
+ <attribute name="X"/>
+ </element>
+ </zeroOrMore>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A/>} )
+ no_error( validator, %q{<A><B X=''/></A>} )
+ no_error( validator, %q{<A><B X=''/><B X=''/><B X=''/></A>} )
+ end
+
+
+ def test_ref_one_plus
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <oneOrMore>
+ <ref name="B"/>
+ </oneOrMore>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B">
+ <attribute name="X"/>
+ </element>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A/>} )
+ no_error( validator, %q{<A><B X=''/></A>} )
+ no_error( validator, %q{<A><B X=''/><B X=''/><B X=''/></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <ref name="B"/>
+ </element>
+ </start>
+
+ <define name="B">
+ <oneOrMore>
+ <element name="B">
+ <attribute name="X"/>
+ </element>
+ </oneOrMore>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A/>} )
+ no_error( validator, %q{<A><B X=''/></A>} )
+ no_error( validator, %q{<A><B X=''/><B X=''/><B X=''/></A>} )
+ end
+
+ def test_ref_interleave
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <interleave>
+ <ref name="B"/>
+ </interleave>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B"/>
+ <element name="C"/>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A><C/></A>} )
+ error( validator, %q{<A><C/><C/></A>} )
+ no_error( validator, %q{<A><B/><C/></A>} )
+ no_error( validator, %q{<A><C/><B/></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <ref name="B"/>
+ </element>
+ </start>
+
+ <define name="B">
+ <interleave>
+ <element name="B"/>
+ <element name="C"/>
+ </interleave>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A><C/></A>} )
+ error( validator, %q{<A><C/><C/></A>} )
+ no_error( validator, %q{<A><B/><C/></A>} )
+ no_error( validator, %q{<A><C/><B/></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <interleave>
+ <ref name="B"/>
+ <ref name="C"/>
+ </interleave>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B"/>
+ </define>
+ <define name="C">
+ <element name="C"/>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A><C/></A>} )
+ error( validator, %q{<A><C/><C/></A>} )
+ no_error( validator, %q{<A><B/><C/></A>} )
+ no_error( validator, %q{<A><C/><B/></A>} )
+ end
+
+ def test_ref_recurse
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <ref name="B"/>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B">
+ <optional>
+ <ref name="B"/>
+ </optional>
+ </element>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ error( validator, %q{<A></A>} )
+ no_error( validator, %q{<A><B/></A>} )
+ no_error( validator, %q{<A><B><B/></B></A>} )
+ end
+
+ def test_ref_optional
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <optional>
+ <ref name="B"/>
+ </optional>
+ </element>
+ </start>
+
+ <define name="B">
+ <element name="B">
+ </element>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ no_error( validator, %q{<A></A>} )
+ no_error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A><B/><B/></A>} )
+ error( validator, %q{<A><C/></A>} )
+
+ rng = %q{
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <element name="A">
+ <ref name="B"/>
+ </element>
+ </start>
+
+ <define name="B">
+ <optional>
+ <element name="B">
+ </element>
+ </optional>
+ </define>
+</grammar>
+ }
+ validator = REXML::Validation::RelaxNG.new( rng )
+
+ no_error( validator, %q{<A></A>} )
+ no_error( validator, %q{<A><B/></A>} )
+ error( validator, %q{<A><B/><B/></A>} )
+ error( validator, %q{<A><C/></A>} )
+ end
+
+
+
+ def error( validator, source )
+ parser = REXML::Parsers::TreeParser.new( source )
+ parser.add_listener( validator.reset )
+ assert_raise( REXML::Validation::ValidationException,
+ "Expected a validation error" ) { parser.parse }
+ end
+
+ def no_error( validator, source )
+ parser = REXML::Parsers::TreeParser.new( source )
+ parser.add_listener( validator.reset )
+ assert_nothing_raised { parser.parse }
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/test_xml_declaration.rb b/jni/ruby/test/rexml/test_xml_declaration.rb
new file mode 100644
index 0000000..a96d40c
--- /dev/null
+++ b/jni/ruby/test/rexml/test_xml_declaration.rb
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+#
+# Created by Henrik Mårtensson on 2007-02-18.
+# Copyright (c) 2007. All rights reserved.
+
+require "rexml/document"
+require "test/unit"
+
+module REXMLTests
+ class TestXmlDeclaration < Test::Unit::TestCase
+ def setup
+ xml = <<-'END_XML'
+ <?xml encoding= 'UTF-8' standalone='yes'?>
+ <root>
+ </root>
+ END_XML
+ @doc = REXML::Document.new xml
+ @root = @doc.root
+ @xml_declaration = @doc.children[0]
+ end
+
+ def test_is_first_child
+ assert_kind_of(REXML::XMLDecl, @xml_declaration)
+ end
+
+ def test_has_document_as_parent
+ assert_kind_of(REXML::Document, @xml_declaration.parent)
+ end
+
+ def test_has_sibling
+ assert_kind_of(REXML::XMLDecl, @root.previous_sibling.previous_sibling)
+ assert_kind_of(REXML::Element, @xml_declaration.next_sibling.next_sibling)
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/xpath/test_attribute.rb b/jni/ruby/test/rexml/xpath/test_attribute.rb
new file mode 100644
index 0000000..95af4de
--- /dev/null
+++ b/jni/ruby/test/rexml/xpath/test_attribute.rb
@@ -0,0 +1,29 @@
+require 'test/unit'
+require 'rexml/document'
+
+module REXMLTests
+ class TestXPathAttribute < Test::Unit::TestCase
+ def setup
+ @xml = <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+ <child name="one">child1</child>
+ <child name="two">child2</child>
+ <child name="three">child3</child>
+</root>
+ XML
+ @document = REXML::Document.new(@xml)
+ end
+
+ def test_elements
+ root = @document.elements["root"]
+ second_child = root.elements["child[@name='two']"]
+ assert_equal("child2", second_child.text)
+ end
+
+ def test_xpath_each
+ children = REXML::XPath.each(@document, "/root/child[@name='two']")
+ assert_equal(["child2"], children.collect(&:text))
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/xpath/test_axis_preceding_sibling.rb b/jni/ruby/test/rexml/xpath/test_axis_preceding_sibling.rb
new file mode 100644
index 0000000..2731807
--- /dev/null
+++ b/jni/ruby/test/rexml/xpath/test_axis_preceding_sibling.rb
@@ -0,0 +1,39 @@
+require "test/unit/testcase"
+require "rexml/document"
+
+module REXMLTests
+ class TestXPathAxisPredcedingSibling < Test::Unit::TestCase
+ include REXML
+ SOURCE = <<-EOF
+ <a id='1'>
+ <e id='2'>
+ <f id='3'/>
+ <f id='4'/>
+ <f id='5'/>
+ <f id='6'/>
+ </e>
+ </a>
+ EOF
+
+ def setup
+ @@doc = Document.new(SOURCE) unless defined? @@doc
+ end
+
+ def test_preceding_sibling_axis
+ context = XPath.first(@@doc,"/a/e/f[last()]")
+ assert_equal "6", context.attributes["id"]
+
+ prev = XPath.first(context, "preceding-sibling::f")
+ assert_equal "5", prev.attributes["id"]
+
+ prev = XPath.first(context, "preceding-sibling::f[1]")
+ assert_equal "5", prev.attributes["id"]
+
+ prev = XPath.first(context, "preceding-sibling::f[2]")
+ assert_equal "4", prev.attributes["id"]
+
+ prev = XPath.first(context, "preceding-sibling::f[3]")
+ assert_equal "3", prev.attributes["id"]
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/xpath/test_base.rb b/jni/ruby/test/rexml/xpath/test_base.rb
new file mode 100644
index 0000000..a57ba63
--- /dev/null
+++ b/jni/ruby/test/rexml/xpath/test_base.rb
@@ -0,0 +1,1089 @@
+require_relative "../rexml_test_utils"
+
+require "rexml/document"
+
+module REXMLTests
+ class TestXPathBase < Test::Unit::TestCase
+ include REXMLTestUtils
+ include REXML
+ SOURCE = <<-EOF
+ <a id='1'>
+ <b id='2' x='y'>
+ <c id='3'/>
+ <c id='4'/>
+ </b>
+ <d id='5'>
+ <c id='6' x='y'/>
+ <c id='7'/>
+ <c id='8'/>
+ <q id='19'/>
+ </d>
+ <e id='9'>
+ <f id='10' a='b'/>
+ <f id='11' a='c'/>
+ <f id='12' a='d'>
+ <g id='13'/>
+ </f>
+ <f id='14' a='d'/>
+ </e>
+ <m id='15'>
+ <n id='16'>
+ <o id='17'>
+ <p id='18'/>
+ </o>
+ </n>
+ </m>
+ </a>
+ EOF
+ JENI_TENNISON = <<-EOJT
+ <a>
+ <b>
+ <c>
+ <d>
+ <e id='x'>
+ <f/>
+ </e>
+ </d>
+ </c>
+ <c>
+ <d>
+ <e id='y'/>
+ </d>
+ </c>
+ </b>
+ <b>
+ <c>
+ <d>
+ <e id='z'/>
+ </d>
+ </c>
+ </b>
+ </a>
+ EOJT
+
+ def setup
+ @@doc = Document.new(SOURCE) unless defined? @@doc
+ @@jeni = Document.new( JENI_TENNISON ) unless defined? @@jeni
+ end
+
+ def each_test( element, xpath )
+ count = 0
+ XPath::each( element, xpath ) { |child|
+ count += 1
+ yield child if block_given?
+ }
+ count
+ end
+
+ def test_descendant
+ doc = Document.new("<a><b><c id='1'/></b><d><b><c id='2'/></b></d></a>")
+ p = XPath.match( doc, "//c" )
+ assert_equal( 2, p.size )
+ p = XPath.first( @@doc, "//p" )
+ assert_equal "p", p.name
+ c = each_test( @@doc, "//c" ) { |child| assert_equal "c", child.name }
+ assert_equal 5, c
+ c = each_test( @@doc.root, "b//c" ) { |child|
+ assert_equal "c", child.name
+ }
+ assert_equal 2, c
+
+ doc = Document.new( "<a><z id='1'/><b><z id='11'/><z id='12'/></b><c><z id='21'/><z id='22'/><d><z id='31'/><z id='32'/></d></c></a>" )
+ # //para[1] : all descendants which are the first para child of their parent
+ assert_equal( 4, XPath.match( doc, "//z[1]" ).size )
+ # /descendant::para[1] : the first descendant para element
+ assert_equal( 1, XPath.match( doc, "/descendant::z[1]" ).size )
+ end
+
+ def test_root
+ source = "<a><b/></a>"
+ doc = Document.new( source )
+ assert_equal doc, doc.root_node
+ assert_equal "a", XPath::first( doc, "/" ).elements[1].name
+ end
+
+ def test_abbreviated_simple_child
+ assert_equal "a", XPath::first(@@doc, "a").name
+ end
+
+ def test_child
+ c = XPath::first( @@doc, "a/b/c" )
+ assert_equal "c", c.name
+ assert_equal "3", XPath::first(@@doc, "a/b/c").attributes["id"]
+ end
+
+ def test_root_child
+ assert_equal "a", XPath::first(@@doc, "/a").name
+ c = XPath::first( @@doc, "a/b/c" )
+ assert_equal "a", XPath::first(c, "/a").name
+ end
+
+ def test_root_children
+ c = XPath::first( @@doc, "a/b/c" )
+ assert_equal "2", XPath::first(c, "/a/b").attributes["id"]
+ end
+
+ def test_abbreviated_step
+ c = XPath::first( @@doc, "a/b/c" )
+ assert_equal("c", c.name)
+ assert_equal("a", XPath::first(@@doc.root, ".").name)
+ assert_equal("b", XPath::first(c, "..").name)
+ assert_equal("a", XPath::first(@@doc, "a/b/..").name)
+
+ doc = File.open(fixture_path("project.xml")) do |f|
+ REXML::Document.new(f)
+ end
+ c = each_test(doc.root, "./Description" ) { |child|
+ assert_equal("Description",child.name)
+ }
+ assert_equal 1, c
+ end
+
+ # Things that aren't tested elsewhere
+ def test_predicates
+ assert_equal "12", XPath::first(@@doc, "a/e/f[3]").attributes["id"]
+ assert_equal "13", XPath::first(@@doc, "a/e/f[3]/g").attributes["id"]
+ assert_equal "14", XPath::first(@@doc, "a/e/f[@a='d'][2]").attributes["id"]
+ assert_equal "14", XPath::first(@@doc, "a/e/f[@a='d'][@id='14']").attributes["id"]
+ assert_equal "a", XPath::first( @@doc, "*[name()='a' and @id='1']" ).name
+ c=each_test( @@doc, "//*[name()='f' and @a='d']") { |i|
+ assert_equal "f", i.name
+ }
+ assert_equal 2, c
+ c=each_test( @@doc, "//*[name()='m' or @a='d']") { |i|
+ assert ["m","f"].include?(i.name)
+ }
+ assert_equal 3, c
+
+ assert_equal "b", XPath::first( @@doc, "//b[@x]" ).name
+ end
+
+ def test_node_type
+ doc = Document.new "<a><?foo bar?><!--comment-->text</a>"
+ #res = XPath::first(doc.root, "text()")
+ #assert_equal "text", res.to_s
+
+ #res = XPath::first(doc, "*")
+ #assert_equal "a", res.name
+
+ assert_equal( :processing_instruction,
+ XPath::first(doc.root, "processing-instruction()").node_type)
+ assert_equal( :comment, XPath::first(doc.root, "comment()").node_type)
+ end
+
+ def test_functions
+ # trivial text() test
+ # confuse-a-function
+ source = "<a>more <b id='1'/><b id='2'>dumb</b><b id='3'/><c/> text</a>"
+ doc = Document.new source
+ res = ""
+ #XPath::each(doc.root, "text()") {|val| res << val.to_s}
+ #assert_equal "more text", res
+
+ #res = XPath::first(doc.root, "b[last()]")
+ #assert_equal '3', res.attributes['id']
+ res = XPath::first(doc.root, "b[position()=2]")
+ assert_equal '2', res.attributes['id']
+ res = XPath::first(doc.root, "*[name()='c']")
+ assert_equal "c", res.name
+ end
+
+ def no_test_ancestor
+ doc = REXML::Document.new(File.new(fixture_path("testsrc.xml")))
+ doc.elements.each("//item") { |el| print el.name
+ if el.attributes['x']
+ puts " -- "+el.attributes['x']
+ else
+ puts
+ end
+ }
+ doc.elements.each("//item/ancestor::") { |el| print el.name
+ if el.attributes['x']
+ puts " -- "+el.attributes['x']
+ else
+ puts
+ end
+ }
+ end
+
+ # Here are some XPath tests that were originally submitted by ...
+ # The code has changed some, but the logic and the source documents are the
+ # same.
+ # This method reads a document from a file, and then a series of xpaths,
+ # also from a file. It then checks each xpath against the source file.
+ def test_more
+ xmlsource = fixture_path("testsrc.xml")
+ xpathtests = fixture_path("xp.tst")
+
+ doc = File.open(xmlsource) {|f| REXML::Document.new(f) }
+ #results = ""
+ results = REXML::Document.new
+ results.add_element "test-results"
+ File.foreach(xpathtests) do |line|
+ line.strip!
+ begin
+ doc.root
+ #puts "#"*80
+ #print "\nDoing #{line} " ; $stdout.flush
+ doc.elements.each(line) do |el|
+ #print "." ; $stdout.flush
+ results.root << el.clone
+ #results << el.to_s
+ end
+ #ObjectSpace.garbage_collect
+ GC::start
+ rescue Exception => z
+ #puts "\n'#{line}' failed"
+ fail("Error on line #{line}:\n#{z.message}\n"+z.backtrace[0,10].join("\n"))
+ #results.root.add_element( "error", {"path"=>line}).text = z.message+"\n"+z.backtrace[0,10].join("\n")
+ #results << "<error path='"+line+"'>"+z.message+"</error>"
+ end
+ end
+ end
+
+ def test_axe_descendant
+ assert_equal "f", XPath::first( @@doc, "descendant::f").name
+ end
+
+ def test_axe_parent
+ q = XPath.first( @@doc, "a/d/c/parent::*/q" )
+ assert_equal 19, q.attributes["id"].to_i
+ end
+
+ def test_abbreviated_attribute
+ assert_equal 'a', XPath::first( @@doc, "a[@id='1']" ).name
+ c = XPath::first( @@doc, "a/b/c[@id='4']" )
+ assert_equal 'c', c.name
+ assert_equal '4', c.attributes['id']
+
+ result = XPath::first( @@doc, "descendant::f[@a='c']")
+ assert_equal "11", result.attributes['id']
+
+ assert_equal "11", XPath::first(@@doc, "a/e/f[@a='c']").attributes["id"]
+ assert_equal "11", XPath::first(@@doc, "a/e/*[@a='c']").attributes["id"]
+ end
+
+ def test_axe_self
+ c = XPath::first( @@doc, "a/b/c" )
+ assert c
+ assert_equal "c", c.name
+ assert_equal "c", XPath::first( c, "self::node()" ).name
+ end
+
+ def test_axe_ancestor
+ doc = REXML::Document.new "
+ <a>
+ <b id='1'>
+ <c>
+ <b id='2'>
+ <d/>
+ </b>
+ </c>
+ </b>
+ </a>"
+
+ d = XPath.first( doc, "//d" )
+ assert_equal "d", d.name
+ b = each_test( d, "ancestor::b" ) { |el|
+ assert((1..2) === el.attributes['id'].to_i,
+ "Expected #{el.attributes['id']} to be either 1 or 2"
+ )
+ }
+ assert_equal 2, b
+ end
+
+ def test_axe_child
+ m = XPath.first( @@doc, "a/child::m" )
+ assert_equal 15, m.attributes['id'].to_i
+ end
+
+ def test_axe_attribute
+ a = XPath.first( @@doc, "a/attribute::id" )
+ assert_equal "1", a.value
+ a = XPath.first( @@doc, "a/e/f[@id='14']/attribute::a" )
+ assert_equal "d", a.value
+ end
+
+ def test_axe_sibling
+ doc = Document.new "<a><b><c/></b><e><f id='10'/><f id='11'/><f id='12'/></e></a>"
+ first_f = XPath.first( doc, "a/e/f" )
+ assert first_f
+ assert_equal '10', first_f.attributes['id']
+ next_f = XPath.first( doc, "a/e/f/following-sibling::node()" )
+ assert_equal '11', next_f.attributes['id']
+
+ b = XPath.first( doc, "a/e/preceding-sibling::node()" )
+ assert_equal 'b', b.name
+ end
+
+ def test_lang
+ doc = File.open(fixture_path("lang0.xml")) {|f| Document.new(f) }
+ #puts IO.read( "test/lang.xml" )
+
+ #puts XPath.match( doc, "//language/*" ).size
+ c = each_test( doc, "//language/*" ) { |element|
+ #puts "#{element.name}: #{element.text}"
+ }
+ assert_equal 4, c
+ end
+
+ def test_namespaces_1
+ source = <<-EOF
+ <foo xmlns:ts="this" xmlns:tt="that">
+ <ts:bar>this bar</ts:bar>
+ <tt:bar>that bar</tt:bar>
+ </foo>
+ EOF
+ doc = Document.new source
+ XPath.each( doc, "//bar" ) {
+ fail "'bar' should match nothing in this case"
+ }
+
+ namespace = {"t"=>"this"}
+ results = XPath.first( doc, "//t:bar", namespace )
+ assert_equal "this bar", results.text
+ end
+
+ def test_namespaces_2
+ source = <<-EOF
+ <foo xmlns:ts="this" xmlns:tt="that">
+ <ts:bar>this bar</ts:bar>
+ <tt:bar>that bar</tt:bar>
+ </foo>
+ EOF
+ doc = Document.new source
+ res = XPath::first(doc, "//*[local_name()='bar']")
+ assert res, "looking for //*[name()='bar']"
+ assert_equal 'this', res.namespace
+ res = XPath::first(doc.root, "*[namespace_uri()='that']")
+ assert_equal 'that bar', res.text
+ end
+
+ def test_complex
+ next_f = XPath.first( @@doc, "a/e/f[@id='11']/following-sibling::*" )
+ assert_equal 12, next_f.attributes['id'].to_i
+ prev_f = XPath.first( @@doc, "a/e/f[@id='11']/preceding-sibling::*" )
+ assert_equal 10, prev_f.attributes['id'].to_i
+ c = each_test( @@doc, "descendant-or-self::*[@x='y']" )
+ assert_equal 2, c
+ end
+
+ def test_grouping
+ t = XPath.first( @@doc, "a/d/*[name()='d' and (name()='f' or name()='q')]" )
+ assert_nil t
+ t = XPath.first( @@doc, "a/d/*[(name()='d' and name()='f') or name()='q']" )
+ assert_equal 'q', t.name
+ end
+
+ def test_preceding
+ d = Document.new "<a><b id='0'/><b id='2'/><b><c id='0'/><c id='1'/><c id='2'/></b><b id='1'/></a>"
+ start = XPath.first( d, "/a/b[@id='1']" )
+ assert_equal 'b', start.name
+ c = XPath.first( start, "preceding::c" )
+ assert_equal '2', c.attributes['id']
+
+ c1, c0 = XPath.match( d, "/a/b/c[@id='2']/preceding::node()" )
+ assert_equal '1', c1.attributes['id']
+ assert_equal '0', c0.attributes['id']
+
+ c2, c1, c0, b, b2, b0 = XPath.match( start, "preceding::node()" )
+
+ assert_equal 'c', c2.name
+ assert_equal 'c', c1.name
+ assert_equal 'c', c0.name
+ assert_equal 'b', b.name
+ assert_equal 'b', b2.name
+ assert_equal 'b', b0.name
+
+ assert_equal '2', c2.attributes['id']
+ assert_equal '1', c1.attributes['id']
+ assert_equal '0', c0.attributes['id']
+ assert b.attributes.empty?
+ assert_equal '2', b2.attributes['id']
+ assert_equal '0', b0.attributes['id']
+
+ d = REXML::Document.new("<a><b/><c/><d/></a>")
+ matches = REXML::XPath.match(d, "/a/d/preceding::node()")
+ assert_equal("c", matches[0].name)
+ assert_equal("b", matches[1].name)
+
+ s = "<a><b><c id='1'/></b><b><b><c id='2'/><c id='3'/></b><c id='4'/></b><c id='NOMATCH'><c id='5'/></c></a>"
+ d = REXML::Document.new(s)
+ c = REXML::XPath.match( d, "//c[@id = '5']")
+ cs = REXML::XPath.match( c, "preceding::c" )
+ assert_equal( 4, cs.length )
+ end
+
+ def test_following
+ d = Document.new "<a><b id='0'/><b/><b><c id='1'/><c id='2'/></b><b id='1'/></a>"
+ start = XPath.first( d, "/a/b[@id='0']" )
+ assert_equal 'b', start.name
+ c = XPath.first( start, "following::c" )
+ assert_equal '1', c.attributes['id']
+
+ s = "<a><b><c><d/></c><e/></b><f><g><h/><i/></g></f><i/></a>"
+ d = Document.new(s)
+ c = XPath.first(d, '/a/b/c')
+ assert_equal 'c', c.name
+ res = XPath.match( c, 'following::*' )
+ assert_equal 6, res.size
+ res = XPath.match( c, 'following::i' )
+ assert_equal 2, res.size
+ end
+
+ # The following three paths were provided by
+ # Jeni Tennison <jeni@jenitennison.com>
+ # a consultant who is also an XSL and XPath expert
+ #def test_child_cubed
+ # els = @@jeni.elements.to_a("*****")
+ # assert_equal 3, els.size
+ #end
+
+ #def test_div_2
+ # results = doc.elements.to_a("/ div 2")
+ #end
+
+ #def test_nested_predicates
+ # puts @@jeni.root.elements[1].elements[1].name
+ # results = @@jeni.root.elements[1].elements[1].elements.to_a("../following-sibling::*[*[name() = name(current())]]")
+ # puts results
+ #end
+
+ # Contributed by Mike Stok
+ def test_starts_with
+ source = <<-EOF
+ <foo>
+ <a href="mailto:a@b.c">a@b.c</a>
+ <a href="http://www.foo.com">http://www.foo.com</a>
+ </foo>
+ EOF
+ doc = Document.new source
+ mailtos = doc.elements.to_a("//a[starts-with(@href, 'mailto:')]")
+ assert_equal 1, mailtos.size
+ assert_equal "mailto:a@b.c", mailtos[0].attributes['href']
+
+ ailtos = doc.elements.to_a("//a[starts-with(@href, 'ailto:')]")
+ assert_equal 0, ailtos.size
+ end
+
+ def test_toms_text_node
+ file = "<a>A<b>B</b><c>C<d>D</d>E</c>F</a>"
+ doc = Document.new(file)
+ assert_equal 'A', XPath.first(doc[0], 'text()').to_s
+ assert_equal 'AF', XPath.match(doc[0], 'text()').collect { |n|
+ n.to_s
+ }.join('')
+ assert_equal 'B', XPath.first(doc[0], 'b/text()').to_s
+ assert_equal 'D', XPath.first(doc[0], '//d/text()').to_s
+ assert_equal 'ABCDEF', XPath.match(doc[0], '//text()').collect {|n|
+ n.to_s
+ }.join('')
+ end
+
+ def test_string_length
+ doc = Document.new <<-EOF
+ <AAA>
+ <Q/>
+ <SSSS/>
+ <BB/>
+ <CCC/>
+ <DDDDDDDD/>
+ <EEEE/>
+ </AAA>
+ EOF
+ assert doc, "create doc"
+
+ set = doc.elements.to_a("//*[string-length(name()) = 3]")
+ assert_equal 2, set.size, "nodes with names length = 3"
+
+ set = doc.elements.to_a("//*[string-length(name()) < 3]")
+ assert_equal 2, set.size, "nodes with names length < 3"
+
+ set = doc.elements.to_a("//*[string-length(name()) > 3]")
+ assert_equal 3, set.size, "nodes with names length > 3"
+ end
+
+ # Test provided by Mike Stok
+ def test_contains
+ source = <<-EOF
+ <foo>
+ <a href="mailto:a@b.c">a@b.c</a>
+ <a href="http://www.foo.com">http://www.foo.com</a>
+ </foo>
+ EOF
+ doc = Document.new source
+
+ [
+ #['o', 2],
+ ['foo', 1], ['bar', 0]].each { |search, expected|
+ set = doc.elements.to_a("//a[contains(@href, '#{search}')]")
+ assert_equal expected, set.size
+ }
+ end
+
+ # Mike Stok and Sean Russell
+ def test_substring
+ # examples from http://www.w3.org/TR/xpath#function-substring
+ doc = Document.new('<test string="12345" />')
+
+ Document.new("<a b='1'/>")
+ #puts XPath.first(d, 'node()[0 + 1]')
+ #d = Document.new("<a b='1'/>")
+ #puts XPath.first(d, 'a[0 mod 0]')
+ [ [1.5, 2.6, '234'],
+ [0, 3, '12'],
+ [0, '0 div 0', ''],
+ [1, '0 div 0', ''],
+ ['-42', '1 div 0', '12345'],
+ ['-1 div 0', '1 div 0', '']
+ ].each { |start, length, expected|
+ set = doc.elements.to_a("//test[substring(@string, #{start}, #{length}) = '#{expected}']")
+ assert_equal 1, set.size, "#{start}, #{length}, '#{expected}'"
+ }
+ end
+
+ def test_translate
+ source = <<-EOF
+ <doc>
+ <case name='w3c one' result='BAr' /> <!-- w3c -->
+ <case name='w3c two' result='AAA' /> <!-- w3c -->
+ <case name='alchemy' result="gold" /> <!-- mike -->
+ <case name='vbxml one' result='A Space Odyssey' />
+ <case name='vbxml two' result='AbCdEf' />
+ </doc>
+ EOF
+
+ doc = Document.new(source)
+
+ [ ['bar', 'abc', 'ABC', 'w3c one'],
+ ['--aaa--','abc-','ABC', 'w3c two'],
+ ['lead', 'dear language', 'doll groover', 'alchemy'],
+ ['A Space Odissei', 'i', 'y', 'vbxml one'],
+ ['abcdefg', 'aceg', 'ACE', 'vbxml two'],
+ ].each { |arg1, arg2, arg3, name|
+ translate = "translate('#{arg1}', '#{arg2}', '#{arg3}')"
+ set = doc.elements.to_a("//case[@result = #{translate}]")
+ assert_equal 1, set.size, translate
+ assert_equal name, set[0].attributes['name']
+ }
+ end
+
+ def test_math
+ d = Document.new( '<a><b/><c/></a>' )
+ assert XPath.first( d.root, 'node()[1]' )
+ assert_equal 'b', XPath.first( d.root, 'node()[1]' ).name
+ assert XPath.first( d.root, 'node()[0 + 1]' )
+ assert_equal 'b', XPath.first( d.root, './node()[0 + 1]' ).name
+ assert XPath.first( d.root, 'node()[1 + 1]' )
+ assert_equal 'c', XPath.first( d.root, './node()[1 + 1]' ).name
+ assert XPath.first( d.root, 'node()[4 div 2]' )
+ assert_equal 'c', XPath.first( d.root, './node()[4 div 2]' ).name
+ assert XPath.first( d.root, 'node()[2 - 1]' )
+ assert_equal 'b', XPath.first( d.root, './node()[2 - 1]' ).name
+ assert XPath.first( d.root, 'node()[5 mod 2]' )
+ assert_equal 'b', XPath.first( d.root, './node()[5 mod 2]' ).name
+ assert XPath.first( d.root, 'node()[8 mod 3]' )
+ assert_equal 'c', XPath.first( d.root, './node()[8 mod 3]' ).name
+ assert XPath.first( d.root, 'node()[1 * 2]' )
+ assert_equal 'c', XPath.first( d.root, './node()[1 * 2]' ).name
+ assert XPath.first( d.root, 'node()[2 + -1]' )
+ assert_equal 'b', XPath.first( d.root, './node()[2 + -1]' ).name
+ end
+
+ def test_name
+ assert_raise( UndefinedNamespaceException, "x should be undefined" ) {
+ REXML::Document.new("<a x='foo'><b/><x:b/></a>")
+ }
+ d = REXML::Document.new("<a xmlns:x='foo'><b/><x:b/></a>")
+ assert_equal 1, d.root.elements.to_a('*[name() = "b"]').size
+ assert_equal 1, d.elements.to_a('//*[name() = "x:b"]').size
+ end
+
+ def test_local_name
+ d = REXML::Document.new("<a xmlns:x='foo'><b/><x:b/></a>")
+ assert_equal 2, d.root.elements.to_a('*[local_name() = "b"]').size
+ assert_equal 2, d.elements.to_a('//*[local_name() = "b"]').size
+ end
+
+ def test_comparisons
+ source = "<a><b id='1'/><b id='2'/><b id='3'/></a>"
+ doc = REXML::Document.new(source)
+
+ # NOTE TO SER: check that number() is required
+ assert_equal 2, REXML::XPath.match(doc, "//b[number(@id) > 1]").size
+ assert_equal 3, REXML::XPath.match(doc, "//b[number(@id) >= 1]").size
+ assert_equal 1, REXML::XPath.match(doc, "//b[number(@id) <= 1]").size
+ assert_equal 1, REXML::XPath.match(doc, "//b[number(@id) = (1 * 1)]").size
+ assert_equal 1, REXML::XPath.match(doc, "//b[number(@id) = (1 mod 2)]").size
+ assert_equal 1, REXML::XPath.match(doc, "//b[number(@id) = (4 div 2)]").size
+ end
+
+ # Contributed by Kouhei
+ def test_substring_before
+ doc = Document.new("<r><a/><b/><c/></r>")
+ assert_equal("a", doc.root.elements.to_a("*[name()=substring-before('abc', 'b')]")[0].name)
+ assert_equal("c", doc.root.elements.to_a("*[name()=substring-after('abc', 'b')]")[0].name)
+ end
+
+ def test_spaces
+ doc = Document.new("<a>
+ <b>
+ <c id='a'/>
+ </b>
+ <c id='b'/>
+ </a>")
+ assert_equal( 1, REXML::XPath.match(doc,
+ "//*[local-name()='c' and @id='b']").size )
+ assert_equal( 1, REXML::XPath.match(doc,
+ "//*[ local-name()='c' and @id='b' ]").size )
+ assert_equal( 1, REXML::XPath.match(doc,
+ "//*[ local-name() = 'c' and @id = 'b' ]").size )
+ assert_equal( 1,
+ REXML::XPath.match(doc, '/a/c[@id]').size )
+ assert_equal( 1,
+ REXML::XPath.match(doc, '/a/c[(@id)]').size )
+ assert_equal( 1,
+ REXML::XPath.match(doc, '/a/c[ @id ]').size )
+ assert_equal( 1,
+ REXML::XPath.match(doc, '/a/c[ (@id) ]').size )
+ assert_equal( 1,
+ REXML::XPath.match(doc, '/a/c[( @id )]').size )
+ assert_equal( 1, REXML::XPath.match(doc.root,
+ '/a/c[ ( @id ) ]').size )
+ assert_equal( 1, REXML::XPath.match(doc,
+ '/a/c [ ( @id ) ] ').size )
+ assert_equal( 1, REXML::XPath.match(doc,
+ ' / a / c [ ( @id ) ] ').size )
+ end
+
+ def test_text_nodes
+ # source = "<root>
+ #<child/>
+ #<child>test</child>
+ #</root>"
+ source = "<root><child>test</child></root>"
+ d = REXML::Document.new( source )
+ r = REXML::XPath.match( d, %q{/root/child[text()="test"]} )
+ assert_equal( 1, r.size )
+ assert_equal( "child", r[0].name )
+ assert_equal( "test", r[0].text )
+ end
+
+ def test_auto_string_value
+ source = "<root><foo/><title>Introduction</title></root>"
+ d = REXML::Document.new( source )
+ #r = REXML::XPath.match( d, %q{/root[title="Introduction"]} )
+ #assert_equal( 1, r.size )
+ source = "<a><b/><c/><c>test</c></a>"
+ d = REXML::Document.new( source )
+ r = REXML::XPath.match( d, %q{/a[c='test']} )
+ assert_equal( 1, r.size )
+ r = REXML::XPath.match( d, %q{a[c='test']} )
+ assert_equal( 1, r.size )
+ r = d.elements["/a[c='test']"]
+ assert_not_nil( r )
+ r = d.elements["a[c='test']"]
+ assert_not_nil( r )
+ r = d.elements["a[c='xtest']"]
+ assert_nil( r )
+ r = REXML::XPath.match( d, %q{a[c='xtest']} )
+ assert_equal( 0, r.size )
+ end
+
+ def test_ordering
+ source = "<a><b><c id='1'/><c id='2'/></b><b><d id='1'/><d id='2'/></b></a>"
+ d = REXML::Document.new( source )
+ r = REXML::XPath.match( d, %q{/a/*/*[1]} )
+ assert_equal( 1, r.size )
+ r.each { |el| assert_equal( '1', el.attribute('id').value ) }
+ end
+
+ def test_descendant_or_self_ordering
+ source = "<a>
+ <b>
+ <c id='1'/>
+ <c id='2'/>
+ </b>
+ <b>
+ <d id='1'>
+ <c id='3'/>
+ </d>
+ <d id='2'>
+ <e>
+ <c id='4'/>
+ </e>
+ </d>
+ </b>
+ </a>"
+ d = REXML::Document.new( source )
+ cs = XPath.match( d, "/descendant-or-self::c" )
+ assert_equal( 4, cs.length )
+ 1.upto(4) {|x| assert_equal( x.to_s, cs[x-1].attributes['id'] ) }
+ end
+
+ def test_and
+ d = Document.new %q{<doc><route run='*' title='HNO'
+ destination='debian_production1' date='*' edition='*'
+ source='debian_satellite1'/></doc>}
+ assert_equal( nil, d.root.elements["route[@run='0']"] )
+ assert_equal( nil, d.root.elements["route[@run='0' and @title='HNO']"] )
+ end
+
+
+ def test_numbers
+ d = Document.new %q{<a x="0" y="*" z="4e" w="e4" v="a"/>}
+
+ xp1 = "/a[ @x = 0 ]"
+ xp2 = "/a[ @x = '0' ]"
+ xp3 = "/a[ (@x + 1) = 1 ]"
+ xp4 = "/a[ @y = 0 ]"
+ xp5 = "/a[ (@z + 1) = 5 ]"
+ xp6 = "/a[ (@w + 1) = 5 ]"
+ xp7 = "/a[ (@v + 1) = 1 ]"
+ xp8 = "/a[ @n = 0 ]"
+
+ assert_equal( 1, XPath.match( d, xp1 ).length )
+ assert_equal( 1, XPath.match( d, xp2 ).length )
+ assert_equal( 1, XPath.match( d, xp3 ).length )
+ assert_equal( 0, XPath.match( d, xp4 ).length )
+ assert_equal( 0, XPath.match( d, xp5 ).length )
+ assert_equal( 0, XPath.match( d, xp6 ).length )
+ assert_equal( 0, XPath.match( d, xp7 ).length )
+ assert_equal( 0, XPath.match( d, xp8 ).length )
+ end
+
+ def test_tobis_preceding
+ doc_string = '<a>
+ <b/>
+ <c>
+ <d/>
+ <e/>
+ </c>
+</a>'
+
+ doc = Document.new(doc_string)
+
+ # e = REXML::XPath.first(doc,'/a/c/e')
+ e = doc.root.get_elements('/a/c/e')[0]
+ assert_equal( 1, e.get_elements('preceding-sibling::*').length )
+ assert_equal( 2, XPath.match(e, 'preceding::*').length )
+ end
+
+
+ def test_filtering
+ #doc=Document.new("<a><b><c1/><c2/></b><b><c3/><c4/></b><b><c5/><c6/></b></a>")
+ #assert_equal( 3, XPath.match( doc, '/a/b/*[1]' ).length )
+ #assert_equal( 2, XPath.match( doc, '/a/b/following-sibling::*[1]' ).length )
+ end
+
+ # Submitted by Alex
+ def test_union
+ data = %Q{<div id="the_div">
+ <span id="the_span">
+ <strong id="the_strong">a</strong>
+ </span>
+ <em id="the_em2">b</em>
+</div>}
+ rd = REXML::Document.new( data )
+ #union = rd.get_elements("/div/span | /div/em")
+ #assert_equal(2, union.length, "/div/span | /div/em" )
+ union = rd.get_elements('//*[name()="em" or name()="strong"]')
+ assert_equal(2, union.length, 'name() and "or" failed')
+ union = rd.get_elements('//em|//strong')
+ assert_equal(2, union.length,
+ 'Both tag types are returned by XPath union operator')
+ end
+
+
+ def test_union2
+ src = <<-EOL
+<div id="the_div">
+<span id="the_span">
+<strong id="the_strong">a</strong>
+</span>
+<em id="the_em2">b</em>
+</div>
+ EOL
+ rd = REXML::Document.new( src )
+ union = rd.get_elements('//em|//strong')
+ assert_equal(2, union.length,
+ 'Both tag types are returned by XPath union operator')
+ end
+
+
+ def test_a_star_star_one
+ string = <<-EOL
+<a>
+ <b>
+ <c1/>
+ <d/>
+ <e/>
+ <f/>
+ </b>
+ <b>
+ <c2/>
+ <d/>
+ <e/>
+ <f/>
+ </b>
+</a>
+ EOL
+ d = REXML::Document.new( string )
+ c1 = XPath.match( d, '/a/*/*[1]' )
+ assert_equal( 1, c1.length )
+ assert_equal( 'c1', c1[0].name )
+ end
+
+ def test_sum
+ d = Document.new("<a>"+
+ "<b>1</b><b>2</b><b>3</b>"+
+ "<c><d>1</d><d>2</d></c>"+
+ "<e att='1'/><e att='2'/>"+
+ "</a>")
+
+ for v,p in [[6, "sum(/a/b)"],
+ [9, "sum(//b | //d)"],
+ [3, "sum(/a/e/@*)"] ]
+ assert_equal( v, XPath::match( d, p ).first )
+ end
+ end
+
+ def test_xpath_namespace
+ d = REXML::Document.new("<tag1 xmlns='ns1'><tag2 xmlns='ns2'/><tada>xa</tada></tag1>")
+ x = d.root
+ num = 0
+ x.each_element('tada') { num += 1 }
+ assert_equal(1, num)
+ end
+
+ def test_ticket_39
+ doc = REXML::Document.new( <<-EOL )
+ <rss>
+ <channel>
+ <!-- removing the namespace declaration makes the test pass -->
+ <convertLineBreaks xmlns="http://www.blogger.com/atom/ns#">true</convertLineBreaks>
+ <item>
+ <title>Item 1</title>
+ </item>
+ <item>
+ <title>Item 2</title>
+ <pubDate>Thu, 13 Oct 2005 19:59:00 +0000</pubDate>
+ </item>
+ <item>
+ <title>Item 3</title>
+ </item>
+ </channel>
+ </rss>
+ EOL
+ root_node = XPath.first(doc, "rss")
+ assert_not_nil root_node
+ channel_node = XPath.first(root_node, "channel")
+ assert_not_nil channel_node
+ items = XPath.match(channel_node, "*")
+ assert_equal 4, items.size
+ items = XPath.match(channel_node, "item")
+ assert_equal 3, items.size # fails
+ end
+
+
+ def test_ticket_42
+ source = "<a></a>"
+ doc = Document.new(source)
+ bElem = Element.new('b')
+ doc.root.add_element(bElem)
+ doc.elements.each('//b[name(..) = "a"]') { |x|
+ assert_equal x,bElem
+ }
+ end
+
+ def test_ticket_56
+ namespaces = {'h' => 'http://www.w3.org/1999/xhtml'}
+
+ finaldoc = REXML::Document.new(File.read(fixture_path('google.2.xml')))
+
+ column_headers = []
+
+ REXML::XPath.each(finaldoc, '//h:form[@action="ModifyCampaign"]//h:th',
+ namespaces) do |el|
+ node = REXML::XPath.first(el, 'h:a/text()', namespaces)
+ column_headers << (node ? node.value : nil)
+ end
+ column_headers.map! { |h| h.to_s.strip.chomp }
+ expected = ["", "", "Current Status", "Current Budget",
+ "Clicks", "Impr.", "CTR", "Avg. CPC", "Cost", "Conv. Rate",
+ "Cost/Conv."]
+ assert_equal( expected, column_headers )
+ end
+
+
+ def test_ticket_70
+ string = <<EOF
+
+<mydoc>
+
+ <someelement attribute="1.10">Text1, text,
+text</someelement>
+
+ <someelement attribute="1.11">Text2, text,
+text</someelement>
+
+
+</mydoc>
+
+EOF
+ doc = Document.new string
+ assert_equal( 1, XPath.match( doc, "//someelement[contains(@attribute,'1.10')]" ).length )
+ end
+
+ def test_ticket_43
+ #url = http://news.search.yahoo.com/news/rss?p=market&ei=UTF-8&fl=0&x=wrt
+
+ sum = File.open(fixture_path("yahoo.xml")) do |f|
+ Document.new(f).elements.to_a("//item").size
+ end
+ assert_equal( 10, sum )
+
+ text = File.open(fixture_path("yahoo.xml")) do |f|
+ Document.new(f).elements.to_a(%Q{//title[contains(text(), "'")]}).collect{|e| e.text}.join
+ end
+ assert_equal( "Broward labor market's a solid performer (Miami Herald)", text )
+ end
+
+ def test_ticket_57
+ data = "<?xml version='1.0'?><a:x xmlns:a='1'><a:y p='p' q='q'><a:z>zzz</a:z></a:y></a:x>"
+
+ r = Document.new(data)
+
+ assert_equal(Text, REXML::XPath.first(r,"a:x/a:y[@p='p' and @q='q']/a:z/text()").class)
+ assert_equal("zzz", REXML::XPath.first(r,"a:x/a:y[@p='p' and @q='q']/a:z/text()").to_s)
+ end
+
+ def test_ticket_59
+ data = "<a>
+ <c id='1'/>
+ <c id='2'/>
+ <b>
+ <c id='3'/>
+ </b>
+ <c id='4'/>
+ <b>
+ <b>
+ <c id='5'/>
+ </b>
+ <c id='6'/>
+ </b>
+ <c id='7'/>
+ <b>
+ <b>
+ <c id='8'/>
+ <b>
+ <c id='9'/>
+ <b>
+ <c id='10'/>
+ </b>
+ <c id='11'/>
+ </b>
+ </b>
+ </b>
+ <c id='12'/>
+ </a>"
+ d = Document.new(data)
+ res = d.elements.to_a( "//c" ).collect {|e| e.attributes['id'].to_i}
+ assert_equal( res, res.sort )
+ end
+
+ def ticket_61_fixture(doc, xpath)
+ matches = []
+ doc.elements.each(xpath) do |element|
+ matches << element
+ assert_equal('Add', element.text)
+ assert_equal('ButtonText', element.attributes['class'])
+ end
+ assert_equal(1, matches.length)
+ end
+
+ def test_ticket_61_text
+ doc = File.open(fixture_path("ticket_61.xml")) do |file|
+ REXML::Document.new file
+ end
+ ticket_61_fixture( doc, "//div[text()='Add' and @class='ButtonText']" )
+ end
+
+ def test_ticket_61_contains
+ doc = File.open(fixture_path("ticket_61.xml")) do |file|
+ REXML::Document.new file
+ end
+ ticket_61_fixture( doc, "//div[contains(.,'Add') and @class='ButtonText']" )
+ end
+
+ def test_namespaces_0
+ d = Document.new(%q{<x:a xmlns:x="y"/>})
+ assert_equal( 1, XPath.match( d, "//x:a" ).size )
+ assert_equal( 1, XPath.match( d, "//x:*" ).size )
+ end
+
+ def test_ticket_71
+ doc = Document.new(%Q{<root xmlns:ns1="xyz" xmlns:ns2="123"><element ns1:attrname="foo" ns2:attrname="bar"/></root>})
+ el = doc.root.elements[1]
+ assert_equal( "element", el.name )
+ el2 = XPath.first( doc.root, "element[@ns:attrname='foo']", { 'ns' => "xyz" } )
+ assert_equal( el, el2 )
+ end
+
+ def test_ticket_78
+ doc = <<-EOT
+ <root>
+ <element>
+ <tag x='1'>123</tag>
+ </element>
+ <element>
+ <tag x='2'>123a</tag>
+ </element>
+ </root>
+ EOT
+ seq = %w{BEGIN 123 END BEGIN 123a END}
+
+ xmlDoc = Document.new(doc)
+
+ ["//element[tag='123']/tag", "//element[tag='123a']/tag"].each do |query|
+ assert_equal( "BEGIN", seq.shift )
+ XPath.each(xmlDoc, query) { |element|
+ assert_equal( seq.shift, element.text )
+ }
+ assert_equal( "END", seq.shift )
+ end
+ end
+
+ def test_ticket_79
+ source = "<a><b><c>test</c></b><b><c>3</c></b></a>"
+ d = REXML::Document.new( source )
+ r = REXML::XPath.match( d, %q{/a/b[c='test']} )
+ assert_equal(1, r.size())
+ r = REXML::XPath.match( d, %q{/a/b[c='3']} )
+ assert_equal(1, r.size())
+ end
+
+ def test_or_and
+ doc = "
+<html>
+ <head>
+ <title>test</title>
+ </head>
+ <body>
+ <p>
+ A <a rel=\"sub\" href=\"/\">link</a>.
+ </p>
+ </body>
+</html>
+"
+
+ xmldoc = REXML::Document.new(doc)
+ xpath = "descendant::node()[(local-name()='link' or local-name()='a') and @rel='sub']"
+ hrefs = []
+ xmldoc.elements.each(xpath) do |element|
+ hrefs << element.attributes["href"]
+ end
+ assert_equal(["/"], hrefs, "Bug #3842 [ruby-core:32447]")
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/xpath/test_node.rb b/jni/ruby/test/rexml/xpath/test_node.rb
new file mode 100644
index 0000000..aec2de1
--- /dev/null
+++ b/jni/ruby/test/rexml/xpath/test_node.rb
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+
+require_relative "../rexml_test_utils"
+
+require "rexml/document"
+
+module REXMLTests
+ class TestXPathNode < Test::Unit::TestCase
+ def matches(xml, xpath)
+ document = REXML::Document.new(xml)
+ REXML::XPath.each(document, xpath).collect(&:to_s)
+ end
+
+ class TestQName < self
+ def test_ascii
+ xml = <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+ <ascii>
+ <child>child</child>
+ </ascii>
+</root>
+ XML
+ assert_equal(["<child>child</child>"],
+ matches(xml, "/root/ascii/child"))
+ end
+
+ def test_non_ascii
+ xml = <<-XML
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+ <non-àscii>
+ <child>child</child>
+ </non-àscii>
+</root>
+ XML
+ assert_equal(["<child>child</child>"],
+ matches(xml, "/root/non-àscii/child"))
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/xpath/test_predicate.rb b/jni/ruby/test/rexml/xpath/test_predicate.rb
new file mode 100644
index 0000000..287e131
--- /dev/null
+++ b/jni/ruby/test/rexml/xpath/test_predicate.rb
@@ -0,0 +1,82 @@
+require "test/unit/testcase"
+require "rexml/document"
+require "rexml/xpath"
+require "rexml/parsers/xpathparser"
+
+module REXMLTests
+ class TestXPathPredicate < Test::Unit::TestCase
+ include REXML
+ SRC=<<-EOL
+ <article>
+ <section role="subdivision" id="1">
+ <para>free flowing text.</para>
+ </section>
+ <section role="division">
+ <section role="subdivision" id="2">
+ <para>free flowing text.</para>
+ </section>
+ <section role="division">
+ <para>free flowing text.</para>
+ </section>
+ </section>
+ </article>
+ EOL
+
+ def setup
+ @doc = REXML::Document.new( SRC )
+ @parser = REXML::Parsers::XPathParser.new
+
+ end
+
+ def test_predicates_parent
+ path = '//section[../self::section[@role="division"]]'
+ m = do_path( path )
+ assert_equal( 2, m.size )
+ assert_equal( "2", m[0].attributes["id"] )
+ assert_nil( m[1].attributes["id"] )
+ end
+
+ def test_predicates_single
+ path = '//section[@role="subdivision" and not(../self::section[@role="division"])]'
+ m = do_path( path )
+ assert_equal( 1, m.size )
+ assert_equal( "1", m[0].attributes["id"] )
+ end
+
+ def test_predicates_multi
+ path = '//section[@role="subdivision"][not(../self::section[@role="division"])]'
+ m = do_path( path )
+ assert_equal( 1, m.size )
+ assert_equal( "1", m[0].attributes["id"] )
+ end
+
+ def do_path( path )
+ m = REXML::XPath.match( @doc, path )
+ #puts path, @parser.parse( path ).inspect
+ return m
+ end
+
+ def test_get_no_siblings_terminal_nodes
+ source = <<-XML
+<a>
+ <b number='1' str='abc'>TEXT1</b>
+ <c number='1'/>
+ <c number='2' str='def'>
+ <b number='3'/>
+ <d number='1' str='abc'>TEXT2</d>
+ <b number='2'><!--COMMENT--></b>
+ </c>
+</a>
+XML
+ doc = REXML::Document.new(source)
+ predicate = "count(child::node()|" +
+ "following-sibling::node()|" +
+ "preceding-sibling::node())=0"
+ m = REXML::XPath.match(doc, "/descendant-or-self::node()[#{predicate}]")
+ assert_equal( [REXML::Text.new("TEXT1"),
+ REXML::Text.new("TEXT2"),
+ REXML::Comment.new("COMMENT")],
+ m )
+ end
+ end
+end
diff --git a/jni/ruby/test/rexml/xpath/test_text.rb b/jni/ruby/test/rexml/xpath/test_text.rb
new file mode 100644
index 0000000..46aba3c
--- /dev/null
+++ b/jni/ruby/test/rexml/xpath/test_text.rb
@@ -0,0 +1,74 @@
+require 'test/unit'
+require 'rexml/document'
+require 'rexml/element'
+require 'rexml/xpath'
+
+module REXMLTests
+ class TestXPathText < Test::Unit::TestCase
+ def setup
+ @doc = REXML::Document.new
+ end
+
+ def tear_down
+ end
+
+ def test_text_as_element
+ node1 = REXML::Element.new('a', @doc)
+ node2 = REXML::Element.new('b', node1)
+ REXML::Text.new('test', false, node2)
+ assert_equal(1, @doc.elements.size, "doc owns 1 element node1")
+ assert_same(node1, @doc.elements[1], "doc owns 1 element node1")
+ assert_equal(1, node1.elements.size, "node1 owns 1 element node2")
+ assert_same(node2, node1.elements[1], "node1 owns 1 element node2")
+ assert_equal(1, node2.size, "node2 owns 1 text element")
+ end
+
+ def test_text_in_xpath_query
+ node1 = REXML::Element.new('a', @doc)
+ node2 = REXML::Element.new('b', node1)
+ textnode = REXML::Text.new('test', false, node2)
+ textnode.parent = node2 # should be unnecessary
+ nodes = @doc.get_elements('//b')
+ assert_equal(1, nodes.size, "document has one element")
+ # why doesn't this query work right?
+ nodes = REXML::XPath.match(@doc, '//text()')
+ assert_equal(1, nodes.size, "//text() should yield one Text element")
+ assert_equal(REXML::Text, nodes[0].class)
+ end
+
+ def test_comment_in_xpath_query
+ node1 = REXML::Element.new('a', @doc)
+ node2 = REXML::Element.new('b', node1)
+ commentnode = REXML::Comment.new('test', node2)
+ nodes = REXML::XPath.match(@doc, '//comment()')
+ assert_equal(1, nodes.size, "//comment() should yield one Comment element")
+ assert_same commentnode, nodes[0]
+ end
+
+ def test_parentage
+ node1 = REXML::Element.new('a', @doc)
+ assert_same(@doc, node1.parent, "node1 parent is document")
+ node2 = REXML::Element.new('b', node1)
+ assert_same(node1, node2.parent, "node2 parent is node1")
+ textnode = REXML::Text.new('test', false, node2)
+ # why isn't the text's parent node2?
+ # Also look at Comment, etc.
+ assert_same(node2, textnode.parent)
+ comment = REXML::Comment.new('Test comment', node2)
+ assert_same(node2, comment.parent)
+ end
+
+ def test_ancestors
+ node1 = REXML::Element.new('a', @doc)
+ node2 = REXML::Element.new('b', node1)
+ textnode = REXML::Text.new('test', false, node2)
+ #textnode.parent = node2 # should be unnecessary
+ assert_same node2, textnode.parent
+ nodes = @doc.get_elements('//b/ancestor::*')
+ assert_equal(1, nodes.size, "<b> has one element ancestor")
+ nodes = @doc.get_elements('//b/ancestor::node()')
+ assert_equal(2, nodes.size, "<b> has two node ancestors")
+ assert nodes[1].kind_of?(REXML::Document)
+ end
+ end
+end
diff --git a/jni/ruby/test/rinda/test_rinda.rb b/jni/ruby/test/rinda/test_rinda.rb
new file mode 100644
index 0000000..812b437
--- /dev/null
+++ b/jni/ruby/test/rinda/test_rinda.rb
@@ -0,0 +1,810 @@
+require 'test/unit'
+
+require 'drb/drb'
+require 'drb/eq'
+require 'rinda/ring'
+require 'rinda/tuplespace'
+require 'timeout'
+require 'singleton'
+
+module Rinda
+
+class MockClock
+ include Singleton
+
+ class MyTS < Rinda::TupleSpace
+ def keeper_thread
+ nil
+ end
+ end
+
+ def initialize
+ @now = 2
+ @reso = 1
+ @ts = MyTS.new
+ @ts.write([2, :now])
+ @inf = 2**31 - 1
+ end
+
+ def now
+ @now.to_f
+ end
+
+ def at(n)
+ n
+ end
+
+ def _forward(n=nil)
+ now ,= @ts.take([nil, :now])
+ @now = now + n
+ @ts.write([@now, :now])
+ end
+
+ def forward(n)
+ while n > 0
+ _forward(@reso)
+ n -= @reso
+ Thread.pass
+ end
+ end
+
+ def rewind
+ @ts.take([nil, :now])
+ @ts.write([@inf, :now])
+ @ts.take([nil, :now])
+ @now = 2
+ @ts.write([2, :now])
+ end
+
+ def sleep(n=nil)
+ now ,= @ts.read([nil, :now])
+ @ts.read([(now + n)..@inf, :now])
+ 0
+ end
+end
+
+module Time
+ def sleep(n)
+ @m.sleep(n)
+ end
+ module_function :sleep
+
+ def at(n)
+ n
+ end
+ module_function :at
+
+ def now
+ defined?(@m) && @m ? @m.now : 2
+ end
+ module_function :now
+
+ def rewind
+ @m.rewind
+ end
+ module_function :rewind
+
+ def forward(n)
+ @m.forward(n)
+ end
+ module_function :forward
+
+ @m = MockClock.instance
+end
+
+class TupleSpace
+ def sleep(n)
+ Kernel.sleep(n * 0.01)
+ end
+end
+
+module TupleSpaceTestModule
+ def sleep(n)
+ if Thread.current == Thread.main
+ Time.forward(n)
+ else
+ Time.sleep(n)
+ end
+ end
+
+ def thread_join(th)
+ while th.alive?
+ Kernel.sleep(0.1)
+ sleep(1)
+ end
+ th.value
+ end
+
+ def test_00_tuple
+ tuple = Rinda::TupleEntry.new([1,2,3])
+ assert(!tuple.canceled?)
+ assert(!tuple.expired?)
+ assert(tuple.alive?)
+ end
+
+ def test_00_template
+ tmpl = Rinda::Template.new([1,2,3])
+ assert_equal(3, tmpl.size)
+ assert_equal(3, tmpl[2])
+ assert(tmpl.match([1,2,3]))
+ assert(!tmpl.match([1,nil,3]))
+
+ tmpl = Rinda::Template.new([/^rinda/i, nil, :hello])
+ assert_equal(3, tmpl.size)
+ assert(tmpl.match(['Rinda', 2, :hello]))
+ assert(!tmpl.match(['Rinda', 2, Symbol]))
+ assert(!tmpl.match([1, 2, :hello]))
+ assert(tmpl.match([/^rinda/i, 2, :hello]))
+
+ tmpl = Rinda::Template.new([Symbol])
+ assert_equal(1, tmpl.size)
+ assert(tmpl.match([:hello]))
+ assert(tmpl.match([Symbol]))
+ assert(!tmpl.match(['Symbol']))
+
+ tmpl = Rinda::Template.new({"message"=>String, "name"=>String})
+ assert_equal(2, tmpl.size)
+ assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
+ assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
+ assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
+ assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
+
+ assert_raise(Rinda::InvalidHashTupleKey) do
+ Rinda::Template.new({:message=>String, "name"=>String})
+ end
+ tmpl = Rinda::Template.new({"name"=>String})
+ assert_equal(1, tmpl.size)
+ assert(tmpl.match({"name"=>"Foo"}))
+ assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
+ assert(!tmpl.match({"message"=>:symbol, "name"=>"Foo", "1"=>2}))
+ assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
+ assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
+
+ tmpl = Rinda::Template.new({"message"=>String, "name"=>String})
+ assert_equal(2, tmpl.size)
+ assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
+ assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
+ assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
+ assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
+
+ tmpl = Rinda::Template.new({"message"=>String})
+ assert_equal(1, tmpl.size)
+ assert(tmpl.match({"message"=>"Hello"}))
+ assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
+ assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
+ assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
+ assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
+
+ tmpl = Rinda::Template.new({"message"=>String, "name"=>nil})
+ assert_equal(2, tmpl.size)
+ assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
+ assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
+ assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
+ assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
+
+ assert_raise(Rinda::InvalidHashTupleKey) do
+ @ts.write({:message=>String, "name"=>String})
+ end
+
+ @ts.write([1, 2, 3])
+ assert_equal([1, 2, 3], @ts.take([1, 2, 3]))
+
+ @ts.write({'1'=>1, '2'=>2, '3'=>3})
+ assert_equal({'1'=>1, '2'=>2, '3'=>3}, @ts.take({'1'=>1, '2'=>2, '3'=>3}))
+
+ entry = @ts.write(['1'=>1, '2'=>2, '3'=>3])
+ assert_raise(Rinda::RequestExpiredError) do
+ assert_equal({'1'=>1, '2'=>2, '3'=>3}, @ts.read({'1'=>1}, 0))
+ end
+ entry.cancel
+ end
+
+ def test_00_DRbObject
+ ro = DRbObject.new(nil, "druby://host:1234")
+ tmpl = Rinda::DRbObjectTemplate.new
+ assert(tmpl === ro)
+
+ tmpl = Rinda::DRbObjectTemplate.new("druby://host:1234")
+ assert(tmpl === ro)
+
+ tmpl = Rinda::DRbObjectTemplate.new("druby://host:12345")
+ assert(!(tmpl === ro))
+
+ tmpl = Rinda::DRbObjectTemplate.new(/^druby:\/\/host:/)
+ assert(tmpl === ro)
+
+ ro = DRbObject.new_with(12345, 1234)
+ assert(!(tmpl === ro))
+
+ ro = DRbObject.new_with("druby://foo:12345", 1234)
+ assert(!(tmpl === ro))
+
+ tmpl = Rinda::DRbObjectTemplate.new(/^druby:\/\/(foo|bar):/)
+ assert(tmpl === ro)
+
+ ro = DRbObject.new_with("druby://bar:12345", 1234)
+ assert(tmpl === ro)
+
+ ro = DRbObject.new_with("druby://baz:12345", 1234)
+ assert(!(tmpl === ro))
+ end
+
+ def test_inp_rdp
+ assert_raise(Rinda::RequestExpiredError) do
+ @ts.take([:empty], 0)
+ end
+
+ assert_raise(Rinda::RequestExpiredError) do
+ @ts.read([:empty], 0)
+ end
+ end
+
+ def test_ruby_talk_264062
+ th = Thread.new { @ts.take([:empty], 1) }
+ sleep(10)
+ assert_raise(Rinda::RequestExpiredError) do
+ thread_join(th)
+ end
+
+ th = Thread.new { @ts.read([:empty], 1) }
+ sleep(10)
+ assert_raise(Rinda::RequestExpiredError) do
+ thread_join(th)
+ end
+ end
+
+ def test_symbol_tuple
+ @ts.write([:symbol, :symbol])
+ @ts.write(['string', :string])
+ assert_equal([[:symbol, :symbol]], @ts.read_all([:symbol, nil]))
+ assert_equal([[:symbol, :symbol]], @ts.read_all([Symbol, nil]))
+ assert_equal([], @ts.read_all([:nil, nil]))
+ end
+
+ def test_core_01
+ 5.times do
+ @ts.write([:req, 2])
+ end
+
+ assert_equal([[:req, 2], [:req, 2], [:req, 2], [:req, 2], [:req, 2]],
+ @ts.read_all([nil, nil]))
+
+ taker = Thread.new(5) do |count|
+ s = 0
+ count.times do
+ tuple = @ts.take([:req, Integer])
+ assert_equal(2, tuple[1])
+ s += tuple[1]
+ end
+ @ts.write([:ans, s])
+ s
+ end
+
+ assert_equal(10, thread_join(taker))
+ assert_equal([:ans, 10], @ts.take([:ans, 10]))
+ assert_equal([], @ts.read_all([nil, nil]))
+ end
+
+ def test_core_02
+ taker = Thread.new(5) do |count|
+ s = 0
+ count.times do
+ tuple = @ts.take([:req, Integer])
+ assert_equal(2, tuple[1])
+ s += tuple[1]
+ end
+ @ts.write([:ans, s])
+ s
+ end
+
+ 5.times do
+ @ts.write([:req, 2])
+ end
+
+ assert_equal(10, thread_join(taker))
+ assert_equal([:ans, 10], @ts.take([:ans, 10]))
+ assert_equal([], @ts.read_all([nil, nil]))
+ end
+
+ def test_core_03_notify
+ notify1 = @ts.notify(nil, [:req, Integer])
+ notify2 = @ts.notify(nil, {"message"=>String, "name"=>String})
+
+ 5.times do
+ @ts.write([:req, 2])
+ end
+
+ 5.times do
+ tuple = @ts.take([:req, Integer])
+ assert_equal(2, tuple[1])
+ end
+
+ 5.times do
+ assert_equal(['write', [:req, 2]], notify1.pop)
+ end
+ 5.times do
+ assert_equal(['take', [:req, 2]], notify1.pop)
+ end
+
+ @ts.write({"message"=>"first", "name"=>"3"})
+ @ts.write({"message"=>"second", "name"=>"1"})
+ @ts.write({"message"=>"third", "name"=>"0"})
+ @ts.take({"message"=>"third", "name"=>"0"})
+ @ts.take({"message"=>"first", "name"=>"3"})
+
+ assert_equal(["write", {"message"=>"first", "name"=>"3"}], notify2.pop)
+ assert_equal(["write", {"message"=>"second", "name"=>"1"}], notify2.pop)
+ assert_equal(["write", {"message"=>"third", "name"=>"0"}], notify2.pop)
+ assert_equal(["take", {"message"=>"third", "name"=>"0"}], notify2.pop)
+ assert_equal(["take", {"message"=>"first", "name"=>"3"}], notify2.pop)
+ end
+
+ def test_cancel_01
+ entry = @ts.write([:removeme, 1])
+ assert_equal([[:removeme, 1]], @ts.read_all([nil, nil]))
+ entry.cancel
+ assert_equal([], @ts.read_all([nil, nil]))
+
+ template = nil
+ taker = Thread.new do
+ @ts.take([:take, nil], 10) do |t|
+ template = t
+ Thread.new do
+ template.cancel
+ end
+ end
+ end
+
+ sleep(2)
+
+ assert_raise(Rinda::RequestCanceledError) do
+ assert_nil(thread_join(taker))
+ end
+
+ assert(template.canceled?)
+
+ @ts.write([:take, 1])
+
+ assert_equal([[:take, 1]], @ts.read_all([nil, nil]))
+ end
+
+ def test_cancel_02
+ entry = @ts.write([:removeme, 1])
+ assert_equal([[:removeme, 1]], @ts.read_all([nil, nil]))
+ entry.cancel
+ assert_equal([], @ts.read_all([nil, nil]))
+
+ template = nil
+ reader = Thread.new do
+ @ts.read([:take, nil], 10) do |t|
+ template = t
+ Thread.new do
+ template.cancel
+ end
+ end
+ end
+
+ sleep(2)
+
+ assert_raise(Rinda::RequestCanceledError) do
+ assert_nil(thread_join(reader))
+ end
+
+ assert(template.canceled?)
+
+ @ts.write([:take, 1])
+
+ assert_equal([[:take, 1]], @ts.read_all([nil, nil]))
+ end
+
+ class SimpleRenewer
+ def initialize(sec, n = 1)
+ @sec = sec
+ @n = n
+ end
+
+ def renew
+ return -1 if @n <= 0
+ @n -= 1
+ return @sec
+ end
+ end
+
+ def test_00_renewer
+ tuple = Rinda::TupleEntry.new([1,2,3], true)
+ assert(!tuple.canceled?)
+ assert(tuple.expired?)
+ assert(!tuple.alive?)
+
+ tuple = Rinda::TupleEntry.new([1,2,3], 1)
+ assert(!tuple.canceled?)
+ assert(!tuple.expired?)
+ assert(tuple.alive?)
+ sleep(2)
+ assert(tuple.expired?)
+ assert(!tuple.alive?)
+
+ @renewer = SimpleRenewer.new(1,2)
+ tuple = Rinda::TupleEntry.new([1,2,3], @renewer)
+ assert(!tuple.canceled?)
+ assert(!tuple.expired?)
+ assert(tuple.alive?)
+ sleep(1)
+ assert(!tuple.canceled?)
+ assert(!tuple.expired?)
+ assert(tuple.alive?)
+ sleep(2)
+ assert(tuple.expired?)
+ assert(!tuple.alive?)
+ end
+end
+
+class TupleSpaceTest < Test::Unit::TestCase
+ include TupleSpaceTestModule
+
+ def setup
+ ThreadGroup.new.add(Thread.current)
+ @ts = Rinda::TupleSpace.new(1)
+ end
+ def teardown
+ # implementation-dependent
+ @ts.instance_eval{
+ if th = @keeper
+ th.kill
+ th.join
+ end
+ }
+ end
+end
+
+class TupleSpaceProxyTest < Test::Unit::TestCase
+ include TupleSpaceTestModule
+
+ def setup
+ ThreadGroup.new.add(Thread.current)
+ @ts_base = Rinda::TupleSpace.new(1)
+ @ts = Rinda::TupleSpaceProxy.new(@ts_base)
+ end
+ def teardown
+ # implementation-dependent
+ @ts_base.instance_eval{
+ if th = @keeper
+ th.kill
+ th.join
+ end
+ }
+ end
+
+ def test_remote_array_and_hash
+ # Don't remove ary/hsh local variables.
+ # These are necessary to protect objects from GC.
+ ary = [1, 2, 3]
+ @ts.write(DRbObject.new(ary))
+ assert_equal([1, 2, 3], @ts.take([1, 2, 3], 0))
+ hsh = {'head' => 1, 'tail' => 2}
+ @ts.write(DRbObject.new(hsh))
+ assert_equal({'head' => 1, 'tail' => 2},
+ @ts.take({'head' => 1, 'tail' => 2}, 0))
+ end
+
+ def test_take_bug_8215
+ service = DRb.start_service(nil, @ts_base)
+
+ uri = service.uri
+
+ args = [EnvUtil.rubybin, *%W[-rdrb/drb -rdrb/eq -rrinda/ring -rrinda/tuplespace -e]]
+
+ take = spawn(*args, <<-'end;', uri)
+ uri = ARGV[0]
+ DRb.start_service
+ ro = DRbObject.new_with_uri(uri)
+ ts = Rinda::TupleSpaceProxy.new(ro)
+ th = Thread.new do
+ ts.take([:test_take, nil])
+ end
+ Kernel.sleep(0.1)
+ th.raise(Interrupt) # causes loss of the taken tuple
+ ts.write([:barrier, :continue])
+ Kernel.sleep
+ end;
+
+ @ts_base.take([:barrier, :continue])
+
+ write = spawn(*args, <<-'end;', uri)
+ uri = ARGV[0]
+ DRb.start_service
+ ro = DRbObject.new_with_uri(uri)
+ ts = Rinda::TupleSpaceProxy.new(ro)
+ ts.write([:test_take, 42])
+ end;
+
+ status = Process.wait(write)
+
+ assert_equal([[:test_take, 42]], @ts_base.read_all([:test_take, nil]),
+ '[bug:8215] tuple lost')
+ ensure
+ service.stop_service if service
+ signal = /mswin|mingw/ =~ RUBY_PLATFORM ? "KILL" : "TERM"
+ Process.kill(signal, write) if write && status.nil?
+ Process.kill(signal, take) if take
+ Process.wait(write) if write && status.nil?
+ Process.wait(take) if take
+ end
+
+ @server = DRb.primary_server || DRb.start_service
+end
+
+module RingIPv6
+ def prepare_ipv6(r)
+ begin
+ Socket.getifaddrs.each do |ifaddr|
+ next unless ifaddr.addr
+ next unless ifaddr.addr.ipv6_linklocal?
+ next if ifaddr.name[0, 2] == "lo"
+ r.multicast_interface = ifaddr.ifindex
+ return ifaddr
+ end
+ rescue NotImplementedError
+ # ifindex() function may not be implemented on Windows.
+ return if
+ Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
+ end
+ skip 'IPv6 not available'
+ end
+end
+
+class TestRingServer < Test::Unit::TestCase
+
+ def setup
+ @port = Rinda::Ring_PORT
+
+ @ts = Rinda::TupleSpace.new
+ @rs = Rinda::RingServer.new(@ts, [], @port)
+ end
+ def teardown
+ # implementation-dependent
+ @ts.instance_eval{
+ if th = @keeper
+ th.kill
+ th.join
+ end
+ }
+ @rs.shutdown
+ end
+
+ def test_do_reply
+ with_timeout(10) {_test_do_reply}
+ end
+
+ def _test_do_reply
+ called = nil
+
+ callback = proc { |ts|
+ called = ts
+ }
+
+ callback = DRb::DRbObject.new callback
+
+ @ts.write [:lookup_ring, callback]
+
+ @rs.do_reply
+
+ wait_for(10) {called}
+
+ assert_same @ts, called
+ end
+
+ def test_do_reply_local
+ with_timeout(10) {_test_do_reply_local}
+ end
+
+ def _test_do_reply_local
+ called = nil
+
+ callback = proc { |ts|
+ called = ts
+ }
+
+ @ts.write [:lookup_ring, callback]
+
+ @rs.do_reply
+
+ wait_for(10) {called}
+
+ assert_same @ts, called
+ end
+
+ def test_make_socket_unicast
+ v4 = @rs.make_socket('127.0.0.1')
+
+ assert_equal('127.0.0.1', v4.local_address.ip_address)
+ assert_equal(@port, v4.local_address.ip_port)
+ end
+
+ def test_make_socket_ipv4_multicast
+ v4mc = @rs.make_socket('239.0.0.1')
+
+ if Socket.const_defined?(:SO_REUSEPORT) then
+ assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
+ else
+ assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
+ end
+
+ assert_equal('0.0.0.0', v4mc.local_address.ip_address)
+ assert_equal(@port, v4mc.local_address.ip_port)
+ end
+
+ def test_make_socket_ipv6_multicast
+ skip 'IPv6 not available' unless
+ Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
+
+ begin
+ v6mc = @rs.make_socket('ff02::1')
+ rescue Errno::EADDRNOTAVAIL
+ return # IPv6 address for multicast not available
+ end
+
+ if Socket.const_defined?(:SO_REUSEPORT) then
+ assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
+ else
+ assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
+ end
+
+ assert_equal('::1', v6mc.local_address.ip_address)
+ assert_equal(@port, v6mc.local_address.ip_port)
+ end
+
+ def test_ring_server_ipv4_multicast
+ @rs.shutdown
+ @rs = Rinda::RingServer.new(@ts, [['239.0.0.1', '0.0.0.0']], @port)
+ v4mc = @rs.instance_variable_get('@sockets').first
+
+ if Socket.const_defined?(:SO_REUSEPORT) then
+ assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
+ else
+ assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
+ end
+
+ assert_equal('0.0.0.0', v4mc.local_address.ip_address)
+ assert_equal(@port, v4mc.local_address.ip_port)
+ end
+
+ def test_ring_server_ipv6_multicast
+ skip 'IPv6 not available' unless
+ Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
+
+ @rs.shutdown
+ begin
+ @rs = Rinda::RingServer.new(@ts, [['ff02::1', '::1', 0]], @port)
+ rescue Errno::EADDRNOTAVAIL
+ return # IPv6 address for multicast not available
+ end
+
+ v6mc = @rs.instance_variable_get('@sockets').first
+
+ if Socket.const_defined?(:SO_REUSEPORT) then
+ assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
+ else
+ assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
+ end
+
+ assert_equal('::1', v6mc.local_address.ip_address)
+ assert_equal(@port, v6mc.local_address.ip_port)
+ end
+
+ def test_shutdown
+ @rs.shutdown
+
+ assert_nil(@rs.do_reply, 'otherwise should hang forever')
+ end
+
+ private
+
+ def with_timeout(n)
+ aoe = Thread.abort_on_exception
+ Thread.abort_on_exception = true
+ tl0 = Thread.list
+ tl = nil
+ th = Thread.new(Thread.current) do |mth|
+ sleep n
+ (tl = Thread.list - tl0).each {|t|t.raise(Timeout::Error)}
+ mth.raise(Timeout::Error)
+ end
+ tl0 << th
+ rescue Timeout::Error => e
+ if tl
+ bt = e.backtrace
+ tl.each do |t|
+ begin
+ t.value
+ rescue Timeout::Error => e
+ bt.unshift("")
+ bt[0, 0] = e.backtrace
+ end
+ end
+ end
+ raise Timeout::Error, "timeout", bt
+ ensure
+ if th
+ th.kill
+ th.join
+ end
+ Thread.abort_on_exception = aoe
+ end
+
+ def wait_for(n)
+ t = n + Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
+ until yield
+ if t < Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
+ flunk "timeout during waiting call"
+ end
+ sleep 0.1
+ end
+ end
+end
+
+class TestRingFinger < Test::Unit::TestCase
+ include RingIPv6
+
+ def setup
+ @rf = Rinda::RingFinger.new
+ end
+
+ def test_make_socket_unicast
+ v4 = @rf.make_socket('127.0.0.1')
+
+ assert(v4.getsockopt(:SOL_SOCKET, :SO_BROADCAST).bool)
+ ensure
+ v4.close if v4
+ end
+
+ def test_make_socket_ipv4_multicast
+ v4mc = @rf.make_socket('239.0.0.1')
+
+ assert_equal(1, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_LOOP).ipv4_multicast_loop)
+ assert_equal(1, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_TTL).ipv4_multicast_ttl)
+ ensure
+ v4mc.close if v4mc
+ end
+
+ def test_make_socket_ipv6_multicast
+ ifaddr = prepare_ipv6(@rf)
+ begin
+ v6mc = @rf.make_socket("ff02::1")
+ rescue Errno::EINVAL
+ # somehow Debian 6.0.7 needs ifname
+ v6mc = @rf.make_socket("ff02::1%#{ifaddr.name}")
+ end
+
+ assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_LOOP).int)
+ assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
+ ensure
+ v6mc.close if v6mc
+ end
+
+ def test_make_socket_ipv4_multicast_hops
+ @rf.multicast_hops = 2
+ v4mc = @rf.make_socket('239.0.0.1')
+ assert_equal(2, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_TTL).ipv4_multicast_ttl)
+ ensure
+ v4mc.close if v4mc
+ end
+
+ def test_make_socket_ipv6_multicast_hops
+ ifaddr = prepare_ipv6(@rf)
+ @rf.multicast_hops = 2
+ begin
+ v6mc = @rf.make_socket("ff02::1")
+ rescue Errno::EINVAL
+ # somehow Debian 6.0.7 needs ifname
+ v6mc = @rf.make_socket("ff02::1%#{ifaddr.name}")
+ end
+ assert_equal(2, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
+ ensure
+ v6mc.close if v6mc
+ end
+
+end
+
+end
+
diff --git a/jni/ruby/test/rinda/test_tuplebag.rb b/jni/ruby/test/rinda/test_tuplebag.rb
new file mode 100644
index 0000000..3e30127
--- /dev/null
+++ b/jni/ruby/test/rinda/test_tuplebag.rb
@@ -0,0 +1,172 @@
+require 'test/unit'
+require 'rinda/tuplespace'
+
+class TestTupleBag < Test::Unit::TestCase
+
+ def setup
+ @tb = Rinda::TupleBag.new
+ end
+
+ def test_delete
+ assert_nothing_raised do
+ val = @tb.delete tup(:val, 1)
+ assert_equal nil, val
+ end
+
+ t = tup(:val, 1)
+ @tb.push t
+
+ val = @tb.delete t
+
+ assert_equal t, val
+
+ assert_equal [], @tb.find_all(tem(:val, 1))
+
+ t1 = tup(:val, 1)
+ t2 = tup(:val, 1)
+ @tb.push t1
+ @tb.push t2
+
+ val = @tb.delete t1
+
+ assert_equal t1, val
+
+ assert_equal [t2], @tb.find_all(tem(:val, 1))
+ end
+
+ def test_delete_unless_alive
+ assert_equal [], @tb.delete_unless_alive
+
+ t1 = tup(:val, nil)
+ t2 = tup(:val, nil)
+
+ @tb.push t1
+ @tb.push t2
+
+ assert_equal [], @tb.delete_unless_alive
+
+ t1.cancel
+
+ assert_equal [t1], @tb.delete_unless_alive, 'canceled'
+
+ t2.renew Object.new
+
+ assert_equal [t2], @tb.delete_unless_alive, 'expired'
+ end
+
+ def test_find
+ template = tem(:val, nil)
+
+ assert_equal nil, @tb.find(template)
+
+ t1 = tup(:other, 1)
+ @tb.push t1
+
+ assert_equal nil, @tb.find(template)
+
+ t2 = tup(:val, 1)
+ @tb.push t2
+
+ assert_equal t2, @tb.find(template)
+
+ t2.cancel
+
+ assert_equal nil, @tb.find(template), 'canceled'
+
+ t3 = tup(:val, 3)
+ @tb.push t3
+
+ assert_equal t3, @tb.find(template)
+
+ t3.renew Object.new
+
+ assert_equal nil, @tb.find(template), 'expired'
+ end
+
+ def test_find_all
+ template = tem(:val, nil)
+
+ t1 = tup(:other, 1)
+ @tb.push t1
+
+ assert_equal [], @tb.find_all(template)
+
+ t2 = tup(:val, 2)
+ t3 = tup(:val, 3)
+
+ @tb.push t2
+ @tb.push t3
+
+ assert_equal [t2, t3], @tb.find_all(template)
+
+ t2.cancel
+
+ assert_equal [t3], @tb.find_all(template), 'canceled'
+
+ t3.renew Object.new
+
+ assert_equal [], @tb.find_all(template), 'expired'
+ end
+
+ def test_find_all_template
+ tuple = tup(:val, 1)
+
+ t1 = tem(:other, nil)
+ @tb.push t1
+
+ assert_equal [], @tb.find_all_template(tuple)
+
+ t2 = tem(:val, nil)
+ t3 = tem(:val, nil)
+
+ @tb.push t2
+ @tb.push t3
+
+ assert_equal [t2, t3], @tb.find_all_template(tuple)
+
+ t2.cancel
+
+ assert_equal [t3], @tb.find_all_template(tuple), 'canceled'
+
+ t3.renew Object.new
+
+ assert_equal [], @tb.find_all_template(tuple), 'expired'
+ end
+
+ def test_has_expires_eh
+ assert !@tb.has_expires?
+
+ t = tup(:val, 1)
+ @tb.push t
+
+ assert @tb.has_expires?
+
+ t.renew Object.new
+
+ assert !@tb.has_expires?
+ end
+
+ def test_push
+ t = tup(:val, 1)
+
+ @tb.push t
+
+ assert_equal t, @tb.find(tem(:val, 1))
+ end
+
+ ##
+ # Create a tuple with +ary+ for its contents
+
+ def tup(*ary)
+ Rinda::TupleEntry.new ary
+ end
+
+ ##
+ # Create a template with +ary+ for its contents
+
+ def tem(*ary)
+ Rinda::TemplateEntry.new ary
+ end
+
+end
+
diff --git a/jni/ruby/test/ripper/dummyparser.rb b/jni/ruby/test/ripper/dummyparser.rb
new file mode 100644
index 0000000..35c08b5
--- /dev/null
+++ b/jni/ruby/test/ripper/dummyparser.rb
@@ -0,0 +1,220 @@
+#
+# dummyparser.rb
+#
+
+require 'ripper'
+
+class Node
+ def initialize(name, *nodes)
+ @name = name
+ @children = nodes
+ end
+
+ attr_reader :name, :children
+
+ def to_s
+ "#{@name}(#{Node.trim_nil(@children).map {|n| n.to_s }.join(',')})"
+ end
+
+ def self.trim_nil(list)
+ if !list.empty? and list.last.nil?
+ list = list[0...-1]
+ list.pop while !list.empty? and list.last.nil?
+ end
+ list
+ end
+end
+
+class NodeList
+ def initialize(list = [])
+ @list = list
+ end
+
+ attr_reader :list
+
+ def push(item)
+ @list.push item
+ self
+ end
+
+ def prepend(items)
+ @list.unshift items
+ end
+
+ def to_s
+ "[#{@list.join(',')}]"
+ end
+end
+
+class DummyParser < Ripper
+ def hook(*names)
+ class << self; self; end.class_eval do
+ names.each do |name|
+ define_method(name) do |*a, &b|
+ result = super(*a, &b)
+ yield(name, *a)
+ result
+ end
+ end
+ end
+ self
+ end
+
+ def on_program(stmts)
+ stmts
+ end
+
+ def on_stmts_new
+ NodeList.new
+ end
+
+ def on_stmts_add(stmts, st)
+ stmts.push st
+ stmts
+ end
+
+ def on_void_stmt
+ Node.new('void')
+ end
+
+ def on_var_ref(name)
+ Node.new('ref', name)
+ end
+
+ def on_var_alias(a, b)
+ Node.new('valias', a, b)
+ end
+
+ def on_alias_error(a)
+ Node.new('aliaserr', a)
+ end
+
+ def on_arg_paren(args)
+ args
+ end
+
+ def on_args_new
+ NodeList.new
+ end
+
+ def on_args_add(list, arg)
+ list.push(arg)
+ end
+
+ def on_args_add_block(list, blk)
+ if blk
+ list.push('&' + blk.to_s)
+ else
+ list
+ end
+ end
+
+ def on_args_add_star(list, arg)
+ list.push('*' + arg.to_s)
+ end
+
+ def on_args_prepend(list, args)
+ list.prepend args
+ list
+ end
+
+ def on_method_add_arg(m, arg)
+ if arg == nil
+ arg = on_args_new
+ end
+ m.children.push arg
+ m
+ end
+
+ def on_method_add_block(m, b)
+ on_args_add_block(m.children, b)
+ m
+ end
+
+ def on_paren(params)
+ params
+ end
+
+ def on_brace_block(params, code)
+ Node.new('block', params, code)
+ end
+
+ def on_block_var(params, shadow)
+ params
+ end
+
+ def on_rest_param(var)
+ "*#{var}"
+ end
+
+ def on_blockarg(var)
+ "&#{var}"
+ end
+
+ def on_params(required, optional, rest, more, keyword, keyword_rest, block)
+ args = NodeList.new
+
+ required.each do |req|
+ args.push(req)
+ end if required
+
+ optional.each do |var, val|
+ args.push("#{var}=#{val}")
+ end if optional
+
+ args.push(rest) if rest
+
+ more.each do |m|
+ args.push(m)
+ end if more
+
+ args.push(block) if block
+ args
+ end
+
+ def on_assoc_new(a, b)
+ Node.new('assoc', a, b)
+ end
+
+ def on_bare_assoc_hash(assoc_list)
+ Node.new('assocs', *assoc_list)
+ end
+
+ def on_assoclist_from_args(a)
+ Node.new('assocs', *a)
+ end
+
+ def on_word_new
+ ""
+ end
+
+ def on_word_add(word, w)
+ word << w
+ end
+
+ def on_words_new
+ NodeList.new
+ end
+
+ def on_words_add(words, word)
+ words.push word
+ end
+
+ def on_qwords_new
+ NodeList.new
+ end
+
+ def on_qwords_add(words, word)
+ words.push word
+ end
+
+ def on_rescue(exc, *rest)
+ Node.new('rescue', (exc && NodeList.new(exc)), *rest)
+ end
+
+ (Ripper::PARSER_EVENTS.map(&:to_s) - instance_methods(false).map {|n|n.to_s.sub(/^on_/, '')}).each do |event|
+ define_method(:"on_#{event}") do |*args|
+ Node.new(event, *args)
+ end
+ end
+end
diff --git a/jni/ruby/test/ripper/test_files.rb b/jni/ruby/test/ripper/test_files.rb
new file mode 100644
index 0000000..5541a09
--- /dev/null
+++ b/jni/ruby/test/ripper/test_files.rb
@@ -0,0 +1,23 @@
+require 'test/unit'
+
+module TestRipper; end
+class TestRipper::Generic < Test::Unit::TestCase
+ def test_parse_files
+ srcdir = File.expand_path("../../..", __FILE__)
+ assert_separately(%W[--disable-gem -rripper - #{srcdir}],
+ __FILE__, __LINE__, <<-'eom', timeout: Float::INFINITY)
+ TEST_RATIO = 0.05 # testing all files needs too long time...
+ class Parser < Ripper
+ PARSER_EVENTS.each {|n| eval "def on_#{n}(*args) r = [:#{n}, *args]; r.inspect; Object.new end" }
+ SCANNER_EVENTS.each {|n| eval "def on_#{n}(*args) r = [:#{n}, *args]; r.inspect; Object.new end" }
+ end
+ dir = ARGV.shift
+ for script in Dir["#{dir}/{lib,sample,ext,test}/**/*.rb"].sort
+ next if TEST_RATIO < rand
+ assert_nothing_raised("ripper failed to parse: #{script.inspect}") {
+ Parser.new(File.read(script), script).parse
+ }
+ end
+ eom
+ end
+end
diff --git a/jni/ruby/test/ripper/test_filter.rb b/jni/ruby/test/ripper/test_filter.rb
new file mode 100644
index 0000000..005a5d8
--- /dev/null
+++ b/jni/ruby/test/ripper/test_filter.rb
@@ -0,0 +1,83 @@
+begin
+ require 'ripper'
+ require 'test/unit'
+ ripper_test = true
+ module TestRipper; end
+rescue LoadError
+end
+
+class TestRipper::Filter < Test::Unit::TestCase
+
+ class Filter < Ripper::Filter
+ def on_default(event, token, data)
+ if data.empty?
+ data[:filename] = filename rescue nil
+ data[:lineno] = lineno
+ data[:column] = column
+ data[:token] = token
+ end
+ data
+ end
+ end
+
+ def filename
+ File.expand_path(__FILE__)
+ end
+
+ def test_filter_filename_unset
+ data = {}
+ filter = Filter.new(File.read(filename))
+ filter.parse(data)
+ assert_equal('-', data[:filename], "[ruby-dev:37856]")
+ assert_equal('-', filter.filename)
+ end
+
+ def test_filter_filename
+ data = {}
+ filter = Filter.new(File.read(filename), filename)
+ assert_equal(filename, filter.filename)
+ filter.parse(data)
+ assert_equal(filename, data[:filename])
+ assert_equal(filename, filter.filename)
+ end
+
+ def test_filter_lineno
+ data = {}
+ src = File.read(filename)
+ src_lines = src.count("\n")
+ filter = Filter.new(src)
+ assert_equal(nil, filter.lineno)
+ filter.parse(data)
+ assert_equal(1, data[:lineno])
+ assert_equal(src_lines, filter.lineno)
+ end
+
+ def test_filter_lineno_set
+ data = {}
+ src = File.read(filename)
+ src_lines = src.count("\n")
+ filter = Filter.new(src, '-', 100)
+ assert_equal(nil, filter.lineno)
+ filter.parse(data)
+ assert_equal(100, data[:lineno])
+ assert_equal(src_lines+100-1, filter.lineno)
+ end
+
+ def test_filter_column
+ data = {}
+ src = File.read(filename)
+ last_columns = src[/(.*)\Z/].size
+ filter = Filter.new(src)
+ assert_equal(nil, filter.column)
+ filter.parse(data)
+ assert_equal(0, data[:column])
+ assert_equal(last_columns, filter.column)
+ end
+
+ def test_filter_token
+ data = {}
+ filter = Filter.new(File.read(filename))
+ filter.parse(data)
+ assert_equal("begin", data[:token])
+ end
+end if ripper_test
diff --git a/jni/ruby/test/ripper/test_parser_events.rb b/jni/ruby/test/ripper/test_parser_events.rb
new file mode 100644
index 0000000..08296a2
--- /dev/null
+++ b/jni/ruby/test/ripper/test_parser_events.rb
@@ -0,0 +1,1232 @@
+begin
+ require_relative 'dummyparser'
+ require 'test/unit'
+ ripper_test = true
+ module TestRipper; end
+rescue LoadError
+end
+
+class TestRipper::ParserEvents < Test::Unit::TestCase
+
+ def test_event_coverage
+ dispatched = Ripper::PARSER_EVENTS
+ tested = self.class.instance_methods(false).grep(/\Atest_(\w+)/) {$1.intern}
+ assert_empty dispatched-tested
+ end
+
+ def parse(str, nm = nil, &bl)
+ dp = DummyParser.new(str)
+ dp.hook(*nm, &bl) if nm
+ dp.parse.to_s
+ end
+
+ def compile_error(str)
+ parse(str, :compile_error) {|e, msg| return msg}
+ end
+
+ def test_program
+ thru_program = false
+ assert_equal '[void()]', parse('', :on_program) {thru_program = true}
+ assert_equal true, thru_program
+ end
+
+ def test_stmts_new
+ assert_equal '[void()]', parse('')
+ end
+
+ def test_stmts_add
+ assert_equal '[ref(nil)]', parse('nil')
+ assert_equal '[ref(nil),ref(nil)]', parse('nil;nil')
+ assert_equal '[ref(nil),ref(nil),ref(nil)]', parse('nil;nil;nil')
+ end
+
+ def test_void_stmt
+ assert_equal '[void()]', parse('')
+ assert_equal '[void()]', parse('; ;')
+ end
+
+ def test_var_ref
+ assert_equal '[assign(var_field(a),ref(a))]', parse('a=a')
+ assert_equal '[ref(nil)]', parse('nil')
+ assert_equal '[ref(true)]', parse('true')
+ end
+
+ def test_vcall
+ assert_equal '[vcall(a)]', parse('a')
+ end
+
+ def test_BEGIN
+ assert_equal '[BEGIN([void()])]', parse('BEGIN{}')
+ assert_equal '[BEGIN([ref(nil)])]', parse('BEGIN{nil}')
+ end
+
+ def test_END
+ assert_equal '[END([void()])]', parse('END{}')
+ assert_equal '[END([ref(nil)])]', parse('END{nil}')
+ end
+
+ def test_alias
+ assert_equal '[alias(symbol_literal(a),symbol_literal(b))]', parse('alias a b')
+ end
+
+ def test_var_alias
+ assert_equal '[valias($a,$g)]', parse('alias $a $g')
+ end
+
+ def test_alias_error
+ assert_equal '[aliaserr(valias($a,$1))]', parse('alias $a $1')
+ end
+
+ def test_arglist
+ assert_equal '[fcall(m,[])]', parse('m()')
+ assert_equal '[fcall(m,[1])]', parse('m(1)')
+ assert_equal '[fcall(m,[1,2])]', parse('m(1,2)')
+ assert_equal '[fcall(m,[*vcall(r)])]', parse('m(*r)')
+ assert_equal '[fcall(m,[1,*vcall(r)])]', parse('m(1,*r)')
+ assert_equal '[fcall(m,[1,2,*vcall(r)])]', parse('m(1,2,*r)')
+ assert_equal '[fcall(m,[&vcall(r)])]', parse('m(&r)')
+ assert_equal '[fcall(m,[1,&vcall(r)])]', parse('m(1,&r)')
+ assert_equal '[fcall(m,[1,2,&vcall(r)])]', parse('m(1,2,&r)')
+ assert_equal '[fcall(m,[*vcall(a),&vcall(b)])]', parse('m(*a,&b)')
+ assert_equal '[fcall(m,[1,*vcall(a),&vcall(b)])]', parse('m(1,*a,&b)')
+ assert_equal '[fcall(m,[1,2,*vcall(a),&vcall(b)])]', parse('m(1,2,*a,&b)')
+ end
+
+ def test_args_add
+ thru_args_add = false
+ parse('m(a)', :on_args_add) {thru_args_add = true}
+ assert_equal true, thru_args_add
+ end
+
+ def test_args_add_block
+ thru_args_add_block = false
+ parse('m(&b)', :on_args_add_block) {thru_args_add_block = true}
+ assert_equal true, thru_args_add_block
+ end
+
+ def test_args_add_star
+ thru_args_add_star = false
+ parse('m(*a)', :on_args_add_star) {thru_args_add_star = true}
+ assert_equal true, thru_args_add_star
+ thru_args_add_star = false
+ parse('m(*a, &b)', :on_args_add_star) {thru_args_add_star = true}
+ assert_equal true, thru_args_add_star
+ end
+
+ def test_args_new
+ thru_args_new = false
+ parse('m()', :on_args_new) {thru_args_new = true}
+ assert_equal true, thru_args_new
+ end
+
+ def test_arg_paren
+ # FIXME
+ end
+
+ def test_aref
+ assert_equal '[aref(vcall(v),[1])]', parse('v[1]')
+ assert_equal '[aref(vcall(v),[1,2])]', parse('v[1,2]')
+ end
+
+ def test_assoclist_from_args
+ thru_assoclist_from_args = false
+ parse('{a=>b}', :on_assoclist_from_args) {thru_assoclist_from_args = true}
+ assert_equal true, thru_assoclist_from_args
+ end
+
+ def test_assocs
+ assert_equal '[fcall(m,[assocs(assoc(1,2))])]', parse('m(1=>2)')
+ assert_equal '[fcall(m,[assocs(assoc(1,2),assoc(3,4))])]', parse('m(1=>2,3=>4)')
+ assert_equal '[fcall(m,[3,assocs(assoc(1,2))])]', parse('m(3,1=>2)')
+ end
+
+ def test_assoc_new
+ thru_assoc_new = false
+ parse('{a=>b}', :on_assoc_new) {thru_assoc_new = true}
+ assert_equal true, thru_assoc_new
+ end
+
+ def test_assoc_splat
+ thru_assoc_splat = false
+ parse('m(**h)', :on_assoc_splat) {thru_assoc_splat = true}
+ assert_equal true, thru_assoc_splat
+ end
+
+ def test_aref_field
+ assert_equal '[assign(aref_field(vcall(a),[1]),2)]', parse('a[1]=2')
+ end
+
+ def test_arg_ambiguous
+ thru_arg_ambiguous = false
+ parse('m //', :on_arg_ambiguous) {thru_arg_ambiguous = true}
+ assert_equal true, thru_arg_ambiguous
+ end
+
+ def test_operator_ambiguous
+ thru_operator_ambiguous = false
+ parse('a=1; a %[]', :on_operator_ambiguous) {thru_operator_ambiguous = true}
+ assert_equal true, thru_operator_ambiguous
+ end
+
+ def test_array # array literal
+ assert_equal '[array([1,2,3])]', parse('[1,2,3]')
+ assert_equal '[array([abc,def])]', parse('%w[abc def]')
+ assert_equal '[array([abc,def])]', parse('%W[abc def]')
+ end
+
+ def test_assign # generic assignment
+ assert_equal '[assign(var_field(v),1)]', parse('v=1')
+ end
+
+ def test_assign_error
+ # for test_coverage
+ end
+
+ def test_assign_error_backref
+ thru_assign_error = false
+ parse('$` = 1', :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ thru_assign_error = false
+ parse('$`, _ = 1', :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ end
+
+ def test_assign_error_const_qualified
+ thru_assign_error = false
+ parse('self::X = 1', :on_assign_error) {thru_assign_error = true}
+ assert_equal false, thru_assign_error
+ parse("def m\n self::X = 1\nend", :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ thru_assign_error = false
+ parse("def m\n self::X, a = 1, 2\nend", :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ end
+
+ def test_assign_error_const
+ thru_assign_error = false
+ parse('X = 1', :on_assign_error) {thru_assign_error = true}
+ assert_equal false, thru_assign_error
+ parse("def m\n X = 1\nend", :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ thru_assign_error = false
+ parse("def m\n X, a = 1, 2\nend", :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ end
+
+ def test_assign_error_const_toplevel
+ thru_assign_error = false
+ parse('::X = 1', :on_assign_error) {thru_assign_error = true}
+ assert_equal false, thru_assign_error
+ parse("def m\n ::X = 1\nend", :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ thru_assign_error = false
+ parse("def m\n ::X, a = 1, 2\nend", :on_assign_error) {thru_assign_error = true}
+ assert_equal true, thru_assign_error
+ end
+
+ def test_bare_assoc_hash
+ thru_bare_assoc_hash = false
+ parse('x[a=>b]', :on_bare_assoc_hash) {thru_bare_assoc_hash = true}
+ assert_equal true, thru_bare_assoc_hash
+ thru_bare_assoc_hash = false
+ parse('x[1, a=>b]', :on_bare_assoc_hash) {thru_bare_assoc_hash = true}
+ assert_equal true, thru_bare_assoc_hash
+ thru_bare_assoc_hash = false
+ parse('x(a=>b)', :on_bare_assoc_hash) {thru_bare_assoc_hash = true}
+ assert_equal true, thru_bare_assoc_hash
+ thru_bare_assoc_hash = false
+ parse('x(1, a=>b)', :on_bare_assoc_hash) {thru_bare_assoc_hash = true}
+ assert_equal true, thru_bare_assoc_hash
+ end
+
+ def test_begin
+ thru_begin = false
+ parse('begin end', :on_begin) {thru_begin = true}
+ assert_equal true, thru_begin
+ end
+
+ %w"and or + - * / % ** | ^ & <=> > >= < <= == === != =~ !~ << >> && ||".each do |op|
+ define_method("test_binary(#{op})") do
+ thru_binary = false
+ parse("a #{op} b", :on_binary) {thru_binary = true}
+ assert_equal true, thru_binary
+ end
+ end
+
+ def test_blockarg
+ thru_blockarg = false
+ parse("def a(&b) end", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+ thru_blockarg = false
+ parse("def a(x, &b) end", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+
+ thru_blockarg = false
+ parse("proc{|&b|}", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+ thru_blockarg = false
+ parse("proc{|x, &b|}", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+ thru_blockarg = false
+ parse("proc{|&b;y|}", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+ thru_blockarg = false
+ parse("proc{|&b,x;y|}", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+
+ thru_blockarg = false
+ parse("proc do |&b| end", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+ thru_blockarg = false
+ parse("proc do |&b, x| end", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+ thru_blockarg = false
+ parse("proc do |&b;y| end", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+ thru_blockarg = false
+ parse("proc do |&b, x;y| end", :on_blockarg) {thru_blockarg = true}
+ assert_equal true, thru_blockarg
+ end
+
+ def test_block_var
+ thru_block_var = false
+ parse("proc{||}", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ thru_block_var = false
+ parse("proc{| |}", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ thru_block_var = false
+ parse("proc{|x|}", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ thru_block_var = false
+ parse("proc{|;y|}", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ thru_block_var = false
+ parse("proc{|x;y|}", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+
+ thru_block_var = false
+ parse("proc do || end", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ thru_block_var = false
+ parse("proc do | | end", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ thru_block_var = false
+ parse("proc do |x| end", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ thru_block_var = false
+ parse("proc do |;y| end", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ thru_block_var = false
+ parse("proc do |x;y| end", :on_block_var) {thru_block_var = true}
+ assert_equal true, thru_block_var
+ end
+
+ def test_block_var_add_block
+ # not used
+ end
+
+ def test_block_var_add_star
+ # not used
+ end
+
+ def test_bodystmt
+ thru_bodystmt = false
+ parse("class X\nend", :on_bodystmt) {thru_bodystmt = true}
+ assert_equal true, thru_bodystmt
+ end
+
+ def test_call
+ bug2233 = '[ruby-core:26165]'
+ tree = nil
+
+ thru_call = false
+ assert_nothing_raised {
+ tree = parse("self.foo", :on_call) {thru_call = true}
+ }
+ assert_equal true, thru_call
+ assert_equal "[call(ref(self),.,foo)]", tree
+ thru_call = false
+ assert_nothing_raised(bug2233) {
+ tree = parse("foo.()", :on_call) {thru_call = true}
+ }
+ assert_equal true, thru_call
+ assert_equal "[call(vcall(foo),.,call,[])]", tree
+ end
+
+ def test_excessed_comma
+ thru_excessed_comma = false
+ parse("proc{|x,|}", :on_excessed_comma) {thru_excessed_comma = true}
+ assert_equal true, thru_excessed_comma
+ thru_excessed_comma = false
+ parse("proc{|x,y,|}", :on_excessed_comma) {thru_excessed_comma = true}
+ assert_equal true, thru_excessed_comma
+
+ thru_excessed_comma = false
+ parse("proc do |x,| end", :on_excessed_comma) {thru_excessed_comma = true}
+ assert_equal true, thru_excessed_comma
+ thru_excessed_comma = false
+ parse("proc do |x,y,| end", :on_excessed_comma) {thru_excessed_comma = true}
+ assert_equal true, thru_excessed_comma
+ end
+
+ def test_heredoc
+ bug1921 = '[ruby-core:24855]'
+ thru_heredoc_beg = false
+ tree = parse("<""<EOS\nheredoc\nEOS\n", :on_heredoc_beg) {thru_heredoc_beg = true}
+ assert_equal true, thru_heredoc_beg
+ assert_match(/string_content\(\),heredoc\n/, tree, bug1921)
+ heredoc = nil
+ parse("<""<EOS\nheredoc1\nheredoc2\nEOS\n", :on_string_add) {|e, n, s| heredoc = s}
+ assert_equal("heredoc1\nheredoc2\n", heredoc, bug1921)
+ heredoc = nil
+ parse("<""<-EOS\nheredoc1\nheredoc2\n\tEOS\n", :on_string_add) {|e, n, s| heredoc = s}
+ assert_equal("heredoc1\nheredoc2\n", heredoc, bug1921)
+ end
+
+ def test_massign
+ thru_massign = false
+ parse("a, b = 1, 2", :on_massign) {thru_massign = true}
+ assert_equal true, thru_massign
+ end
+
+ def test_mlhs_add
+ thru_mlhs_add = false
+ parse("a, b = 1, 2", :on_mlhs_add) {thru_mlhs_add = true}
+ assert_equal true, thru_mlhs_add
+ end
+
+ def test_mlhs_add_star
+ bug2232 = '[ruby-core:26163]'
+ bug4364 = '[ruby-core:35078]'
+
+ thru_mlhs_add_star = false
+ tree = parse("a, *b = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_match(/mlhs_add_star\(mlhs_add\(mlhs_new\(\),a\),b\)/, tree)
+ thru_mlhs_add_star = false
+ tree = parse("a, *b, c = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_match(/mlhs_add\(mlhs_add_star\(mlhs_add\(mlhs_new\(\),a\),b\),mlhs_add\(mlhs_new\(\),c\)\)/, tree, bug2232)
+ thru_mlhs_add_star = false
+ tree = parse("a, *, c = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_match(/mlhs_add\(mlhs_add_star\(mlhs_add\(mlhs_new\(\),a\)\),mlhs_add\(mlhs_new\(\),c\)\)/, tree, bug4364)
+ thru_mlhs_add_star = false
+ tree = parse("*b, c = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_match(/mlhs_add\(mlhs_add_star\(mlhs_new\(\),b\),mlhs_add\(mlhs_new\(\),c\)\)/, tree, bug4364)
+ thru_mlhs_add_star = false
+ tree = parse("*, c = 1, 2", :on_mlhs_add_star) {thru_mlhs_add_star = true}
+ assert_equal true, thru_mlhs_add_star
+ assert_match(/mlhs_add\(mlhs_add_star\(mlhs_new\(\)\),mlhs_add\(mlhs_new\(\),c\)\)/, tree, bug4364)
+ end
+
+ def test_mlhs_new
+ thru_mlhs_new = false
+ parse("a, b = 1, 2", :on_mlhs_new) {thru_mlhs_new = true}
+ assert_equal true, thru_mlhs_new
+ end
+
+ def test_mlhs_paren
+ thru_mlhs_paren = false
+ parse("a, b = 1, 2", :on_mlhs_paren) {thru_mlhs_paren = true}
+ assert_equal false, thru_mlhs_paren
+ thru_mlhs_paren = false
+ parse("(a, b) = 1, 2", :on_mlhs_paren) {thru_mlhs_paren = true}
+ assert_equal true, thru_mlhs_paren
+ end
+
+ def test_brace_block
+ thru_brace_block = false
+ parse('proc {}', :on_brace_block) {thru_brace_block = true}
+ assert_equal true, thru_brace_block
+ end
+
+ def test_break
+ thru_break = false
+ parse('proc {break}', :on_break) {thru_break = true}
+ assert_equal true, thru_break
+ end
+
+ def test_case
+ thru_case = false
+ parse('case foo when true; end', :on_case) {thru_case = true}
+ assert_equal true, thru_case
+ end
+
+ def test_class
+ thru_class = false
+ parse('class Foo; end', :on_class) {thru_class = true}
+ assert_equal true, thru_class
+ end
+
+ def test_class_name_error
+ thru_class_name_error = false
+ parse('class foo; end', :on_class_name_error) {thru_class_name_error = true}
+ assert_equal true, thru_class_name_error
+ end
+
+ def test_command
+ thru_command = false
+ parse('foo a b', :on_command) {thru_command = true}
+ assert_equal true, thru_command
+ end
+
+ def test_command_call
+ thru_command_call = false
+ parse('foo.bar a, b', :on_command_call) {thru_command_call = true}
+ assert_equal true, thru_command_call
+ end
+
+ def test_const_ref
+ thru_const_ref = false
+ parse('class A;end', :on_const_ref) {thru_const_ref = true}
+ assert_equal true, thru_const_ref
+ thru_const_ref = false
+ parse('module A;end', :on_const_ref) {thru_const_ref = true}
+ assert_equal true, thru_const_ref
+ end
+
+ def test_const_path_field
+ thru_const_path_field = false
+ parse('foo::X = 1', :on_const_path_field) {thru_const_path_field = true}
+ assert_equal true, thru_const_path_field
+ end
+
+ def test_const_path_ref
+ thru_const_path_ref = false
+ parse('foo::X', :on_const_path_ref) {thru_const_path_ref = true}
+ assert_equal true, thru_const_path_ref
+ end
+
+ def test_def
+ thru_def = false
+ parse('def foo; end', :on_def) {
+ thru_def = true
+ }
+ assert_equal true, thru_def
+ assert_equal '[def(foo,[],bodystmt([void()]))]', parse('def foo ;end')
+ end
+
+ def test_defined
+ thru_defined = false
+ parse('defined?(x)', :on_defined) {thru_defined = true}
+ assert_equal true, thru_defined
+ end
+
+ def test_defs
+ thru_defs = false
+ parse('def foo.bar; end', :on_defs) {thru_defs = true}
+ assert_equal true, thru_defs
+ end
+
+ def test_do_block
+ thru_do_block = false
+ parse('proc do end', :on_do_block) {thru_do_block = true}
+ assert_equal true, thru_do_block
+ end
+
+ def test_dot2
+ thru_dot2 = false
+ parse('a..b', :on_dot2) {thru_dot2 = true}
+ assert_equal true, thru_dot2
+ end
+
+ def test_dot3
+ thru_dot3 = false
+ parse('a...b', :on_dot3) {thru_dot3 = true}
+ assert_equal true, thru_dot3
+ end
+
+ def test_dyna_symbol
+ thru_dyna_symbol = false
+ parse(':"#{foo}"', :on_dyna_symbol) {thru_dyna_symbol = true}
+ assert_equal true, thru_dyna_symbol
+
+ thru_dyna_symbol = false
+ parse('{"#{foo}": 1}', :on_dyna_symbol) {thru_dyna_symbol = true}
+ assert_equal true, thru_dyna_symbol
+ end
+
+ def test_else
+ thru_else = false
+ parse('if foo; bar else zot end', :on_else) {thru_else = true}
+ assert_equal true, thru_else
+ end
+
+ def test_elsif
+ thru_elsif = false
+ parse('if foo; bar elsif qux; zot end', :on_elsif) {thru_elsif = true}
+ assert_equal true, thru_elsif
+ end
+
+ def test_ensure
+ thru_ensure = false
+ parse('begin foo ensure bar end', :on_ensure) {thru_ensure = true}
+ assert_equal true, thru_ensure
+ end
+
+ def test_fcall
+ thru_fcall = false
+ parse('foo()', :on_fcall) {thru_fcall = true}
+ assert_equal true, thru_fcall
+ end
+
+ def test_field
+ thru_field = false
+ parse('foo.x = 1', :on_field) {thru_field = true}
+ assert_equal true, thru_field
+ end
+
+ def test_for
+ thru_for = false
+ parse('for i in foo; end', :on_for) {thru_for = true}
+ assert_equal true, thru_for
+ end
+
+ def test_hash
+ thru_hash = false
+ parse('{1=>2}', :on_hash) {thru_hash = true}
+ assert_equal true, thru_hash
+ thru_hash = false
+ parse('{a: 2}', :on_hash) {thru_hash = true}
+ assert_equal true, thru_hash
+ end
+
+ def test_if
+ thru_if = false
+ parse('if false; end', :on_if) {thru_if = true}
+ assert_equal true, thru_if
+ end
+
+ def test_if_mod
+ thru_if_mod = false
+ parse('nil if nil', :on_if_mod) {thru_if_mod = true}
+ assert_equal true, thru_if_mod
+ end
+
+ def test_ifop
+ thru_ifop = false
+ parse('a ? b : c', :on_ifop) {thru_ifop = true}
+ assert_equal true, thru_ifop
+ end
+
+ def test_lambda
+ thru_lambda = false
+ parse('->{}', :on_lambda) {thru_lambda = true}
+ assert_equal true, thru_lambda
+ end
+
+ def test_magic_comment
+ thru_magic_comment = false
+ parse('# -*- bug-5753: ruby-dev:44984 -*-', :on_magic_comment) {|*x|thru_magic_comment = x}
+ assert_equal [:on_magic_comment, "bug_5753", "ruby-dev:44984"], thru_magic_comment
+ end
+
+ def test_method_add_block
+ thru_method_add_block = false
+ parse('a {}', :on_method_add_block) {thru_method_add_block = true}
+ assert_equal true, thru_method_add_block
+ thru_method_add_block = false
+ parse('a do end', :on_method_add_block) {thru_method_add_block = true}
+ assert_equal true, thru_method_add_block
+ end
+
+ def test_method_add_arg
+ thru_method_add_arg = false
+ parse('a()', :on_method_add_arg) {thru_method_add_arg = true}
+ assert_equal true, thru_method_add_arg
+ thru_method_add_arg = false
+ parse('a {}', :on_method_add_arg) {thru_method_add_arg = true}
+ assert_equal true, thru_method_add_arg
+ thru_method_add_arg = false
+ parse('a.b(1)', :on_method_add_arg) {thru_method_add_arg = true}
+ assert_equal true, thru_method_add_arg
+ thru_method_add_arg = false
+ parse('a::b(1)', :on_method_add_arg) {thru_method_add_arg = true}
+ assert_equal true, thru_method_add_arg
+ end
+
+ def test_module
+ thru_module = false
+ parse('module A; end', :on_module) {thru_module = true}
+ assert_equal true, thru_module
+ end
+
+ def test_mrhs_add
+ thru_mrhs_add = false
+ parse('a = a, b', :on_mrhs_add) {thru_mrhs_add = true}
+ assert_equal true, thru_mrhs_add
+ end
+
+ def test_mrhs_add_star
+ thru_mrhs_add_star = false
+ parse('a = a, *b', :on_mrhs_add_star) {thru_mrhs_add_star = true}
+ assert_equal true, thru_mrhs_add_star
+ end
+
+ def test_mrhs_new
+ thru_mrhs_new = false
+ parse('a = *a', :on_mrhs_new) {thru_mrhs_new = true}
+ assert_equal true, thru_mrhs_new
+ end
+
+ def test_mrhs_new_from_args
+ thru_mrhs_new_from_args = false
+ parse('a = a, b', :on_mrhs_new_from_args) {thru_mrhs_new_from_args = true}
+ assert_equal true, thru_mrhs_new_from_args
+ end
+
+ def test_next
+ thru_next = false
+ parse('a {next}', :on_next) {thru_next = true}
+ assert_equal true, thru_next
+ end
+
+ def test_opassign
+ thru_opassign = false
+ parse('a += b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a -= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a *= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a /= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a %= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a **= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a &= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a |= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a <<= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a >>= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a &&= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a ||= b', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ thru_opassign = false
+ parse('a::X ||= c 1', :on_opassign) {thru_opassign = true}
+ assert_equal true, thru_opassign
+ end
+
+ def test_opassign_error
+ thru_opassign = []
+ events = [:on_opassign]
+ parse('$~ ||= 1', events) {|a,*b|
+ thru_opassign << a
+ }
+ assert_equal events, thru_opassign
+ end
+
+ def test_param_error
+ thru_param_error = false
+ parse('def foo(A) end', :on_param_error) {thru_param_error = true}
+ assert_equal true, thru_param_error
+ thru_param_error = false
+ parse('def foo($a) end', :on_param_error) {thru_param_error = true}
+ assert_equal true, thru_param_error
+ thru_param_error = false
+ parse('def foo(@a) end', :on_param_error) {thru_param_error = true}
+ assert_equal true, thru_param_error
+ thru_param_error = false
+ parse('def foo(@@a) end', :on_param_error) {thru_param_error = true}
+ assert_equal true, thru_param_error
+ end
+
+ def test_params
+ arg = nil
+ thru_params = false
+ parse('a {||}', :on_params) {|_, *v| thru_params = true; arg = v}
+ assert_equal true, thru_params
+ assert_equal [nil, nil, nil, nil, nil, nil, nil], arg
+ thru_params = false
+ parse('a {|x|}', :on_params) {|_, *v| thru_params = true; arg = v}
+ assert_equal true, thru_params
+ assert_equal [["x"], nil, nil, nil, nil, nil, nil], arg
+ thru_params = false
+ parse('a {|*x|}', :on_params) {|_, *v| thru_params = true; arg = v}
+ assert_equal true, thru_params
+ assert_equal [nil, nil, "*x", nil, nil, nil, nil], arg
+ thru_params = false
+ parse('a {|x: 1|}', :on_params) {|_, *v| thru_params = true; arg = v}
+ assert_equal true, thru_params
+ assert_equal [nil, nil, nil, nil, [["x:", "1"]], nil, nil], arg
+ thru_params = false
+ parse('a {|x:|}', :on_params) {|_, *v| thru_params = true; arg = v}
+ assert_equal true, thru_params
+ assert_equal [nil, nil, nil, nil, [["x:", false]], nil, nil], arg
+ thru_params = false
+ parse('a {|**x|}', :on_params) {|_, *v| thru_params = true; arg = v}
+ assert_equal true, thru_params
+ assert_equal [nil, nil, nil, nil, nil, "x", nil], arg
+ end
+
+ def test_paren
+ thru_paren = false
+ parse('()', :on_paren) {thru_paren = true}
+ assert_equal true, thru_paren
+ end
+
+ def test_parse_error
+ thru_parse_error = false
+ parse('<>', :on_parse_error) {thru_parse_error = true}
+ assert_equal true, thru_parse_error
+ end
+
+ def test_qwords_add
+ thru_qwords_add = false
+ parse('%w[a]', :on_qwords_add) {thru_qwords_add = true}
+ assert_equal true, thru_qwords_add
+ end
+
+ def test_qsymbols_add
+ thru_qsymbols_add = false
+ parse('%i[a]', :on_qsymbols_add) {thru_qsymbols_add = true}
+ assert_equal true, thru_qsymbols_add
+ end
+
+ def test_symbols_add
+ thru_symbols_add = false
+ parse('%I[a]', :on_symbols_add) {thru_symbols_add = true}
+ assert_equal true, thru_symbols_add
+ end
+
+ def test_qwords_new
+ thru_qwords_new = false
+ parse('%w[]', :on_qwords_new) {thru_qwords_new = true}
+ assert_equal true, thru_qwords_new
+ end
+
+ def test_qsymbols_new
+ thru_qsymbols_new = false
+ parse('%i[]', :on_qsymbols_new) {thru_qsymbols_new = true}
+ assert_equal true, thru_qsymbols_new
+ end
+
+ def test_symbols_new
+ thru_symbols_new = false
+ parse('%I[]', :on_symbols_new) {thru_symbols_new = true}
+ assert_equal true, thru_symbols_new
+ end
+
+ def test_redo
+ thru_redo = false
+ parse('redo', :on_redo) {thru_redo = true}
+ assert_equal true, thru_redo
+ end
+
+ def test_regexp_add
+ thru_regexp_add = false
+ parse('/foo/', :on_regexp_add) {thru_regexp_add = true}
+ assert_equal true, thru_regexp_add
+ end
+
+ def test_regexp_literal
+ thru_regexp_literal = false
+ parse('//', :on_regexp_literal) {thru_regexp_literal = true}
+ assert_equal true, thru_regexp_literal
+ end
+
+ def test_regexp_new
+ thru_regexp_new = false
+ parse('//', :on_regexp_new) {thru_regexp_new = true}
+ assert_equal true, thru_regexp_new
+ end
+
+ def test_rescue
+ thru_rescue = false
+ parsed = parse('begin; 1; rescue => e; 2; end', :on_rescue) {thru_rescue = true}
+ assert_equal true, thru_rescue
+ assert_match(/1.*rescue/, parsed)
+ assert_match(/rescue\(,var_field\(e\),\[2\]\)/, parsed)
+ end
+
+ def test_rescue_class
+ thru_rescue = false
+ parsed = parse('begin; 1; rescue RuntimeError => e; 2; end', :on_rescue) {thru_rescue = true}
+ assert_equal true, thru_rescue
+ assert_match(/1.*rescue/, parsed)
+ assert_match(/rescue\(\[ref\(RuntimeError\)\],var_field\(e\),\[2\]\)/, parsed)
+ end
+
+ def test_rescue_mod
+ thru_rescue_mod = false
+ parsed = parse('1 rescue 2', :on_rescue_mod) {thru_rescue_mod = true}
+ assert_equal true, thru_rescue_mod
+ bug4716 = '[ruby-core:36248]'
+ assert_equal "[rescue_mod(1,2)]", parsed, bug4716
+ end
+
+ def test_rest_param
+ thru_rest_param = false
+ parse('def a(*) end', :on_rest_param) {thru_rest_param = true}
+ assert_equal true, thru_rest_param
+ thru_rest_param = false
+ parse('def a(*x) end', :on_rest_param) {thru_rest_param = true}
+ assert_equal true, thru_rest_param
+ end
+
+ def test_retry
+ thru_retry = false
+ parse('retry', :on_retry) {thru_retry = true}
+ assert_equal true, thru_retry
+ end
+
+ def test_return
+ thru_return = false
+ parse('return a', :on_return) {thru_return = true}
+ assert_equal true, thru_return
+ end
+
+ def test_return0
+ thru_return0 = false
+ parse('return', :on_return0) {thru_return0 = true}
+ assert_equal true, thru_return0
+ end
+
+ def test_sclass
+ thru_sclass = false
+ parse('class << a; end', :on_sclass) {thru_sclass = true}
+ assert_equal true, thru_sclass
+ end
+
+ def test_string_add
+ thru_string_add = false
+ parse('"aa"', :on_string_add) {thru_string_add = true}
+ assert_equal true, thru_string_add
+ end
+
+ def test_string_concat
+ thru_string_concat = false
+ parse('"a" "b"', :on_string_concat) {thru_string_concat = true}
+ assert_equal true, thru_string_concat
+ end
+
+ def test_string_content
+ thru_string_content = false
+ parse('""', :on_string_content) {thru_string_content = true}
+ assert_equal true, thru_string_content
+ thru_string_content = false
+ parse('"a"', :on_string_content) {thru_string_content = true}
+ assert_equal true, thru_string_content
+ thru_string_content = false
+ parse('%[a]', :on_string_content) {thru_string_content = true}
+ assert_equal true, thru_string_content
+ thru_string_content = false
+ parse('\'a\'', :on_string_content) {thru_string_content = true}
+ assert_equal true, thru_string_content
+ thru_string_content = false
+ parse('%<a>', :on_string_content) {thru_string_content = true}
+ assert_equal true, thru_string_content
+ thru_string_content = false
+ parse('%!a!', :on_string_content) {thru_string_content = true}
+ assert_equal true, thru_string_content
+ thru_string_content = false
+ parse('%q!a!', :on_string_content) {thru_string_content = true}
+ assert_equal true, thru_string_content
+ thru_string_content = false
+ parse('%Q!a!', :on_string_content) {thru_string_content = true}
+ assert_equal true, thru_string_content
+ end
+
+ def test_string_dvar
+ thru_string_dvar = false
+ parse('"#$a"', :on_string_dvar) {thru_string_dvar = true}
+ assert_equal true, thru_string_dvar
+ thru_string_dvar = false
+ parse('\'#$a\'', :on_string_dvar) {thru_string_dvar = true}
+ assert_equal false, thru_string_dvar
+ thru_string_dvar = false
+ parse('"#@a"', :on_string_dvar) {thru_string_dvar = true}
+ assert_equal true, thru_string_dvar
+ thru_string_dvar = false
+ parse('\'#@a\'', :on_string_dvar) {thru_string_dvar = true}
+ assert_equal false, thru_string_dvar
+ thru_string_dvar = false
+ parse('"#@@a"', :on_string_dvar) {thru_string_dvar = true}
+ assert_equal true, thru_string_dvar
+ thru_string_dvar = false
+ parse('\'#@@a\'', :on_string_dvar) {thru_string_dvar = true}
+ assert_equal false, thru_string_dvar
+ thru_string_dvar = false
+ parse('"#$1"', :on_string_dvar) {thru_string_dvar = true}
+ assert_equal true, thru_string_dvar
+ thru_string_dvar = false
+ parse('\'#$1\'', :on_string_dvar) {thru_string_dvar = true}
+ assert_equal false, thru_string_dvar
+ end
+
+ def test_string_embexpr
+ thru_string_embexpr = false
+ parse('"#{}"', :on_string_embexpr) {thru_string_embexpr = true}
+ assert_equal true, thru_string_embexpr
+ thru_string_embexpr = false
+ parse('\'#{}\'', :on_string_embexpr) {thru_string_embexpr = true}
+ assert_equal false, thru_string_embexpr
+ end
+
+ def test_string_literal
+ thru_string_literal = false
+ parse('""', :on_string_literal) {thru_string_literal = true}
+ assert_equal true, thru_string_literal
+ end
+
+ def test_super
+ thru_super = false
+ parse('super()', :on_super) {thru_super = true}
+ assert_equal true, thru_super
+ end
+
+ def test_symbol
+ thru_symbol = false
+ parse(':a', :on_symbol) {thru_symbol = true}
+ assert_equal true, thru_symbol
+ thru_symbol = false
+ parse(':$a', :on_symbol) {thru_symbol = true}
+ assert_equal true, thru_symbol
+ thru_symbol = false
+ parse(':@a', :on_symbol) {thru_symbol = true}
+ assert_equal true, thru_symbol
+ thru_symbol = false
+ parse(':@@a', :on_symbol) {thru_symbol = true}
+ assert_equal true, thru_symbol
+ thru_symbol = false
+ parse(':==', :on_symbol) {thru_symbol = true}
+ assert_equal true, thru_symbol
+ end
+
+ def test_symbol_literal
+ thru_symbol_literal = false
+ parse(':a', :on_symbol_literal) {thru_symbol_literal = true}
+ assert_equal true, thru_symbol_literal
+ end
+
+ def test_top_const_field
+ thru_top_const_field = false
+ parse('::A=1', :on_top_const_field) {thru_top_const_field = true}
+ assert_equal true, thru_top_const_field
+ end
+
+ def test_top_const_ref
+ thru_top_const_ref = false
+ parse('::A', :on_top_const_ref) {thru_top_const_ref = true}
+ assert_equal true, thru_top_const_ref
+ end
+
+ def test_unary
+ thru_unary = false
+ parse('not a 1, 2', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('not (a)', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('!a', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('-10', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('-10*2', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('-10.1', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('-10.1*2', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('-a', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('+a', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('~a', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ thru_unary = false
+ parse('not()', :on_unary) {thru_unary = true}
+ assert_equal true, thru_unary
+ end
+
+ def test_undef
+ thru_undef = false
+ parse('undef a', :on_undef) {thru_undef = true}
+ assert_equal true, thru_undef
+ thru_undef = false
+ parse('undef <=>', :on_undef) {thru_undef = true}
+ assert_equal true, thru_undef
+ thru_undef = false
+ parse('undef a, b', :on_undef) {thru_undef = true}
+ assert_equal true, thru_undef
+ end
+
+ def test_unless
+ thru_unless = false
+ parse('unless a; end', :on_unless) {thru_unless = true}
+ assert_equal true, thru_unless
+ end
+
+ def test_unless_mod
+ thru_unless_mod = false
+ parse('nil unless a', :on_unless_mod) {thru_unless_mod = true}
+ assert_equal true, thru_unless_mod
+ end
+
+ def test_until
+ thru_until = false
+ parse('until a; end', :on_until) {thru_until = true}
+ assert_equal true, thru_until
+ end
+
+ def test_until_mod
+ thru_until_mod = false
+ parse('nil until a', :on_until_mod) {thru_until_mod = true}
+ assert_equal true, thru_until_mod
+ end
+
+ def test_var_field
+ thru_var_field = false
+ parse('a = 1', :on_var_field) {thru_var_field = true}
+ assert_equal true, thru_var_field
+ thru_var_field = false
+ parse('a += 1', :on_var_field) {thru_var_field = true}
+ assert_equal true, thru_var_field
+ end
+
+ def test_when
+ thru_when = false
+ parse('case a when b; end', :on_when) {thru_when = true}
+ assert_equal true, thru_when
+ thru_when = false
+ parse('case when a; end', :on_when) {thru_when = true}
+ assert_equal true, thru_when
+ end
+
+ def test_while
+ thru_while = false
+ parse('while a; end', :on_while) {thru_while = true}
+ assert_equal true, thru_while
+ end
+
+ def test_while_mod
+ thru_while_mod = false
+ parse('nil while a', :on_while_mod) {thru_while_mod = true}
+ assert_equal true, thru_while_mod
+ end
+
+ def test_word_add
+ thru_word_add = false
+ parse('%W[a]', :on_word_add) {thru_word_add = true}
+ assert_equal true, thru_word_add
+ end
+
+ def test_word_new
+ thru_word_new = false
+ parse('%W[a]', :on_word_new) {thru_word_new = true}
+ assert_equal true, thru_word_new
+ end
+
+ def test_words_add
+ thru_words_add = false
+ parse('%W[a]', :on_words_add) {thru_words_add = true}
+ assert_equal true, thru_words_add
+ end
+
+ def test_words_new
+ thru_words_new = false
+ parse('%W[]', :on_words_new) {thru_words_new = true}
+ assert_equal true, thru_words_new
+ end
+
+ def test_xstring_add
+ thru_xstring_add = false
+ parse('`x`', :on_xstring_add) {thru_xstring_add = true}
+ assert_equal true, thru_xstring_add
+ end
+
+ def test_xstring_literal
+ thru_xstring_literal = false
+ parse('``', :on_xstring_literal) {thru_xstring_literal = true}
+ assert_equal true, thru_xstring_literal
+ end
+
+ def test_xstring_new
+ thru_xstring_new = false
+ parse('``', :on_xstring_new) {thru_xstring_new = true}
+ assert_equal true, thru_xstring_new
+ end
+
+ def test_yield
+ thru_yield = false
+ parse('yield a', :on_yield) {thru_yield = true}
+ assert_equal true, thru_yield
+ end
+
+ def test_yield0
+ thru_yield0 = false
+ parse('yield', :on_yield0) {thru_yield0 = true}
+ assert_equal true, thru_yield0
+ end
+
+ def test_zsuper
+ thru_zsuper = false
+ parse('super', :on_zsuper) {thru_zsuper = true}
+ assert_equal true, thru_zsuper
+ end
+
+ def test_local_variables
+ cmd = 'command(w,[regexp_literal(regexp_add(regexp_new(),25 # ),/)])'
+ div = 'binary(ref(w),/,25)'
+ bug1939 = '[ruby-core:24923]'
+
+ assert_equal("[#{cmd}]", parse('w /25 # /'), bug1939)
+ assert_equal("[assign(var_field(w),1),#{div}]", parse("w = 1; w /25 # /"), bug1939)
+ assert_equal("[fcall(p,[],&block([w],[#{div}]))]", parse("p{|w|w /25 # /\n}"), bug1939)
+ assert_equal("[def(p,[w],bodystmt([#{div}]))]", parse("def p(w)\nw /25 # /\nend"), bug1939)
+ end
+
+ def test_block_variables
+ assert_equal("[fcall(proc,[],&block([],[void()]))]", parse("proc{|;y|}"))
+ if defined?(Process::RLIMIT_AS)
+ assert_in_out_err(["-I#{File.dirname(__FILE__)}", "-rdummyparser"],
+ 'Process.setrlimit(Process::RLIMIT_AS,100*1024*1024); puts DummyParser.new("proc{|;y|!y}").parse',
+ ["[fcall(proc,[],&block([],[unary(!,ref(y))]))]"], [], '[ruby-dev:39423]')
+ end
+ end
+
+ def test_unterminated_regexp
+ assert_equal("unterminated regexp meets end of file", compile_error('/'))
+ end
+
+ def test_invalid_instance_variable_name
+ assert_equal("`@1' is not allowed as an instance variable name", compile_error('@1'))
+ assert_equal("`@%' is not allowed as an instance variable name", compile_error('@%'))
+ end
+
+ def test_invalid_class_variable_name
+ assert_equal("`@@1' is not allowed as a class variable name", compile_error('@@1'))
+ assert_equal("`@@%' is not allowed as a class variable name", compile_error('@@%'))
+ end
+
+ def test_invalid_global_variable_name
+ assert_equal("`$%' is not allowed as a global variable name", compile_error('$%'))
+ end
+end if ripper_test
diff --git a/jni/ruby/test/ripper/test_ripper.rb b/jni/ruby/test/ripper/test_ripper.rb
new file mode 100644
index 0000000..1544e56
--- /dev/null
+++ b/jni/ruby/test/ripper/test_ripper.rb
@@ -0,0 +1,63 @@
+begin
+ require 'ripper'
+ require 'test/unit'
+ ripper_test = true
+ module TestRipper; end
+rescue LoadError
+end
+
+class TestRipper::Ripper < Test::Unit::TestCase
+
+ def setup
+ @ripper = Ripper.new '1 + 1'
+ end
+
+ def test_column
+ assert_nil @ripper.column
+ end
+
+ def test_encoding
+ assert_equal Encoding::UTF_8, @ripper.encoding
+ ripper = Ripper.new('# coding: iso-8859-15')
+ ripper.parse
+ assert_equal Encoding::ISO_8859_15, ripper.encoding
+ ripper = Ripper.new('# -*- coding: iso-8859-15 -*-')
+ ripper.parse
+ assert_equal Encoding::ISO_8859_15, ripper.encoding
+ end
+
+ def test_end_seen_eh
+ @ripper.parse
+ assert_not_predicate @ripper, :end_seen?
+ ripper = Ripper.new('__END__')
+ ripper.parse
+ assert_predicate ripper, :end_seen?
+ end
+
+ def test_filename
+ assert_equal '(ripper)', @ripper.filename
+ filename = "ripper"
+ ripper = Ripper.new("", filename)
+ filename.clear
+ assert_equal "ripper", ripper.filename
+ end
+
+ def test_lineno
+ assert_nil @ripper.lineno
+ end
+
+ def test_parse
+ assert_nil @ripper.parse
+ end
+
+ def test_yydebug
+ assert_not_predicate @ripper, :yydebug
+ end
+
+ def test_yydebug_equals
+ @ripper.yydebug = true
+
+ assert_predicate @ripper, :yydebug
+ end
+
+end if ripper_test
diff --git a/jni/ruby/test/ripper/test_scanner_events.rb b/jni/ruby/test/ripper/test_scanner_events.rb
new file mode 100644
index 0000000..e57300e
--- /dev/null
+++ b/jni/ruby/test/ripper/test_scanner_events.rb
@@ -0,0 +1,909 @@
+#
+# test_scanner_events.rb
+#
+begin
+ require 'ripper'
+ require 'test/unit'
+ ripper_test = true
+ module TestRipper; end
+rescue LoadError
+end
+
+class TestRipper::ScannerEvents < Test::Unit::TestCase
+
+ def test_event_coverage
+ dispatched = Ripper::SCANNER_EVENTS.map {|event,_| event }
+ dispatched.each do |e|
+ assert_equal true, respond_to?("test_#{e}", true), "event not tested: #{e}"
+ end
+ end
+
+ def scan(target, str)
+ sym = "on_#{target}".intern
+ Ripper.lex(str).select {|_1,type,_2| type == sym }.map {|_1,_2,tok| tok }
+ end
+
+ def test_tokenize
+ assert_equal [],
+ Ripper.tokenize('')
+ assert_equal ['a'],
+ Ripper.tokenize('a')
+ assert_equal ['1'],
+ Ripper.tokenize('1')
+ assert_equal ['1', ';', 'def', ' ', 'm', '(', 'arg', ')', 'end'],
+ Ripper.tokenize("1;def m(arg)end")
+ assert_equal ['print', '(', '<<''EOS', ')', "\n", "heredoc\n", "EOS\n"],
+ Ripper.tokenize("print(<<""EOS)\nheredoc\nEOS\n")
+ assert_equal ['print', '(', ' ', '<<''EOS', ')', "\n", "heredoc\n", "EOS\n"],
+ Ripper.tokenize("print( <<""EOS)\nheredoc\nEOS\n")
+ assert_equal ["\#\n", "\n", "\#\n", "\n", "nil", "\n"],
+ Ripper.tokenize("\#\n\n\#\n\nnil\n")
+ assert_equal ["1", " ", ".", "foo", "\n"],
+ Ripper.tokenize("1 .foo\n")
+ assert_equal ["1", "\n", " ", ".", "foo", "\n"],
+ Ripper.tokenize("1\n .foo\n")
+ end
+
+ def test_lex
+ assert_equal [],
+ Ripper.lex('')
+ assert_equal [[[1,0], :on_ident, "a"]],
+ Ripper.lex('a')
+ assert_equal [[[1, 0], :on_kw, "nil"]],
+ Ripper.lex("nil")
+ assert_equal [[[1, 0], :on_kw, "def"],
+ [[1, 3], :on_sp, " "],
+ [[1, 4], :on_ident, "m"],
+ [[1, 5], :on_lparen, "("],
+ [[1, 6], :on_ident, "a"],
+ [[1, 7], :on_rparen, ")"],
+ [[1, 8], :on_kw, "end"]],
+ Ripper.lex("def m(a)end")
+ assert_equal [[[1, 0], :on_int, "1"],
+ [[1, 1], :on_nl, "\n"],
+ [[2, 0], :on_int, "2"],
+ [[2, 1], :on_nl, "\n"],
+ [[3, 0], :on_int, "3"]],
+ Ripper.lex("1\n2\n3")
+ assert_equal [[[1, 0], :on_heredoc_beg, "<<""EOS"],
+ [[1, 5], :on_nl, "\n"],
+ [[2, 0], :on_tstring_content, "heredoc\n"],
+ [[3, 0], :on_heredoc_end, "EOS"]],
+ Ripper.lex("<<""EOS\nheredoc\nEOS")
+ assert_equal [[[1, 0], :on_heredoc_beg, "<<""EOS"],
+ [[1, 5], :on_nl, "\n"],
+ [[2, 0], :on_heredoc_end, "EOS"]],
+ Ripper.lex("<<""EOS\nEOS"),
+ "bug#4543"
+ assert_equal [[[1, 0], :on_regexp_beg, "/"],
+ [[1, 1], :on_tstring_content, "foo\nbar"],
+ [[2, 3], :on_regexp_end, "/"]],
+ Ripper.lex("/foo\nbar/")
+ assert_equal [[[1, 0], :on_regexp_beg, "/"],
+ [[1, 1], :on_tstring_content, "foo\n\u3020"],
+ [[2, 3], :on_regexp_end, "/"]],
+ Ripper.lex("/foo\n\u3020/")
+ assert_equal [[[1, 0], :on_tstring_beg, "'"],
+ [[1, 1], :on_tstring_content, "foo\n\xe3\x80\xa0"],
+ [[2, 3], :on_tstring_end, "'"]],
+ Ripper.lex("'foo\n\xe3\x80\xa0'")
+ assert_equal [[[1, 0], :on_tstring_beg, "'"],
+ [[1, 1], :on_tstring_content, "\u3042\n\u3044"],
+ [[2, 3], :on_tstring_end, "'"]],
+ Ripper.lex("'\u3042\n\u3044'")
+ assert_equal [[[1, 0], :on_rational, "1r"],
+ [[1, 2], :on_nl, "\n"],
+ [[2, 0], :on_imaginary, "2i"],
+ [[2, 2], :on_nl, "\n"],
+ [[3, 0], :on_imaginary, "3ri"],
+ [[3, 3], :on_nl, "\n"],
+ [[4, 0], :on_rational, "4.2r"],
+ [[4, 4], :on_nl, "\n"],
+ [[5, 0], :on_imaginary, "5.6ri"],
+ ],
+ Ripper.lex("1r\n2i\n3ri\n4.2r\n5.6ri")
+ end
+
+ def test_location
+ assert_location ""
+ assert_location " "
+ assert_location ":"
+ assert_location "\n"
+ assert_location "\r\n"
+ assert_location "\n\n\n\n\n\r\n\n\n"
+ assert_location "\n;\n;\n;\n;\n"
+ assert_location "nil"
+ assert_location "@ivar"
+ assert_location "1;2;3"
+ assert_location "1\n2\n3"
+ assert_location "1\n2\n3\n"
+ assert_location "def m(a) nil end"
+ assert_location "if true then false else nil end"
+ assert_location "BEGIN{print nil}"
+ assert_location "%w(a b\nc\r\nd \ne )"
+ assert_location %Q["a\nb\r\nc"]
+ assert_location "print(<<""EOS)\nheredoc\nEOS\n"
+ assert_location "print(<<-\"EOS\")\nheredoc\n EOS\n"
+ end
+
+ def assert_location(src)
+ buf = ''
+ Ripper.lex(src).each do |pos, type, tok|
+ line, col = *pos
+ assert_equal buf.count("\n") + 1, line,
+ "wrong lineno: #{tok.inspect} (#{type}) [#{line}:#{col}]"
+ assert_equal buf.sub(/\A.*\n/m, '').size, col,
+ "wrong column: #{tok.inspect} (#{type}) [#{line}:#{col}]"
+ buf << tok
+ end
+ assert_equal src, buf
+ end
+
+ def test_backref
+ assert_equal ["$`", "$&", "$'", '$1', '$2', '$3'],
+ scan('backref', %q[m($~, $`, $&, $', $1, $2, $3)])
+ end
+
+ def test_backtick
+ assert_equal ["`"],
+ scan('backtick', %q[p `make all`])
+ end
+
+ def test_comma
+ assert_equal [','] * 6,
+ scan('comma', %q[ m(0,1,2,3,4,5,6) ])
+ assert_equal [],
+ scan('comma', %q[".,.,.,.,.,.,.."])
+ assert_equal [],
+ scan('comma', "<<""EOS\n,,,,,,,,,,\nEOS")
+ end
+
+ def test_period
+ assert_equal [],
+ scan('period', '')
+ assert_equal ['.'],
+ scan('period', 'a.b')
+ assert_equal ['.'],
+ scan('period', 'Object.new')
+ assert_equal [],
+ scan('period', '"."')
+ assert_equal [],
+ scan('period', '1..2')
+ assert_equal [],
+ scan('period', '1...3')
+ end
+
+ def test_const
+ assert_equal ['CONST'],
+ scan('const', 'CONST')
+ assert_equal ['C'],
+ scan('const', 'C')
+ assert_equal ['CONST_A'],
+ scan('const', 'CONST_A')
+ assert_equal ['Const', 'Const2', 'Const3'],
+ scan('const', 'Const; Const2; Const3')
+ assert_equal ['Const'],
+ scan('const', 'Const(a)')
+ assert_equal ['M', 'A', 'A2'],
+ scan('const', 'M(A,A2)')
+ assert_equal [],
+ scan('const', '')
+ assert_equal [],
+ scan('const', 'm(lvar, @ivar, @@cvar, $gvar)')
+ end
+
+ def test_cvar
+ assert_equal [],
+ scan('cvar', '')
+ assert_equal ['@@cvar'],
+ scan('cvar', '@@cvar')
+ assert_equal ['@@__cvar__'],
+ scan('cvar', '@@__cvar__')
+ assert_equal ['@@CVAR'],
+ scan('cvar', '@@CVAR')
+ assert_equal ['@@cvar'],
+ scan('cvar', ' @@cvar#comment')
+ assert_equal ['@@cvar'],
+ scan('cvar', ':@@cvar')
+ assert_equal ['@@cvar'],
+ scan('cvar', 'm(lvar, @ivar, @@cvar, $gvar)')
+ assert_equal [],
+ scan('cvar', '"@@cvar"')
+ end
+
+ def test_embexpr_beg
+ assert_equal [],
+ scan('embexpr_beg', '')
+ assert_equal ['#{'],
+ scan('embexpr_beg', '"#{expr}"')
+ assert_equal [],
+ scan('embexpr_beg', '%q[#{expr}]')
+ assert_equal ['#{'],
+ scan('embexpr_beg', '%Q[#{expr}]')
+ assert_equal ['#{'],
+ scan('embexpr_beg', "m(<<""EOS)\n\#{expr}\nEOS")
+ end
+
+ def test_embexpr_end
+ assert_equal [],
+ scan('embexpr_end', '')
+ assert_equal ['}'],
+ scan('embexpr_end', '"#{expr}"')
+ assert_equal [],
+ scan('embexpr_end', '%q[#{expr}]')
+ assert_equal ['}'],
+ scan('embexpr_end', '%Q[#{expr}]')
+ assert_equal ['}'],
+ scan('embexpr_end', "m(<<""EOS)\n\#{expr}\nEOS")
+ end
+
+ def test_embvar
+ assert_equal [],
+ scan('embvar', '')
+ assert_equal ['#'],
+ scan('embvar', '"#$gvar"')
+ assert_equal ['#'],
+ scan('embvar', '"#@ivar"')
+ assert_equal ['#'],
+ scan('embvar', '"#@@cvar"')
+ assert_equal [],
+ scan('embvar', '"#lvar"')
+ assert_equal [],
+ scan('embvar', '"#"')
+ assert_equal [],
+ scan('embvar', '"\#$gvar"')
+ assert_equal [],
+ scan('embvar', '"\#@ivar"')
+ assert_equal [],
+ scan('embvar', '%q[#@ivar]')
+ assert_equal ['#'],
+ scan('embvar', '%Q[#@ivar]')
+ end
+
+ def test_float
+ assert_equal [],
+ scan('float', '')
+ assert_equal ['1.000'],
+ scan('float', '1.000')
+ assert_equal ['123.456'],
+ scan('float', '123.456')
+ assert_equal ['1.2345678901234567890123456789'],
+ scan('float', '1.2345678901234567890123456789')
+ assert_equal ['1.000'],
+ scan('float', ' 1.000# comment')
+ assert_equal ['1.234e5'],
+ scan('float', '1.234e5')
+ assert_equal ['1.234e1234567890'],
+ scan('float', '1.234e1234567890')
+ assert_equal ['1.0'],
+ scan('float', 'm(a,b,1.0,c,d)')
+ end
+
+ def test_rational
+ assert_equal [],
+ scan('rational', '')
+ assert_equal ['1r', '10r', '10.1r'],
+ scan('rational', 'm(1r,10r,10.1r)')
+ end
+
+ def test_gvar
+ assert_equal [],
+ scan('gvar', '')
+ assert_equal ['$a'],
+ scan('gvar', '$a')
+ assert_equal ['$A'],
+ scan('gvar', '$A')
+ assert_equal ['$gvar'],
+ scan('gvar', 'm(lvar, @ivar, @@cvar, $gvar)')
+ assert_equal %w($_ $~ $* $$ $? $! $@ $/ $\\ $; $, $. $= $: $< $> $"),
+ scan('gvar', 'm($_, $~, $*, $$, $?, $!, $@, $/, $\\, $;, $,, $., $=, $:, $<, $>, $")')
+ end
+
+ def test_ident
+ assert_equal [],
+ scan('ident', '')
+ assert_equal ['lvar'],
+ scan('ident', 'lvar')
+ assert_equal ['m', 'lvar'],
+ scan('ident', 'm(lvar, @ivar, @@cvar, $gvar)')
+ end
+
+ def test_imaginary
+ assert_equal [],
+ scan('imaginary', '')
+ assert_equal ['1i', '10ri', '10.0i', '10.1ri'],
+ scan('imaginary', 'm(1i,10ri,10.0i,10.1ri)')
+ end
+
+ def test_int
+ assert_equal [],
+ scan('int', '')
+ assert_equal ['1', '10', '100000000000000'],
+ scan('int', 'm(1,10,100000000000000)')
+ end
+
+ def test_ivar
+ assert_equal [],
+ scan('ivar', '')
+ assert_equal ['@ivar'],
+ scan('ivar', '@ivar')
+ assert_equal ['@__ivar__'],
+ scan('ivar', '@__ivar__')
+ assert_equal ['@IVAR'],
+ scan('ivar', '@IVAR')
+ assert_equal ['@ivar'],
+ scan('ivar', 'm(lvar, @ivar, @@cvar, $gvar)')
+ end
+
+ def test_kw
+ assert_equal [],
+ scan('kw', '')
+ assert_equal %w(not),
+ scan('kw', 'not 1')
+ assert_equal %w(and),
+ scan('kw', '1 and 2')
+ assert_equal %w(or),
+ scan('kw', '1 or 2')
+ assert_equal %w(if then else end),
+ scan('kw', 'if 1 then 2 else 3 end')
+ assert_equal %w(if then elsif else end),
+ scan('kw', 'if 1 then 2 elsif 3 else 4 end')
+ assert_equal %w(unless then end),
+ scan('kw', 'unless 1 then end')
+ assert_equal %w(if true),
+ scan('kw', '1 if true')
+ assert_equal %w(unless false),
+ scan('kw', '2 unless false')
+ assert_equal %w(case when when else end),
+ scan('kw', 'case n; when 1; when 2; else 3 end')
+ assert_equal %w(while do nil end),
+ scan('kw', 'while 1 do nil end')
+ assert_equal %w(until do nil end),
+ scan('kw', 'until 1 do nil end')
+ assert_equal %w(while),
+ scan('kw', '1 while 2')
+ assert_equal %w(until),
+ scan('kw', '1 until 2')
+ assert_equal %w(while break next retry end),
+ scan('kw', 'while 1; break; next; retry end')
+ assert_equal %w(for in next break end),
+ scan('kw', 'for x in obj; next 1; break 2 end')
+ assert_equal %w(begin rescue retry end),
+ scan('kw', 'begin 1; rescue; retry; end')
+ assert_equal %w(rescue),
+ scan('kw', '1 rescue 2')
+ assert_equal %w(def redo return end),
+ scan('kw', 'def m() redo; return end')
+ assert_equal %w(def yield yield end),
+ scan('kw', 'def m() yield; yield 1 end')
+ assert_equal %w(def super super super end),
+ scan('kw', 'def m() super; super(); super(1) end')
+ assert_equal %w(alias),
+ scan('kw', 'alias a b')
+ assert_equal %w(undef),
+ scan('kw', 'undef public')
+ assert_equal %w(class end),
+ scan('kw', 'class A < Object; end')
+ assert_equal %w(module end),
+ scan('kw', 'module M; end')
+ assert_equal %w(class end),
+ scan('kw', 'class << obj; end')
+ assert_equal %w(BEGIN),
+ scan('kw', 'BEGIN { }')
+ assert_equal %w(END),
+ scan('kw', 'END { }')
+ assert_equal %w(self),
+ scan('kw', 'self.class')
+ assert_equal %w(nil true false),
+ scan('kw', 'p(nil, true, false)')
+ assert_equal %w(__FILE__ __LINE__),
+ scan('kw', 'p __FILE__, __LINE__')
+ assert_equal %w(defined?),
+ scan('kw', 'defined?(Object)')
+ end
+
+ def test_lbrace
+ assert_equal [],
+ scan('lbrace', '')
+ assert_equal ['{'],
+ scan('lbrace', '3.times{ }')
+ assert_equal ['{'],
+ scan('lbrace', '3.times { }')
+ assert_equal ['{'],
+ scan('lbrace', '3.times{}')
+ assert_equal [],
+ scan('lbrace', '"{}"')
+ assert_equal ['{'],
+ scan('lbrace', '{1=>2}')
+ end
+
+ def test_rbrace
+ assert_equal [],
+ scan('rbrace', '')
+ assert_equal ['}'],
+ scan('rbrace', '3.times{ }')
+ assert_equal ['}'],
+ scan('rbrace', '3.times { }')
+ assert_equal ['}'],
+ scan('rbrace', '3.times{}')
+ assert_equal [],
+ scan('rbrace', '"{}"')
+ assert_equal ['}'],
+ scan('rbrace', '{1=>2}')
+ end
+
+ def test_lbracket
+ assert_equal [],
+ scan('lbracket', '')
+ assert_equal ['['],
+ scan('lbracket', '[]')
+ assert_equal ['['],
+ scan('lbracket', 'a[1]')
+ assert_equal [],
+ scan('lbracket', 'm(%q[])')
+ end
+
+ def test_rbracket
+ assert_equal [],
+ scan('rbracket', '')
+ assert_equal [']'],
+ scan('rbracket', '[]')
+ assert_equal [']'],
+ scan('rbracket', 'a[1]')
+ assert_equal [],
+ scan('rbracket', 'm(%q[])')
+ end
+
+ def test_lparen
+ assert_equal [],
+ scan('lparen', '')
+ assert_equal ['('],
+ scan('lparen', '()')
+ assert_equal ['('],
+ scan('lparen', 'm()')
+ assert_equal ['('],
+ scan('lparen', 'm (a)')
+ assert_equal [],
+ scan('lparen', '"()"')
+ assert_equal [],
+ scan('lparen', '"%w()"')
+ end
+
+ def test_rparen
+ assert_equal [],
+ scan('rparen', '')
+ assert_equal [')'],
+ scan('rparen', '()')
+ assert_equal [')'],
+ scan('rparen', 'm()')
+ assert_equal [')'],
+ scan('rparen', 'm (a)')
+ assert_equal [],
+ scan('rparen', '"()"')
+ assert_equal [],
+ scan('rparen', '"%w()"')
+ end
+
+ def test_op
+ assert_equal [],
+ scan('op', '')
+ assert_equal ['|'],
+ scan('op', '1 | 1')
+ assert_equal ['^'],
+ scan('op', '1 ^ 1')
+ assert_equal ['&'],
+ scan('op', '1 & 1')
+ assert_equal ['<=>'],
+ scan('op', '1 <=> 1')
+ assert_equal ['=='],
+ scan('op', '1 == 1')
+ assert_equal ['==='],
+ scan('op', '1 === 1')
+ assert_equal ['=~'],
+ scan('op', '1 =~ 1')
+ assert_equal ['>'],
+ scan('op', '1 > 1')
+ assert_equal ['>='],
+ scan('op', '1 >= 1')
+ assert_equal ['<'],
+ scan('op', '1 < 1')
+ assert_equal ['<='],
+ scan('op', '1 <= 1')
+ assert_equal ['<''<'],
+ scan('op', '1 <''< 1')
+ assert_equal ['>>'],
+ scan('op', '1 >> 1')
+ assert_equal ['+'],
+ scan('op', '1 + 1')
+ assert_equal ['-'],
+ scan('op', '1 - 1')
+ assert_equal ['*'],
+ scan('op', '1 * 1')
+ assert_equal ['/'],
+ scan('op', '1 / 1')
+ assert_equal ['%'],
+ scan('op', '1 % 1')
+ assert_equal ['**'],
+ scan('op', '1 ** 1')
+ assert_equal ['~'],
+ scan('op', '~1')
+ assert_equal ['-'],
+ scan('op', '-a')
+ assert_equal ['+'],
+ scan('op', '+a')
+ assert_equal ['[]'],
+ scan('op', ':[]')
+ assert_equal ['[]='],
+ scan('op', ':[]=')
+ assert_equal [],
+ scan('op', %q[`make all`])
+ end
+
+ def test_symbeg
+ assert_equal [],
+ scan('symbeg', '')
+ assert_equal [':'],
+ scan('symbeg', ':sym')
+ assert_equal [':'],
+ scan('symbeg', '[1,2,3,:sym]')
+ assert_equal [],
+ scan('symbeg', '":sym"')
+ assert_equal [],
+ scan('symbeg', 'a ? b : c')
+ end
+
+ def test_tstring_beg
+ assert_equal [],
+ scan('tstring_beg', '')
+ assert_equal ['"'],
+ scan('tstring_beg', '"abcdef"')
+ assert_equal ['%q['],
+ scan('tstring_beg', '%q[abcdef]')
+ assert_equal ['%Q['],
+ scan('tstring_beg', '%Q[abcdef]')
+ end
+
+ def test_tstring_content
+ assert_equal [],
+ scan('tstring_content', '')
+ assert_equal ['abcdef'],
+ scan('tstring_content', '"abcdef"')
+ assert_equal ['abcdef'],
+ scan('tstring_content', '%q[abcdef]')
+ assert_equal ['abcdef'],
+ scan('tstring_content', '%Q[abcdef]')
+ assert_equal ['abc', 'def'],
+ scan('tstring_content', '"abc#{1}def"')
+ assert_equal ['sym'],
+ scan('tstring_content', ':"sym"')
+ assert_equal ['a b c'],
+ scan('tstring_content', ':"a b c"'),
+ "bug#4544"
+ assert_equal ["a\nb\nc"],
+ scan('tstring_content', ":'a\nb\nc'"),
+ "bug#4544"
+ end
+
+ def test_tstring_end
+ assert_equal [],
+ scan('tstring_end', '')
+ assert_equal ['"'],
+ scan('tstring_end', '"abcdef"')
+ assert_equal [']'],
+ scan('tstring_end', '%q[abcdef]')
+ assert_equal [']'],
+ scan('tstring_end', '%Q[abcdef]')
+ end
+
+ def test_regexp_beg
+ assert_equal [],
+ scan('regexp_beg', '')
+ assert_equal ['/'],
+ scan('regexp_beg', '/re/')
+ assert_equal ['%r<'],
+ scan('regexp_beg', '%r<re>')
+ assert_equal [],
+ scan('regexp_beg', '5 / 5')
+ end
+
+ def test_regexp_end
+ assert_equal [],
+ scan('regexp_end', '')
+ assert_equal ['/'],
+ scan('regexp_end', '/re/')
+ assert_equal ['>'],
+ scan('regexp_end', '%r<re>')
+ end
+
+ def test_words_beg
+ assert_equal [],
+ scan('words_beg', '')
+ assert_equal ['%W('],
+ scan('words_beg', '%W()')
+ assert_equal ['%W('],
+ scan('words_beg', '%W(w w w)')
+ assert_equal ['%W( '],
+ scan('words_beg', '%W( w w w )')
+ end
+
+ def test_qwords_beg
+ assert_equal [],
+ scan('qwords_beg', '')
+ assert_equal ['%w('],
+ scan('qwords_beg', '%w()')
+ assert_equal ['%w('],
+ scan('qwords_beg', '%w(w w w)')
+ assert_equal ['%w( '],
+ scan('qwords_beg', '%w( w w w )')
+ end
+
+ def test_qsymbols_beg
+ assert_equal [],
+ scan('qsymbols_beg', '')
+ assert_equal ['%i('],
+ scan('qsymbols_beg', '%i()')
+ assert_equal ['%i('],
+ scan('qsymbols_beg', '%i(w w w)')
+ assert_equal ['%i( '],
+ scan('qsymbols_beg', '%i( w w w )')
+ end
+
+ def test_symbols_beg
+ assert_equal [],
+ scan('symbols_beg', '')
+ assert_equal ['%I('],
+ scan('symbols_beg', '%I()')
+ assert_equal ['%I('],
+ scan('symbols_beg', '%I(w w w)')
+ assert_equal ['%I( '],
+ scan('symbols_beg', '%I( w w w )')
+ end
+
+ # FIXME: Close paren must not present (`words_end' scanner event?).
+ def test_words_sep
+ assert_equal [],
+ scan('words_sep', '')
+ assert_equal [')'],
+ scan('words_sep', '%w()')
+ assert_equal [' ', ' ', ')'],
+ scan('words_sep', '%w(w w w)')
+ assert_equal [' ', ' ', ' )'],
+ scan('words_sep', '%w( w w w )')
+ assert_equal ["\n", ' ', ' )'],
+ scan('words_sep', "%w( w\nw w )")
+ end
+
+ def test_heredoc_beg
+ assert_equal [],
+ scan('heredoc_beg', '')
+ assert_equal ['<<''EOS'],
+ scan('heredoc_beg', "<<""EOS\nheredoc\nEOS")
+ assert_equal ['<<''EOS'],
+ scan('heredoc_beg', "<<""EOS\nheredoc\nEOS\n")
+ assert_equal ['<<''EOS'],
+ scan('heredoc_beg', "<<""EOS\nheredoc\nEOS \n")
+ assert_equal ['<<''-EOS'],
+ scan('heredoc_beg', "<<""-EOS\nheredoc\n\tEOS \n")
+ assert_equal ['<<''"EOS"'],
+ scan('heredoc_beg', '<<''"EOS"'"\nheredoc\nEOS")
+ assert_equal ["<<""'EOS'"],
+ scan('heredoc_beg', "<<""'EOS'\nheredoc\nEOS")
+ assert_equal ['<<''`EOS`'],
+ scan('heredoc_beg', "<<""`EOS`\nheredoc\nEOS")
+ assert_equal ['<<''" "'],
+ scan('heredoc_beg', '<<''" "'"\nheredoc\nEOS")
+ end
+
+ def test_tstring_content_HEREDOC
+ assert_equal [],
+ scan('tstring_content', '')
+ assert_equal ["heredoc\n"],
+ scan('tstring_content', "<<""EOS\nheredoc\nEOS")
+ assert_equal ["heredoc\n"],
+ scan('tstring_content', "<<""EOS\nheredoc\nEOS\n")
+ assert_equal ["here\ndoc \nEOS \n"],
+ scan('tstring_content', "<<""EOS\nhere\ndoc \nEOS \n")
+ assert_equal ["heredoc\n\tEOS \n"],
+ scan('tstring_content', "<<""-EOS\nheredoc\n\tEOS \n")
+ bug7255 = '[ruby-core:48703]'
+ assert_equal ["there\n""heredoc", "\n"],
+ scan('tstring_content', "<<""EOS\n""there\n""heredoc\#{foo}\nEOS"),
+ bug7255
+ assert_equal ["there\n""heredoc", "\n"],
+ scan('tstring_content', "<<""EOS\n""there\n""heredoc\#@foo\nEOS"),
+ bug7255
+ bug10392 = '[ruby-dev:48647] [Bug #10392]'
+ assert_equal [" E\n\n"],
+ scan('tstring_content', "<<""'E'\n E\n\n"),
+ bug10392
+ end
+
+ def test_heredoc_end
+ assert_equal [],
+ scan('heredoc_end', '')
+ assert_equal ["EOS"],
+ scan('heredoc_end', "<<""EOS\nEOS"),
+ "bug#4543"
+ assert_equal ["EOS"],
+ scan('heredoc_end', "<<""EOS\nheredoc\nEOS")
+ assert_equal ["EOS\n"],
+ scan('heredoc_end', "<<""EOS\nheredoc\nEOS\n")
+ assert_equal [],
+ scan('heredoc_end', "<<""EOS\nheredoc\nEOS \n")
+ assert_equal [],
+ scan('heredoc_end', "<<""-EOS\nheredoc\n\tEOS \n")
+ end
+
+ def test_semicolon
+ assert_equal [],
+ scan('semicolon', '')
+ assert_equal %w(;),
+ scan('semicolon', ';')
+ assert_equal %w(; ;),
+ scan('semicolon', '; ;')
+ assert_equal %w(; ; ;),
+ scan('semicolon', 'nil;nil;nil;')
+ assert_equal %w(; ; ;),
+ scan('semicolon', 'nil;nil;nil;nil')
+ assert_equal [],
+ scan('semicolon', '";"')
+ assert_equal [],
+ scan('semicolon', '%w(;)')
+ assert_equal [],
+ scan('semicolon', '/;/')
+ end
+
+ def test_comment
+ assert_equal [],
+ scan('comment', '')
+ assert_equal ['# comment'],
+ scan('comment', '# comment')
+ assert_equal ["# comment\n"],
+ scan('comment', "# comment\n")
+ assert_equal ["# comment\n"],
+ scan('comment', "# comment\n1 + 1")
+ assert_equal ["# comment\n"],
+ scan('comment', "1 + 1 + 1# comment\n1 + 1")
+ end
+
+ def test_embdoc_beg
+ assert_equal [],
+ scan('embdoc_beg', '')
+ assert_equal ["=begin\n"],
+ scan('embdoc_beg', "=begin\ndoc\n=end")
+ assert_equal ["=begin \n"],
+ scan('embdoc_beg', "=begin \ndoc\n=end\n")
+ assert_equal ["=begin comment\n"],
+ scan('embdoc_beg', "=begin comment\ndoc\n=end\n")
+ end
+
+ def test_embdoc
+ assert_equal [],
+ scan('embdoc', '')
+ assert_equal ["doc\n"],
+ scan('embdoc', "=begin\ndoc\n=end")
+ assert_equal ["doc\n"],
+ scan('embdoc', "=begin\ndoc\n=end\n")
+ end
+
+ def test_embdoc_end
+ assert_equal [],
+ scan('embdoc_end', '')
+ assert_equal ["=end"],
+ scan('embdoc_end', "=begin\ndoc\n=end")
+ assert_equal ["=end\n"],
+ scan('embdoc_end', "=begin\ndoc\n=end\n")
+ end
+
+ def test_sp
+ assert_equal [],
+ scan('sp', '')
+ assert_equal [' '],
+ scan('sp', ' ')
+ assert_equal [' '],
+ scan('sp', ' 1')
+ assert_equal [],
+ scan('sp', "\n")
+ assert_equal [' '],
+ scan('sp', " \n")
+ assert_equal [' ', ' '],
+ scan('sp', "1 + 1")
+ assert_equal [],
+ scan('sp', "' '")
+ assert_equal [],
+ scan('sp', "%w( )")
+ assert_equal [],
+ scan('sp', "%w( w )")
+ assert_equal [],
+ scan('sp', "p(/ /)")
+ end
+
+ # `nl' event always means End-Of-Statement.
+ def test_nl
+ assert_equal [],
+ scan('nl', '')
+ assert_equal [],
+ scan('nl', "\n")
+ assert_equal ["\n"],
+ scan('nl', "1 + 1\n")
+ assert_equal ["\n", "\n"],
+ scan('nl', "1 + 1\n2 + 2\n")
+ assert_equal [],
+ scan('nl', "1 +\n1")
+ assert_equal [],
+ scan('nl', "1;\n")
+ assert_equal ["\r\n"],
+ scan('nl', "1 + 1\r\n")
+ assert_equal [],
+ scan('nl', "1;\r\n")
+ end
+
+ def test_ignored_nl
+ assert_equal [],
+ scan('ignored_nl', '')
+ assert_equal ["\n"],
+ scan('ignored_nl', "\n")
+ assert_equal [],
+ scan('ignored_nl', "1 + 1\n")
+ assert_equal [],
+ scan('ignored_nl', "1 + 1\n2 + 2\n")
+ assert_equal ["\n"],
+ scan('ignored_nl', "1 +\n1")
+ assert_equal ["\n"],
+ scan('ignored_nl', "1;\n")
+ assert_equal [],
+ scan('ignored_nl', "1 + 1\r\n")
+ assert_equal ["\r\n"],
+ scan('ignored_nl', "1;\r\n")
+ end
+
+ def test___end__
+ assert_equal [],
+ scan('__end__', "")
+ assert_equal ["__END__"],
+ scan('__end__', "__END__")
+ assert_equal ["__END__\n"],
+ scan('__end__', "__END__\n")
+ assert_equal ["__END__\n"],
+ Ripper.tokenize("__END__\njunk junk junk")
+ assert_equal ["__END__"],
+ scan('__end__', "1\n__END__")
+ assert_equal [],
+ scan('__end__', "print('__END__')")
+ end
+
+ def test_CHAR
+ assert_equal [],
+ scan('CHAR', "")
+ assert_equal ["?a"],
+ scan('CHAR', "?a")
+ assert_equal [],
+ scan('CHAR', "@ivar")
+ end
+
+ def test_label
+ assert_equal %w(foo:),
+ scan('label', '{foo: 1}')
+ end
+
+ def test_label_end
+ assert_equal %w(":),
+ scan('label_end', '{"foo-bar": 1}')
+ end
+
+ def test_tlambda
+ assert_equal %w(->),
+ scan('tlambda', '->{}')
+ end
+
+ def test_tlambeg
+ assert_equal %w({),
+ scan('tlambeg', '-> {}')
+ end
+
+ def test_tlambda_arg
+ assert_equal %w(),
+ scan('tlambda_arg', '-> {}')
+ end
+
+end if ripper_test
diff --git a/jni/ruby/test/ripper/test_sexp.rb b/jni/ruby/test/ripper/test_sexp.rb
new file mode 100644
index 0000000..2c5bcda
--- /dev/null
+++ b/jni/ruby/test/ripper/test_sexp.rb
@@ -0,0 +1,44 @@
+begin
+ require 'ripper'
+ require 'test/unit'
+ ripper_test = true
+ module TestRipper; end
+rescue LoadError
+end
+
+class TestRipper::Sexp < Test::Unit::TestCase
+ def test_compile_error
+ assert_nil Ripper.sexp("/")
+ assert_nil Ripper.sexp("-")
+ assert_nil Ripper.sexp("+")
+ assert_nil Ripper.sexp("*")
+ assert_nil Ripper.sexp("end")
+ assert_nil Ripper.sexp("end 1")
+ assert_nil Ripper.sexp("/*")
+ assert_nil Ripper.sexp("/*/")
+ assert_nil Ripper.sexp("/+/")
+ end
+
+ def test_regexp_content
+ sexp = Ripper.sexp('//')
+ assert_nil search_sexp(:@tstring_content, search_sexp(:regexp_literal, sexp))
+
+ sexp = Ripper.sexp('/foo/')
+ assert_equal 'foo', search_sexp(:@tstring_content, search_sexp(:regexp_literal, sexp))[1]
+
+ sexp = Ripper.sexp("/foo\nbar/")
+ assert_equal "foo\nbar", search_sexp(:@tstring_content, search_sexp(:regexp_literal, sexp))[1]
+
+ sexp = Ripper.sexp('/(?<n>a(b|\g<n>))/')
+ assert_equal '(?<n>a(b|\g<n>))', search_sexp(:@tstring_content, search_sexp(:regexp_literal, sexp))[1]
+ end
+
+ def search_sexp(sym, sexp)
+ return sexp if !sexp or sexp[0] == sym
+ sexp.find do |e|
+ if Array === e and e = search_sexp(sym, e)
+ return e
+ end
+ end
+ end
+end if ripper_test
diff --git a/jni/ruby/test/rss/dot.png b/jni/ruby/test/rss/dot.png
new file mode 100644
index 0000000..9c6960f
--- /dev/null
+++ b/jni/ruby/test/rss/dot.png
Binary files differ
diff --git a/jni/ruby/test/rss/rss-assertions.rb b/jni/ruby/test/rss/rss-assertions.rb
new file mode 100644
index 0000000..f2e9dc0
--- /dev/null
+++ b/jni/ruby/test/rss/rss-assertions.rb
@@ -0,0 +1,2090 @@
+require 'erb'
+
+module RSS
+ module Assertions
+ def _wrap_assertion
+ yield
+ end
+
+ def assert_parse(rss, assert_method, *args)
+ __send__("assert_#{assert_method}", *args) do
+ ::RSS::Parser.parse(rss)
+ end
+ __send__("assert_#{assert_method}", *args) do
+ ::RSS::Parser.parse(rss, false).validate
+ end
+ end
+
+ def assert_ns(prefix, uri)
+ _wrap_assertion do
+ begin
+ yield
+ flunk("Not raise NSError")
+ rescue ::RSS::NSError => e
+ assert_equal(prefix, e.prefix)
+ assert_equal(uri, e.uri)
+ end
+ end
+ end
+
+ def assert_missing_tag(tag, parent)
+ _wrap_assertion do
+ begin
+ yield
+ flunk("Not raise MissingTagError")
+ rescue ::RSS::MissingTagError => e
+ assert_equal(tag, e.tag)
+ assert_equal(parent, e.parent)
+ end
+ end
+ end
+
+ def assert_too_much_tag(tag, parent)
+ _wrap_assertion do
+ begin
+ yield
+ flunk("Not raise TooMuchTagError")
+ rescue ::RSS::TooMuchTagError => e
+ assert_equal(tag, e.tag)
+ assert_equal(parent, e.parent)
+ end
+ end
+ end
+
+ def assert_missing_attribute(tag, attrname)
+ _wrap_assertion do
+ begin
+ yield
+ flunk("Not raise MissingAttributeError")
+ rescue ::RSS::MissingAttributeError => e
+ assert_equal(tag, e.tag)
+ assert_equal(attrname, e.attribute)
+ end
+ end
+ end
+
+ def assert_not_expected_tag(tag, uri, parent)
+ _wrap_assertion do
+ begin
+ yield
+ flunk("Not raise NotExpectedTagError")
+ rescue ::RSS::NotExpectedTagError => e
+ assert_equal(tag, e.tag)
+ assert_equal(uri, e.uri)
+ assert_equal(parent, e.parent)
+ end
+ end
+ end
+
+ def assert_not_available_value(tag, value, attribute=nil)
+ _wrap_assertion do
+ begin
+ yield
+ flunk("Not raise NotAvailableValueError")
+ rescue ::RSS::NotAvailableValueError => e
+ assert_equal(tag, e.tag)
+ assert_equal(value, e.value)
+ assert_equal(attribute, e.attribute)
+ end
+ end
+ end
+
+ def assert_not_set_error(name, variables)
+ _wrap_assertion do
+ begin
+ yield
+ flunk("Not raise NotSetError")
+ rescue ::RSS::NotSetError => e
+ assert_equal(name, e.name)
+ assert_kind_of(Array, variables)
+ assert_equal(variables.sort, e.variables.sort)
+ end
+ end
+ end
+
+ def assert_xml_declaration(version, encoding, standalone, rss)
+ _wrap_assertion do
+ assert_equal(version, rss.version)
+ assert_equal(encoding, rss.encoding)
+ assert_equal(standalone, rss.standalone)
+ end
+ end
+
+ def assert_xml_stylesheet_attrs(attrs, xsl)
+ _wrap_assertion do
+ n_attrs = normalized_attrs(attrs)
+ ::RSS::XMLStyleSheet::ATTRIBUTES.each do |name|
+ assert_equal(n_attrs[name], xsl.__send__(name))
+ end
+ end
+ end
+
+ def assert_xml_stylesheet(target, attrs, xsl)
+ _wrap_assertion do
+ if attrs.has_key?(:href)
+ if !attrs.has_key?(:type) and attrs.has_key?(:guess_type)
+ attrs[:type] = attrs[:guess_type]
+ end
+ assert_equal("xml-stylesheet", target)
+ assert_xml_stylesheet_attrs(attrs, xsl)
+ else
+ assert_nil(target)
+ assert_equal("", xsl.to_s)
+ end
+ end
+ end
+
+ def assert_xml_stylesheet_pis(attrs_ary, rss=nil)
+ _wrap_assertion do
+ if rss.nil?
+ rss = ::RSS::RDF.new
+ setup_rss10(rss)
+ end
+ xss_strs = []
+ attrs_ary.each do |attrs|
+ xss = ::RSS::XMLStyleSheet.new(attrs)
+ xss_strs.push(xss.to_s)
+ rss.xml_stylesheets.push(xss)
+ end
+ pi_str = rss.to_s.gsub(/<\?xml .*\n/, "").gsub(/\s*<[^\?].*\z/m, "")
+ assert_equal(xss_strs.join("\n"), pi_str)
+ end
+ end
+
+ def assert_xml_stylesheets(attrs, xss)
+ _wrap_assertion do
+ xss.each_with_index do |xs, i|
+ assert_xml_stylesheet_attrs(attrs[i], xs)
+ end
+ end
+ end
+
+
+ def assert_atom_person(tag_name, generator)
+ _wrap_assertion do
+ name = "Mark Pilgrim"
+ uri = "http://example.org/"
+ email = "f8dy@example.com"
+
+ assert_parse(generator.call(<<-EOA), :missing_tag, "name", tag_name)
+ <#{tag_name}/>
+EOA
+
+ assert_parse(generator.call(<<-EOA), :missing_tag, "name", tag_name)
+ <#{tag_name}>
+ <uri>#{uri}</uri>
+ <email>#{email}</email>
+ </#{tag_name}>
+EOA
+
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <#{tag_name}>
+ <name>#{name}</name>
+ </#{tag_name}>
+EOA
+
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <#{tag_name}>
+ <name>#{name}</name>
+ <uri>#{uri}</uri>
+ <email>#{email}</email>
+ </#{tag_name}>
+EOA
+
+ person = yield(feed)
+ assert_not_nil(person)
+ assert_equal(name, person.name.content)
+ assert_equal(uri, person.uri.content)
+ assert_equal(email, person.email.content)
+ end
+ end
+
+ def assert_atom_category(generator)
+ _wrap_assertion do
+ term = "Music"
+ scheme = "http://xmlns.com/wordnet/1.6/"
+ label = "music"
+
+ missing_args = [:missing_attribute, "category", "term"]
+ assert_parse(generator.call(<<-EOA), *missing_args)
+ <category/>
+EOA
+
+ assert_parse(generator.call(<<-EOA), *missing_args)
+ <category scheme="#{scheme}" label="#{label}"/>
+EOA
+
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <category term="#{term}"/>
+EOA
+
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <category term="#{term}" scheme="#{scheme}" label="#{label}"/>
+EOA
+
+ category = yield(feed)
+ assert_not_nil(category)
+ assert_equal(term, category.term)
+ assert_equal(scheme, category.scheme)
+ assert_equal(label, category.label)
+ end
+ end
+
+ def assert_atom_link(generator)
+ _wrap_assertion do
+ href = "http://example.org/feed.atom"
+ rel = "self"
+ type = "application/atom+xml"
+ hreflang = "en"
+ title = "Atom"
+ length = "1024"
+
+ assert_parse(generator.call(<<-EOA), :missing_attribute, "link", "href")
+ <link/>
+EOA
+
+ assert_parse(generator.call(<<-EOA), :missing_attribute, "link", "href")
+ <link rel="#{rel}" type="#{type}" hreflang="#{hreflang}"
+ title="#{title}" length="#{length}"/>
+EOA
+
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <link href="#{href}"/>
+EOA
+
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <link href="#{href}" rel="#{rel}" type="#{type}" hreflang="#{hreflang}"
+ title="#{title}" length="#{length}"/>
+EOA
+
+ link = yield(feed)
+ assert_not_nil(link)
+ assert_equal(href, link.href)
+ assert_equal(rel, link.rel)
+ assert_equal(type, link.type)
+ assert_equal(hreflang, link.hreflang)
+ assert_equal(title, link.title)
+ assert_equal(length, link.length)
+
+
+ href = "http://example.org/index.html.ja"
+ parent = link.parent.tag_name
+ return if parent == "source"
+
+ optional_attributes = %w(hreflang="ja" type="text/html")
+ 0.upto(optional_attributes.size) do |i|
+ combination(optional_attributes, i).each do |attributes|
+ attrs = attributes.join(" ")
+ assert_parse(generator.call(<<-EOA), :too_much_tag, "link", parent)
+ <link rel="alternate" #{attrs} href="#{href}"/>
+ <link rel="alternate" #{attrs} href="#{href}"/>
+EOA
+ end
+ end
+ end
+ end
+
+ def assert_atom_generator(generator)
+ _wrap_assertion do
+ uri = "http://www.example.com/"
+ version = "1.0"
+ content = "Example Toolkit"
+
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <generator/>
+EOA
+
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <generator uri="#{uri}" version="#{version}"/>
+EOA
+
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <generator uri="#{uri}" version="#{version}">#{content}</generator>
+EOA
+
+ gen = yield(feed)
+ assert_not_nil(gen)
+ assert_equal(uri, gen.uri)
+ assert_equal(version, gen.version)
+ assert_equal(content, gen.content)
+ end
+ end
+
+ def assert_atom_icon(generator)
+ _wrap_assertion do
+ content = "http://www.example.com/example.png"
+
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <icon/>
+EOA
+
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <icon>#{content}</icon>
+EOA
+
+ icon = yield(feed)
+ assert_not_nil(icon)
+ assert_equal(content, icon.content)
+ end
+ end
+
+ def assert_atom_text_construct(tag_name, generator)
+ _wrap_assertion do
+ [nil, "text", "html"].each do |type|
+ attr = ""
+ attr = " type=\"#{type}\"" if type
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <#{tag_name}#{attr}/>
+EOA
+ end
+
+ assert_parse(generator.call(<<-EOA), :missing_tag, "div", tag_name)
+ <#{tag_name} type="xhtml"/>
+EOA
+
+ args = ["x", Atom::URI, tag_name]
+ assert_parse(generator.call(<<-EOA), :not_expected_tag, *args)
+ <#{tag_name} type="xhtml"><x/></#{tag_name}>
+EOA
+
+ invalid_value = "invalid"
+ args = ["type", invalid_value]
+ assert_parse(generator.call(<<-EOA), :not_available_value, *args)
+ <#{tag_name} type="#{invalid_value}"/>
+EOA
+
+ [
+ [nil, "A lot of effort went into making this effortless"],
+ ["text", "A lot of effort went into making this effortless"],
+ ["html", "A <em>lot</em> of effort went into making this effortless"],
+ ].each do |type, content|
+ attr = ""
+ attr = " type=\"#{type}\"" if type
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <#{tag_name}#{attr}>#{h content}</#{tag_name}>
+EOA
+
+ element = yield(feed)
+ assert_not_nil(element)
+ assert_equal(type, element.type)
+ assert_equal(content, element.content)
+ end
+
+ [false, true].each do |with_space|
+ xhtml_uri = "http://www.w3.org/1999/xhtml"
+ xhtml_content = "<div xmlns=\"#{xhtml_uri}\">abc</div>"
+ xhtml_element = RSS::XML::Element.new("div", nil, xhtml_uri,
+ {"xmlns" => xhtml_uri},
+ ["abc"])
+ content = xhtml_content
+ content = " #{content} " if with_space
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <#{tag_name} type="xhtml">#{content}</#{tag_name}>
+EOA
+
+ element = yield(feed)
+ assert_not_nil(element)
+ assert_equal("xhtml", element.type)
+ assert_equal(xhtml_content, element.content)
+ assert_equal(xhtml_element, element.xhtml)
+ end
+ end
+ end
+
+ def assert_atom_date_construct(tag_name, generator)
+ _wrap_assertion do
+ args = [tag_name, ""]
+ assert_parse(generator.call(<<-EOR), :not_available_value, *args)
+ <#{tag_name}/>
+EOR
+
+ [
+ ["xxx", false],
+ ["2007", false],
+ ["2007/02/09", true],
+ ].each do |invalid_value, can_parse|
+ assert_not_available_value(tag_name, invalid_value) do
+ RSS::Parser.parse(generator.call(<<-EOR))
+ <#{tag_name}>#{invalid_value}</#{tag_name}>
+EOR
+ end
+
+ feed = RSS::Parser.parse(generator.call(<<-EOR), false)
+ <#{tag_name}>#{invalid_value}</#{tag_name}>
+EOR
+ value = yield(feed).content
+ if can_parse
+ assert_equal(Time.parse(invalid_value), value)
+ else
+ assert_nil(value)
+ end
+ end
+
+ [
+ "2003-12-13T18:30:02Z",
+ "2003-12-13T18:30:02.25Z",
+ "2003-12-13T18:30:02+01:00",
+ "2003-12-13T18:30:02.25+01:00",
+ ].each do |valid_value|
+ assert_parse(generator.call(<<-EOR), :nothing_raised)
+ <#{tag_name}>#{valid_value}</#{tag_name}>
+EOR
+
+ feed = RSS::Parser.parse(generator.call(<<-EOR))
+ <#{tag_name}>#{valid_value}</#{tag_name}>
+EOR
+ assert_equal(Time.parse(valid_value), yield(feed).content)
+ end
+ end
+ end
+
+ def assert_atom_logo(generator)
+ _wrap_assertion do
+ content = "http://www.example.com/example.png"
+
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <logo/>
+EOA
+
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <logo>#{content}</logo>
+EOA
+
+ logo = yield(feed)
+ assert_not_nil(logo)
+ assert_equal(content, logo.content)
+ end
+ end
+
+ def assert_atom_content(generator, &getter)
+ _wrap_assertion do
+ assert_atom_content_inline_text(generator, &getter)
+ assert_atom_content_inline_xhtml(generator, &getter)
+ assert_atom_content_inline_other(generator, &getter)
+ assert_atom_content_out_of_line(generator, &getter)
+ end
+ end
+
+ def assert_atom_content_inline_text(generator)
+ _wrap_assertion do
+ [nil, "text", "html"].each do |type|
+ content = "<content"
+ content << " type='#{type}'" if type
+
+ suffix = "/>"
+ assert_parse(generator.call(content + suffix), :nothing_raised)
+ suffix = ">xxx</content>"
+ assert_parse(generator.call(content + suffix), :nothing_raised)
+ end
+
+ [
+ ["text", "sample content"],
+ ["text/plain", "sample content"],
+ ["html", "<em>sample</em> content"]
+ ].each do |type, content_content|
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <content type="#{type}">#{h content_content}</content>
+EOA
+ content = yield(feed)
+ assert_equal(type, content.type)
+ if %w(text html).include?(type)
+ assert(content.inline_text?)
+ else
+ assert(!content.inline_text?)
+ end
+ if type == "html"
+ assert(content.inline_html?)
+ else
+ assert(!content.inline_html?)
+ end
+ assert(!content.inline_xhtml?)
+ if type == "text/plain"
+ assert(content.inline_other?)
+ assert(content.inline_other_text?)
+ else
+ assert(!content.inline_other?)
+ assert(!content.inline_other_text?)
+ end
+ assert(!content.inline_other_xml?)
+ assert(!content.inline_other_base64?)
+ assert(!content.out_of_line?)
+ assert(!content.have_xml_content?)
+ assert_equal(content_content, content.content)
+ end
+ end
+ end
+
+ def assert_atom_content_inline_xhtml(generator)
+ _wrap_assertion do
+ args = ["div", "content"]
+ assert_parse(generator.call(<<-EOA), :missing_tag, *args)
+ <content type="xhtml"/>
+EOA
+
+ args = ["x", Atom::URI, "content"]
+ assert_parse(generator.call(<<-EOA), :not_expected_tag, *args)
+ <content type="xhtml"><x/></content>
+EOA
+
+ xhtml_uri = "http://www.w3.org/1999/xhtml"
+ xhtml_content = "<div xmlns=\"#{xhtml_uri}\">abc</div>"
+ xhtml_element = RSS::XML::Element.new("div", nil, xhtml_uri,
+ {"xmlns" => xhtml_uri},
+ ["abc"])
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <content type="xhtml">#{xhtml_content}</content>
+EOA
+
+ content = yield(feed)
+ assert_not_nil(content)
+ assert_equal("xhtml", content.type)
+ assert(!content.inline_text?)
+ assert(!content.inline_html?)
+ assert(content.inline_xhtml?)
+ assert(!content.inline_other?)
+ assert(!content.inline_other_text?)
+ assert(!content.inline_other_xml?)
+ assert(!content.inline_other_base64?)
+ assert(!content.out_of_line?)
+ assert(content.have_xml_content?)
+ assert_equal(xhtml_content, content.content)
+ assert_equal(xhtml_element, content.xhtml)
+ end
+ end
+
+ def assert_atom_content_inline_other(generator, &getter)
+ _wrap_assertion do
+ assert_atom_content_inline_other_text(generator, &getter)
+ assert_atom_content_inline_other_xml(generator, &getter)
+ end
+ end
+
+ def assert_atom_content_inline_other_text(generator)
+ _wrap_assertion do
+ type = "image/png"
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <content type="#{type}"/>
+EOA
+
+ png_file = File.join(File.dirname(__FILE__), "dot.png")
+ png = File.open(png_file, "rb") do |file|
+ file.read.force_encoding("binary")
+ end
+ base64_content = [png].pack("m").delete("\n")
+
+ [false, true].each do |with_space|
+ xml_content = base64_content
+ xml_content = " #{base64_content}" if with_space
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <content type="#{type}">#{xml_content}</content>
+EOA
+
+ content = yield(feed)
+ assert_not_nil(content)
+ assert_equal(type, content.type)
+ assert(!content.inline_text?)
+ assert(!content.inline_html?)
+ assert(!content.inline_xhtml?)
+ assert(content.inline_other?)
+ assert(!content.inline_other_text?)
+ assert(!content.inline_other_xml?)
+ assert(content.inline_other_base64?)
+ assert(!content.out_of_line?)
+ assert(!content.have_xml_content?)
+ assert_equal(png, content.content)
+
+ xml = REXML::Document.new(content.to_s).root
+ assert_rexml_element([], {"type" => type}, base64_content, xml)
+ end
+ end
+ end
+
+ def assert_atom_content_inline_other_xml(generator)
+ _wrap_assertion do
+ type = "image/svg+xml"
+
+ assert_parse(generator.call(<<-EOA), :nothing_raised)
+ <content type="#{type}"/>
+EOA
+
+ svg_uri = "http://www.w3.org/2000/svg"
+ svg_width = "50pt"
+ svg_height = "20pt"
+ svg_version = "1.0"
+ text_x = "15"
+ text_y = "15"
+ text = "text"
+ svg_content = <<-EOS
+<svg
+ xmlns="#{svg_uri}"
+ width="#{svg_width}"
+ height="#{svg_height}"
+ version="#{svg_version}"
+><text x="#{text_x}" y="#{text_y}">#{text}</text
+></svg>
+EOS
+
+ text_element = RSS::XML::Element.new("text", nil, svg_uri,
+ {
+ "x" => text_x,
+ "y" => text_y,
+ },
+ [text])
+ svg_element = RSS::XML::Element.new("svg", nil, svg_uri,
+ {
+ "xmlns" => svg_uri,
+ "width" => svg_width,
+ "height" => svg_height,
+ "version" => svg_version,
+ },
+ [text_element])
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <content type="#{type}">#{svg_content}</content>
+EOA
+
+ content = yield(feed)
+ assert_not_nil(content)
+ assert_equal(type, content.type)
+ assert(!content.inline_text?)
+ assert(!content.inline_html?)
+ assert(!content.inline_xhtml?)
+ assert(content.inline_other?)
+ assert(!content.inline_other_text?)
+ assert(content.inline_other_xml?)
+ assert(!content.inline_other_base64?)
+ assert(!content.out_of_line?)
+ assert(content.have_xml_content?)
+ assert_equal(REXML::Document.new(svg_content).to_s.chomp,
+ REXML::Document.new(content.content).to_s.chomp)
+ assert_equal(svg_element, content.xml)
+ assert_nil(content.xhtml)
+ end
+ end
+
+ def assert_atom_content_out_of_line(generator)
+ _wrap_assertion do
+ text_type = "text/plain"
+ text_src = "http://example.com/README.txt"
+
+ missing_args = [:missing_attribute, "content", "type"]
+ # RSS Parser raises error even if this is "should" not "must".
+ assert_parse(generator.call(<<-EOA), *missing_args)
+ <content src="#{text_src}"/>
+EOA
+
+ content_content = "xxx"
+ not_available_value_args = [:not_available_value,
+ "content", content_content]
+ assert_parse(generator.call(<<-EOA), *not_available_value_args)
+ <content type="#{text_type}" src="#{text_src}">#{content_content}</content>
+EOA
+
+ feed = RSS::Parser.parse(generator.call(<<-EOA))
+ <content type="#{text_type}" src="#{text_src}"/>
+EOA
+ content = yield(feed)
+ assert_not_nil(content)
+ assert_equal(text_type, content.type)
+ assert_equal(text_src, content.src)
+ assert(!content.inline_text?)
+ assert(!content.inline_html?)
+ assert(!content.inline_xhtml?)
+ assert(!content.inline_other?)
+ assert(!content.inline_other_text?)
+ assert(!content.inline_other_xml?)
+ assert(!content.inline_other_base64?)
+ assert(content.out_of_line?)
+ assert(!content.have_xml_content?)
+ assert_nil(content.xml)
+ assert_nil(content.xhtml)
+ assert_equal("", content.content)
+ end
+ end
+
+ def assert_atom_source(generator, &getter)
+ _wrap_assertion do
+ assert_atom_source_author(generator, &getter)
+ assert_atom_source_category(generator, &getter)
+ assert_atom_source_contributor(generator, &getter)
+ assert_atom_source_generator(generator, &getter)
+ assert_atom_source_icon(generator, &getter)
+ assert_atom_source_id(generator, &getter)
+ assert_atom_source_link(generator, &getter)
+ assert_atom_source_logo(generator, &getter)
+ assert_atom_source_rights(generator, &getter)
+ assert_atom_source_subtitle(generator, &getter)
+ assert_atom_source_title(generator, &getter)
+ assert_atom_source_updated(generator, &getter)
+ end
+ end
+
+ def assert_atom_source_author(generator)
+ assert_atom_person("author", generator) do |feed|
+ source = yield(feed)
+ assert_equal(1, source.authors.size)
+ source.author
+ end
+ end
+
+ def assert_atom_source_category(generator)
+ assert_atom_category(generator) do |feed|
+ source = yield(feed)
+ assert_equal(1, source.categories.size)
+ source.category
+ end
+ end
+
+ def assert_atom_source_contributor(generator)
+ assert_atom_person("contributor", generator) do |feed|
+ source = yield(feed)
+ assert_equal(1, source.contributors.size)
+ source.contributor
+ end
+ end
+
+ def assert_atom_source_generator(generator)
+ assert_atom_generator(generator) do |feed|
+ yield(feed).generator
+ end
+ end
+
+ def assert_atom_source_icon(generator)
+ assert_atom_icon(generator) do |feed|
+ yield(feed).icon
+ end
+ end
+
+ def assert_atom_source_id(generator)
+ id_content = "urn:uuid:a2fb588b-5674-4898-b420-265a734fea69"
+ id = "<id>#{id_content}</id>"
+ feed = RSS::Parser.parse(generator.call(id))
+ assert_equal(id_content, yield(feed).id.content)
+ end
+
+ def assert_atom_source_link(generator)
+ assert_atom_link(generator) do |feed|
+ source = yield(feed)
+ assert_equal(1, source.links.size)
+ source.link
+ end
+ end
+
+ def assert_atom_source_logo(generator)
+ assert_atom_logo(generator) do |feed|
+ yield(feed).logo
+ end
+ end
+
+ def assert_atom_source_rights(generator)
+ assert_atom_text_construct("rights", generator) do |feed|
+ yield(feed).rights
+ end
+ end
+
+ def assert_atom_source_subtitle(generator)
+ assert_atom_text_construct("subtitle", generator) do |feed|
+ yield(feed).subtitle
+ end
+ end
+
+ def assert_atom_source_title(generator)
+ assert_atom_text_construct("title", generator) do |feed|
+ yield(feed).title
+ end
+ end
+
+ def assert_atom_source_updated(generator)
+ assert_atom_date_construct("updated", generator) do |feed|
+ yield(feed).updated
+ end
+ end
+
+ def assert_dublin_core(elems, target)
+ _wrap_assertion do
+ elems.each do |name, value|
+ assert_equal(value, target.__send__("dc_#{name}"))
+ end
+ end
+ end
+
+ def assert_multiple_dublin_core(elems, target)
+ _wrap_assertion do
+ elems.each do |name, values, plural|
+ plural ||= "#{name}s"
+ actual = target.__send__("dc_#{plural}").collect{|x| x.value}
+ assert_equal(values, actual)
+ end
+ end
+ end
+
+ def assert_syndication(elems, target)
+ _wrap_assertion do
+ elems.each do |name, value|
+ meth = "sy_#{name}"
+ value = value.to_i if meth == "sy_updateFrequency"
+ assert_equal(value, target.__send__(meth ))
+ end
+ end
+ end
+
+ def assert_content(elems, target)
+ _wrap_assertion do
+ elems.each do |name, value|
+ assert_equal(value, target.__send__("content_#{name}"))
+ end
+ end
+ end
+
+ def assert_trackback(attrs, target)
+ _wrap_assertion do
+ n_attrs = normalized_attrs(attrs)
+ if n_attrs["ping"]
+ assert_equal(n_attrs["ping"], target.trackback_ping)
+ end
+ if n_attrs["abouts"]
+ n_attrs["abouts"].each_with_index do |about, i|
+ assert_equal(about, target.trackback_abouts[i].value)
+ end
+ end
+ end
+ end
+
+ def assert_taxo_topic(topics, target)
+ _wrap_assertion do
+ topics.each_with_index do |topic, i|
+ taxo_topic = target.taxo_topics[i]
+ topic.each do |name, value|
+ case name
+ when :link
+ assert_equal(value, taxo_topic.about)
+ assert_equal(value, taxo_topic.taxo_link)
+ when :topics
+ assert_equal(value, taxo_topic.taxo_topics.resources)
+ else
+ assert_equal(value, taxo_topic.__send__("dc_#{name}"))
+ end
+ end
+ end
+ end
+ end
+
+
+ def assert_attributes(attrs, names, target)
+ _wrap_assertion do
+ n_attrs = normalized_attrs(attrs)
+ names.each do |info|
+ if info.is_a?(String)
+ name = info
+ type = nil
+ else
+ name, type = info
+ end
+ value = n_attrs[name]
+ if value.is_a?(Time)
+ actual = target.__send__(name)
+ assert_instance_of(Time, actual)
+ assert_equal(value.to_i, actual.to_i)
+ elsif value
+ case type
+ when :integer
+ value = value.to_i
+ when :boolean
+ value = value == "true" if value.is_a?(String)
+ end
+ assert_equal(value, target.__send__(name))
+ end
+ end
+ end
+ end
+
+ def assert_rexml_element(children, attrs, text, element, text_type=nil)
+ _wrap_assertion do
+ if children
+ children_info = element.elements.collect {|e| [e.namespace, e.name]}
+ assert_equal(children.collect {|uri, name| [uri, name]}.sort,
+ children_info.sort)
+ end
+ if attrs
+ assert_equal(attrs.collect {|k, v| [k, v]}.sort,
+ element.attributes.collect {|k, v| [k, v]}.sort)
+ end
+ case text_type
+ when :time
+ assert_not_nil(element.text)
+ assert_equal(Time.parse(text).to_s, Time.parse(element.text).to_s)
+ else
+ assert_equal(text, element.text)
+ end
+ end
+ end
+
+ def _assert_maker_atom_persons(feed_type, maker_readers, feed_readers)
+ _wrap_assertion do
+ persons = []
+ feed = RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ targets.each do |target|
+ person = {
+ :name => target.name,
+ :uri => target.uri,
+ :email => target.email,
+ }
+ persons << person if person[:name]
+ end
+ end
+
+ actual_persons = chain_reader(feed, feed_readers) || []
+ actual_persons = actual_persons.collect do |person|
+ {
+ :name => person.name ? person.name.content : nil,
+ :uri => person.uri ? person.uri.content : nil,
+ :email => person.email ? person.email.content : nil,
+ }
+ end
+ assert_equal(persons, actual_persons)
+ end
+ end
+
+ def assert_maker_atom_persons(feed_type, maker_readers, feed_readers,
+ not_set_error_name=nil,
+ parent_not_set_error_name=nil,
+ parent_not_set_variable=nil)
+ _wrap_assertion do
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+
+ args = [feed_type, maker_readers, feed_readers]
+ if parent_not_set_error_name or parent_not_set_variable
+ assert_not_set_error(parent_not_set_error_name,
+ parent_not_set_variable) do
+ _assert_maker_atom_persons(*args) do |maker|
+ yield maker
+ end
+ end
+ else
+ _assert_maker_atom_persons(*args) do |maker|
+ yield maker
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(name)) do
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ targets.new_child
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(name)) do
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ target = targets.new_child
+ target.uri = "http://example.com/~me/"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(name)) do
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ target = targets.new_child
+ target.email = "me@example.com"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(name)) do
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ target = targets.new_child
+ target.uri = "http://example.com/~me/"
+ target.email = "me@example.com"
+ end
+ end
+
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ target = targets.new_child
+ target.name = "me"
+ end
+
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ target = targets.new_child
+ target.name = "me"
+ target.uri = "http://example.com/~me/"
+ end
+
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ target = targets.new_child
+ target.name = "me"
+ target.email = "me@example.com"
+ end
+
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ target = targets.new_child
+ target.name = "me"
+ target.uri = "http://example.com/~me/"
+ target.email = "me@example.com"
+ end
+
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+
+ target = targets.new_child
+ target.name = "me"
+ target.uri = "http://example.com/~me/"
+ target.email = "me@example.com"
+
+ target = targets.new_child
+ target.name = "you"
+ target.uri = "http://example.com/~you/"
+ target.email = "you@example.com"
+ end
+
+ assert_not_set_error(not_set_error_name, %w(name)) do
+ _assert_maker_atom_persons(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+
+ target = targets.new_child
+ target.name = "me"
+ target.uri = "http://example.com/~me/"
+ target.email = "me@example.com"
+
+ targets.new_child
+ end
+ end
+ end
+ end
+
+ def _assert_maker_atom_text_construct(feed_type, maker_readers,
+ feed_readers, &block)
+ maker_extractor = Proc.new do |target|
+ text = {
+ :type => target.type,
+ :content => target.content,
+ :xml_content => target.xml_content,
+ }
+ if text[:type] == "xhtml"
+ if text[:xml_content]
+ xml_content = text[:xml_content]
+ xhtml_uri = "http://www.w3.org/1999/xhtml"
+ unless xml_content.is_a?(RSS::XML::Element) and
+ ["div", xhtml_uri] == [xml_content.name, xml_content.uri]
+ children = xml_content
+ children = [children] unless children.is_a?(Array)
+ xml_content = RSS::XML::Element.new("div", nil, xhtml_uri,
+ {"xmlns" => xhtml_uri},
+ children)
+ text[:xml_content] = xml_content
+ end
+ text
+ else
+ nil
+ end
+ else
+ text[:content] ? text : nil
+ end
+ end
+ feed_extractor = Proc.new do |target|
+ {
+ :type => target.type,
+ :content => target.content,
+ :xml_content => target.xhtml,
+ }
+ end
+ _assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ &block)
+ end
+
+ def assert_maker_atom_text_construct(feed_type, maker_readers, feed_readers,
+ parent_not_set_error_name=nil,
+ parent_not_set_variable=nil,
+ not_set_error_name=nil)
+ _wrap_assertion do
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+
+ args = [feed_type, maker_readers, feed_readers]
+ if parent_not_set_error_name or parent_not_set_variable
+ assert_not_set_error(parent_not_set_error_name,
+ parent_not_set_variable) do
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ end
+ end
+ else
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(content)) do
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ target.type = "text"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(content)) do
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ target.type = "html"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(xml_content)) do
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ target.type = "xhtml"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(xml_content)) do
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ target.type = "xhtml"
+ target.content = "Content"
+ end
+ end
+
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ target.type = "text"
+ target.content = "Content"
+ end
+
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ target.type = "html"
+ target.content = "<em>Content</em>"
+ end
+
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ target.type = "xhtml"
+ target.xml_content = "text only"
+ end
+
+ _assert_maker_atom_text_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ target.type = "xhtml"
+ target.xml_content = RSS::XML::Element.new("unknown")
+ end
+ end
+ end
+
+ def _assert_maker_atom_date_construct(feed_type, maker_readers,
+ feed_readers, &block)
+ maker_extractor = Proc.new do |target|
+ date = {
+ :content => target,
+ }
+ date[:content] ? date : nil
+ end
+ feed_extractor = Proc.new do |target|
+ {
+ :content => target.content,
+ }
+ end
+ _assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ &block)
+ end
+
+ def assert_maker_atom_date_construct(feed_type, maker_readers, feed_readers,
+ parent_not_set_error_name=nil,
+ parent_not_set_variable=nil)
+ _wrap_assertion do
+ args = [feed_type, maker_readers, feed_readers]
+ if parent_not_set_error_name or parent_not_set_variable
+ assert_not_set_error(parent_not_set_error_name,
+ parent_not_set_variable) do
+ _assert_maker_atom_date_construct(*args) do |maker|
+ yield maker
+ end
+ end
+ else
+ _assert_maker_atom_date_construct(*args) do |maker|
+ yield maker
+ end
+ end
+
+ maker_readers = maker_readers.dup
+ writer = "#{maker_readers.pop}="
+ _assert_maker_atom_date_construct(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.__send__(writer, Time.now)
+ end
+ end
+ end
+
+ def _assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor)
+ _wrap_assertion do
+ element = nil
+ feed = RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ element = maker_extractor.call(target)
+ end
+
+ target = chain_reader(feed, feed_readers)
+ if target
+ actual_element = feed_extractor.call(target)
+ else
+ actual_element = nil
+ end
+ assert_equal(element, actual_element)
+ end
+ end
+
+ def _assert_maker_atom_elements(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ invalid_feed_checker=nil)
+ _wrap_assertion do
+ elements = []
+ invalid_feed_exception = nil
+ feed = nil
+ begin
+ feed = RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ targets.each do |target|
+ element = maker_extractor.call(target)
+ elements << element if element
+ end
+ if invalid_feed_checker
+ invalid_feed_exception = invalid_feed_checker.call(targets)
+ end
+ end
+ rescue RSS::Error
+ if invalid_feed_exception.is_a?(RSS::TooMuchTagError)
+ assert_too_much_tag(invalid_feed_exception.tag,
+ invalid_feed_exception.parent) do
+ raise
+ end
+ else
+ raise
+ end
+ end
+
+ if invalid_feed_exception.nil?
+ actual_elements = chain_reader(feed, feed_readers) || []
+ actual_elements = actual_elements.collect do |target|
+ feed_extractor.call(target)
+ end
+ assert_equal(elements, actual_elements)
+ end
+ end
+ end
+
+ def assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ setup_target, optional_variables,
+ required_variable, assert_method_name,
+ not_set_error_name=nil,
+ *additional_args)
+ _wrap_assertion do
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+
+ 0.upto(optional_variables.size) do |i|
+ combination(optional_variables, i).each do |names|
+ have = {}
+ names.each do |name|
+ have[name.intern] = true
+ end
+ have_required_variable_too =
+ have.merge({required_variable.intern => true})
+
+ assert_not_set_error(not_set_error_name, [required_variable]) do
+ __send__(assert_method_name, feed_type, maker_readers,
+ feed_readers, *additional_args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ setup_target.call(target, have)
+ end
+ end
+
+ __send__(assert_method_name, feed_type, maker_readers, feed_readers,
+ *additional_args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers) {|x| x}
+ setup_target.call(target, have_required_variable_too)
+ end
+ end
+ end
+ end
+ end
+
+ def assert_maker_atom_elements(feed_type, maker_readers, feed_readers,
+ setup_target, optional_variables,
+ required_variable, assert_method_name,
+ not_set_error_name=nil,
+ *additional_args)
+ _wrap_assertion do
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+
+ 0.upto(optional_variables.size) do |i|
+ combination(optional_variables, i).each do |names|
+ have = {}
+ names.each do |name|
+ have[name.intern] = true
+ end
+ have_required_variable_too =
+ have.merge({required_variable.intern => true})
+
+ assert_not_set_error(not_set_error_name, [required_variable]) do
+ __send__(assert_method_name, feed_type, maker_readers,
+ feed_readers, *additional_args) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ setup_target.call(targets, have)
+ end
+ end
+
+ __send__(assert_method_name, feed_type, maker_readers, feed_readers,
+ *additional_args) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ setup_target.call(targets, have_required_variable_too)
+ end
+
+ __send__(assert_method_name, feed_type, maker_readers, feed_readers,
+ *additional_args) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ setup_target.call(targets, have_required_variable_too)
+ setup_target.call(targets, have_required_variable_too)
+ end
+
+ assert_not_set_error(not_set_error_name, [required_variable]) do
+ __send__(assert_method_name, feed_type, maker_readers, feed_readers,
+ *additional_args) do |maker|
+ yield maker
+ targets = chain_reader(maker, maker_readers)
+ setup_target.call(targets, have_required_variable_too)
+ setup_target.call(targets, have)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def _assert_maker_atom_categories(feed_type, maker_readers,
+ feed_readers, &block)
+ maker_extractor = Proc.new do |target|
+ category = {
+ :term => target.term,
+ :scheme => target.scheme,
+ :label => target.label,
+ }
+ category[:term] ? category : nil
+ end
+ feed_extractor = Proc.new do |target|
+ {
+ :term => target.term,
+ :scheme => target.scheme,
+ :label => target.label,
+ }
+ end
+ _assert_maker_atom_elements(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor, &block)
+ end
+
+ def assert_maker_atom_categories(feed_type, maker_readers, feed_readers,
+ not_set_error_name=nil, &block)
+ _wrap_assertion do
+ _assert_maker_atom_categories(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ end
+
+ setup_target = Proc.new do |targets, have|
+ target = targets.new_child
+ target.term = "music" if have[:term]
+ target.scheme = "http://example.com/category/music" if have[:scheme]
+ target.label = "Music" if have[:label]
+ end
+
+ optional_variables = %w(scheme label)
+
+ assert_maker_atom_elements(feed_type, maker_readers, feed_readers,
+ setup_target, optional_variables,
+ "term", :_assert_maker_atom_categories,
+ not_set_error_name, &block)
+ end
+ end
+
+ def _assert_maker_atom_generator(feed_type, maker_readers,
+ feed_readers, &block)
+ maker_extractor = Proc.new do |target|
+ generator = {
+ :uri => target.uri,
+ :version => target.version,
+ :content => target.content,
+ }
+ generator[:content] ? generator : nil
+ end
+ feed_extractor = Proc.new do |target|
+ {
+ :uri => target.uri,
+ :version => target.version,
+ :content => target.content,
+ }
+ end
+ _assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ &block)
+ end
+
+ def assert_maker_atom_generator(feed_type, maker_readers, feed_readers,
+ not_set_error_name=nil, &block)
+ _wrap_assertion do
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+
+ _assert_maker_atom_generator(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ end
+
+ setup_target = Proc.new do |target, have|
+ target.content = "RSS Maker" if have[:content]
+ target.uri = "http://example.com/rss/maker" if have[:uri]
+ target.version = "0.0.1" if have[:version]
+ end
+
+ optional_variables = %w(uri version)
+
+ assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ setup_target, optional_variables,
+ "content", :_assert_maker_atom_generator,
+ not_set_error_name, &block)
+ end
+ end
+
+ def _assert_maker_atom_icon(feed_type, maker_readers, feed_readers,
+ accessor_base, &block)
+ maker_extractor = Proc.new do |target|
+ icon = {
+ :content => target.__send__(accessor_base),
+ }
+ icon[:content] ? icon : nil
+ end
+ feed_extractor = Proc.new do |target|
+ {
+ :content => target.content,
+ }
+ end
+ _assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ &block)
+ end
+
+ def assert_maker_atom_icon(feed_type, maker_readers, feed_readers,
+ accessor_base=nil, not_set_error_name=nil)
+ _wrap_assertion do
+ accessor_base ||= "url"
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+
+ _assert_maker_atom_icon(feed_type, maker_readers, feed_readers,
+ accessor_base) do |maker|
+ yield maker
+ end
+
+ _assert_maker_atom_icon(feed_type, maker_readers, feed_readers,
+ accessor_base) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.__send__("#{accessor_base}=", "http://example.com/icon.png")
+ end
+ end
+ end
+
+ def _assert_maker_atom_links(feed_type, maker_readers, feed_readers,
+ allow_duplication=false, &block)
+ maker_extractor = Proc.new do |target|
+ link = {
+ :href => target.href,
+ :rel => target.rel,
+ :type => target.type,
+ :hreflang => target.hreflang,
+ :title => target.title,
+ :length => target.length,
+ }
+ link[:href] ? link : nil
+ end
+ feed_extractor = Proc.new do |target|
+ {
+ :href => target.href,
+ :rel => target.rel,
+ :type => target.type,
+ :hreflang => target.hreflang,
+ :title => target.title,
+ :length => target.length,
+ }
+ end
+
+ if feed_readers.first == "entries"
+ parent = "entry"
+ else
+ parent = feed_type
+ end
+ invalid_feed_checker = Proc.new do |targets|
+ infos = {}
+ invalid_exception = nil
+ targets.each do |target|
+ key = [target.hreflang, target.type]
+ if infos.has_key?(key)
+ invalid_exception = RSS::TooMuchTagError.new("link", parent)
+ break
+ end
+ infos[key] = true if target.rel.nil? or target.rel == "alternate"
+ end
+ invalid_exception
+ end
+ invalid_feed_checker = nil if allow_duplication
+ _assert_maker_atom_elements(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ invalid_feed_checker,
+ &block)
+ end
+
+ def assert_maker_atom_links(feed_type, maker_readers, feed_readers,
+ not_set_error_name=nil, allow_duplication=false,
+ &block)
+ _wrap_assertion do
+ _assert_maker_atom_links(feed_type, maker_readers,
+ feed_readers) do |maker|
+ yield maker
+ end
+
+ langs = %(ja en fr zh po)
+ setup_target = Proc.new do |targets, have|
+ target = targets.new_child
+ lang = langs[targets.size % langs.size]
+ target.href = "http://example.com/index.html.#{lang}" if have[:href]
+ target.rel = "alternate" if have[:rel]
+ target.type = "text/xhtml" if have[:type]
+ target.hreflang = lang if have[:hreflang]
+ target.title = "FrontPage(#{lang})" if have[:title]
+ target.length = 1024 if have[:length]
+ end
+
+ optional_variables = %w(rel type hreflang title length)
+
+ assert_maker_atom_elements(feed_type, maker_readers, feed_readers,
+ setup_target, optional_variables,
+ "href", :_assert_maker_atom_links,
+ not_set_error_name, allow_duplication,
+ &block)
+ end
+ end
+
+ def _assert_maker_atom_logo(feed_type, maker_readers, feed_readers,
+ accessor_base, &block)
+ maker_extractor = Proc.new do |target|
+ logo = {
+ :uri => target.__send__(accessor_base),
+ }
+ logo[:uri] ? logo : nil
+ end
+ feed_extractor = Proc.new do |target|
+ {
+ :uri => target.content,
+ }
+ end
+ _assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ &block)
+ end
+
+ def assert_maker_atom_logo(feed_type, maker_readers, feed_readers,
+ accessor_base=nil, not_set_error_name=nil)
+ _wrap_assertion do
+ accessor_base ||= "uri"
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+
+ _assert_maker_atom_logo(feed_type, maker_readers, feed_readers,
+ accessor_base) do |maker|
+ yield maker
+ end
+
+ _assert_maker_atom_logo(feed_type, maker_readers, feed_readers,
+ accessor_base) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.__send__("#{accessor_base}=", "http://example.com/logo.png")
+ end
+ end
+ end
+
+ def _assert_maker_atom_id(feed_type, maker_readers, feed_readers, &block)
+ maker_extractor = Proc.new do |target|
+ id = {
+ :uri => target.id,
+ }
+ id[:uri] ? id : nil
+ end
+ feed_extractor = Proc.new do |target|
+ if target.id
+ {
+ :uri => target.id.content,
+ }
+ else
+ nil
+ end
+ end
+ _assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ &block)
+ end
+
+ def assert_maker_atom_id(feed_type, maker_readers, feed_readers,
+ not_set_error_name=nil)
+ _wrap_assertion do
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+
+ args = [feed_type, maker_readers, feed_readers]
+ _assert_maker_atom_id(*args) do |maker|
+ yield maker
+ end
+
+ _assert_maker_atom_id(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.id = "http://example.com/id/1"
+ end
+ end
+ end
+
+ def _assert_maker_atom_content(feed_type, maker_readers,
+ feed_readers, &block)
+ maker_extractor = Proc.new do |target|
+ content = {
+ :type => target.type,
+ :src => target.src,
+ :content => target.content,
+ :xml => target.xml,
+ :inline_text => target.inline_text?,
+ :inline_html => target.inline_html?,
+ :inline_xhtml => target.inline_xhtml?,
+ :inline_other => target.inline_other?,
+ :inline_other_text => target.inline_other_text?,
+ :inline_other_xml => target.inline_other_xml?,
+ :inline_other_base64 => target.inline_other_base64?,
+ :out_of_line => target.out_of_line?,
+ }
+ content[:src] = nil if content[:src] and content[:content]
+ if content[:type] or content[:content]
+ content
+ else
+ nil
+ end
+ end
+ feed_extractor = Proc.new do |target|
+ {
+ :type => target.type,
+ :src => target.src,
+ :content => target.content,
+ :xml => target.xml,
+ :inline_text => target.inline_text?,
+ :inline_html => target.inline_html?,
+ :inline_xhtml => target.inline_xhtml?,
+ :inline_other => target.inline_other?,
+ :inline_other_text => target.inline_other_text?,
+ :inline_other_xml => target.inline_other_xml?,
+ :inline_other_base64 => target.inline_other_base64?,
+ :out_of_line => target.out_of_line?,
+ }
+ end
+ _assert_maker_atom_element(feed_type, maker_readers, feed_readers,
+ maker_extractor, feed_extractor,
+ &block)
+ end
+
+ def assert_maker_atom_content(feed_type, maker_readers, feed_readers,
+ not_set_error_name=nil, &block)
+ _wrap_assertion do
+ not_set_error_name ||= "maker.#{maker_readers.join('.')}"
+ args = [feed_type, maker_readers, feed_readers, not_set_error_name]
+ assert_maker_atom_content_inline_text(*args, &block)
+ assert_maker_atom_content_inline_xhtml(*args, &block)
+ assert_maker_atom_content_inline_other(*args, &block)
+ assert_maker_atom_content_out_of_line(*args, &block)
+ end
+ end
+
+ def assert_maker_atom_content_inline_text(feed_type, maker_readers,
+ feed_readers, not_set_error_name)
+ _wrap_assertion do
+ args = [feed_type, maker_readers, feed_readers]
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ end
+
+ assert_not_set_error(not_set_error_name, %w(content)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "text"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(content)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "html"
+ end
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.content = ""
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "text"
+ target.content = "example content"
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "html"
+ target.content = "<em>text</em>"
+ end
+ end
+ end
+
+ def assert_maker_atom_content_inline_xhtml(feed_type, maker_readers,
+ feed_readers, not_set_error_name)
+ _wrap_assertion do
+ args = [feed_type, maker_readers, feed_readers]
+ assert_not_set_error(not_set_error_name, %w(xml_content)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "xhtml"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(xml_content)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "xhtml"
+ target.content = "dummy"
+ end
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "xhtml"
+ target.xml_content = "text"
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "xhtml"
+ target.xml = "text"
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "xhtml"
+ target.xml_content =
+ RSS::XML::Element.new("em", nil, nil, {}, ["text"])
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "xhtml"
+ target.xml = RSS::XML::Element.new("em", nil, nil, {}, ["text"])
+ end
+
+
+ xhtml_uri = "http://www.w3.org/1999/xhtml"
+ em = RSS::XML::Element.new("em", nil, nil, {}, ["text"])
+ em_with_xhtml_uri =
+ RSS::XML::Element.new("em", nil, xhtml_uri, {}, ["text"])
+ feed = RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "xhtml"
+ target.xml = em
+ end
+ assert_equal(RSS::XML::Element.new("div", nil, xhtml_uri,
+ {"xmlns" => xhtml_uri},
+ [em_with_xhtml_uri]),
+ chain_reader(feed, feed_readers).xml)
+
+ div = RSS::XML::Element.new("div", nil, xhtml_uri,
+ {"xmlns" => xhtml_uri,
+ "class" => "sample"},
+ ["text"])
+ feed = RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "xhtml"
+ target.xml = div
+ end
+ assert_equal(div, chain_reader(feed, feed_readers).xml)
+ end
+ end
+
+ def assert_maker_atom_content_inline_other(*args, &block)
+ _wrap_assertion do
+ assert_maker_atom_content_inline_other_xml(*args, &block)
+ assert_maker_atom_content_inline_other_text(*args, &block)
+ assert_maker_atom_content_inline_other_base64(*args, &block)
+ end
+ end
+
+ def assert_maker_atom_content_inline_other_xml(feed_type, maker_readers,
+ feed_readers,
+ not_set_error_name)
+ _wrap_assertion do
+ args = [feed_type, maker_readers, feed_readers]
+ assert_not_set_error(not_set_error_name, %w(xml_content)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "application/xml"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(xml_content)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "svg/image+xml"
+ end
+ end
+
+ svg_uri = "http://www.w3.org/2000/svg"
+ rect = RSS::XML::Element.new("rect", nil, svg_uri,
+ {"x" => "0.5cm",
+ "y" => "0.5cm",
+ "width" => "2cm",
+ "height" => "1cm"})
+ svg = RSS::XML::Element.new("svg", nil, svg_uri,
+ {"xmlns" => svg_uri,
+ "version" => "1.1",
+ "width" => "5cm",
+ "height" => "4cm"},
+ [rect])
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "image/svg+xml"
+ target.xml = svg
+ end
+
+ feed = RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "image/svg+xml"
+ target.xml = svg
+ end
+ assert_equal(svg, chain_reader(feed, feed_readers).xml)
+ end
+ end
+
+ def assert_maker_atom_content_inline_other_text(feed_type, maker_readers,
+ feed_readers,
+ not_set_error_name)
+ _wrap_assertion do
+ args = [feed_type, maker_readers, feed_readers]
+ assert_not_set_error(not_set_error_name, %w(content)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "text/plain"
+ end
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "text/plain"
+ target.content = "text"
+ end
+ end
+ end
+
+ def assert_maker_atom_content_inline_other_base64(feed_type, maker_readers,
+ feed_readers,
+ not_set_error_name)
+ _wrap_assertion do
+ args = [feed_type, maker_readers, feed_readers]
+ content = "\211PNG\r\n\032\n"
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "image/png"
+ target.content = content
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "image/png"
+ target.src = "http://example.com/logo.png"
+ target.content = content
+ end
+
+ feed = RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "image/png"
+ target.src = "http://example.com/logo.png"
+ target.content = content
+ end
+ target = chain_reader(feed, feed_readers)
+ assert_nil(target.src)
+ assert_equal(content, target.content)
+ end
+ end
+
+ def assert_maker_atom_content_out_of_line(feed_type, maker_readers,
+ feed_readers, not_set_error_name)
+ _wrap_assertion do
+ args = [feed_type, maker_readers, feed_readers]
+ assert_not_set_error(not_set_error_name, %w(content)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "image/png"
+ end
+ end
+
+ assert_not_set_error(not_set_error_name, %w(type)) do
+ RSS::Maker.make("atom:#{feed_type}") do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.src = "http://example.com/logo.png"
+ end
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "image/png"
+ target.src = "http://example.com/logo.png"
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "image/png"
+ target.content = "\211PNG\r\n\032\n"
+ end
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "application/xml"
+ target.src = "http://example.com/sample.xml"
+ end
+
+
+ _assert_maker_atom_content(*args) do |maker|
+ yield maker
+ target = chain_reader(maker, maker_readers)
+ target.type = "text/plain"
+ target.src = "http://example.com/README.txt"
+ end
+ end
+ end
+
+ def assert_slash_elements(expected, target)
+ assert_equal(expected,
+ {
+ "section" => target.slash_section,
+ "department" => target.slash_department,
+ "comments" => target.slash_comments,
+ "hit_parades" => target.slash_hit_parades,
+ })
+ assert_equal(expected["hit_parades"].join(","),
+ target.slash_hit_parade)
+ end
+
+ def chain_reader(target, readers, &block)
+ readers.inject(target) do |result, reader|
+ return nil if result.nil?
+ result.__send__(reader, &block)
+ end
+ end
+
+ def normalized_attrs(attrs)
+ n_attrs = {}
+ attrs.each do |name, value|
+ n_attrs[name.to_s] = value
+ end
+ n_attrs
+ end
+
+ def combination(elements, n)
+ if n <= 0 or elements.size < n
+ []
+ elsif n == 1
+ elements.collect {|element| [element]}
+ else
+ first, *rest = elements
+ combination(rest, n - 1).collect do |sub_elements|
+ [first, *sub_elements]
+ end + combination(rest, n)
+ end
+ end
+
+ def tag(name, content=nil, attributes={})
+ attributes = attributes.collect do |key, value|
+ "#{ERB::Util.h(key)}=\"#{ERB::Util.h(value)}\""
+ end.join(" ")
+ begin_tag = "<#{name}"
+ begin_tag << " #{attributes}" unless attributes.empty?
+ if content
+ "#{begin_tag}>#{content}</#{name}>\n"
+ else
+ "#{begin_tag}/>\n"
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/rss-testcase.rb b/jni/ruby/test/rss/rss-testcase.rb
new file mode 100644
index 0000000..2e39433
--- /dev/null
+++ b/jni/ruby/test/rss/rss-testcase.rb
@@ -0,0 +1,478 @@
+require "erb"
+
+require "test/unit"
+require_relative 'rss-assertions'
+
+require "rss"
+
+module RSS
+ class TestCase < Test::Unit::TestCase
+ include ERB::Util
+
+ include RSS
+ include Assertions
+
+ XMLDECL_VERSION = "1.0"
+ XMLDECL_ENCODING = "UTF-8"
+ XMLDECL_STANDALONE = "no"
+
+ RDF_ABOUT = "http://www.xml.com/xml/news.rss"
+ RDF_RESOURCE = "http://xml.com/universal/images/xml_tiny.gif"
+ TITLE_VALUE = "XML.com"
+ LINK_VALUE = "http://xml.com/pub"
+ URL_VALUE = "http://xml.com/universal/images/xml_tiny.gif"
+ NAME_VALUE = "hogehoge"
+ LANGUAGE_VALUE = "ja"
+ DESCRIPTION_VALUE = "
+ XML.com features a rich mix of information and services
+ for the XML community.
+ "
+ RESOURCES = [
+ "http://xml.com/pub/2000/08/09/xslt/xslt.html",
+ "http://xml.com/pub/2000/08/09/rdfdb/index.html",
+ ]
+
+ CLOUD_DOMAIN = "data.ourfavoritesongs.com"
+ CLOUD_PORT = "80"
+ CLOUD_PATH = "/RPC2"
+ CLOUD_REGISTER_PROCEDURE = "ourFavoriteSongs.rssPleaseNotify"
+ CLOUD_PROTOCOL = "xml-rpc"
+
+ ENCLOSURE_URL = "http://www.scripting.com/mp3s/weatherReportSuite.mp3"
+ ENCLOSURE_LENGTH = "12216320"
+ ENCLOSURE_TYPE = "audio/mpeg"
+
+ CATEGORY_DOMAIN = "http://www.superopendirectory.com/"
+
+ FEED_TITLE = "dive into mark"
+ FEED_UPDATED = "2003-12-13T18:30:02Z"
+ FEED_AUTHOR_NAME = "John Doe"
+ FEED_ID = "urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6"
+
+ ENTRY_TITLE = "Atom-Powered Robots Run Amok"
+ ENTRY_LINK = "http://example.org/2003/12/13/atom03"
+ ENTRY_ID = "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a"
+ ENTRY_UPDATED = "2003-12-13T18:30:02Z"
+ ENTRY_SUMMARY = "Some text."
+
+ t = Time.iso8601("2000-01-01T12:00:05+00:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+
+ DC_ELEMENTS = {
+ :title => "hoge",
+ :description =>
+ " XML is placing increasingly heavy loads on
+ the existing technical infrastructure of the Internet.",
+ :creator => "Rael Dornfest (mailto:rael@oreilly.com)",
+ :subject => "XML",
+ :publisher => "The O'Reilly Network",
+ :contributor => "hogehoge",
+ :type => "fugafuga",
+ :format => "hohoho",
+ :identifier => "fufufu",
+ :source => "barbar",
+ :language => "ja",
+ :relation => "cococo",
+ :rights => "Copyright (c) 2000 O'Reilly &amp; Associates, Inc.",
+ :date => t,
+ }
+
+ DC_NODES = DC_ELEMENTS.collect do |name, value|
+ "<#{DC_PREFIX}:#{name}>#{value}</#{DC_PREFIX}:#{name}>"
+ end.join("\n")
+
+ def default_test
+ # This class isn't tested
+ end
+
+ private
+ def make_xmldecl(v=XMLDECL_VERSION, e=XMLDECL_ENCODING, s=XMLDECL_STANDALONE)
+ rv = "<?xml version='#{v}'"
+ rv << " encoding='#{e}'" if e
+ rv << " standalone='#{s}'" if s
+ rv << "?>"
+ rv
+ end
+
+ def make_RDF(content=nil, xmlns=[])
+ <<-EORSS
+#{make_xmldecl}
+<rdf:RDF xmlns="#{URI}" xmlns:rdf="#{RDF::URI}"
+#{xmlns.collect {|pre, uri| "xmlns:#{pre}='#{uri}'"}.join(' ')}>
+#{block_given? ? yield : content}
+</rdf:RDF>
+EORSS
+ end
+
+ def make_channel(content=nil)
+ <<-EOC
+<channel rdf:about="#{RDF_ABOUT}">
+ <title>#{TITLE_VALUE}</title>
+ <link>#{LINK_VALUE}</link>
+ <description>#{DESCRIPTION_VALUE}</description>
+
+ <image rdf:resource="#{RDF_RESOURCE}" />
+
+ <items>
+ <rdf:Seq>
+#{RESOURCES.collect do |res| '<rdf:li resource="' + res + '" />' end.join("\n")}
+ </rdf:Seq>
+ </items>
+
+ <textinput rdf:resource="#{RDF_RESOURCE}" />
+
+#{block_given? ? yield : content}
+</channel>
+EOC
+ end
+
+ def make_image(content=nil)
+ <<-EOI
+<image rdf:about="#{RDF_ABOUT}">
+ <title>#{TITLE_VALUE}</title>
+ <url>#{URL_VALUE}</url>
+ <link>#{LINK_VALUE}</link>
+#{block_given? ? yield : content}
+</image>
+EOI
+ end
+
+ def make_item(content=nil)
+ <<-EOI
+<item rdf:about="#{RDF_ABOUT}">
+ <title>#{TITLE_VALUE}</title>
+ <link>#{LINK_VALUE}</link>
+ <description>#{DESCRIPTION_VALUE}</description>
+#{block_given? ? yield : content}
+</item>
+EOI
+ end
+
+ def make_textinput(content=nil)
+ <<-EOT
+<textinput rdf:about="#{RDF_ABOUT}">
+ <title>#{TITLE_VALUE}</title>
+ <description>#{DESCRIPTION_VALUE}</description>
+ <name>#{NAME_VALUE}</name>
+ <link>#{LINK_VALUE}</link>
+#{block_given? ? yield : content}
+</textinput>
+EOT
+ end
+
+ def make_sample_RDF
+ make_RDF(<<-EOR)
+#{make_channel}
+#{make_image}
+#{make_item}
+#{make_textinput}
+EOR
+ end
+
+ def make_rss20(content=nil, xmlns=[])
+ <<-EORSS
+#{make_xmldecl}
+<rss version="2.0"
+#{xmlns.collect {|pre, uri| "xmlns:#{pre}='#{uri}'"}.join(' ')}>
+#{block_given? ? yield : content}
+</rss>
+EORSS
+ end
+
+ def make_sample_items20
+ RESOURCES.collect do |res|
+ elems = ["<link>#{res}</link>"]
+ elems << "<title>title of #{res}</title>"
+ elems = elems.join("\n")
+ "<item>\n#{elems}\n</item>"
+ end.join("\n")
+ end
+
+ def make_channel20(content=nil)
+ <<-EOC
+<channel>
+ <title>#{TITLE_VALUE}</title>
+ <link>#{LINK_VALUE}</link>
+ <description>#{DESCRIPTION_VALUE}</description>
+ <language>#{LANGUAGE_VALUE}</language>
+
+ <image>
+ <url>#{RDF_RESOURCE}</url>
+ <title>#{TITLE_VALUE}</title>
+ <link>#{LINK_VALUE}</link>
+ </image>
+
+#{make_sample_items20}
+
+ <textInput>
+ <title>#{TITLE_VALUE}</title>
+ <description>#{DESCRIPTION_VALUE}</description>
+ <name>#{NAME_VALUE}</name>
+ <link>#{RDF_RESOURCE}</link>
+ </textInput>
+
+#{block_given? ? yield : content}
+</channel>
+EOC
+ end
+
+ def make_item20(content=nil)
+ <<-EOI
+<item>
+ <title>#{TITLE_VALUE}</title>
+ <link>#{LINK_VALUE}</link>
+ <description>#{DESCRIPTION_VALUE}</description>
+#{block_given? ? yield : content}
+</item>
+EOI
+ end
+
+ def make_cloud20
+ <<-EOC
+<cloud
+ domain="#{CLOUD_DOMAIN}"
+ port="#{CLOUD_PORT}"
+ path="#{CLOUD_PATH}"
+ registerProcedure="#{CLOUD_REGISTER_PROCEDURE}"
+ protocol="#{CLOUD_PROTOCOL}" />
+EOC
+ end
+
+ def make_sample_rss20
+ make_rss20(<<-EOR)
+#{make_channel20}
+EOR
+ end
+
+ def make_feed_without_entry(content=nil, xmlns=[])
+ <<-EOA
+<feed xmlns="#{Atom::URI}"
+#{xmlns.collect {|pre, uri| "xmlns:#{pre}='#{uri}'"}.join(' ')}>
+ <id>#{FEED_ID}</id>
+ <title>#{FEED_TITLE}</title>
+ <updated>#{FEED_UPDATED}</updated>
+ <author>
+ <name>#{FEED_AUTHOR_NAME}</name>
+ </author>
+#{block_given? ? yield : content}
+</feed>
+EOA
+ end
+
+ def make_entry(content=nil)
+ <<-EOA
+ <entry>
+ <title>#{ENTRY_TITLE}</title>
+ <id>#{ENTRY_ID}</id>
+ <updated>#{ENTRY_UPDATED}</updated>
+#{block_given? ? yield : content}
+ </entry>
+EOA
+ end
+
+ def make_feed_with_open_entry(content=nil, xmlns=[], &block)
+ make_feed_without_entry(<<-EOA, xmlns)
+#{make_entry(content, &block)}
+EOA
+ end
+
+ def make_feed_with_open_entry_source(content=nil, xmlns=[])
+ make_feed_with_open_entry(<<-EOA, xmlns)
+ <source>
+#{block_given? ? yield : content}
+ </source>
+EOA
+ end
+
+ def make_feed(content=nil, xmlns=[])
+ make_feed_without_entry(<<-EOA, xmlns)
+ <entry>
+ <title>#{ENTRY_TITLE}</title>
+ <link href="#{ENTRY_LINK}"/>
+ <id>#{ENTRY_ID}</id>
+ <updated>#{ENTRY_UPDATED}</updated>
+ <summary>#{ENTRY_SUMMARY}</summary>
+ </entry>
+#{block_given? ? yield : content}
+EOA
+ end
+
+ def make_entry_document(content=nil, xmlns=[])
+ <<-EOA
+<entry xmlns="#{Atom::URI}"
+#{xmlns.collect {|pre, uri| "xmlns:#{pre}='#{uri}'"}.join(' ')}>
+ <id>#{ENTRY_ID}</id>
+ <title>#{ENTRY_TITLE}</title>
+ <updated>#{ENTRY_UPDATED}</updated>
+ <author>
+ <name>#{FEED_AUTHOR_NAME}</name>
+ </author>
+#{block_given? ? yield : content}
+</entry>
+EOA
+ end
+
+ def make_entry_document_with_open_source(content=nil, xmlns=[])
+ make_entry_document(<<-EOA, xmlns)
+ <source>
+#{block_given? ? yield : content}
+ </source>
+EOA
+ end
+
+ def make_element(elem_name, attrs, contents)
+ attrs_str = attrs.collect do |name, value|
+ "#{h name}='#{h value}'"
+ end.join(" ")
+ attrs_str = " #{attrs_str}" unless attrs_str.empty?
+
+ if contents.is_a?(String)
+ contents_str = h(contents)
+ else
+ contents_str = contents.collect do |name, value|
+ "#{Element::INDENT}<#{h name}>#{h value}</#{h name}>"
+ end.join("\n")
+ contents_str = "\n#{contents_str}\n"
+ end
+
+ "<#{h elem_name}#{attrs_str}>#{contents_str}</#{h elem_name}>"
+ end
+
+ def xmlns_container(xmlns_decls, content)
+ attributes = xmlns_decls.collect do |prefix, uri|
+ "xmlns:#{h prefix}=\"#{h uri}\""
+ end.join(" ")
+ "<dummy #{attributes}>#{content}</dummy>"
+ end
+
+ private
+ def setup_rss10(rdf)
+ assert_equal("", rdf.to_s)
+
+ channel = RDF::Channel.new
+ assert_equal("", channel.to_s)
+ channel.about = "http://example.com/index.rdf"
+ channel.title = "title"
+ channel.link = "http://example.com/"
+ channel.description = "description"
+ assert_equal("", channel.to_s)
+
+ item_title = "item title"
+ item_link = "http://example.com/item"
+ channel.items = RDF::Channel::Items.new
+ channel.items.Seq.lis << RDF::Channel::Items::Seq::Li.new(item_link)
+ assert_not_equal("", channel.to_s)
+
+ rdf.channel = channel
+ assert_equal("", rdf.to_s)
+
+ item = RDF::Item.new
+ item.title = item_title
+ item.link = item_link
+ item.about = item_link
+ rdf.items << item
+ assert_not_equal("", rdf.to_s)
+ end
+
+ def setup_rss20(rss)
+ assert_equal("", rss.to_s)
+
+ channel = Rss::Channel.new
+ assert_equal("", channel.to_s)
+ channel.title = "title"
+ channel.link = "http://example.com/"
+ channel.description = "description"
+ assert_not_equal("", channel.to_s)
+
+ rss.channel = channel
+ assert_not_equal("", rss.to_s)
+ end
+
+ def setup_dummy_channel(maker)
+ about = "http://hoge.com"
+ title = "fugafuga"
+ link = "http://hoge.com/feed.xml"
+ description = "fugafugafugafuga"
+ language = "ja"
+
+ maker.channel.about = about
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+ end
+
+ def setup_dummy_channel_atom(maker)
+ updated = Time.now
+ author = "Foo"
+
+ setup_dummy_channel(maker)
+ maker.channel.links.first.rel = "self"
+ maker.channel.links.first.type = "application/atom+xml"
+ maker.channel.updated = updated
+ maker.channel.author = author
+ end
+
+ def setup_dummy_image(maker)
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+
+ maker.channel.link = link if maker.channel.link.nil?
+
+ maker.image.title = title
+ maker.image.url = url
+ end
+
+ def setup_dummy_textinput(maker)
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com/search.cgi"
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+
+ def setup_dummy_item(maker)
+ title = "TITLE"
+ link = "http://hoge.com/"
+
+ item = maker.items.new_item
+ item.title = title
+ item.link = link
+ end
+
+ def setup_dummy_item_atom(maker)
+ setup_dummy_item(maker)
+
+ item = maker.items.first
+ item.id = "http://example.net/xxx"
+ item.updated = Time.now
+ end
+
+ def setup_taxo_topic(target, topics)
+ topics.each do |topic|
+ taxo_topic = target.taxo_topics.new_taxo_topic
+ topic.each do |name, value|
+ case name
+ when :link
+ taxo_topic.taxo_link = value
+ when :topics
+ value.each do |t|
+ taxo_topic.taxo_topics << t
+ end
+ else
+ dc_elems = taxo_topic.__send__("dc_#{name}s")
+ dc_elem = dc_elems.__send__("new_#{name}")
+ dc_elem.value = value
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_1.0.rb b/jni/ruby/test/rss/test_1.0.rb
new file mode 100644
index 0000000..758eecf
--- /dev/null
+++ b/jni/ruby/test/rss/test_1.0.rb
@@ -0,0 +1,307 @@
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+
+module RSS
+ class TestRSS10Core < TestCase
+
+ def setup
+ @rdf_prefix = "rdf"
+ @rdf_uri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ @uri = "http://purl.org/rss/1.0/"
+ end
+
+ def test_RDF
+ version = "1.0"
+ encoding = "UTF-8"
+ standalone = false
+
+ rdf = RDF.new(version, encoding, standalone)
+ setup_rss10(rdf)
+ doc = REXML::Document.new(rdf.to_s)
+
+ xmldecl = doc.xml_decl
+
+ assert_equal(version, xmldecl.version)
+ assert_equal(encoding, xmldecl.encoding.to_s)
+ assert_equal(standalone, !xmldecl.standalone.nil?)
+
+ assert_equal(@rdf_uri, doc.root.namespace)
+ end
+
+ def test_not_displayed_xml_stylesheets
+ rdf = RDF.new()
+ plain_rdf = rdf.to_s
+ 3.times do
+ rdf.xml_stylesheets.push(XMLStyleSheet.new)
+ assert_equal(plain_rdf, rdf.to_s)
+ end
+ end
+
+ def test_xml_stylesheets
+ [
+ [{:href => "a.xsl", :type => "text/xsl"}],
+ [
+ {:href => "a.xsl", :type => "text/xsl"},
+ {:href => "a.css", :type => "text/css"},
+ ],
+ ].each do |attrs_ary|
+ assert_xml_stylesheet_pis(attrs_ary)
+ end
+ end
+
+ def test_channel
+ about = "http://hoge.com"
+ resource = "http://hoge.com/hoge.png"
+
+ item_title = "item title"
+ item_link = "http://hoge.com/item"
+
+ image = RDF::Channel::Image.new(resource)
+ items = RDF::Channel::Items.new
+ items.Seq.lis << items.class::Seq::Li.new(item_link)
+ textinput = RDF::Channel::Textinput.new(resource)
+
+ rss_item = RDF::Item.new
+ rss_item.title = item_title
+ rss_item.link = item_link
+ rss_item.about = item_link
+
+ h = {
+ 'title' => "fugafuga",
+ 'link' => "http://hoge.com",
+ 'description' => "fugafugafugafuga",
+ 'image' => image,
+ 'items' => items,
+ 'textinput' => textinput,
+ }
+
+ channel = RDF::Channel.new(about)
+ %w(title link description image items textinput).each do |x|
+ channel.__send__("#{x}=", h[x])
+ end
+
+ doc = REXML::Document.new(make_RDF(<<-EOR))
+#{channel}
+<items>
+#{rss_item}
+</items>
+EOR
+ c = doc.root.elements[1]
+
+ assert_equal(about, c.attributes["about"])
+ %w(title link description image textinput).each do |x|
+ elem = c.elements[x]
+ assert_equal(x, elem.name)
+ assert_equal(@uri, elem.namespace)
+ if x == "image" or x == "textinput"
+ excepted = resource
+ res = elem.attributes.get_attribute("resource")
+ assert_equal(@rdf_uri, res.namespace)
+ value = res.value
+ else
+ excepted = h[x]
+ value = elem.text
+ end
+ assert_equal(excepted, value)
+ end
+ assert_equal(@uri, c.elements["items"].namespace)
+ assert_equal("items", c.elements["items"].name)
+ end
+
+ def test_channel_image
+ resource = "http://hoge.com/hoge.png"
+ image = RDF::Channel::Image.new(resource)
+
+ doc = REXML::Document.new(make_RDF(image.to_s))
+ i = doc.root.elements[1]
+
+ assert_equal("image", i.name)
+ assert_equal(@uri, i.namespace)
+
+ res = i.attributes.get_attribute("resource")
+
+ assert_equal(@rdf_uri, res.namespace)
+ assert_equal(resource, res.value)
+ end
+
+ def test_channel_textinput
+ resource = "http://hoge.com/hoge.png"
+ textinput = RDF::Channel::Textinput.new(resource)
+
+ doc = REXML::Document.new(make_RDF(textinput.to_s))
+ t = doc.root.elements[1]
+
+ assert_equal("textinput", t.name)
+ assert_equal(@uri, t.namespace)
+
+ res = t.attributes.get_attribute("resource")
+
+ assert_equal(@rdf_uri, res.namespace)
+ assert_equal(resource, res.value)
+ end
+
+ def test_channel_items
+ item_link = "http://example.com/item"
+
+ items = RDF::Channel::Items.new
+ li = items.Seq.class::Li.new(item_link)
+ items.Seq.lis << li
+
+ doc = REXML::Document.new(make_RDF(items.to_s))
+ i = doc.root.elements[1]
+
+ assert_equal("items", i.name)
+ assert_equal(@uri, i.namespace)
+
+ assert_equal(1, i.elements.size)
+ seq = i.elements[1]
+ assert_equal("Seq", seq.name)
+ assert_equal(@rdf_uri, seq.namespace)
+
+ assert_equal(1, seq.elements.size)
+ l = seq.elements[1]
+ assert_equal("li", l.name)
+ assert_equal(@rdf_uri, l.namespace)
+ assert_equal(item_link, l.attributes["resource"])
+ end
+
+ def test_seq
+ item_link = "http://example.com/item"
+ seq = RDF::Seq.new
+ li = seq.class::Li.new(item_link)
+ seq.lis << li
+
+ doc = REXML::Document.new(make_RDF(seq.to_s))
+ s = doc.root.elements[1]
+
+ assert_equal("Seq", s.name)
+ assert_equal(@rdf_uri, s.namespace)
+
+ assert_equal(1, s.elements.size)
+ l = s.elements[1]
+ assert_equal("li", l.name)
+ assert_equal(@rdf_uri, l.namespace)
+ assert_equal(item_link, l.attributes["resource"])
+ end
+
+ def test_li
+ resource = "http://hoge.com/"
+ li = RDF::Li.new(resource)
+
+ doc = REXML::Document.new(make_RDF(li.to_s))
+ l = doc.root.elements[1]
+
+ assert_equal("li", l.name)
+ assert_equal(@rdf_uri, l.namespace(l.prefix))
+
+ res = l.attributes.get_attribute("resource")
+
+ assert_equal('', res.instance_eval("@prefix"))
+ assert_equal(resource, res.value)
+ end
+
+ def test_image
+ about = "http://hoge.com"
+ h = {
+ 'title' => "fugafuga",
+ 'url' => "http://hoge.com/hoge",
+ 'link' => "http://hoge.com/fuga",
+ }
+
+ image = RDF::Image.new(about)
+ %w(title url link).each do |x|
+ image.__send__("#{x}=", h[x])
+ end
+
+ doc = REXML::Document.new(make_RDF(image.to_s))
+ i = doc.root.elements[1]
+
+ assert_equal(about, i.attributes["about"])
+ %w(title url link).each do |x|
+ elem = i.elements[x]
+ assert_equal(x, elem.name)
+ assert_equal(@uri, elem.namespace)
+ assert_equal(h[x], elem.text)
+ end
+ end
+
+ def test_item
+ about = "http://hoge.com"
+ h = {
+ 'title' => "fugafuga",
+ 'link' => "http://hoge.com/fuga",
+ 'description' => "hogehogehoge",
+ }
+
+ item = RDF::Item.new(about)
+ %w(title link description).each do |x|
+ item.__send__("#{x}=", h[x])
+ end
+
+ doc = REXML::Document.new(make_RDF(item.to_s))
+ i = doc.root.elements[1]
+
+ assert_equal(about, i.attributes["about"])
+ %w(title link description).each do |x|
+ elem = i.elements[x]
+ assert_equal(x, elem.name)
+ assert_equal(@uri, elem.namespace)
+ assert_equal(h[x], elem.text)
+ end
+ end
+
+ def test_textinput
+ about = "http://hoge.com"
+ h = {
+ 'title' => "fugafuga",
+ 'link' => "http://hoge.com/fuga",
+ 'name' => "foo",
+ 'description' => "hogehogehoge",
+ }
+
+ textinput = RDF::Textinput.new(about)
+ %w(title link name description).each do |x|
+ textinput.__send__("#{x}=", h[x])
+ end
+
+ doc = REXML::Document.new(make_RDF(textinput.to_s))
+ t = doc.root.elements[1]
+
+ assert_equal(about, t.attributes["about"])
+ %w(title link name description).each do |x|
+ elem = t.elements[x]
+ assert_equal(x, elem.name)
+ assert_equal(@uri, elem.namespace)
+ assert_equal(h[x], elem.text)
+ end
+ end
+
+ def test_to_xml
+ rss = RSS::Parser.parse(make_sample_RDF)
+ assert_equal(rss.to_s, rss.to_xml)
+ assert_equal(rss.to_s, rss.to_xml("1.0"))
+ rss09 = rss.to_xml("0.91") do |maker|
+ maker.channel.language = "en-us"
+ end
+ rss09 = RSS::Parser.parse(rss09)
+ assert_equal("0.91", rss09.rss_version)
+ assert_equal(["rss", "0.91", nil], rss09.feed_info)
+ rss20 = RSS::Parser.parse(rss.to_xml("2.0"))
+ assert_equal("2.0", rss20.rss_version)
+ assert_equal(["rss", "2.0", nil], rss20.feed_info)
+
+ atom_xml = rss.to_xml("atom") do |maker|
+ maker.channel.author = "Alice"
+ maker.channel.updated ||= Time.now
+ maker.items.each do |item|
+ item.updated ||= Time.now
+ end
+ end
+ atom = RSS::Parser.parse(atom_xml)
+ assert_equal(["atom", "1.0", "feed"], atom.feed_info)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_2.0.rb b/jni/ruby/test/rss/test_2.0.rb
new file mode 100644
index 0000000..37285ce
--- /dev/null
+++ b/jni/ruby/test/rss/test_2.0.rb
@@ -0,0 +1,411 @@
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+module RSS
+ class TestRSS20Core < TestCase
+
+ def setup
+ @rss_version = "2.0"
+ end
+
+ def test_Rss
+ version = "1.0"
+ encoding = "UTF-8"
+ standalone = false
+
+ rss = Rss.new(@rss_version, version, encoding, standalone)
+ setup_rss20(rss)
+
+ doc = REXML::Document.new(rss.to_s(false))
+
+ xmldecl = doc.xml_decl
+
+ assert_equal(version, xmldecl.version)
+ assert_equal(encoding, xmldecl.encoding.to_s)
+ assert_equal(standalone, !xmldecl.standalone.nil?)
+
+ assert_equal("", doc.root.namespace)
+ assert_equal(@rss_version, doc.root.attributes["version"])
+ end
+
+ def test_not_displayed_xml_stylesheets
+ rss = Rss.new(@rss_version)
+ plain_rss = rss.to_s
+ 3.times do
+ rss.xml_stylesheets.push(XMLStyleSheet.new)
+ assert_equal(plain_rss, rss.to_s)
+ end
+ end
+
+ def test_xml_stylesheets
+ [
+ [{:href => "a.xsl", :type => "text/xsl"}],
+ [
+ {:href => "a.xsl", :type => "text/xsl"},
+ {:href => "a.css", :type => "text/css"},
+ ],
+ ].each do |attrs_ary|
+ rss = Rss.new(@rss_version)
+ setup_rss20(rss)
+ assert_xml_stylesheet_pis(attrs_ary, rss)
+ end
+ end
+
+ def test_channel
+ h = {
+ 'title' => "fugafuga",
+ 'link' => "http://hoge.com",
+ 'description' => "fugafugafugafuga",
+
+ 'language' => "en-us",
+ 'copyright' => "Copyright 2002, Spartanburg Herald-Journal",
+ 'managingEditor' => "geo@herald.com (George Matesky)",
+ 'webMaster' => "betty@herald.com (Betty Guernsey)",
+ 'pubDate' => Time.parse("Sat, 07 Sep 2002 00:00:01 GMT"),
+ 'lastBuildDate' => Time.parse("Sat, 07 Sep 2002 09:42:31 GMT"),
+ 'generator' => "MightyInHouse Content System v2.3",
+ 'docs' => "http://blogs.law.harvard.edu/tech/rss",
+ 'ttl' => "60",
+ 'rating' => '(PICS-1.1 "http://www.rsac.org/ratingsv01.html" l gen true comment "RSACi North America Server" for "http://www.rsac.org" on "1996.04.16T08:15-0500" r (n 0 s 0 v 0 l 0))',
+ }
+ categories = [
+ {
+ :content => "Newspapers",
+ },
+ {
+ :domain => "Syndic8",
+ :content => "1765",
+ }
+ ]
+
+ channel = Rss::Channel.new
+
+ elems = %w(title link description language copyright
+ managingEditor webMaster pubDate lastBuildDate
+ generator docs ttl rating)
+ elems.each do |x|
+ value = h[x]
+ value = value.rfc822 if %w(pubDate lastBuildDate).include?(x)
+ channel.__send__("#{x}=", value)
+ end
+ categories.each do |cat|
+ channel.categories << Rss::Channel::Category.new(cat[:domain],
+ cat[:content])
+ end
+
+ doc = REXML::Document.new(make_rss20(channel.to_s))
+ c = doc.root.elements[1]
+
+ elems.each do |x|
+ elem = c.elements[x]
+ assert_equal(x, elem.name)
+ assert_equal("", elem.namespace)
+ expected = h[x]
+ case x
+ when "pubDate", "lastBuildDate"
+ assert_equal(expected, Time.parse(elem.text))
+ when "ttl"
+ expected = channel.__send__(x)
+ assert_equal(expected, elem.text.to_i)
+ else
+ assert_equal(expected, elem.text)
+ end
+ end
+ categories.each_with_index do |cat, i|
+ cat = cat.dup
+ cat[:domain] ||= nil
+ category = c.elements["category[#{i+1}]"]
+ actual = {
+ :domain => category.attributes["domain"],
+ :content => category.text,
+ }
+ assert_equal(cat, actual)
+ end
+ end
+
+ def test_channel_cloud
+ cloud_params = {
+ :domain => "rpc.sys.com",
+ :port => "80",
+ :path => "/RPC2",
+ :registerProcedure => "myCloud.rssPleaseNotify",
+ :protocol => "xml-rpc",
+ }
+ cloud = Rss::Channel::Cloud.new(cloud_params[:domain],
+ cloud_params[:port],
+ cloud_params[:path],
+ cloud_params[:registerProcedure],
+ cloud_params[:protocol])
+ cloud_params[:port] = cloud.port
+
+ doc = REXML::Document.new(cloud.to_s)
+ cloud_elem = doc.root
+
+ actual = {}
+ cloud_elem.attributes.each do |name, value|
+ value = value.to_i if name == "port"
+ actual[name.intern] = value
+ end
+ assert_equal(cloud_params, actual)
+ end
+
+ def test_channel_image
+ image_params = {
+ :url => "http://hoge.com/hoge.png",
+ :title => "fugafuga",
+ :link => "http://hoge.com",
+ :width => "144",
+ :height => "400",
+ :description => "an image",
+ }
+ image = Rss::Channel::Image.new(image_params[:url],
+ image_params[:title],
+ image_params[:link],
+ image_params[:width],
+ image_params[:height],
+ image_params[:description])
+
+ doc = REXML::Document.new(image.to_s)
+ image_elem = doc.root
+
+ image_params.each do |name, value|
+ value = image.__send__(name)
+ actual = image_elem.elements[name.to_s].text
+ actual = actual.to_i if [:width, :height].include?(name)
+ assert_equal(value, actual)
+ end
+ end
+
+ def test_channel_textInput
+ textInput_params = {
+ :title => "fugafuga",
+ :description => "text hoge fuga",
+ :name => "hoge",
+ :link => "http://hoge.com",
+ }
+ textInput = Rss::Channel::TextInput.new(textInput_params[:title],
+ textInput_params[:description],
+ textInput_params[:name],
+ textInput_params[:link])
+
+ doc = REXML::Document.new(textInput.to_s)
+ input_elem = doc.root
+
+ textInput_params.each do |name, value|
+ actual = input_elem.elements[name.to_s].text
+ assert_equal(value, actual)
+ end
+ end
+
+ def test_channel_skip_days
+ skipDays_values = [
+ "Sunday",
+ "Monday",
+ ]
+ skipDays = Rss::Channel::SkipDays.new
+ skipDays_values.each do |value|
+ skipDays.days << Rss::Channel::SkipDays::Day.new(value)
+ end
+
+ doc = REXML::Document.new(skipDays.to_s)
+ days_elem = doc.root
+
+ skipDays_values.each_with_index do |value, i|
+ assert_equal(value, days_elem.elements[i + 1].text)
+ end
+ end
+
+ def test_channel_skip_hours
+ skipHours_values = [
+ "0",
+ "13",
+ ]
+ skipHours = Rss::Channel::SkipHours.new
+ skipHours_values.each do |value|
+ skipHours.hours << Rss::Channel::SkipHours::Hour.new(value)
+ end
+
+ doc = REXML::Document.new(skipHours.to_s)
+ hours_elem = doc.root
+
+ skipHours_values.each_with_index do |value, i|
+ expected = skipHours.hours[i].content
+ assert_equal(expected, hours_elem.elements[i + 1].text.to_i)
+ end
+ end
+
+ def test_item
+ h = {
+ 'title' => "fugafuga",
+ 'link' => "http://hoge.com/",
+ 'description' => "text hoge fuga",
+ 'author' => "oprah@oxygen.net",
+ 'comments' => "http://www.myblog.org/cgi-local/mt/mt-comments.cgi?entry_id=290",
+ 'pubDate' => Time.parse("Sat, 07 Sep 2002 00:00:01 GMT"),
+ }
+ categories = [
+ {
+ :content => "Newspapers",
+ },
+ {
+ :domain => "Syndic8",
+ :content => "1765",
+ }
+ ]
+
+ channel = Rss::Channel.new
+ channel.title = "title"
+ channel.link = "http://example.com/"
+ channel.description = "description"
+
+ item = Rss::Channel::Item.new
+ channel.items << item
+
+ elems = %w(title link description author comments pubDate)
+ elems.each do |x|
+ value = h[x]
+ value = value.rfc822 if x == "pubDate"
+ item.__send__("#{x}=", value)
+ end
+ categories.each do |cat|
+ item.categories << Rss::Channel::Category.new(cat[:domain],
+ cat[:content])
+ end
+
+ doc = REXML::Document.new(channel.to_s)
+ channel_elem = doc.root
+
+ item_elem = channel_elem.elements["item[1]"]
+ elems.each do |x|
+ elem = item_elem.elements[x]
+ assert_equal(x, elem.name)
+ assert_equal("", elem.namespace)
+ expected = h[x]
+ case x
+ when "pubDate"
+ assert_equal(expected, Time.parse(elem.text))
+ else
+ assert_equal(expected, elem.text)
+ end
+ end
+ categories.each_with_index do |cat, i|
+ cat = cat.dup
+ cat[:domain] ||= nil
+ category = item_elem.elements["category[#{i+1}]"]
+ actual = {
+ :domain => category.attributes["domain"],
+ :content => category.text,
+ }
+ assert_equal(cat, actual)
+ end
+ end
+
+ def test_item_enclosure
+ enclosure_params = {
+ :url => "http://www.scripting.com/mp3s/weatherReportSuite.mp3",
+ :length => "12216320",
+ :type => "audio/mpeg",
+ }
+
+ enclosure = Rss::Channel::Item::Enclosure.new(enclosure_params[:url],
+ enclosure_params[:length],
+ enclosure_params[:type])
+ enclosure_params[:length] = enclosure.length
+
+ doc = REXML::Document.new(enclosure.to_s)
+ enclosure_elem = doc.root
+
+ actual = {}
+ enclosure_elem.attributes.each do |name, value|
+ value = value.to_i if name == "length"
+ actual[name.intern] = value
+ end
+ assert_equal(enclosure_params, actual)
+ end
+
+ def test_item_guid
+ test_params = [
+ {
+ :content => "http://some.server.com/weblogItem3207",
+ },
+ {
+ :isPermaLink => "true",
+ :content => "http://inessential.com/2002/09/01.php#a2",
+ },
+ ]
+
+ test_params.each do |guid_params|
+ guid = Rss::Channel::Item::Guid.new(guid_params[:isPermaLink],
+ guid_params[:content])
+ if guid_params.has_key?(:isPermaLink)
+ guid_params[:isPermaLink] = guid.isPermaLink
+ end
+ if guid.isPermaLink.nil?
+ assert_equal(true, guid.PermaLink?)
+ else
+ assert_equal(guid.isPermaLink, guid.PermaLink?)
+ end
+
+ doc = REXML::Document.new(guid.to_s)
+ guid_elem = doc.root
+
+ actual = {}
+ actual[:content] = guid_elem.text if guid_elem.text
+ guid_elem.attributes.each do |name, value|
+ value = value == "true" if name == "isPermaLink"
+ actual[name.intern] = value
+ end
+ assert_equal(guid_params, actual)
+ end
+ end
+
+ def test_item_source
+ source_params = {
+ :url => "http://www.tomalak.org/links2.xml",
+ :content => "Tomalak's Realm",
+ }
+
+ source = Rss::Channel::Item::Source.new(source_params[:url],
+ source_params[:content])
+
+ doc = REXML::Document.new(source.to_s)
+ source_elem = doc.root
+
+ actual = {}
+ actual[:content] = source_elem.text
+ source_elem.attributes.each do |name, value|
+ actual[name.intern] = value
+ end
+ assert_equal(source_params, actual)
+ end
+
+ def test_to_xml
+ rss = RSS::Parser.parse(make_sample_rss20)
+ assert_equal(rss.to_s, rss.to_xml)
+ assert_equal(rss.to_s, rss.to_xml("2.0"))
+ rss09_xml = rss.to_xml("0.91") do |maker|
+ setup_dummy_image(maker)
+ end
+ rss09 = RSS::Parser.parse(rss09_xml)
+ assert_equal("0.91", rss09.rss_version)
+ rss10 = rss.to_xml("1.0") do |maker|
+ maker.channel.about = "http://www.example.com/index.rdf"
+ end
+ rss10 = RSS::Parser.parse(rss10)
+ assert_equal("1.0", rss10.rss_version)
+
+ atom_xml = rss.to_xml("atom1.0") do |maker|
+ maker.channel.id = "http://www.example.com/atom.xml"
+ maker.channel.author = "Alice"
+ maker.channel.updated = Time.now
+ maker.items.each do |item|
+ item.author = "Bob"
+ item.updated = Time.now
+ end
+ end
+ atom = RSS::Parser.parse(atom_xml)
+ assert_equal(["atom", "1.0", "feed"], atom.feed_info)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_accessor.rb b/jni/ruby/test/rss/test_accessor.rb
new file mode 100644
index 0000000..ea15316
--- /dev/null
+++ b/jni/ruby/test/rss/test_accessor.rb
@@ -0,0 +1,103 @@
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/2.0"
+require "rss/syndication"
+require "rss/image"
+
+module RSS
+ class TestAccessor < TestCase
+ def test_date
+ channel = Rss::Channel.new
+ channel.pubDate = nil
+ assert_nil(channel.pubDate)
+
+ time = Time.now
+ channel.pubDate = time
+ assert_equal(time, channel.pubDate)
+
+ time = Time.parse(Time.now.rfc822)
+ channel.pubDate = time.rfc822
+ assert_equal(time, channel.pubDate)
+
+ time = Time.parse(Time.now.iso8601)
+ value = time.iso8601
+ assert_not_available_value("pubDate", value) do
+ channel.pubDate = value
+ end
+
+ channel.do_validate = false
+ time = Time.parse(Time.now.iso8601)
+ value = time.iso8601
+ channel.pubDate = value
+ assert_equal(time, channel.pubDate)
+
+ channel.pubDate = nil
+ assert_nil(channel.pubDate)
+ end
+
+ def test_integer
+ image_item = RDF::Item::ImageItem.new
+
+ image_item.width = nil
+ assert_nil(image_item.width)
+
+ width = 10
+ image_item.width = width
+ assert_equal(width, image_item.width)
+
+ width = 10.0
+ image_item.width = width
+ assert_equal(width, image_item.width)
+
+ width = "10"
+ image_item.width = width
+ assert_equal(width.to_i, image_item.width)
+
+ width = "10.0"
+ assert_not_available_value("image:width", width) do
+ image_item.width = width
+ end
+
+ image_item.do_validate = false
+ width = "10.0"
+ image_item.width = width
+ assert_equal(width.to_i, image_item.width)
+
+ image_item.width = nil
+ assert_nil(image_item.width)
+ end
+
+ def test_positive_integer
+ channel = RDF::Channel.new
+
+ channel.sy_updateFrequency = nil
+ assert_nil(channel.sy_updateFrequency)
+
+ freq = 10
+ channel.sy_updateFrequency = freq
+ assert_equal(freq, channel.sy_updateFrequency)
+
+ freq = 10.0
+ channel.sy_updateFrequency = freq
+ assert_equal(freq, channel.sy_updateFrequency)
+
+ freq = "10"
+ channel.sy_updateFrequency = freq
+ assert_equal(freq.to_i, channel.sy_updateFrequency)
+
+ freq = "10.0"
+ assert_not_available_value("sy:updateFrequency", freq) do
+ channel.sy_updateFrequency = freq
+ end
+
+ channel.do_validate = false
+ freq = "10.0"
+ channel.sy_updateFrequency = freq
+ assert_equal(freq.to_i, channel.sy_updateFrequency)
+
+ channel.sy_updateFrequency = nil
+ assert_nil(channel.sy_updateFrequency)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_atom.rb b/jni/ruby/test/rss/test_atom.rb
new file mode 100644
index 0000000..557cb80
--- /dev/null
+++ b/jni/ruby/test/rss/test_atom.rb
@@ -0,0 +1,683 @@
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/atom"
+
+module RSS
+ class TestAtomCore < TestCase
+ def setup
+ @uri = "http://www.w3.org/2005/Atom"
+ @xhtml_uri = "http://www.w3.org/1999/xhtml"
+ end
+
+ def test_feed
+ version = "1.0"
+ encoding = "UTF-8"
+ standalone = false
+
+ feed = Atom::Feed.new(version, encoding, standalone)
+ assert_equal("", feed.to_s)
+
+ author = feed.class::Author.new
+ name = feed.class::Author::Name.new
+ name.content = "an author"
+ author.name = name
+ assert_not_equal("", author.to_s)
+ feed.authors << author
+ assert_equal("", feed.to_s)
+
+ id = feed.class::Id.new
+ id.content = "http://example.com/atom.xml"
+ assert_not_equal("", id.to_s)
+ feed.id = id
+ assert_equal("", feed.to_s)
+
+ title = feed.class::Title.new
+ title.content = "a title"
+ assert_not_equal("", title.to_s)
+ feed.title = title
+ assert_equal("", feed.to_s)
+
+ updated = feed.class::Updated.new
+ updated.content = Time.now
+ assert_not_equal("", updated.to_s)
+ feed.updated = updated
+ assert_not_equal("", feed.to_s)
+
+
+ feed.authors.clear
+ assert_equal("", feed.to_s)
+ entry = Atom::Feed::Entry.new
+ setup_entry(entry)
+ assert_not_equal("", entry.to_s)
+
+ author = entry.authors.first
+ entry.authors.clear
+ assert_equal("", entry.to_s)
+ entry.parent = feed
+ assert_equal("", entry.to_s)
+ feed.authors << author
+ assert_not_equal("", entry.to_s)
+ feed.authors.clear
+ feed.entries << entry
+ assert_equal("", feed.to_s)
+ entry.authors << author
+ assert_not_equal("", entry.to_s)
+ assert_not_equal("", feed.to_s)
+
+ doc = REXML::Document.new(feed.to_s)
+ xmldecl = doc.xml_decl
+
+ assert_equal(version, xmldecl.version)
+ assert_equal(encoding, xmldecl.encoding.to_s)
+ assert_equal(standalone, !xmldecl.standalone.nil?)
+
+ assert_equal(@uri, doc.root.namespace)
+ end
+
+ def test_entry
+ version = "1.0"
+ encoding = "UTF-8"
+ standalone = false
+
+ entry = Atom::Entry.new(version, encoding, standalone)
+ setup_entry(entry)
+
+ author = entry.authors.first
+ entry.authors.clear
+ assert_equal("", entry.to_s)
+ source = Atom::Entry::Source.new
+ source.authors << author
+ entry.source = source
+ assert_not_equal("", entry.to_s)
+
+ doc = REXML::Document.new(entry.to_s)
+ xmldecl = doc.xml_decl
+
+ assert_equal(version, xmldecl.version)
+ assert_equal(encoding, xmldecl.encoding.to_s)
+ assert_equal(standalone, !xmldecl.standalone.nil?)
+
+ assert_equal(@uri, doc.root.namespace)
+ end
+
+ def test_not_displayed_xml_stylesheets
+ feed = Atom::Feed.new
+ plain_feed = feed.to_s
+ 3.times do
+ feed.xml_stylesheets.push(XMLStyleSheet.new)
+ assert_equal(plain_feed, feed.to_s)
+ end
+ end
+
+ def test_atom_author
+ assert_atom_person_to_s(Atom::Feed::Author)
+ assert_atom_person_to_s(Atom::Feed::Entry::Author)
+ assert_atom_person_to_s(Atom::Entry::Author)
+ assert_atom_person_to_s(Atom::Feed::Entry::Source::Author)
+ assert_atom_person_to_s(Atom::Entry::Source::Author)
+ end
+
+ def test_atom_category
+ assert_atom_category_to_s(Atom::Feed::Category)
+ assert_atom_category_to_s(Atom::Feed::Entry::Category)
+ assert_atom_category_to_s(Atom::Entry::Category)
+ assert_atom_category_to_s(Atom::Feed::Entry::Source::Category)
+ assert_atom_category_to_s(Atom::Entry::Source::Category)
+ end
+
+ def test_atom_contributor
+ assert_atom_person_to_s(Atom::Feed::Contributor)
+ assert_atom_person_to_s(Atom::Feed::Entry::Contributor)
+ assert_atom_person_to_s(Atom::Entry::Contributor)
+ assert_atom_person_to_s(Atom::Feed::Entry::Source::Contributor)
+ assert_atom_person_to_s(Atom::Entry::Source::Contributor)
+ end
+
+ def test_atom_generator
+ assert_atom_generator_to_s(Atom::Feed::Generator)
+ assert_atom_generator_to_s(Atom::Feed::Entry::Source::Generator)
+ assert_atom_generator_to_s(Atom::Entry::Source::Generator)
+ end
+
+ def test_atom_icon
+ assert_atom_icon_to_s(Atom::Feed::Icon)
+ assert_atom_icon_to_s(Atom::Feed::Entry::Source::Icon)
+ assert_atom_icon_to_s(Atom::Entry::Source::Icon)
+ end
+
+ def test_atom_id
+ assert_atom_id_to_s(Atom::Feed::Id)
+ assert_atom_id_to_s(Atom::Feed::Entry::Id)
+ assert_atom_id_to_s(Atom::Entry::Id)
+ assert_atom_id_to_s(Atom::Feed::Entry::Source::Id)
+ assert_atom_id_to_s(Atom::Entry::Source::Id)
+ end
+
+ def test_atom_link
+ assert_atom_link_to_s(Atom::Feed::Link)
+ assert_atom_link_to_s(Atom::Feed::Entry::Link)
+ assert_atom_link_to_s(Atom::Entry::Link)
+ assert_atom_link_to_s(Atom::Feed::Entry::Source::Link)
+ assert_atom_link_to_s(Atom::Entry::Source::Link)
+ end
+
+ def test_atom_logo
+ assert_atom_logo_to_s(Atom::Feed::Logo)
+ assert_atom_logo_to_s(Atom::Feed::Entry::Source::Logo)
+ assert_atom_logo_to_s(Atom::Entry::Source::Logo)
+ end
+
+ def test_atom_rights
+ assert_atom_text_construct_to_s(Atom::Feed::Rights)
+ assert_atom_text_construct_to_s(Atom::Feed::Entry::Rights)
+ assert_atom_text_construct_to_s(Atom::Entry::Rights)
+ assert_atom_text_construct_to_s(Atom::Feed::Entry::Source::Rights)
+ assert_atom_text_construct_to_s(Atom::Entry::Source::Rights)
+ end
+
+ def test_atom_subtitle
+ assert_atom_text_construct_to_s(Atom::Feed::Subtitle)
+ assert_atom_text_construct_to_s(Atom::Feed::Entry::Source::Subtitle)
+ assert_atom_text_construct_to_s(Atom::Entry::Source::Subtitle)
+ end
+
+ def test_atom_title
+ assert_atom_text_construct_to_s(Atom::Feed::Title)
+ assert_atom_text_construct_to_s(Atom::Feed::Entry::Title)
+ assert_atom_text_construct_to_s(Atom::Entry::Title)
+ assert_atom_text_construct_to_s(Atom::Feed::Entry::Source::Title)
+ assert_atom_text_construct_to_s(Atom::Entry::Source::Title)
+ end
+
+ def test_atom_updated
+ assert_atom_date_construct_to_s(Atom::Feed::Updated)
+ assert_atom_date_construct_to_s(Atom::Feed::Entry::Updated)
+ assert_atom_date_construct_to_s(Atom::Entry::Updated)
+ assert_atom_date_construct_to_s(Atom::Feed::Entry::Source::Updated)
+ assert_atom_date_construct_to_s(Atom::Entry::Source::Updated)
+ end
+
+ def test_atom_content
+ assert_atom_content_to_s(Atom::Feed::Entry::Content)
+ assert_atom_content_to_s(Atom::Entry::Content)
+ end
+
+ def test_atom_published
+ assert_atom_date_construct_to_s(Atom::Feed::Entry::Published)
+ assert_atom_date_construct_to_s(Atom::Entry::Published)
+ end
+
+ def test_atom_summary
+ assert_atom_text_construct_to_s(Atom::Feed::Entry::Summary)
+ assert_atom_text_construct_to_s(Atom::Entry::Summary)
+ end
+
+
+ def test_to_xml(with_convenience_way=true)
+ atom = RSS::Parser.parse(make_feed)
+ assert_equal(atom.to_s, atom.to_xml)
+ assert_equal(atom.to_s, atom.to_xml("atom"))
+ assert_equal(atom.to_s, atom.to_xml("atom1.0"))
+ assert_equal(atom.to_s, atom.to_xml("atom1.0:feed"))
+ assert_equal(atom.to_s, atom.to_xml("atom:feed"))
+
+ rss09_xml = atom.to_xml("0.91") do |maker|
+ maker.channel.language = "en-us"
+ maker.channel.link = "http://example.com/"
+ if with_convenience_way
+ maker.channel.description = atom.title.content
+ else
+ maker.channel.description {|d| d.content = atom.title.content}
+ end
+
+ maker.image.url = "http://example.com/logo.png"
+ maker.image.title = "Logo"
+ end
+ rss09 = RSS::Parser.parse(rss09_xml)
+ assert_equal(["rss", "0.91", nil], rss09.feed_info)
+
+ rss20_xml = atom.to_xml("2.0") do |maker|
+ maker.channel.link = "http://example.com/"
+ if with_convenience_way
+ maker.channel.description = atom.title.content
+ else
+ maker.channel.description {|d| d.content = atom.title.content}
+ end
+ end
+ rss20 = RSS::Parser.parse(rss20_xml)
+ assert_equal("2.0", rss20.rss_version)
+ assert_equal(["rss", "2.0", nil], rss20.feed_info)
+ end
+
+ def test_to_xml_with_new_api_since_018
+ test_to_xml(false)
+ end
+
+ private
+ def setup_entry(entry)
+ _wrap_assertion do
+ assert_equal("", entry.to_s)
+
+ author = entry.class::Author.new
+ name = entry.class::Author::Name.new
+ name.content = "an author"
+ author.name = name
+ assert_not_equal("", author.to_s)
+ entry.authors << author
+ assert_equal("", entry.to_s)
+
+ id = entry.class::Id.new
+ id.content = "http://example.com/atom.xml"
+ assert_not_equal("", id.to_s)
+ entry.id = id
+ assert_equal("", entry.to_s)
+
+ title = entry.class::Title.new
+ title.content = "a title"
+ assert_not_equal("", title.to_s)
+ entry.title = title
+ assert_equal("", entry.to_s)
+
+ updated = entry.class::Updated.new
+ updated.content = Time.now
+ assert_not_equal("", updated.to_s)
+ entry.updated = updated
+ assert_not_equal("", entry.to_s)
+ end
+ end
+
+
+ def assert_atom_person_to_s(target_class)
+ _wrap_assertion do
+ name = "A person"
+ uri = "http://example.com/person/"
+ email = "person@example.com"
+
+ target = target_class.new
+ assert_equal("", target.to_s)
+
+ target = target_class.new
+ person_name = target_class::Name.new
+ person_name.content = name
+ target.name = person_name
+ xml_target = REXML::Document.new(target.to_s).root
+ assert_equal(["name"], xml_target.elements.collect {|e| e.name})
+ assert_equal([name], xml_target.elements.collect {|e| e.text})
+
+ person_uri = target_class::Uri.new
+ person_uri.content = uri
+ target.uri = person_uri
+ xml_target = REXML::Document.new(target.to_s).root
+ assert_equal(["name", "uri"], xml_target.elements.collect {|e| e.name})
+ assert_equal([name, uri], xml_target.elements.collect {|e| e.text})
+
+ person_email = target_class::Email.new
+ person_email.content = email
+ target.email = person_email
+ xml_target = REXML::Document.new(target.to_s).root
+ assert_equal(["name", "uri", "email"],
+ xml_target.elements.collect {|e| e.name})
+ assert_equal([name, uri, email],
+ xml_target.elements.collect {|e| e.text})
+ end
+ end
+
+ def assert_atom_category_to_s(target_class)
+ _wrap_assertion do
+ term = "music"
+ scheme = "http://example.com/music"
+ label = "Music"
+
+ category = target_class.new
+ assert_equal("", category.to_s)
+
+ category = target_class.new
+ category.scheme = scheme
+ assert_equal("", category.to_s)
+
+ category = target_class.new
+ category.label = label
+ assert_equal("", category.to_s)
+
+ category = target_class.new
+ category.scheme = scheme
+ category.label = label
+ assert_equal("", category.to_s)
+
+ category = target_class.new
+ category.term = term
+ xml = REXML::Document.new(category.to_s).root
+ assert_rexml_element([], {"term" => term}, nil, xml)
+
+ category = target_class.new
+ category.term = term
+ category.scheme = scheme
+ xml = REXML::Document.new(category.to_s).root
+ assert_rexml_element([], {"term" => term, "scheme" => scheme}, nil, xml)
+
+ category = target_class.new
+ category.term = term
+ category.label = label
+ xml = REXML::Document.new(category.to_s).root
+ assert_rexml_element([], {"term" => term, "label" => label}, nil, xml)
+
+ category = target_class.new
+ category.term = term
+ category.scheme = scheme
+ category.label = label
+ xml = REXML::Document.new(category.to_s).root
+ attrs = {"term" => term, "scheme" => scheme, "label" => label}
+ assert_rexml_element([], attrs, nil, xml)
+ end
+ end
+
+ def assert_atom_generator_to_s(target_class)
+ _wrap_assertion do
+ content = "Feed generator"
+ uri = "http://example.com/generator"
+ version = "0.0.1"
+
+ generator = target_class.new
+ assert_equal("", generator.to_s)
+
+ generator = target_class.new
+ generator.uri = uri
+ assert_equal("", generator.to_s)
+
+ generator = target_class.new
+ generator.version = version
+ assert_equal("", generator.to_s)
+
+ generator = target_class.new
+ generator.uri = uri
+ generator.version = version
+ assert_equal("", generator.to_s)
+
+ generator = target_class.new
+ generator.content = content
+ xml = REXML::Document.new(generator.to_s).root
+ assert_rexml_element([], {}, content, xml)
+
+ generator = target_class.new
+ generator.content = content
+ generator.uri = uri
+ xml = REXML::Document.new(generator.to_s).root
+ assert_rexml_element([], {"uri" => uri}, content, xml)
+
+ generator = target_class.new
+ generator.content = content
+ generator.version = version
+ xml = REXML::Document.new(generator.to_s).root
+ assert_rexml_element([], {"version" => version}, content, xml)
+
+ generator = target_class.new
+ generator.content = content
+ generator.uri = uri
+ generator.version = version
+ xml = REXML::Document.new(generator.to_s).root
+ assert_rexml_element([], {"uri" => uri, "version" => version},
+ content, xml)
+ end
+ end
+
+ def assert_atom_icon_to_s(target_class)
+ _wrap_assertion do
+ content = "http://example.com/icon.png"
+
+ icon = target_class.new
+ assert_equal("", icon.to_s)
+
+ icon = target_class.new
+ icon.content = content
+ xml = REXML::Document.new(icon.to_s).root
+ assert_rexml_element([], {}, content, xml)
+ end
+ end
+
+ def assert_atom_id_to_s(target_class)
+ _wrap_assertion do
+ content = "http://example.com/1"
+
+ id = target_class.new
+ assert_equal("", id.to_s)
+
+ id = target_class.new
+ id.content = content
+ xml = REXML::Document.new(id.to_s).root
+ assert_rexml_element([], {}, content, xml)
+ end
+ end
+
+ def assert_atom_link_to_s(target_class)
+ _wrap_assertion do
+ href = "http://example.com/atom.xml"
+ optvs = {
+ 'rel' => "self",
+ 'type' => "application/atom+xml",
+ 'hreflang' => "ja",
+ 'title' => "Atom Feed",
+ 'length' => "801",
+ }
+
+ link = target_class.new
+ assert_equal("", link.to_s)
+
+ link = target_class.new
+ link.href = href
+ xml = REXML::Document.new(link.to_s).root
+ assert_rexml_element([], {"href" => href}, nil, xml)
+
+ optional_arguments = %w(rel type hreflang title length)
+ optional_arguments.each do |name|
+ rest = optional_arguments.reject {|x| x == name}
+
+ link = target_class.new
+ link.__send__("#{name}=", optvs[name])
+ assert_equal("", link.to_s)
+
+ rest.each do |n|
+ link.__send__("#{n}=", optvs[n])
+ assert_equal("", link.to_s)
+ end
+
+ link = target_class.new
+ link.href = href
+ link.__send__("#{name}=", optvs[name])
+ attrs = [["href", href], [name, optvs[name]]]
+ xml = REXML::Document.new(link.to_s).root
+ assert_rexml_element([], attrs, nil, xml)
+
+ rest.each do |n|
+ link.__send__("#{n}=", optvs[n])
+ attrs << [n, optvs[n]]
+ xml = REXML::Document.new(link.to_s).root
+ assert_rexml_element([], attrs, nil, xml)
+ end
+ end
+ end
+ end
+
+ def assert_atom_logo_to_s(target_class)
+ _wrap_assertion do
+ content = "http://example.com/logo.png"
+
+ logo = target_class.new
+ assert_equal("", logo.to_s)
+
+ logo = target_class.new
+ logo.content = content
+ xml = REXML::Document.new(logo.to_s).root
+ assert_rexml_element([], {}, content, xml)
+ end
+ end
+
+ def assert_atom_text_construct_to_s(target_class)
+ _wrap_assertion do
+ text_content = "plain text"
+ html_content = "<em>#{text_content}</em>"
+ xhtml_uri = "http://www.w3.org/1999/xhtml"
+ xhtml_em = RSS::XML::Element.new("em", nil, xhtml_uri, {}, text_content)
+ xhtml_content = RSS::XML::Element.new("div", nil, xhtml_uri,
+ {"xmlns" => xhtml_uri},
+ [xhtml_em])
+
+ text = target_class.new
+ assert_equal("", text.to_s)
+
+ text = target_class.new
+ text.type = "text"
+ assert_equal("", text.to_s)
+
+ text = target_class.new
+ text.content = text_content
+ xml = REXML::Document.new(text.to_s).root
+ assert_rexml_element([], {}, text_content, xml)
+
+ text = target_class.new
+ text.type = "text"
+ text.content = text_content
+ xml = REXML::Document.new(text.to_s).root
+ assert_rexml_element([], {"type" => "text"}, text_content, xml)
+
+ text = target_class.new
+ text.type = "html"
+ text.content = html_content
+ xml = REXML::Document.new(text.to_s).root
+ assert_rexml_element([], {"type" => "html"}, html_content, xml)
+
+ text = target_class.new
+ text.type = "xhtml"
+ text.content = xhtml_content
+ assert_equal("", text.to_s)
+
+ text = target_class.new
+ text.type = "xhtml"
+ text.__send__(target_class.xml_setter, xhtml_content)
+ xml = REXML::Document.new(text.to_s).root
+ assert_rexml_element([[xhtml_uri, "div"]], {"type" => "xhtml"},
+ nil, xml)
+ assert_rexml_element([[xhtml_uri, "em"]], nil, nil, xml.elements[1])
+ assert_rexml_element([], {}, text_content, xml.elements[1].elements[1])
+
+ text = target_class.new
+ text.type = "xhtml"
+ text.__send__(target_class.xml_setter, xhtml_em)
+ xml = REXML::Document.new(text.to_s).root
+ assert_rexml_element([[xhtml_uri, "div"]], {"type" => "xhtml"},
+ nil, xml)
+ assert_rexml_element([[xhtml_uri, "em"]], nil, nil, xml.elements[1])
+ assert_rexml_element([], {}, text_content, xml.elements[1].elements[1])
+ end
+ end
+
+ def assert_atom_date_construct_to_s(target_class)
+ _wrap_assertion do
+ date = target_class.new
+ assert_equal("", date.to_s)
+
+ [
+ "2003-12-13T18:30:02Z",
+ "2003-12-13T18:30:02.25Z",
+ "2003-12-13T18:30:02+01:00",
+ "2003-12-13T18:30:02.25+01:00",
+ ].each do |content|
+ date = target_class.new
+ date.content = content
+ xml = REXML::Document.new(date.to_s).root
+ assert_rexml_element([], {}, content, xml, :time)
+
+ date = target_class.new
+ date.content = Time.parse(content)
+ xml = REXML::Document.new(date.to_s).root
+ assert_rexml_element([], {}, content, xml, :time)
+ end
+ end
+ end
+
+ def assert_atom_content_to_s(target_class)
+ _wrap_assertion do
+ assert_atom_text_construct_to_s(target_class)
+ assert_atom_content_inline_other_xml_to_s(target_class)
+ assert_atom_content_inline_other_text_to_s(target_class)
+ assert_atom_content_inline_other_base64_to_s(target_class)
+ assert_atom_content_out_of_line_to_s(target_class)
+ end
+ end
+
+ def assert_atom_content_inline_other_xml_to_s(target_class)
+ _wrap_assertion do
+ content = target_class.new
+ content.type = "text/xml"
+ assert_equal("", content.to_s)
+
+ content = target_class.new
+ content.type = "text/xml"
+ content.xml = RSS::XML::Element.new("em")
+ xml = REXML::Document.new(content.to_s).root
+ assert_rexml_element([["", "em"]], {"type" => "text/xml"}, nil, xml)
+ end
+ end
+
+ def assert_atom_content_inline_other_text_to_s(target_class)
+ _wrap_assertion do
+ content = target_class.new
+ content.type = "text/plain"
+ assert_equal("", content.to_s)
+
+ content = target_class.new
+ content.type = "text/plain"
+ content.xml = RSS::XML::Element.new("em")
+ assert_equal("", content.to_s)
+
+ content = target_class.new
+ content.type = "text/plain"
+ content.content = "content"
+ xml = REXML::Document.new(content.to_s).root
+ assert_rexml_element([], {"type" => "text/plain"}, "content", xml)
+ end
+ end
+
+ def assert_atom_content_inline_other_base64_to_s(target_class)
+ _wrap_assertion do
+ type = "image/png"
+ png_file = File.join(File.dirname(__FILE__), "dot.png")
+ original_content = File.open(png_file, "rb") do |file|
+ file.read.force_encoding("binary")
+ end
+
+ content = target_class.new
+ content.type = type
+ content.content = original_content
+ xml = REXML::Document.new(content.to_s).root
+ assert_rexml_element([], {"type" => type},
+ [original_content].pack("m").delete("\n"),
+ xml)
+ end
+ end
+
+ def assert_atom_content_out_of_line_to_s(target_class)
+ _wrap_assertion do
+ type = "application/zip"
+ src = "http://example.com/xxx.zip"
+
+ content = target_class.new
+ assert(!content.out_of_line?)
+ content.src = src
+ assert(content.out_of_line?)
+ xml = REXML::Document.new(content.to_s).root
+ assert_rexml_element([], {"src" => src}, nil, xml)
+
+ content = target_class.new
+ assert(!content.out_of_line?)
+ content.type = type
+ assert(!content.out_of_line?)
+ content.src = src
+ assert(content.out_of_line?)
+ xml = REXML::Document.new(content.to_s).root
+ assert_rexml_element([], {"type" => type, "src" => src}, nil, xml)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_content.rb b/jni/ruby/test/rss/test_content.rb
new file mode 100644
index 0000000..13f22a2
--- /dev/null
+++ b/jni/ruby/test/rss/test_content.rb
@@ -0,0 +1,104 @@
+require "cgi"
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/content"
+
+module RSS
+ class TestContent < TestCase
+ def setup
+ @prefix = "content"
+ @uri = "http://purl.org/rss/1.0/modules/content/"
+
+ @elems = {
+ :encoded => "<em>ATTENTION</em>",
+ }
+
+ @content_nodes = @elems.collect do |name, value|
+ "<#{@prefix}:#{name}>#{CGI.escapeHTML(value.to_s)}</#{@prefix}:#{name}>"
+ end.join("\n")
+
+ @rss10_source = make_RDF(<<-EOR, {@prefix => @uri})
+#{make_channel()}
+#{make_image()}
+#{make_item(@content_nodes)}
+#{make_textinput()}
+EOR
+
+ @rss10 = Parser.parse(@rss10_source)
+
+
+ @rss20_source = make_rss20(<<-EOR, {@prefix => @uri})
+#{make_channel20(make_item20(@content_nodes))}
+EOR
+
+ @rss20 = Parser.parse(@rss20_source)
+ end
+
+ def test_parser
+ assert_nothing_raised do
+ Parser.parse(@rss10_source)
+ end
+
+ assert_nothing_raised do
+ Parser.parse(@rss20_source)
+ end
+
+ @elems.each do |tag, value|
+ tag_name = "#{@prefix}:#{tag}"
+ content_encodes = make_element(tag_name, {}, value) * 2
+
+ assert_too_much_tag(tag.to_s, "item") do
+ Parser.parse(make_RDF(<<-EOR, {@prefix => @uri}))
+#{make_channel}
+#{make_item(content_encodes)}
+EOR
+ end
+
+ assert_too_much_tag(tag.to_s, "item") do
+ Parser.parse(make_rss20(<<-EOR, {@prefix => @uri}))
+#{make_channel20(make_item20(content_encodes))}
+EOR
+ end
+ end
+ end
+
+ def test_accessor
+ new_value = {
+ :encoded => "<![CDATA[<it>hoge</it>]]>",
+ }
+
+ @elems.each do |name, value|
+ [@rss10, @rss20].each do |rss|
+ meth = "#{RSS::CONTENT_PREFIX}_#{name}"
+ parent = rss.items.last
+ assert_equal(value, parent.__send__(meth))
+ parent.__send__("#{meth}=", new_value[name].to_s)
+ assert_equal(new_value[name], parent.__send__(meth))
+ end
+ end
+ end
+
+ def test_to_s
+ @elems.each do |name, value|
+ excepted = make_element("#{@prefix}:#{name}", {}, value)
+ meth = "#{RSS::CONTENT_PREFIX}_#{name}_element"
+ [@rss10, @rss20].each do |rss|
+ assert_equal(excepted, rss.items.last.__send__(meth))
+ end
+ end
+
+ [@rss10_source, @rss20_source].each do |source|
+ REXML::Document.new(source).root.each_element do |parent|
+ next unless parent.name != "item"
+ parent.each_element do |elem|
+ if elem.namespace == @uri
+ assert_equal(elem.text, @elems[elem.name.intern].to_s)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_dublincore.rb b/jni/ruby/test/rss/test_dublincore.rb
new file mode 100644
index 0000000..eb03d84
--- /dev/null
+++ b/jni/ruby/test/rss/test_dublincore.rb
@@ -0,0 +1,269 @@
+require "cgi"
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/dublincore"
+
+module RSS
+ class TestDublinCore < TestCase
+ def setup
+ @rss10_parents = [%w(channel), %w(image), %w(item), %w(textinput)]
+
+ @rss10_source = make_RDF(<<-EOR, {DC_PREFIX => DC_URI})
+#{make_channel(DC_NODES)}
+#{make_image(DC_NODES)}
+#{make_item(DC_NODES)}
+#{make_textinput(DC_NODES)}
+EOR
+
+ @rss20_parents = [%w(channel), %w(items last)]
+
+ @rss20_source = make_rss20(<<-EOR, {DC_PREFIX => DC_URI})
+#{make_channel20(DC_NODES + make_item20(DC_NODES))}
+EOR
+
+ @atom_feed_parents = [[], %w(entries last)]
+
+ @atom_feed_source = make_feed(<<-EOR, {DC_PREFIX => DC_URI})
+#{DC_NODES}
+#{make_entry(DC_NODES)}
+EOR
+
+ @atom_entry_parents = [[]]
+
+ @atom_entry_source = make_entry_document(<<-EOR, {DC_PREFIX => DC_URI})
+#{DC_NODES}
+EOR
+ end
+
+ def test_parser
+ rss10_maker = Proc.new do |content, xmlns|
+ make_RDF(<<-EOR, xmlns)
+#{make_channel(content)}
+#{make_image(content)}
+#{make_item(content)}
+#{make_textinput(content)}
+EOR
+ end
+ assert_dc_parse(@rss10_source, @rss10_parents, false, &rss10_maker)
+ assert_dc_parse(@rss10_source, @rss10_parents, true, &rss10_maker)
+
+ rss20_maker = Proc.new do |content, xmlns|
+ make_rss20(<<-EOR, xmlns)
+#{make_channel20(content + make_item20(content))}
+EOR
+ end
+ assert_dc_parse(@rss20_source, @rss20_parents, false, &rss20_maker)
+ assert_dc_parse(@rss20_source, @rss20_parents, true, &rss20_maker)
+
+ atom_feed_maker = Proc.new do |content, xmlns|
+ make_feed(<<-EOR, xmlns)
+#{content}
+#{make_entry(content)}
+EOR
+ end
+ assert_dc_parse(@atom_feed_source, @atom_feed_parents, false,
+ &atom_feed_maker)
+ assert_dc_parse(@atom_feed_source, @atom_feed_parents, true,
+ &atom_feed_maker)
+
+ atom_entry_maker = Proc.new do |content, xmlns|
+ make_entry_document(<<-EOR, xmlns)
+#{content}
+EOR
+ end
+ assert_dc_parse(@atom_entry_source, @atom_entry_parents, false,
+ &atom_entry_maker)
+ assert_dc_parse(@atom_entry_source, @atom_entry_parents, true,
+ &atom_entry_maker)
+ end
+
+ def test_singular_accessor
+ assert_dc_singular_accessor(@rss10_source, @rss10_parents)
+ assert_dc_singular_accessor(@rss20_source, @rss20_parents)
+ assert_dc_singular_accessor(@atom_feed_source, @atom_feed_parents)
+ assert_dc_singular_accessor(@atom_entry_source, @atom_entry_parents)
+ end
+
+ def test_plural_accessor
+ assert_dc_plural_accessor(@rss10_source, @rss10_parents, false)
+ assert_dc_plural_accessor(@rss10_source, @rss10_parents, true)
+
+ assert_dc_plural_accessor(@rss20_source, @rss20_parents, false)
+ assert_dc_plural_accessor(@rss20_source, @rss20_parents, true)
+
+ assert_dc_plural_accessor(@atom_feed_source, @atom_feed_parents, false)
+ assert_dc_plural_accessor(@atom_feed_source, @atom_feed_parents, true)
+
+ assert_dc_plural_accessor(@atom_entry_source, @atom_entry_parents, false)
+ assert_dc_plural_accessor(@atom_entry_source, @atom_entry_parents, true)
+ end
+
+ def test_to_s
+ assert_dc_to_s(@rss10_source, @rss10_parents, false)
+ assert_dc_to_s(@rss10_source, @rss10_parents, true)
+
+ targets = ["channel", "channel/item[3]"]
+ assert_dc_to_s(@rss20_source, @rss20_parents, false, targets)
+ assert_dc_to_s(@rss20_source, @rss20_parents, true, targets)
+
+ targets = [".", "entry"]
+ assert_dc_to_s(@atom_feed_source, @atom_feed_parents, false, targets)
+ assert_dc_to_s(@atom_feed_source, @atom_feed_parents, true, targets)
+
+ targets = ["."]
+ assert_dc_to_s(@atom_entry_source, @atom_entry_parents, false, targets)
+ assert_dc_to_s(@atom_entry_source, @atom_entry_parents, true, targets)
+ end
+
+ private
+ def dc_plural_suffix(name, check_backward_compatibility)
+ if name == :rights
+ if check_backward_compatibility
+ "es"
+ else
+ "_list"
+ end
+ else
+ "s"
+ end
+ end
+
+ def assert_dc_parse(source, parents, check_backward_compatibility, &maker)
+ assert_nothing_raised do
+ Parser.parse(source)
+ end
+
+ DC_ELEMENTS.each do |name, value|
+ parents.each do |parent_readers|
+ feed = nil
+ assert_nothing_raised do
+ tag = "#{DC_PREFIX}:#{name}"
+ dc_content = "<#{tag}>#{value}</#{tag}>\n"
+ dc_content *= 2
+ feed = Parser.parse(maker.call(dc_content, {DC_PREFIX => DC_URI}))
+ end
+ parent = chain_reader(feed, parent_readers)
+
+ plural_suffix = dc_plural_suffix(name, check_backward_compatibility)
+ plural_reader = "dc_#{name}#{plural_suffix}"
+ values = parent.__send__(plural_reader).collect(&:value)
+ value = CGI.unescapeHTML(value) if value.kind_of?(String)
+ assert_equal([value, value], values)
+ end
+ end
+ end
+
+ def assert_dc_singular_accessor(source, parents)
+ feed = Parser.parse(source)
+ new_value = "hoge"
+
+ parents.each do |parent_readers|
+ parent = chain_reader(feed, parent_readers)
+ DC_ELEMENTS.each do |name, value|
+ parsed_value = parent.__send__("dc_#{name}")
+ value = CGI.unescapeHTML(value) if value.kind_of?(String)
+ assert_equal(value, parsed_value)
+ if name == :date
+ t = Time.iso8601("2003-01-01T02:30:23+09:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+ parent.__send__("dc_#{name}=", t.iso8601)
+ assert_equal(t, parent.__send__("dc_#{name}"))
+ if parent.class.method_defined?(:date_without_dc_date=)
+ assert_nil(parent.date)
+ else
+ assert_equal(t, parent.date)
+ end
+
+ parent.date = value
+ assert_equal(value, parent.date)
+ assert_equal(value, parent.__send__("dc_#{name}"))
+ else
+ parent.__send__("dc_#{name}=", new_value)
+ assert_equal(new_value, parent.__send__("dc_#{name}"))
+ end
+ end
+ end
+ end
+
+ def assert_dc_plural_accessor(source, parents, check_backward_compatibility)
+ feed = Parser.parse(source)
+ new_value = "hoge"
+
+ DC_ELEMENTS.each do |name, value|
+ parents.each do |parent_readers|
+ parent = chain_reader(feed, parent_readers)
+ parsed_value = parent.__send__("dc_#{name}")
+ value = CGI.unescapeHTML(value) if value.kind_of?(String)
+ assert_equal(value, parsed_value)
+
+ plural_suffix = dc_plural_suffix(name, check_backward_compatibility)
+ plural_reader = "dc_#{name}#{plural_suffix}"
+ klass_name = "DublinCore#{Utils.to_class_name(name.to_s)}"
+ klass = DublinCoreModel.const_get(klass_name)
+ if name == :date
+ t = Time.iso8601("2003-01-01T02:30:23+09:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+ elems = parent.__send__(plural_reader)
+ elems << klass.new(t.iso8601)
+ new_elems = parent.__send__(plural_reader)
+ values = new_elems.collect{|x| x.value}
+ assert_equal([parent.__send__("dc_#{name}"), t], values)
+ else
+ elems = parent.__send__(plural_reader)
+ elems << klass.new(new_value)
+ new_elems = parent.__send__(plural_reader)
+ values = new_elems.collect{|x| x.value}
+ assert_equal([parent.__send__("dc_#{name}"), new_value],
+ values)
+ end
+ end
+ end
+ end
+
+ def assert_dc_to_s(source, parents, check_backward_compatibility,
+ targets=nil)
+ feed = Parser.parse(source)
+
+ DC_ELEMENTS.each do |name, value|
+ excepted = "<#{DC_PREFIX}:#{name}>#{value}</#{DC_PREFIX}:#{name}>"
+ parents.each do |parent_readers|
+ parent = chain_reader(feed, parent_readers)
+ assert_equal(excepted, parent.__send__("dc_#{name}_elements"))
+ end
+
+ plural_suffix = dc_plural_suffix(name, check_backward_compatibility)
+ reader = "dc_#{name}#{plural_suffix}"
+ excepted = Array.new(2, excepted).join("\n")
+ parents.each do |parent_readers|
+ parent = chain_reader(feed, parent_readers)
+ elems = parent.__send__(reader)
+ klass_name = "DublinCore#{Utils.to_class_name(name.to_s)}"
+ klass = DublinCoreModel.const_get(klass_name)
+ elems << klass.new(parent.__send__("dc_#{name}"))
+ assert_equal(excepted, parent.__send__("dc_#{name}_elements"))
+ end
+ end
+
+ targets ||= parents.collect do |parent_readers|
+ parent_readers.first
+ end
+ feed_root = REXML::Document.new(source).root
+ targets.each do |target_xpath|
+ parent = feed_root.elements[target_xpath]
+ parent.each_element do |elem|
+ if elem.namespace == DC_URI
+ assert_equal(elem.text,
+ CGI.unescapeHTML(DC_ELEMENTS[elem.name.intern].to_s))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_image.rb b/jni/ruby/test/rss/test_image.rb
new file mode 100644
index 0000000..d8f0b26
--- /dev/null
+++ b/jni/ruby/test/rss/test_image.rb
@@ -0,0 +1,214 @@
+require "cgi"
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/image"
+
+module RSS
+ class TestImage < TestCase
+
+ def setup
+ @prefix = "image"
+ @uri = "http://purl.org/rss/1.0/modules/image/"
+
+ @favicon_attrs = {
+ "rdf:about" => "http://www.kuro5hin.org/favicon.ico",
+ "#{@prefix}:size" => "small",
+ }
+ @favicon_contents = {"dc:title" => "Kuro5hin",}
+ @items = [
+ [
+ {
+ "rdf:about" => "http://www.example.org/item.png",
+ "rdf:resource" => "http://www.example.org/item",
+ },
+ {
+ "dc:title" => "Example Image",
+ "#{@prefix}:width" => "100",
+ "#{@prefix}:height" => "65",
+ },
+ ],
+ [
+ {
+ "rdf:about" => "http://www.kuro5hin.org/images/topics/culture.jpg",
+ },
+ {
+ "dc:title" => "Culture",
+ "#{@prefix}:width" => "80",
+ "#{@prefix}:height" => "50",
+ },
+ ]
+ ]
+
+
+ @channel_nodes = make_element("#{@prefix}:favicon",
+ @favicon_attrs,
+ @favicon_contents)
+ items = ""
+ @items.each do |attrs, contents|
+ image_item = make_element("#{@prefix}:item", attrs, contents)
+ items << make_item(image_item)
+ end
+
+ @ns = {
+ @prefix => @uri,
+ DC_PREFIX => DC_URI,
+ }
+ @rss_source = make_RDF(<<-EOR, @ns)
+#{make_channel(@channel_nodes)}
+#{make_image}
+#{items}
+#{make_textinput}
+EOR
+
+ @rss = Parser.parse(@rss_source)
+ end
+
+ def test_parser
+ assert_nothing_raised do
+ Parser.parse(@rss_source)
+ end
+
+ assert_too_much_tag("favicon", "channel") do
+ Parser.parse(make_RDF(<<-EOR, @ns))
+#{make_channel(@channel_nodes * 2)}
+#{make_item}
+EOR
+ end
+
+ attrs = {"rdf:about" => "http://www.example.org/item.png"}
+ contents = [["#{@prefix}:width", "80"]] * 5
+ image_item = make_element("#{@prefix}:item", attrs, contents)
+ assert_too_much_tag("width", "item") do
+ Parser.parse(make_RDF(<<-EOR, @ns))
+#{make_channel}
+#{make_item(image_item)}
+EOR
+ end
+ end
+
+ def test_favicon_accessor
+ favicon = @rss.channel.image_favicon
+ [
+ %w(about rdf:about http://example.com/favicon.ico),
+ %w(size image:size large),
+ %w(image_size image:size medium),
+ ].each do |name, full_name, new_value|
+ assert_equal(@favicon_attrs[full_name], favicon.__send__(name))
+ favicon.__send__("#{name}=", new_value)
+ assert_equal(new_value, favicon.__send__(name))
+ favicon.__send__("#{name}=", @favicon_attrs[full_name])
+ assert_equal(@favicon_attrs[full_name], favicon.__send__(name))
+ end
+
+ %w(small medium large).each do |value|
+ assert_nothing_raised do
+ favicon.size = value
+ favicon.image_size = value
+ end
+ end
+
+ %w(aaa AAA SMALL MEDIUM LARGE).each do |value|
+ args = ["#{@prefix}:favicon", value, "#{@prefix}:size"]
+ assert_not_available_value(*args) do
+ favicon.size = value
+ end
+ assert_not_available_value(*args) do
+ favicon.image_size = value
+ end
+ end
+
+ [
+ %w(dc_title dc:title sample-favicon),
+ ].each do |name, full_name, new_value|
+ assert_equal(@favicon_contents[full_name], favicon.__send__(name))
+ favicon.__send__("#{name}=", new_value)
+ assert_equal(new_value, favicon.__send__(name))
+ favicon.__send__("#{name}=", @favicon_contents[full_name])
+ assert_equal(@favicon_contents[full_name], favicon.__send__(name))
+ end
+ end
+
+ def test_item_accessor
+ @rss.items.each_with_index do |item, i|
+ image_item = item.image_item
+ attrs, contents = @items[i]
+ [
+ %w(about rdf:about http://example.com/image.png),
+ %w(resource rdf:resource http://example.com/),
+ ].each do |name, full_name, new_value|
+ assert_equal(attrs[full_name], image_item.__send__(name))
+ image_item.__send__("#{name}=", new_value)
+ assert_equal(new_value, image_item.__send__(name))
+ image_item.__send__("#{name}=", attrs[full_name])
+ assert_equal(attrs[full_name], image_item.__send__(name))
+ end
+
+ [
+ ["width", "image:width", "111"],
+ ["image_width", "image:width", "44"],
+ ["height", "image:height", "222"],
+ ["image_height", "image:height", "88"],
+ ].each do |name, full_name, new_value|
+ assert_equal(contents[full_name].to_i, image_item.__send__(name))
+ image_item.__send__("#{name}=", new_value)
+ assert_equal(new_value.to_i, image_item.__send__(name))
+ image_item.__send__("#{name}=", contents[full_name])
+ assert_equal(contents[full_name].to_i, image_item.__send__(name))
+ end
+
+ [
+ ["dc_title", "dc:title", "sample-image"],
+ ].each do |name, full_name, new_value|
+ assert_equal(contents[full_name], image_item.__send__(name))
+ image_item.__send__("#{name}=", new_value)
+ assert_equal(new_value, image_item.__send__(name))
+ image_item.__send__("#{name}=", contents[full_name])
+ assert_equal(contents[full_name], image_item.__send__(name))
+ end
+ end
+ end
+
+ def test_favicon_to_s
+ favicon = @rss.channel.image_favicon
+ expected_xml = image_xmlns_container(make_element("#{@prefix}:favicon",
+ @favicon_attrs,
+ @favicon_contents))
+ expected = REXML::Document.new(expected_xml)
+ actual_xml = image_xmlns_container(favicon.to_s(false, ""))
+ actual = REXML::Document.new(actual_xml)
+ assert_equal(expected.to_s, actual.to_s)
+ end
+
+ def test_item_to_s
+ @rss.items.each_with_index do |item, i|
+ attrs, contents = @items[i]
+ expected_xml = make_element("#{@prefix}:item", attrs, contents)
+ expected_xml = image_xmlns_container(expected_xml)
+ expected = REXML::Document.new(expected_xml)
+ actual_xml = image_xmlns_container(item.image_item.to_s(false, ""))
+ actual = REXML::Document.new(actual_xml)
+
+ assert_equal(expected[0].attributes, actual[0].attributes)
+
+ %w(image:height image:width dc:title).each do |name|
+ actual_target = actual.elements["//#{name}"]
+ expected_target = expected.elements["//#{name}"]
+ assert_equal(expected_target.to_s, actual_target.to_s)
+ end
+ end
+ end
+
+ private
+ def image_xmlns_container(content)
+ xmlns_container({
+ @prefix => @uri,
+ "dc" => "http://purl.org/dc/elements/1.1/",
+ "rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ },
+ content)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_inherit.rb b/jni/ruby/test/rss/test_inherit.rb
new file mode 100644
index 0000000..8b640dc
--- /dev/null
+++ b/jni/ruby/test/rss/test_inherit.rb
@@ -0,0 +1,40 @@
+require_relative "rss-testcase"
+
+require "rss/1.0"
+
+module RSS
+ class TestInherit < TestCase
+
+ class InheritedImage < RSS::RDF::Image
+ def self.indent_size; 1; end
+ def self.tag_name; 'image'; end
+ end
+
+ def setup
+ @rss = make_RDF(<<-EOR)
+#{make_channel}
+#{make_image}
+#{make_item}
+#{make_textinput}
+EOR
+ end
+
+ def test_inherit
+ rss = RSS::Parser.parse(@rss)
+ orig_image = rss.image
+ prefix = "[INHERIT]"
+ image = InheritedImage.new("#{prefix} #{orig_image.about}")
+ image.title = "#{prefix} #{orig_image.title}"
+ image.url = "#{prefix} #{orig_image.url}"
+ image.link = "#{prefix} #{orig_image.link}"
+ rss.image = image
+
+ new_rss = RSS::Parser.parse(rss.to_s)
+ new_image = new_rss.image
+ assert_equal("#{prefix} #{orig_image.about}", new_image.about)
+ assert_equal("#{prefix} #{orig_image.title}", new_image.title)
+ assert_equal("#{prefix} #{orig_image.url}", new_image.url)
+ assert_equal("#{prefix} #{orig_image.link}", new_image.link)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_itunes.rb b/jni/ruby/test/rss/test_itunes.rb
new file mode 100644
index 0000000..ec06337
--- /dev/null
+++ b/jni/ruby/test/rss/test_itunes.rb
@@ -0,0 +1,347 @@
+require "cgi"
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/2.0"
+require "rss/itunes"
+
+module RSS
+ class TestITunes < TestCase
+ def test_author
+ assert_itunes_author(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+
+ assert_itunes_author(%w(items last)) do |content, xmlns|
+ make_rss20(make_channel20(make_item20(content)), xmlns)
+ end
+ end
+
+ def test_block
+ assert_itunes_block(%w(items last)) do |content, xmlns|
+ make_rss20(make_channel20(make_item20(content)), xmlns)
+ end
+ end
+
+ def test_category
+ assert_itunes_category(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+ end
+
+ def test_image
+ assert_itunes_image(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+ end
+
+ def test_duration
+ assert_itunes_duration(%w(items last)) do |content, xmlns|
+ make_rss20(make_channel20(make_item20(content)), xmlns)
+ end
+ end
+
+ def test_explicit
+ assert_itunes_explicit(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+
+ assert_itunes_explicit(%w(items last)) do |content, xmlns|
+ make_rss20(make_channel20(make_item20(content)), xmlns)
+ end
+ end
+
+ def test_keywords
+ assert_itunes_keywords(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+
+ assert_itunes_keywords(%w(items last)) do |content, xmlns|
+ make_rss20(make_channel20(make_item20(content)), xmlns)
+ end
+ end
+
+ def test_new_feed_url
+ assert_itunes_new_feed_url(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+ end
+
+ def test_owner
+ assert_itunes_owner(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+ end
+
+ def test_subtitle
+ assert_itunes_subtitle(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+
+ assert_itunes_subtitle(%w(items last)) do |content, xmlns|
+ make_rss20(make_channel20(make_item20(content)), xmlns)
+ end
+ end
+
+ def test_summary
+ assert_itunes_summary(%w(channel)) do |content, xmlns|
+ make_rss20(make_channel20(content), xmlns)
+ end
+
+ assert_itunes_summary(%w(items last)) do |content, xmlns|
+ make_rss20(make_channel20(make_item20(content)), xmlns)
+ end
+ end
+
+ private
+ def itunes_rss20_parse(content, &maker)
+ xmlns = {"itunes" => "http://www.itunes.com/dtds/podcast-1.0.dtd"}
+ rss20_xml = maker.call(content, xmlns)
+ ::RSS::Parser.parse(rss20_xml)
+ end
+
+ def assert_itunes_author(readers, &rss20_maker)
+ _wrap_assertion do
+ author = "John Lennon"
+ rss20 = itunes_rss20_parse(tag("itunes:author", author), &rss20_maker)
+ target = chain_reader(rss20, readers)
+ assert_equal(author, target.itunes_author)
+ end
+ end
+
+ def _assert_itunes_block(value, boolean_value, readers, &rss20_maker)
+ rss20 = itunes_rss20_parse(tag("itunes:block", value), &rss20_maker)
+ target = chain_reader(rss20, readers)
+ assert_equal(value, target.itunes_block)
+ assert_equal(boolean_value, target.itunes_block?)
+ end
+
+ def assert_itunes_block(readers, &rss20_maker)
+ _wrap_assertion do
+ _assert_itunes_block("yes", true, readers, &rss20_maker)
+ _assert_itunes_block("Yes", true, readers, &rss20_maker)
+ _assert_itunes_block("no", false, readers, &rss20_maker)
+ _assert_itunes_block("", false, readers, &rss20_maker)
+ end
+ end
+
+ def _assert_itunes_category(categories, readers, &rss20_maker)
+ cats = categories.collect do |category|
+ if category.is_a?(Array)
+ category, sub_category = category
+ tag("itunes:category",
+ tag("itunes:category", nil, {"text" => sub_category}),
+ {"text" => category})
+ else
+ tag("itunes:category", nil, {"text" => category})
+ end
+ end.join
+ rss20 = itunes_rss20_parse(cats, &rss20_maker)
+ target = chain_reader(rss20, readers)
+ actual_categories = target.itunes_categories.collect do |category|
+ cat = category.text
+ if category.itunes_categories.empty?
+ cat
+ else
+ [cat, *category.itunes_categories.collect {|c| c.text}]
+ end
+ end
+ assert_equal(categories, actual_categories)
+ end
+
+ def assert_itunes_category(readers, &rss20_maker)
+ _wrap_assertion do
+ _assert_itunes_category(["Audio Blogs"], readers, &rss20_maker)
+ _assert_itunes_category([["Arts & Entertainment", "Games"]],
+ readers, &rss20_maker)
+ _assert_itunes_category([["Arts & Entertainment", "Games"],
+ ["Technology", "Computers"],
+ "Audio Blogs"],
+ readers, &rss20_maker)
+ end
+ end
+
+ def assert_itunes_image(readers, &rss20_maker)
+ _wrap_assertion do
+ url = "http://example.com/podcasts/everything/AllAboutEverything.jpg"
+ content = tag("itunes:image", nil, {"href" => url})
+ rss20 = itunes_rss20_parse(content, &rss20_maker)
+ target = chain_reader(rss20, readers)
+ assert_not_nil(target.itunes_image)
+ assert_equal(url, target.itunes_image.href)
+
+ assert_missing_attribute("image", "href") do
+ content = tag("itunes:image")
+ itunes_rss20_parse(content, &rss20_maker)
+ end
+ end
+ end
+
+ def _assert_itunes_duration(hour, minute, second, value,
+ readers, &rss20_maker)
+ content = tag("itunes:duration", value)
+ rss20 = itunes_rss20_parse(content, &rss20_maker)
+ duration = chain_reader(rss20, readers).itunes_duration
+ assert_equal(value, duration.content)
+ assert_equal(hour, duration.hour)
+ assert_equal(minute, duration.minute)
+ assert_equal(second, duration.second)
+ end
+
+ def _assert_itunes_duration_not_available_value(value, &rss20_maker)
+ assert_not_available_value("duration", value) do
+ content = tag("itunes:duration", value)
+ itunes_rss20_parse(content, &rss20_maker)
+ end
+ end
+
+ def assert_itunes_duration(readers, &rss20_maker)
+ _wrap_assertion do
+ _assert_itunes_duration(7, 14, 5, "07:14:05", readers, &rss20_maker)
+ _assert_itunes_duration(7, 14, 5, "7:14:05", readers, &rss20_maker)
+ _assert_itunes_duration(0, 4, 55, "04:55", readers, &rss20_maker)
+ _assert_itunes_duration(0, 4, 5, "4:05", readers, &rss20_maker)
+
+ _assert_itunes_duration_not_available_value("5", &rss20_maker)
+ _assert_itunes_duration_not_available_value("09:07:14:05", &rss20_maker)
+ _assert_itunes_duration_not_available_value("10:5", &rss20_maker)
+ _assert_itunes_duration_not_available_value("10:03:5", &rss20_maker)
+ _assert_itunes_duration_not_available_value("10:3:05", &rss20_maker)
+
+ _assert_itunes_duration_not_available_value("xx:xx:xx", &rss20_maker)
+ end
+ end
+
+ def _assert_itunes_explicit(explicit, value, readers, &rss20_maker)
+ content = tag("itunes:explicit", value)
+ rss20 = itunes_rss20_parse(content, &rss20_maker)
+ target = chain_reader(rss20, readers)
+ assert_equal(value, target.itunes_explicit)
+ assert_equal(explicit, target.itunes_explicit?)
+ end
+
+ def assert_itunes_explicit(readers, &rss20_maker)
+ _wrap_assertion do
+ _assert_itunes_explicit(true, "yes", readers, &rss20_maker)
+ _assert_itunes_explicit(false, "clean", readers, &rss20_maker)
+ _assert_itunes_explicit(nil, "no", readers, &rss20_maker)
+ end
+ end
+
+ def _assert_itunes_keywords(keywords, value, readers, &rss20_maker)
+ content = tag("itunes:keywords", value)
+ rss20 = itunes_rss20_parse(content, &rss20_maker)
+ target = chain_reader(rss20, readers)
+ assert_equal(keywords, target.itunes_keywords)
+ end
+
+ def assert_itunes_keywords(readers, &rss20_maker)
+ _wrap_assertion do
+ _assert_itunes_keywords(["salt"], "salt", readers, &rss20_maker)
+ _assert_itunes_keywords(["salt"], " salt ", readers, &rss20_maker)
+ _assert_itunes_keywords(["salt", "pepper", "shaker", "exciting"],
+ "salt, pepper, shaker, exciting",
+ readers, &rss20_maker)
+ _assert_itunes_keywords(["metric", "socket", "wrenches", "toolsalt"],
+ "metric, socket, wrenches, toolsalt",
+ readers, &rss20_maker)
+ _assert_itunes_keywords(["olitics", "red", "blue", "state"],
+ "olitics, red, blue, state",
+ readers, &rss20_maker)
+ end
+ end
+
+ def assert_itunes_new_feed_url(readers, &rss20_maker)
+ _wrap_assertion do
+ url = "http://newlocation.com/example.rss"
+ content = tag("itunes:new-feed-url", url)
+ rss20 = itunes_rss20_parse(content, &rss20_maker)
+ target = chain_reader(rss20, readers)
+ assert_equal(url, target.itunes_new_feed_url)
+ end
+ end
+
+ def _assert_itunes_owner(name, email, readers, &rss20_maker)
+ content = tag("itunes:owner",
+ tag("itunes:name", name) + tag("itunes:email", email))
+ rss20 = itunes_rss20_parse(content, &rss20_maker)
+ owner = chain_reader(rss20, readers).itunes_owner
+ assert_equal(name, owner.itunes_name)
+ assert_equal(email, owner.itunes_email)
+ end
+
+ def assert_itunes_owner(readers, &rss20_maker)
+ _wrap_assertion do
+ _assert_itunes_owner("John Doe", "john.doe@example.com",
+ readers, &rss20_maker)
+
+ assert_missing_tag("name", "owner") do
+ content = tag("itunes:owner")
+ itunes_rss20_parse(content, &rss20_maker)
+ end
+
+ assert_missing_tag("name", "owner") do
+ content = tag("itunes:owner",
+ tag("itunes:email", "john.doe@example.com"))
+ itunes_rss20_parse(content, &rss20_maker)
+ end
+
+ assert_missing_tag("email", "owner") do
+ content = tag("itunes:owner", tag("itunes:name", "John Doe"))
+ itunes_rss20_parse(content, &rss20_maker)
+ end
+ end
+ end
+
+ def _assert_itunes_subtitle(value, readers, &rss20_maker)
+ content = tag("itunes:subtitle", value)
+ rss20 = itunes_rss20_parse(content, &rss20_maker)
+ target = chain_reader(rss20, readers)
+ assert_equal(value, target.itunes_subtitle)
+ end
+
+ def assert_itunes_subtitle(readers, &rss20_maker)
+ _wrap_assertion do
+ _assert_itunes_subtitle("A show about everything", readers, &rss20_maker)
+ _assert_itunes_subtitle("A short primer on table spices",
+ readers, &rss20_maker)
+ _assert_itunes_subtitle("Comparing socket wrenches is fun!",
+ readers, &rss20_maker)
+ _assert_itunes_subtitle("Red + Blue != Purple", readers, &rss20_maker)
+ end
+ end
+
+ def _assert_itunes_summary(value, readers, &rss20_maker)
+ content = tag("itunes:summary", value)
+ rss20 = itunes_rss20_parse(content, &rss20_maker)
+ target = chain_reader(rss20, readers)
+ assert_equal(value, target.itunes_summary)
+ end
+
+ def assert_itunes_summary(readers, &rss20_maker)
+ _wrap_assertion do
+ _assert_itunes_summary("All About Everything is a show about " +
+ "everything. Each week we dive into any " +
+ "subject known to man and talk about it as " +
+ "much as we can. Look for our Podcast in " +
+ "the iTunes Music Store",
+ readers, &rss20_maker)
+ _assert_itunes_summary("This week we talk about salt and pepper " +
+ "shakers, comparing and contrasting pour " +
+ "rates, construction materials, and overall " +
+ "aesthetics. Come and join the party!",
+ readers, &rss20_maker)
+ _assert_itunes_summary("This week we talk about metric vs. old " +
+ "english socket wrenches. Which one is " +
+ "better? Do you really need both? Get all " +
+ "of your answers here.",
+ readers, &rss20_maker)
+ _assert_itunes_summary("This week we talk about surviving in a " +
+ "Red state if you're a Blue person. Or " +
+ "vice versa.",
+ readers, &rss20_maker)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_0.9.rb b/jni/ruby/test/rss/test_maker_0.9.rb
new file mode 100644
index 0000000..f6e39c6
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_0.9.rb
@@ -0,0 +1,474 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMaker09 < TestCase
+ def test_supported?
+ assert(RSS::Maker.supported?("0.9"))
+ assert(RSS::Maker.supported?("rss0.9"))
+ assert(RSS::Maker.supported?("0.91"))
+ assert(RSS::Maker.supported?("rss0.91"))
+ assert(RSS::Maker.supported?("0.92"))
+ assert(RSS::Maker.supported?("rss0.92"))
+ assert(!RSS::Maker.supported?("0.93"))
+ assert(!RSS::Maker.supported?("rss0.93"))
+ end
+
+ def test_find_class
+ assert_equal(RSS::Maker::RSS091, RSS::Maker["0.91"])
+ assert_equal(RSS::Maker::RSS091, RSS::Maker["rss0.91"])
+ assert_equal(RSS::Maker::RSS092, RSS::Maker["0.9"])
+ assert_equal(RSS::Maker::RSS092, RSS::Maker["0.92"])
+ assert_equal(RSS::Maker::RSS092, RSS::Maker["rss0.92"])
+ end
+
+ def test_rss
+ assert_raise(LocalJumpError) do
+ RSS::Maker.make("0.91")
+ end
+
+ rss = RSS::Maker.make("0.9") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+ end
+ assert_equal("0.92", rss.rss_version)
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+ end
+ assert_equal("0.91", rss.rss_version)
+
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.encoding = "EUC-JP"
+ end
+ assert_equal("0.91", rss.rss_version)
+ assert_equal("EUC-JP", rss.encoding)
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.standalone = "yes"
+ end
+ assert_equal("0.91", rss.rss_version)
+ assert_equal("yes", rss.standalone)
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.encoding = "EUC-JP"
+ maker.standalone = "yes"
+ end
+ assert_equal("0.91", rss.rss_version)
+ assert_equal("EUC-JP", rss.encoding)
+ assert_equal("yes", rss.standalone)
+ end
+
+ def test_channel
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+ language = "ja"
+ copyright = "foo"
+ managingEditor = "bar"
+ webMaster = "web master"
+ rating = '(PICS-1.1 "http://www.rsac.org/ratingsv01.html" l gen true comment "RSACi North America Server" for "http://www.rsac.org" on "1996.04.16T08:15-0500" r (n 0 s 0 v 0 l 0))'
+ docs = "http://foo.com/doc"
+ skipDays = [
+ "Sunday",
+ "Monday",
+ ]
+ skipHours = [
+ "0",
+ "13",
+ ]
+ pubDate = Time.now
+ lastBuildDate = Time.now
+
+ image_url = "http://example.com/logo.png"
+ image_title = "Logo"
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+ maker.channel.copyright = copyright
+ maker.channel.managingEditor = managingEditor
+ maker.channel.webMaster = webMaster
+ maker.channel.rating = rating
+ maker.channel.docs = docs
+ maker.channel.pubDate = pubDate
+ maker.channel.lastBuildDate = lastBuildDate
+
+ skipDays.each do |day|
+ maker.channel.skipDays.new_day do |new_day|
+ new_day.content = day
+ end
+ end
+ skipHours.each do |hour|
+ maker.channel.skipHours.new_hour do |new_hour|
+ new_hour.content = hour
+ end
+ end
+
+ maker.image.url = image_url
+ maker.image.title = image_title
+ end
+ channel = rss.channel
+
+ assert_equal(title, channel.title)
+ assert_equal(link, channel.link)
+ assert_equal(description, channel.description)
+ assert_equal(language, channel.language)
+ assert_equal(copyright, channel.copyright)
+ assert_equal(managingEditor, channel.managingEditor)
+ assert_equal(webMaster, channel.webMaster)
+ assert_equal(rating, channel.rating)
+ assert_equal(docs, channel.docs)
+ assert_equal(pubDate, channel.pubDate)
+ assert_equal(pubDate, channel.date)
+ assert_equal(lastBuildDate, channel.lastBuildDate)
+
+ skipDays.each_with_index do |day, i|
+ assert_equal(day, channel.skipDays.days[i].content)
+ end
+ skipHours.each_with_index do |hour, i|
+ assert_equal(hour.to_i, channel.skipHours.hours[i].content)
+ end
+
+ assert(channel.items.empty?)
+
+ assert_equal(image_url, channel.image.url)
+ assert_equal(image_title, channel.image.title)
+ assert_equal(link, channel.image.link)
+
+ assert_nil(channel.textInput)
+ end
+
+ def test_not_valid_channel
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+ language = "ja"
+
+ assert_not_set_error("maker.channel", %w(title)) do
+ RSS::Maker.make("0.91") do |maker|
+ # maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(link)) do
+ RSS::Maker.make("0.91") do |maker|
+ maker.channel.title = title
+ # maker.channel.link = link
+ maker.channel.link = nil
+ maker.channel.description = description
+ maker.channel.language = language
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(description)) do
+ RSS::Maker.make("0.91") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ # maker.channel.description = description
+ maker.channel.language = language
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(language)) do
+ RSS::Maker.make("0.91") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ # maker.channel.language = language
+ end
+ end
+ end
+
+ def test_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+ width = "144"
+ height = "400"
+ description = "an image"
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ image = rss.image
+ assert_equal(title, image.title)
+ assert_equal(link, image.link)
+ assert_equal(url, image.url)
+ assert_equal(width.to_i, image.width)
+ assert_equal(height.to_i, image.height)
+ assert_equal(description, image.description)
+
+ assert_not_set_error("maker.channel", %w(description title language)) do
+ RSS::Maker.make("0.91") do |maker|
+ # setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ end
+ end
+
+ def test_not_valid_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+ width = "144"
+ height = "400"
+ description = "an image"
+
+ assert_not_set_error("maker.image", %w(title)) do
+ RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ # maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(link)) do
+ RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ # maker.channel.link = link
+ maker.channel.link = nil
+
+ maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ end
+
+ assert_not_set_error("maker.image", %w(url)) do
+ RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ # maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ end
+ end
+
+ def test_items(with_convenience_way=true)
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+ end
+ assert(rss.channel.items.empty?)
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.items.new_item do |item|
+ item.title = title
+ item.link = link
+ # item.description = description
+ end
+
+ setup_dummy_image(maker)
+ end
+ assert_equal(1, rss.channel.items.size)
+ item = rss.channel.items.first
+ assert_equal(title, item.title)
+ assert_equal(link, item.link)
+ assert_nil(item.description)
+
+
+ item_size = 5
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |_item|
+ _item.title = "#{title}#{i}"
+ _item.link = "#{link}#{i}"
+ _item.description = "#{description}#{i}"
+ end
+ end
+ maker.items.do_sort = true
+
+ setup_dummy_image(maker)
+ end
+ assert_equal(item_size, rss.items.size)
+ rss.channel.items.each_with_index do |_item, i|
+ assert_equal("#{title}#{i}", _item.title)
+ assert_equal("#{link}#{i}", _item.link)
+ assert_equal("#{description}#{i}", _item.description)
+ end
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |_item|
+ _item.title = "#{title}#{i}"
+ _item.link = "#{link}#{i}"
+ _item.description = "#{description}#{i}"
+ end
+ end
+ maker.items.do_sort = Proc.new do |x, y|
+ if with_convenience_way
+ y.title[-1] <=> x.title[-1]
+ else
+ y.title {|t| t.content[-1]} <=> x.title {|t| t.content[-1]}
+ end
+ end
+
+ setup_dummy_image(maker)
+ end
+ assert_equal(item_size, rss.items.size)
+ rss.channel.items.reverse.each_with_index do |_item, i|
+ assert_equal("#{title}#{i}", _item.title)
+ assert_equal("#{link}#{i}", _item.link)
+ assert_equal("#{description}#{i}", _item.description)
+ end
+ end
+
+ def test_items_with_new_api_since_018
+ test_items(false)
+ end
+
+ def test_textInput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ textInput = rss.channel.textInput
+ assert_equal(title, textInput.title)
+ assert_equal(description, textInput.description)
+ assert_equal(name, textInput.name)
+ assert_equal(link, textInput.link)
+
+ assert_not_set_error("maker.channel",
+ %w(link language description title)) do
+ RSS::Maker.make("0.91") do |maker|
+ # setup_dummy_channel(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ end
+ end
+
+ def test_not_valid_textInput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ # maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ assert_nil(rss.channel.textInput)
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.textinput.title = title
+ # maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ assert_nil(rss.channel.textInput)
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ # maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ assert_nil(rss.channel.textInput)
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ # maker.textinput.link = link
+ end
+ assert_nil(rss.channel.textInput)
+ end
+
+ def test_date_in_string
+ date = Time.now
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.items.new_item do |item|
+ item.title = "The first item"
+ item.link = "http://example.com/blog/1.html"
+ item.date = date.rfc822
+ end
+ end
+
+ assert_equal(date.iso8601, rss.items[0].date.iso8601)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_1.0.rb b/jni/ruby/test/rss/test_maker_1.0.rb
new file mode 100644
index 0000000..b31abb9
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_1.0.rb
@@ -0,0 +1,516 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMaker10 < TestCase
+ def test_supported?
+ assert(RSS::Maker.supported?("1.0"))
+ assert(RSS::Maker.supported?("rss1.0"))
+ assert(!RSS::Maker.supported?("1.1"))
+ assert(!RSS::Maker.supported?("rss1.1"))
+ end
+
+ def test_find_class
+ assert_equal(RSS::Maker::RSS10, RSS::Maker["1.0"])
+ assert_equal(RSS::Maker::RSS10, RSS::Maker["rss1.0"])
+ end
+
+ def test_rdf
+ assert_raise(LocalJumpError) do
+ RSS::Maker.make("1.0")
+ end
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+ end
+ assert_equal("1.0", rss.rss_version)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.encoding = "EUC-JP"
+
+ setup_dummy_item(maker)
+ end
+ assert_equal("1.0", rss.rss_version)
+ assert_equal("EUC-JP", rss.encoding)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.standalone = "yes"
+
+ setup_dummy_item(maker)
+ end
+ assert_equal("1.0", rss.rss_version)
+ assert_equal("yes", rss.standalone)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.encoding = "EUC-JP"
+ maker.standalone = "yes"
+
+ setup_dummy_item(maker)
+ end
+ assert_equal("1.0", rss.rss_version)
+ assert_equal("EUC-JP", rss.encoding)
+ assert_equal("yes", rss.standalone)
+ end
+
+ def test_channel
+ about = "http://hoge.com"
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.channel.about = about
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+
+ setup_dummy_item(maker)
+ end
+ channel = rss.channel
+ assert_equal(about, channel.about)
+ assert_equal(title, channel.title)
+ assert_equal(link, channel.link)
+ assert_equal(description, channel.description)
+ assert_equal(1, channel.items.Seq.lis.size)
+ assert_nil(channel.image)
+ assert_nil(channel.textinput)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.channel.about = about
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+
+ setup_dummy_image(maker)
+
+ setup_dummy_textinput(maker)
+
+ setup_dummy_item(maker)
+ end
+ channel = rss.channel
+ assert_equal(about, channel.about)
+ assert_equal(title, channel.title)
+ assert_equal(link, channel.link)
+ assert_equal(description, channel.description)
+ assert_equal(1, channel.items.Seq.lis.size)
+ assert_equal(rss.image.about, channel.image.resource)
+ assert_equal(rss.textinput.about, channel.textinput.resource)
+ end
+
+ def test_channel_language
+ about = "http://hoge.com"
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+ language = "ja"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.channel.about = about
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+
+ setup_dummy_item(maker)
+ end
+ channel = rss.channel
+ assert_equal(language, channel.dc_language)
+ end
+
+ def test_not_valid_channel
+ about = "http://hoge.com"
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+
+ assert_not_set_error("maker.channel", %w(about)) do
+ RSS::Maker.make("1.0") do |maker|
+ # maker.channel.about = about
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(title)) do
+ RSS::Maker.make("1.0") do |maker|
+ maker.channel.about = about
+ # maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(link)) do
+ RSS::Maker.make("1.0") do |maker|
+ maker.channel.about = about
+ maker.channel.title = title
+ # maker.channel.link = link
+ maker.channel.description = description
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(description)) do
+ RSS::Maker.make("1.0") do |maker|
+ maker.channel.about = about
+ maker.channel.title = title
+ maker.channel.link = link
+ # maker.channel.description = description
+ end
+ end
+ end
+
+
+ def test_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+
+ setup_dummy_item(maker)
+ end
+ image = rss.image
+ assert_equal(url, image.about)
+ assert_equal(url, rss.channel.image.resource)
+ assert_equal(title, image.title)
+ assert_equal(link, image.link)
+ assert_equal(url, image.url)
+
+ assert_not_set_error("maker.channel", %w(about title description)) do
+ RSS::Maker.make("1.0") do |maker|
+ # setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+ end
+ end
+ end
+
+ def test_not_valid_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ # maker.image.url = url
+ maker.image.title = title
+
+ setup_dummy_item(maker)
+ end
+ assert_nil(rss.channel.image)
+ assert_nil(rss.image)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.url = url
+ # maker.image.title = title
+
+ setup_dummy_item(maker)
+ end
+ assert_nil(rss.channel.image)
+ assert_nil(rss.image)
+
+ assert_not_set_error("maker.channel", %w(link)) do
+ RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ # maker.channel.link = link
+ maker.channel.link = nil
+
+ maker.image.url = url
+ maker.image.title = title
+
+ setup_dummy_item(maker)
+ end
+ end
+ end
+
+ def test_items(with_convenience_way=true)
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+
+ assert_not_set_error("maker", %w(items)) do
+ RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ end
+ end
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.items.new_item do |item|
+ item.title = title
+ item.link = link
+ # item.description = description
+ end
+ end
+ assert_equal(1, rss.items.size)
+ item = rss.items.first
+ assert_equal(link, item.about)
+ assert_equal(title, item.title)
+ assert_equal(link, item.link)
+ assert_nil(item.description)
+
+
+ item_size = 5
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |_item|
+ _item.title = "#{title}#{i}"
+ _item.link = "#{link}#{i}"
+ _item.description = "#{description}#{i}"
+ end
+ end
+ maker.items.do_sort = true
+ end
+ assert_equal(item_size, rss.items.size)
+ rss.items.each_with_index do |_item, i|
+ assert_equal("#{link}#{i}", _item.about)
+ assert_equal("#{title}#{i}", _item.title)
+ assert_equal("#{link}#{i}", _item.link)
+ assert_equal("#{description}#{i}", _item.description)
+ end
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |_item|
+ _item.title = "#{title}#{i}"
+ _item.link = "#{link}#{i}"
+ _item.description = "#{description}#{i}"
+ end
+ end
+ maker.items.do_sort = Proc.new do |x, y|
+ if with_convenience_way
+ y.title[-1] <=> x.title[-1]
+ else
+ y.title {|t| t.content[-1]} <=> x.title {|t| t.content[-1]}
+ end
+ end
+ end
+ assert_equal(item_size, rss.items.size)
+ rss.items.reverse.each_with_index do |_item, i|
+ assert_equal("#{link}#{i}", _item.about)
+ assert_equal("#{title}#{i}", _item.title)
+ assert_equal("#{link}#{i}", _item.link)
+ assert_equal("#{description}#{i}", _item.description)
+ end
+
+ max_size = item_size / 2
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |_item|
+ _item.title = "#{title}#{i}"
+ _item.link = "#{link}#{i}"
+ _item.description = "#{description}#{i}"
+ end
+ end
+ maker.items.max_size = max_size
+ end
+ assert_equal(max_size, rss.items.size)
+ rss.items.each_with_index do |_item, i|
+ assert_equal("#{link}#{i}", _item.about)
+ assert_equal("#{title}#{i}", _item.title)
+ assert_equal("#{link}#{i}", _item.link)
+ assert_equal("#{description}#{i}", _item.description)
+ end
+
+ max_size = 0
+ assert_not_set_error("maker", %w(items)) do
+ RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |_item|
+ _item.title = "#{title}#{i}"
+ _item.link = "#{link}#{i}"
+ _item.description = "#{description}#{i}"
+ end
+ end
+ maker.items.max_size = max_size
+ end
+ end
+
+ max_size = -2
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |_item|
+ _item.title = "#{title}#{i}"
+ _item.link = "#{link}#{i}"
+ _item.description = "#{description}#{i}"
+ end
+ end
+ maker.items.max_size = max_size
+ end
+ assert_equal(item_size + max_size + 1, rss.items.size)
+ rss.items.each_with_index do |_item, i|
+ assert_equal("#{link}#{i}", _item.about)
+ assert_equal("#{title}#{i}", _item.title)
+ assert_equal("#{link}#{i}", _item.link)
+ assert_equal("#{description}#{i}", _item.description)
+ end
+ end
+
+ def test_items_with_new_api_since_018
+ test_items(false)
+ end
+
+ def test_not_valid_items
+ title = "TITLE"
+ link = "http://hoge.com/"
+
+ assert_not_set_error("maker.item", %w(title)) do
+ RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.items.new_item do |item|
+ # item.title = title
+ item.link = link
+ end
+ end
+ end
+
+ assert_not_set_error("maker.item", %w(link)) do
+ RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.items.new_item do |item|
+ item.title = title
+ # item.link = link
+ end
+ end
+ end
+
+ assert_not_set_error("maker.item", %w(title link)) do
+ RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.items.new_item do |item|
+ # item.title = title
+ # item.link = link
+ end
+ end
+ end
+ end
+
+ def test_textinput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.link = link
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+
+ setup_dummy_item(maker)
+ end
+ textinput = rss.textinput
+ assert_equal(link, textinput.about)
+ assert_equal(link, rss.channel.textinput.resource)
+ assert_equal(title, textinput.title)
+ assert_equal(name, textinput.name)
+ assert_equal(description, textinput.description)
+ assert_equal(link, textinput.link)
+
+ assert_not_set_error("maker.channel", %w(about link description title)) do
+ RSS::Maker.make("1.0") do |maker|
+ # setup_dummy_channel(maker)
+
+ maker.textinput.link = link
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ end
+ end
+ end
+
+ def test_not_valid_textinput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ # maker.textinput.link = link
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+
+ setup_dummy_item(maker)
+ end
+ assert_nil(rss.channel.textinput)
+ assert_nil(rss.textinput)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.link = link
+ # maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+
+ setup_dummy_item(maker)
+ end
+ assert_nil(rss.channel.textinput)
+ assert_nil(rss.textinput)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.link = link
+ maker.textinput.title = title
+ # maker.textinput.description = description
+ maker.textinput.name = name
+
+ setup_dummy_item(maker)
+ end
+ assert_nil(rss.channel.textinput)
+ assert_nil(rss.textinput)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.link = link
+ maker.textinput.title = title
+ maker.textinput.description = description
+ # maker.textinput.name = name
+
+ setup_dummy_item(maker)
+ end
+ assert_nil(rss.channel.textinput)
+ assert_nil(rss.textinput)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_2.0.rb b/jni/ruby/test/rss/test_maker_2.0.rb
new file mode 100644
index 0000000..48c0f91
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_2.0.rb
@@ -0,0 +1,757 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMaker20 < TestCase
+ def test_supported?
+ assert(RSS::Maker.supported?("2.0"))
+ assert(RSS::Maker.supported?("rss2.0"))
+ assert(!RSS::Maker.supported?("2.2"))
+ assert(!RSS::Maker.supported?("rss2.2"))
+ end
+
+ def test_find_class
+ assert_equal(RSS::Maker::RSS20, RSS::Maker["2.0"])
+ assert_equal(RSS::Maker::RSS20, RSS::Maker["rss2.0"])
+ end
+
+ def test_rss
+ assert_raise(LocalJumpError) do
+ RSS::Maker.make("2.0")
+ end
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ end
+ assert_equal("2.0", rss.rss_version)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.encoding = "EUC-JP"
+ end
+ assert_equal("2.0", rss.rss_version)
+ assert_equal("EUC-JP", rss.encoding)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.standalone = "yes"
+ end
+ assert_equal("2.0", rss.rss_version)
+ assert_equal("yes", rss.standalone)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.encoding = "EUC-JP"
+ maker.standalone = "yes"
+ end
+ assert_equal("2.0", rss.rss_version)
+ assert_equal("EUC-JP", rss.encoding)
+ assert_equal("yes", rss.standalone)
+ end
+
+ def test_channel
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+ language = "ja"
+ copyright = "foo"
+ managingEditor = "bar"
+ webMaster = "web master"
+ rating = '(PICS-1.1 "http://www.rsac.org/ratingsv01.html" l gen true comment "RSACi North America Server" for "http://www.rsac.org" on "1996.04.16T08:15-0500" r (n 0 s 0 v 0 l 0))'
+ docs = "http://foo.com/doc"
+ skipDays = [
+ "Sunday",
+ "Monday",
+ ]
+ skipHours = [
+ "0",
+ "13",
+ ]
+ pubDate = Time.now
+ lastBuildDate = Time.now
+ categories = [
+ "Nespapers",
+ "misc",
+ ]
+ generator = "RSS Maker"
+ ttl = "60"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+ maker.channel.copyright = copyright
+ maker.channel.managingEditor = managingEditor
+ maker.channel.webMaster = webMaster
+ maker.channel.rating = rating
+ maker.channel.docs = docs
+ maker.channel.pubDate = pubDate
+ maker.channel.lastBuildDate = lastBuildDate
+
+ skipDays.each do |day|
+ maker.channel.skipDays.new_day do |new_day|
+ new_day.content = day
+ end
+ end
+ skipHours.each do |hour|
+ maker.channel.skipHours.new_hour do |new_hour|
+ new_hour.content = hour
+ end
+ end
+
+ categories.each do |category|
+ maker.channel.categories.new_category do |new_category|
+ new_category.content = category
+ end
+ end
+
+ maker.channel.generator = generator
+ maker.channel.ttl = ttl
+ end
+ channel = rss.channel
+
+ assert_equal(title, channel.title)
+ assert_equal(link, channel.link)
+ assert_equal(description, channel.description)
+ assert_equal(language, channel.language)
+ assert_equal(copyright, channel.copyright)
+ assert_equal(managingEditor, channel.managingEditor)
+ assert_equal(webMaster, channel.webMaster)
+ assert_equal(rating, channel.rating)
+ assert_equal(docs, channel.docs)
+ assert_equal(pubDate, channel.pubDate)
+ assert_equal(pubDate, channel.date)
+ assert_equal(lastBuildDate, channel.lastBuildDate)
+
+ skipDays.each_with_index do |day, i|
+ assert_equal(day, channel.skipDays.days[i].content)
+ end
+ skipHours.each_with_index do |hour, i|
+ assert_equal(hour.to_i, channel.skipHours.hours[i].content)
+ end
+
+ channel.categories.each_with_index do |category, i|
+ assert_equal(categories[i], category.content)
+ end
+
+ assert_equal(generator, channel.generator)
+ assert_equal(ttl.to_i, channel.ttl)
+
+ assert(channel.items.empty?)
+ assert_nil(channel.image)
+ assert_nil(channel.textInput)
+ end
+
+ def test_not_valid_channel
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+ language = "ja"
+
+ assert_not_set_error("maker.channel", %w(title)) do
+ RSS::Maker.make("2.0") do |maker|
+ # maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(link)) do
+ RSS::Maker.make("2.0") do |maker|
+ maker.channel.title = title
+ # maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(description)) do
+ RSS::Maker.make("2.0") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ # maker.channel.description = description
+ maker.channel.language = language
+ end
+ end
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ # maker.channel.language = language
+ end
+ assert_not_nil(rss)
+ end
+
+
+ def test_cloud
+ domain = "rpc.sys.com"
+ port = "80"
+ path = "/RPC2"
+ registerProcedure = "myCloud.rssPleaseNotify"
+ protocol = "xml-rpc"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.channel.cloud.domain = domain
+ maker.channel.cloud.port = port
+ maker.channel.cloud.path = path
+ maker.channel.cloud.registerProcedure = registerProcedure
+ maker.channel.cloud.protocol = protocol
+ end
+ cloud = rss.channel.cloud
+ assert_equal(domain, cloud.domain)
+ assert_equal(port.to_i, cloud.port)
+ assert_equal(path, cloud.path)
+ assert_equal(registerProcedure, cloud.registerProcedure)
+ assert_equal(protocol, cloud.protocol)
+ end
+
+ def test_not_valid_cloud
+ domain = "rpc.sys.com"
+ port = "80"
+ path = "/RPC2"
+ registerProcedure = "myCloud.rssPleaseNotify"
+ protocol = "xml-rpc"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ # maker.channel.cloud.domain = domain
+ maker.channel.cloud.port = port
+ maker.channel.cloud.path = path
+ maker.channel.cloud.registerProcedure = registerProcedure
+ maker.channel.cloud.protocol = protocol
+ end
+ assert_nil(rss.channel.cloud)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.channel.cloud.domain = domain
+ # maker.channel.cloud.port = port
+ maker.channel.cloud.path = path
+ maker.channel.cloud.registerProcedure = registerProcedure
+ maker.channel.cloud.protocol = protocol
+ end
+ assert_nil(rss.channel.cloud)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.channel.cloud.domain = domain
+ maker.channel.cloud.port = port
+ # maker.channel.cloud.path = path
+ maker.channel.cloud.registerProcedure = registerProcedure
+ maker.channel.cloud.protocol = protocol
+ end
+ assert_nil(rss.channel.cloud)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.channel.cloud.domain = domain
+ maker.channel.cloud.port = port
+ maker.channel.cloud.path = path
+ # maker.channel.cloud.registerProcedure = registerProcedure
+ maker.channel.cloud.protocol = protocol
+ end
+ assert_nil(rss.channel.cloud)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.channel.cloud.domain = domain
+ maker.channel.cloud.port = port
+ maker.channel.cloud.path = path
+ maker.channel.cloud.registerProcedure = registerProcedure
+ # maker.channel.cloud.protocol = protocol
+ end
+ assert_nil(rss.channel.cloud)
+ end
+
+
+ def test_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+ width = "144"
+ height = "400"
+ description = "an image"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ image = rss.image
+ assert_equal(title, image.title)
+ assert_equal(link, image.link)
+ assert_equal(url, image.url)
+ assert_equal(width.to_i, image.width)
+ assert_equal(height.to_i, image.height)
+ assert_equal(description, image.description)
+
+ assert_not_set_error("maker.channel", %w(title description)) do
+ RSS::Maker.make("2.0") do |maker|
+ # setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ end
+ end
+
+ def test_not_valid_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+ width = "144"
+ height = "400"
+ description = "an image"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ # maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ assert_nil(rss.image)
+
+ assert_not_set_error("maker.channel", %w(link)) do
+ RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ # maker.channel.link = link
+ maker.channel.link = nil
+
+ maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ end
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ # maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+ assert_nil(rss.image)
+ end
+
+ def test_items(with_convenience_way=true)
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+ author = "oprah@oxygen.net"
+ comments = "http://www.myblog.org/cgi-local/mt/mt-comments.cgi?entry_id=290"
+ pubDate = Time.now
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ end
+ assert(rss.channel.items.empty?)
+
+ item_size = 5
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+ item.author = "#{author}#{i}"
+ item.comments = "#{comments}#{i}"
+ item.date = pubDate
+ end
+ end
+ maker.items.do_sort = true
+ end
+ assert_equal(item_size, rss.items.size)
+ rss.channel.items.each_with_index do |item, i|
+ assert_equal("#{title}#{i}", item.title)
+ assert_equal("#{link}#{i}", item.link)
+ assert_equal("#{description}#{i}", item.description)
+ assert_equal("#{author}#{i}", item.author)
+ assert_equal("#{comments}#{i}", item.comments)
+ assert_equal(pubDate, item.pubDate)
+ assert_equal(pubDate, item.date)
+ end
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+ item.author = "#{author}#{i}"
+ item.comments = "#{comments}#{i}"
+ item.date = pubDate
+ end
+ end
+ maker.items.do_sort = Proc.new do |x, y|
+ if with_convenience_way
+ y.title[-1] <=> x.title[-1]
+ else
+ y.title {|t| t.content[-1]} <=> x.title {|t| t.content[-1]}
+ end
+ end
+ end
+ assert_equal(item_size, rss.items.size)
+ rss.channel.items.reverse.each_with_index do |item, i|
+ assert_equal("#{title}#{i}", item.title)
+ assert_equal("#{link}#{i}", item.link)
+ assert_equal("#{description}#{i}", item.description)
+ assert_equal("#{author}#{i}", item.author)
+ assert_equal("#{comments}#{i}", item.comments)
+ assert_equal(pubDate, item.pubDate)
+ assert_equal(pubDate, item.date)
+ end
+ end
+
+ def test_items_with_new_api_since_018
+ test_items(false)
+ end
+
+ def test_pubDate_without_description
+ title = "TITLE"
+ link = "http://hoge.com/"
+ # description = "text hoge fuga"
+ author = "oprah@oxygen.net"
+ pubDate = Time.now
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.items.new_item do |item|
+ item.title = title
+ item.link = link
+ # item.description = description
+ item.author = author
+ item.pubDate = pubDate
+ end
+ end
+ assert_equal(1, rss.items.size)
+ rss.channel.items.each_with_index do |item, i|
+ assert_equal(title, item.title)
+ assert_equal(link, item.link)
+ # assert_equal(description, item.description)
+ assert_equal(author, item.author)
+ assert_equal(pubDate, item.pubDate)
+ assert_equal(pubDate, item.date)
+ end
+ end
+
+ def test_guid
+ isPermaLink = "true"
+ content = "http://inessential.com/2002/09/01.php#a2"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ guid = maker.items.last.guid
+ guid.isPermaLink = isPermaLink
+ guid.content = content
+ end
+ guid = rss.channel.items.last.guid
+ assert_equal(isPermaLink == "true", guid.isPermaLink)
+ assert_equal(content, guid.content)
+ end
+
+ def test_guid_permanent_link
+ content = "http://inessential.com/2002/09/01.php#a2"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ guid = maker.items.last.guid
+ assert_equal(nil, guid.permanent_link?)
+ assert_equal(guid.isPermaLink, guid.permanent_link?)
+ guid.permanent_link = true
+ assert_equal(true, guid.permanent_link?)
+ assert_equal(guid.isPermaLink, guid.permanent_link?)
+ guid.content = content
+ end
+ guid = rss.channel.items.last.guid
+ assert_equal(true, guid.isPermaLink)
+ assert_equal(content, guid.content)
+ end
+
+ def test_guid_permanent_link_false
+ content = "http://inessential.com/2002/09/01.php#a2"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ guid = maker.items.last.guid
+ assert_equal(nil, guid.permanent_link?)
+ assert_equal(guid.isPermaLink, guid.permanent_link?)
+ guid.permanent_link = false
+ assert_equal(false, guid.permanent_link?)
+ assert_equal(guid.isPermaLink, guid.permanent_link?)
+ guid.content = content
+ end
+ guid = rss.channel.items.last.guid
+ assert_equal(false, guid.isPermaLink)
+ assert_equal(content, guid.content)
+ end
+
+ def test_not_valid_guid
+ # content = "http://inessential.com/2002/09/01.php#a2"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ # guid = maker.items.last.guid
+ # guid.content = content
+ end
+ assert_nil(rss.channel.items.last.guid)
+ end
+
+ def test_enclosure
+ url = "http://www.scripting.com/mp3s/weatherReportSuite.mp3"
+ length = "12216320"
+ type = "audio/mpeg"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ enclosure = maker.items.last.enclosure
+ enclosure.url = url
+ enclosure.length = length
+ enclosure.type = type
+ end
+ enclosure = rss.channel.items.last.enclosure
+ assert_equal(url, enclosure.url)
+ assert_equal(length.to_i, enclosure.length)
+ assert_equal(type, enclosure.type)
+ end
+
+ def test_not_valid_enclosure
+ url = "http://www.scripting.com/mp3s/weatherReportSuite.mp3"
+ length = "12216320"
+ type = "audio/mpeg"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ enclosure = maker.items.last.enclosure
+ # enclosure.url = url
+ enclosure.length = length
+ enclosure.type = type
+ end
+ assert_nil(rss.channel.items.last.enclosure)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ enclosure = maker.items.last.enclosure
+ enclosure.url = url
+ # enclosure.length = length
+ enclosure.type = type
+ end
+ assert_nil(rss.channel.items.last.enclosure)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ enclosure = maker.items.last.enclosure
+ enclosure.url = url
+ enclosure.length = length
+ # enclosure.type = type
+ end
+ assert_nil(rss.channel.items.last.enclosure)
+ end
+
+
+ def test_source
+ url = "http://static.userland.com/tomalak/links2.xml"
+ content = "Tomalak's Realm"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ source = maker.items.last.source
+ source.url = url
+ source.content = content
+ end
+ source = rss.channel.items.last.source
+ assert_equal(url, source.url)
+ assert_equal(content, source.content)
+ end
+
+ def test_not_valid_source
+ url = "http://static.userland.com/tomalak/links2.xml"
+ content = "Tomalak's Realm"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ source = maker.items.last.source
+ # source.url = url
+ source.content = content
+ end
+ assert_nil(rss.channel.items.last.source)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ source = maker.items.last.source
+ source.url = url
+ # source.content = content
+ end
+ assert_nil(rss.channel.items.last.source)
+ end
+
+ def test_category
+ domain = "http://www.fool.com/cusips"
+ content = "MSFT"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ maker.items.last.categories.new_category do |category|
+ category.domain = domain
+ category.content = content
+ end
+ end
+ category = rss.channel.items.last.categories.last
+ assert_equal(domain, category.domain)
+ assert_equal(content, category.content)
+ end
+
+ def test_not_valid_category
+ # content = "Grateful Dead"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ maker.items.last.categories.new_category do |category|
+ # category.content = content
+ end
+ end
+ assert(rss.channel.items.last.categories.empty?)
+ end
+
+ def test_textInput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ textInput = rss.channel.textInput
+ assert_equal(title, textInput.title)
+ assert_equal(description, textInput.description)
+ assert_equal(name, textInput.name)
+ assert_equal(link, textInput.link)
+
+ assert_not_set_error("maker.channel", %w(link description title)) do
+ RSS::Maker.make("2.0") do |maker|
+ # setup_dummy_channel(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ end
+ end
+
+ def test_not_valid_textInput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ # maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ assert_nil(rss.channel.textInput)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.title = title
+ # maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ assert_nil(rss.channel.textInput)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ # maker.textinput.name = name
+ maker.textinput.link = link
+ end
+ assert_nil(rss.channel.textInput)
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ # maker.textinput.link = link
+ end
+ assert_nil(rss.channel.textInput)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_atom_entry.rb b/jni/ruby/test/rss/test_maker_atom_entry.rb
new file mode 100644
index 0000000..9618723
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_atom_entry.rb
@@ -0,0 +1,393 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerAtomEntry < TestCase
+ def test_supported?
+ assert(RSS::Maker.supported?("atom:entry"))
+ assert(RSS::Maker.supported?("atom1.0:entry"))
+ assert(!RSS::Maker.supported?("atom2.0:entry"))
+ end
+
+ def test_find_class
+ assert_equal(RSS::Maker::Atom::Entry, RSS::Maker["atom:entry"])
+ assert_equal(RSS::Maker::Atom::Entry, RSS::Maker["atom1.0:entry"])
+ end
+
+ def test_root_element
+ entry = Maker.make("atom:entry") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ assert_equal(["atom", "1.0", "entry"], entry.feed_info)
+
+ entry = Maker.make("atom:entry") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.encoding = "EUC-JP"
+ end
+ assert_equal(["atom", "1.0", "entry"], entry.feed_info)
+ assert_equal("EUC-JP", entry.encoding)
+
+ entry = Maker.make("atom:entry") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.standalone = "yes"
+ end
+ assert_equal(["atom", "1.0", "entry"], entry.feed_info)
+ assert_equal("yes", entry.standalone)
+
+ entry = Maker.make("atom:entry") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.encoding = "EUC-JP"
+ maker.standalone = "yes"
+ end
+ assert_equal(["atom", "1.0", "entry"], entry.feed_info)
+ assert_equal("EUC-JP", entry.encoding)
+ assert_equal("yes", entry.standalone)
+ end
+
+ def test_invalid_feed
+ assert_not_set_error("maker.item", %w(id title author updated)) do
+ Maker.make("atom:entry") do |maker|
+ end
+ end
+
+ assert_not_set_error("maker.item", %w(id title updated)) do
+ Maker.make("atom:entry") do |maker|
+ maker.channel.author = "foo"
+ end
+ end
+
+ assert_not_set_error("maker.item", %w(title updated)) do
+ Maker.make("atom:entry") do |maker|
+ maker.channel.author = "foo"
+ maker.channel.id = "http://example.com"
+ end
+ end
+
+ assert_not_set_error("maker.item", %w(updated)) do
+ Maker.make("atom:entry") do |maker|
+ maker.channel.author = "foo"
+ maker.channel.id = "http://example.com"
+ maker.channel.title = "Atom Feed"
+ end
+ end
+
+ assert_not_set_error("maker.item", %w(author)) do
+ Maker.make("atom:entry") do |maker|
+ maker.channel.id = "http://example.com"
+ maker.channel.title = "Atom Feed"
+ maker.channel.updated = Time.now
+ end
+ end
+
+ entry = Maker.make("atom:entry") do |maker|
+ maker.channel.author = "Foo"
+ maker.channel.id = "http://example.com"
+ maker.channel.title = "Atom Feed"
+ maker.channel.updated = Time.now
+ end
+ assert_not_nil(entry)
+ end
+
+ def test_author
+ assert_maker_atom_persons("entry",
+ ["channel", "authors"],
+ ["authors"],
+ "maker.channel.author") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_persons("entry",
+ ["items", "first", "authors"],
+ ["authors"],
+ "maker.item.author",
+ "maker.item", ["author"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.channel.authors.clear
+ maker.items.first.authors.clear
+ end
+
+ assert_maker_atom_persons("entry",
+ ["items", "first", "source", "authors"],
+ ["source", "authors"],
+ "maker.item.source.author") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_category
+ assert_maker_atom_categories("entry",
+ ["channel", "categories"],
+ ["categories"],
+ "maker.channel.category") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_categories("entry",
+ ["items", "first", "categories"],
+ ["categories"],
+ "maker.item.category") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_categories("entry",
+ ["items", "first", "source", "categories"],
+ ["source", "categories"],
+ "maker.item.source.category") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_content
+ assert_maker_atom_content("entry",
+ ["items", "first", "content"],
+ ["content"],
+ "maker.item.content") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_contributor
+ assert_maker_atom_persons("entry",
+ ["channel", "contributors"],
+ ["contributors"],
+ "maker.channel.contributor") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_persons("entry",
+ ["items", "first", "contributors"],
+ ["contributors"],
+ "maker.item.contributor") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_persons("entry",
+ ["items", "first", "source", "contributors"],
+ ["source", "contributors"],
+ "maker.item.source.contributor") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_link
+ assert_maker_atom_links("entry",
+ ["channel", "links"],
+ ["links"],
+ "maker.channel.link") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.channel.links.clear
+ maker.items.first.links.clear
+ end
+
+ assert_maker_atom_links("entry",
+ ["items", "first", "links"],
+ ["links"],
+ "maker.item.link") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.channel.links.clear
+ maker.items.first.links.clear
+ end
+
+ assert_maker_atom_links("entry",
+ ["items", "first", "source", "links"],
+ ["source", "links"],
+ "maker.item.source.link", true) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_published
+ assert_maker_atom_date_construct("entry",
+ ["items", "first", "published"],
+ ["published"]
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_rights
+ assert_maker_atom_text_construct("entry",
+ ["channel", "copyright"],
+ ["rights"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_text_construct("entry",
+ ["items", "first", "rights"],
+ ["rights"],
+ nil, nil, "maker.item.rights"
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_text_construct("entry",
+ ["items", "first", "source", "rights"],
+ ["source", "rights"],
+ nil, nil, "maker.item.source.rights"
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+
+ def test_source_generator
+ assert_maker_atom_generator("entry",
+ ["items", "first", "source", "generator"],
+ ["source", "generator"],
+ "maker.item.source.generator") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_source_icon
+ assert_maker_atom_icon("entry",
+ ["items", "first", "source", "icon"],
+ ["source", "icon"],
+ nil, "maker.item.source.icon") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_source_id
+ assert_maker_atom_id("entry",
+ ["items", "first", "source"],
+ ["source"],
+ "maker.item.source") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_source_logo
+ assert_maker_atom_logo("entry",
+ ["items", "first", "source", "logo"],
+ ["source", "logo"],
+ nil,
+ "maker.item.source.logo") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_source_subtitle
+ assert_maker_atom_text_construct("entry",
+ ["items", "first", "source", "subtitle"],
+ ["source", "subtitle"],
+ nil, nil,
+ "maker.item.source.subtitle") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_summary
+ assert_maker_atom_text_construct("entry",
+ ["items", "first", "description"],
+ ["summary"],
+ nil, nil, "maker.item.description"
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_title
+ assert_maker_atom_text_construct("entry",
+ ["channel", "title"], ["title"],
+ "maker.item", ["title"],
+ "maker.channel.title") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.channel.title = nil
+ maker.items.first.title = nil
+ end
+
+ assert_maker_atom_text_construct("entry",
+ ["items", "first", "title"],
+ ["title"],
+ "maker.item", ["title"],
+ "maker.item.title") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.channel.title = nil
+ maker.items.first.title = nil
+ end
+
+ assert_maker_atom_text_construct("entry",
+ ["items", "first", "source", "title"],
+ ["source", "title"],
+ nil, nil, "maker.item.source.title"
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_updated
+ assert_maker_atom_date_construct("entry",
+ ["channel", "updated"], ["updated"],
+ "maker.item", ["updated"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.channel.updated = nil
+ maker.items.first.updated = nil
+ end
+
+ assert_maker_atom_date_construct("entry",
+ ["items", "first", "updated"],
+ ["updated"],
+ "maker.item", ["updated"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.channel.updated = nil
+ maker.items.first.updated = nil
+ end
+
+ assert_maker_atom_date_construct("entry",
+ ["items", "first", "source", "updated"],
+ ["source", "updated"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_date
+ date = Time.parse("2004/11/1 10:10")
+ feed = Maker.make("atom:entry") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.channel.date = nil
+ maker.items.new_item do |item|
+ item.link = "http://example.com/article.html"
+ item.title = "Sample Article"
+ item.date = date
+ end
+ end
+ assert_equal(date, feed.items[0].updated.content)
+ assert_equal([date], feed.items[0].dc_dates.collect {|_date| _date.value})
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_atom_feed.rb b/jni/ruby/test/rss/test_maker_atom_feed.rb
new file mode 100644
index 0000000..d866001
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_atom_feed.rb
@@ -0,0 +1,454 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerAtomFeed < TestCase
+ def test_supported?
+ assert(RSS::Maker.supported?("atom"))
+ assert(RSS::Maker.supported?("atom:feed"))
+ assert(RSS::Maker.supported?("atom1.0"))
+ assert(RSS::Maker.supported?("atom1.0:feed"))
+ assert(!RSS::Maker.supported?("atom2.0"))
+ assert(!RSS::Maker.supported?("atom2.0:feed"))
+ end
+
+ def test_find_class
+ assert_equal(RSS::Maker::Atom::Feed, RSS::Maker["atom"])
+ assert_equal(RSS::Maker::Atom::Feed, RSS::Maker["atom:feed"])
+ assert_equal(RSS::Maker::Atom::Feed, RSS::Maker["atom1.0"])
+ assert_equal(RSS::Maker::Atom::Feed, RSS::Maker["atom1.0:feed"])
+ end
+
+ def test_root_element
+ feed = Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+ assert_equal(["atom", "1.0", "feed"], feed.feed_info)
+
+ feed = Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.encoding = "EUC-JP"
+ end
+ assert_equal(["atom", "1.0", "feed"], feed.feed_info)
+ assert_equal("EUC-JP", feed.encoding)
+
+ feed = Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.standalone = "yes"
+ end
+ assert_equal(["atom", "1.0", "feed"], feed.feed_info)
+ assert_equal("yes", feed.standalone)
+
+ feed = Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.encoding = "EUC-JP"
+ maker.standalone = "yes"
+ end
+ assert_equal(["atom", "1.0", "feed"], feed.feed_info)
+ assert_equal("EUC-JP", feed.encoding)
+ assert_equal("yes", feed.standalone)
+ end
+
+ def test_invalid_feed
+ assert_not_set_error("maker.channel", %w(id title author updated)) do
+ Maker.make("atom") do |maker|
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(id title updated)) do
+ Maker.make("atom") do |maker|
+ maker.channel.author = "foo"
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(title updated)) do
+ Maker.make("atom") do |maker|
+ maker.channel.author = "foo"
+ maker.channel.id = "http://example.com"
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(updated)) do
+ Maker.make("atom") do |maker|
+ maker.channel.author = "foo"
+ maker.channel.id = "http://example.com"
+ maker.channel.title = "Atom Feed"
+ end
+ end
+
+ assert_not_set_error("maker.channel", %w(author)) do
+ Maker.make("atom") do |maker|
+ maker.channel.id = "http://example.com"
+ maker.channel.title = "Atom Feed"
+ maker.channel.updated = Time.now
+ end
+ end
+
+ feed = Maker.make("atom") do |maker|
+ maker.channel.author = "Foo"
+ maker.channel.id = "http://example.com"
+ maker.channel.title = "Atom Feed"
+ maker.channel.updated = Time.now
+ end
+ assert_not_nil(feed)
+ end
+
+ def test_author
+ assert_maker_atom_persons("feed",
+ ["channel", "authors"],
+ ["authors"],
+ "maker.channel.author") do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+
+ assert_not_set_error("maker.channel", %w(author)) do
+ RSS::Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.channel.authors.clear
+ end
+ end
+
+ assert_maker_atom_persons("feed",
+ ["items", "first", "authors"],
+ ["entries", "first", "authors"],
+ "maker.item.author") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_persons("feed",
+ ["items", "first", "source", "authors"],
+ ["entries", "first", "source", "authors"],
+ "maker.item.source.author") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_category
+ assert_maker_atom_categories("feed",
+ ["channel", "categories"],
+ ["categories"],
+ "maker.channel.category") do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+
+ assert_maker_atom_categories("feed",
+ ["items", "first", "categories"],
+ ["entries", "first", "categories"],
+ "maker.item.category") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_categories("feed",
+ ["items", "first", "source", "categories"],
+ ["entries", "first", "source", "categories"],
+ "maker.item.source.category") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_contributor
+ assert_maker_atom_persons("feed",
+ ["channel", "contributors"],
+ ["contributors"],
+ "maker.channel.contributor") do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+
+ assert_maker_atom_persons("feed",
+ ["items", "first", "contributors"],
+ ["entries", "first", "contributors"],
+ "maker.item.contributor") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_persons("feed",
+ ["items", "first", "source", "contributors"],
+ ["entries", "first", "source", "contributors"],
+ "maker.item.source.contributor") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_generator
+ assert_maker_atom_generator("feed",
+ ["channel", "generator"],
+ ["generator"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_generator("feed",
+ ["items", "first", "source", "generator"],
+ ["entries", "first", "source", "generator"],
+ "maker.item.source.generator") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_icon
+ assert_maker_atom_icon("feed", ["channel"], ["icon"], "icon") do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+
+ assert_maker_atom_icon("feed",
+ ["items", "first", "source", "icon"],
+ ["entries", "first", "source", "icon"],
+ nil, "maker.item.source.icon") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_link
+ assert_maker_atom_links("feed",
+ ["channel", "links"],
+ ["links"],
+ "maker.channel.link") do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+
+ assert_maker_atom_links("feed",
+ ["items", "first", "links"],
+ ["entries", "first", "links"],
+ "maker.item.link") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_links("feed",
+ ["items", "first", "source", "links"],
+ ["entries", "first", "source", "links"],
+ "maker.item.source.link", true) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_logo
+ assert_maker_atom_logo("feed", ["channel"], ["logo"], "logo") do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+
+ assert_maker_atom_logo("feed", ["image"], ["logo"], "url") do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+
+ assert_maker_atom_logo("feed",
+ ["items", "first", "source", "logo"],
+ ["entries", "first", "source", "logo"],
+ nil, "maker.item.source.logo") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_rights
+ assert_maker_atom_text_construct("feed",
+ ["channel", "copyright"],
+ ["rights"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ end
+
+ assert_maker_atom_text_construct("feed",
+ ["items", "first", "rights"],
+ ["entries", "first", "rights"],
+ nil, nil, "maker.item.rights"
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+
+ assert_maker_atom_text_construct("feed",
+ ["items", "first", "source", "rights"],
+ ["entries", "first", "source", "rights"],
+ nil, nil, "maker.item.source.rights"
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_subtitle
+ assert_maker_atom_text_construct("feed",
+ ["channel", "subtitle"],
+ ["subtitle"],
+ nil, nil,
+ "maker.channel.description") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.channel.description = nil
+ end
+
+ assert_maker_atom_text_construct("feed",
+ ["channel", "subtitle"],
+ ["subtitle"],
+ nil, nil,
+ "maker.channel.description") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.channel.description {|d| d.content = nil}
+ end
+
+ assert_maker_atom_text_construct("feed",
+ ["items", "first", "source", "subtitle"],
+ ["entries", "first",
+ "source", "subtitle"],
+ nil, nil,
+ "maker.item.source.subtitle") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_title
+ assert_maker_atom_text_construct("feed",
+ ["channel", "title"], ["title"],
+ "maker.channel", ["title"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.channel.title = nil
+ end
+
+ assert_maker_atom_text_construct("feed",
+ ["items", "first", "title"],
+ ["entries", "first", "title"],
+ "maker.item", ["title"],
+ "maker.item.title") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.items.first.title = nil
+ end
+
+ assert_maker_atom_text_construct("feed",
+ ["items", "first", "source", "title"],
+ ["entries", "first", "source", "title"],
+ nil, nil, "maker.item.source.title"
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_updated
+ assert_maker_atom_date_construct("feed",
+ ["channel", "updated"], ["updated"],
+ "maker.channel", ["updated"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.channel.updated = nil
+ end
+
+ assert_maker_atom_date_construct("feed",
+ ["items", "first", "updated"],
+ ["entries", "first", "updated"],
+ "maker.item", ["updated"]) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ maker.items.first.updated = nil
+ end
+
+ assert_maker_atom_date_construct("feed",
+ ["items", "first", "source", "updated"],
+ ["entries", "first", "source", "updated"]
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_published
+ assert_maker_atom_date_construct("feed",
+ ["items", "first", "published"],
+ ["entries", "first", "published"]
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_summary
+ assert_maker_atom_text_construct("feed",
+ ["items", "first", "description"],
+ ["entries", "first", "summary"],
+ nil, nil, "maker.item.description"
+ ) do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_content
+ assert_maker_atom_content("feed",
+ ["items", "first", "content"],
+ ["entries", "first", "content"],
+ "maker.item.content") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_id
+ assert_maker_atom_id("feed",
+ ["items", "first", "source"],
+ ["entries", "first", "source"],
+ "maker.item.source") do |maker|
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ end
+
+ def test_language
+ language = "ja"
+ feed = Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.channel.language = "ja"
+ end
+ assert_equal(language, feed.dc_language)
+ end
+
+ def test_date
+ date = Time.parse("2004/11/1 10:10")
+ feed = Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.items.new_item do |item|
+ item.link = "http://example.com/article.html"
+ item.title = "sample article"
+ item.date = date
+ end
+ end
+ assert_equal(date, feed.items[0].updated.content)
+ assert_equal([date], feed.items[0].dc_dates.collect {|_date| _date.value})
+ end
+
+ def test_channel_dc_date
+ date = Time.parse("2004/11/1 10:10")
+ feed = Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.channel.updated = nil
+ maker.channel.dc_date = date
+ setup_dummy_item_atom(maker)
+ end
+ assert_equal(date, feed.updated.content)
+ assert_equal([date], feed.dc_dates.collect {|_date| _date.value})
+ end
+
+ def test_item_dc_date
+ date = Time.parse("2004/11/1 10:10")
+ feed = Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+ maker.items.new_item do |item|
+ item.link = "http://example.com/article.html"
+ item.title = "sample article"
+ item.dc_date = date
+ end
+ end
+ assert_equal(date, feed.items[0].updated.content)
+ assert_equal([date], feed.items[0].dc_dates.collect {|_date| _date.value})
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_content.rb b/jni/ruby/test/rss/test_maker_content.rb
new file mode 100644
index 0000000..ba884f1
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_content.rb
@@ -0,0 +1,47 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerContent < TestCase
+
+ def setup
+ @uri = "http://purl.org/rss/1.0/modules/content/"
+
+ @elements = {
+ :encoded => "<em>ATTENTION</em>",
+ }
+ end
+
+ def test_rss10
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ @elements.each do |name, value|
+ item.__send__("#{accessor_name(name)}=", value)
+ end
+ end
+ assert_content(@elements, rss.items.last)
+ end
+
+ def test_rss20
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ @elements.each do |name, value|
+ item.__send__("#{accessor_name(name)}=", value)
+ end
+ end
+ assert_content(@elements, rss.items.last)
+ end
+
+ private
+ def accessor_name(name)
+ "content_#{name}"
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_dc.rb b/jni/ruby/test/rss/test_maker_dc.rb
new file mode 100644
index 0000000..977350c
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_dc.rb
@@ -0,0 +1,149 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerDublinCore < TestCase
+
+ def setup
+ @uri = "http://purl.org/dc/elements/1.1/"
+
+ t = Time.iso8601("2000-01-01T12:00:05+00:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+
+ @elements = {
+ :title => "hoge",
+ :description =>
+ " XML is placing increasingly heavy loads on
+ the existing technical infrastructure of the Internet.",
+ :creator => "Rael Dornfest (mailto:rael@oreilly.com)",
+ :subject => "XML",
+ :publisher => "The O'Reilly Network",
+ :contributor => "hogehoge",
+ :type => "fugafuga",
+ :format => "hohoho",
+ :identifier => "fufufu",
+ :source => "barbar",
+ :language => "ja",
+ :relation => "cococo",
+ :rights => "Copyright (c) 2000 O'Reilly &amp; Associates, Inc.",
+ :date => t,
+ }
+ end
+
+ def test_rss10
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ set_elements(maker.channel)
+
+ setup_dummy_image(maker)
+ set_elements(maker.image)
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ @elements.each do |name, value|
+ item.__send__("#{accessor_name(name)}=", value)
+ end
+
+ setup_dummy_textinput(maker)
+ set_elements(maker.textinput)
+ end
+ assert_dublin_core(@elements, rss.channel)
+ assert_dublin_core(@elements, rss.image)
+ assert_dublin_core(@elements, rss.items.last)
+ assert_dublin_core(@elements, rss.textinput)
+ end
+
+ def test_rss10_multiple
+ assert_multiple_dublin_core_rss10("_list")
+ assert_multiple_dublin_core_rss10("es")
+ end
+
+ def assert_multiple_dublin_core_rss10(multiple_rights_suffix)
+ elems = []
+ @elements.each do |name, value|
+ plural = name.to_s + (name == :rights ? multiple_rights_suffix : "s")
+ values = [value]
+ if name == :date
+ values << value + 60
+ else
+ values << value * 2
+ end
+ elems << [name, values, plural]
+ end
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ set_multiple_elements(maker.channel, elems)
+
+ setup_dummy_image(maker)
+ set_multiple_elements(maker.image, elems)
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ elems.each do |name, values, plural|
+ dc_elems = item.__send__("dc_#{plural}")
+ values.each do |value|
+ elem = dc_elems.__send__("new_#{name}")
+ elem.value = value
+ end
+ end
+
+ setup_dummy_textinput(maker)
+ set_multiple_elements(maker.textinput, elems)
+ end
+ assert_multiple_dublin_core(elems, rss.channel)
+ assert_multiple_dublin_core(elems, rss.image)
+ assert_multiple_dublin_core(elems, rss.items.last)
+ assert_multiple_dublin_core(elems, rss.textinput)
+ end
+
+ def test_date
+ t1 = Time.iso8601("2000-01-01T12:00:05+00:00")
+ t2 = Time.iso8601("2005-01-01T12:00:05+00:00")
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.date = t1
+ maker.channel.dc_dates.new_date do |date|
+ date.value = t2
+ end
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ item.date = t2
+ item.dc_dates.new_date do |date|
+ date.value = t1
+ end
+ end
+ assert_equal([t1, t2], rss.channel.dc_dates.collect{|x| x.value})
+ assert_equal([t2, t1], rss.items.last.dc_dates.collect{|x| x.value})
+ end
+
+ private
+ def accessor_name(name)
+ "dc_#{name}"
+ end
+
+ def set_elements(target, elems=@elements)
+ elems.each do |name, value|
+ target.__send__("#{accessor_name(name)}=", value)
+ end
+ end
+
+ def set_multiple_elements(target, elems)
+ elems.each do |name, values, plural|
+ plural ||= "#{name}s"
+ dc_elems = target.__send__("dc_#{plural}")
+ values.each do |value|
+ dc_elems.__send__("new_#{name}") do |new_dc_elem|
+ new_dc_elem.value = value
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_image.rb b/jni/ruby/test/rss/test_maker_image.rb
new file mode 100644
index 0000000..d01654c
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_image.rb
@@ -0,0 +1,62 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerImage < TestCase
+
+ def setup
+ @uri = "http://web.resource.org/rss/1.0/modules/image/"
+
+ @favicon_infos = {
+ "about" => "http://www.kuro5hin.org/favicon.ico",
+ "image_size" => "small",
+ "dc_title" => "example",
+ }
+ @item_infos = {
+ "about" => "http://www.example.org/item.png",
+ "resource" => "http://www.example.org/item",
+ "dc_title" => "Example Image",
+ "image_width" => "100",
+ "image_height" => "65",
+ }
+ end
+
+ def test_rss10
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ @favicon_infos.each do |name, value|
+ maker.channel.image_favicon.__send__("#{name}=", value)
+ end
+
+ setup_dummy_image(maker)
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ @item_infos.each do |name, value|
+ item.image_item.__send__("#{name}=", value)
+ end
+
+ setup_dummy_textinput(maker)
+ end
+
+ setup_rss = RSS::Maker.make("1.0") do |maker|
+ rss.setup_maker(maker)
+ end
+
+ [rss, setup_rss].each_with_index do |target, i|
+ favicon = target.channel.image_favicon
+ assert_equal(@favicon_infos["about"], favicon.about)
+ assert_equal(@favicon_infos["image_size"], favicon.image_size)
+ assert_equal(@favicon_infos["dc_title"], favicon.dc_title)
+
+ item = target.items.last.image_item
+ assert_equal(@item_infos["about"], item.about)
+ assert_equal(@item_infos["resource"], item.resource)
+ assert_equal(@item_infos["image_width"].to_i, item.image_width)
+ assert_equal(@item_infos["image_height"].to_i, item.image_height)
+ assert_equal(@item_infos["dc_title"], item.dc_title)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_itunes.rb b/jni/ruby/test/rss/test_maker_itunes.rb
new file mode 100644
index 0000000..0d47171
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_itunes.rb
@@ -0,0 +1,469 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerITunes < TestCase
+ def test_author
+ assert_maker_itunes_author(%w(channel))
+ assert_maker_itunes_author(%w(items last))
+ end
+
+ def test_block
+ assert_maker_itunes_block(%w(channel))
+ assert_maker_itunes_block(%w(items last))
+ end
+
+ def test_category
+ assert_maker_itunes_category(%w(channel))
+ end
+
+ def test_image
+ assert_maker_itunes_image(%w(channel))
+ end
+
+ def test_duration
+ assert_maker_itunes_duration(%w(items last))
+ end
+
+ def test_explicit
+ assert_maker_itunes_explicit(%w(channel))
+ assert_maker_itunes_explicit(%w(items last))
+ end
+
+ def test_keywords
+ assert_maker_itunes_keywords(%w(channel))
+ assert_maker_itunes_keywords(%w(items last))
+ end
+
+ def test_new_feed_url
+ assert_maker_itunes_new_feed_url(%w(channel))
+ end
+
+ def test_owner
+ assert_maker_itunes_owner(%w(channel))
+ end
+
+ def test_subtitle
+ assert_maker_itunes_subtitle(%w(channel))
+ assert_maker_itunes_subtitle(%w(items last))
+ end
+
+ def test_summary
+ assert_maker_itunes_summary(%w(channel))
+ assert_maker_itunes_summary(%w(items last))
+ end
+
+ private
+
+ def assert_maker_itunes_author(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ author = "John Doe"
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ target.itunes_author = author
+ end
+ target = chain_reader(rss20, feed_readers)
+ assert_equal(author, target.itunes_author)
+ end
+ end
+
+ def _assert_maker_itunes_block(value, boolean_value, maker_readers,
+ feed_readers)
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ target.itunes_block = value
+ assert_equal(value, target.itunes_block)
+ assert_equal(boolean_value, target.itunes_block?)
+ end
+ target = chain_reader(rss20, feed_readers)
+ if [true, false].include?(value)
+ value = value ? "yes" : "no"
+ end
+ assert_equal(value, target.itunes_block)
+ assert_equal(boolean_value, target.itunes_block?)
+ end
+
+ def assert_maker_itunes_block(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ _assert_maker_itunes_block("yes", true, maker_readers, feed_readers)
+ _assert_maker_itunes_block("Yes", true, maker_readers, feed_readers)
+ _assert_maker_itunes_block("no", false, maker_readers, feed_readers)
+ _assert_maker_itunes_block("", false, maker_readers, feed_readers)
+ _assert_maker_itunes_block(true, true, maker_readers, feed_readers)
+ _assert_maker_itunes_block(false, false, maker_readers, feed_readers)
+ _assert_maker_itunes_block(nil, false, maker_readers, feed_readers)
+ end
+ end
+
+ def _assert_maker_itunes_category(categories, maker_readers, feed_readers)
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ categories.each do |category|
+ sub_target = target.itunes_categories
+ if category.is_a?(Array)
+ category.each do |sub_category|
+ sub_target = sub_target.new_category
+ sub_target.text = sub_category
+ end
+ else
+ sub_target.new_category.text = category
+ end
+ end
+ end
+
+ target = chain_reader(rss20, feed_readers)
+ actual_categories = target.itunes_categories.collect do |category|
+ cat = category.text
+ if category.itunes_categories.empty?
+ cat
+ else
+ [cat, *category.itunes_categories.collect {|c| c.text}]
+ end
+ end
+ assert_equal(categories, actual_categories)
+ end
+
+ def assert_maker_itunes_category(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ _assert_maker_itunes_category(["Audio Blogs"],
+ maker_readers, feed_readers)
+ _assert_maker_itunes_category([["Arts & Entertainment", "Games"]],
+ maker_readers, feed_readers)
+ _assert_maker_itunes_category([["Arts & Entertainment", "Games"],
+ ["Technology", "Computers"],
+ "Audio Blogs"],
+ maker_readers, feed_readers)
+ end
+ end
+
+ def assert_maker_itunes_image(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ url = "http://example.com/podcasts/everything/AllAboutEverything.jpg"
+
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ target.itunes_image = url
+ end
+
+ target = chain_reader(rss20, feed_readers)
+ assert_not_nil(target.itunes_image)
+ assert_equal(url, target.itunes_image.href)
+ end
+ end
+
+ def _assert_maker_itunes_duration(hour, minute, second, value,
+ maker_readers, feed_readers)
+ _assert_maker_itunes_duration_by_value(hour, minute, second, value,
+ maker_readers, feed_readers)
+ _assert_maker_itunes_duration_by_hour_minute_second(hour, minute, second,
+ value,
+ maker_readers,
+ feed_readers)
+ end
+
+ def _assert_maker_itunes_duration_by(hour, minute, second, value,
+ maker_readers, feed_readers)
+ expected_value = nil
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ expected_value = yield(target)
+ assert_equal(expected_value, target.itunes_duration)
+ target.itunes_duration do |duration|
+ assert_equal([hour, minute, second, expected_value],
+ [duration.hour, duration.minute,
+ duration.second, duration.content])
+ end
+ end
+ target = chain_reader(rss20, feed_readers)
+ duration = target.itunes_duration
+ assert_not_nil(duration)
+ assert_equal([hour, minute, second, expected_value],
+ [duration.hour, duration.minute,
+ duration.second, duration.content])
+ end
+
+ def _assert_maker_itunes_duration_by_value(hour, minute, second, value,
+ maker_readers, feed_readers)
+ _assert_maker_itunes_duration_by(hour, minute, second, value,
+ maker_readers, feed_readers) do |target|
+ target.itunes_duration = value
+ value
+ end
+ end
+
+ def _assert_maker_itunes_duration_by_hour_minute_second(hour, minute, second,
+ value,
+ maker_readers,
+ feed_readers)
+ _assert_maker_itunes_duration_by(hour, minute, second, value,
+ maker_readers, feed_readers) do |target|
+ target.itunes_duration do |duration|
+ duration.hour = hour
+ duration.minute = minute
+ duration.second = second
+ end
+ value.split(":").collect {|v| "%02d" % v.to_i}.join(":")
+ end
+ end
+
+ def _assert_maker_itunes_duration_invalid_value(value, maker_readers)
+ assert_raise(ArgumentError) do
+ ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ target.itunes_duration = value
+ end
+ end
+ end
+
+ def assert_maker_itunes_duration(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ _assert_maker_itunes_duration(7, 14, 5, "07:14:05", maker_readers,
+ feed_readers)
+ _assert_maker_itunes_duration(7, 14, 5, "7:14:05", maker_readers,
+ feed_readers)
+ _assert_maker_itunes_duration(0, 4, 55, "04:55", maker_readers,
+ feed_readers)
+ _assert_maker_itunes_duration(0, 4, 5, "4:05", maker_readers,
+ feed_readers)
+
+ _assert_maker_itunes_duration_invalid_value("5", maker_readers)
+ _assert_maker_itunes_duration_invalid_value("09:07:14:05", maker_readers)
+ _assert_maker_itunes_duration_invalid_value("10:5", maker_readers)
+ _assert_maker_itunes_duration_invalid_value("10:03:5", maker_readers)
+ _assert_maker_itunes_duration_invalid_value("10:3:05", maker_readers)
+
+ _assert_maker_itunes_duration_invalid_value("xx:xx:xx", maker_readers)
+ end
+ end
+
+ def _assert_maker_itunes_explicit(explicit, value,
+ maker_readers, feed_readers)
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ target.itunes_explicit = value
+ assert_equal(explicit, target.itunes_explicit?)
+ end
+ target = chain_reader(rss20, feed_readers)
+ assert_equal(value, target.itunes_explicit)
+ assert_equal(explicit, target.itunes_explicit?)
+ end
+
+ def assert_maker_itunes_explicit(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ _assert_maker_itunes_explicit(true, "yes", maker_readers, feed_readers)
+ _assert_maker_itunes_explicit(false, "clean",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_explicit(nil, "no", maker_readers, feed_readers)
+ end
+ end
+
+ def _assert_maker_itunes_keywords(keywords, value,
+ maker_readers, feed_readers)
+ _assert_maker_itunes_keywords_by_value(keywords, value,
+ maker_readers, feed_readers)
+ _assert_maker_itunes_keywords_by_keywords(keywords, maker_readers,
+ feed_readers)
+ end
+
+ def _assert_maker_itunes_keywords_by(keywords, maker_readers, feed_readers)
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ yield(target)
+ end
+ assert_nothing_raised do
+ rss20 = ::RSS::Parser.parse(rss20.to_s)
+ end
+ target = chain_reader(rss20, feed_readers)
+ assert_equal(keywords, target.itunes_keywords)
+ end
+
+ def _assert_maker_itunes_keywords_by_value(keywords, value,
+ maker_readers, feed_readers)
+ _assert_maker_itunes_keywords_by(keywords, maker_readers,
+ feed_readers) do |target|
+ target.itunes_keywords = value
+ end
+ end
+
+ def _assert_maker_itunes_keywords_by_keywords(keywords,
+ maker_readers, feed_readers)
+ _assert_maker_itunes_keywords_by(keywords, maker_readers,
+ feed_readers) do |target|
+ target.itunes_keywords = keywords
+ end
+ end
+
+ def assert_maker_itunes_keywords(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ _assert_maker_itunes_keywords(["salt"], "salt",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_keywords(["salt"], " salt ",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_keywords(["salt", "pepper", "shaker", "exciting"],
+ "salt, pepper, shaker, exciting",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_keywords(["metric", "socket", "wrenches",
+ "toolsalt"],
+ "metric, socket, wrenches, toolsalt",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_keywords(["olitics", "red", "blue", "state"],
+ "olitics, red, blue, state",
+ maker_readers, feed_readers)
+ end
+ end
+
+ def assert_maker_itunes_new_feed_url(maker_readers, feed_readers=nil)
+ feed_readers ||= maker_readers
+ url = "http://newlocation.com/example.rss"
+
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ target.itunes_new_feed_url = url
+ end
+ target = chain_reader(rss20, feed_readers)
+ assert_equal(url, target.itunes_new_feed_url)
+ end
+
+ def _assert_maker_itunes_owner(name, email, maker_readers, feed_readers)
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ owner = target.itunes_owner
+ owner.itunes_name = name
+ owner.itunes_email = email
+ end
+ owner = chain_reader(rss20, feed_readers).itunes_owner
+ if name.nil? and email.nil?
+ assert_nil(owner)
+ else
+ assert_not_nil(owner)
+ assert_equal(name, owner.itunes_name)
+ assert_equal(email, owner.itunes_email)
+ end
+ end
+
+ def assert_maker_itunes_owner(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ _assert_maker_itunes_owner("John Doe", "john.doe@example.com",
+ maker_readers, feed_readers)
+
+ not_set_name = (["maker"] + maker_readers + ["itunes_owner"]).join(".")
+ assert_not_set_error(not_set_name, ["itunes_name"]) do
+ _assert_maker_itunes_owner(nil, "john.doe@example.com",
+ maker_readers, feed_readers)
+ end
+ assert_not_set_error(not_set_name, ["itunes_email"]) do
+ _assert_maker_itunes_owner("John Doe", nil,
+ maker_readers, feed_readers)
+ end
+
+ _assert_maker_itunes_owner(nil, nil, maker_readers, feed_readers)
+ end
+ end
+
+ def _assert_maker_itunes_subtitle(subtitle, maker_readers, feed_readers)
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ target.itunes_subtitle = subtitle
+ end
+
+ target = chain_reader(rss20, feed_readers)
+ assert_equal(subtitle, target.itunes_subtitle)
+ end
+
+ def assert_maker_itunes_subtitle(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ _assert_maker_itunes_subtitle("A show about everything",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_subtitle("A short primer on table spices",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_subtitle("Comparing socket wrenches is fun!",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_subtitle("Red + Blue != Purple",
+ maker_readers, feed_readers)
+ end
+ end
+
+ def _assert_maker_itunes_summary(summary, maker_readers, feed_readers)
+ rss20 = ::RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ target = chain_reader(maker, maker_readers)
+ target.itunes_summary = summary
+ end
+
+ target = chain_reader(rss20, feed_readers)
+ assert_equal(summary, target.itunes_summary)
+ end
+
+ def assert_maker_itunes_summary(maker_readers, feed_readers=nil)
+ _wrap_assertion do
+ feed_readers ||= maker_readers
+ _assert_maker_itunes_summary("All About Everything is a show about " +
+ "everything. Each week we dive into any " +
+ "subject known to man and talk about it " +
+ "as much as we can. Look for our Podcast " +
+ "in the iTunes Music Store",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_summary("This week we talk about salt and pepper " +
+ "shakers, comparing and contrasting pour " +
+ "rates, construction materials, and " +
+ "overall aesthetics. Come and join the " +
+ "party!",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_summary("This week we talk about metric vs. old " +
+ "english socket wrenches. Which one is " +
+ "better? Do you really need both? Get " +
+ "all of your answers here.",
+ maker_readers, feed_readers)
+ _assert_maker_itunes_summary("This week we talk about surviving in a " +
+ "Red state if you're a Blue person. Or " +
+ "vice versa.",
+ maker_readers, feed_readers)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_slash.rb b/jni/ruby/test/rss/test_maker_slash.rb
new file mode 100644
index 0000000..ea95a99
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_slash.rb
@@ -0,0 +1,37 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerSlash < TestCase
+ def setup
+ @elements = {
+ "section" => "articles",
+ "department" => "not-an-ocean-unless-there-are-lobsters",
+ "comments" => 177,
+ "hit_parades" => [177, 155, 105, 33, 6, 3, 0],
+ }
+ end
+
+ def test_rss10
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ @elements.each do |name, value|
+ item.send("slash_#{name}=", value)
+ end
+ end
+
+ item = rss.items.last
+ assert_not_nil(item)
+ assert_slash_elements(item)
+ end
+
+ private
+ def assert_slash_elements(target)
+ super(@elements, target)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_sy.rb b/jni/ruby/test/rss/test_maker_sy.rb
new file mode 100644
index 0000000..7117e3a
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_sy.rb
@@ -0,0 +1,44 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerSyndication < TestCase
+
+ def setup
+ @uri = "http://purl.org/rss/1.0/modules/syndication/"
+
+ t = Time.iso8601("2000-01-01T12:00:05+00:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+
+ @elements = {
+ :updatePeriod => "hourly",
+ :updateFrequency => "2",
+ :updateBase => t,
+ }
+ end
+
+ def test_rss10
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ set_elements(maker.channel)
+ setup_dummy_item(maker)
+ end
+ assert_syndication(@elements, rss.channel)
+ end
+
+ private
+ def accessor_name(name)
+ "sy_#{name}"
+ end
+
+ def set_elements(target)
+ @elements.each do |name, value|
+ target.__send__("#{accessor_name(name)}=", value)
+ end
+ end
+
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_taxo.rb b/jni/ruby/test/rss/test_maker_taxo.rb
new file mode 100644
index 0000000..4c4c2e3
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_taxo.rb
@@ -0,0 +1,81 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerTaxonomy < TestCase
+
+ def setup
+ @uri = "http://purl.org/rss/1.0/modules/taxonomy/"
+
+ @resources = [
+ "http://meerkat.oreillynet.com/?c=cat23",
+ "http://meerkat.oreillynet.com/?c=47",
+ "http://dmoz.org/Computers/Data_Formats/Markup_Languages/XML/",
+ ]
+
+ @topics = [
+ {
+ :link => "http://meerkat.oreillynet.com/?c=cat23",
+ :title => "Data: XML",
+ :description => "A Meerkat channel",
+ },
+ {
+ :link => "http://dmoz.org/Computers/Data_Formats/Markup_Languages/XML/",
+ :title => "XML",
+ :subject => "XML",
+ :description => "DMOZ category",
+ :topics => [
+ "http://meerkat.oreillynet.com/?c=cat23",
+ "http://dmoz.org/Computers/Data_Formats/Markup_Languages/SGML/",
+ "http://dmoz.org/Computers/Programming/Internet/",
+ ]
+ },
+ ]
+ end
+
+ def test_rss10
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ set_topics(maker.channel)
+
+ setup_dummy_item(maker)
+ set_topics(maker.items.last)
+
+ setup_taxo_topic(maker, @topics)
+ end
+ assert_equal(@resources, rss.channel.taxo_topics.resources)
+ assert_equal(@resources, rss.items.last.taxo_topics.resources)
+ assert_taxo_topic(@topics, rss)
+ end
+
+ def _test_date
+ t1 = Time.iso8601("2000-01-01T12:00:05+00:00")
+ t2 = Time.iso8601("2005-01-01T12:00:05+00:00")
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.date = t1
+ maker.channel.dc_dates.new_date do |date|
+ date.value = t2
+ end
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ item.date = t2
+ item.dc_dates.new_date do |date|
+ date.value = t1
+ end
+ end
+ assert_equal([t1, t2], rss.channel.dc_dates.collect{|x| x.value})
+ assert_equal([t2, t1], rss.items.last.dc_dates.collect{|x| x.value})
+ end
+
+ private
+ def set_topics(target, resources=@resources)
+ resources.each do |value|
+ target.taxo_topics << value
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_trackback.rb b/jni/ruby/test/rss/test_maker_trackback.rb
new file mode 100644
index 0000000..411bf31
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_trackback.rb
@@ -0,0 +1,41 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerTrackBack < TestCase
+
+ def setup
+ @uri = "http://madskills.com/public/xml/rss/module/trackback/"
+
+ @elements = {
+ :ping => "http://bar.com/tb.cgi?tb_id=rssplustrackback",
+ :abouts => [
+ "http://foo.com/trackback/tb.cgi?tb_id=20020923",
+ "http://bar.com/trackback/tb.cgi?tb_id=20041114",
+ ],
+ }
+ end
+
+ def test_rss10
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ setup_dummy_item(maker)
+ item = maker.items.last
+ item.trackback_ping = @elements[:ping]
+ @elements[:abouts].each do |about|
+ item.trackback_abouts.new_about do |new_about|
+ new_about.value = about
+ end
+ end
+ end
+ assert_trackback(@elements, rss.items.last)
+ end
+
+ private
+ def accessor_name(name)
+ "trackback_#{name}"
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_maker_xml-stylesheet.rb b/jni/ruby/test/rss/test_maker_xml-stylesheet.rb
new file mode 100644
index 0000000..4f7c62a
--- /dev/null
+++ b/jni/ruby/test/rss/test_maker_xml-stylesheet.rb
@@ -0,0 +1,83 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestMakerXMLStyleSheet < TestCase
+
+ def test_xml_stylesheet
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+ end
+
+ xss = rss.xml_stylesheets.first
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+
+
+ href = 'http://example.com/index.xsl'
+ type = 'text/xsl'
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.xml_stylesheets.new_xml_stylesheet do |_xss|
+ _xss.href = href
+ end
+
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+ end
+
+ xss = rss.xml_stylesheets.first
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ end
+
+ def test_not_valid_xml_stylesheet
+ href = 'xss.XXX'
+ type = "text/xsl"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ # xss.href = href
+ xss.type = type
+ end
+
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+ end
+ assert(rss.xml_stylesheets.empty?)
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ # xss.type = type
+ end
+
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+ end
+ assert(rss.xml_stylesheets.empty?)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/rss/test_parser.rb b/jni/ruby/test/rss/test_parser.rb
new file mode 100644
index 0000000..8d8d1e1
--- /dev/null
+++ b/jni/ruby/test/rss/test_parser.rb
@@ -0,0 +1,64 @@
+require "tempfile"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/dublincore"
+
+module RSS
+ class TestParser < TestCase
+ def setup
+ @_default_parser = Parser.default_parser
+ @rss10 = make_RDF(<<-EOR)
+#{make_channel}
+#{make_item}
+#{make_textinput}
+#{make_image}
+EOR
+ @rss_tmp = Tempfile.new(%w"rss10- .rdf")
+ @rss_tmp.print(@rss10)
+ @rss_tmp.close
+ @rss_file = @rss_tmp.path.untaint
+ end
+
+ def teardown
+ Parser.default_parser = @_default_parser
+ @rss_tmp.close(true)
+ end
+
+ def test_default_parser
+ assert_nothing_raised do
+ Parser.default_parser = RSS::AVAILABLE_PARSERS.first
+ end
+
+ assert_raise(RSS::NotValidXMLParser) do
+ Parser.default_parser = RSS::Parser
+ end
+ end
+
+ def test_parse
+ assert_not_nil(RSS::Parser.parse(@rss_file))
+
+ garbage_rss_file = @rss_file + "-garbage"
+ if RSS::Parser.default_parser.name == "RSS::XMLParserParser"
+ assert_raise(RSS::NotWellFormedError) do
+ RSS::Parser.parse(garbage_rss_file)
+ end
+ else
+ assert_nil(RSS::Parser.parse(garbage_rss_file))
+ end
+ end
+
+ def test_parse_tag_includes_hyphen
+ assert_nothing_raised do
+ RSS::Parser.parse(make_RDF(<<-EOR))
+<xCal:x-calconnect-venue xmlns:xCal="urn:ietf:params:xml:ns:xcal" />
+#{make_channel}
+#{make_item}
+#{make_textinput}
+#{make_image}
+EOR
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_parser_1.0.rb b/jni/ruby/test/rss/test_parser_1.0.rb
new file mode 100644
index 0000000..2efecda
--- /dev/null
+++ b/jni/ruby/test/rss/test_parser_1.0.rb
@@ -0,0 +1,528 @@
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/dublincore"
+
+module RSS
+ class TestParser10 < TestCase
+ def test_RDF
+ assert_ns("", RDF::URI) do
+ Parser.parse(<<-EOR)
+#{make_xmldecl}
+<RDF/>
+EOR
+ end
+
+ assert_ns("", RDF::URI) do
+ Parser.parse(<<-EOR)
+#{make_xmldecl}
+<RDF xmlns="hoge"/>
+EOR
+ end
+
+ assert_ns("rdf", RDF::URI) do
+ Parser.parse(<<-EOR)
+#{make_xmldecl}
+<rdf:RDF xmlns:rdf="hoge"/>
+EOR
+ end
+
+ assert_parse(<<-EOR, :missing_tag, "channel", "RDF")
+#{make_xmldecl}
+<rdf:RDF xmlns:rdf="#{RSS::RDF::URI}"/>
+EOR
+
+ assert_parse(<<-EOR, :missing_tag, "channel", "RDF")
+#{make_xmldecl}
+<RDF xmlns="#{RSS::RDF::URI}"/>
+EOR
+
+ assert_parse(<<-EOR, :missing_tag, "channel", "RDF")
+#{make_xmldecl}
+<RDF xmlns="#{RSS::RDF::URI}"/>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "item", "RDF")
+#{make_channel}
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "item", "RDF")
+#{make_channel}
+#{make_image}
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "item", "RDF")
+#{make_channel}
+#{make_textinput}
+EOR
+
+ assert_too_much_tag("image", "RDF") do
+ Parser.parse(make_RDF(<<-EOR))
+#{make_channel}
+#{make_image}
+#{make_image}
+#{make_item}
+#{make_textinput}
+EOR
+ end
+
+ assert_parse(make_RDF(<<-EOR), :nothing_raised)
+#{make_channel}
+#{make_item}
+#{make_image}
+#{make_textinput}
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :nothing_raised)
+#{make_channel}
+#{make_item}
+#{make_textinput}
+#{make_image}
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :nothing_raised)
+#{make_channel}
+#{make_image}
+#{make_item}
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :nothing_raised)
+#{make_channel}
+#{make_image}
+#{make_item}
+#{make_textinput}
+EOR
+
+ 1.step(15, 3) do |i|
+ rss = make_RDF() do
+ res = make_channel
+ i.times { res << make_item }
+ res
+ end
+ assert_parse(rss, :nothing_raised)
+ end
+ end
+
+ def test_undefined_entity
+ return unless RSS::Parser.default_parser.raise_for_undefined_entity?
+ assert_parse(make_RDF(<<-EOR), :raises, RSS::NotWellFormedError)
+#{make_channel}
+#{make_image}
+<item rdf:about="#{RDF_ABOUT}">
+ <title>#{TITLE_VALUE} &UNKNOWN_ENTITY;</title>
+ <link>#{LINK_VALUE}</link>
+ <description>#{DESCRIPTION_VALUE}</description>
+</item>
+#{make_textinput}
+EOR
+ end
+
+ def test_channel
+ assert_parse(make_RDF(<<-EOR), :missing_attribute, "channel", "rdf:about")
+<channel />
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "title", "channel")
+<channel rdf:about="http://example.com/"/>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "link", "channel")
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+</channel>
+EOR
+
+ assert_parse(make_RDF(<<EOR), :missing_tag, "description", "channel")
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+</channel>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "items", "channel")
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+ <description>hogehoge</description>
+</channel>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_attribute, "image", "rdf:resource")
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+ <description>hogehoge</description>
+ <image/>
+</channel>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "items", "channel")
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+ <description>hogehoge</description>
+ <image rdf:resource="http://example.com/hoge.png" />
+</channel>
+EOR
+
+ rss = make_RDF(<<-EOR)
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+ <description>hogehoge</description>
+ <image rdf:resource="http://example.com/hoge.png" />
+ <items/>
+</channel>
+EOR
+
+ assert_missing_tag("Seq", "items") do
+ Parser.parse(rss)
+ end
+
+ assert_missing_tag("item", "RDF") do
+ Parser.parse(rss, false).validate
+ end
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "item", "RDF")
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+ <description>hogehoge</description>
+ <image rdf:resource="http://example.com/hoge.png" />
+ <items>
+ <rdf:Seq>
+ </rdf:Seq>
+ </items>
+</channel>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_attribute, "textinput", "rdf:resource")
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+ <description>hogehoge</description>
+ <image rdf:resource="http://example.com/hoge.png" />
+ <items>
+ <rdf:Seq>
+ </rdf:Seq>
+ </items>
+ <textinput/>
+</channel>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "item", "RDF")
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+ <description>hogehoge</description>
+ <image rdf:resource="http://example.com/hoge.png" />
+ <items>
+ <rdf:Seq>
+ </rdf:Seq>
+ </items>
+ <textinput rdf:resource="http://example.com/search" />
+</channel>
+EOR
+ end
+
+ def test_rdf_li
+ rss = make_RDF(<<-EOR)
+<channel rdf:about="http://example.com/">
+ <title>hoge</title>
+ <link>http://example.com/</link>
+ <description>hogehoge</description>
+ <image rdf:resource="http://example.com/hoge.png" />
+ <items>
+ <rdf:Seq>
+ <rdf:li \#{rdf_li_attr}/>
+ </rdf:Seq>
+ </items>
+ <textinput rdf:resource="http://example.com/search" />
+</channel>
+#{make_item}
+EOR
+
+ source = Proc.new do |rdf_li_attr|
+ eval(%Q[%Q[#{rss}]], binding)
+ end
+
+ attr = %q[resource="http://example.com/hoge"]
+ assert_parse(source.call(attr), :nothing_raised)
+
+ attr = %q[rdf:resource="http://example.com/hoge"]
+ assert_parse(source.call(attr), :nothing_raised)
+
+ assert_parse(source.call(""), :missing_attribute, "li", "resource")
+ end
+
+ def test_image
+ assert_parse(make_RDF(<<-EOR), :missing_attribute, "image", "rdf:about")
+#{make_channel}
+<image>
+</image>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "title", "image")
+#{make_channel}
+<image rdf:about="http://example.com/hoge.png">
+</image>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "url", "image")
+#{make_channel}
+<image rdf:about="http://example.com/hoge.png">
+ <title>hoge</title>
+</image>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "link", "image")
+#{make_channel}
+<image rdf:about="http://example.com/hoge.png">
+ <title>hoge</title>
+ <url>http://example.com/hoge.png</url>
+</image>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "item", "RDF")
+#{make_channel}
+<image rdf:about="http://example.com/hoge.png">
+ <title>hoge</title>
+ <url>http://example.com/hoge.png</url>
+ <link>http://example.com/</link>
+</image>
+EOR
+
+ rss = make_RDF(<<-EOR)
+#{make_channel}
+<image rdf:about="http://example.com/hoge.png">
+ <link>http://example.com/</link>
+ <url>http://example.com/hoge.png</url>
+ <title>hoge</title>
+</image>
+EOR
+
+ assert_missing_tag("item", "RDF") do
+ Parser.parse(rss)
+ end
+
+ assert_missing_tag("item", "RDF") do
+ Parser.parse(rss, false).validate
+ end
+ end
+
+ def test_item
+ assert_parse(make_RDF(<<-EOR), :missing_attribute, "item", "rdf:about")
+#{make_channel}
+#{make_image}
+<item>
+</item>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "title", "item")
+#{make_channel}
+#{make_image}
+<item rdf:about="http://example.com/hoge.html">
+</item>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "link", "item")
+#{make_channel}
+#{make_image}
+<item rdf:about="http://example.com/hoge.html">
+ <title>hoge</title>
+</item>
+EOR
+
+ assert_too_much_tag("title", "item") do
+ Parser.parse(make_RDF(<<-EOR))
+#{make_channel}
+#{make_image}
+<item rdf:about="http://example.com/hoge.html">
+ <title>hoge</title>
+ <title>hoge</title>
+ <link>http://example.com/hoge.html</link>
+</item>
+EOR
+ end
+
+ assert_parse(make_RDF(<<-EOR), :nothing_raised)
+#{make_channel}
+#{make_image}
+<item rdf:about="http://example.com/hoge.html">
+ <title>hoge</title>
+ <link>http://example.com/hoge.html</link>
+</item>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :nothing_raised)
+#{make_channel}
+#{make_image}
+<item rdf:about="http://example.com/hoge.html">
+ <title>hoge</title>
+ <link>http://example.com/hoge.html</link>
+ <description>hogehoge</description>
+</item>
+EOR
+ end
+
+ def test_textinput
+ assert_parse(make_RDF(<<-EOR), :missing_attribute, "textinput", "rdf:about")
+#{make_channel}
+#{make_image}
+#{make_item}
+<textinput>
+</textinput>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "title", "textinput")
+#{make_channel}
+#{make_image}
+#{make_item}
+<textinput rdf:about="http://example.com/search.html">
+</textinput>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "description", "textinput")
+#{make_channel}
+#{make_image}
+#{make_item}
+<textinput rdf:about="http://example.com/search.html">
+ <title>hoge</title>
+</textinput>
+EOR
+
+ assert_too_much_tag("title", "textinput") do
+ Parser.parse(make_RDF(<<-EOR))
+#{make_channel}
+#{make_image}
+#{make_item}
+<textinput rdf:about="http://example.com/search.html">
+ <title>hoge</title>
+ <title>hoge</title>
+ <description>hogehoge</description>
+</textinput>
+EOR
+ end
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "name", "textinput")
+#{make_channel}
+#{make_image}
+#{make_item}
+<textinput rdf:about="http://example.com/search.html">
+ <title>hoge</title>
+ <description>hogehoge</description>
+</textinput>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :missing_tag, "link", "textinput")
+#{make_channel}
+#{make_image}
+#{make_item}
+<textinput rdf:about="http://example.com/search.html">
+ <title>hoge</title>
+ <description>hogehoge</description>
+ <name>key</name>
+</textinput>
+EOR
+
+ assert_parse(make_RDF(<<-EOR), :nothing_raised)
+#{make_channel}
+#{make_image}
+#{make_item}
+<textinput rdf:about="http://example.com/search.html">
+ <title>hoge</title>
+ <description>hogehoge</description>
+ <name>key</name>
+ <link>http://example.com/search.html</link>
+</textinput>
+EOR
+ end
+
+ def test_ignore
+ name = "a"
+ rss = make_RDF(<<-EOR)
+#{make_channel}
+#{make_item}
+<#{name}/>
+EOR
+ assert_not_expected_tag(name, ::RSS::URI, "RDF") do
+ Parser.parse(rss, true, false)
+ end
+
+ uri = ""
+ name = "a"
+ rss = make_RDF(<<-EOR)
+#{make_channel}
+#{make_item}
+<#{name} xmlns=""/>
+EOR
+ assert_parse(rss, :nothing_raised)
+ assert_not_expected_tag(name, uri, "RDF") do
+ Parser.parse(rss, true, false)
+ end
+
+ uri = "http://example.com/"
+ name = "a"
+ rss = make_RDF(<<-EOR)
+#{make_channel}
+#{make_item}
+<x:#{name} xmlns:x="#{uri}"/>
+EOR
+ assert_parse(rss, :nothing_raised)
+ assert_not_expected_tag(name, uri, "RDF") do
+ Parser.parse(rss, true, false)
+ end
+
+ uri = ::RSS::URI
+ name = "a"
+ rss = make_RDF(<<-EOR)
+#{make_channel}
+#{make_item}
+#{make_image("<#{name}/>")}
+EOR
+ assert_parse(rss, :nothing_raised)
+ assert_not_expected_tag(name, uri, "image") do
+ Parser.parse(rss, true, false)
+ end
+
+ uri = CONTENT_URI
+ name = "encoded"
+ elem = "<#{name} xmlns='#{uri}'/>"
+ rss = make_RDF(<<-EOR)
+#{make_channel}
+#{make_item}
+#{make_image(elem)}
+EOR
+ assert_parse(rss, :nothing_raised)
+ assert_not_expected_tag(name, uri, "image") do
+ Parser.parse(rss, true, false)
+ end
+ end
+
+ def test_unknown_duplicated_element
+ xmlns = {"test" => "http://localhost/test"}
+ assert_parse(make_RDF(<<-EOR, xmlns), :nothing_raised)
+ #{make_channel("<test:string/>")}
+ #{make_item}
+ #{make_image}
+ EOR
+ end
+
+ def test_unknown_case_insensitive_duplicated_element
+ xmlns = {
+ "foaf" => "http://xmlns.com/foaf/0.1/",
+ "dc" => "http://purl.org/dc/elements/1.1/",
+ }
+ assert_parse(make_RDF(<<-EOR, xmlns), :nothing_raised)
+ #{make_channel}
+ #{make_item}
+ #{make_image}
+ <foaf:Image rdf:about="http://example.com/myself.png">
+ <dc:title>Myself</dc:title>
+ <dc:link>http://example.com/</dc:link>
+ </foaf:Image>
+ EOR
+ end
+ end
+end
+
diff --git a/jni/ruby/test/rss/test_parser_2.0.rb b/jni/ruby/test/rss/test_parser_2.0.rb
new file mode 100644
index 0000000..68c7d1c
--- /dev/null
+++ b/jni/ruby/test/rss/test_parser_2.0.rb
@@ -0,0 +1,122 @@
+require_relative "rss-testcase"
+
+require "rss/2.0"
+
+module RSS
+ class TestParser20 < TestCase
+ def test_rss20
+ assert_parse(make_rss20(<<-EOR), :missing_tag, "channel", "rss")
+EOR
+
+ assert_parse(make_rss20(<<-EOR), :nothing_raised)
+#{make_channel20("")}
+EOR
+ end
+
+ def test_cloud20
+ attrs = [
+ ["domain", CLOUD_DOMAIN],
+ ["port", CLOUD_PORT],
+ ["path", CLOUD_PATH],
+ ["registerProcedure", CLOUD_REGISTER_PROCEDURE],
+ ["protocol", CLOUD_PROTOCOL],
+ ]
+
+ (attrs.size + 1).times do |i|
+ missing_attr = attrs[i]
+ if missing_attr
+ meth = :missing_attribute
+ args = ["cloud", missing_attr[0]]
+ else
+ meth = :nothing_raised
+ args = []
+ end
+
+ cloud_attrs = []
+ attrs.each_with_index do |attr, j|
+ unless i == j
+ cloud_attrs << %Q[#{attr[0]}="#{attr[1]}"]
+ end
+ end
+
+ assert_parse(make_rss20(<<-EOR), meth, *args)
+#{make_channel20(%Q[<cloud #{cloud_attrs.join("\n")}/>])}
+EOR
+ end
+ end
+
+ def test_source20
+ assert_parse(make_rss20(<<-EOR), :missing_attribute, "source", "url")
+#{make_channel20(make_item20(%Q[<source>Example</source>]))}
+EOR
+
+ assert_parse(make_rss20(<<-EOR), :nothing_raised)
+#{make_channel20(make_item20(%Q[<source url="http://example.com/" />]))}
+EOR
+
+ assert_parse(make_rss20(<<-EOR), :nothing_raised)
+#{make_channel20(make_item20(%Q[<source url="http://example.com/">Example</source>]))}
+EOR
+ end
+
+ def test_enclosure20
+ attrs = [
+ ["url", ENCLOSURE_URL],
+ ["length", ENCLOSURE_LENGTH],
+ ["type", ENCLOSURE_TYPE],
+ ]
+
+ (attrs.size + 1).times do |i|
+ missing_attr = attrs[i]
+ if missing_attr
+ meth = :missing_attribute
+ args = ["enclosure", missing_attr[0]]
+ else
+ meth = :nothing_raised
+ args = []
+ end
+
+ enclosure_attrs = []
+ attrs.each_with_index do |attr, j|
+ unless i == j
+ enclosure_attrs << %Q[#{attr[0]}="#{attr[1]}"]
+ end
+ end
+
+ assert_parse(make_rss20(<<-EOR), meth, *args)
+#{make_channel20(%Q[
+#{make_item20(%Q[
+<enclosure
+ #{enclosure_attrs.join("\n")} />
+ ])}
+ ])}
+EOR
+ end
+ end
+
+ def test_category20
+ values = [nil, CATEGORY_DOMAIN]
+ values.each do |value|
+ domain = ""
+ domain << %Q[domain="#{value}"] if value
+
+ ["", "Example Text"].each do |text|
+ rss_src = make_rss20(<<-EOR)
+#{make_channel20(%Q[
+#{make_item20(%Q[
+<category #{domain}>#{text}</category>
+ ])}
+ ])}
+EOR
+ assert_parse(rss_src, :nothing_raised)
+
+ rss = RSS::Parser.parse(rss_src)
+ category = rss.items.last.categories.first
+ assert_equal(value, category.domain)
+ assert_equal(text, category.content)
+ end
+ end
+ end
+ end
+end
+
diff --git a/jni/ruby/test/rss/test_parser_atom_entry.rb b/jni/ruby/test/rss/test_parser_atom_entry.rb
new file mode 100644
index 0000000..352fbbe
--- /dev/null
+++ b/jni/ruby/test/rss/test_parser_atom_entry.rb
@@ -0,0 +1,163 @@
+require_relative "rss-testcase"
+
+require "rss/atom"
+
+module RSS
+ class TestParserAtom < TestCase
+ def test_entry_validation
+ assert_ns("", Atom::URI) do
+ Parser.parse(<<-EOA)
+<entry/>
+EOA
+ end
+
+ assert_ns("", Atom::URI) do
+ Parser.parse(<<-EOA)
+<entry xmlns="hoge"/>
+EOA
+ end
+
+ assert_parse(<<-EOA, :missing_tag, "id", "entry") do
+<entry xmlns="#{Atom::URI}"/>
+EOA
+ end
+
+ assert_parse(<<-EOA, :missing_tag, "title", "entry") do
+<entry xmlns="#{Atom::URI}">
+ <id>urn:uuid:506e336c-a26e-4457-917b-b89dca7ae746</id>
+</entry>
+EOA
+ end
+
+ assert_parse(<<-EOA, :missing_tag, "updated", "entry") do
+<entry xmlns="#{Atom::URI}">
+ <id>urn:uuid:506e336c-a26e-4457-917b-b89dca7ae746</id>
+ <title>Example Entry</title>
+</entry>
+EOA
+ end
+
+ assert_parse(<<-EOA, :missing_tag, "author", "entry") do
+<entry xmlns="#{Atom::URI}">
+ <id>urn:uuid:506e336c-a26e-4457-917b-b89dca7ae746</id>
+ <title>Example Entry</title>
+ <updated>2003-10-10T18:30:02Z</updated>
+</entry>
+EOA
+ end
+
+ assert_parse(<<-EOA, :nothing_raised) do
+<entry xmlns="#{Atom::URI}">
+ <id>urn:uuid:506e336c-a26e-4457-917b-b89dca7ae746</id>
+ <title>Example Entry</title>
+ <updated>2003-10-10T18:30:02Z</updated>
+ <author>
+ <name>A person</name>
+ </author>
+</entry>
+EOA
+ end
+ end
+
+ def test_entry
+ entry = RSS::Parser.parse(<<-EOA)
+<?xml version="1.0" encoding="utf-8"?>
+<entry xmlns="http://www.w3.org/2005/Atom">
+ <author>
+ <name>A person</name>
+ </author>
+ <title>Atom-Powered Robots Run Amok</title>
+ <link href="http://example.org/2003/12/13/atom03"/>
+ <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
+ <updated>2003-12-13T18:30:02Z</updated>
+ <summary>Some text.</summary>
+</entry>
+EOA
+ assert_not_nil(entry)
+ assert_equal("Atom-Powered Robots Run Amok", entry.title.content)
+ assert_equal("http://example.org/2003/12/13/atom03", entry.link.href)
+ assert_equal("urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a",
+ entry.id.content)
+ assert_equal(Time.parse("2003-12-13T18:30:02Z"), entry.updated.content)
+ assert_equal("Some text.", entry.summary.content)
+ end
+
+ def test_entry_author
+ assert_atom_person("author", method(:make_entry_document)) do |entry|
+ assert_equal(2, entry.authors.size)
+ entry.authors.last
+ end
+ end
+
+ def test_entry_category
+ assert_atom_category(method(:make_entry_document)) do |entry|
+ assert_equal(1, entry.categories.size)
+ entry.category
+ end
+ end
+
+ def test_entry_content_text
+ assert_atom_content(method(:make_entry_document)) do |entry|
+ entry.content
+ end
+ end
+
+ def test_entry_contributor
+ assert_atom_person("contributor", method(:make_entry_document)) do |entry|
+ assert_equal(1, entry.contributors.size)
+ entry.contributor
+ end
+ end
+
+ def test_entry_id
+ entry = RSS::Parser.parse(make_entry_document)
+ assert_equal(ENTRY_ID, entry.id.content)
+ end
+
+ def test_entry_link
+ assert_atom_link(method(:make_entry_document)) do |entry|
+ assert_equal(1, entry.links.size)
+ entry.link
+ end
+ end
+
+ def test_published
+ generator = method(:make_entry_document)
+ assert_atom_date_construct("published", generator) do |entry|
+ entry.published
+ end
+ end
+
+ def test_entry_rights
+ generator = method(:make_entry_document)
+ assert_atom_text_construct("rights", generator) do |entry|
+ entry.rights
+ end
+ end
+
+ def test_entry_source
+ generator = method(:make_entry_document_with_open_source)
+ assert_atom_source(generator) do |entry|
+ assert_not_nil(entry.source)
+ entry.source
+ end
+ end
+
+ def test_entry_summary
+ generator = method(:make_entry_document)
+ assert_atom_text_construct("summary", generator) do |entry|
+ entry.summary
+ end
+ end
+
+ def test_entry_title
+ entry = RSS::Parser.parse(make_entry_document)
+ assert_equal(ENTRY_TITLE, entry.title.content)
+ end
+
+ def test_entry_updated
+ entry = RSS::Parser.parse(make_entry_document)
+ assert_equal(Time.parse(ENTRY_UPDATED), entry.updated.content)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_parser_atom_feed.rb b/jni/ruby/test/rss/test_parser_atom_feed.rb
new file mode 100644
index 0000000..a5731b2
--- /dev/null
+++ b/jni/ruby/test/rss/test_parser_atom_feed.rb
@@ -0,0 +1,276 @@
+require_relative "rss-testcase"
+
+require "rss/atom"
+
+module RSS
+ class TestParserAtomFeed < TestCase
+ def test_feed_validation
+ assert_ns("", Atom::URI) do
+ Parser.parse(<<-EOA)
+<feed/>
+EOA
+ end
+
+ assert_ns("", Atom::URI) do
+ Parser.parse(<<-EOA)
+<feed xmlns="hoge"/>
+EOA
+ end
+
+ assert_parse(<<-EOA, :missing_tag, "id", "feed") do
+<feed xmlns="#{Atom::URI}"/>
+EOA
+ end
+
+ assert_parse(<<-EOA, :missing_tag, "title", "feed") do
+<feed xmlns="#{Atom::URI}">
+ <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
+</feed>
+EOA
+ end
+
+ assert_parse(<<-EOA, :missing_tag, "updated", "feed") do
+<feed xmlns="#{Atom::URI}">
+ <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
+ <title>Example Feed</title>
+</feed>
+EOA
+ end
+
+ assert_parse(<<-EOA, :missing_tag, "author", "feed") do
+<feed xmlns="#{Atom::URI}">
+ <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
+ <title>Example Feed</title>
+ <updated>2003-12-13T18:30:02Z</updated>
+</feed>
+EOA
+ end
+
+ assert_parse(<<-EOA, :nothing_raised) do
+<feed xmlns="#{Atom::URI}">
+ <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
+ <title>Example Feed</title>
+ <updated>2003-12-13T18:30:02Z</updated>
+ <author>
+ <name>A person</name>
+ </author>
+</feed>
+EOA
+ end
+ end
+
+ def test_lang
+ feed = RSS::Parser.parse(<<-EOA)
+<feed xmlns="#{Atom::URI}" xml:lang="ja">
+ <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
+ <title xml:lang="en">Example Feed</title>
+ <updated>2003-12-13T18:30:02Z</updated>
+ <author xml:lang="en">
+ <name>A person</name>
+ </author>
+</feed>
+EOA
+
+ assert_equal("ja", feed.lang)
+ assert_equal("ja", feed.id.lang)
+ assert_equal("en", feed.title.lang)
+ assert_equal("ja", feed.updated.lang)
+ assert_equal("en", feed.author.lang)
+ assert_equal("en", feed.author.name.lang)
+ end
+
+ def test_base
+ feed = RSS::Parser.parse(<<-EOA)
+<feed xmlns="#{Atom::URI}" xml:base="http://example.com/">
+ <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
+ <title xml:lang="en">Example Feed</title>
+ <updated>2003-12-13T18:30:02Z</updated>
+ <generator uri="generator">Generator</generator>
+ <link hreflang="ja" href="http://example.org/link1"/>
+ <link hreflang="en" href="link2"/>
+ <link hreflang="fr" xml:base="http://example.net/" href="link3"/>
+ <author>
+ <name>A person</name>
+ <uri>person</uri>
+ </author>
+</feed>
+EOA
+
+ assert_equal("http://example.com/", feed.base)
+ assert_equal("http://example.com/", feed.id.base)
+ assert_equal("http://example.com/", feed.title.base)
+ assert_equal("http://example.com/", feed.updated.base)
+ assert_equal("http://example.com/", feed.generator.base)
+ assert_equal("http://example.com/generator", feed.generator.uri)
+
+ assert_equal("http://example.com/", feed.links[0].base)
+ assert_equal("http://example.org/link1", feed.links[0].href)
+ assert_equal("http://example.com/", feed.links[1].base)
+ assert_equal("http://example.com/link2", feed.links[1].href)
+ assert_equal("http://example.net/", feed.links[2].base)
+ assert_equal("http://example.net/link3", feed.links[2].href)
+ assert_equal("http://example.com/person", feed.author.uri.content)
+ end
+
+ def test_feed_author
+ assert_atom_person("author", method(:make_feed)) do |feed|
+ assert_equal(2, feed.authors.size)
+ feed.authors[1]
+ end
+ end
+
+ def test_entry_author
+ generator = method(:make_feed_with_open_entry)
+ assert_atom_person("author", generator) do |feed|
+ assert_equal(1, feed.entries.size)
+ assert_equal(1, feed.entry.authors.size)
+ feed.entry.author
+ end
+ end
+
+ def test_feed_category
+ assert_atom_category(method(:make_feed)) do |feed|
+ assert_equal(1, feed.categories.size)
+ feed.category
+ end
+ end
+
+ def test_entry_category
+ assert_atom_category(method(:make_feed_with_open_entry)) do |feed|
+ assert_equal(1, feed.entries.size)
+ assert_equal(1, feed.entry.categories.size)
+ feed.entry.category
+ end
+ end
+
+ def test_entry_content
+ assert_atom_content(method(:make_feed_with_open_entry)) do |feed|
+ assert_equal(1, feed.entries.size)
+ feed.entry.content
+ end
+ end
+
+ def test_feed_contributor
+ assert_atom_person("contributor", method(:make_feed)) do |feed|
+ assert_equal(1, feed.contributors.size)
+ feed.contributor
+ end
+ end
+
+ def test_entry_contributor
+ generator = method(:make_feed_with_open_entry)
+ assert_atom_person("contributor", generator) do |feed|
+ assert_equal(1, feed.entries.size)
+ assert_equal(1, feed.entry.contributors.size)
+ feed.entry.contributor
+ end
+ end
+
+ def test_feed_generator
+ assert_atom_generator(method(:make_feed)) do |feed|
+ feed.generator
+ end
+ end
+
+ def test_feed_icon
+ assert_atom_icon(method(:make_feed)) do |feed|
+ feed.icon
+ end
+ end
+
+ def test_feed_id
+ feed = RSS::Parser.parse(make_feed(''))
+ assert_equal(FEED_ID, feed.id.content)
+ end
+
+ def test_entry_id
+ feed = RSS::Parser.parse(make_feed(''))
+ assert_equal(ENTRY_ID, feed.entry.id.content)
+ end
+
+ def test_feed_link
+ assert_atom_link(method(:make_feed)) do |feed|
+ assert_equal(1, feed.links.size)
+ feed.link
+ end
+ end
+
+ def test_entry_link
+ assert_atom_link(method(:make_feed_with_open_entry)) do |feed|
+ assert_equal(1, feed.entries.size)
+ assert_equal(1, feed.entry.links.size)
+ feed.entry.link
+ end
+ end
+
+ def test_feed_logo
+ assert_atom_logo(method(:make_feed)) do |feed|
+ feed.logo
+ end
+ end
+
+ def test_feed_rights
+ assert_atom_text_construct("rights", method(:make_feed)) do |feed|
+ feed.rights
+ end
+ end
+
+ def test_entry_rights
+ generator = method(:make_feed_with_open_entry)
+ assert_atom_text_construct("rights", generator) do |feed|
+ assert_equal(1, feed.entries.size)
+ feed.entry.rights
+ end
+ end
+
+ def test_entry_source
+ assert_atom_source(method(:make_feed_with_open_entry_source)) do |feed|
+ assert_equal(1, feed.entries.size)
+ assert_not_nil(feed.entry.source)
+ feed.entry.source
+ end
+ end
+
+ def test_feed_subtitle
+ assert_atom_text_construct("subtitle", method(:make_feed)) do |feed|
+ feed.subtitle
+ end
+ end
+
+ def test_feed_title
+ feed = RSS::Parser.parse(make_feed(''))
+ assert_equal(FEED_TITLE, feed.title.content)
+ end
+
+ def test_entry_title
+ feed = RSS::Parser.parse(make_feed(''))
+ assert_equal(ENTRY_TITLE, feed.entry.title.content)
+ end
+
+ def test_feed_updated
+ feed = RSS::Parser.parse(make_feed(''))
+ assert_equal(Time.parse(FEED_UPDATED), feed.updated.content)
+ end
+
+ def test_entry_updated
+ feed = RSS::Parser.parse(make_feed(''))
+ assert_equal(Time.parse(ENTRY_UPDATED), feed.entry.updated.content)
+ end
+
+ def test_entry_published
+ generator = method(:make_feed_with_open_entry)
+ assert_atom_date_construct("published", generator) do |feed|
+ assert_equal(1, feed.entries.size)
+ feed.entry.published
+ end
+ end
+
+ def test_entry_summary
+ generator = method(:make_feed_with_open_entry)
+ assert_atom_text_construct("summary", generator) do |feed|
+ assert_equal(1, feed.entries.size)
+ feed.entry.summary
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_setup_maker_0.9.rb b/jni/ruby/test/rss/test_setup_maker_0.9.rb
new file mode 100644
index 0000000..2714b9c
--- /dev/null
+++ b/jni/ruby/test/rss/test_setup_maker_0.9.rb
@@ -0,0 +1,246 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestSetupMaker09 < TestCase
+
+ def test_setup_maker_channel
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+ language = "ja"
+ copyright = "foo"
+ managingEditor = "bar"
+ webMaster = "web master"
+ rating = '(PICS-1.1 "http://www.rsac.org/ratingsv01.html" l gen true comment "RSACi North America Server" for "http://www.rsac.org" on "1996.04.16T08:15-0500" r (n 0 s 0 v 0 l 0))'
+ docs = "http://foo.com/doc"
+ skipDays = [
+ "Sunday",
+ "Monday",
+ ]
+ skipHours = [
+ "0",
+ "13",
+ ]
+ pubDate = Time.now
+ lastBuildDate = Time.now
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+ maker.channel.copyright = copyright
+ maker.channel.managingEditor = managingEditor
+ maker.channel.webMaster = webMaster
+ maker.channel.rating = rating
+ maker.channel.docs = docs
+ maker.channel.pubDate = pubDate
+ maker.channel.lastBuildDate = lastBuildDate
+
+ skipDays.each do |day|
+ maker.channel.skipDays.new_day do |new_day|
+ new_day.content = day
+ end
+ end
+ skipHours.each do |hour|
+ maker.channel.skipHours.new_hour do |new_hour|
+ new_hour.content = hour
+ end
+ end
+
+ setup_dummy_image(maker)
+ end
+
+ assert_not_set_error("maker.image", %w(title url)) do
+ RSS::Maker.make("0.91") do |maker|
+ rss.channel.setup_maker(maker)
+ end
+ end
+
+ new_rss = RSS::Maker.make("0.91") do |maker|
+ rss.channel.setup_maker(maker)
+ setup_dummy_image(maker)
+ end
+ channel = new_rss.channel
+
+ assert_equal(title, channel.title)
+ assert_equal(link, channel.link)
+ assert_equal(description, channel.description)
+ assert_equal(language, channel.language)
+ assert_equal(copyright, channel.copyright)
+ assert_equal(managingEditor, channel.managingEditor)
+ assert_equal(webMaster, channel.webMaster)
+ assert_equal(rating, channel.rating)
+ assert_equal(docs, channel.docs)
+ assert_equal(pubDate, channel.pubDate)
+ assert_equal(lastBuildDate, channel.lastBuildDate)
+
+ skipDays.each_with_index do |day, i|
+ assert_equal(day, channel.skipDays.days[i].content)
+ end
+ skipHours.each_with_index do |hour, i|
+ assert_equal(hour.to_i, channel.skipHours.hours[i].content)
+ end
+
+ assert(channel.items.empty?)
+ assert_nil(channel.textInput)
+ end
+
+ def test_setup_maker_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+ width = "144"
+ height = "400"
+ description = "an image"
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+
+ new_rss = RSS::Maker.make("0.91") do |maker|
+ rss.channel.setup_maker(maker)
+ rss.image.setup_maker(maker)
+ end
+
+ image = new_rss.image
+ assert_equal(title, image.title)
+ assert_equal(link, image.link)
+ assert_equal(url, image.url)
+ assert_equal(width.to_i, image.width)
+ assert_equal(height.to_i, image.height)
+ assert_equal(description, image.description)
+ end
+
+ def test_setup_maker_textinput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+
+ new_rss = RSS::Maker.make("0.91") do |maker|
+ rss.channel.setup_maker(maker)
+ rss.image.setup_maker(maker)
+ rss.textinput.setup_maker(maker)
+ end
+
+ textInput = new_rss.channel.textInput
+ assert_equal(title, textInput.title)
+ assert_equal(description, textInput.description)
+ assert_equal(name, textInput.name)
+ assert_equal(link, textInput.link)
+ end
+
+ def test_setup_maker_items(for_backward_compatibility=false)
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+
+ item_size = 5
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+ end
+ end
+
+ setup_dummy_image(maker)
+ end
+
+ new_rss = RSS::Maker.make("0.91") do |maker|
+ rss.channel.setup_maker(maker)
+
+ rss.items.each do |item|
+ if for_backward_compatibility
+ item.setup_maker(maker)
+ else
+ item.setup_maker(maker.items)
+ end
+ end
+
+ rss.image.setup_maker(maker)
+ end
+
+ assert_equal(item_size, new_rss.items.size)
+ new_rss.items.each_with_index do |item, i|
+ assert_equal("#{title}#{i}", item.title)
+ assert_equal("#{link}#{i}", item.link)
+ assert_equal("#{description}#{i}", item.description)
+ end
+ end
+
+ def test_setup_maker_items_backward_compatibility
+ test_setup_maker_items(true)
+ end
+
+ def test_setup_maker
+ encoding = "EUC-JP"
+ standalone = true
+
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ rss = RSS::Maker.make("0.91") do |maker|
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ setup_dummy_channel(maker)
+ setup_dummy_image(maker)
+ end
+
+ new_rss = RSS::Maker.make("0.91") do |maker|
+ rss.setup_maker(maker)
+ end
+
+ assert_equal("0.91", new_rss.rss_version)
+ assert_equal(encoding, new_rss.encoding)
+ assert_equal(standalone, new_rss.standalone)
+
+ xss = rss.xml_stylesheets.first
+ assert_equal(1, rss.xml_stylesheets.size)
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_setup_maker_1.0.rb b/jni/ruby/test/rss/test_setup_maker_1.0.rb
new file mode 100644
index 0000000..0408ca9
--- /dev/null
+++ b/jni/ruby/test/rss/test_setup_maker_1.0.rb
@@ -0,0 +1,550 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestSetupMaker10 < TestCase
+
+ def setup
+ t = Time.iso8601("2000-01-01T12:00:05+00:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+
+ @dc_elems = {
+ :title => "hoge",
+ :description =>
+ " XML is placing increasingly heavy loads on
+ the existing technical infrastructure of the Internet.",
+ :creator => "Rael Dornfest (mailto:rael@oreilly.com)",
+ :subject => "XML",
+ :publisher => "The O'Reilly Network",
+ :contributor => "hogehoge",
+ :type => "fugafuga",
+ :format => "hohoho",
+ :identifier => "fufufu",
+ :source => "barbar",
+ :language => "ja",
+ :relation => "cococo",
+ :rights => "Copyright (c) 2000 O'Reilly &amp; Associates, Inc.",
+ :date => t,
+ }
+
+ @sy_elems = {
+ :updatePeriod => "hourly",
+ :updateFrequency => "2",
+ :updateBase => t,
+ }
+
+ @content_elems = {
+ :encoded => "<em>ATTENTION</em>",
+ }
+
+ @trackback_elems = {
+ :ping => "http://bar.com/tb.cgi?tb_id=rssplustrackback",
+ :about => [
+ "http://foo.com/trackback/tb.cgi?tb_id=20020923",
+ "http://foo.com/trackback/tb.cgi?tb_id=20021010",
+ ],
+ }
+
+ @taxo_topic_elems = [
+ {
+ :link => "http://meerkat.oreillynet.com/?c=cat23",
+ :title => "Data: XML",
+ :description => "A Meerkat channel",
+ },
+ {
+ :link => "http://dmoz.org/Computers/Data_Formats/Markup_Languages/XML/",
+ :title => "XML",
+ :subject => "XML",
+ :description => "DMOZ category",
+ :topics => [
+ "http://meerkat.oreillynet.com/?c=cat23",
+ "http://dmoz.org/Computers/Data_Formats/Markup_Languages/SGML/",
+ "http://dmoz.org/Computers/Programming/Internet/",
+ ]
+ },
+ ]
+ end
+
+ def test_setup_maker_channel
+ about = "http://hoge.com"
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.channel.about = about
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+
+ @dc_elems.each do |var, value|
+ maker.channel.__send__("dc_#{var}=", value)
+ end
+
+ @sy_elems.each do |var, value|
+ maker.channel.__send__("sy_#{var}=", value)
+ end
+
+ setup_dummy_item(maker)
+ end
+
+ new_rss = RSS::Maker.make("1.0") do |maker|
+ rss.channel.setup_maker(maker)
+ rss.items.each do |item|
+ item.setup_maker(maker)
+ end
+ end
+ channel = new_rss.channel
+
+ assert_equal(about, channel.about)
+ assert_equal(title, channel.title)
+ assert_equal(link, channel.link)
+ assert_equal(description, channel.description)
+ assert_equal(1, channel.items.Seq.lis.size)
+ assert_nil(channel.image)
+ assert_nil(channel.textinput)
+
+ @dc_elems.each do |var, value|
+ assert_equal(value, channel.__send__("dc_#{var}"))
+ end
+
+ @sy_elems.each do |var, value|
+ value = value.to_i if var == :updateFrequency
+ assert_equal(value, channel.__send__("sy_#{var}"))
+ end
+
+ end
+
+ def test_setup_maker_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+
+ @dc_elems.each do |var, value|
+ maker.image.__send__("dc_#{var}=", value)
+ end
+
+ setup_dummy_item(maker)
+ end
+
+ new_rss = RSS::Maker.make("1.0") do |maker|
+ rss.channel.setup_maker(maker)
+ rss.image.setup_maker(maker)
+ rss.items.each do |item|
+ item.setup_maker(maker)
+ end
+ end
+
+ image = new_rss.image
+ assert_equal(url, image.about)
+ assert_equal(url, new_rss.channel.image.resource)
+ assert_equal(title, image.title)
+ assert_equal(link, image.link)
+ assert_equal(url, image.url)
+
+ @dc_elems.each do |var, value|
+ assert_equal(image.__send__("dc_#{var}"), value)
+ end
+ end
+
+ def test_setup_maker_textinput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.link = link
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+
+ @dc_elems.each do |var, value|
+ maker.textinput.__send__("dc_#{var}=", value)
+ end
+
+ setup_dummy_item(maker)
+ end
+
+ new_rss = RSS::Maker.make("1.0") do |maker|
+ rss.channel.setup_maker(maker)
+ rss.textinput.setup_maker(maker)
+ rss.items.each do |item|
+ item.setup_maker(maker)
+ end
+ end
+
+ textinput = new_rss.textinput
+ assert_equal(link, textinput.about)
+ assert_equal(link, new_rss.channel.textinput.resource)
+ assert_equal(title, textinput.title)
+ assert_equal(name, textinput.name)
+ assert_equal(description, textinput.description)
+ assert_equal(link, textinput.link)
+
+ @dc_elems.each do |var, value|
+ assert_equal(textinput.__send__("dc_#{var}"), value)
+ end
+ end
+
+ def test_setup_maker_items(for_backward_compatibility=false)
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+
+ item_size = 5
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+
+ @dc_elems.each do |var, value|
+ item.__send__("dc_#{var}=", value)
+ end
+
+ @content_elems.each do |var, value|
+ item.__send__("content_#{var}=", value)
+ end
+
+ item.trackback_ping = @trackback_elems[:ping]
+ @trackback_elems[:about].each do |value|
+ item.trackback_abouts.new_about do |new_about|
+ new_about.value = value
+ end
+ end
+ end
+ end
+ end
+
+ new_rss = RSS::Maker.make("1.0") do |maker|
+ rss.channel.setup_maker(maker)
+
+ rss.items.each do |item|
+ if for_backward_compatibility
+ item.setup_maker(maker)
+ else
+ item.setup_maker(maker.items)
+ end
+ end
+ end
+
+ assert_equal(item_size, new_rss.items.size)
+ new_rss.items.each_with_index do |item, i|
+ assert_equal("#{link}#{i}", item.about)
+ assert_equal("#{title}#{i}", item.title)
+ assert_equal("#{link}#{i}", item.link)
+ assert_equal("#{description}#{i}", item.description)
+
+ @dc_elems.each do |var, value|
+ assert_equal(item.__send__("dc_#{var}"), value)
+ end
+
+ @content_elems.each do |var, value|
+ assert_equal(item.__send__("content_#{var}"), value)
+ end
+
+ assert_equal(@trackback_elems[:ping], item.trackback_ping)
+ assert_equal(@trackback_elems[:about].size, item.trackback_abouts.size)
+ item.trackback_abouts.each_with_index do |about, j|
+ assert_equal(@trackback_elems[:about][j], about.value)
+ end
+ end
+ end
+
+ def test_setup_maker_items_sort
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+
+ item_size = 5
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ item = RSS::RDF::Item.new("#{link}#{i}")
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+ item.dc_date = Time.now + i * 60
+ item.setup_maker(maker.items)
+ end
+ maker.items.do_sort = false
+ end
+ assert_equal(item_size, rss.items.size)
+ rss.items.each_with_index do |item, i|
+ assert_equal("#{link}#{i}", item.about)
+ assert_equal("#{title}#{i}", item.title)
+ assert_equal("#{link}#{i}", item.link)
+ assert_equal("#{description}#{i}", item.description)
+ end
+
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ item = RSS::RDF::Item.new("#{link}#{i}")
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+ item.dc_date = Time.now + i * 60
+ item.setup_maker(maker.items)
+ end
+ maker.items.do_sort = true
+ end
+ assert_equal(item_size, rss.items.size)
+ rss.items.reverse.each_with_index do |item, i|
+ assert_equal("#{link}#{i}", item.about)
+ assert_equal("#{title}#{i}", item.title)
+ assert_equal("#{link}#{i}", item.link)
+ assert_equal("#{description}#{i}", item.description)
+ end
+ end
+
+ def test_setup_maker_items_backward_compatibility
+ test_setup_maker_items(true)
+ end
+
+ def test_setup_maker
+ encoding = "EUC-JP"
+ standalone = true
+
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+ end
+
+ new_rss = RSS::Maker.make("1.0") do |maker|
+ rss.setup_maker(maker)
+ end
+
+ assert_equal("1.0", new_rss.rss_version)
+ assert_equal(encoding, new_rss.encoding)
+ assert_equal(standalone, new_rss.standalone)
+
+ xss = new_rss.xml_stylesheets.first
+ assert_equal(1, new_rss.xml_stylesheets.size)
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+ end
+
+ def test_setup_maker_full
+ encoding = "EUC-JP"
+ standalone = true
+
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ channel_about = "http://hoge.com"
+ channel_title = "fugafuga"
+ channel_link = "http://hoge.com"
+ channel_description = "fugafugafugafuga"
+
+ image_title = "fugafuga"
+ image_url = "http://hoge.com/hoge.png"
+
+ textinput_title = "fugafuga"
+ textinput_description = "text hoge fuga"
+ textinput_name = "hoge"
+ textinput_link = "http://hoge.com"
+
+ item_title = "TITLE"
+ item_link = "http://hoge.com/"
+ item_description = "text hoge fuga"
+
+ item_size = 5
+
+ rss = RSS::Maker.make("1.0") do |maker|
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ maker.channel.about = channel_about
+ maker.channel.title = channel_title
+ maker.channel.link = channel_link
+ maker.channel.description = channel_description
+ @dc_elems.each do |var, value|
+ maker.channel.__send__("dc_#{var}=", value)
+ end
+ @sy_elems.each do |var, value|
+ maker.channel.__send__("sy_#{var}=", value)
+ end
+
+ maker.image.title = image_title
+ maker.image.url = image_url
+ @dc_elems.each do |var, value|
+ maker.image.__send__("dc_#{var}=", value)
+ end
+
+ maker.textinput.link = textinput_link
+ maker.textinput.title = textinput_title
+ maker.textinput.description = textinput_description
+ maker.textinput.name = textinput_name
+ @dc_elems.each do |var, value|
+ maker.textinput.__send__("dc_#{var}=", value)
+ end
+
+ item_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{item_title}#{i}"
+ item.link = "#{item_link}#{i}"
+ item.description = "#{item_description}#{i}"
+
+ @dc_elems.each do |var, value|
+ item.__send__("dc_#{var}=", value)
+ end
+
+ @content_elems.each do |var, value|
+ item.__send__("content_#{var}=", value)
+ end
+
+ item.trackback_ping = @trackback_elems[:ping]
+ @trackback_elems[:about].each do |value|
+ item.trackback_abouts.new_about do |new_about|
+ new_about.value = value
+ end
+ end
+ end
+ end
+
+ setup_taxo_topic(maker, @taxo_topic_elems)
+ end
+
+ new_rss = RSS::Maker.make("1.0") do |maker|
+ rss.setup_maker(maker)
+ end
+
+ assert_equal("1.0", new_rss.rss_version)
+ assert_equal(encoding, new_rss.encoding)
+ assert_equal(standalone, new_rss.standalone)
+
+ xss = new_rss.xml_stylesheets.first
+ assert_equal(1, new_rss.xml_stylesheets.size)
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+
+ channel = new_rss.channel
+ assert_equal(channel_about, channel.about)
+ assert_equal(channel_title, channel.title)
+ assert_equal(channel_link, channel.link)
+ assert_equal(channel_description, channel.description)
+ item_resources = []
+ item_size.times do |i|
+ item_resources << "#{item_link}#{i}"
+ end
+ assert_equal(item_resources, channel.items.resources)
+ assert_equal(image_url, channel.image.resource)
+ assert_equal(textinput_link, channel.textinput.resource)
+ @dc_elems.each do |var, value|
+ assert_equal(value, channel.__send__("dc_#{var}"))
+ end
+ @sy_elems.each do |var, value|
+ value = value.to_i if var == :updateFrequency
+ assert_equal(value, channel.__send__("sy_#{var}"))
+ end
+
+ image = new_rss.image
+ assert_equal(image_url, image.about)
+ assert_equal(image_url, new_rss.channel.image.resource)
+ assert_equal(image_title, image.title)
+ assert_equal(channel_link, image.link)
+ assert_equal(image_url, image.url)
+ @dc_elems.each do |var, value|
+ assert_equal(image.__send__("dc_#{var}"), value)
+ end
+
+ textinput = new_rss.textinput
+ assert_equal(textinput_link, textinput.about)
+ assert_equal(textinput_link, new_rss.channel.textinput.resource)
+ assert_equal(textinput_title, textinput.title)
+ assert_equal(textinput_name, textinput.name)
+ assert_equal(textinput_description, textinput.description)
+ assert_equal(textinput_link, textinput.link)
+ @dc_elems.each do |var, value|
+ assert_equal(textinput.__send__("dc_#{var}"), value)
+ end
+
+ assert_equal(item_size, new_rss.items.size)
+ new_rss.items.each_with_index do |item, i|
+ assert_equal("#{item_link}#{i}", item.about)
+ assert_equal("#{item_title}#{i}", item.title)
+ assert_equal("#{item_link}#{i}", item.link)
+ assert_equal("#{item_description}#{i}", item.description)
+
+ @dc_elems.each do |var, value|
+ assert_equal(item.__send__("dc_#{var}"), value)
+ end
+
+ @content_elems.each do |var, value|
+ assert_equal(item.__send__("content_#{var}"), value)
+ end
+
+ assert_equal(@trackback_elems[:ping], item.trackback_ping)
+ assert_equal(@trackback_elems[:about].size, item.trackback_abouts.size)
+ item.trackback_abouts.each_with_index do |about, j|
+ assert_equal(@trackback_elems[:about][j], about.value)
+ end
+ end
+
+ assert_taxo_topic(@taxo_topic_elems, new_rss)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/rss/test_setup_maker_2.0.rb b/jni/ruby/test/rss/test_setup_maker_2.0.rb
new file mode 100644
index 0000000..fcf1f57
--- /dev/null
+++ b/jni/ruby/test/rss/test_setup_maker_2.0.rb
@@ -0,0 +1,308 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestSetupMaker20 < TestCase
+
+ def test_setup_maker_channel
+ title = "fugafuga"
+ link = "http://hoge.com"
+ description = "fugafugafugafuga"
+ language = "ja"
+ copyright = "foo"
+ managingEditor = "bar"
+ webMaster = "web master"
+ rating = '(PICS-1.1 "http://www.rsac.org/ratingsv01.html" l gen true comment "RSACi North America Server" for "http://www.rsac.org" on "1996.04.16T08:15-0500" r (n 0 s 0 v 0 l 0))'
+ docs = "http://foo.com/doc"
+ skipDays = [
+ "Sunday",
+ "Monday",
+ ]
+ skipHours = [
+ "0",
+ "13",
+ ]
+ pubDate = Time.now
+ lastBuildDate = Time.now
+ categories = [
+ "Nespapers",
+ "misc",
+ ]
+ generator = "RSS Maker"
+ ttl = "60"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ maker.channel.title = title
+ maker.channel.link = link
+ maker.channel.description = description
+ maker.channel.language = language
+ maker.channel.copyright = copyright
+ maker.channel.managingEditor = managingEditor
+ maker.channel.webMaster = webMaster
+ maker.channel.rating = rating
+ maker.channel.docs = docs
+ maker.channel.pubDate = pubDate
+ maker.channel.lastBuildDate = lastBuildDate
+
+ skipDays.each do |day|
+ maker.channel.skipDays.new_day do |new_day|
+ new_day.content = day
+ end
+ end
+ skipHours.each do |hour|
+ maker.channel.skipHours.new_hour do |new_hour|
+ new_hour.content = hour
+ end
+ end
+
+
+ categories.each do |category|
+ maker.channel.categories.new_category do |new_category|
+ new_category.content = category
+ end
+ end
+
+ maker.channel.generator = generator
+ maker.channel.ttl = ttl
+ end
+
+ new_rss = RSS::Maker.make("2.0") do |maker|
+ rss.channel.setup_maker(maker)
+ end
+ channel = new_rss.channel
+
+ assert_equal(title, channel.title)
+ assert_equal(link, channel.link)
+ assert_equal(description, channel.description)
+ assert_equal(language, channel.language)
+ assert_equal(copyright, channel.copyright)
+ assert_equal(managingEditor, channel.managingEditor)
+ assert_equal(webMaster, channel.webMaster)
+ assert_equal(rating, channel.rating)
+ assert_equal(docs, channel.docs)
+ assert_equal(pubDate, channel.pubDate)
+ assert_equal(lastBuildDate, channel.lastBuildDate)
+
+ skipDays.each_with_index do |day, i|
+ assert_equal(day, channel.skipDays.days[i].content)
+ end
+ skipHours.each_with_index do |hour, i|
+ assert_equal(hour.to_i, channel.skipHours.hours[i].content)
+ end
+
+
+ channel.categories.each_with_index do |category, i|
+ assert_equal(categories[i], category.content)
+ end
+
+ assert_equal(generator, channel.generator)
+ assert_equal(ttl.to_i, channel.ttl)
+
+
+ assert(channel.items.empty?)
+ assert_nil(channel.image)
+ assert_nil(channel.textInput)
+ end
+
+ def test_setup_maker_image
+ title = "fugafuga"
+ link = "http://hoge.com"
+ url = "http://hoge.com/hoge.png"
+ width = "144"
+ height = "400"
+ description = "an image"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+ maker.channel.link = link
+
+ maker.image.title = title
+ maker.image.url = url
+ maker.image.width = width
+ maker.image.height = height
+ maker.image.description = description
+ end
+
+ new_rss = RSS::Maker.make("2.0") do |maker|
+ rss.channel.setup_maker(maker)
+ rss.image.setup_maker(maker)
+ end
+
+ image = new_rss.image
+ assert_equal(title, image.title)
+ assert_equal(link, image.link)
+ assert_equal(url, image.url)
+ assert_equal(width.to_i, image.width)
+ assert_equal(height.to_i, image.height)
+ assert_equal(description, image.description)
+ end
+
+ def test_setup_maker_textinput
+ title = "fugafuga"
+ description = "text hoge fuga"
+ name = "hoge"
+ link = "http://hoge.com"
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ maker.textinput.title = title
+ maker.textinput.description = description
+ maker.textinput.name = name
+ maker.textinput.link = link
+ end
+
+ new_rss = RSS::Maker.make("2.0") do |maker|
+ rss.channel.setup_maker(maker)
+ rss.textinput.setup_maker(maker)
+ end
+
+ textInput = new_rss.channel.textInput
+ assert_equal(title, textInput.title)
+ assert_equal(description, textInput.description)
+ assert_equal(name, textInput.name)
+ assert_equal(link, textInput.link)
+ end
+
+ def test_setup_maker_items(for_backward_compatibility=false)
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+ author = "oprah@oxygen.net"
+ comments = "http://www.myblog.org/cgi-local/mt/mt-comments.cgi?entry_id=290"
+ pubDate = Time.now
+
+ guid_isPermaLink = "true"
+ guid_content = "http://inessential.com/2002/09/01.php#a2"
+
+ enclosure_url = "http://www.scripting.com/mp3s/weatherReportSuite.mp3"
+ enclosure_length = "12216320"
+ enclosure_type = "audio/mpeg"
+
+ source_url = "http://static.userland.com/tomalak/links2.xml"
+ source_content = "Tomalak's Realm"
+
+ category_domain = "http://www.fool.com/cusips"
+ category_content = "MSFT"
+
+ item_size = 5
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_dummy_channel(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+ item.author = "#{author}#{i}"
+ item.comments = "#{comments}#{i}"
+ item.date = pubDate
+
+ item.guid.isPermaLink = guid_isPermaLink
+ item.guid.content = guid_content
+
+ item.enclosure.url = enclosure_url
+ item.enclosure.length = enclosure_length
+ item.enclosure.type = enclosure_type
+
+ item.source.url = source_url
+ item.source.content = source_content
+
+ category = item.categories.new_category
+ category.domain = category_domain
+ category.content = category_content
+ end
+ end
+ end
+
+ new_rss = RSS::Maker.make("2.0") do |maker|
+ rss.channel.setup_maker(maker)
+
+ rss.items.each do |item|
+ if for_backward_compatibility
+ item.setup_maker(maker)
+ else
+ item.setup_maker(maker.items)
+ end
+ end
+ end
+
+ assert_equal(item_size, new_rss.items.size)
+ new_rss.items.each_with_index do |item, i|
+ assert_equal("#{title}#{i}", item.title)
+ assert_equal("#{link}#{i}", item.link)
+ assert_equal("#{description}#{i}", item.description)
+ assert_equal("#{author}#{i}", item.author)
+ assert_equal("#{comments}#{i}", item.comments)
+ assert_equal(pubDate, item.pubDate)
+
+ assert_equal(guid_isPermaLink == "true", item.guid.isPermaLink)
+ assert_equal(guid_content, item.guid.content)
+
+ assert_equal(enclosure_url, item.enclosure.url)
+ assert_equal(enclosure_length.to_i, item.enclosure.length)
+ assert_equal(enclosure_type, item.enclosure.type)
+
+ assert_equal(source_url, item.source.url)
+ assert_equal(source_content, item.source.content)
+
+ assert_equal(1, item.categories.size)
+ assert_equal(category_domain, item.category.domain)
+ assert_equal(category_content, item.category.content)
+ end
+
+ end
+
+ def test_setup_maker_items_backward_compatibility
+ test_setup_maker_items(true)
+ end
+
+ def test_setup_maker
+ encoding = "EUC-JP"
+ standalone = true
+
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ rss = RSS::Maker.make("2.0") do |maker|
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ setup_dummy_channel(maker)
+ end
+
+ new_rss = RSS::Maker.make("2.0") do |maker|
+ rss.setup_maker(maker)
+ end
+
+ assert_equal("2.0", new_rss.rss_version)
+ assert_equal(encoding, new_rss.encoding)
+ assert_equal(standalone, new_rss.standalone)
+
+ xss = rss.xml_stylesheets.first
+ assert_equal(1, rss.xml_stylesheets.size)
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/rss/test_setup_maker_atom_entry.rb b/jni/ruby/test/rss/test_setup_maker_atom_entry.rb
new file mode 100644
index 0000000..ec9df5e
--- /dev/null
+++ b/jni/ruby/test/rss/test_setup_maker_atom_entry.rb
@@ -0,0 +1,409 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestSetupMakerAtomEntry < TestCase
+ def setup
+ t = Time.iso8601("2000-01-01T12:00:05+00:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+
+ @dc_elems = {
+ :title => "hoge",
+ :description =>
+ " XML is placing increasingly heavy loads on
+ the existing technical infrastructure of the Internet.",
+ :creator => "Rael Dornfest (mailto:rael@oreilly.com)",
+ :subject => "XML",
+ :publisher => "The O'Reilly Network",
+ :contributor => "hogehoge",
+ :type => "fugafuga",
+ :format => "hohoho",
+ :identifier => "fufufu",
+ :source => "barbar",
+ :language => "ja",
+ :relation => "cococo",
+ :rights => "Copyright (c) 2000 O'Reilly &amp; Associates, Inc.",
+ :date => t,
+ }
+ end
+
+ def test_setup_maker_entry(with_dc=true)
+ authors = [
+ {
+ :name => "Bob",
+ :uri => "http://example.com/~bob/",
+ :email => "bob@example.com",
+ },
+ {
+ :name => "Alice",
+ :uri => "http://example.com/~alice/",
+ :email => "alice@example.com",
+ },
+ ]
+ categories = [
+ {
+ :term => "music",
+ :label => "Music",
+ },
+ {
+ :term => "book",
+ :scheme => "http://example.com/category/book/",
+ :label => "Book",
+ },
+ ]
+ contributors = [
+ {
+ :name => "Chris",
+ :email => "chris@example.com",
+ },
+ {
+ :name => "Eva",
+ :uri => "http://example.com/~eva/",
+ },
+ ]
+ id = "urn:uuid:8b105336-7e20-45fc-bb78-37fb3e1db25a"
+ link = "http://hoge.com"
+ published = Time.now - 60 * 3600
+ rights = "Copyrights (c) 2007 Alice and Bob"
+ description = "fugafugafugafuga"
+ title = "fugafuga"
+ updated = Time.now
+
+ feed = RSS::Maker.make("atom:entry") do |maker|
+ maker.items.new_item do |item|
+ authors.each do |author_info|
+ item.authors.new_author do |author|
+ author_info.each do |key, value|
+ author.__send__("#{key}=", value)
+ end
+ end
+ end
+
+ categories.each do |category_info|
+ item.categories.new_category do |category|
+ category_info.each do |key, value|
+ category.__send__("#{key}=", value)
+ end
+ end
+ end
+
+ contributors.each do |contributor_info|
+ item.contributors.new_contributor do |contributor|
+ contributor_info.each do |key, value|
+ contributor.__send__("#{key}=", value)
+ end
+ end
+ end
+
+ item.id = id
+ item.link = link
+ item.published = published
+ item.rights = rights
+ item.description = description
+ item.title = title
+ item.updated = updated
+
+ if with_dc
+ @dc_elems.each do |var, value|
+ if var == :date
+ item.new_dc_date(value)
+ else
+ item.__send__("dc_#{var}=", value)
+ end
+ end
+ end
+ end
+ end
+ assert_not_nil(feed)
+
+ new_feed = RSS::Maker.make("atom:entry") do |maker|
+ feed.setup_maker(maker)
+ end
+ assert_not_nil(new_feed)
+
+ new_authors = new_feed.authors.collect do |author|
+ {
+ :name => author.name.content,
+ :uri => author.uri.content,
+ :email => author.email.content,
+ }
+ end
+ assert_equal(authors, new_authors)
+
+ new_categories = new_feed.categories.collect do |category|
+ {
+ :term => category.term,
+ :scheme => category.scheme,
+ :label => category.label,
+ }.reject {|key, value| value.nil?}
+ end
+ assert_equal(categories, new_categories)
+
+ new_contributors = new_feed.contributors.collect do |contributor|
+ info = {}
+ info[:name] = contributor.name.content
+ info[:uri] = contributor.uri.content if contributor.uri
+ info[:email] = contributor.email.content if contributor.email
+ info
+ end
+ assert_equal(contributors, new_contributors)
+
+ assert_equal(id, new_feed.id.content)
+ assert_equal(link, new_feed.link.href)
+ assert_equal(published, new_feed.published.content)
+ assert_equal(rights, new_feed.rights.content)
+ assert_equal(description, new_feed.summary.content)
+ assert_equal(title, new_feed.title.content)
+ assert_equal(updated, new_feed.updated.content)
+
+ if with_dc
+ @dc_elems.each do |var, value|
+ if var == :date
+ assert_equal([updated, value],
+ new_feed.dc_dates.collect {|date| date.value})
+ else
+ assert_equal(value, new_feed.__send__("dc_#{var}"))
+ end
+ end
+ end
+
+ assert_equal(1, new_feed.items.size)
+ end
+
+ def test_setup_maker_entry_without_dc
+ test_setup_maker_entry(false)
+ end
+
+ def test_setup_maker_items(for_backward_compatibility=false)
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+ updated = Time.now
+
+ item_size = 5
+ feed = RSS::Maker.make("atom:entry") do |maker|
+ setup_dummy_channel_atom(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+ item.updated = updated + i * 60
+ end
+ end
+ end
+
+ new_feed = RSS::Maker.make("atom:entry") do |maker|
+ feed.items.each do |item|
+ if for_backward_compatibility
+ item.setup_maker(maker)
+ else
+ item.setup_maker(maker.items)
+ end
+ end
+
+ feed.items.clear
+ feed.setup_maker(maker)
+ end
+
+ assert_equal(1, new_feed.items.size)
+ new_feed.items[0..1].each_with_index do |item, i|
+ assert_equal("#{title}#{i}", item.title.content)
+ assert_equal("#{link}#{i}", item.link.href)
+ assert_equal("#{description}#{i}", item.summary.content)
+ assert_equal(updated + i * 60, item.updated.content)
+ end
+ end
+
+ def test_setup_maker_items_sort
+ title = "TITLE"
+ link = "http://hoge.com/"
+ summary = "text hoge fuga"
+ updated = Time.now
+
+ feed_size = 5
+ feed = RSS::Maker.make("atom:entry") do |maker|
+ setup_dummy_channel_atom(maker)
+
+ feed_size.times do |i|
+ entry_class = RSS::Atom::Entry
+ entry = entry_class.new
+ entry.title = entry_class::Title.new(:content => "#{title}#{i}")
+ entry.links << entry_class::Link.new(:href => "#{link}#{i}")
+ entry.summary = entry_class::Summary.new(:content => "#{summary}#{i}")
+ entry.updated = entry_class::Updated.new(:content => updated + i * 60)
+ entry.setup_maker(maker.items)
+ end
+ maker.items.do_sort = false
+ end
+ assert_equal(1, feed.items.size)
+
+ assert_equal("#{title}0", feed.title.content)
+ assert_equal("#{link}0", feed.link.href)
+ assert_equal("#{summary}0", feed.summary.content)
+
+
+ feed = RSS::Maker.make("atom:entry") do |maker|
+ setup_dummy_channel_atom(maker)
+
+ feed_size.times do |i|
+ entry_class = RSS::Atom::Entry
+ entry = entry_class.new
+ entry.title = entry_class::Title.new(:content => "#{title}#{i}")
+ entry.links << entry_class::Link.new(:href => "#{link}#{i}")
+ entry.summary = entry_class::Summary.new(:content => "#{summary}#{i}")
+ entry.updated = entry_class::Updated.new(:content => updated + i * 60)
+ entry.setup_maker(maker.items)
+ end
+ maker.items.do_sort = true
+ end
+ assert_equal(1, feed.items.size)
+
+ assert_equal("#{title}#{feed_size - 1}", feed.title.content)
+ assert_equal("#{link}#{feed_size - 1}", feed.link.href)
+ assert_equal("#{summary}#{feed_size - 1}", feed.summary.content)
+ end
+
+ def test_setup_maker_items_backward_compatibility
+ test_setup_maker_items(true)
+ end
+
+ def test_setup_maker
+ encoding = "EUC-JP"
+ standalone = true
+
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ feed = RSS::Maker.make("atom:entry") do |maker|
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ assert_not_nil(feed)
+
+ new_feed = RSS::Maker.make("atom:entry") do |maker|
+ feed.setup_maker(maker)
+ end
+
+ assert_equal(["atom", "1.0", "entry"], new_feed.feed_info)
+ assert_equal(encoding, new_feed.encoding)
+ assert_equal(standalone, new_feed.standalone)
+
+ xss = new_feed.xml_stylesheets.first
+ assert_equal(1, new_feed.xml_stylesheets.size)
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+ end
+
+ def test_setup_maker_full
+ encoding = "EUC-JP"
+ standalone = true
+
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ channel_about = "http://hoge.com"
+ channel_title = "fugafuga"
+ channel_link = "http://hoge.com"
+ channel_description = "fugafugafugafuga"
+ channel_author = "Bob"
+
+ image_url = "http://hoge.com/hoge.png"
+
+ item_title = "TITLE"
+ item_link = "http://hoge.com/"
+ item_description = "text hoge fuga"
+
+ entry_size = 5
+ feed = RSS::Maker.make("atom:entry") do |maker|
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ maker.channel.about = channel_about
+ maker.channel.title = channel_title
+ maker.channel.link = channel_link
+ maker.channel.description = channel_description
+ maker.channel.author = channel_author
+ @dc_elems.each do |var, value|
+ maker.channel.__send__("dc_#{var}=", value)
+ end
+
+ maker.image.url = image_url
+
+ entry_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{item_title}#{i}"
+ item.link = "#{item_link}#{i}"
+ item.description = "#{item_description}#{i}"
+
+ @dc_elems.each do |var, value|
+ item.__send__("dc_#{var}=", value)
+ end
+ end
+ end
+ end
+
+ new_feed = RSS::Maker.make("atom:entry") do |maker|
+ feed.setup_maker(maker)
+ end
+
+ assert_equal(["atom", "1.0", "entry"], new_feed.feed_info)
+ assert_equal(encoding, new_feed.encoding)
+ assert_equal(standalone, new_feed.standalone)
+
+ xss = new_feed.xml_stylesheets.first
+ assert_equal(1, new_feed.xml_stylesheets.size)
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+
+ assert_equal("#{item_title}0", new_feed.title.content)
+ assert_equal("#{item_link}0", new_feed.link.href)
+ assert_equal("#{item_description}0", new_feed.summary.content)
+ @dc_elems.each do |var, value|
+ assert_equal(value, new_feed.__send__("dc_#{var}"))
+ end
+ assert_equal(1, new_feed.items.size)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_setup_maker_atom_feed.rb b/jni/ruby/test/rss/test_setup_maker_atom_feed.rb
new file mode 100644
index 0000000..ad2c693
--- /dev/null
+++ b/jni/ruby/test/rss/test_setup_maker_atom_feed.rb
@@ -0,0 +1,445 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestSetupMakerAtomFeed < TestCase
+ def setup
+ t = Time.iso8601("2000-01-01T12:00:05+00:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+
+ @dc_elems = {
+ :title => "hoge",
+ :description =>
+ " XML is placing increasingly heavy loads on
+ the existing technical infrastructure of the Internet.",
+ :creator => "Rael Dornfest (mailto:rael@oreilly.com)",
+ :subject => "XML",
+ :publisher => "The O'Reilly Network",
+ :contributor => "hogehoge",
+ :type => "fugafuga",
+ :format => "hohoho",
+ :identifier => "fufufu",
+ :source => "barbar",
+ :language => "ja",
+ :relation => "cococo",
+ :rights => "Copyright (c) 2000 O'Reilly &amp; Associates, Inc.",
+ :date => t,
+ }
+ end
+
+ def test_setup_maker_feed(with_dc=true)
+ authors = [
+ {
+ :name => "Bob",
+ :uri => "http://example.com/~bob/",
+ :email => "bob@example.com",
+ },
+ {
+ :name => "Alice",
+ :uri => "http://example.com/~alice/",
+ :email => "alice@example.com",
+ },
+ ]
+ categories = [
+ {
+ :term => "music",
+ :label => "Music",
+ },
+ {
+ :term => "book",
+ :scheme => "http://example.com/category/book/",
+ :label => "Book",
+ },
+ ]
+ contributors = [
+ {
+ :name => "Chris",
+ :email => "chris@example.com",
+ },
+ {
+ :name => "Eva",
+ :uri => "http://example.com/~eva/",
+ },
+ ]
+ generator = {
+ :uri => "http://example.com/generator/",
+ :version => "0.0.1",
+ :content => "Feed Generator",
+ }
+ icon = "http://example.com/icon.png"
+ about = "http://hoge.com"
+ title = "fugafuga"
+ link = "http://hoge.com"
+ logo = "http://example.com/logo.png"
+ rights = "Copyrights (c) 2007 Alice and Bob"
+ description = "fugafugafugafuga"
+ updated = Time.now
+
+ feed = RSS::Maker.make("atom") do |maker|
+ authors.each do |author_info|
+ maker.channel.authors.new_author do |author|
+ author_info.each do |key, value|
+ author.__send__("#{key}=", value)
+ end
+ end
+ end
+
+ categories.each do |category_info|
+ maker.channel.categories.new_category do |category|
+ category_info.each do |key, value|
+ category.__send__("#{key}=", value)
+ end
+ end
+ end
+
+ contributors.each do |contributor_info|
+ maker.channel.contributors.new_contributor do |contributor|
+ contributor_info.each do |key, value|
+ contributor.__send__("#{key}=", value)
+ end
+ end
+ end
+
+ generator.each do |key, value|
+ maker.channel.generator do |g|
+ g.__send__("#{key}=", value)
+ end
+ end
+
+ maker.channel.icon = icon
+
+ maker.channel.about = about
+ maker.channel.link = link
+ maker.channel.logo = logo
+ maker.channel.rights = rights
+ maker.channel.title = title
+ maker.channel.description = description
+ maker.channel.updated = updated
+
+ if with_dc
+ @dc_elems.each do |var, value|
+ if var == :date
+ maker.channel.new_dc_date(value)
+ else
+ maker.channel.__send__("dc_#{var}=", value)
+ end
+ end
+ end
+
+ setup_dummy_item_atom(maker)
+ end
+ assert_not_nil(feed)
+
+ new_feed = RSS::Maker.make("atom") do |maker|
+ feed.setup_maker(maker)
+ end
+ assert_not_nil(new_feed)
+
+ new_authors = new_feed.authors.collect do |author|
+ {
+ :name => author.name.content,
+ :uri => author.uri.content,
+ :email => author.email.content,
+ }
+ end
+ assert_equal(authors, new_authors)
+
+ new_categories = new_feed.categories.collect do |category|
+ {
+ :term => category.term,
+ :scheme => category.scheme,
+ :label => category.label,
+ }.reject {|key, value| value.nil?}
+ end
+ assert_equal(categories, new_categories)
+
+ new_contributors = new_feed.contributors.collect do |contributor|
+ info = {}
+ info[:name] = contributor.name.content
+ info[:uri] = contributor.uri.content if contributor.uri
+ info[:email] = contributor.email.content if contributor.email
+ info
+ end
+ assert_equal(contributors, new_contributors)
+
+ new_generator = {
+ :uri => new_feed.generator.uri,
+ :version => new_feed.generator.version,
+ :content => new_feed.generator.content,
+ }
+ assert_equal(generator, new_generator)
+
+ assert_equal(icon, new_feed.icon.content)
+ assert_equal(about, new_feed.id.content)
+ assert_equal(link, new_feed.link.href)
+ assert_equal(logo, new_feed.logo.content)
+ assert_equal(rights, new_feed.rights.content)
+ assert_equal(description, new_feed.subtitle.content)
+ assert_equal(title, new_feed.title.content)
+ assert_equal(updated, new_feed.updated.content)
+
+ if with_dc
+ @dc_elems.each do |var, value|
+ if var == :date
+ assert_equal([updated, value],
+ new_feed.dc_dates.collect {|date| date.value})
+ else
+ assert_equal(value, new_feed.__send__("dc_#{var}"))
+ end
+ end
+ end
+
+ assert_equal(1, new_feed.items.size)
+ end
+
+ def test_setup_maker_feed_without_dc
+ test_setup_maker_feed(false)
+ end
+
+ def test_setup_maker_items(for_backward_compatibility=false)
+ title = "TITLE"
+ link = "http://hoge.com/"
+ description = "text hoge fuga"
+ updated = Time.now
+
+ item_size = 5
+ feed = RSS::Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+
+ item_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{title}#{i}"
+ item.link = "#{link}#{i}"
+ item.description = "#{description}#{i}"
+ item.updated = updated + i * 60
+ end
+ end
+ end
+
+ new_feed = RSS::Maker.make("atom") do |maker|
+ feed.items.each do |item|
+ if for_backward_compatibility
+ item.setup_maker(maker)
+ else
+ item.setup_maker(maker.items)
+ end
+ end
+
+ feed.items.clear
+ feed.setup_maker(maker)
+ end
+
+ assert_equal(item_size, new_feed.items.size)
+ new_feed.items.each_with_index do |item, i|
+ assert_equal("#{title}#{i}", item.title.content)
+ assert_equal("#{link}#{i}", item.link.href)
+ assert_equal("#{description}#{i}", item.summary.content)
+ assert_equal(updated + i * 60, item.updated.content)
+ end
+ end
+
+ def test_setup_maker_items_sort
+ title = "TITLE"
+ link = "http://hoge.com/"
+ summary = "text hoge fuga"
+ updated = Time.now
+
+ feed_size = 5
+ feed = RSS::Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+
+ feed_size.times do |i|
+ entry_class = RSS::Atom::Feed::Entry
+ entry = entry_class.new
+ entry.title = entry_class::Title.new(:content => "#{title}#{i}")
+ entry.links << entry_class::Link.new(:href => "#{link}#{i}")
+ entry.summary = entry_class::Summary.new(:content => "#{summary}#{i}")
+ entry.updated = entry_class::Updated.new(:content => updated + i * 60)
+ entry.setup_maker(maker.items)
+ end
+ maker.items.do_sort = false
+ end
+ assert_equal(feed_size, feed.entries.size)
+ feed.entries.each_with_index do |entry, i|
+ assert_equal("#{title}#{i}", entry.title.content)
+ assert_equal("#{link}#{i}", entry.link.href)
+ assert_equal("#{summary}#{i}", entry.summary.content)
+ end
+
+
+ feed = RSS::Maker.make("atom") do |maker|
+ setup_dummy_channel_atom(maker)
+
+ feed_size.times do |i|
+ entry_class = RSS::Atom::Feed::Entry
+ entry = entry_class.new
+ entry.title = entry_class::Title.new(:content => "#{title}#{i}")
+ entry.links << entry_class::Link.new(:href => "#{link}#{i}")
+ entry.summary = entry_class::Summary.new(:content => "#{summary}#{i}")
+ entry.updated = entry_class::Updated.new(:content => updated + i * 60)
+ entry.setup_maker(maker.items)
+ end
+ maker.items.do_sort = true
+ end
+ assert_equal(feed_size, feed.entries.size)
+ feed.entries.reverse.each_with_index do |entry, i|
+ assert_equal("#{title}#{i}", entry.title.content)
+ assert_equal("#{link}#{i}", entry.link.href)
+ assert_equal("#{summary}#{i}", entry.summary.content)
+ end
+ end
+
+ def test_setup_maker_items_backward_compatibility
+ test_setup_maker_items(true)
+ end
+
+ def test_setup_maker
+ encoding = "EUC-JP"
+ standalone = true
+
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ feed = RSS::Maker.make("atom") do |maker|
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ setup_dummy_channel_atom(maker)
+ setup_dummy_item_atom(maker)
+ end
+ assert_not_nil(feed)
+
+ new_feed = RSS::Maker.make("atom") do |maker|
+ feed.setup_maker(maker)
+ end
+
+ assert_equal(["atom", "1.0", "feed"], new_feed.feed_info)
+ assert_equal(encoding, new_feed.encoding)
+ assert_equal(standalone, new_feed.standalone)
+
+ xss = new_feed.xml_stylesheets.first
+ assert_equal(1, new_feed.xml_stylesheets.size)
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+ end
+
+ def test_setup_maker_full
+ encoding = "EUC-JP"
+ standalone = true
+
+ href = 'a.xsl'
+ type = 'text/xsl'
+ title = 'sample'
+ media = 'printer'
+ charset = 'UTF-8'
+ alternate = 'yes'
+
+ channel_about = "http://hoge.com"
+ channel_title = "fugafuga"
+ channel_link = "http://hoge.com"
+ channel_description = "fugafugafugafuga"
+ channel_author = "Bob"
+
+ image_url = "http://hoge.com/hoge.png"
+
+ item_title = "TITLE"
+ item_link = "http://hoge.com/"
+ item_description = "text hoge fuga"
+
+ entry_size = 5
+ feed = RSS::Maker.make("atom") do |maker|
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ maker.xml_stylesheets.new_xml_stylesheet do |xss|
+ xss.href = href
+ xss.type = type
+ xss.title = title
+ xss.media = media
+ xss.charset = charset
+ xss.alternate = alternate
+ end
+
+ maker.channel.about = channel_about
+ maker.channel.title = channel_title
+ maker.channel.link = channel_link
+ maker.channel.description = channel_description
+ maker.channel.author = channel_author
+ @dc_elems.each do |var, value|
+ maker.channel.__send__("dc_#{var}=", value)
+ end
+
+ maker.image.url = image_url
+
+ entry_size.times do |i|
+ maker.items.new_item do |item|
+ item.title = "#{item_title}#{i}"
+ item.link = "#{item_link}#{i}"
+ item.description = "#{item_description}#{i}"
+
+ @dc_elems.each do |var, value|
+ item.__send__("dc_#{var}=", value)
+ end
+ end
+ end
+ end
+
+ new_feed = RSS::Maker.make("atom") do |maker|
+ feed.setup_maker(maker)
+ end
+
+ assert_equal(["atom", "1.0", "feed"], new_feed.feed_info)
+ assert_equal(encoding, new_feed.encoding)
+ assert_equal(standalone, new_feed.standalone)
+
+ xss = new_feed.xml_stylesheets.first
+ assert_equal(1, new_feed.xml_stylesheets.size)
+ assert_equal(href, xss.href)
+ assert_equal(type, xss.type)
+ assert_equal(title, xss.title)
+ assert_equal(media, xss.media)
+ assert_equal(charset, xss.charset)
+ assert_equal(alternate, xss.alternate)
+
+ assert_equal(channel_title, new_feed.title.content)
+ assert_equal(channel_link, new_feed.link.href)
+ assert_equal(channel_description, new_feed.subtitle.content)
+ assert_equal(channel_author, new_feed.author.name.content)
+ assert_equal(image_url, new_feed.logo.content)
+ @dc_elems.each do |var, value|
+ assert_equal(value, new_feed.__send__("dc_#{var}"))
+ end
+
+ assert_equal(entry_size, new_feed.entries.size)
+ new_feed.entries.each_with_index do |entry, i|
+ assert_equal("#{item_title}#{i}", entry.title.content)
+ assert_equal("#{item_link}#{i}", entry.link.href)
+ assert_equal("#{item_description}#{i}", entry.summary.content)
+
+ @dc_elems.each do |var, value|
+ assert_equal(value, entry.__send__("dc_#{var}"))
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_setup_maker_itunes.rb b/jni/ruby/test/rss/test_setup_maker_itunes.rb
new file mode 100644
index 0000000..246fecf
--- /dev/null
+++ b/jni/ruby/test/rss/test_setup_maker_itunes.rb
@@ -0,0 +1,143 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestSetupMakerITunes < TestCase
+ def test_setup_maker_simple
+ author = "John Doe"
+ block = true
+ categories = ["Audio Blogs"]
+ image = "http://example.com/podcasts/everything/AllAboutEverything.jpg"
+ duration = "4:05"
+ duration_components = [0, 4, 5]
+ explicit = true
+ keywords = ["salt", "pepper", "shaker", "exciting"]
+ owner = {:name => "John Doe", :email => "john.doe@example.com"}
+ subtitle = "A show about everything"
+ summary = "All About Everything is a show about " +
+ "everything. Each week we dive into any " +
+ "subject known to man and talk about it " +
+ "as much as we can. Look for our Podcast " +
+ "in the iTunes Music Store"
+
+ feed = RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ channel = maker.channel
+ channel.itunes_author = author
+ channel.itunes_block = block
+ categories.each do |category|
+ channel.itunes_categories.new_category.text = category
+ end
+ channel.itunes_image = image
+ channel.itunes_explicit = explicit
+ channel.itunes_keywords = keywords
+ channel.itunes_owner.itunes_name = owner[:name]
+ channel.itunes_owner.itunes_email = owner[:email]
+ channel.itunes_subtitle = subtitle
+ channel.itunes_summary = summary
+
+ item = maker.items.last
+ item.itunes_author = author
+ item.itunes_block = block
+ item.itunes_duration = duration
+ item.itunes_explicit = explicit
+ item.itunes_keywords = keywords
+ item.itunes_subtitle = subtitle
+ item.itunes_summary = summary
+ end
+ assert_not_nil(feed)
+
+ new_feed = RSS::Maker.make("rss2.0") do |maker|
+ feed.setup_maker(maker)
+ end
+ assert_not_nil(new_feed)
+
+ channel = new_feed.channel
+ item = new_feed.items.last
+
+ assert_equal(author, channel.itunes_author)
+ assert_equal(author, item.itunes_author)
+
+ assert_equal(block, channel.itunes_block?)
+ assert_equal(block, item.itunes_block?)
+
+ assert_equal(categories,
+ collect_itunes_categories(channel.itunes_categories))
+
+ assert_equal(image, channel.itunes_image.href)
+
+ assert_equal(duration_components,
+ [item.itunes_duration.hour,
+ item.itunes_duration.minute,
+ item.itunes_duration.second])
+
+ assert_equal(explicit, channel.itunes_explicit?)
+ assert_equal(explicit, item.itunes_explicit?)
+
+ assert_equal(keywords, channel.itunes_keywords)
+ assert_equal(keywords, item.itunes_keywords)
+
+ assert_equal(owner,
+ {
+ :name => channel.itunes_owner.itunes_name,
+ :email => channel.itunes_owner.itunes_email
+ })
+
+ assert_equal(subtitle, channel.itunes_subtitle)
+ assert_equal(subtitle, item.itunes_subtitle)
+
+ assert_equal(summary, channel.itunes_summary)
+ assert_equal(summary, item.itunes_summary)
+ end
+
+ def test_setup_maker_with_nested_categories
+ categories = [["Arts & Entertainment", "Games"],
+ ["Technology", "Computers"],
+ "Audio Blogs"]
+
+ feed = RSS::Maker.make("rss2.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ channel = maker.channel
+ categories.each do |category|
+ target = channel.itunes_categories
+ if category.is_a?(Array)
+ category.each do |sub_category|
+ target = target.new_category
+ target.text = sub_category
+ end
+ else
+ target.new_category.text = category
+ end
+ end
+ end
+ assert_not_nil(feed)
+
+ new_feed = RSS::Maker.make("rss2.0") do |maker|
+ feed.setup_maker(maker)
+ end
+ assert_not_nil(new_feed)
+
+ channel = new_feed.channel
+
+ assert_equal(categories,
+ collect_itunes_categories(channel.itunes_categories))
+ end
+
+ private
+ def collect_itunes_categories(categories)
+ categories.collect do |c|
+ rest = collect_itunes_categories(c.itunes_categories)
+ if rest.empty?
+ c.text
+ else
+ [c.text, *rest]
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_setup_maker_slash.rb b/jni/ruby/test/rss/test_setup_maker_slash.rb
new file mode 100644
index 0000000..cd12db9
--- /dev/null
+++ b/jni/ruby/test/rss/test_setup_maker_slash.rb
@@ -0,0 +1,38 @@
+require_relative "rss-testcase"
+
+require "rss/maker"
+
+module RSS
+ class TestSetupMakerSlash < TestCase
+ def test_setup_maker
+ elements = {
+ "section" => "articles",
+ "department" => "not-an-ocean-unless-there-are-lobsters",
+ "comments" => 177,
+ "hit_parades" => [177, 155, 105, 33, 6, 3, 0],
+ }
+
+ rss = RSS::Maker.make("rss1.0") do |maker|
+ setup_dummy_channel(maker)
+ setup_dummy_item(maker)
+
+ item = maker.items.last
+ item.slash_section = elements["section"]
+ item.slash_department = elements["department"]
+ item.slash_comments = elements["comments"]
+ item.slash_hit_parade = elements["hit_parades"].join(",")
+ end
+ assert_not_nil(rss)
+
+ new_rss = RSS::Maker.make("rss1.0") do |maker|
+ rss.setup_maker(maker)
+ end
+ assert_not_nil(new_rss)
+
+ item = new_rss.items.last
+ assert_not_nil(item)
+
+ assert_slash_elements(elements, item)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_slash.rb b/jni/ruby/test/rss/test_slash.rb
new file mode 100644
index 0000000..757e787
--- /dev/null
+++ b/jni/ruby/test/rss/test_slash.rb
@@ -0,0 +1,64 @@
+require "cgi"
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/slash"
+
+module RSS
+ class TestSlash < TestCase
+ def setup
+ @elements = {
+ "section" => "articles",
+ "department" => "not-an-ocean-unless-there-are-lobsters",
+ "comments" => 177,
+ "hit_parades" => [177, 155, 105, 33, 6, 3, 0],
+ }
+
+ slash_nodes = @elements.collect do |name, value|
+ if name == "hit_parades"
+ name = "hit_parade"
+ value = value.join(",")
+ end
+ "<slash:#{name}>#{value}</slash:#{name}>"
+ end.join("\n")
+
+ slash_ns = {"slash" => "http://purl.org/rss/1.0/modules/slash/"}
+ @source = make_RDF(<<-EOR, slash_ns)
+#{make_channel}
+#{make_image}
+#{make_item(slash_nodes)}
+#{make_textinput}
+EOR
+ end
+
+ def test_parser
+ rss = RSS::Parser.parse(@source)
+
+ assert_not_nil(rss)
+
+ item = rss.items[0]
+ assert_not_nil(item)
+
+ assert_slash_elements(item)
+ end
+
+ def test_to_s
+ rss = RSS::Parser.parse(@source)
+ rss = RSS::Parser.parse(rss.to_s)
+
+ assert_not_nil(rss)
+
+ item = rss.items[0]
+ assert_not_nil(item)
+
+ assert_slash_elements(item)
+ end
+
+ private
+ def assert_slash_elements(target)
+ super(@elements, target)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_syndication.rb b/jni/ruby/test/rss/test_syndication.rb
new file mode 100644
index 0000000..17875f1
--- /dev/null
+++ b/jni/ruby/test/rss/test_syndication.rb
@@ -0,0 +1,125 @@
+require "cgi"
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/syndication"
+
+module RSS
+ class TestSyndication < TestCase
+
+ def setup
+ @prefix = "sy"
+ @uri = "http://purl.org/rss/1.0/modules/syndication/"
+
+ @parents = %w(channel)
+
+ t = Time.iso8601("2000-01-01T12:00:05+00:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+
+ @elems = {
+ :updatePeriod => "hourly",
+ :updateFrequency => "2",
+ :updateBase => t,
+ }
+
+ @sy_nodes = @elems.collect do |name, value|
+ "<#{@prefix}:#{name}>#{CGI.escapeHTML(value.to_s)}</#{@prefix}:#{name}>"
+ end.join("\n")
+
+ @rss_source = make_RDF(<<-EOR, {@prefix => @uri})
+#{make_channel(@sy_nodes)}
+#{make_image()}
+#{make_item()}
+#{make_textinput()}
+EOR
+
+ @rss = Parser.parse(@rss_source)
+ end
+
+ def test_parser
+
+ assert_nothing_raised do
+ Parser.parse(@rss_source)
+ end
+
+ @elems.each do |tag, value|
+ assert_too_much_tag(tag.to_s, "channel") do
+ Parser.parse(make_RDF(<<-EOR, {@prefix => @uri}))
+#{make_channel(("<" + @prefix + ":" + tag.to_s + ">" +
+ CGI.escapeHTML(value.to_s) +
+ "</" + @prefix + ":" + tag.to_s + ">") * 2)}
+#{make_item}
+EOR
+ end
+ end
+
+ end
+
+ def test_accessor
+
+ t = Time.iso8601("2003-01-01T12:00:23+09:00")
+ class << t
+ alias_method(:to_s, :iso8601)
+ end
+
+ new_value = {
+ :updatePeriod => "daily",
+ :updateFrequency => "11",
+ :updateBase => t,
+ }
+
+ @elems.each do |name, value|
+ value = value.to_i if name == :updateFrequency
+ @parents.each do |parent|
+ assert_equal(value, @rss.__send__(parent).__send__("sy_#{name}"))
+ @rss.__send__(parent).__send__("sy_#{name}=", new_value[name])
+ new_val = new_value[name]
+ new_val = new_val.to_i if name == :updateFrequency
+ assert_equal(new_val, @rss.__send__(parent).__send__("sy_#{name}"))
+ end
+ end
+
+ %w(hourly daily weekly monthly yearly).each do |x|
+ @parents.each do |parent|
+ assert_nothing_raised do
+ @rss.__send__(parent).sy_updatePeriod = x
+ end
+ end
+ end
+
+ %w(-2 0.3 -0.4).each do |x|
+ @parents.each do |parent|
+ assert_not_available_value("sy:updateBase", x) do
+ @rss.__send__(parent).sy_updateBase = x
+ end
+ end
+ end
+
+ end
+
+ def test_to_s
+
+ @elems.each do |name, value|
+ excepted = "<#{@prefix}:#{name}>#{value}</#{@prefix}:#{name}>"
+ @parents.each do |parent|
+ assert_equal(excepted,
+ @rss.__send__(parent).__send__("sy_#{name}_element"))
+ end
+ end
+
+ REXML::Document.new(@rss_source).root.each_element do |parent|
+ if @parents.include?(parent.name)
+ parent.each_element do |elem|
+ if elem.namespace == @uri
+ assert_equal(elem.text, @elems[elem.name.intern].to_s)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_taxonomy.rb b/jni/ruby/test/rss/test_taxonomy.rb
new file mode 100644
index 0000000..34937e4
--- /dev/null
+++ b/jni/ruby/test/rss/test_taxonomy.rb
@@ -0,0 +1,172 @@
+require "cgi"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/2.0"
+require "rss/taxonomy"
+
+module RSS
+ class TestTaxonomy < TestCase
+
+ def setup
+ @prefix = "taxo"
+ @uri = "http://purl.org/rss/1.0/modules/taxonomy/"
+ @dc_prefix = "dc"
+ @dc_uri = "http://purl.org/dc/elements/1.1/"
+
+ @ns = {
+ @prefix => @uri,
+ @dc_prefix => @dc_uri,
+ }
+
+ @topics_parents = %w(channel item)
+
+ @topics_lis = [
+ "http://meerkat.oreillynet.com/?c=cat23",
+ "http://meerkat.oreillynet.com/?c=47",
+ "http://dmoz.org/Computers/Data_Formats/Markup_Languages/XML/",
+ ]
+
+ @topics_node = "<#{@prefix}:topics>\n"
+ @topics_node << " <rdf:Bag>\n"
+ @topics_lis.each do |value|
+ resource = CGI.escapeHTML(value)
+ @topics_node << " <rdf:li resource=\"#{resource}\"/>\n"
+ end
+ @topics_node << " </rdf:Bag>\n"
+ @topics_node << "</#{@prefix}:topics>"
+
+ @topic_topics_lis = \
+ [
+ "http://meerkat.oreillynet.com/?c=cat23",
+ "http://dmoz.org/Computers/Data_Formats/Markup_Languages/SGML/",
+ "http://dmoz.org/Computers/Programming/Internet/",
+ ]
+
+ @topic_contents = \
+ [
+ {
+ :link => "http://meerkat.oreillynet.com/?c=cat23",
+ :title => "Data: XML",
+ :description => "A Meerkat channel",
+ },
+ {
+ :link => "http://dmoz.org/Computers/Data_Formats/Markup_Languages/XML/",
+ :title => "XML",
+ :subject => "XML",
+ :description => "DMOZ category",
+ :topics => @topic_topics_lis,
+ }
+ ]
+
+ @topic_nodes = @topic_contents.collect do |info|
+ link = info[:link]
+ rv = "<#{@prefix}:topic rdf:about=\"#{link}\">\n"
+ info.each do |name, value|
+ case name
+ when :topics
+ rv << " <#{@prefix}:topics>\n"
+ rv << " <rdf:Bag>\n"
+ value.each do |li|
+ resource = CGI.escapeHTML(li)
+ rv << " <rdf:li resource=\"#{resource}\"/>\n"
+ end
+ rv << " </rdf:Bag>\n"
+ rv << " </#{@prefix}:topics>\n"
+ else
+ prefix = (name == :link ? @prefix : @dc_prefix)
+ rv << " <#{prefix}:#{name}>#{value}</#{prefix}:#{name}>\n"
+ end
+ end
+ rv << "</#{@prefix}:topic>"
+ end
+
+ @rss_source = make_RDF(<<-EOR, @ns)
+#{make_channel(@topics_node)}
+#{make_image()}
+#{make_item(@topics_node)}
+#{make_textinput()}
+#{@topic_nodes.join("\n")}
+EOR
+
+ @rss = Parser.parse(@rss_source)
+ end
+
+ def test_parser
+ assert_nothing_raised do
+ Parser.parse(@rss_source)
+ end
+
+ assert_too_much_tag("topics", "channel") do
+ Parser.parse(make_RDF(<<-EOR, @ns))
+#{make_channel(@topics_node * 2)}
+#{make_item()}
+EOR
+ end
+
+ assert_too_much_tag("topics", "item") do
+ Parser.parse(make_RDF(<<-EOR, @ns))
+#{make_channel()}
+#{make_item(@topics_node * 2)}
+EOR
+ end
+ end
+
+ def test_accessor
+ topics = @rss.channel.taxo_topics
+ assert_equal(@topics_lis.sort,
+ topics.Bag.lis.collect {|li| li.resource}.sort)
+ assert_equal(@topics_lis.sort, topics.resources.sort)
+
+ assert_equal(@rss.taxo_topics.first, @rss.taxo_topic)
+
+ @topic_contents.each_with_index do |info, i|
+ topic = @rss.taxo_topics[i]
+ info.each do |name, value|
+ case name
+ when :link
+ assert_equal(value, topic.about)
+ assert_equal(value, topic.taxo_link)
+ when :topics
+ assert_equal(value.sort, topic.taxo_topics.resources.sort)
+ else
+ assert_equal(value, topic.__send__("dc_#{name}"))
+ end
+ end
+ end
+ end
+
+ def test_to_s
+ @topics_parents.each do |parent|
+ meth = "taxo_topics_element"
+ assert_equal(@topics_node, @rss.__send__(parent).__send__(meth))
+ end
+
+ @topic_nodes.each_with_index do |node, i|
+ expected_xml = taxo_xmlns_container(node)
+ expected = REXML::Document.new(expected_xml).root.elements[1]
+ actual_xml = taxo_xmlns_container(@rss.taxo_topics[i].to_s(true, ""))
+ actual = REXML::Document.new(actual_xml).root.elements[1]
+ expected_elems = expected.reject {|x| x.is_a?(REXML::Text)}
+ actual_elems = actual.reject {|x| x.is_a?(REXML::Text)}
+ expected_elems.sort! {|x, y| x.name <=> y.name}
+ actual_elems.sort! {|x, y| x.name <=> y.name}
+ assert_equal(expected_elems.collect {|x| x.to_s},
+ actual_elems.collect {|x| x.to_s})
+ assert_equal(expected.attributes.sort, actual.attributes.sort)
+ end
+ end
+
+ private
+ def taxo_xmlns_container(content)
+ xmlns_container({
+ @prefix => @uri,
+ "dc" => "http://purl.org/dc/elements/1.1/",
+ "rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ },
+ content)
+ end
+ end
+end
+
diff --git a/jni/ruby/test/rss/test_to_s.rb b/jni/ruby/test/rss/test_to_s.rb
new file mode 100644
index 0000000..4ef83ce
--- /dev/null
+++ b/jni/ruby/test/rss/test_to_s.rb
@@ -0,0 +1,670 @@
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/maker"
+require "rss/1.0"
+require "rss/2.0"
+require "rss/content"
+require "rss/dublincore"
+require "rss/syndication"
+require "rss/trackback"
+
+module RSS
+ class TestToS < TestCase
+ def setup
+ @image_url = "http://example.com/foo.png"
+ @textinput_link = "http://example.com/search.cgi"
+ @item_links = [
+ "http://example.com/1",
+ "http://example.com/2",
+ ]
+
+ setup_xml_declaration_info
+ setup_xml_stylesheet_infos
+ setup_channel_info
+ setup_item_infos
+ setup_image_info
+ setup_textinput_info
+
+ setup_dublin_core_info
+ setup_syndication_info
+ setup_content_info
+ setup_trackback_info
+ end
+
+ def test_to_s_10
+ rss = RSS::Maker.make("1.0") do |maker|
+ setup_full(maker)
+ end
+
+ assert_xml_declaration(@version, @encoding, @standalone, rss)
+ assert_xml_stylesheets(@xs_infos, rss.xml_stylesheets)
+ assert_channel10(@channel_info, rss.channel)
+ assert_items10(@item_infos, rss.items)
+ rss.items.each do |item|
+ assert_trackback(@trackback_info, item)
+ end
+ assert_image10(@image_info, rss.image)
+ assert_textinput10(@textinput_info, rss.textinput)
+
+ rss = RSS::Parser.parse(rss.to_s)
+
+ assert_xml_declaration(@version, @encoding, @standalone, rss)
+ assert_xml_stylesheets(@xs_infos, rss.xml_stylesheets)
+ assert_channel10(@channel_info, rss.channel)
+ assert_items10(@item_infos, rss.items)
+ assert_image10(@image_info, rss.image)
+ assert_textinput10(@textinput_info, rss.textinput)
+ end
+
+ def test_to_s_09
+ rss = RSS::Maker.make("0.91") do |maker|
+ setup_full(maker)
+ end
+
+ assert_xml_declaration(@version, @encoding, @standalone, rss)
+ assert_xml_stylesheets(@xs_infos, rss.xml_stylesheets)
+ assert_channel09(@channel_info, rss.channel)
+ assert_items09(@item_infos, rss.items)
+ assert_image09(@image_info, rss.image)
+ assert_textinput09(@textinput_info, rss.textinput)
+
+ rss = RSS::Parser.parse(rss.to_s)
+
+ assert_xml_declaration(@version, @encoding, @standalone, rss)
+ assert_xml_stylesheets(@xs_infos, rss.xml_stylesheets)
+ assert_channel09(@channel_info, rss.channel)
+ assert_items09(@item_infos, rss.items)
+ assert_image09(@image_info, rss.image)
+ assert_textinput09(@textinput_info, rss.textinput)
+ end
+
+ def test_to_s_20
+ rss = RSS::Maker.make("2.0") do |maker|
+ setup_full(maker)
+ end
+
+ assert_xml_declaration(@version, @encoding, @standalone, rss)
+ assert_xml_stylesheets(@xs_infos, rss.xml_stylesheets)
+ assert_channel20(@channel_info, rss.channel)
+ assert_items20(@item_infos, rss.items)
+ assert_image20(@image_info, rss.image)
+ assert_textinput20(@textinput_info, rss.textinput)
+
+ rss = RSS::Parser.parse(rss.to_s)
+
+ assert_xml_declaration(@version, @encoding, @standalone, rss)
+ assert_xml_stylesheets(@xs_infos, rss.xml_stylesheets)
+ assert_channel20(@channel_info, rss.channel)
+ assert_items20(@item_infos, rss.items)
+ assert_image20(@image_info, rss.image)
+ assert_textinput20(@textinput_info, rss.textinput)
+ end
+
+ private
+ def setup_xml_declaration_info
+ @version = "1.0"
+ @encoding = "UTF-8"
+ @standalone = false
+ end
+
+ def setup_xml_stylesheet_infos
+ @xs_infos = [
+ {
+ "href" => "XXX.xsl",
+ "type" => "text/xsl",
+ "title" => "XXX",
+ "media" => "print",
+ "alternate" => "no",
+ },
+ {
+ "href" => "YYY.css",
+ "type" => "text/css",
+ "title" => "YYY",
+ "media" => "all",
+ "alternate" => "no",
+ },
+ ]
+ end
+
+ def setup_channel_info
+ @channel_info = {
+ "about" => "http://example.com/index.rdf",
+ "title" => "Sample RSS",
+ "link" => "http://example.com/",
+ "description" => "Sample\n\n\n\n\nSite",
+ "language" => "en",
+ "copyright" => "FDL",
+ "managingEditor" => "foo@example.com",
+ "webMaster" => "webmaster@example.com",
+ "rating" => '(PICS-1.1 "http://www.rsac.org/ratingsv01.html" l gen true comment "RSACi North America Server" for "http://www.rsac.org" on "1996.04.16T08:15-0500" r (n 0 s 0 v 0 l 0))',
+ "docs" => "http://backend.userland.com/rss091",
+ "skipDays" => [
+ "Monday",
+ "Friday",
+ ],
+ "skipHours" => [
+ "12",
+ "23",
+ ],
+ "date" => Time.now,
+ "lastBuildDate" => Time.now - 3600,
+ "generator" => "RSS Maker",
+ "ttl" => "60",
+ "cloud" => {
+ "domain" => "rpc.sys.com",
+ "port" => "80",
+ "path" => "/RPC2",
+ "registerProcedure" => "myCloud.rssPleaseNotify",
+ "protocol" => "xml-rpc",
+ },
+ "category" => {
+ "domain" => "http://example.com/misc/",
+ "content" => "misc",
+ },
+
+ "image" => {
+ "resource" => @image_url,
+ },
+
+ "textinput" => {
+ "resource" => @textinput_link,
+ },
+
+ "items" => @item_links.collect{|link| {"resource" => link}},
+ }
+ end
+
+ def setup_item_infos
+ @item_infos = [
+ {
+ "title" => "Sample item1",
+ "link" => @item_links[0],
+ "description" => "Sample description1",
+ "date" => Time.now - 3600,
+ "author" => "foo@example.com",
+ "comments" => "http://example.com/1/comments",
+ "guid" => {
+ "isPermaLink" => "true",
+ "content" => "http://example.com/1",
+ },
+ "enclosure" => {
+ "url" => "http://example.com/1.mp3",
+ "length" => "100",
+ "type" => "audio/mpeg",
+ },
+ "source" => {
+ "url" => "http:/example.com/",
+ "content" => "Sample site",
+ },
+ "category" => {
+ "domain" => "http://example.com/misc/",
+ "content" => "misc",
+ },
+ },
+
+ {
+ "title" => "Sample item2",
+ "link" => @item_links[1],
+ "description" => "Sample description2",
+ "date" => Time.now - 7200,
+ "author" => "foo@example.com",
+ "comments" => "http://example.com/2/comments",
+ "guid" => {
+ "isPermaLink" => "false",
+ "content" => "http://example.com/2",
+ },
+ "enclosure" => {
+ "url" => "http://example.com/2.mp3",
+ "length" => "200",
+ "type" => "audio/mpeg",
+ },
+ "source" => {
+ "url" => "http:/example.com/",
+ "content" => "Sample site",
+ },
+ "category" => {
+ "domain" => "http://example.com/misc/",
+ "content" => "misc",
+ },
+ },
+ ]
+ end
+
+ def setup_image_info
+ @image_info = {
+ "title" => "Sample image",
+ "url" => @image_url,
+ "width" => "88",
+ "height" => "31",
+ "description" => "Sample",
+ }
+ end
+
+ def setup_textinput_info
+ @textinput_info = {
+ "title" => "Sample textinput",
+ "description" => "Search",
+ "name" => "key",
+ "link" => @textinput_link,
+ }
+ end
+
+ def setup_dublin_core_info
+ @dc_info = {
+ "title" => "DC title",
+ "description" => "DC desc",
+ "creator" => "DC creator",
+ "subject" => "DC subject",
+ "publisher" => "DC publisher",
+ "contributor" => "DC contributor",
+ "type" => "DC type",
+ "format" => "DC format",
+ "identifier" => "DC identifier",
+ "source" => "DC source",
+ "language" => "ja",
+ "relation" => "DC relation",
+ "coverage" => "DC coverage",
+ "rights" => "DC rights",
+ "date" => Time.now - 60,
+ }
+ end
+
+ def setup_syndication_info
+ @sy_info = {
+ "updatePeriod" => "hourly",
+ "updateFrequency" => "2",
+ "updateBase" => Time.now - 3600,
+ }
+ end
+
+ def setup_content_info
+ @content_info = {
+ "encoded" => "<p>p</p>",
+ }
+ end
+
+ def setup_trackback_info
+ @trackback_info = {
+ "ping" => "http://example.com/tb.cgi?tb_id=XXX",
+ "abouts" => [
+ "http://example.net/tb.cgi?tb_id=YYY",
+ "http://example.org/tb.cgi?tb_id=ZZZ",
+ ]
+ }
+ end
+
+
+ def setup_full(maker)
+ setup_xml_declaration(maker)
+ setup_xml_stylesheets(maker)
+ setup_channel(maker)
+ setup_image(maker)
+ setup_items(maker)
+ setup_textinput(maker)
+ end
+
+ def setup_xml_declaration(maker)
+ %w(version encoding standalone).each do |name|
+ maker.__send__("#{name}=", instance_eval("@#{name}"))
+ end
+ end
+
+ def setup_xml_stylesheets(maker)
+ @xs_infos.each do |info|
+ xs = maker.xml_stylesheets.new_xml_stylesheet
+ info.each do |name, value|
+ xs.__send__("#{name}=", value)
+ end
+ end
+ end
+
+ def setup_channel(maker)
+ channel = maker.channel
+ info = @channel_info
+
+ %w(about title link description language copyright
+ managingEditor webMaster rating docs date
+ lastBuildDate generator ttl).each do |name|
+ channel.__send__("#{name}=", info[name])
+ end
+
+ skipDays = channel.skipDays
+ info["skipDays"].each do |day|
+ new_day = skipDays.new_day
+ new_day.content = day
+ end
+
+ skipHours = channel.skipHours
+ info["skipHours"].each do |hour|
+ new_hour = skipHours.new_hour
+ new_hour.content = hour
+ end
+
+ cloud = channel.cloud
+ %w(domain port path registerProcedure protocol).each do |name|
+ cloud.__send__("#{name}=", info["cloud"][name])
+ end
+
+ category = channel.categories.new_category
+ %w(domain content).each do |name|
+ category.__send__("#{name}=", info["category"][name])
+ end
+ end
+
+ def setup_image(maker)
+ image = maker.image
+ info = @image_info
+
+ %w(title url width height description).each do |name|
+ image.__send__("#{name}=", info[name])
+ end
+ end
+
+ def setup_items(maker)
+ items = maker.items
+
+ @item_infos.each do |info|
+ item = items.new_item
+ %w(title link description date author comments).each do |name|
+ item.__send__("#{name}=", info[name])
+ end
+
+ guid = item.guid
+ %w(isPermaLink content).each do |name|
+ guid.__send__("#{name}=", info["guid"][name])
+ end
+
+ enclosure = item.enclosure
+ %w(url length type).each do |name|
+ enclosure.__send__("#{name}=", info["enclosure"][name])
+ end
+
+ source = item.source
+ %w(url content).each do |name|
+ source.__send__("#{name}=", info["source"][name])
+ end
+
+ category = item.categories.new_category
+ %w(domain content).each do |name|
+ category.__send__("#{name}=", info["category"][name])
+ end
+
+ setup_trackback(item)
+ end
+ end
+
+ def setup_textinput(maker)
+ textinput = maker.textinput
+ info = @textinput_info
+
+ %w(title description name link).each do |name|
+ textinput.__send__("#{name}=", info[name])
+ end
+ end
+
+ def setup_content(target)
+ prefix = "content"
+ %w(encoded).each do |name|
+ target.__send__("#{prefix}_#{name}=", @content_info[name])
+ end
+ end
+
+ def setup_dublin_core(target)
+ prefix = "dc"
+ %w(title description creator subject publisher
+ contributor type format identifier source language
+ relation coverage rights).each do |name|
+ target.__send__("#{prefix}_#{name}=", @dc_info[name])
+ end
+ end
+
+ def setup_syndicate(target)
+ prefix = "sy"
+ %w(updatePeriod updateFrequency updateBase).each do |name|
+ target.__send__("#{prefix}_#{name}=", @sy_info[name])
+ end
+ end
+
+ def setup_trackback(target)
+ target.trackback_ping = @trackback_info["ping"]
+ @trackback_info["abouts"].each do |about|
+ new_about = target.trackback_abouts.new_about
+ new_about.value = about
+ end
+ end
+
+
+ def assert_channel10(attrs, channel)
+ _wrap_assertion do
+ n_attrs = normalized_attrs(attrs)
+
+ names = %w(about title link description)
+ assert_attributes(attrs, names, channel)
+
+ %w(image items textinput).each do |name|
+ value = n_attrs[name]
+ if value
+ target = channel.__send__(name)
+ __send__("assert_channel10_#{name}", value, target)
+ end
+ end
+ end
+ end
+
+ def assert_channel10_image(attrs, image)
+ _wrap_assertion do
+ assert_attributes(attrs, %w(resource), image)
+ end
+ end
+
+ def assert_channel10_textinput(attrs, textinput)
+ _wrap_assertion do
+ assert_attributes(attrs, %w(resource), textinput)
+ end
+ end
+
+ def assert_channel10_items(attrs, items)
+ _wrap_assertion do
+ assert_equal(items.resources, items.Seq.lis.collect {|x| x.resource})
+ items.Seq.lis.each_with_index do |li, i|
+ assert_attributes(attrs[i], %w(resource), li)
+ end
+ end
+ end
+
+ def assert_image10(attrs, image)
+ _wrap_assertion do
+ names = %w(about title url link)
+ assert_attributes(attrs, names, image)
+ end
+ end
+
+ def assert_items10(attrs, items)
+ _wrap_assertion do
+ names = %w(about title link description)
+ items.each_with_index do |item, i|
+ assert_attributes(attrs[i], names, item)
+ end
+ end
+ end
+
+ def assert_textinput10(attrs, textinput)
+ _wrap_assertion do
+ names = %w(about title description name link)
+ assert_attributes(attrs, names, textinput)
+ end
+ end
+
+
+ def assert_channel09(attrs, channel)
+ _wrap_assertion do
+ n_attrs = normalized_attrs(attrs)
+
+ names = %w(title description link language rating
+ copyright pubDate lastBuildDate docs
+ managingEditor webMaster)
+ assert_attributes(attrs, names, channel)
+
+ %w(skipHours skipDays).each do |name|
+ value = n_attrs[name]
+ if value
+ target = channel.__send__(name)
+ __send__("assert_channel09_#{name}", value, target)
+ end
+ end
+ end
+ end
+
+ def assert_channel09_skipDays(contents, skipDays)
+ _wrap_assertion do
+ days = skipDays.days
+ contents.each_with_index do |content, i|
+ assert_equal(content, days[i].content)
+ end
+ end
+ end
+
+ def assert_channel09_skipHours(contents, skipHours)
+ _wrap_assertion do
+ hours = skipHours.hours
+ contents.each_with_index do |content, i|
+ assert_equal(content.to_i, hours[i].content)
+ end
+ end
+ end
+
+ def assert_image09(attrs, image)
+ _wrap_assertion do
+ names = %w(url link title description)
+ names << ["width", :integer]
+ names << ["height", :integer]
+ assert_attributes(attrs, names, image)
+ end
+ end
+
+ def assert_items09(attrs, items)
+ _wrap_assertion do
+ names = %w(title link description)
+ items.each_with_index do |item, i|
+ assert_attributes(attrs[i], names, item)
+ end
+ end
+ end
+
+ def assert_textinput09(attrs, textinput)
+ _wrap_assertion do
+ names = %w(title description name link)
+ assert_attributes(attrs, names, textinput)
+ end
+ end
+
+
+ def assert_channel20(attrs, channel)
+ _wrap_assertion do
+ n_attrs = normalized_attrs(attrs)
+
+ names = %w(title link description language copyright
+ managingEditor webMaster pubDate
+ lastBuildDate generator docs rating)
+ names << ["ttl", :integer]
+ assert_attributes(attrs, names, channel)
+
+ %w(cloud categories skipHours skipDays).each do |name|
+ value = n_attrs[name]
+ if value
+ target = channel.__send__(name)
+ __send__("assert_channel20_#{name}", value, target)
+ end
+ end
+ end
+ end
+
+ def assert_channel20_skipDays(contents, skipDays)
+ assert_channel09_skipDays(contents, skipDays)
+ end
+
+ def assert_channel20_skipHours(contents, skipHours)
+ assert_channel09_skipHours(contents, skipHours)
+ end
+
+ def assert_channel20_cloud(attrs, cloud)
+ _wrap_assertion do
+ names = %w(domain path registerProcedure protocol)
+ names << ["port", :integer]
+ assert_attributes(attrs, names, cloud)
+ end
+ end
+
+ def assert_channel20_categories(attrs, categories)
+ _wrap_assertion do
+ names = %w(domain content)
+ categories.each_with_index do |category, i|
+ assert_attributes(attrs[i], names, category)
+ end
+ end
+ end
+
+ def assert_image20(attrs, image)
+ _wrap_assertion do
+ names = %w(url link title description)
+ names << ["width", :integer]
+ names << ["height", :integer]
+ assert_attributes(attrs, names, image)
+ end
+ end
+
+ def assert_items20(attrs, items)
+ _wrap_assertion do
+ names = %w(about title link description)
+ items.each_with_index do |item, i|
+ assert_attributes(attrs[i], names, item)
+
+ n_attrs = normalized_attrs(attrs[i])
+
+ %w(source enclosure categories guid).each do |name|
+ value = n_attrs[name]
+ if value
+ target = item.__send__(name)
+ __send__("assert_items20_#{name}", value, target)
+ end
+ end
+ end
+ end
+ end
+
+ def assert_items20_source(attrs, source)
+ _wrap_assertion do
+ assert_attributes(attrs, %w(url content), source)
+ end
+ end
+
+ def assert_items20_enclosure(attrs, enclosure)
+ _wrap_assertion do
+ names = ["url", ["length", :integer], "type"]
+ assert_attributes(attrs, names, enclosure)
+ end
+ end
+
+ def assert_items20_categories(attrs, categories)
+ _wrap_assertion do
+ assert_channel20_categories(attrs, categories)
+ end
+ end
+
+ def assert_items20_guid(attrs, guid)
+ _wrap_assertion do
+ names = [["isPermaLink", :boolean], ["content"]]
+ assert_attributes(attrs, names, guid)
+ end
+ end
+
+ def assert_textinput20(attrs, textinput)
+ _wrap_assertion do
+ names = %w(title description name link)
+ assert_attributes(attrs, names, textinput)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_trackback.rb b/jni/ruby/test/rss/test_trackback.rb
new file mode 100644
index 0000000..8560c25
--- /dev/null
+++ b/jni/ruby/test/rss/test_trackback.rb
@@ -0,0 +1,135 @@
+require "cgi"
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/2.0"
+require "rss/trackback"
+
+module RSS
+ class TestTrackBack < TestCase
+
+ def setup
+ @prefix = "trackback"
+ @uri = "http://madskills.com/public/xml/rss/module/trackback/"
+
+ @parents = %w(item)
+
+ @elems = {
+ :ping => "http://bar.com/tb.cgi?tb_id=rssplustrackback",
+ :about => "http://foo.com/trackback/tb.cgi?tb_id=20020923",
+ }
+
+ @content_nodes = @elems.collect do |name, value|
+ "<#{@prefix}:#{name} rdf:resource=\"#{CGI.escapeHTML(value.to_s)}\"/>"
+ end.join("\n")
+
+ @content_nodes2 = @elems.collect do |name, value|
+ "<#{@prefix}:#{name}>#{CGI.escapeHTML(value.to_s)}</#{@prefix}:#{name}>"
+ end.join("\n")
+
+ @rss_source = make_RDF(<<-EOR, {@prefix => @uri})
+#{make_channel()}
+#{make_image()}
+#{make_item(@content_nodes)}
+#{make_textinput()}
+EOR
+
+ @rss = Parser.parse(@rss_source)
+
+ @rss20_source = make_rss20(nil, {@prefix => @uri}) do
+ make_channel20(nil) do
+ make_item20(@content_nodes2)
+ end
+ end
+
+ @rss20 = Parser.parse(@rss20_source, false)
+ end
+
+ def test_parser
+
+ assert_nothing_raised do
+ Parser.parse(@rss_source)
+ end
+
+ @elems.find_all{|k, v| k == :ping}.each do |tag, value|
+ assert_too_much_tag(tag.to_s, "item") do
+ Parser.parse(make_RDF(<<-EOR, {@prefix => @uri}))
+#{make_channel()}
+#{make_item(("<" + @prefix + ":" + tag.to_s + " rdf:resource=\"" +
+ CGI.escapeHTML(value.to_s) +
+ "\"/>") * 2)}
+EOR
+ end
+ end
+
+ @elems.find_all{|k, v| k == :about}.each do |tag, value|
+ assert_missing_tag("trackback:ping", "item") do
+ Parser.parse(make_RDF(<<-EOR, {@prefix => @uri}))
+#{make_channel()}
+#{make_item(("<" + @prefix + ":" + tag.to_s + " rdf:resource=\"" +
+ CGI.escapeHTML(value.to_s) +
+ "\"/>") * 2)}
+EOR
+ end
+
+ end
+
+ end
+
+ def test_accessor
+
+ new_value = {
+ :ping => "http://baz.com/trackback/tb.cgi?tb_id=20030808",
+ :about => "http://hoge.com/trackback/tb.cgi?tb_id=90030808",
+ }
+
+ @elems.each do |name, value|
+ @parents.each do |parent|
+ accessor = "#{RSS::TRACKBACK_PREFIX}_#{name}"
+ target = @rss.__send__(parent)
+ target20 = @rss20.channel.__send__(parent, -1)
+ assert_equal(value, target.__send__(accessor))
+ assert_equal(value, target20.__send__(accessor))
+ if name == :about
+ # abount is zero or more
+ target.__send__("#{accessor}=", 0, new_value[name].to_s)
+ target20.__send__("#{accessor}=", 0, new_value[name].to_s)
+ else
+ target.__send__("#{accessor}=", new_value[name].to_s)
+ target20.__send__("#{accessor}=", new_value[name].to_s)
+ end
+ assert_equal(new_value[name], target.__send__(accessor))
+ assert_equal(new_value[name], target20.__send__(accessor))
+ end
+ end
+
+ end
+
+ def test_to_s
+
+ @elems.each do |name, value|
+ excepted = %Q!<#{@prefix}:#{name} rdf:resource="#{CGI.escapeHTML(value)}"/>!
+ @parents.each do |parent|
+ meth = "#{RSS::TRACKBACK_PREFIX}_#{name}_element"
+ meth << "s" if name == :about
+ assert_equal(excepted, @rss.__send__(parent).__send__(meth))
+ end
+ end
+
+ REXML::Document.new(@rss_source).root.each_element do |parent|
+ if @parents.include?(parent.name)
+ parent.each_element do |elem|
+ if elem.namespace == @uri
+ assert_equal(elem.attributes["resource"], @elems[elem.name.intern])
+ end
+ end
+ end
+ end
+
+ end
+
+ end
+end
+
diff --git a/jni/ruby/test/rss/test_version.rb b/jni/ruby/test/rss/test_version.rb
new file mode 100644
index 0000000..d2175ab
--- /dev/null
+++ b/jni/ruby/test/rss/test_version.rb
@@ -0,0 +1,9 @@
+require_relative "rss-testcase"
+
+module RSS
+ class TestVersion < TestCase
+ def test_version
+ assert_equal("0.2.7", ::RSS::VERSION)
+ end
+ end
+end
diff --git a/jni/ruby/test/rss/test_xml-stylesheet.rb b/jni/ruby/test/rss/test_xml-stylesheet.rb
new file mode 100644
index 0000000..c67922f
--- /dev/null
+++ b/jni/ruby/test/rss/test_xml-stylesheet.rb
@@ -0,0 +1,108 @@
+require "rexml/document"
+
+require_relative "rss-testcase"
+
+require "rss/1.0"
+require "rss/xml-stylesheet"
+
+module RSS
+ class TestXMLStyleSheet < TestCase
+
+ def test_accessor
+ [
+ {:href => "a.xsl", :type => "text/xsl"},
+ {:media => "print", :title => "FOO"},
+ {:charset => "UTF-8", :alternate => "yes"},
+ ].each do |attrs|
+ assert_xml_stylesheet_attrs(attrs, XMLStyleSheet.new(attrs))
+ end
+ end
+
+ def test_to_s
+ [
+ {:href => "a.xsl", :type => "text/xsl"},
+ {:type => "text/xsl"},
+ {:href => "a.xsl", :guess_type => "text/xsl"},
+ {:href => "a.css", :type => "text/css"},
+ {:href => "a.css", :type => "text/xsl",
+ :guess_type => "text/css"},
+ {:href => "a.xsl", :type => "text/xsl",
+ :title => "sample", :media => "printer",
+ :charset => "UTF-8", :alternate => "yes"},
+ {:href => "a.css", :guess_type => "text/css",
+ :alternate => "no"},
+ {:type => "text/xsl", :title => "sample",
+ :media => "printer", :charset => "UTF-8",
+ :alternate => "yes"},
+ ].each do |attrs|
+ target, contents = parse_pi(XMLStyleSheet.new(attrs).to_s)
+ assert_xml_stylesheet(target, attrs, XMLStyleSheet.new(contents))
+ end
+ end
+
+ def test_bad_alternate
+ %w(a ___ ??? BAD_ALTERNATE).each do |value|
+ xss = XMLStyleSheet.new
+ assert_raise(NotAvailableValueError) do
+ xss.alternate = value
+ end
+ xss.do_validate = false
+ assert_nothing_raised do
+ xss.alternate = value
+ end
+ assert_nil(xss.alternate)
+ end
+ end
+
+ def test_parse
+ [
+ [{:href => "a.xsl", :type => "text/xsl"},],
+ [{:media => "print", :title => "FOO"},],
+ [{:charset => "UTF-8", :alternate => "yes"},],
+ [{:href => "a.xsl", :type => "text/xsl"},
+ {:type => "text/xsl"},
+ {:href => "a.xsl", :guess_type => "text/xsl"},
+ {:href => "a.css", :type => "text/css"},
+ {:href => "a.css", :type => "text/xsl",
+ :guess_type => "text/css"},
+ {:href => "a.xsl", :type => "text/xsl",
+ :title => "sample", :media => "printer",
+ :charset => "UTF-8", :alternate => "yes"},
+ {:href => "a.css", :guess_type => "text/css",
+ :alternate => "no"},
+ {:type => "text/xsl", :title => "sample",
+ :media => "printer", :charset => "UTF-8",
+ :alternate => "yes"},],
+ ].each do |xsss|
+ doc = REXML::Document.new(make_sample_RDF)
+ root = doc.root
+ xsss.each do |xss|
+ content = xss.collect do |key, name|
+ %Q[#{key}="#{name}"]
+ end.join(" ")
+ pi = REXML::Instruction.new("xml-stylesheet", content)
+ root.previous_sibling = pi
+ end
+ rss = Parser.parse(doc.to_s)
+ have_href_xsss = xsss.find_all {|xss| xss.has_key?(:href)}
+ assert_equal(have_href_xsss.size, rss.xml_stylesheets.size)
+ rss.xml_stylesheets.each_with_index do |stylesheet, i|
+ target, = parse_pi(stylesheet.to_s)
+ assert_xml_stylesheet(target, have_href_xsss[i], stylesheet)
+ end
+ end
+ end
+
+ def parse_pi(pi)
+ /\A\s*<\?(\S+)([^?]*\?+(?:[^?>][^?]*\?+)*)>\s*\z/ =~ pi
+ target = $1
+ dummy = REXML::Document.new("<dummy #{$2.to_s.chop}/>").root
+ contents = {}
+ dummy.attributes.each do |name, value|
+ contents[name] = value
+ end
+ [target, contents]
+ end
+
+ end
+end
diff --git a/jni/ruby/test/ruby/allpairs.rb b/jni/ruby/test/ruby/allpairs.rb
new file mode 100644
index 0000000..27b6f59
--- /dev/null
+++ b/jni/ruby/test/ruby/allpairs.rb
@@ -0,0 +1,102 @@
+module AllPairs
+ module_function
+
+ def make_prime(v)
+ return 2 if v < 2
+ ary = [true] * (v*2)
+ 2.upto(Math.sqrt(ary.length).ceil) {|i|
+ return i if ary[i] && v <= i
+ (i*2).step(ary.length, i) {|j|
+ ary[j] = false
+ }
+ }
+ v.upto(ary.length-1) {|i|
+ return i if ary[i]
+ }
+ raise "[bug] prime not found greater than #{v}"
+ end
+
+ def make_basic_block(prime)
+ prime.times {|i|
+ prime.times {|j|
+ row = [i]
+ 0.upto(prime-1) {|m|
+ row << (i*m + j) % prime
+ }
+ yield row
+ }
+ }
+ end
+
+ def combine_block(tbl1, tbl2)
+ result = []
+ tbl2.each {|row|
+ result << row * tbl1.first.length
+ }
+ tbl1.each_with_index {|row, k|
+ next if k == 0
+ result << row.map {|i| [i] * tbl2.first.length }.flatten
+ }
+ result
+ end
+
+ def make_large_block(v, prime)
+ if prime <= v+1
+ make_basic_block(v) {|row|
+ yield row
+ }
+ else
+ tbl = []
+ make_basic_block(v) {|row|
+ tbl << row
+ }
+ tbls = [tbl]
+ while tbl.first.length ** 2 < prime
+ tbl = combine_block(tbl, tbl)
+ tbls << tbl
+ end
+ tbl1 = tbls.find {|t| prime <= t.first.length * tbl.first.length }
+ tbl = combine_block(tbl, tbl1)
+ tbl.each {|row|
+ yield row
+ }
+ end
+ end
+
+ def each_index(*vs)
+ n = vs.length
+ max_v = vs.max
+ h = {}
+ make_large_block(max_v, n) {|row|
+ row = vs.zip(row).map {|v, i| i % v }
+ next if h[row]
+ h[row] = true
+ yield row
+ }
+ end
+
+ # generate all pairs test.
+ def each(*args)
+ args.map! {|a| a.to_a }
+ each_index(*args.map {|a| a.length}) {|is|
+ yield is.zip(args).map {|i, a| a[i] }
+ }
+ end
+
+ # generate all combination in cartesian product. (not all-pairs test)
+ def exhaustive_each(*args)
+ args = args.map {|a| a.to_a }
+ i = 0
+ while true
+ n = i
+ as = []
+ args.reverse_each {|a|
+ n, m = n.divmod(a.length)
+ as.unshift a[m]
+ }
+ break if 0 < n
+ yield as
+ i += 1
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/beginmainend.rb b/jni/ruby/test/ruby/beginmainend.rb
new file mode 100644
index 0000000..6cdfb15
--- /dev/null
+++ b/jni/ruby/test/ruby/beginmainend.rb
@@ -0,0 +1,80 @@
+errout = ARGV.shift
+
+BEGIN {
+ puts "b1"
+ local_begin1 = "local_begin1"
+ $global_begin1 = "global_begin1"
+ ConstBegin1 = "ConstBegin1"
+}
+
+BEGIN {
+ puts "b2"
+
+ BEGIN {
+ puts "b2-1"
+ }
+}
+
+# for scope check
+#raise if defined?(local_begin1)
+raise unless defined?($global_begin1)
+raise unless defined?(::ConstBegin1)
+local_for_end2 = "e2"
+$global_for_end1 = "e1"
+
+puts "main"
+
+END {
+ puts local_for_end2 # e2
+}
+
+eval <<EOE
+ BEGIN {
+ puts "b3"
+
+ BEGIN {
+ puts "b3-1"
+ }
+ }
+
+ BEGIN {
+ puts "b4"
+ }
+
+ END {
+ puts "e3"
+ }
+
+ END {
+ puts "e4"
+
+ END {
+ puts "e4-1"
+
+ END {
+ puts "e4-1-1"
+ }
+ }
+
+ END {
+ puts "e4-2"
+ }
+ }
+EOE
+
+END {
+ exit
+ puts "should not be dumped"
+
+ END {
+ puts "not reached"
+ }
+}
+
+END {
+ puts $global_for_end1 # e1
+
+ END {
+ puts "e1-1"
+ }
+}
diff --git a/jni/ruby/test/ruby/enc/test_big5.rb b/jni/ruby/test/ruby/enc/test_big5.rb
new file mode 100644
index 0000000..e8fe027
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_big5.rb
@@ -0,0 +1,28 @@
+require "test/unit"
+
+class TestBig5 < Test::Unit::TestCase
+ def s(s)
+ s.force_encoding("big5")
+ end
+
+ def test_mbc_enc_len
+ assert_equal(1, s("\xa1\xa1").size)
+ end
+
+ def test_mbc_to_code
+ assert_equal(0xa1a1, s("\xa1\xa1").ord)
+ end
+
+ def test_code_to_mbc
+ assert_equal(s("\xa1\xa1"), 0xa1a1.chr("big5"))
+ end
+
+ def test_mbc_case_fold
+ r = Regexp.new(s("(\xa1\xa1)\\1"), "i")
+ assert_match(r, s("\xa1\xa1\xa1\xa1"))
+ end
+
+ def test_left_adjust_char_head
+ assert_equal(s("\xa1\xa1"), s("\xa1\xa1\xa1\xa1").chop)
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_cp949.rb b/jni/ruby/test/ruby/enc/test_cp949.rb
new file mode 100644
index 0000000..e675c7b
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_cp949.rb
@@ -0,0 +1,28 @@
+require "test/unit"
+
+class TestCP949 < Test::Unit::TestCase
+ def s(s)
+ s.force_encoding("cp949")
+ end
+
+ def test_mbc_enc_len
+ assert_equal(1, s("\xa1\xa1").size)
+ end
+
+ def test_mbc_to_code
+ assert_equal(0xa1a1, s("\xa1\xa1").ord)
+ end
+
+ def test_code_to_mbc
+ assert_equal(s("\xa1\xa1"), 0xa1a1.chr("cp949"))
+ end
+
+ def test_mbc_case_fold
+ r = Regexp.new(s("(\xa1\xa1)\\1"), "i")
+ assert_match(r, s("\xa1\xa1\xa1\xa1"))
+ end
+
+ def test_left_adjust_char_head
+ assert_equal(s("\xa1\xa1"), s("\xa1\xa1\xa1\xa1").chop)
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_emoji.rb b/jni/ruby/test/ruby/enc/test_emoji.rb
new file mode 100644
index 0000000..1f80c5a
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_emoji.rb
@@ -0,0 +1,442 @@
+require 'test/unit'
+
+module Emoji
+
+ class TestRenameSJIS < Test::Unit::TestCase
+ def test_shift_jis
+ assert_raise(ArgumentError) { "".force_encoding("Shift_JIS-DoCoMo") }
+ assert_raise(ArgumentError) { "".force_encoding("Shift_JIS-KDDI") }
+ assert_raise(ArgumentError) { "".force_encoding("Shift_JIS-SoftBank") }
+ end
+ end
+
+ class TestUTF8_BLACK_SUN_WITH_RAYS < Test::Unit::TestCase
+ include Emoji
+
+ def setup
+ @codes = {
+ "UTF8-DoCoMo" => utf8_docomo("\u{E63E}"),
+ "UTF8-KDDI" => utf8_kddi("\u{E488}"),
+ "UTF8-SoftBank" => utf8_softbank("\u{E04A}"),
+ "UTF-8" => "\u{2600}",
+ }
+ end
+
+ def test_convert
+ @codes.each do |from_enc, from_str|
+ @codes.each do |to_enc, to_str|
+ next if from_enc == to_enc
+ assert_equal to_str, from_str.encode(to_enc), "convert from #{from_enc} to #{to_enc}"
+ end
+ end
+ end
+ end
+
+ class TestDoCoMo < Test::Unit::TestCase
+ include Emoji
+
+ def setup
+ setup_instance_variable(self)
+ end
+
+ def test_encoding_name
+ %w(UTF8-DoCoMo
+ SJIS-DoCoMo).each do |n|
+ assert_include Encoding.name_list, n, "encoding not found: #{n}"
+ end
+ end
+
+ def test_comparison
+ assert_not_equal Encoding::UTF_8, Encoding::UTF8_DoCoMo
+ assert_not_equal Encoding::Windows_31J, Encoding::SJIS_DoCoMo
+ end
+
+ def test_from_utf8
+ assert_nothing_raised { assert_equal utf8_docomo(@aiueo_utf8), to_utf8_docomo(@aiueo_utf8) }
+ assert_nothing_raised { assert_equal sjis_docomo(@aiueo_sjis), to_sjis_docomo(@aiueo_utf8) }
+ end
+
+ def test_from_sjis
+ assert_nothing_raised { assert_equal utf8_docomo(@aiueo_utf8), to_utf8_docomo(@aiueo_sjis) }
+ assert_nothing_raised { assert_equal sjis_docomo(@aiueo_sjis), to_sjis_docomo(@aiueo_sjis) }
+ end
+
+ def test_to_utf8
+ assert_nothing_raised { assert_equal @utf8, to_utf8(@utf8_docomo) }
+ assert_nothing_raised { assert_equal @utf8, to_utf8(@sjis_docomo) }
+ end
+
+ def test_to_sjis
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis(@utf8_docomo) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis(@sjis_docomo) }
+ end
+
+ def test_to_eucjp
+ assert_raise(Encoding::UndefinedConversionError) { to_eucjp(@utf8_docomo) }
+ assert_raise(Encoding::UndefinedConversionError) { to_eucjp(@sjis_docomo) }
+ end
+
+ def test_docomo
+ assert_nothing_raised { assert_equal @utf8_docomo, to_utf8_docomo(@sjis_docomo) }
+ assert_nothing_raised { assert_equal @sjis_docomo, to_sjis_docomo(@utf8_docomo) }
+ end
+
+ def test_to_kddi
+ assert_nothing_raised { assert_equal @utf8_kddi, to_utf8_kddi(@utf8_docomo) }
+ assert_nothing_raised { assert_equal @sjis_kddi, to_sjis_kddi(@utf8_docomo) }
+ assert_nothing_raised { assert_equal @iso2022jp_kddi, to_iso2022jp_kddi(@utf8_docomo) }
+
+ assert_nothing_raised { assert_equal @utf8_kddi, to_utf8_kddi(@sjis_docomo) }
+ assert_nothing_raised { assert_equal @sjis_kddi, to_sjis_kddi(@sjis_docomo) }
+ assert_nothing_raised { assert_equal @iso2022jp_kddi, to_iso2022jp_kddi(@sjis_docomo) }
+
+ assert_raise(Encoding::UndefinedConversionError) { to_utf8_kddi(@utf8_docomo_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis_kddi(@utf8_docomo_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_iso2022jp_kddi(@utf8_docomo_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { to_utf8_kddi(@sjis_docomo_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis_kddi(@sjis_docomo_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_iso2022jp_kddi(@sjis_docomo_only) }
+ end
+
+ def test_to_softbank
+ assert_nothing_raised { assert_equal @utf8_softbank, to_utf8_softbank(@utf8_docomo) }
+ assert_nothing_raised { assert_equal @sjis_softbank, to_sjis_softbank(@utf8_docomo) }
+
+ assert_nothing_raised { assert_equal @utf8_softbank, to_utf8_softbank(@sjis_docomo) }
+ assert_nothing_raised { assert_equal @sjis_softbank, to_sjis_softbank(@sjis_docomo) }
+
+ assert_raise(Encoding::UndefinedConversionError) { to_utf8_softbank(@utf8_docomo_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis_softbank(@utf8_docomo_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { to_utf8_softbank(@sjis_docomo_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis_softbank(@sjis_docomo_only) }
+ end
+ end
+
+ class TestKDDI < Test::Unit::TestCase
+ include Emoji
+
+ def setup
+ setup_instance_variable(self)
+ end
+
+ def test_encoding_name
+ %w(UTF8-KDDI
+ SJIS-KDDI
+ ISO-2022-JP-KDDI
+ stateless-ISO-2022-JP-KDDI).each do |n|
+ assert_include Encoding.name_list, n, "encoding not found: #{n}"
+ end
+ end
+
+ def test_comparison
+ assert_not_equal Encoding::UTF_8, Encoding::UTF8_KDDI
+ assert_not_equal Encoding::Windows_31J, Encoding::SJIS_KDDI
+ assert_not_equal Encoding::ISO_2022_JP, Encoding::ISO_2022_JP_KDDI
+ assert_not_equal Encoding::Stateless_ISO_2022_JP, Encoding::Stateless_ISO_2022_JP_KDDI
+ end
+
+ def test_from_utf8
+ assert_nothing_raised { assert_equal utf8_kddi(@aiueo_utf8), to_utf8_kddi(@aiueo_utf8) }
+ assert_nothing_raised { assert_equal sjis_kddi(@aiueo_sjis), to_sjis_kddi(@aiueo_utf8) }
+ assert_nothing_raised { assert_equal iso2022jp_kddi(@aiueo_iso2022jp), to_iso2022jp_kddi(@aiueo_utf8) }
+ end
+
+ def test_from_sjis
+ assert_nothing_raised { assert_equal utf8_kddi(@aiueo_utf8), to_utf8_kddi(@aiueo_sjis) }
+ assert_nothing_raised { assert_equal sjis_kddi(@aiueo_sjis), to_sjis_kddi(@aiueo_sjis) }
+ assert_nothing_raised { assert_equal iso2022jp_kddi(@aiueo_iso2022jp), to_iso2022jp_kddi(@aiueo_sjis) }
+ end
+
+ def test_from_iso2022jp
+ assert_nothing_raised { assert_equal utf8_kddi(@aiueo_utf8), to_utf8_kddi(@aiueo_iso2022jp) }
+ assert_nothing_raised { assert_equal sjis_kddi(@aiueo_sjis), to_sjis_kddi(@aiueo_iso2022jp) }
+ assert_nothing_raised { assert_equal iso2022jp_kddi(@aiueo_iso2022jp), to_iso2022jp_kddi(@aiueo_iso2022jp) }
+ end
+
+ def test_to_utf8
+ assert_nothing_raised { assert_equal @utf8, to_utf8(@utf8_kddi) }
+ assert_nothing_raised { assert_equal @utf8, to_utf8(@utf8_undoc_kddi) }
+ assert_nothing_raised { assert_equal @utf8, to_utf8(@sjis_kddi) }
+ assert_nothing_raised { assert_equal @utf8, to_utf8(@iso2022jp_kddi) }
+ end
+
+ def test_to_sjis
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis(@utf8_kddi) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis(@utf8_undoc_kddi) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis(@sjis_kddi) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis(@iso2022jp_kddi) }
+ end
+
+ def test_to_eucjp
+ assert_raise(Encoding::UndefinedConversionError) { to_eucjp(@utf8_kddi) }
+ assert_raise(Encoding::UndefinedConversionError) { to_eucjp(@utf8_undoc_kddi) }
+ assert_raise(Encoding::UndefinedConversionError) { to_eucjp(@sjis_kddi) }
+ assert_raise(Encoding::UndefinedConversionError) { to_eucjp(@iso2022jp_kddi) }
+ end
+
+ def test_kddi
+ assert_nothing_raised { assert_equal @utf8_kddi, to_utf8_kddi(@sjis_kddi) }
+ assert_nothing_raised { assert_equal @utf8_kddi, to_utf8_kddi(@iso2022jp_kddi) }
+ assert_nothing_raised { assert_equal @sjis_kddi, to_sjis_kddi(@sjis_kddi) }
+ assert_nothing_raised { assert_equal @sjis_kddi, to_sjis_kddi(@utf8_undoc_kddi) }
+ assert_nothing_raised { assert_equal @sjis_kddi, to_sjis_kddi(@iso2022jp_kddi) }
+ assert_nothing_raised { assert_equal @iso2022jp_kddi, to_iso2022jp_kddi(@sjis_kddi) }
+ assert_nothing_raised { assert_equal @iso2022jp_kddi, to_iso2022jp_kddi(@utf8_undoc_kddi) }
+ assert_nothing_raised { assert_equal @iso2022jp_kddi, to_iso2022jp_kddi(@iso2022jp_kddi) }
+ end
+
+ def test_to_docomo
+ assert_nothing_raised { assert_equal @utf8_docomo, to_utf8_docomo(@utf8_kddi) }
+ assert_nothing_raised { assert_equal @sjis_docomo, to_sjis_docomo(@utf8_kddi) }
+
+ assert_nothing_raised { assert_equal @utf8_docomo, to_utf8_docomo(@utf8_undoc_kddi) }
+ assert_nothing_raised { assert_equal @sjis_docomo, to_sjis_docomo(@utf8_undoc_kddi) }
+
+ assert_nothing_raised { assert_equal @utf8_docomo, to_utf8_docomo(@sjis_kddi) }
+ assert_nothing_raised { assert_equal @sjis_docomo, to_sjis_docomo(@sjis_kddi) }
+
+ assert_nothing_raised { assert_equal @utf8_docomo, to_utf8_docomo(@iso2022jp_kddi) }
+ assert_nothing_raised { assert_equal @sjis_docomo, to_sjis_docomo(@iso2022jp_kddi) }
+
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @utf8_docomo, to_utf8_docomo(@utf8_kddi_only) }
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @sjis_docomo, to_sjis_docomo(@utf8_kddi_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @utf8_docomo, to_utf8_docomo(@utf8_undoc_kddi_only) }
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @sjis_docomo, to_sjis_docomo(@utf8_undoc_kddi_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @utf8_docomo, to_utf8_docomo(@sjis_kddi_only) }
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @sjis_docomo, to_sjis_docomo(@sjis_kddi_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @utf8_docomo, to_utf8_docomo(@iso2022jp_kddi_only) }
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @sjis_docomo, to_sjis_docomo(@iso2022jp_kddi_only) }
+ end
+
+ def test_to_softbank
+ assert_nothing_raised { assert_equal @utf8_softbank, to_utf8_softbank(@utf8_kddi) }
+ assert_nothing_raised { assert_equal @sjis_softbank, to_sjis_softbank(@utf8_kddi) }
+
+ assert_nothing_raised { assert_equal @utf8_softbank, to_utf8_softbank(@utf8_undoc_kddi) }
+ assert_nothing_raised { assert_equal @sjis_softbank, to_sjis_softbank(@utf8_undoc_kddi) }
+
+ assert_nothing_raised { assert_equal @utf8_softbank, to_utf8_softbank(@sjis_kddi) }
+ assert_nothing_raised { assert_equal @sjis_softbank, to_sjis_softbank(@sjis_kddi) }
+
+ assert_nothing_raised { assert_equal @utf8_softbank, to_utf8_softbank(@iso2022jp_kddi) }
+ assert_nothing_raised { assert_equal @sjis_softbank, to_sjis_softbank(@iso2022jp_kddi) }
+
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @utf8_softbank, to_utf8_softbank(@utf8_kddi_only) }
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @sjis_softbank, to_sjis_softbank(@utf8_kddi_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @utf8_softbank, to_utf8_softbank(@utf8_undoc_kddi_only) }
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @sjis_softbank, to_sjis_softbank(@utf8_undoc_kddi_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @utf8_softbank, to_utf8_softbank(@sjis_kddi_only) }
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @sjis_softbank, to_sjis_softbank(@sjis_kddi_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @utf8_softbank, to_utf8_softbank(@iso2022jp_kddi_only) }
+ assert_raise(Encoding::UndefinedConversionError) { assert_equal @sjis_softbank, to_sjis_softbank(@iso2022jp_kddi_only) }
+ end
+ end
+
+ class TestSoftBank < Test::Unit::TestCase
+ include Emoji
+
+ def setup
+ setup_instance_variable(self)
+ end
+
+ def test_encoding_name
+ %w(UTF8-SoftBank
+ SJIS-SoftBank).each do |n|
+ assert_include Encoding.name_list, n, "encoding not found: #{n}"
+ end
+ end
+
+ def test_comparison
+ assert_not_equal Encoding::UTF_8, Encoding::UTF8_SoftBank
+ assert_not_equal Encoding::Windows_31J, Encoding::SJIS_SoftBank
+ end
+
+ def test_from_utf8
+ assert_nothing_raised { assert_equal utf8_softbank(@aiueo_utf8), to_utf8_softbank(@aiueo_utf8) }
+ assert_nothing_raised { assert_equal sjis_softbank(@aiueo_sjis), to_sjis_softbank(@aiueo_utf8) }
+ end
+
+ def test_from_sjis
+ assert_nothing_raised { assert_equal utf8_softbank(@aiueo_utf8), to_utf8_softbank(@aiueo_sjis) }
+ assert_nothing_raised { assert_equal sjis_softbank(@aiueo_sjis), to_sjis_softbank(@aiueo_sjis) }
+ end
+
+ def test_to_utf8
+ assert_nothing_raised { assert_equal @utf8, to_utf8(@utf8_softbank) }
+ assert_nothing_raised { assert_equal @utf8, to_utf8(@sjis_softbank) }
+ end
+
+ def test_to_sjis
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis(@utf8_softbank) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis(@sjis_softbank) }
+ end
+
+ def test_to_eucjp
+ assert_raise(Encoding::UndefinedConversionError) { to_eucjp(@utf8_softbank) }
+ assert_raise(Encoding::UndefinedConversionError) { to_eucjp(@sjis_softbank) }
+ end
+
+ def test_softbank
+ assert_nothing_raised { assert_equal @utf8_softbank, to_utf8_softbank(@sjis_softbank) }
+ assert_nothing_raised { assert_equal @sjis_softbank, to_sjis_softbank(@utf8_softbank) }
+ end
+
+ def test_to_docomo
+ assert_nothing_raised { assert_equal @utf8_docomo, to_utf8_docomo(@utf8_softbank) }
+ assert_nothing_raised { assert_equal @sjis_docomo, to_sjis_docomo(@utf8_softbank) }
+
+ assert_nothing_raised { assert_equal @utf8_docomo, to_utf8_docomo(@sjis_softbank) }
+ assert_nothing_raised { assert_equal @sjis_docomo, to_sjis_docomo(@sjis_softbank) }
+
+ assert_raise(Encoding::UndefinedConversionError) { to_utf8_docomo(@utf8_softbank_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis_docomo(@utf8_softbank_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { to_utf8_docomo(@sjis_softbank_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis_docomo(@sjis_softbank_only) }
+ end
+
+ def test_to_kddi
+ assert_nothing_raised { assert_equal @utf8_kddi, to_utf8_kddi(@utf8_softbank) }
+ assert_nothing_raised { assert_equal @sjis_kddi, to_sjis_kddi(@utf8_softbank) }
+ assert_nothing_raised { assert_equal @iso2022jp_kddi, to_iso2022jp_kddi(@utf8_softbank) }
+
+ assert_nothing_raised { assert_equal @utf8_kddi, to_utf8_kddi(@sjis_softbank) }
+ assert_nothing_raised { assert_equal @sjis_kddi, to_sjis_kddi(@sjis_softbank) }
+ assert_nothing_raised { assert_equal @iso2022jp_kddi, to_iso2022jp_kddi(@sjis_softbank) }
+
+ assert_raise(Encoding::UndefinedConversionError) { to_utf8_kddi(@utf8_softbank_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis_kddi(@utf8_softbank_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_iso2022jp_kddi(@utf8_softbank_only) }
+
+ assert_raise(Encoding::UndefinedConversionError) { to_utf8_kddi(@sjis_softbank_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_sjis_kddi(@sjis_softbank_only) }
+ assert_raise(Encoding::UndefinedConversionError) { to_iso2022jp_kddi(@sjis_softbank_only) }
+ end
+ end
+
+ private
+
+ def setup_instance_variable(obj)
+ obj.instance_eval do
+ @aiueo_utf8 = "\u{3042}\u{3044}\u{3046}\u{3048}\u{304A}"
+ @aiueo_sjis = to_sjis(@aiueo_utf8)
+ @aiueo_iso2022jp = to_iso2022jp(@aiueo_utf8)
+
+ @utf8 = "\u{2600}"
+
+ @utf8_docomo = utf8_docomo("\u{E63E}")
+ @sjis_docomo = sjis_docomo("\xF8\x9F")
+ @utf8_docomo_only = utf8_docomo("\u{E6B1}")
+ @sjis_docomo_only = sjis_docomo("\xF9\x55")
+
+ @utf8_kddi = utf8_kddi("\u{E488}")
+ @utf8_undoc_kddi = utf8_kddi("\u{EF60}")
+ @sjis_kddi = sjis_kddi("\xF6\x60")
+ @iso2022jp_kddi = iso2022jp_kddi("\x1B$B\x75\x41\x1B(B")
+ @stateless_iso2022jp_kddi = stateless_iso2022jp_kddi("\x92\xF5\xC1")
+ @utf8_kddi_only = utf8_kddi("\u{E5B3}")
+ @utf8_undoc_kddi_only = utf8_kddi("\u{F0D0}")
+ @sjis_kddi_only = sjis_kddi("\xF7\xD0")
+ @iso2022jp_kddi_only = iso2022jp_kddi("\x1B$B\x78\x52\x1B(B")
+ @stateless_iso2022jp_kddi_only = stateless_iso2022jp_kddi("\x92\xF8\xD2")
+
+ @utf8_softbank = utf8_softbank("\u{E04A}")
+ @sjis_softbank = sjis_softbank("\xF9\x8B")
+ @utf8_softbank_only = utf8_softbank("\u{E524}")
+ @sjis_softbank_only = sjis_softbank("\xFB\xC4")
+ end
+ end
+
+ def utf8(str)
+ str.force_encoding("UTF-8")
+ end
+
+ def to_utf8(str)
+ str.encode("UTF-8")
+ end
+
+ def to_sjis(str)
+ str.encode("Windows-31J")
+ end
+
+ def to_eucjp(str)
+ str.encode("eucJP-ms")
+ end
+
+ def to_iso2022jp(str)
+ str.encode("ISO-2022-JP")
+ end
+
+ def utf8_docomo(str)
+ str.force_encoding("UTF8-DoCoMo")
+ end
+
+ def to_utf8_docomo(str)
+ str.encode("UTF8-DoCoMo")
+ end
+
+ def utf8_kddi(str)
+ str.force_encoding("UTF8-KDDI")
+ end
+
+ def to_utf8_kddi(str)
+ str.encode("UTF8-KDDI")
+ end
+
+ def utf8_softbank(str)
+ str.force_encoding("UTF8-SoftBank")
+ end
+
+ def to_utf8_softbank(str)
+ str.encode("UTF8-SoftBank")
+ end
+
+ def sjis_docomo(str)
+ str.force_encoding("SJIS-DoCoMo")
+ end
+
+ def to_sjis_docomo(str)
+ str.encode("SJIS-DoCoMo")
+ end
+
+ def sjis_kddi(str)
+ str.force_encoding("SJIS-KDDI")
+ end
+
+ def to_sjis_kddi(str)
+ str.encode("SJIS-KDDI")
+ end
+
+ def sjis_softbank(str)
+ str.force_encoding("SJIS-SoftBank")
+ end
+
+ def to_sjis_softbank(str)
+ str.encode("SJIS-SoftBank")
+ end
+
+ def iso2022jp_kddi(str)
+ str.force_encoding("ISO-2022-JP-KDDI")
+ end
+
+ def to_iso2022jp_kddi(str)
+ str.encode("ISO-2022-JP-KDDI")
+ end
+
+ def stateless_iso2022jp_kddi(str)
+ str.force_encoding("stateless-ISO-2022-JP-KDDI")
+ end
+
+ def to_stateless_iso2022jp_kddi(str)
+ str.encode("stateless-ISO-2022-JP-KDDI")
+ end
+
+end
diff --git a/jni/ruby/test/ruby/enc/test_euc_jp.rb b/jni/ruby/test/ruby/enc/test_euc_jp.rb
new file mode 100644
index 0000000..510ee46
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_euc_jp.rb
@@ -0,0 +1,24 @@
+# vim: set fileencoding=euc-jp
+
+require "test/unit"
+
+class TestEUC_JP < Test::Unit::TestCase
+ def test_mbc_case_fold
+ assert_match(/()(a)\1\2/i, "aA")
+ assert_match(/()(a)\1\2/i, "aA")
+ end
+
+ def test_property
+ assert_match(/{0}\p{Hiragana}{4}/, "Ҥ餬")
+ assert_no_match(/{0}\p{Hiragana}{4}/, "")
+ assert_no_match(/{0}\p{Hiragana}{4}/, "")
+ assert_no_match(/{0}\p{Katakana}{4}/, "Ҥ餬")
+ assert_match(/{0}\p{Katakana}{4}/, "")
+ assert_no_match(/{0}\p{Katakana}{4}/, "")
+ assert_raise(RegexpError) { Regexp.new('{0}\p{foobarbaz}') }
+ end
+
+ def test_charboundary
+ assert_nil(/\xA2\xA2/ =~ "\xA1\xA2\xA2\xA3")
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_euc_kr.rb b/jni/ruby/test/ruby/enc/test_euc_kr.rb
new file mode 100644
index 0000000..5413fa6
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_euc_kr.rb
@@ -0,0 +1,36 @@
+require "test/unit"
+
+class TestEucKr < Test::Unit::TestCase
+ def s(s)
+ s.force_encoding("euc-kr")
+ end
+
+ def test_mbc_enc_len
+ assert_equal(1, s("\xa1\xa1").size)
+ end
+
+ def test_mbc_to_code
+ assert_equal(0xa1a1, s("\xa1\xa1").ord)
+ end
+
+ def test_code_to_mbc
+ assert_equal(s("\xa1\xa1"), 0xa1a1.chr("euc-kr"))
+ end
+
+ def test_mbc_case_fold
+ r = Regexp.new(s("(\xa1\xa1)\\1"), "i")
+ assert_match(r, s("\xa1\xa1\xa1\xa1"))
+ end
+
+ def test_left_adjust_char_head
+ assert_equal(s("\xa1\xa1"), s("\xa1\xa1\xa1\xa1").chop)
+ end
+
+ def test_euro_sign
+ assert_equal("\u{20ac}", s("\xa2\xe6").encode("utf-8"))
+ end
+
+ def test_registered_mark
+ assert_equal("\u{00ae}", s("\xa2\xe7").encode("utf-8"))
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_euc_tw.rb b/jni/ruby/test/ruby/enc/test_euc_tw.rb
new file mode 100644
index 0000000..f36d86b
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_euc_tw.rb
@@ -0,0 +1,28 @@
+require "test/unit"
+
+class TestEucTw < Test::Unit::TestCase
+ def s(s)
+ s.force_encoding("euc-tw")
+ end
+
+ def test_mbc_enc_len
+ assert_equal(1, s("\xa1\xa1").size)
+ end
+
+ def test_mbc_to_code
+ assert_equal(0xa1a1, s("\xa1\xa1").ord)
+ end
+
+ def test_code_to_mbc
+ assert_equal(s("\xa1\xa1"), 0xa1a1.chr("euc-tw"))
+ end
+
+ def test_mbc_case_fold
+ r = Regexp.new(s("(\xa1\xa1)\\1"), "i")
+ assert_match(r, s("\xa1\xa1\xa1\xa1"))
+ end
+
+ def test_left_adjust_char_head
+ assert_equal(s("\xa1\xa1"), s("\xa1\xa1\xa1\xa1").chop)
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_gb18030.rb b/jni/ruby/test/ruby/enc/test_gb18030.rb
new file mode 100644
index 0000000..f379504
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_gb18030.rb
@@ -0,0 +1,126 @@
+require "test/unit"
+
+class TestGB18030 < Test::Unit::TestCase
+ def s(s)
+ s.force_encoding("gb18030")
+ end
+
+ def test_mbc_enc_len
+ assert_equal(1, s("\x81\x40").size)
+ assert_equal(1, s("\x81\x30\x81\x30").size)
+ end
+
+ def test_mbc_to_code
+ assert_equal(0x8140, s("\x81\x40").ord)
+ end
+
+ def test_code_to_mbc
+ assert_equal(s("\x81\x40"), 0x8140.chr("gb18030"))
+ end
+
+ def test_mbc_case_fold
+ r = Regexp.new(s("(\x81\x40)\\1"), "i")
+ assert_match(r, s("\x81\x40\x81\x40"))
+ end
+
+ def scheck(c, i)
+ assert_equal(s(c.reverse.take(c.size - i).join), s(c.reverse.join).chop)
+ end
+
+ def fcheck(c)
+ c = s(c.reverse.join)
+ assert_raise(ArgumentError, c) { c.chop }
+ end
+
+ def test_left_adjust_char_head
+ # C1: 00-2f, 3a-3f, 7f, ff
+ # C2: 40-7e, 80
+ # C4: 30-39
+ # CM: 81-fe
+ c1 = "\x2f"
+ c2 = "\x40"
+ c4 = "\x30"
+ cm = "\x81"
+
+ # S_START-c1
+ # S_START-c2-S_one_C2-0
+ # S_START-c2-S_one_C2-c1
+ # S_START-c2-S_one_C2-cm-S_odd_CM_one_CX-c1
+ # S_START-c2-S_one_C2-cm-S_odd_CM_one_CX-cm-S_even_CM_one_CX-c1
+ # S_START-c2-S_one_C2-cm-S_odd_CM_one_CX-cm-S_even_CM_one_CX-cm-S_odd_CM_one_CX(rec)
+ # S_START-c4-S_one_C4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-c4-S_one_C4_even_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-c4-S_one_C4_even_CMC4-cm-S_odd_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-c4-S_one_C4_even_CMC4-cm-S_odd_CMC4-c4-S_one_C4_odd_CMC4(rec)
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-c4-S_one_C4_even_CMC4-cm-S_odd_CMC4-cm-S_odd_CM_odd_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-c4-S_one_C4_even_CMC4-cm-S_odd_CMC4-cm-S_odd_CM_odd_CMC4-cm-S_even_CM_odd_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-c4-S_one_C4_even_CMC4-cm-S_odd_CMC4-cm-S_odd_CM_odd_CMC4-cm-S_even_CM_odd_CMC4-cm-S_odd_CM_odd_CMC4(rec)
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-cm-S_odd_CM_even_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-cm-S_odd_CM_even_CMC4-cm-S_even_CM_even_CMC4-c1
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-c4-S_one_C4_odd_CMC4-cm-S_even_CMC4-cm-S_odd_CM_even_CMC4-cm-S_even_CM_even_CMC4-cm-S_odd_CM_even_CMC4(rec)
+ # S_START-c4-S_one_C4-cm-S_one_CMC4-cm-S_even_CM_one_CX(rec)
+ # S_START-cm-S_one_CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-c4-S_even_C4CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-c4-S_even_C4CM-cm-S_one_CM_even_C4CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-c4-S_even_C4CM-cm-S_one_CM_even_C4CM-c4-S_odd_C4CM(rec)
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-c4-S_even_C4CM-cm-S_one_CM_even_C4CM-cm-S_even_CM_even_C4CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-c4-S_even_C4CM-cm-S_one_CM_even_C4CM-cm-S_even_CM_even_C4CM-cm-S_odd_CM_even_C4CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-c4-S_even_C4CM-cm-S_one_CM_even_C4CM-cm-S_even_CM_even_C4CM-cm-S_odd_CM_even_C4CM-cm-S_even_CM_even_C4CM(rec)
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-cm-S_even_CM_odd_C4CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-cm-S_even_CM_odd_C4CM-cm-S_odd_CM_odd_C4CM-c1
+ # S_START-cm-S_one_CM-c4-S_odd_C4CM-cm-S_one_CM_odd_C4CM-cm-S_even_CM_odd_C4CM-cm-S_odd_CM_odd_C4CM-cm-S_even_CM_odd_C4CM(rec)
+ # S_START-cm-S_one_CM-cm-S_odd_CM_one_CX(rec)
+
+ scheck([c1], 1)
+ scheck([c2], 1)
+ scheck([c2, c1], 1)
+ scheck([c2, cm, c1], 2)
+ scheck([c2, cm, cm, c1], 1)
+ scheck([c2, cm, cm, cm], 2)
+ scheck([c4], 1)
+ scheck([c4, c1], 1)
+ scheck([c4, cm], 2)
+ scheck([c4, cm, c1], 2)
+ scheck([c4, cm, c4, c1], 2)
+ scheck([c4, cm, c4, cm], 4)
+ scheck([c4, cm, c4, cm, c1], 4)
+ scheck([c4, cm, c4, cm, c4], 4)
+ scheck([c4, cm, c4, cm, c4, c1], 4)
+ scheck([c4, cm, c4, cm, c4, cm], 2)
+ scheck([c4, cm, c4, cm, c4, cm, c1], 2)
+ scheck([c4, cm, c4, cm, c4, cm, c4], 2)
+ scheck([c4, cm, c4, cm, c4, cm, cm, c1], 4)
+ scheck([c4, cm, c4, cm, c4, cm, cm, cm], 2)
+ scheck([c4, cm, c4, cm, c4, cm, cm, cm, c1], 2)
+ scheck([c4, cm, c4, cm, c4, cm, cm, cm, cm], 4)
+ scheck([c4, cm, c4, cm, cm, c1], 2)
+ scheck([c4, cm, c4, cm, cm, cm], 4)
+ scheck([c4, cm, c4, cm, cm, cm, c1], 4)
+ scheck([c4, cm, c4, cm, cm, cm, cm], 2)
+ scheck([c4, cm, cm], 1)
+ scheck([cm], 1)
+ scheck([cm, c1], 1)
+ scheck([cm, c4, c1], 1)
+ scheck([cm, c4, cm], 3)
+ scheck([cm, c4, cm, c1], 3)
+ scheck([cm, c4, cm, c4], 3)
+ scheck([cm, c4, cm, c4, c1], 3)
+ scheck([cm, c4, cm, c4, cm], 1)
+ scheck([cm, c4, cm, c4, cm, c1], 1)
+ scheck([cm, c4, cm, c4, cm, c4], 1)
+ scheck([cm, c4, cm, c4, cm, cm, c1], 3)
+ scheck([cm, c4, cm, c4, cm, cm, cm], 1)
+ scheck([cm, c4, cm, c4, cm, cm, cm, c1], 1)
+ scheck([cm, c4, cm, c4, cm, cm, cm, cm], 3)
+ scheck([cm, c4, cm, cm, c1], 1)
+ scheck([cm, c4, cm, cm, cm], 3)
+ scheck([cm, c4, cm, cm, cm, c1], 3)
+ scheck([cm, c4, cm, cm, cm, cm], 1)
+ scheck([cm, cm], 2)
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_gbk.rb b/jni/ruby/test/ruby/enc/test_gbk.rb
new file mode 100644
index 0000000..d6dc5d6
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_gbk.rb
@@ -0,0 +1,28 @@
+require "test/unit"
+
+class TestGBK < Test::Unit::TestCase
+ def s(s)
+ s.force_encoding("gbk")
+ end
+
+ def test_mbc_enc_len
+ assert_equal(1, s("\x81\x40").size)
+ end
+
+ def test_mbc_to_code
+ assert_equal(0x8140, s("\x81\x40").ord)
+ end
+
+ def test_code_to_mbc
+ assert_equal(s("\x81\x40"), 0x8140.chr("gbk"))
+ end
+
+ def test_mbc_case_fold
+ r = Regexp.new(s("(\x81\x40)\\1"), "i")
+ assert_match(r, s("\x81\x40\x81\x40"))
+ end
+
+ def test_left_adjust_char_head
+ assert_equal(s("\x81\x40"), s("\x81\x40\x81\x40").chop)
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_iso_8859.rb b/jni/ruby/test/ruby/enc/test_iso_8859.rb
new file mode 100644
index 0000000..64cc7cd
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_iso_8859.rb
@@ -0,0 +1,163 @@
+require 'test/unit'
+
+class TestISO8859 < Test::Unit::TestCase
+ ASSERTS = %q(
+ assert_match(/^(\xdf)\1$/i, "\xdf\xdf")
+ assert_match(/^(\xdf)\1$/i, "ssss")
+ # assert_match(/^(\xdf)\1$/i, "\xdfss") # this must be bug...
+ assert_match(/^[\xdfz]+$/i, "sszzsszz")
+ assert_match(/^SS$/i, "\xdf")
+ assert_match(/^Ss$/i, "\xdf")
+ ((0xc0..0xde).to_a - [0xd7]).each do |c|
+ c1 = c.chr("ENCODING")
+ c2 = (c + 0x20).chr("ENCODING")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ assert_match(/^\xff$/i, "\xff")
+ )
+
+ def test_iso_8859_1
+ eval("# encoding: iso8859-1\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-1"))
+ end
+
+ def test_iso_8859_2
+ eval("# encoding: iso8859-2\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-2"))
+ end
+
+ def test_iso_8859_3
+ eval(%q(# encoding: iso8859-3
+ assert_match(/^(\xdf)\1$/i, "\xdf\xdf")
+ assert_match(/^(\xdf)\1$/i, "ssss")
+ assert_match(/^[\xdfz]+$/i, "sszzsszz")
+ assert_match(/^SS$/i, "\xdf")
+ assert_match(/^Ss$/i, "\xdf")
+ [0xa1, 0xa6, *(0xa9..0xac), 0xaf].each do |c|
+ c1 = c.chr("iso8859-3")
+ c2 = (c + 0x10).chr("iso8859-3")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ ([*(0xc0..0xde)] - [0xc3, 0xd0, 0xd7]).each do |c|
+ c1 = c.chr("iso8859-3")
+ c2 = (c + 0x20).chr("iso8859-3")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ ))
+ end
+
+ def test_iso_8859_4
+ eval("# encoding: iso8859-4\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-4"))
+ end
+
+ def test_iso_8859_5
+ eval(%q(# encoding: iso8859-5
+ (0xb0..0xcf).each do |c|
+ c1 = c.chr("iso8859-5")
+ c2 = (c + 0x20).chr("iso8859-5")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ ((0xa1..0xaf).to_a - [0xad]).each do |c|
+ c1 = c.chr("iso8859-5")
+ c2 = (c + 0x50).chr("iso8859-5")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ ))
+ end
+
+ def test_iso_8859_6
+ eval(%q(# encoding: iso8859-6
+ [0xa4, 0xac, 0xbb, 0xbf, *(0xc1..0xda), *(0xe0..0xf2)].each do |c|
+ c1 = c.chr("iso8859-6")
+ assert_match(/^(#{ c1 })\1$/i, c1 * 2)
+ end
+ ))
+ end
+
+ def test_iso_8859_7
+ eval(%q(# encoding: iso8859-7
+ ((0xa0..0xfe).to_a - [0xae, 0xd2]).each do |c|
+ c1 = c.chr("iso8859-7")
+ assert_match(/^(#{ c1 })\1$/i, c1 * 2)
+ end
+ ((0xc1..0xd9).to_a - [0xd2]).each do |c|
+ c1 = c.chr("iso8859-7")
+ c2 = (c + 0x20).chr("iso8859-7")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ ))
+ end
+
+ def test_iso_8859_8
+ eval(%q(# encoding: iso8859-8
+ [0xa0, *(0xa2..0xbe), *(0xdf..0xfa), 0xfc, 0xfd].each do |c|
+ c1 = c.chr("iso8859-8")
+ assert_match(/^(#{ c1 })\1$/i, c1 * 2)
+ end
+ ))
+ end
+
+ def test_iso_8859_9
+ eval(%q(# encoding: iso8859-9
+ assert_match(/^(\xdf)\1$/i, "\xdf\xdf")
+ assert_match(/^(\xdf)\1$/i, "ssss")
+ assert_match(/^[\xdfz]+$/i, "sszzsszz")
+ assert_match(/^SS$/i, "\xdf")
+ assert_match(/^Ss$/i, "\xdf")
+ ([*(0xc0..0xdc)] - [0xd7]).each do |c|
+ c1 = c.chr("iso8859-9")
+ c2 = (c + 0x20).chr("iso8859-9")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ ))
+ end
+
+ def test_iso_8859_10
+ eval("# encoding: iso8859-10\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-10"))
+ end
+
+ def test_iso_8859_11
+ eval(%q(# encoding: iso8859-11
+ [*(0xa0..0xda), *(0xdf..0xfb)].each do |c|
+ c1 = c.chr("iso8859-11")
+ assert_match(/^(#{ c1 })\1$/i, c1 * 2)
+ end
+ ))
+ end
+
+ def test_iso_8859_13
+ eval("# encoding: iso8859-13\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-13"))
+ end
+
+ def test_iso_8859_14
+ eval("# encoding: iso8859-14\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-14"))
+ end
+
+ def test_iso_8859_15
+ eval("# encoding: iso8859-15\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-15"))
+ end
+
+ def test_iso_8859_16
+ eval("# encoding: iso8859-16\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-16"))
+ end
+end
+
diff --git a/jni/ruby/test/ruby/enc/test_koi8.rb b/jni/ruby/test/ruby/enc/test_koi8.rb
new file mode 100644
index 0000000..ce2d892
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_koi8.rb
@@ -0,0 +1,22 @@
+require "test/unit"
+
+class TestKOI8 < Test::Unit::TestCase
+ ASSERTS = %q(
+ (0xc0..0xdf).each do |c|
+ c1 = c.chr("ENCODING")
+ c2 = (c + 0x20).chr("ENCODING")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ )
+
+ def test_koi8_r
+ eval("# encoding: koi8-r\n" + ASSERTS.gsub("ENCODING", "koi8-r"))
+ end
+
+ def test_koi8_u
+ eval("# encoding: koi8-u\n" + ASSERTS.gsub("ENCODING", "koi8-u"))
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_shift_jis.rb b/jni/ruby/test/ruby/enc/test_shift_jis.rb
new file mode 100644
index 0000000..1bd47fa
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_shift_jis.rb
@@ -0,0 +1,27 @@
+# vim: set fileencoding=shift_jis
+
+require "test/unit"
+
+class TestShiftJIS < Test::Unit::TestCase
+ def test_mbc_case_fold
+ assert_match(/()(a)\1\2/i, "aA")
+ assert_match(/()(a)\1\2/i, "a`A")
+ end
+
+ def test_property
+ assert_match(/{0}\p{Hiragana}{4}/, "Ђ炪")
+ assert_no_match(/{0}\p{Hiragana}{4}/, "J^Ji")
+ assert_no_match(/{0}\p{Hiragana}{4}/, "")
+ assert_no_match(/{0}\p{Katakana}{4}/, "Ђ炪")
+ assert_match(/{0}\p{Katakana}{4}/, "J^Ji")
+ assert_no_match(/{0}\p{Katakana}{4}/, "")
+ assert_raise(RegexpError) { Regexp.new('{0}\p{foobarbaz}') }
+ end
+
+ def test_code_to_mbclen
+ s = ""
+ s << 0x82a9
+ assert_equal("", s)
+ assert_raise(RangeError) { s << 0x82 }
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_utf16.rb b/jni/ruby/test/ruby/enc/test_utf16.rb
new file mode 100644
index 0000000..63929c6
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_utf16.rb
@@ -0,0 +1,384 @@
+require 'test/unit'
+
+class TestUTF16 < Test::Unit::TestCase
+ def encdump(obj)
+ case obj
+ when String
+ d = obj.dump
+ if /\.force_encoding\("[A-Za-z0-9.:_+-]*"\)\z/ =~ d
+ d
+ else
+ "#{d}.force_encoding(#{obj.encoding.name.dump})"
+ end
+ when Regexp
+ "Regexp.new(#{encdump(obj.source)}, #{obj.options})"
+ else
+ raise Argument, "unexpected: #{obj.inspect}"
+ end
+ end
+
+ def enccall(recv, meth, *args)
+ desc = ''
+ if String === recv
+ desc << encdump(recv)
+ else
+ desc << recv.inspect
+ end
+ desc << '.' << meth.to_s
+ if !args.empty?
+ desc << '('
+ args.each_with_index {|a, i|
+ desc << ',' if 0 < i
+ if String === a
+ desc << encdump(a)
+ else
+ desc << a.inspect
+ end
+ }
+ desc << ')'
+ end
+ result = nil
+ assert_nothing_raised(desc) {
+ result = recv.send(meth, *args)
+ }
+ result
+ end
+
+ def assert_str_equal(expected, actual, message=nil)
+ full_message = build_message(message, <<EOT)
+#{encdump expected} expected but not equal to
+#{encdump actual}.
+EOT
+ assert_equal(expected, actual, full_message)
+ end
+
+ # tests start
+
+ def test_utf16be_valid_encoding
+ [
+ "\x00\x00",
+ "\xd7\xff",
+ "\xd8\x00\xdc\x00",
+ "\xdb\xff\xdf\xff",
+ "\xe0\x00",
+ "\xff\xff",
+ ].each {|s|
+ s.force_encoding("utf-16be")
+ assert_equal(true, s.valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ [
+ "\x00",
+ "\xd7",
+ "\xd8\x00",
+ "\xd8\x00\xd8\x00",
+ "\xdc\x00",
+ "\xdc\x00\xd8\x00",
+ "\xdc\x00\xdc\x00",
+ "\xe0",
+ "\xff",
+ ].each {|s|
+ s.force_encoding("utf-16be")
+ assert_equal(false, s.valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ end
+
+ def test_utf16le_valid_encoding
+ [
+ "\x00\x00",
+ "\xff\xd7",
+ "\x00\xd8\x00\xdc",
+ "\xff\xdb\xff\xdf",
+ "\x00\xe0",
+ "\xff\xff",
+ ].each {|s|
+ s.force_encoding("utf-16le")
+ assert_equal(true, s.valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ [
+ "\x00",
+ "\xd7",
+ "\x00\xd8",
+ "\x00\xd8\x00\xd8",
+ "\x00\xdc",
+ "\x00\xdc\x00\xd8",
+ "\x00\xdc\x00\xdc",
+ "\xe0",
+ "\xff",
+ ].each {|s|
+ s.force_encoding("utf-16le")
+ assert_equal(false, s.valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ end
+
+ def test_strftime
+ s = "aa".force_encoding("utf-16be")
+ assert_raise(ArgumentError, "Time.now.strftime(#{encdump s})") { Time.now.strftime(s) }
+ end
+
+ def test_intern
+ s = "aaaa".force_encoding("utf-16be")
+ assert_equal(s.encoding, s.intern.to_s.encoding, "#{encdump s}.intern.to_s.encoding")
+ end
+
+ def test_sym_eq
+ s = "aa".force_encoding("utf-16le")
+ assert_not_equal(:aa, s.intern, "#{encdump s}.intern != :aa")
+ end
+
+ def test_compatible
+ s1 = "aa".force_encoding("utf-16be")
+ s2 = "z".force_encoding("us-ascii")
+ assert_nil(Encoding.compatible?(s1, s2), "Encoding.compatible?(#{encdump s1}, #{encdump s2})")
+ end
+
+ def test_casecmp
+ s1 = "aa".force_encoding("utf-16be")
+ s2 = "AA"
+ assert_not_equal(0, s1.casecmp(s2), "#{encdump s1}.casecmp(#{encdump s2})")
+ end
+
+ def test_end_with
+ s1 = "ab".force_encoding("utf-16be")
+ s2 = "b".force_encoding("utf-16be")
+ assert_equal(false, s1.end_with?(s2), "#{encdump s1}.end_with?(#{encdump s2})")
+ end
+
+ def test_hex
+ assert_raise(Encoding::CompatibilityError) {
+ "ff".encode("utf-16le").hex
+ }
+ assert_raise(Encoding::CompatibilityError) {
+ "ff".encode("utf-16be").hex
+ }
+ end
+
+ def test_oct
+ assert_raise(Encoding::CompatibilityError) {
+ "77".encode("utf-16le").oct
+ }
+ assert_raise(Encoding::CompatibilityError) {
+ "77".encode("utf-16be").oct
+ }
+ end
+
+ def test_count
+ s1 = "aa".force_encoding("utf-16be")
+ s2 = "aa"
+ assert_raise(Encoding::CompatibilityError, "#{encdump s1}.count(#{encdump s2})") {
+ s1.count(s2)
+ }
+ end
+
+ def test_plus
+ s1 = "a".force_encoding("us-ascii")
+ s2 = "aa".force_encoding("utf-16be")
+ assert_raise(Encoding::CompatibilityError, "#{encdump s1} + #{encdump s2}") {
+ s1 + s2
+ }
+ end
+
+ def test_encoding_find
+ assert_raise(ArgumentError) {
+ Encoding.find("utf-8".force_encoding("utf-16be"))
+ }
+ end
+
+ def test_interpolation
+ s = "aa".force_encoding("utf-16be")
+ assert_raise(Encoding::CompatibilityError, "\"a\#{#{encdump s}}\"") {
+ "a#{s}"
+ }
+ end
+
+ def test_slice!
+ enccall("aa".force_encoding("UTF-16BE"), :slice!, -1)
+ end
+
+ def test_plus_empty1
+ s1 = ""
+ s2 = "aa".force_encoding("utf-16be")
+ assert_nothing_raised("#{encdump s1} << #{encdump s2}") {
+ s1 + s2
+ }
+ end
+
+ def test_plus_empty2
+ s1 = "aa"
+ s2 = "".force_encoding("utf-16be")
+ assert_nothing_raised("#{encdump s1} << #{encdump s2}") {
+ s1 + s2
+ }
+ end
+
+ def test_plus_nonempty
+ s1 = "aa"
+ s2 = "bb".force_encoding("utf-16be")
+ assert_raise(Encoding::CompatibilityError, "#{encdump s1} << #{encdump s2}") {
+ s1 + s2
+ }
+ end
+
+ def test_concat_empty1
+ s1 = ""
+ s2 = "aa".force_encoding("utf-16be")
+ assert_nothing_raised("#{encdump s1} << #{encdump s2}") {
+ s1 << s2
+ }
+ end
+
+ def test_concat_empty2
+ s1 = "aa"
+ s2 = "".force_encoding("utf-16be")
+ assert_nothing_raised("#{encdump s1} << #{encdump s2}") {
+ s1 << s2
+ }
+ end
+
+ def test_concat_nonempty
+ s1 = "aa"
+ s2 = "bb".force_encoding("utf-16be")
+ assert_raise(Encoding::CompatibilityError, "#{encdump s1} << #{encdump s2}") {
+ s1 << s2
+ }
+ end
+
+ def test_chomp
+ s = "\1\n".force_encoding("utf-16be")
+ assert_equal(s, s.chomp, "#{encdump s}.chomp")
+ s = "\0\n".force_encoding("utf-16be")
+ assert_equal("", s.chomp, "#{encdump s}.chomp")
+ s = "\0\r\0\n".force_encoding("utf-16be")
+ assert_equal("", s.chomp, "#{encdump s}.chomp")
+ end
+
+ def test_succ
+ s = "\xff\xff".force_encoding("utf-16be")
+ assert_predicate(s.succ, :valid_encoding?, "#{encdump s}.succ.valid_encoding?")
+
+ s = "\xdb\xff\xdf\xff".force_encoding("utf-16be")
+ assert_predicate(s.succ, :valid_encoding?, "#{encdump s}.succ.valid_encoding?")
+ end
+
+ def test_regexp_union
+ enccall(Regexp, :union, "aa".force_encoding("utf-16be"), "bb".force_encoding("utf-16be"))
+ end
+
+ def test_empty_regexp
+ s = "".force_encoding("utf-16be")
+ assert_equal(Encoding.find("utf-16be"), Regexp.new(s).encoding,
+ "Regexp.new(#{encdump s}).encoding")
+ end
+
+ def test_regexp_match
+ assert_raise(Encoding::CompatibilityError) { Regexp.new("aa".force_encoding("utf-16be")) =~ "aa" }
+ end
+
+ def test_gsub
+ s = "abcd".force_encoding("utf-16be")
+ assert_nothing_raised {
+ s.gsub(Regexp.new(".".encode("utf-16be")), "xy")
+ }
+ s = "ab\0\ncd".force_encoding("utf-16be")
+ assert_raise(Encoding::CompatibilityError) {
+ s.gsub(Regexp.new(".".encode("utf-16be")), "xy")
+ }
+ end
+
+ def test_split_awk
+ s = " ab cd ".encode("utf-16be")
+ r = s.split(" ".encode("utf-16be"))
+ assert_equal(2, r.length)
+ assert_str_equal("ab".encode("utf-16be"), r[0])
+ assert_str_equal("cd".encode("utf-16be"), r[1])
+ end
+
+ def test_count2
+ e = "abc".count("^b")
+ assert_equal(e, "abc".encode("utf-16be").count("^b".encode("utf-16be")))
+ assert_equal(e, "abc".encode("utf-16le").count("^b".encode("utf-16le")))
+ end
+
+ def test_header
+ assert_raise(ArgumentError) { eval("# encoding:utf-16le\nfoo") }
+ assert_raise(ArgumentError) { eval("# encoding:utf-16be\nfoo") }
+ end
+
+
+ def test_is_mbc_newline
+ sl = "f\0o\0o\0\n\0b\0a\0r\0\n\0b\0a\0z\0\n\0".force_encoding("utf-16le")
+ sb = "\0f\0o\0o\0\n\0b\0a\0r\0\n\0b\0a\0z\0\n".force_encoding("utf-16be")
+ al = sl.lines.to_a
+ ab = sb.lines.to_a
+ assert_equal("f\0o\0o\0\n\0".force_encoding("utf-16le"), al.shift)
+ assert_equal("b\0a\0r\0\n\0".force_encoding("utf-16le"), al.shift)
+ assert_equal("b\0a\0z\0\n\0".force_encoding("utf-16le"), al.shift)
+ assert_equal("\0f\0o\0o\0\n".force_encoding("utf-16be"), ab.shift)
+ assert_equal("\0b\0a\0r\0\n".force_encoding("utf-16be"), ab.shift)
+ assert_equal("\0b\0a\0z\0\n".force_encoding("utf-16be"), ab.shift)
+
+ sl = "f\0o\0o\0\n\0".force_encoding("utf-16le")
+ sb = "\0f\0o\0o\0\n".force_encoding("utf-16be")
+ sl2 = "f\0o\0o\0".force_encoding("utf-16le")
+ sb2 = "\0f\0o\0o".force_encoding("utf-16be")
+ assert_equal(sl2, sl.chomp)
+ assert_equal(sl2, sl.chomp.chomp)
+ assert_equal(sb2, sb.chomp)
+ assert_equal(sb2, sb.chomp.chomp)
+
+ sl = "f\0o\0o\0\n".force_encoding("utf-16le")
+ sb = "\0f\0o\0o\n".force_encoding("utf-16be")
+ assert_equal(sl, sl.chomp)
+ assert_equal(sb, sb.chomp)
+ end
+
+ def test_code_to_mbc
+ assert_equal("a\0".force_encoding("utf-16le"), "a".ord.chr("utf-16le"))
+ assert_equal("\0a".force_encoding("utf-16be"), "a".ord.chr("utf-16be"))
+ end
+
+ def utf8_to_utf16(s, e)
+ s.chars.map {|c| c.ord.chr(e) }.join
+ end
+
+ def test_mbc_case_fold
+ rl = Regexp.new(utf8_to_utf16("^(\u3042)(a)\\1\\2$", "utf-16le"), "i")
+ rb = Regexp.new(utf8_to_utf16("^(\u3042)(a)\\1\\2$", "utf-16be"), "i")
+ assert_equal(Encoding.find("utf-16le"), rl.encoding)
+ assert_equal(Encoding.find("utf-16be"), rb.encoding)
+ assert_match(rl, utf8_to_utf16("\u3042a\u3042a", "utf-16le"))
+ assert_match(rb, utf8_to_utf16("\u3042a\u3042a", "utf-16be"))
+ end
+
+ def test_surrogate_pair
+ sl = "\x42\xd8\xb7\xdf".force_encoding("utf-16le")
+ sb = "\xd8\x42\xdf\xb7".force_encoding("utf-16be")
+
+ assert_equal(1, sl.size)
+ assert_equal(1, sb.size)
+ assert_equal(0x20bb7, sl.ord)
+ assert_equal(0x20bb7, sb.ord)
+ assert_equal(sl, 0x20bb7.chr("utf-16le"))
+ assert_equal(sb, 0x20bb7.chr("utf-16be"))
+ assert_equal("", sl.chop)
+ assert_equal("", sb.chop)
+ end
+
+ def test_regexp_escape
+ s = "\0*".force_encoding("UTF-16BE")
+ r = Regexp.new(Regexp.escape(s))
+ assert_match(r, s, "#{encdump(r)} =~ #{encdump(s)}")
+ end
+
+ def test_casecmp2
+ assert_equal(0, "\0A".force_encoding("UTF-16BE").casecmp("\0a".force_encoding("UTF-16BE")))
+ assert_not_equal(0, "\0A".force_encoding("UTF-16LE").casecmp("\0a".force_encoding("UTF-16LE")))
+ assert_not_equal(0, "A\0".force_encoding("UTF-16BE").casecmp("a\0".force_encoding("UTF-16BE")))
+ assert_equal(0, "A\0".force_encoding("UTF-16LE").casecmp("a\0".force_encoding("UTF-16LE")))
+
+ ary = ["01".force_encoding("UTF-16LE"),
+ "10".force_encoding("UTF-16LE")]
+ e = ary.sort {|x,y| x <=> y }
+ a = ary.sort {|x,y| x.casecmp(y) }
+ assert_equal(e, a)
+ end
+end
diff --git a/jni/ruby/test/ruby/enc/test_utf32.rb b/jni/ruby/test/ruby/enc/test_utf32.rb
new file mode 100644
index 0000000..29a2240
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_utf32.rb
@@ -0,0 +1,93 @@
+require 'test/unit'
+
+class TestUTF32 < Test::Unit::TestCase
+ def encdump(str)
+ d = str.dump
+ if /\.force_encoding\("[A-Za-z0-9.:_+-]*"\)\z/ =~ d
+ d
+ else
+ "#{d}.force_encoding(#{str.encoding.name.dump})"
+ end
+ end
+
+ def assert_str_equal(expected, actual, message=nil)
+ full_message = build_message(message, <<EOT)
+#{encdump expected} expected but not equal to
+#{encdump actual}.
+EOT
+ assert_equal(expected, actual, full_message)
+ end
+
+ def test_substr
+ assert_str_equal(
+ "abcdefgh".force_encoding("utf-32le"),
+ "abcdefgh".force_encoding("utf-32le")[0,3])
+ assert_str_equal(
+ "abcdefgh".force_encoding("utf-32be"),
+ "abcdefgh".force_encoding("utf-32be")[0,3])
+ end
+
+ def test_mbc_len
+ al = "abcdefghijkl".force_encoding("utf-32le").each_char.to_a
+ ab = "abcdefghijkl".force_encoding("utf-32be").each_char.to_a
+ assert_equal("abcd".force_encoding("utf-32le"), al.shift)
+ assert_equal("efgh".force_encoding("utf-32le"), al.shift)
+ assert_equal("ijkl".force_encoding("utf-32le"), al.shift)
+ assert_equal("abcd".force_encoding("utf-32be"), ab.shift)
+ assert_equal("efgh".force_encoding("utf-32be"), ab.shift)
+ assert_equal("ijkl".force_encoding("utf-32be"), ab.shift)
+ end
+
+ def ascii_to_utf16le(s)
+ s.unpack("C*").map {|x| [x,0,0,0] }.flatten.pack("C*").force_encoding("utf-32le")
+ end
+
+ def ascii_to_utf16be(s)
+ s.unpack("C*").map {|x| [0,0,0,x] }.flatten.pack("C*").force_encoding("utf-32be")
+ end
+
+ def test_mbc_newline
+ al = ascii_to_utf16le("foo\nbar\nbaz\n").lines.to_a
+ ab = ascii_to_utf16be("foo\nbar\nbaz\n").lines.to_a
+
+ assert_equal(ascii_to_utf16le("foo\n"), al.shift)
+ assert_equal(ascii_to_utf16le("bar\n"), al.shift)
+ assert_equal(ascii_to_utf16le("baz\n"), al.shift)
+ assert_equal(ascii_to_utf16be("foo\n"), ab.shift)
+ assert_equal(ascii_to_utf16be("bar\n"), ab.shift)
+ assert_equal(ascii_to_utf16be("baz\n"), ab.shift)
+
+ sl = "a\0".force_encoding("utf-32le")
+ sb = "a\0".force_encoding("utf-32be")
+ assert_equal(sl, sl.chomp)
+ assert_equal(sb, sb.chomp)
+ end
+
+ def test_mbc_to_code
+ sl = "a\0\0\0".force_encoding("utf-32le")
+ sb = "\0\0\0a".force_encoding("utf-32be")
+ assert_equal("a".ord, sl.ord)
+ assert_equal("a".ord, sb.ord)
+ end
+
+ def utf8_to_utf32(s, e)
+ s.chars.map {|c| c.ord.chr(e) }.join
+ end
+
+ def test_mbc_case_fold
+ rl = Regexp.new(utf8_to_utf32("^(\u3042)(a)\\1\\2$", "utf-32le"), "i")
+ rb = Regexp.new(utf8_to_utf32("^(\u3042)(a)\\1\\2$", "utf-32be"), "i")
+ assert_equal(Encoding.find("utf-32le"), rl.encoding)
+ assert_equal(Encoding.find("utf-32be"), rb.encoding)
+ assert_match(rl, utf8_to_utf32("\u3042a\u3042a", "utf-32le"))
+ assert_match(rb, utf8_to_utf32("\u3042a\u3042a", "utf-32be"))
+ end
+
+ def test_code_to_mbc
+ sl = "a\0\0\0".force_encoding("utf-32le")
+ sb = "\0\0\0a".force_encoding("utf-32be")
+ assert_equal(sl, "a".ord.chr("utf-32le"))
+ assert_equal(sb, "a".ord.chr("utf-32be"))
+ end
+end
+
diff --git a/jni/ruby/test/ruby/enc/test_windows_1251.rb b/jni/ruby/test/ruby/enc/test_windows_1251.rb
new file mode 100644
index 0000000..6fbf315
--- /dev/null
+++ b/jni/ruby/test/ruby/enc/test_windows_1251.rb
@@ -0,0 +1,16 @@
+# encoding:windows-1251
+
+require "test/unit"
+
+class TestWindows1251 < Test::Unit::TestCase
+ def test_windows_1251
+ (0xc0..0xdf).each do |c|
+ c1 = c.chr("windows-1251")
+ c2 = (c + 0x20).chr("windows-1251")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/endblockwarn_rb b/jni/ruby/test/ruby/endblockwarn_rb
new file mode 100644
index 0000000..7b7f97f
--- /dev/null
+++ b/jni/ruby/test/ruby/endblockwarn_rb
@@ -0,0 +1,12 @@
+def end1
+ END {}
+end
+
+end1
+
+eval <<EOE
+ def end2
+ END {}
+ end
+EOE
+
diff --git a/jni/ruby/test/ruby/lbtest.rb b/jni/ruby/test/ruby/lbtest.rb
new file mode 100644
index 0000000..ae047fb
--- /dev/null
+++ b/jni/ruby/test/ruby/lbtest.rb
@@ -0,0 +1,49 @@
+require 'thread'
+
+class LocalBarrier
+ def initialize(n)
+ @wait = Queue.new
+ @done = Queue.new
+ @keeper = begin_keeper(n)
+ end
+
+ def sync
+ @done.push(true)
+ @wait.pop
+ end
+
+ def join
+ @keeper.join
+ end
+
+ private
+ def begin_keeper(n)
+ Thread.start do
+ n.times do
+ @done.pop
+ end
+ n.times do
+ @wait.push(true)
+ end
+ end
+ end
+end
+
+n = 10
+
+lb = LocalBarrier.new(n)
+
+(n - 1).times do |i|
+ Thread.start do
+ sleep((rand(n) + 1) / 100.0)
+ print "#{i}: done\n"
+ lb.sync
+ print "#{i}: cont\n"
+ end
+end
+
+lb.sync
+print "#{n-1}: cont\n"
+# lb.join # [ruby-dev:30653]
+
+print "exit.\n"
diff --git a/jni/ruby/test/ruby/marshaltestlib.rb b/jni/ruby/test/ruby/marshaltestlib.rb
new file mode 100644
index 0000000..665d365
--- /dev/null
+++ b/jni/ruby/test/ruby/marshaltestlib.rb
@@ -0,0 +1,436 @@
+# coding: utf-8
+module MarshalTestLib
+ # include this module to a Test::Unit::TestCase and define encode(o) and
+ # decode(s) methods. e.g.
+ #
+ # def encode(o)
+ # SOAPMarshal.dump(o)
+ # end
+ #
+ # def decode(s)
+ # SOAPMarshal.load(s)
+ # end
+
+ NegativeZero = (-1.0 / (1.0 / 0.0))
+
+ module Mod1; end
+ module Mod2; end
+
+ def marshaltest(o1)
+ str = encode(o1)
+ print str.dump, "\n" if $DEBUG
+ o2 = decode(str)
+ o2
+ end
+
+ def marshal_equal(o1, msg = nil)
+ msg = msg ? msg + "(#{ caller[0] })" : caller[0]
+ o2 = marshaltest(o1)
+ assert_equal(o1.class, o2.class, msg)
+ iv1 = o1.instance_variables.sort
+ iv2 = o2.instance_variables.sort
+ assert_equal(iv1, iv2)
+ val1 = iv1.map {|var| o1.instance_eval {eval var.to_s}}
+ val2 = iv1.map {|var| o2.instance_eval {eval var.to_s}}
+ assert_equal(val1, val2, msg)
+ if block_given?
+ assert_equal(yield(o1), yield(o2), msg)
+ else
+ assert_equal(o1, o2, msg)
+ end
+ end
+
+ def marshal_equal_with_ancestry(o1, msg = nil)
+ marshal_equal(o1, msg) do |o|
+ ancestry = o.singleton_class.ancestors
+ ancestry[ancestry.index(o.singleton_class)] = :singleton_class
+ ancestry
+ end
+ end
+
+ class MyObject; def initialize(v) @v = v end; attr_reader :v; end
+ def test_object
+ o1 = Object.new
+ o1.instance_eval { @iv = 1 }
+ marshal_equal(o1) {|o| o.instance_eval { @iv }}
+ end
+
+ def test_object_subclass
+ marshal_equal(MyObject.new(2)) {|o| o.v}
+ end
+
+ def test_object_extend
+ o1 = Object.new
+ o1.extend(Mod1)
+ marshal_equal_with_ancestry(o1)
+ o1.extend(Mod2)
+ marshal_equal_with_ancestry(o1)
+ end
+
+ def test_object_subclass_extend
+ o1 = MyObject.new(2)
+ o1.extend(Mod1)
+ marshal_equal_with_ancestry(o1)
+ o1.extend(Mod2)
+ marshal_equal_with_ancestry(o1)
+ end
+
+ def test_object_prepend
+ bug8041 = '[ruby-core:53202] [Bug #8041]'
+
+ o1 = MyObject.new(42)
+ o1.singleton_class.class_eval {prepend Mod1}
+ assert_nothing_raised(ArgumentError, bug8041) {
+ marshal_equal_with_ancestry(o1, bug8041)
+ }
+ end
+
+ class MyArray < Array
+ def initialize(v, *args)
+ super(args)
+ @v = v
+ end
+ end
+ def test_array
+ marshal_equal(5)
+ marshal_equal([1,2,3])
+ end
+
+ def test_array_subclass
+ marshal_equal(MyArray.new(0, 1, 2, 3))
+ end
+
+ def test_array_ivar
+ o1 = Array.new
+ o1.instance_eval { @iv = 1 }
+ marshal_equal(o1) {|o| o.instance_eval { @iv }}
+ end
+
+ class MyException < Exception; def initialize(v, *args) super(*args); @v = v; end; attr_reader :v; end
+ def test_exception
+ marshal_equal(Exception.new('foo')) {|o| o.message}
+ marshal_equal(assert_raise(NoMethodError) {no_such_method()}) {|o| o.message}
+ end
+
+ def test_exception_subclass
+ marshal_equal(MyException.new(20, "bar")) {|o| [o.message, o.v]}
+ end
+
+ def test_false
+ marshal_equal(false)
+ end
+
+ class MyHash < Hash; def initialize(v, *args) super(*args); @v = v; end end
+ def test_hash
+ marshal_equal({1=>2, 3=>4})
+ end
+
+ def test_hash_default
+ h = Hash.new(:default)
+ h[5] = 6
+ marshal_equal(h)
+ end
+
+ def test_hash_subclass
+ h = MyHash.new(7, 8)
+ h[4] = 5
+ marshal_equal(h)
+ end
+
+ def test_hash_default_proc
+ h = Hash.new {}
+ assert_raise(TypeError) { marshaltest(h) }
+ end
+
+ def test_hash_ivar
+ o1 = Hash.new
+ o1.instance_eval { @iv = 1 }
+ marshal_equal(o1) {|o| o.instance_eval { @iv }}
+ end
+
+ def test_hash_extend
+ o1 = Hash.new
+ o1.extend(Mod1)
+ marshal_equal_with_ancestry(o1)
+ o1.extend(Mod2)
+ marshal_equal_with_ancestry(o1)
+ end
+
+ def test_hash_subclass_extend
+ o1 = MyHash.new(2)
+ o1.extend(Mod1)
+ marshal_equal_with_ancestry(o1)
+ o1.extend(Mod2)
+ marshal_equal_with_ancestry(o1)
+ end
+
+ def test_bignum
+ marshal_equal(-0x4000_0000_0000_0001)
+ marshal_equal(-0x4000_0001)
+ marshal_equal(0x4000_0000)
+ marshal_equal(0x4000_0000_0000_0000)
+ end
+
+ def test_fixnum
+ marshal_equal(-0x4000_0000)
+ marshal_equal(-0x3fff_ffff)
+ marshal_equal(-1)
+ marshal_equal(0)
+ marshal_equal(1)
+ marshal_equal(0x3fff_ffff)
+ end
+
+ def test_float
+ marshal_equal(-1.0)
+ marshal_equal(0.0)
+ marshal_equal(1.0)
+ end
+
+ def test_float_inf_nan
+ marshal_equal(1.0/0.0)
+ marshal_equal(-1.0/0.0)
+ marshal_equal(0.0/0.0) {|o| o.nan?}
+ marshal_equal(NegativeZero) {|o| 1.0/o}
+ end
+
+ class MyRange < Range; def initialize(v, *args) super(*args); @v = v; end end
+ def test_range
+ marshal_equal(1..2)
+ marshal_equal(1...3)
+ end
+
+ def test_range_subclass
+ marshal_equal(MyRange.new(4,5,8, false))
+ end
+
+ class MyRegexp < Regexp; def initialize(v, *args) super(*args); @v = v; end end
+ def test_regexp
+ marshal_equal(/a/)
+ marshal_equal(/A/i)
+ marshal_equal(/A/mx)
+ marshal_equal(/a\u3042/)
+ marshal_equal(/aあ/)
+ assert_equal(Regexp.new("あ".force_encoding("ASCII-8BIT")),
+ Marshal.load("\004\b/\b\343\201\202\000"))
+ assert_equal(/au3042/, Marshal.load("\004\b/\fa\\u3042\000"))
+ #assert_equal(/au3042/u, Marshal.load("\004\b/\fa\\u3042@")) # spec
+ end
+
+ def test_regexp_subclass
+ marshal_equal(MyRegexp.new(10, "a"))
+ end
+
+ class MyString < String; def initialize(v, *args) super(*args); @v = v; end end
+ def test_string
+ marshal_equal("abc")
+ end
+
+ def test_string_ivar
+ o1 = ""
+ o1.instance_eval { @iv = 1 }
+ marshal_equal(o1) {|o| o.instance_eval { @iv }}
+ end
+
+ def test_string_subclass
+ marshal_equal(MyString.new(10, "a"))
+ end
+
+ def test_string_subclass_cycle
+ str = MyString.new(10, "b")
+ str.instance_eval { @v = str }
+ marshal_equal(str) { |o|
+ assert_same(o, o.instance_eval { @v })
+ o.instance_eval { @v }
+ }
+ end
+
+ def test_string_subclass_extend
+ o = "abc"
+ o.extend(Mod1)
+ str = MyString.new(o, "c")
+ marshal_equal(str) { |v|
+ assert_kind_of(Mod1, v.instance_eval { @v })
+ }
+ end
+
+ MyStruct = Struct.new("MyStruct", :a, :b)
+ class MySubStruct < MyStruct; def initialize(v, *args) super(*args); @v = v; end end
+ def test_struct
+ marshal_equal(MyStruct.new(1,2))
+ end
+
+ def test_struct_subclass
+ marshal_equal(MySubStruct.new(10,1,2))
+ end
+
+ def test_struct_ivar
+ o1 = MyStruct.new
+ o1.instance_eval { @iv = 1 }
+ marshal_equal(o1) {|o| o.instance_eval { @iv }}
+ end
+
+ def test_struct_subclass_extend
+ o1 = MyStruct.new
+ o1.extend(Mod1)
+ marshal_equal_with_ancestry(o1)
+ o1.extend(Mod2)
+ marshal_equal_with_ancestry(o1)
+ end
+
+ def test_symbol
+ marshal_equal(:a)
+ marshal_equal(:a?)
+ marshal_equal(:a!)
+ marshal_equal(:a=)
+ marshal_equal(:|)
+ marshal_equal(:^)
+ marshal_equal(:&)
+ marshal_equal(:<=>)
+ marshal_equal(:==)
+ marshal_equal(:===)
+ marshal_equal(:=~)
+ marshal_equal(:>)
+ marshal_equal(:>=)
+ marshal_equal(:<)
+ marshal_equal(:<=)
+ marshal_equal(:<<)
+ marshal_equal(:>>)
+ marshal_equal(:+)
+ marshal_equal(:-)
+ marshal_equal(:*)
+ marshal_equal(:/)
+ marshal_equal(:%)
+ marshal_equal(:**)
+ marshal_equal(:~)
+ marshal_equal(:+@)
+ marshal_equal(:-@)
+ marshal_equal(:[])
+ marshal_equal(:[]=)
+ marshal_equal(:`) #`
+ marshal_equal("a b".intern)
+ end
+
+ class MyTime < Time; def initialize(v, *args) super(*args); @v = v; end end
+ def test_time
+ # once there was a bug caused by usec overflow. try a little harder.
+ 10.times do
+ t = Time.now
+ marshal_equal(t, t.usec.to_s)
+ end
+ end
+
+ def test_time_subclass
+ marshal_equal(MyTime.new(10))
+ end
+
+ def test_time_ivar
+ o1 = Time.now
+ o1.instance_eval { @iv = 1 }
+ marshal_equal(o1) {|o| o.instance_eval { @iv }}
+ end
+
+ def test_time_in_array
+ t = Time.now
+ assert_equal([t,t], Marshal.load(Marshal.dump([t, t])), "[ruby-dev:34159]")
+ end
+
+ def test_true
+ marshal_equal(true)
+ end
+
+ def test_nil
+ marshal_equal(nil)
+ end
+
+ def test_share
+ o = [:share]
+ o1 = [o, o]
+ o2 = marshaltest(o1)
+ assert_same(o2.first, o2.last)
+ end
+
+ class CyclicRange < Range
+ def <=>(other); true; end
+ end
+ def test_range_cyclic
+ return unless CyclicRange.respond_to?(:allocate) # test for 1.8
+ o1 = CyclicRange.allocate
+ o1.instance_eval { initialize(o1, o1) }
+ o2 = marshaltest(o1)
+ assert_same(o2, o2.begin)
+ assert_same(o2, o2.end)
+ end
+
+ def test_singleton
+ o = Object.new
+ def o.m() end
+ assert_raise(TypeError) { marshaltest(o) }
+
+ bug8043 = '[ruby-core:53206] [Bug #8043]'
+ class << o; prepend Mod1; end
+ assert_raise(TypeError, bug8043) {marshaltest(o)}
+
+ o = Object.new
+ c = class << o
+ @v = 1
+ class C; self; end
+ end
+ assert_raise(TypeError) { marshaltest(o) }
+ assert_raise(TypeError) { marshaltest(c) }
+ assert_raise(TypeError) { marshaltest(ARGF) }
+ assert_raise(TypeError) { marshaltest(ENV) }
+ end
+
+ def test_extend
+ o = Object.new
+ o.extend Mod1
+ marshal_equal(o) { |obj| obj.kind_of? Mod1 }
+ o = Object.new
+ o.extend Mod1
+ o.extend Mod2
+ marshal_equal_with_ancestry(o)
+ o = Object.new
+ o.extend Module.new
+ assert_raise(TypeError) { marshaltest(o) }
+ end
+
+ def test_extend_string
+ o = ""
+ o.extend Mod1
+ marshal_equal(o) { |obj| obj.kind_of? Mod1 }
+ o = ""
+ o.extend Mod1
+ o.extend Mod2
+ marshal_equal_with_ancestry(o)
+ o = ""
+ o.extend Module.new
+ assert_raise(TypeError) { marshaltest(o) }
+ end
+
+ def test_anonymous
+ c = Class.new
+ assert_raise(TypeError) { marshaltest(c) }
+ o = c.new
+ assert_raise(TypeError) { marshaltest(o) }
+ m = Module.new
+ assert_raise(TypeError) { marshaltest(m) }
+ end
+
+ def test_string_empty
+ marshal_equal("")
+ end
+
+ def test_string_crlf
+ marshal_equal("\r\n")
+ end
+
+ def test_string_escape
+ marshal_equal("\0<;;>\1;;")
+ end
+
+ MyStruct2 = Struct.new(:a, :b)
+ def test_struct_toplevel
+ o = MyStruct2.new(1,2)
+ marshal_equal(o)
+ end
+end
diff --git a/jni/ruby/test/ruby/sentence.rb b/jni/ruby/test/ruby/sentence.rb
new file mode 100644
index 0000000..50f42d6
--- /dev/null
+++ b/jni/ruby/test/ruby/sentence.rb
@@ -0,0 +1,668 @@
+# == sentence library
+#
+# = Features
+#
+# * syntax based sentences generation
+# * sentence operations such as substitution.
+#
+# = Example
+#
+# Some arithmetic expressions using "+", "-", "*" and "/" are generated as follows.
+#
+# require 'sentence'
+# Sentence.each({
+# :exp => [["num"],
+# [:exp, "+", :exp],
+# [:exp, "-", :exp],
+# [:exp, "*", :exp],
+# [:exp, "/", :exp]]
+# }, :exp, 2) {|sent| p sent }
+# #=>
+# #<Sentence: "num">
+# #<Sentence: ("num") "+" ("num")>
+# #<Sentence: ("num") "+" (("num") "+" ("num"))>
+# #<Sentence: ("num") "+" (("num") "-" ("num"))>
+# #<Sentence: ("num") "+" (("num") "*" ("num"))>
+# #<Sentence: ("num") "+" (("num") "/" ("num"))>
+# #<Sentence: (("num") "+" ("num")) "+" ("num")>
+# ...
+#
+# Sentence.each takes 3 arguments.
+# The first argument is the syntax for the expressions.
+# The second argument, :exp, is a generating nonterminal.
+# The third argument, 2, limits derivation to restrict results finitely.
+#
+# Some arithmetic expressions including parenthesis can be generated as follows.
+#
+# syntax = {
+# :factor => [["n"],
+# ["(", :exp, ")"]],
+# :term => [[:factor],
+# [:term, "*", :factor],
+# [:term, "/", :factor]],
+# :exp => [[:term],
+# [:exp, "+", :term],
+# [:exp, "-", :term]]
+# }
+# Sentence.each(syntax, :exp, 2) {|sent| p sent }
+# #=>
+# #<Sentence: (("n"))>
+# #<Sentence: (("(" ((("n"))) ")"))>
+# #<Sentence: (("(" ((("(" ((("n"))) ")"))) ")"))>
+# #<Sentence: (("(" (((("n")) "*" ("n"))) ")"))>
+# #<Sentence: (("(" (((("n")) "/" ("n"))) ")"))>
+# #<Sentence: (("(" (((("n"))) "+" (("n"))) ")"))>
+# #<Sentence: (("(" (((("n"))) "-" (("n"))) ")"))>
+# #<Sentence: ((("n")) "*" ("n"))>
+# #<Sentence: ((("n")) "*" ("(" ((("n"))) ")"))>
+# ...
+#
+# Sentence#to_s can be used to concatenate strings
+# in a sentence:
+#
+# Sentence.each(syntax, :exp, 2) {|sent| p sent.to_s }
+# #=>
+# "n"
+# "(n)"
+# "((n))"
+# "(n*n)"
+# "(n/n)"
+# "(n+n)"
+# "(n-n)"
+# "n*n"
+# "n*(n)"
+# ...
+#
+
+# Sentence() instantiates a sentence object.
+#
+# Sentence("foo", "bar")
+# #=> #<Sentence: "foo" "bar">
+#
+# Sentence("foo", ["bar", "baz"])
+# #=> #<Sentence: "foo" ("bar" "baz")>
+#
+def Sentence(*ary)
+ Sentence.new(ary)
+end
+
+# Sentence class represents a tree with string leaves.
+#
+class Sentence
+ # _ary_ represents a tree.
+ # It should be a possibly nested array which contains strings.
+ #
+ # Note that _ary_ is not copied.
+ # Don't modify _ary_ after the sentence object is instantiated.
+ #
+ # Sentence.new(["a", "pen"])
+ # #<Sentence: "a" "pen">
+ #
+ # Sentence.new(["I", "have", ["a", "pen"]])
+ # #<Sentence: "I" "have" ("a" "pen")>
+ #
+ def initialize(ary)
+ @sent = ary
+ end
+
+ # returns a string which is concatenation of all strings.
+ # No separator is used.
+ #
+ # Sentence("2", "+", "3").to_s
+ # "2+3"
+ #
+ # Sentence("2", "+", ["3", "*", "5"]).to_s
+ # "2+3*5"
+ #
+ def to_s
+ @sent.join('')
+ end
+
+ # returns a string which is concatenation of all strings separated by _sep_.
+ # If _sep_ is not given, single space is used.
+ #
+ # Sentence("I", "have", ["a", "pen"]).join
+ # "I have a pen"
+ #
+ # Sentence("I", "have", ["a", "pen"]).join("/")
+ # "I/have/a/pen"
+ #
+ # Sentence("a", [], "b").join("/")
+ # "a/b"
+ #
+ def join(sep=' ')
+ @sent.flatten.join(sep)
+ end
+
+ # returns a tree as a nested array.
+ #
+ # Note that the result is not copied.
+ # Don't modify the result.
+ #
+ # Sentence(["foo", "bar"], "baz").to_a
+ # #=> [["foo", "bar"], "baz"]
+ #
+ def to_a
+ @sent
+ end
+
+ # returns <i>i</i>th element as a sentence or string.
+ #
+ # s = Sentence(["foo", "bar"], "baz")
+ # s #=> #<Sentence: ("foo" "bar") "baz">
+ # s[0] #=> #<Sentence: "foo" "bar">
+ # s[1] #=> "baz"
+ #
+ def [](i)
+ e = @sent[i]
+ e.respond_to?(:to_ary) ? Sentence.new(e) : e
+ end
+
+ # returns the number of top level elements.
+ #
+ # Sentence.new(%w[foo bar]).length
+ # #=> 2
+ #
+ # Sentence(%w[2 * 7], "+", %w[3 * 5]).length
+ # #=> 3
+ #
+ def length
+ @sent.length
+ end
+
+ # iterates over children.
+ #
+ # Sentence(%w[2 * 7], "+", %w[3 * 5]).each {|v| p v }
+ # #=>
+ # #<Sentence: "2" "*" "7">
+ # "+"
+ # #<Sentence: "3" "*" "5">
+ #
+ def each # :yield: element
+ @sent.each_index {|i|
+ yield self[i]
+ }
+ end
+ include Enumerable
+
+ def inspect
+ "#<#{self.class}: #{inner_inspect(@sent, '')}>"
+ end
+
+ # :stopdoc:
+ def inner_inspect(ary, r)
+ first = true
+ ary.each {|obj|
+ r << ' ' if !first
+ first = false
+ if obj.respond_to? :to_ary
+ r << '('
+ inner_inspect(obj, r)
+ r << ')'
+ else
+ r << obj.inspect
+ end
+ }
+ r
+ end
+ # :startdoc:
+
+ # returns new sentence object which
+ # _target_ is substituted by the block.
+ #
+ # Sentence#subst invokes <tt>_target_ === _string_</tt> for each
+ # string in the sentence.
+ # The strings which === returns true are substituted by the block.
+ # The block is invoked with the substituting string.
+ #
+ # Sentence.new(%w[2 + 3]).subst("+") { "*" }
+ # #<Sentence: "2" "*" "3">
+ #
+ # Sentence.new(%w[2 + 3]).subst(/\A\d+\z/) {|s| ((s.to_i)*2).to_s }
+ # #=> #<Sentence: "4" "+" "6">
+ #
+ def subst(target, &b) # :yield: string
+ Sentence.new(subst_rec(@sent, target, &b))
+ end
+
+ # :stopdoc:
+ def subst_rec(obj, target, &b)
+ if obj.respond_to? :to_ary
+ a = []
+ obj.each {|e| a << subst_rec(e, target, &b) }
+ a
+ elsif target === obj
+ yield obj
+ else
+ obj
+ end
+ end
+ # :startdoc:
+
+ # find a subsentence and return it.
+ # The block is invoked for each subsentence in preorder manner.
+ # The first subsentence which the block returns true is returned.
+ #
+ # Sentence(%w[2 * 7], "+", %w[3 * 5]).find_subtree {|s| s[1] == "*" }
+ # #=> #<Sentence: "2" "*" "7">
+ #
+ def find_subtree(&b) # :yield: sentence
+ find_subtree_rec(@sent, &b)
+ end
+
+ # :stopdoc:
+ def find_subtree_rec(obj, &b)
+ if obj.respond_to? :to_ary
+ s = Sentence.new(obj)
+ if b.call s
+ return s
+ else
+ obj.each {|e|
+ r = find_subtree_rec(e, &b)
+ return r if r
+ }
+ end
+ end
+ nil
+ end
+ # :startdoc:
+
+ # returns a new sentence object which expands according to the condition
+ # given by the block.
+ #
+ # The block is invoked for each subsentence.
+ # The subsentences which the block returns true are
+ # expanded into parent.
+ #
+ # s = Sentence(%w[2 * 7], "+", %w[3 * 5])
+ # #=> #<Sentence: ("2" "*" "7") "+" ("3" "*" "5")>
+ #
+ # s.expand { true }
+ # #=> #<Sentence: "2" "*" "7" "+" "3" "*" "5">
+ #
+ # s.expand {|s| s[0] == "3" }
+ # #=> #<Sentence: (("2" "*" "7") "+" "3" "*" "5")>
+ #
+ def expand(&b) # :yield: sentence
+ Sentence.new(expand_rec(@sent, &b))
+ end
+
+ # :stopdoc:
+ def expand_rec(obj, r=[], &b)
+ if obj.respond_to? :to_ary
+ obj.each {|o|
+ s = Sentence.new(o)
+ if b.call s
+ expand_rec(o, r, &b)
+ else
+ a = []
+ expand_rec(o, a, &b)
+ r << a
+ end
+ }
+ else
+ r << obj
+ end
+ r
+ end
+ # :startdoc:
+
+ # Sentence.each generates sentences
+ # by deriving the start symbol _sym_ using _syntax_.
+ # The derivation is restricted by an positive integer _limit_ to
+ # avoid infinite generation.
+ #
+ # Sentence.each yields the block with a generated sentence.
+ #
+ # Sentence.each({
+ # :exp => [["n"],
+ # [:exp, "+", :exp],
+ # [:exp, "*", :exp]]
+ # }, :exp, 1) {|sent| p sent }
+ # #=>
+ # #<Sentence: "n">
+ # #<Sentence: ("n") "+" ("n")>
+ # #<Sentence: ("n") "*" ("n")>
+ #
+ # Sentence.each({
+ # :exp => [["n"],
+ # [:exp, "+", :exp],
+ # [:exp, "*", :exp]]
+ # }, :exp, 2) {|sent| p sent }
+ # #=>
+ # #<Sentence: "n">
+ # #<Sentence: ("n") "+" ("n")>
+ # #<Sentence: ("n") "+" (("n") "+" ("n"))>
+ # #<Sentence: ("n") "+" (("n") "*" ("n"))>
+ # #<Sentence: (("n") "+" ("n")) "+" ("n")>
+ # #<Sentence: (("n") "*" ("n")) "+" ("n")>
+ # #<Sentence: ("n") "*" ("n")>
+ # #<Sentence: ("n") "*" (("n") "+" ("n"))>
+ # #<Sentence: ("n") "*" (("n") "*" ("n"))>
+ # #<Sentence: (("n") "+" ("n")) "*" ("n")>
+ # #<Sentence: (("n") "*" ("n")) "*" ("n")>
+ #
+ def Sentence.each(syntax, sym, limit)
+ Gen.new(syntax).each_tree(sym, limit) {|tree|
+ yield Sentence.new(tree)
+ }
+ end
+
+ # Sentence.expand_syntax returns an expanded syntax:
+ # * No rule derives to empty sequence
+ # * Underivable rule simplified
+ # * No channel rule
+ # * Symbols which has zero or one choices are not appered in rhs.
+ #
+ # Note that the rules which can derive empty and non-empty
+ # sequences are modified to derive only non-empty sequences.
+ #
+ # Sentence.expand_syntax({
+ # :underivable1 => [],
+ # :underivable2 => [[:underivable1]],
+ # :underivable3 => [[:underivable3]],
+ # :empty_only1 => [[]],
+ # :empty_only2 => [[:just_empty1, :just_empty1]],
+ # :empty_or_not => [[], ["foo"]],
+ # :empty_or_not_2 => [[:empty_or_not, :empty_or_not]],
+ # :empty_or_not_3 => [[:empty_or_not, :empty_or_not, :empty_or_not]],
+ # :empty_or_not_4 => [[:empty_or_not_2, :empty_or_not_2]],
+ # :channel1 => [[:channeled_data]],
+ # :channeled_data => [["a", "b"], ["c", "d"]],
+ # :single_choice => [["single", "choice"]],
+ # :single_choice_2 => [[:single_choice, :single_choice]],
+ # })
+ # #=>
+ # {
+ # :underivable1=>[], # underivable rules are simplified to [].
+ # :underivable2=>[],
+ # :underivable3=>[],
+ # :empty_only1=>[], # derivation to empty sequence are removed.
+ # :empty_only2=>[],
+ # :empty_or_not=>[["foo"]], # empty sequences are removed too.
+ # :empty_or_not_2=>[["foo"], ["foo", "foo"]],
+ # :empty_or_not_3=>[["foo"], ["foo", "foo"], ["foo", "foo", "foo"]],
+ # :empty_or_not_4=> [["foo"], ["foo", "foo"], [:empty_or_not_2, :empty_or_not_2]],
+ # :channel1=>[["a", "b"], ["c", "d"]], # channel rules are removed.
+ # :channeled_data=>[["a", "b"], ["c", "d"]],
+ # :single_choice=>[["single", "choice"]], # single choice rules are expanded.
+ # :single_choice_2=>[["single", "choice", "single", "choice"]],
+ # }
+ #
+ # Sentence.expand_syntax({
+ # :factor => [["n"],
+ # ["(", :exp, ")"]],
+ # :term => [[:factor],
+ # [:term, "*", :factor],
+ # [:term, "/", :factor]],
+ # :exp => [[:term],
+ # [:exp, "+", :term],
+ # [:exp, "-", :term]]
+ # })
+ # #=>
+ # {:exp=> [["n"],
+ # ["(", :exp, ")"],
+ # [:exp, "+", :term],
+ # [:exp, "-", :term],
+ # [:term, "*", :factor],
+ # [:term, "/", :factor]],
+ # :factor=> [["n"],
+ # ["(", :exp, ")"]],
+ # :term=> [["n"],
+ # ["(", :exp, ")"],
+ # [:term, "*", :factor],
+ # [:term, "/", :factor]]
+ # }
+ #
+ def Sentence.expand_syntax(syntax)
+ Sentence::Gen.expand_syntax(syntax)
+ end
+
+ # :stopdoc:
+ class Gen
+ def Gen.each_tree(syntax, sym, limit, &b)
+ Gen.new(syntax).each_tree(sym, limit, &b)
+ end
+
+ def Gen.each_string(syntax, sym, limit, &b)
+ Gen.new(syntax).each_string(sym, limit, &b)
+ end
+
+ def initialize(syntax)
+ @syntax = syntax
+ end
+
+ def self.expand_syntax(syntax)
+ syntax = simplify_underivable_rules(syntax)
+ syntax = simplify_emptyonly_rules(syntax)
+ syntax = make_rules_no_empseq(syntax)
+ syntax = expand_channel_rules(syntax)
+
+ syntax = expand_noalt_rules(syntax)
+ syntax = reorder_rules(syntax)
+ end
+
+ def self.simplify_underivable_rules(syntax)
+ deribable_syms = {}
+ changed = true
+ while changed
+ changed = false
+ syntax.each {|sym, rules|
+ next if deribable_syms[sym]
+ rules.each {|rhs|
+ if rhs.all? {|e| String === e || deribable_syms[e] }
+ deribable_syms[sym] = true
+ changed = true
+ break
+ end
+ }
+ }
+ end
+ result = {}
+ syntax.each {|sym, rules|
+ if deribable_syms[sym]
+ rules2 = []
+ rules.each {|rhs|
+ rules2 << rhs if rhs.all? {|e| String === e || deribable_syms[e] }
+ }
+ result[sym] = rules2.uniq
+ else
+ result[sym] = []
+ end
+ }
+ result
+ end
+
+ def self.simplify_emptyonly_rules(syntax)
+ justempty_syms = {}
+ changed = true
+ while changed
+ changed = false
+ syntax.each {|sym, rules|
+ next if justempty_syms[sym]
+ if !rules.empty? && rules.all? {|rhs| rhs.all? {|e| justempty_syms[e] } }
+ justempty_syms[sym] = true
+ changed = true
+ end
+ }
+ end
+ result = {}
+ syntax.each {|sym, rules|
+ result[sym] = rules.map {|rhs| rhs.reject {|e| justempty_syms[e] } }.uniq
+ }
+ result
+ end
+
+ def self.expand_emptyable_syms(rhs, emptyable_syms)
+ if rhs.empty?
+ yield []
+ else
+ first = rhs[0]
+ rest = rhs[1..-1]
+ if emptyable_syms[first]
+ expand_emptyable_syms(rest, emptyable_syms) {|rhs2|
+ yield [first] + rhs2
+ yield rhs2
+ }
+ else
+ expand_emptyable_syms(rest, emptyable_syms) {|rhs2|
+ yield [first] + rhs2
+ }
+ end
+ end
+ end
+
+ def self.make_rules_no_empseq(syntax)
+ emptyable_syms = {}
+ changed = true
+ while changed
+ changed = false
+ syntax.each {|sym, rules|
+ next if emptyable_syms[sym]
+ rules.each {|rhs|
+ if rhs.all? {|e| emptyable_syms[e] }
+ emptyable_syms[sym] = true
+ changed = true
+ break
+ end
+ }
+ }
+ end
+ result = {}
+ syntax.each {|sym, rules|
+ rules2 = []
+ rules.each {|rhs|
+ expand_emptyable_syms(rhs, emptyable_syms) {|rhs2|
+ next if rhs2.empty?
+ rules2 << rhs2
+ }
+ }
+ result[sym] = rules2.uniq
+ }
+ result
+ end
+
+ def self.expand_channel_rules(syntax)
+ channel_rules = {}
+ syntax.each {|sym, rules|
+ channel_rules[sym] = {sym=>true}
+ rules.each {|rhs|
+ if rhs.length == 1 && Symbol === rhs[0]
+ channel_rules[sym][rhs[0]] = true
+ end
+ }
+ }
+ changed = true
+ while changed
+ changed = false
+ channel_rules.each {|sym, set|
+ n1 = set.size
+ set.keys.each {|s|
+ set.update(channel_rules[s])
+ }
+ n2 = set.size
+ changed = true if n1 < n2
+ }
+ end
+ result = {}
+ syntax.each {|sym, rules|
+ rules2 = []
+ channel_rules[sym].each_key {|s|
+ syntax[s].each {|rhs|
+ unless rhs.length == 1 && Symbol === rhs[0]
+ rules2 << rhs
+ end
+ }
+ }
+ result[sym] = rules2.uniq
+ }
+ result
+ end
+
+ def self.expand_noalt_rules(syntax)
+ noalt_syms = {}
+ syntax.each {|sym, rules|
+ if rules.length == 1
+ noalt_syms[sym] = true
+ end
+ }
+ result = {}
+ syntax.each {|sym, rules|
+ rules2 = []
+ rules.each {|rhs|
+ rhs2 = []
+ rhs.each {|e|
+ if noalt_syms[e]
+ rhs2.concat syntax[e][0]
+ else
+ rhs2 << e
+ end
+ }
+ rules2 << rhs2
+ }
+ result[sym] = rules2.uniq
+ }
+ result
+ end
+
+ def self.reorder_rules(syntax)
+ result = {}
+ syntax.each {|sym, rules|
+ result[sym] = rules.sort_by {|rhs|
+ [rhs.find_all {|e| Symbol === e }.length, rhs.length]
+ }
+ }
+ result
+ end
+
+ def each_tree(sym, limit)
+ generate_from_sym(sym, limit) {|_, tree|
+ yield tree
+ }
+ nil
+ end
+
+ def each_string(sym, limit)
+ generate_from_sym(sym, limit) {|_, tree|
+ yield [tree].join('')
+ }
+ nil
+ end
+
+ def generate_from_sym(sym, limit, &b)
+ return if limit < 0
+ if String === sym
+ yield limit, sym
+ else
+ rules = @syntax[sym]
+ raise "undefined rule: #{sym}" if !rules
+ rules.each {|rhs|
+ if rhs.length == 1 || rules.length == 1
+ limit1 = limit
+ else
+ limit1 = limit-1
+ end
+ generate_from_rhs(rhs, limit1, &b)
+ }
+ end
+ nil
+ end
+
+ def generate_from_rhs(rhs, limit)
+ return if limit < 0
+ if rhs.empty?
+ yield limit, []
+ else
+ generate_from_sym(rhs[0], limit) {|limit1, child|
+ generate_from_rhs(rhs[1..-1], limit1) {|limit2, arr|
+ yield limit2, [child, *arr]
+ }
+ }
+ end
+ nil
+ end
+ end
+ # :startdoc:
+
+end
+
diff --git a/jni/ruby/test/ruby/test_alias.rb b/jni/ruby/test/ruby/test_alias.rb
new file mode 100644
index 0000000..8901fc2
--- /dev/null
+++ b/jni/ruby/test/ruby/test_alias.rb
@@ -0,0 +1,196 @@
+require 'test/unit'
+
+class TestAlias < Test::Unit::TestCase
+ class Alias0
+ def foo
+ "foo"
+ end
+ end
+
+ class Alias1 < Alias0
+ alias bar foo
+
+ def foo
+ "foo+#{super}"
+ end
+ end
+
+ class Alias2 < Alias1
+ alias baz foo
+ undef foo
+ end
+
+ class Alias3 < Alias2
+ def foo
+ super
+ end
+
+ def bar
+ super
+ end
+
+ def quux
+ super
+ end
+ end
+
+ def test_alias
+ x = Alias2.new
+ assert_equal "foo", x.bar
+ assert_equal "foo+foo", x.baz
+ assert_equal "foo+foo", x.baz # test_check for cache
+
+ x = Alias3.new
+ assert_raise(NoMethodError) { x.foo }
+ assert_equal "foo", x.bar
+ assert_raise(NoMethodError) { x.quux }
+ end
+
+ class C
+ def m
+ $SAFE
+ end
+ end
+
+ def test_nonexistmethod
+ assert_raise(NameError){
+ Class.new{
+ alias_method :foobarxyzzy, :barbaz
+ }
+ }
+ end
+
+ def test_send_alias
+ x = "abc"
+ class << x
+ alias_method :try, :__send__
+ end
+ assert_equal("ABC", x.try(:upcase), '[ruby-dev:38824]')
+ end
+
+ def test_special_const_alias
+ assert_raise(TypeError) do
+ 1.instance_eval do
+ alias to_string to_s
+ end
+ end
+ end
+
+ def test_alias_with_zsuper_method
+ c = Class.new
+ c.class_eval do
+ def foo
+ :ok
+ end
+ def bar
+ :ng
+ end
+ private :foo
+ end
+ d = Class.new(c)
+ d.class_eval do
+ public :foo
+ alias bar foo
+ end
+ assert_equal(:ok, d.new.bar)
+ end
+
+ module SuperInAliasedModuleMethod
+ module M
+ def foo
+ super << :M
+ end
+
+ alias bar foo
+ end
+
+ class Base
+ def foo
+ [:Base]
+ end
+ end
+
+ class Derived < Base
+ include M
+ end
+ end
+
+ # [ruby-dev:46028]
+ def test_super_in_aliased_module_method # fails in 1.8
+ assert_equal([:Base, :M], SuperInAliasedModuleMethod::Derived.new.bar)
+ end
+
+ def test_alias_wb_miss
+ assert_normal_exit %q{
+ require 'stringio'
+ GC.verify_internal_consistency
+ GC.start
+ class StringIO
+ alias_method :read_nonblock, :sysread
+ end
+ GC.verify_internal_consistency
+ }
+ end
+
+ def test_cyclic_zsuper
+ bug9475 = '[ruby-core:60431] [Bug #9475]'
+
+ a = Module.new do
+ def foo
+ "A"
+ end
+ end
+
+ b = Class.new do
+ include a
+ attr_reader :b
+
+ def foo
+ @b ||= 0
+ raise SystemStackError if (@b += 1) > 1
+ # "foo from B"
+ super + "B"
+ end
+ end
+
+ c = Class.new(b) do
+ alias orig_foo foo
+
+ def foo
+ # "foo from C"
+ orig_foo + "C"
+ end
+ end
+
+ b.class_eval do
+ alias orig_foo foo
+ attr_reader :b2
+
+ def foo
+ @b2 ||= 0
+ raise SystemStackError if (@b2 += 1) > 1
+ # "foo from B (again)"
+ orig_foo + "B2"
+ end
+ end
+
+ assert_nothing_raised(SystemStackError, bug9475) do
+ assert_equal("ABC", c.new.foo, bug9475)
+ end
+ end
+
+ def test_alias_in_module
+ bug9663 = '[ruby-core:61635] [Bug #9663]'
+
+ assert_separately(['-', bug9663], <<-'end;')
+ bug = ARGV[0]
+
+ m = Module.new do
+ alias orig_to_s to_s
+ end
+
+ o = Object.new.extend(m)
+ assert_equal(o.to_s, o.orig_to_s, bug)
+ end;
+ end
+end
diff --git a/jni/ruby/test/ruby/test_argf.rb b/jni/ruby/test/ruby/test_argf.rb
new file mode 100644
index 0000000..6975b83
--- /dev/null
+++ b/jni/ruby/test/ruby/test_argf.rb
@@ -0,0 +1,859 @@
+require 'test/unit'
+require 'timeout'
+require 'tmpdir'
+require 'tempfile'
+require 'fileutils'
+
+class TestArgf < Test::Unit::TestCase
+ def setup
+ @tmpdir = Dir.mktmpdir
+ @tmp_count = 0
+ @t1 = make_tempfile0("argf-foo")
+ @t1.binmode
+ @t1.puts "1"
+ @t1.puts "2"
+ @t1.close
+ @t2 = make_tempfile0("argf-bar")
+ @t2.binmode
+ @t2.puts "3"
+ @t2.puts "4"
+ @t2.close
+ @t3 = make_tempfile0("argf-baz")
+ @t3.binmode
+ @t3.puts "5"
+ @t3.puts "6"
+ @t3.close
+ end
+
+ def teardown
+ FileUtils.rmtree(@tmpdir)
+ end
+
+ def make_tempfile0(basename)
+ @tmp_count += 1
+ open("#{@tmpdir}/#{basename}-#{@tmp_count}", "w")
+ end
+
+ def make_tempfile
+ t = make_tempfile0("argf-qux")
+ t.puts "foo"
+ t.puts "bar"
+ t.puts "baz"
+ t.close
+ t
+ end
+
+ def ruby(*args, external_encoding: Encoding::UTF_8)
+ args = ['-e', '$>.write($<.read)'] if args.empty?
+ ruby = EnvUtil.rubybin
+ f = IO.popen([ruby] + args, 'r+', external_encoding: external_encoding)
+ yield(f)
+ ensure
+ f.close unless !f || f.closed?
+ end
+
+ def no_safe_rename
+ /cygwin|mswin|mingw|bccwin/ =~ RUBY_PLATFORM
+ end
+
+ def assert_src_expected(line, src, args = nil)
+ args ||= [@t1.path, @t2.path, @t3.path]
+ expected = src.split(/^/)
+ ruby('-e', src, *args) do |f|
+ expected.each_with_index do |e, i|
+ /#=> *(.*)/ =~ e or next
+ a = f.gets
+ assert_not_nil(a, "[ruby-dev:34445]: remained")
+ assert_equal($1, a.chomp, "[ruby-dev:34445]: line #{line+i}")
+ end
+ end
+ end
+
+ def test_argf
+ assert_src_expected(__LINE__+1, <<-'SRC')
+ a = ARGF
+ b = a.dup
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["1", 1, "1", 1]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["2", 2, "2", 2]
+ a.rewind
+ b.rewind
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["1", 1, "1", 3]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["2", 2, "2", 4]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["3", 3, "3", 5]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["4", 4, "4", 6]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 7]
+ a.rewind
+ b.rewind
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 8]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["6", 6, "6", 9]
+ SRC
+ end
+
+ def test_lineno
+ assert_src_expected(__LINE__+1, <<-'SRC')
+ a = ARGF
+ a.gets; p $. #=> 1
+ a.gets; p $. #=> 2
+ a.gets; p $. #=> 3
+ a.rewind; p $. #=> 3
+ a.gets; p $. #=> 3
+ a.gets; p $. #=> 4
+ a.rewind; p $. #=> 4
+ a.gets; p $. #=> 3
+ a.lineno = 1000; p $. #=> 1000
+ a.gets; p $. #=> 1001
+ a.gets; p $. #=> 1002
+ $. = 2000
+ a.gets; p $. #=> 2001
+ a.gets; p $. #=> 2001
+ SRC
+ end
+
+ def test_lineno2
+ assert_src_expected(__LINE__+1, <<-'SRC')
+ a = ARGF.dup
+ a.gets; p $. #=> 1
+ a.gets; p $. #=> 2
+ a.gets; p $. #=> 1
+ a.rewind; p $. #=> 1
+ a.gets; p $. #=> 1
+ a.gets; p $. #=> 2
+ a.gets; p $. #=> 1
+ a.lineno = 1000; p $. #=> 1
+ a.gets; p $. #=> 2
+ a.gets; p $. #=> 2
+ $. = 2000
+ a.gets; p $. #=> 2000
+ a.gets; p $. #=> 2000
+ SRC
+ end
+
+ def test_lineno3
+ assert_in_out_err(["-", @t1.path, @t2.path], <<-INPUT, %w"1 1 1 2 2 2 3 3 1 4 4 2", [], "[ruby-core:25205]")
+ ARGF.each do |line|
+ puts [$., ARGF.lineno, ARGF.file.lineno]
+ end
+ INPUT
+ end
+
+ def test_inplace
+ assert_in_out_err(["-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ ARGF.inplace_mode = '.bak'
+ while line = ARGF.gets
+ puts line.chomp + '.new'
+ end
+ INPUT
+ assert_equal("1.new\n2.new\n", File.read(@t1.path))
+ assert_equal("3.new\n4.new\n", File.read(@t2.path))
+ assert_equal("5.new\n6.new\n", File.read(@t3.path))
+ assert_equal("1\n2\n", File.read(@t1.path + ".bak"))
+ assert_equal("3\n4\n", File.read(@t2.path + ".bak"))
+ assert_equal("5\n6\n", File.read(@t3.path + ".bak"))
+ end
+
+ def test_inplace2
+ assert_in_out_err(["-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ ARGF.inplace_mode = '.bak'
+ puts ARGF.gets.chomp + '.new'
+ puts ARGF.gets.chomp + '.new'
+ p ARGF.inplace_mode
+ ARGF.inplace_mode = nil
+ puts ARGF.gets.chomp + '.new'
+ puts ARGF.gets.chomp + '.new'
+ p ARGF.inplace_mode
+ ARGF.inplace_mode = '.bak'
+ puts ARGF.gets.chomp + '.new'
+ p ARGF.inplace_mode
+ ARGF.inplace_mode = nil
+ puts ARGF.gets.chomp + '.new'
+ INPUT
+ assert_equal("1.new\n2.new\n\".bak\"\n3.new\n4.new\nnil\n", File.read(@t1.path))
+ assert_equal("3\n4\n", File.read(@t2.path))
+ assert_equal("5.new\n\".bak\"\n6.new\n", File.read(@t3.path))
+ assert_equal("1\n2\n", File.read(@t1.path + ".bak"))
+ assert_equal(false, File.file?(@t2.path + ".bak"))
+ assert_equal("5\n6\n", File.read(@t3.path + ".bak"))
+ end
+
+ def test_inplace3
+ assert_in_out_err(["-i.bak", "-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ puts ARGF.gets.chomp + '.new'
+ puts ARGF.gets.chomp + '.new'
+ p $-i
+ $-i = nil
+ puts ARGF.gets.chomp + '.new'
+ puts ARGF.gets.chomp + '.new'
+ p $-i
+ $-i = '.bak'
+ puts ARGF.gets.chomp + '.new'
+ p $-i
+ $-i = nil
+ puts ARGF.gets.chomp + '.new'
+ INPUT
+ assert_equal("1.new\n2.new\n\".bak\"\n3.new\n4.new\nnil\n", File.read(@t1.path))
+ assert_equal("3\n4\n", File.read(@t2.path))
+ assert_equal("5.new\n\".bak\"\n6.new\n", File.read(@t3.path))
+ assert_equal("1\n2\n", File.read(@t1.path + ".bak"))
+ assert_equal(false, File.file?(@t2.path + ".bak"))
+ assert_equal("5\n6\n", File.read(@t3.path + ".bak"))
+ end
+
+ def test_inplace_rename_impossible
+ t = make_tempfile
+
+ assert_in_out_err(["-", t.path], <<-INPUT) do |r, e|
+ ARGF.inplace_mode = '/\\\\:'
+ while line = ARGF.gets
+ puts line.chomp + '.new'
+ end
+ INPUT
+ assert_match(/Can't rename .* to .*: .*. skipping file/, e.first) #'
+ assert_equal([], r)
+ assert_equal("foo\nbar\nbaz\n", File.read(t.path))
+ end
+
+ base = "argf-\u{30c6 30b9 30c8}"
+ name = "#{@tmpdir}/#{base}"
+ File.write(name, "foo")
+ argf = ARGF.class.new(name)
+ argf.inplace_mode = '/\\:'
+ assert_warning(/#{base}/) {argf.gets}
+ end
+
+ def test_inplace_no_backup
+ t = make_tempfile
+
+ assert_in_out_err(["-", t.path], <<-INPUT) do |r, e|
+ ARGF.inplace_mode = ''
+ while line = ARGF.gets
+ puts line.chomp + '.new'
+ end
+ INPUT
+ if no_safe_rename
+ assert_match(/Can't do inplace edit without backup/, e.join) #'
+ else
+ assert_equal([], e)
+ assert_equal([], r)
+ assert_equal("foo.new\nbar.new\nbaz.new\n", File.read(t.path))
+ end
+ end
+ end
+
+ def test_inplace_dup
+ t = make_tempfile
+
+ assert_in_out_err(["-", t.path], <<-INPUT, [], [])
+ ARGF.inplace_mode = '.bak'
+ f = ARGF.dup
+ while line = f.gets
+ puts line.chomp + '.new'
+ end
+ INPUT
+ assert_equal("foo.new\nbar.new\nbaz.new\n", File.read(t.path))
+ end
+
+ def test_inplace_stdin
+ assert_in_out_err(["-", "-"], <<-INPUT, [], /Can't do inplace edit for stdio; skipping/)
+ ARGF.inplace_mode = '.bak'
+ f = ARGF.dup
+ while line = f.gets
+ puts line.chomp + '.new'
+ end
+ INPUT
+ end
+
+ def test_inplace_stdin2
+ assert_in_out_err(["-"], <<-INPUT, [], /Can't do inplace edit for stdio/)
+ ARGF.inplace_mode = '.bak'
+ while line = ARGF.gets
+ puts line.chomp + '.new'
+ end
+ INPUT
+ end
+
+ def test_encoding
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ p ARGF.external_encoding.is_a?(Encoding)
+ p ARGF.internal_encoding.is_a?(Encoding)
+ ARGF.gets
+ p ARGF.external_encoding.is_a?(Encoding)
+ p ARGF.internal_encoding
+ SRC
+ assert_equal("true\ntrue\ntrue\nnil\n", f.read)
+ end
+ end
+
+ def test_tell
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ begin
+ ARGF.binmode
+ loop do
+ p ARGF.tell
+ p ARGF.gets
+ end
+ rescue ArgumentError
+ puts "end"
+ end
+ SRC
+ a = f.read.split("\n")
+ [0, 2, 4, 2, 4, 2, 4].map {|i| i.to_s }.
+ zip((1..6).map {|i| '"' + i.to_s + '\n"' } + ["nil"]).flatten.
+ each do |x|
+ assert_equal(x, a.shift)
+ end
+ assert_equal('end', a.shift)
+ end
+ end
+
+ def test_seek
+ assert_src_expected(__LINE__+1, <<-'SRC')
+ ARGF.seek(4)
+ p ARGF.gets #=> "3\n"
+ ARGF.seek(0, IO::SEEK_END)
+ p ARGF.gets #=> "5\n"
+ ARGF.seek(4)
+ p ARGF.gets #=> nil
+ begin
+ ARGF.seek(0)
+ rescue
+ puts "end" #=> end
+ end
+ SRC
+ end
+
+ def test_set_pos
+ assert_src_expected(__LINE__+1, <<-'SRC')
+ ARGF.pos = 4
+ p ARGF.gets #=> "3\n"
+ ARGF.pos = 4
+ p ARGF.gets #=> "5\n"
+ ARGF.pos = 4
+ p ARGF.gets #=> nil
+ begin
+ ARGF.pos = 4
+ rescue
+ puts "end" #=> end
+ end
+ SRC
+ end
+
+ def test_rewind
+ assert_src_expected(__LINE__+1, <<-'SRC')
+ ARGF.pos = 4
+ ARGF.rewind
+ p ARGF.gets #=> "1\n"
+ ARGF.pos = 4
+ p ARGF.gets #=> "3\n"
+ ARGF.pos = 4
+ p ARGF.gets #=> "5\n"
+ ARGF.pos = 4
+ p ARGF.gets #=> nil
+ begin
+ ARGF.rewind
+ rescue
+ puts "end" #=> end
+ end
+ SRC
+ end
+
+ def test_fileno
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ begin
+ ARGF.fileno
+ rescue
+ puts "end"
+ end
+ SRC
+ a = f.read.split("\n")
+ fd1, fd2, fd3, fd4, tag = a
+ assert_match(/^\d+$/, fd1)
+ assert_match(/^\d+$/, fd2)
+ assert_match(/^\d+$/, fd3)
+ assert_match(/^\d+$/, fd4)
+ assert_equal('end', tag)
+ end
+ end
+
+ def test_to_io
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ 8.times do
+ p ARGF.to_io
+ ARGF.gets
+ end
+ SRC
+ a = f.read.split("\n")
+ f11, f12, f13, f21, f22, f31, f32, f4 = a
+ assert_equal(f11, f12)
+ assert_equal(f11, f13)
+ assert_equal(f21, f22)
+ assert_equal(f31, f32)
+ assert_match(/\(closed\)/, f4)
+ f4.sub!(/ \(closed\)/, "")
+ assert_equal(f31, f4)
+ end
+ end
+
+ def test_eof
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ begin
+ 8.times do
+ p ARGF.eof?
+ ARGF.gets
+ end
+ rescue IOError
+ puts "end"
+ end
+ SRC
+ a = f.read.split("\n")
+ (%w(false) + (%w(false true) * 3) + %w(end)).each do |x|
+ assert_equal(x, a.shift)
+ end
+ end
+
+ t1 = open("#{@tmpdir}/argf-hoge", "w")
+ t1.binmode
+ t1.puts "foo"
+ t1.close
+ t2 = open("#{@tmpdir}/argf-moge", "w")
+ t2.binmode
+ t2.puts "bar"
+ t2.close
+ ruby('-e', 'STDERR.reopen(STDOUT); ARGF.gets; ARGF.skip; p ARGF.eof?', t1.path, t2.path) do |f|
+ assert_equal(%w(false), f.read.split(/\n/))
+ end
+ end
+
+ def test_read
+ ruby('-e', "p ARGF.read(8)", @t1.path, @t2.path, @t3.path) do |f|
+ assert_equal("\"1\\n2\\n3\\n4\\n\"\n", f.read)
+ end
+ end
+
+ def test_read2
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = ""
+ ARGF.read(8, s)
+ p s
+ SRC
+ assert_equal("\"1\\n2\\n3\\n4\\n\"\n", f.read)
+ end
+ end
+
+ def test_read2_with_not_empty_buffer
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = "0123456789"
+ ARGF.read(8, s)
+ p s
+ SRC
+ assert_equal("\"1\\n2\\n3\\n4\\n\"\n", f.read)
+ end
+ end
+
+ def test_read3
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ nil while ARGF.gets
+ p ARGF.read
+ p ARGF.read(0, "")
+ SRC
+ assert_equal("nil\n\"\"\n", f.read)
+ end
+ end
+
+ def test_readpartial
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = ""
+ begin
+ loop do
+ s << ARGF.readpartial(1)
+ t = ""; ARGF.readpartial(1, t); s << t
+ # not empty buffer
+ u = "abcdef"; ARGF.readpartial(1, u); s << u
+ end
+ rescue EOFError
+ puts s
+ end
+ SRC
+ assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
+ end
+ end
+
+ def test_readpartial2
+ ruby('-e', <<-SRC) do |f|
+ s = ""
+ begin
+ loop do
+ s << ARGF.readpartial(1)
+ t = ""; ARGF.readpartial(1, t); s << t
+ end
+ rescue EOFError
+ $stdout.binmode
+ puts s
+ end
+ SRC
+ f.binmode
+ f.puts("foo")
+ f.puts("bar")
+ f.puts("baz")
+ f.close_write
+ assert_equal("foo\nbar\nbaz\n", f.read)
+ end
+ end
+
+ def test_getc
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = ""
+ while c = ARGF.getc
+ s << c
+ end
+ puts s
+ SRC
+ assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
+ end
+ end
+
+ def test_getbyte
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = []
+ while c = ARGF.getbyte
+ s << c
+ end
+ p s
+ SRC
+ assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
+ end
+ end
+
+ def test_readchar
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = ""
+ begin
+ while c = ARGF.readchar
+ s << c
+ end
+ rescue EOFError
+ puts s
+ end
+ SRC
+ assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
+ end
+ end
+
+ def test_readbyte
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ begin
+ s = []
+ while c = ARGF.readbyte
+ s << c
+ end
+ rescue EOFError
+ p s
+ end
+ SRC
+ assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
+ end
+ end
+
+ def test_each_line
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = []
+ ARGF.each_line {|l| s << l }
+ p s
+ SRC
+ assert_equal("[\"1\\n\", \"2\\n\", \"3\\n\", \"4\\n\", \"5\\n\", \"6\\n\"]\n", f.read)
+ end
+ end
+
+ def test_each_line_paragraph
+ assert_in_out_err(['-e', 'ARGF.each_line("") {|para| p para}'], "a\n\nb\n",
+ ["\"a\\n\\n\"", "\"b\\n\""], [])
+ end
+
+ def test_each_byte
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = []
+ ARGF.each_byte {|c| s << c }
+ p s
+ SRC
+ assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
+ end
+ end
+
+ def test_each_char
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ s = ""
+ ARGF.each_char {|c| s << c }
+ puts s
+ SRC
+ assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
+ end
+ end
+
+ def test_filename
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ begin
+ puts ARGF.filename.dump
+ end while ARGF.gets
+ puts ARGF.filename.dump
+ SRC
+ a = f.read.split("\n")
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t2.path.dump, a.shift)
+ assert_equal(@t2.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ end
+ end
+
+ def test_filename2
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ begin
+ puts $FILENAME.dump
+ end while ARGF.gets
+ puts $FILENAME.dump
+ SRC
+ a = f.read.split("\n")
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t2.path.dump, a.shift)
+ assert_equal(@t2.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ end
+ end
+
+ def test_file
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ begin
+ puts ARGF.file.path.dump
+ end while ARGF.gets
+ puts ARGF.file.path.dump
+ SRC
+ a = f.read.split("\n")
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t1.path.dump, a.shift)
+ assert_equal(@t2.path.dump, a.shift)
+ assert_equal(@t2.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ assert_equal(@t3.path.dump, a.shift)
+ end
+ end
+
+ def test_binmode
+ bug5268 = '[ruby-core:39234]'
+ open(@t3.path, "wb") {|f| f.write "5\r\n6\r\n"}
+ ruby('-e', "ARGF.binmode; STDOUT.binmode; puts ARGF.read", @t1.path, @t2.path, @t3.path) do |f|
+ f.binmode
+ assert_equal("1\n2\n3\n4\n5\r\n6\r\n", f.read, bug5268)
+ end
+ end
+
+ def test_textmode
+ bug5268 = '[ruby-core:39234]'
+ open(@t3.path, "wb") {|f| f.write "5\r\n6\r\n"}
+ ruby('-e', "STDOUT.binmode; puts ARGF.read", @t1.path, @t2.path, @t3.path) do |f|
+ f.binmode
+ assert_equal("1\n2\n3\n4\n5\n6\n", f.read, bug5268)
+ end
+ end unless IO::BINARY.zero?
+
+ def test_skip
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ ARGF.skip
+ puts ARGF.gets
+ ARGF.skip
+ puts ARGF.read
+ SRC
+ assert_equal("1\n3\n4\n5\n6\n", f.read)
+ end
+ end
+
+ def test_skip_in_each_line
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ ARGF.each_line {|l| print l; ARGF.skip}
+ SRC
+ assert_equal("1\n3\n5\n", f.read, '[ruby-list:49185]')
+ end
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ ARGF.each_line {|l| ARGF.skip; puts [l, ARGF.gets].map {|s| s ? s.chomp : s.inspect}.join("+")}
+ SRC
+ assert_equal("1+3\n4+5\n6+nil\n", f.read, '[ruby-list:49185]')
+ end
+ end
+
+ def test_skip_in_each_byte
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ ARGF.each_byte {|l| print l; ARGF.skip}
+ SRC
+ assert_equal("135".unpack("C*").join(""), f.read, '[ruby-list:49185]')
+ end
+ end
+
+ def test_skip_in_each_char
+ [[@t1, "\u{3042}"], [@t2, "\u{3044}"], [@t3, "\u{3046}"]].each do |f, s|
+ File.write(f.path, s, mode: "w:utf-8")
+ end
+ ruby('-Eutf-8', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ ARGF.each_char {|l| print l; ARGF.skip}
+ SRC
+ assert_equal("\u{3042 3044 3046}", f.read, '[ruby-list:49185]')
+ end
+ end
+
+ def test_skip_in_each_codepoint
+ [[@t1, "\u{3042}"], [@t2, "\u{3044}"], [@t3, "\u{3046}"]].each do |f, s|
+ File.write(f.path, s, mode: "w:utf-8")
+ end
+ ruby('-Eutf-8', '-Eutf-8', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ ARGF.each_codepoint {|l| printf "%x:", l; ARGF.skip}
+ SRC
+ assert_equal("3042:3044:3046:", f.read, '[ruby-list:49185]')
+ end
+ end
+
+ def test_close
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ ARGF.close
+ puts ARGF.read
+ SRC
+ assert_equal("3\n4\n5\n6\n", f.read)
+ end
+ end
+
+ def test_close_replace
+ ruby('-e', <<-SRC) do |f|
+ ARGF.close
+ ARGV.replace ['#{@t1.path}', '#{@t2.path}', '#{@t3.path}']
+ puts ARGF.read
+ SRC
+ assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
+ end
+ end
+
+ def test_closed
+ ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ 3.times do
+ p ARGF.closed?
+ ARGF.gets
+ ARGF.gets
+ end
+ p ARGF.closed?
+ ARGF.gets
+ p ARGF.closed?
+ SRC
+ assert_equal("false\nfalse\nfalse\nfalse\ntrue\n", f.read)
+ end
+ end
+
+ def test_argv
+ ruby('-e', "p ARGF.argv; p $*", @t1.path, @t2.path, @t3.path) do |f|
+ assert_equal([@t1.path, @t2.path, @t3.path].inspect, f.gets.chomp)
+ assert_equal([@t1.path, @t2.path, @t3.path].inspect, f.gets.chomp)
+ end
+ end
+
+ def test_readlines_limit_0
+ bug4024 = '[ruby-dev:42538]'
+ t = make_tempfile
+ argf = ARGF.class.new(t.path)
+ begin
+ assert_raise(ArgumentError, bug4024) do
+ argf.readlines(0)
+ end
+ ensure
+ argf.close
+ end
+ end
+
+ def test_each_line_limit_0
+ bug4024 = '[ruby-dev:42538]'
+ t = make_tempfile
+ argf = ARGF.class.new(t.path)
+ begin
+ assert_raise(ArgumentError, bug4024) do
+ argf.each_line(0).next
+ end
+ ensure
+ argf.close
+ end
+ end
+
+ def test_unreadable
+ bug4274 = '[ruby-core:34446]'
+ paths = (1..2).map do
+ t = Tempfile.new("bug4274-")
+ path = t.path
+ t.close!
+ path
+ end
+ argf = ARGF.class.new(*paths)
+ paths.each do |path|
+ assert_raise_with_message(Errno::ENOENT, /- #{Regexp.quote(path)}\z/) {argf.gets}
+ end
+ assert_nil(argf.gets, bug4274)
+ end
+
+ def test_readlines_twice
+ bug5952 = '[ruby-dev:45160]'
+ assert_ruby_status(["-e", "2.times {STDIN.tty?; readlines}"], "", bug5952)
+ end
+
+ def test_lines
+ ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ $stderr = $stdout
+ s = []
+ ARGF.lines {|l| s << l }
+ p s
+ SRC
+ assert_match(/deprecated/, f.gets)
+ assert_equal("[\"1\\n\", \"2\\n\", \"3\\n\", \"4\\n\", \"5\\n\", \"6\\n\"]\n", f.read)
+ end
+ end
+
+ def test_bytes
+ ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ $stderr = $stdout
+ print Marshal.dump(ARGF.bytes.to_a)
+ SRC
+ assert_match(/deprecated/, f.gets)
+ assert_equal([49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10], Marshal.load(f.read))
+ end
+ end
+
+ def test_chars
+ ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ $stderr = $stdout
+ print [Marshal.dump(ARGF.chars.to_a)].pack('m')
+ SRC
+ assert_match(/deprecated/, f.gets)
+ assert_equal(["1", "\n", "2", "\n", "3", "\n", "4", "\n", "5", "\n", "6", "\n"], Marshal.load(f.read.unpack('m').first))
+ end
+ end
+
+ def test_codepoints
+ ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
+ $stderr = $stdout
+ print Marshal.dump(ARGF.codepoints.to_a)
+ SRC
+ assert_match(/deprecated/, f.gets)
+ assert_equal([49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10], Marshal.load(f.read))
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_arity.rb b/jni/ruby/test/ruby/test_arity.rb
new file mode 100644
index 0000000..e026841
--- /dev/null
+++ b/jni/ruby/test/ruby/test_arity.rb
@@ -0,0 +1,69 @@
+require 'test/unit'
+
+class TestArity < Test::Unit::TestCase
+ def err_mess(method_proc = nil, argc = 0)
+ args = (1..argc).to_a
+ assert_raise_with_message(ArgumentError, /wrong number of arguments \((.*)\)/) do
+ case method_proc
+ when nil
+ yield
+ when Symbol
+ method(method_proc).call(*args)
+ else
+ method_proc.call(*args)
+ end
+ end
+ $1
+ end
+
+ def a
+ end
+
+ def b(a, b, c, d=1, e=2, f, g, h, i, &block)
+ end
+
+ def c(a, b, c, d=1, e=2, *rest)
+ end
+
+ def d(a, b: 42)
+ end
+
+ def e(a, b:42, **c)
+ end
+
+ def f(a, b, c=1, *rest, d: 3)
+ end
+
+ def test_method_err_mess
+ assert_equal "1 for 0", err_mess(:a, 1)
+ assert_equal "10 for 7..9", err_mess(:b, 10)
+ assert_equal "2 for 3+", err_mess(:c, 2)
+ assert_equal "2 for 1", err_mess(:d, 2)
+ assert_equal "0 for 1", err_mess(:d, 0)
+ assert_equal "2 for 1", err_mess(:e, 2)
+ assert_equal "0 for 1", err_mess(:e, 0)
+ assert_equal "1 for 2+", err_mess(:f, 1)
+ end
+
+ def test_proc_err_mess
+ assert_equal "0 for 1..2", err_mess(->(b, c=42){}, 0)
+ assert_equal "1 for 2+", err_mess(->(a, b, c=42, *d){}, 1)
+ assert_equal "3 for 4+", err_mess(->(a, b, *c, d, e){}, 3)
+ assert_equal "3 for 1..2", err_mess(->(b, c=42){}, 3)
+ assert_equal "1 for 0", err_mess(->(&block){}, 1)
+ # Double checking:
+ p = Proc.new{|b, c=42| :ok}
+ assert_equal :ok, p.call(1, 2, 3)
+ assert_equal :ok, p.call
+ end
+
+ def test_message_change_issue_6085
+ assert_equal "3 for 1..2", err_mess{ SignalException.new(1, "", nil) }
+ assert_equal "1 for 0", err_mess{ Hash.new(1){} }
+ assert_equal "3 for 1..2", err_mess{ Module.send :define_method, 1, 2, 3 }
+ assert_equal "1 for 2", err_mess{ "".sub!(//) }
+ assert_equal "0 for 1..2", err_mess{ "".sub!{} }
+ assert_equal "0 for 1+", err_mess{ exec }
+ assert_equal "0 for 1+", err_mess{ Struct.new }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_array.rb b/jni/ruby/test/ruby/test_array.rb
new file mode 100644
index 0000000..8c8b63c
--- /dev/null
+++ b/jni/ruby/test/ruby/test_array.rb
@@ -0,0 +1,2533 @@
+# coding: US-ASCII
+require 'test/unit'
+
+class TestArray < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ @cls = Array
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def test_percent_i
+ assert_equal([:foo, :bar], %i[foo bar])
+ assert_equal([:"\"foo"], %i["foo])
+ end
+
+ def test_percent_I
+ x = 10
+ assert_equal([:foo, :b10], %I[foo b#{x}])
+ assert_equal([:"\"foo10"], %I["foo#{x}])
+ end
+
+ def test_0_literal
+ assert_equal([1, 2, 3, 4], [1, 2] + [3, 4])
+ assert_equal([1, 2, 1, 2], [1, 2] * 2)
+ assert_equal("1:2", [1, 2] * ":")
+
+ assert_equal([1, 2].hash, [1, 2].hash)
+
+ assert_equal([2,3], [1,2,3] & [2,3,4])
+ assert_equal([1,2,3,4], [1,2,3] | [2,3,4])
+ assert_equal([1,2,3] - [2,3], [1])
+
+ x = [0, 1, 2, 3, 4, 5]
+ assert_equal(2, x[2])
+ assert_equal([1, 2, 3], x[1..3])
+ assert_equal([1, 2, 3], x[1,3])
+
+ x[0, 2] = 10
+ assert_equal([10, 2, 3, 4, 5], x)
+
+ x[0, 0] = -1
+ assert_equal([-1, 10, 2, 3, 4, 5], x)
+
+ x[-1, 1] = 20
+ assert_equal(20, x[-1])
+ assert_equal(20, x.pop)
+ end
+
+ def test_array_andor_0
+ assert_equal([2], ([1,2,3]&[2,4,6]))
+ assert_equal([1,2,3,4,6], ([1,2,3]|[2,4,6]))
+ end
+
+ def test_compact_0
+ a = [nil, 1, nil, nil, 5, nil, nil]
+ assert_equal [1, 5], a.compact
+ assert_equal [nil, 1, nil, nil, 5, nil, nil], a
+ a.compact!
+ assert_equal [1, 5], a
+ end
+
+ def test_uniq_0
+ x = [1, 1, 4, 2, 5, 4, 5, 1, 2]
+ x.uniq!
+ assert_equal([1, 4, 2, 5], x)
+ end
+
+ def test_empty_0
+ assert_equal true, [].empty?
+ assert_equal false, [1].empty?
+ assert_equal false, [1, 1, 4, 2, 5, 4, 5, 1, 2].empty?
+ end
+
+ def test_sort_0
+ x = ["it", "came", "to", "pass", "that", "..."]
+ x = x.sort.join(" ")
+ assert_equal("... came it pass that to", x)
+ x = [2,5,3,1,7]
+ x.sort!{|a,b| a<=>b} # sort with condition
+ assert_equal([1,2,3,5,7], x)
+ x.sort!{|a,b| b-a} # reverse sort
+ assert_equal([7,5,3,2,1], x)
+ end
+
+ def test_split_0
+ x = "The Book of Mormon"
+ assert_equal(x.reverse, x.split(//).reverse!.join)
+ assert_equal(x.reverse, x.reverse!)
+ assert_equal("g:n:i:r:t:s: :e:t:y:b: :1", "1 byte string".split(//).reverse.join(":"))
+ x = "a b c d"
+ assert_equal(['a', 'b', 'c', 'd'], x.split)
+ assert_equal(['a', 'b', 'c', 'd'], x.split(' '))
+ end
+
+ def test_misc_0
+ assert(defined? "a".chomp, '"a".chomp is not defined')
+ assert_equal(["a", "b", "c"], "abc".scan(/./))
+ assert_equal([["1a"], ["2b"], ["3c"]], "1a2b3c".scan(/(\d.)/))
+ # non-greedy match
+ assert_equal([["a", "12"], ["b", "22"]], "a=12;b=22".scan(/(.*?)=(\d*);?/))
+
+ x = [1]
+ assert_equal('1:1:1:1:1', (x * 5).join(":"))
+ assert_equal('1', (x * 1).join(":"))
+ assert_equal('', (x * 0).join(":"))
+
+ *x = *(1..7).to_a
+ assert_equal(7, x.size)
+ assert_equal([1, 2, 3, 4, 5, 6, 7], x)
+
+ x = [1,2,3]
+ x[1,0] = x
+ assert_equal([1,1,2,3,2,3], x)
+
+ x = [1,2,3]
+ x[-1,0] = x
+ assert_equal([1,2,1,2,3,3], x)
+
+ x = [1,2,3]
+ x.concat(x)
+ assert_equal([1,2,3,1,2,3], x)
+
+ x = [1,2,3]
+ x.clear
+ assert_equal([], x)
+
+ x = [1,2,3]
+ y = x.dup
+ x << 4
+ y << 5
+ assert_equal([1,2,3,4], x)
+ assert_equal([1,2,3,5], y)
+ end
+
+ def test_beg_end_0
+ x = [1, 2, 3, 4, 5]
+
+ assert_equal(1, x.first)
+ assert_equal([1], x.first(1))
+ assert_equal([1, 2, 3], x.first(3))
+
+ assert_equal(5, x.last)
+ assert_equal([5], x.last(1))
+ assert_equal([3, 4, 5], x.last(3))
+
+ assert_equal(1, x.shift)
+ assert_equal([2, 3, 4], x.shift(3))
+ assert_equal([5], x)
+
+ assert_equal([2, 3, 4, 5], x.unshift(2, 3, 4))
+ assert_equal([1, 2, 3, 4, 5], x.unshift(1))
+ assert_equal([1, 2, 3, 4, 5], x)
+
+ assert_equal(5, x.pop)
+ assert_equal([3, 4], x.pop(2))
+ assert_equal([1, 2], x)
+
+ assert_equal([1, 2, 3, 4], x.push(3, 4))
+ assert_equal([1, 2, 3, 4, 5], x.push(5))
+ assert_equal([1, 2, 3, 4, 5], x)
+ end
+
+ def test_find_all_0
+ assert_respond_to([], :find_all)
+ assert_respond_to([], :select) # Alias
+ assert_equal([], [].find_all{ |obj| obj == "foo"})
+
+ x = ["foo", "bar", "baz", "baz", 1, 2, 3, 3, 4]
+ assert_equal(["baz","baz"], x.find_all{ |obj| obj == "baz" })
+ assert_equal([3,3], x.find_all{ |obj| obj == 3 })
+ end
+
+ def test_fill_0
+ assert_equal([-1, -1, -1, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1))
+ assert_equal([0, 1, 2, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, 3))
+ assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3, 2))
+ assert_equal([0, 1, 2, -1, -1, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, 3, 5))
+ assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2, 2))
+ assert_equal([0, 1, -1, -1, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, 2, 5))
+ assert_equal([0, 1, 2, 3, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, -2, 1))
+ assert_equal([0, 1, 2, 3, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, -2, 3))
+ assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3..4))
+ assert_equal([0, 1, 2, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3...4))
+ assert_equal([0, 1, -1, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2..-2))
+ assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2...-2))
+ assert_equal([10, 11, 12, 13, 14, 15], [0, 1, 2, 3, 4, 5].fill{|i| i+10})
+ assert_equal([0, 1, 2, 13, 14, 15], [0, 1, 2, 3, 4, 5].fill(3){|i| i+10})
+ assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3, 2){|i| i+10})
+ assert_equal([0, 1, 2, 13, 14, 15, 16, 17], [0, 1, 2, 3, 4, 5].fill(3, 5){|i| i+10})
+ assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3..4){|i| i+10})
+ assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10})
+ assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10})
+ assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10})
+ end
+
+ # From rubicon
+
+ def test_00_new
+ a = @cls.new()
+ assert_instance_of(@cls, a)
+ assert_equal(0, a.length)
+ assert_nil(a[0])
+ end
+
+ def test_01_square_brackets
+ a = @cls[ 5, 4, 3, 2, 1 ]
+ assert_instance_of(@cls, a)
+ assert_equal(5, a.length)
+ 5.times { |i| assert_equal(5-i, a[i]) }
+ assert_nil(a[6])
+ end
+
+ def test_AND # '&'
+ assert_equal(@cls[1, 3], @cls[ 1, 1, 3, 5 ] & @cls[ 1, 2, 3 ])
+ assert_equal(@cls[], @cls[ 1, 1, 3, 5 ] & @cls[ ])
+ assert_equal(@cls[], @cls[ ] & @cls[ 1, 2, 3 ])
+ assert_equal(@cls[], @cls[ 1, 2, 3 ] & @cls[ 4, 5, 6 ])
+ end
+
+ def test_MUL # '*'
+ assert_equal(@cls[], @cls[]*3)
+ assert_equal(@cls[1, 1, 1], @cls[1]*3)
+ assert_equal(@cls[1, 2, 1, 2, 1, 2], @cls[1, 2]*3)
+ assert_equal(@cls[], @cls[1, 2, 3] * 0)
+ assert_raise(ArgumentError) { @cls[1, 2]*(-3) }
+
+ assert_equal('1-2-3-4-5', @cls[1, 2, 3, 4, 5] * '-')
+ assert_equal('12345', @cls[1, 2, 3, 4, 5] * '')
+
+ end
+
+ def test_PLUS # '+'
+ assert_equal(@cls[], @cls[] + @cls[])
+ assert_equal(@cls[1], @cls[1] + @cls[])
+ assert_equal(@cls[1], @cls[] + @cls[1])
+ assert_equal(@cls[1, 1], @cls[1] + @cls[1])
+ assert_equal(@cls['cat', 'dog', 1, 2, 3], %w(cat dog) + (1..3).to_a)
+ end
+
+ def test_MINUS # '-'
+ assert_equal(@cls[], @cls[1] - @cls[1])
+ assert_equal(@cls[1], @cls[1, 2, 3, 4, 5] - @cls[2, 3, 4, 5])
+ # Ruby 1.8 feature change
+ #assert_equal(@cls[1], @cls[1, 2, 1, 3, 1, 4, 1, 5] - @cls[2, 3, 4, 5])
+ assert_equal(@cls[1, 1, 1, 1], @cls[1, 2, 1, 3, 1, 4, 1, 5] - @cls[2, 3, 4, 5])
+ a = @cls[]
+ 1000.times { a << 1 }
+ assert_equal(1000, a.length)
+ #assert_equal(@cls[1], a - @cls[2])
+ assert_equal(@cls[1] * 1000, a - @cls[2])
+ #assert_equal(@cls[1], @cls[1, 2, 1] - @cls[2])
+ assert_equal(@cls[1, 1], @cls[1, 2, 1] - @cls[2])
+ assert_equal(@cls[1, 2, 3], @cls[1, 2, 3] - @cls[4, 5, 6])
+ end
+
+ def test_LSHIFT # '<<'
+ a = @cls[]
+ a << 1
+ assert_equal(@cls[1], a)
+ a << 2 << 3
+ assert_equal(@cls[1, 2, 3], a)
+ a << nil << 'cat'
+ assert_equal(@cls[1, 2, 3, nil, 'cat'], a)
+ a << a
+ assert_equal(@cls[1, 2, 3, nil, 'cat', a], a)
+ end
+
+ def test_CMP # '<=>'
+ assert_equal(0, @cls[] <=> @cls[])
+ assert_equal(0, @cls[1] <=> @cls[1])
+ assert_equal(0, @cls[1, 2, 3, 'cat'] <=> @cls[1, 2, 3, 'cat'])
+ assert_equal(-1, @cls[] <=> @cls[1])
+ assert_equal(1, @cls[1] <=> @cls[])
+ assert_equal(-1, @cls[1, 2, 3] <=> @cls[1, 2, 3, 'cat'])
+ assert_equal(1, @cls[1, 2, 3, 'cat'] <=> @cls[1, 2, 3])
+ assert_equal(-1, @cls[1, 2, 3, 'cat'] <=> @cls[1, 2, 3, 'dog'])
+ assert_equal(1, @cls[1, 2, 3, 'dog'] <=> @cls[1, 2, 3, 'cat'])
+ end
+
+ def test_EQUAL # '=='
+ assert_operator(@cls[], :==, @cls[])
+ assert_operator(@cls[1], :==, @cls[1])
+ assert_operator(@cls[1, 1, 2, 2], :==, @cls[1, 1, 2, 2])
+ assert_operator(@cls[1.0, 1.0, 2.0, 2.0], :==, @cls[1, 1, 2, 2])
+ end
+
+ def test_VERY_EQUAL # '==='
+ assert_operator(@cls[], :===, @cls[])
+ assert_operator(@cls[1], :===, @cls[1])
+ assert_operator(@cls[1, 1, 2, 2], :===, @cls[1, 1, 2, 2])
+ assert_operator(@cls[1.0, 1.0, 2.0, 2.0], :===, @cls[1, 1, 2, 2])
+ end
+
+ def test_AREF # '[]'
+ a = @cls[*(1..100).to_a]
+
+ assert_equal(1, a[0])
+ assert_equal(100, a[99])
+ assert_nil(a[100])
+ assert_equal(100, a[-1])
+ assert_equal(99, a[-2])
+ assert_equal(1, a[-100])
+ assert_nil(a[-101])
+ assert_nil(a[-101,0])
+ assert_nil(a[-101,1])
+ assert_nil(a[-101,-1])
+ assert_nil(a[10,-1])
+
+ assert_equal(@cls[1], a[0,1])
+ assert_equal(@cls[100], a[99,1])
+ assert_equal(@cls[], a[100,1])
+ assert_equal(@cls[100], a[99,100])
+ assert_equal(@cls[100], a[-1,1])
+ assert_equal(@cls[99], a[-2,1])
+ assert_equal(@cls[], a[-100,0])
+ assert_equal(@cls[1], a[-100,1])
+
+ assert_equal(@cls[10, 11, 12], a[9, 3])
+ assert_equal(@cls[10, 11, 12], a[-91, 3])
+
+ assert_equal(@cls[1], a[0..0])
+ assert_equal(@cls[100], a[99..99])
+ assert_equal(@cls[], a[100..100])
+ assert_equal(@cls[100], a[99..200])
+ assert_equal(@cls[100], a[-1..-1])
+ assert_equal(@cls[99], a[-2..-2])
+
+ assert_equal(@cls[10, 11, 12], a[9..11])
+ assert_equal(@cls[10, 11, 12], a[-91..-89])
+
+ assert_nil(a[10, -3])
+ # Ruby 1.8 feature change:
+ # Array#[size..x] returns [] instead of nil.
+ #assert_nil(a[10..7])
+ assert_equal [], a[10..7]
+
+ assert_raise(TypeError) {a['cat']}
+ end
+
+ def test_ASET # '[]='
+ a = @cls[*(0..99).to_a]
+ assert_equal(0, a[0] = 0)
+ assert_equal(@cls[0] + @cls[*(1..99).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(0, a[10,10] = 0)
+ assert_equal(@cls[*(0..9).to_a] + @cls[0] + @cls[*(20..99).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(0, a[-1] = 0)
+ assert_equal(@cls[*(0..98).to_a] + @cls[0], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(0, a[-10, 10] = 0)
+ assert_equal(@cls[*(0..89).to_a] + @cls[0], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(0, a[0,1000] = 0)
+ assert_equal(@cls[0] , a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(0, a[10..19] = 0)
+ assert_equal(@cls[*(0..9).to_a] + @cls[0] + @cls[*(20..99).to_a], a)
+
+ b = @cls[*%w( a b c )]
+ a = @cls[*(0..99).to_a]
+ assert_equal(b, a[0,1] = b)
+ assert_equal(b + @cls[*(1..99).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(b, a[10,10] = b)
+ assert_equal(@cls[*(0..9).to_a] + b + @cls[*(20..99).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(b, a[-1, 1] = b)
+ assert_equal(@cls[*(0..98).to_a] + b, a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(b, a[-10, 10] = b)
+ assert_equal(@cls[*(0..89).to_a] + b, a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(b, a[0,1000] = b)
+ assert_equal(b , a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(b, a[10..19] = b)
+ assert_equal(@cls[*(0..9).to_a] + b + @cls[*(20..99).to_a], a)
+
+ # Ruby 1.8 feature change:
+ # assigning nil does not remove elements.
+=begin
+ a = @cls[*(0..99).to_a]
+ assert_equal(nil, a[0,1] = nil)
+ assert_equal(@cls[*(1..99).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(nil, a[10,10] = nil)
+ assert_equal(@cls[*(0..9).to_a] + @cls[*(20..99).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(nil, a[-1, 1] = nil)
+ assert_equal(@cls[*(0..98).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(nil, a[-10, 10] = nil)
+ assert_equal(@cls[*(0..89).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(nil, a[0,1000] = nil)
+ assert_equal(@cls[] , a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(nil, a[10..19] = nil)
+ assert_equal(@cls[*(0..9).to_a] + @cls[*(20..99).to_a], a)
+=end
+
+ a = @cls[1, 2, 3]
+ a[1, 0] = a
+ assert_equal([1, 1, 2, 3, 2, 3], a)
+
+ a = @cls[1, 2, 3]
+ a[-1, 0] = a
+ assert_equal([1, 2, 1, 2, 3, 3], a)
+
+ a = @cls[]
+ a[5,0] = [5]
+ assert_equal([nil, nil, nil, nil, nil, 5], a)
+
+ a = @cls[1]
+ a[1,0] = [2]
+ assert_equal([1, 2], a)
+
+ a = @cls[1]
+ a[1,1] = [2]
+ assert_equal([1, 2], a)
+ end
+
+ def test_assoc
+ a1 = @cls[*%w( cat feline )]
+ a2 = @cls[*%w( dog canine )]
+ a3 = @cls[*%w( mule asinine )]
+
+ a = @cls[ a1, a2, a3 ]
+
+ assert_equal(a1, a.assoc('cat'))
+ assert_equal(a3, a.assoc('mule'))
+ assert_equal(nil, a.assoc('asinine'))
+ assert_equal(nil, a.assoc('wombat'))
+ assert_equal(nil, a.assoc(1..2))
+ end
+
+ def test_at
+ a = @cls[*(0..99).to_a]
+ assert_equal(0, a.at(0))
+ assert_equal(10, a.at(10))
+ assert_equal(99, a.at(99))
+ assert_equal(nil, a.at(100))
+ assert_equal(99, a.at(-1))
+ assert_equal(0, a.at(-100))
+ assert_equal(nil, a.at(-101))
+ assert_raise(TypeError) { a.at('cat') }
+ end
+
+ def test_clear
+ a = @cls[1, 2, 3]
+ b = a.clear
+ assert_equal(@cls[], a)
+ assert_equal(@cls[], b)
+ assert_equal(a.__id__, b.__id__)
+ end
+
+ def test_clone
+ for taint in [ false, true ]
+ for frozen in [ false, true ]
+ a = @cls[*(0..99).to_a]
+ a.taint if taint
+ a.freeze if frozen
+ b = a.clone
+
+ assert_equal(a, b)
+ assert_not_equal(a.__id__, b.__id__)
+ assert_equal(a.frozen?, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
+ end
+ end
+ end
+
+ def test_collect
+ a = @cls[ 1, 'cat', 1..1 ]
+ assert_equal([ Fixnum, String, Range], a.collect {|e| e.class} )
+ assert_equal([ 99, 99, 99], a.collect { 99 } )
+
+ assert_equal([], @cls[].collect { 99 })
+
+ # Ruby 1.9 feature change:
+ # Enumerable#collect without block returns an Enumerator.
+ #assert_equal([1, 2, 3], @cls[1, 2, 3].collect)
+ assert_kind_of Enumerator, @cls[1, 2, 3].collect
+ end
+
+ # also update map!
+ def test_collect!
+ a = @cls[ 1, 'cat', 1..1 ]
+ assert_equal([ Fixnum, String, Range], a.collect! {|e| e.class} )
+ assert_equal([ Fixnum, String, Range], a)
+
+ a = @cls[ 1, 'cat', 1..1 ]
+ assert_equal([ 99, 99, 99], a.collect! { 99 } )
+ assert_equal([ 99, 99, 99], a)
+
+ a = @cls[ ]
+ assert_equal([], a.collect! { 99 })
+ assert_equal([], a)
+ end
+
+ def test_compact
+ a = @cls[ 1, nil, nil, 2, 3, nil, 4 ]
+ assert_equal(@cls[1, 2, 3, 4], a.compact)
+
+ a = @cls[ nil, 1, nil, 2, 3, nil, 4 ]
+ assert_equal(@cls[1, 2, 3, 4], a.compact)
+
+ a = @cls[ 1, nil, nil, 2, 3, nil, 4, nil ]
+ assert_equal(@cls[1, 2, 3, 4], a.compact)
+
+ a = @cls[ 1, 2, 3, 4 ]
+ assert_equal(@cls[1, 2, 3, 4], a.compact)
+ end
+
+ def test_compact!
+ a = @cls[ 1, nil, nil, 2, 3, nil, 4 ]
+ assert_equal(@cls[1, 2, 3, 4], a.compact!)
+ assert_equal(@cls[1, 2, 3, 4], a)
+
+ a = @cls[ nil, 1, nil, 2, 3, nil, 4 ]
+ assert_equal(@cls[1, 2, 3, 4], a.compact!)
+ assert_equal(@cls[1, 2, 3, 4], a)
+
+ a = @cls[ 1, nil, nil, 2, 3, nil, 4, nil ]
+ assert_equal(@cls[1, 2, 3, 4], a.compact!)
+ assert_equal(@cls[1, 2, 3, 4], a)
+
+ a = @cls[ 1, 2, 3, 4 ]
+ assert_equal(nil, a.compact!)
+ assert_equal(@cls[1, 2, 3, 4], a)
+ end
+
+ def test_concat
+ assert_equal(@cls[1, 2, 3, 4], @cls[1, 2].concat(@cls[3, 4]))
+ assert_equal(@cls[1, 2, 3, 4], @cls[].concat(@cls[1, 2, 3, 4]))
+ assert_equal(@cls[1, 2, 3, 4], @cls[1, 2, 3, 4].concat(@cls[]))
+ assert_equal(@cls[], @cls[].concat(@cls[]))
+ assert_equal(@cls[@cls[1, 2], @cls[3, 4]], @cls[@cls[1, 2]].concat(@cls[@cls[3, 4]]))
+
+ a = @cls[1, 2, 3]
+ a.concat(a)
+ assert_equal([1, 2, 3, 1, 2, 3], a)
+
+ assert_raise(TypeError) { [0].concat(:foo) }
+ assert_raise(RuntimeError) { [0].freeze.concat(:foo) }
+ end
+
+ def test_count
+ a = @cls[1, 2, 3, 1, 2]
+ assert_equal(5, a.count)
+ assert_equal(2, a.count(1))
+ assert_equal(3, a.count {|x| x % 2 == 1 })
+ assert_equal(2, a.count(1) {|x| x % 2 == 1 })
+ assert_raise(ArgumentError) { a.count(0, 1) }
+
+ bug8654 = '[ruby-core:56072]'
+ assert_in_out_err [], <<-EOS, ["0"], [], bug8654
+ a1 = []
+ a2 = Array.new(100) { |i| i }
+ a2.count do |i|
+ p i
+ a2.replace(a1) if i == 0
+ end
+ EOS
+
+ assert_in_out_err [], <<-EOS, ["[]", "0"], [], bug8654
+ ARY = Array.new(100) { |i| i }
+ class Fixnum
+ alias old_equal ==
+ def == other
+ ARY.replace([]) if self.equal?(0)
+ p ARY
+ self.equal?(other)
+ end
+ end
+ p ARY.count(42)
+ EOS
+ end
+
+ def test_delete
+ a = @cls[*('cab'..'cat').to_a]
+ assert_equal('cap', a.delete('cap'))
+ assert_equal(@cls[*('cab'..'cao').to_a] + @cls[*('caq'..'cat').to_a], a)
+
+ a = @cls[*('cab'..'cat').to_a]
+ assert_equal('cab', a.delete('cab'))
+ assert_equal(@cls[*('cac'..'cat').to_a], a)
+
+ a = @cls[*('cab'..'cat').to_a]
+ assert_equal('cat', a.delete('cat'))
+ assert_equal(@cls[*('cab'..'cas').to_a], a)
+
+ a = @cls[*('cab'..'cat').to_a]
+ assert_equal(nil, a.delete('cup'))
+ assert_equal(@cls[*('cab'..'cat').to_a], a)
+
+ a = @cls[*('cab'..'cat').to_a]
+ assert_equal(99, a.delete('cup') { 99 } )
+ assert_equal(@cls[*('cab'..'cat').to_a], a)
+
+ o = Object.new
+ def o.==(other); true; end
+ o2 = Object.new
+ def o2.==(other); true; end
+ a = @cls[1, o, o2, 2]
+ assert_equal(o2, a.delete(42))
+ assert_equal([1, 2], a)
+ end
+
+ def test_delete_at
+ a = @cls[*(1..5).to_a]
+ assert_equal(3, a.delete_at(2))
+ assert_equal(@cls[1, 2, 4, 5], a)
+
+ a = @cls[*(1..5).to_a]
+ assert_equal(4, a.delete_at(-2))
+ assert_equal(@cls[1, 2, 3, 5], a)
+
+ a = @cls[*(1..5).to_a]
+ assert_equal(nil, a.delete_at(5))
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[*(1..5).to_a]
+ assert_equal(nil, a.delete_at(-6))
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+ end
+
+ # also reject!
+ def test_delete_if
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.delete_if { false })
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.delete_if { true })
+ assert_equal(@cls[], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.delete_if { |i| i > 3 })
+ assert_equal(@cls[1, 2, 3], a)
+
+ bug2545 = '[ruby-core:27366]'
+ a = @cls[ 5, 6, 7, 8, 9, 10 ]
+ assert_equal(9, a.delete_if {|i| break i if i > 8; assert_equal(a[0], i) || true if i < 7})
+ assert_equal(@cls[7, 8, 9, 10], a, bug2545)
+ end
+
+ def test_dup
+ for taint in [ false, true ]
+ for frozen in [ false, true ]
+ a = @cls[*(0..99).to_a]
+ a.taint if taint
+ a.freeze if frozen
+ b = a.dup
+
+ assert_equal(a, b)
+ assert_not_equal(a.__id__, b.__id__)
+ assert_equal(false, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
+ end
+ end
+ end
+
+ def test_each
+ a = @cls[*%w( ant bat cat dog )]
+ i = 0
+ a.each { |e|
+ assert_equal(a[i], e)
+ i += 1
+ }
+ assert_equal(4, i)
+
+ a = @cls[]
+ i = 0
+ a.each { |e|
+ assert_equal(a[i], e)
+ i += 1
+ }
+ assert_equal(0, i)
+
+ assert_equal(a, a.each {})
+ end
+
+ def test_each_index
+ a = @cls[*%w( ant bat cat dog )]
+ i = 0
+ a.each_index { |ind|
+ assert_equal(i, ind)
+ i += 1
+ }
+ assert_equal(4, i)
+
+ a = @cls[]
+ i = 0
+ a.each_index { |ind|
+ assert_equal(i, ind)
+ i += 1
+ }
+ assert_equal(0, i)
+
+ assert_equal(a, a.each_index {})
+ end
+
+ def test_empty?
+ assert_empty(@cls[])
+ assert_not_empty(@cls[1])
+ end
+
+ def test_eql?
+ assert_send([@cls[], :eql?, @cls[]])
+ assert_send([@cls[1], :eql?, @cls[1]])
+ assert_send([@cls[1, 1, 2, 2], :eql?, @cls[1, 1, 2, 2]])
+ assert_not_send([@cls[1.0, 1.0, 2.0, 2.0], :eql?, @cls[1, 1, 2, 2]])
+ end
+
+ def test_fill
+ assert_equal(@cls[], @cls[].fill(99))
+ assert_equal(@cls[], @cls[].fill(99, 0))
+ assert_equal(@cls[99], @cls[].fill(99, 0, 1))
+ assert_equal(@cls[99], @cls[].fill(99, 0..0))
+
+ assert_equal(@cls[99], @cls[1].fill(99))
+ assert_equal(@cls[99], @cls[1].fill(99, 0))
+ assert_equal(@cls[99], @cls[1].fill(99, 0, 1))
+ assert_equal(@cls[99], @cls[1].fill(99, 0..0))
+
+ assert_equal(@cls[99, 99], @cls[1, 2].fill(99))
+ assert_equal(@cls[99, 99], @cls[1, 2].fill(99, 0))
+ assert_equal(@cls[99, 99], @cls[1, 2].fill(99, nil))
+ assert_equal(@cls[1, 99], @cls[1, 2].fill(99, 1, nil))
+ assert_equal(@cls[99, 2], @cls[1, 2].fill(99, 0, 1))
+ assert_equal(@cls[99, 2], @cls[1, 2].fill(99, 0..0))
+ end
+
+ def test_first
+ assert_equal(3, @cls[3, 4, 5].first)
+ assert_equal(nil, @cls[].first)
+ end
+
+ def test_flatten
+ a1 = @cls[ 1, 2, 3]
+ a2 = @cls[ 5, 6 ]
+ a3 = @cls[ 4, a2 ]
+ a4 = @cls[ a1, a3 ]
+ assert_equal(@cls[1, 2, 3, 4, 5, 6], a4.flatten)
+ assert_equal(@cls[ a1, a3], a4)
+
+ a5 = @cls[ a1, @cls[], a3 ]
+ assert_equal(@cls[1, 2, 3, 4, 5, 6], a5.flatten)
+ assert_equal(@cls[], @cls[].flatten)
+ assert_equal(@cls[],
+ @cls[@cls[@cls[@cls[],@cls[]],@cls[@cls[]],@cls[]],@cls[@cls[@cls[]]]].flatten)
+
+ assert_raise(TypeError, "[ruby-dev:31197]") { [[]].flatten("") }
+
+ a6 = @cls[[1, 2], 3]
+ a6.taint
+ a7 = a6.flatten
+ assert_equal(true, a7.tainted?)
+
+ a8 = @cls[[1, 2], 3]
+ a9 = a8.flatten(0)
+ assert_equal(a8, a9)
+ assert_not_same(a8, a9)
+ end
+
+ def test_flatten!
+ a1 = @cls[ 1, 2, 3]
+ a2 = @cls[ 5, 6 ]
+ a3 = @cls[ 4, a2 ]
+ a4 = @cls[ a1, a3 ]
+ assert_equal(@cls[1, 2, 3, 4, 5, 6], a4.flatten!)
+ assert_equal(@cls[1, 2, 3, 4, 5, 6], a4)
+
+ a5 = @cls[ a1, @cls[], a3 ]
+ assert_equal(@cls[1, 2, 3, 4, 5, 6], a5.flatten!)
+ assert_nil(a5.flatten!(0), '[ruby-core:23382]')
+ assert_equal(@cls[1, 2, 3, 4, 5, 6], a5)
+
+ assert_nil(@cls[].flatten!)
+ assert_equal(@cls[],
+ @cls[@cls[@cls[@cls[],@cls[]],@cls[@cls[]],@cls[]],@cls[@cls[@cls[]]]].flatten!)
+
+ assert_nil(@cls[].flatten!(0), '[ruby-core:23382]')
+ end
+
+ def test_flatten_with_callcc
+ need_continuation
+ o = Object.new
+ def o.to_ary() callcc {|k| @cont = k; [1,2,3]} end
+ begin
+ assert_equal([10, 20, 1, 2, 3, 30, 1, 2, 3, 40], [10, 20, o, 30, o, 40].flatten)
+ rescue => e
+ else
+ o.instance_eval {@cont}.call
+ end
+ assert_instance_of(RuntimeError, e, '[ruby-dev:34798]')
+ assert_match(/reentered/, e.message, '[ruby-dev:34798]')
+ end
+
+ def test_permutation_with_callcc
+ need_continuation
+ n = 1000
+ cont = nil
+ ary = [1,2,3]
+ begin
+ ary.permutation {
+ callcc {|k| cont = k} unless cont
+ }
+ rescue => e
+ end
+ n -= 1
+ cont.call if 0 < n
+ assert_instance_of(RuntimeError, e)
+ assert_match(/reentered/, e.message)
+ end
+
+ def test_product_with_callcc
+ need_continuation
+ n = 1000
+ cont = nil
+ ary = [1,2,3]
+ begin
+ ary.product {
+ callcc {|k| cont = k} unless cont
+ }
+ rescue => e
+ end
+ n -= 1
+ cont.call if 0 < n
+ assert_instance_of(RuntimeError, e)
+ assert_match(/reentered/, e.message)
+ end
+
+ def test_combination_with_callcc
+ need_continuation
+ n = 1000
+ cont = nil
+ ary = [1,2,3]
+ begin
+ ary.combination(2) {
+ callcc {|k| cont = k} unless cont
+ }
+ rescue => e
+ end
+ n -= 1
+ cont.call if 0 < n
+ assert_instance_of(RuntimeError, e)
+ assert_match(/reentered/, e.message)
+ end
+
+ def test_repeated_permutation_with_callcc
+ need_continuation
+ n = 1000
+ cont = nil
+ ary = [1,2,3]
+ begin
+ ary.repeated_permutation(2) {
+ callcc {|k| cont = k} unless cont
+ }
+ rescue => e
+ end
+ n -= 1
+ cont.call if 0 < n
+ assert_instance_of(RuntimeError, e)
+ assert_match(/reentered/, e.message)
+ end
+
+ def test_repeated_combination_with_callcc
+ need_continuation
+ n = 1000
+ cont = nil
+ ary = [1,2,3]
+ begin
+ ary.repeated_combination(2) {
+ callcc {|k| cont = k} unless cont
+ }
+ rescue => e
+ end
+ n -= 1
+ cont.call if 0 < n
+ assert_instance_of(RuntimeError, e)
+ assert_match(/reentered/, e.message)
+ end
+
+ def test_hash
+ a1 = @cls[ 'cat', 'dog' ]
+ a2 = @cls[ 'cat', 'dog' ]
+ a3 = @cls[ 'dog', 'cat' ]
+ assert_equal(a1.hash, a2.hash)
+ assert_not_equal(a1.hash, a3.hash)
+ bug9231 = '[ruby-core:58993] [Bug #9231]'
+ assert_not_equal(false.hash, @cls[].hash, bug9231)
+ end
+
+ def test_include?
+ a = @cls[ 'cat', 99, /a/, @cls[ 1, 2, 3] ]
+ assert_include(a, 'cat')
+ assert_include(a, 99)
+ assert_include(a, /a/)
+ assert_include(a, [1,2,3])
+ assert_not_include(a, 'ca')
+ assert_not_include(a, [1,2])
+ end
+
+ def test_index
+ a = @cls[ 'cat', 99, /a/, 99, @cls[ 1, 2, 3] ]
+ assert_equal(0, a.index('cat'))
+ assert_equal(1, a.index(99))
+ assert_equal(4, a.index([1,2,3]))
+ assert_nil(a.index('ca'))
+ assert_nil(a.index([1,2]))
+
+ assert_equal(1, a.index(99) {|x| x == 'cat' })
+ end
+
+ def test_values_at
+ a = @cls[*('a'..'j').to_a]
+ assert_equal(@cls['a', 'c', 'e'], a.values_at(0, 2, 4))
+ assert_equal(@cls['j', 'h', 'f'], a.values_at(-1, -3, -5))
+ assert_equal(@cls['h', nil, 'a'], a.values_at(-3, 99, 0))
+ end
+
+ def test_join
+ $, = ""
+ a = @cls[]
+ assert_equal("", a.join)
+ assert_equal("", a.join(','))
+ assert_equal(Encoding::US_ASCII, a.join.encoding)
+
+ $, = ""
+ a = @cls[1, 2]
+ assert_equal("12", a.join)
+ assert_equal("12", a.join(nil))
+ assert_equal("1,2", a.join(','))
+
+ $, = ""
+ a = @cls[1, 2, 3]
+ assert_equal("123", a.join)
+ assert_equal("123", a.join(nil))
+ assert_equal("1,2,3", a.join(','))
+
+ $, = ":"
+ a = @cls[1, 2, 3]
+ assert_equal("1:2:3", a.join)
+ assert_equal("1:2:3", a.join(nil))
+ assert_equal("1,2,3", a.join(','))
+
+ $, = ""
+ a = @cls[1, 2, 3]
+ a.taint
+ s = a.join
+ assert_equal(true, s.tainted?)
+
+ bug5902 = '[ruby-core:42161]'
+ sep = ":".taint
+
+ s = @cls[].join(sep)
+ assert_equal(false, s.tainted?, bug5902)
+ s = @cls[1].join(sep)
+ assert_equal(false, s.tainted?, bug5902)
+ s = @cls[1, 2].join(sep)
+ assert_equal(true, s.tainted?, bug5902)
+
+ e = ''.force_encoding('EUC-JP')
+ u = ''.force_encoding('UTF-8')
+ assert_equal(Encoding::US_ASCII, [[]].join.encoding)
+ assert_equal(Encoding::US_ASCII, [1, [u]].join.encoding)
+ assert_equal(Encoding::UTF_8, [u, [e]].join.encoding)
+ assert_equal(Encoding::UTF_8, [u, [1]].join.encoding)
+ bug5379 = '[ruby-core:39776]'
+ assert_equal(Encoding::US_ASCII, [[], u, nil].join.encoding, bug5379)
+ assert_equal(Encoding::UTF_8, [[], "\u3042", nil].join.encoding, bug5379)
+ ensure
+ $, = nil
+ end
+
+ def test_last
+ assert_equal(nil, @cls[].last)
+ assert_equal(1, @cls[1].last)
+ assert_equal(99, @cls[*(3..99).to_a].last)
+ end
+
+ def test_length
+ assert_equal(0, @cls[].length)
+ assert_equal(1, @cls[1].length)
+ assert_equal(2, @cls[1, nil].length)
+ assert_equal(2, @cls[nil, 1].length)
+ assert_equal(234, @cls[*(0..233).to_a].length)
+ end
+
+ # also update collect!
+ def test_map!
+ a = @cls[ 1, 'cat', 1..1 ]
+ assert_equal(@cls[ Fixnum, String, Range], a.map! {|e| e.class} )
+ assert_equal(@cls[ Fixnum, String, Range], a)
+
+ a = @cls[ 1, 'cat', 1..1 ]
+ assert_equal(@cls[ 99, 99, 99], a.map! { 99 } )
+ assert_equal(@cls[ 99, 99, 99], a)
+
+ a = @cls[ ]
+ assert_equal(@cls[], a.map! { 99 })
+ assert_equal(@cls[], a)
+ end
+
+ def test_pack
+ a = @cls[*%w( cat wombat x yy)]
+ assert_equal("catwomx yy ", a.pack("A3A3A3A3"))
+ assert_equal("cat", a.pack("A*"))
+ assert_equal("cwx yy ", a.pack("A3@1A3@2A3A3"))
+ assert_equal("catwomx\000\000yy\000", a.pack("a3a3a3a3"))
+ assert_equal("cat", a.pack("a*"))
+ assert_equal("ca", a.pack("a2"))
+ assert_equal("cat\000\000", a.pack("a5"))
+
+ assert_equal("\x61", @cls["01100001"].pack("B8"))
+ assert_equal("\x61", @cls["01100001"].pack("B*"))
+ assert_equal("\x61", @cls["0110000100110111"].pack("B8"))
+ assert_equal("\x61\x37", @cls["0110000100110111"].pack("B16"))
+ assert_equal("\x61\x37", @cls["01100001", "00110111"].pack("B8B8"))
+ assert_equal("\x60", @cls["01100001"].pack("B4"))
+ assert_equal("\x40", @cls["01100001"].pack("B2"))
+
+ assert_equal("\x86", @cls["01100001"].pack("b8"))
+ assert_equal("\x86", @cls["01100001"].pack("b*"))
+ assert_equal("\x86", @cls["0110000100110111"].pack("b8"))
+ assert_equal("\x86\xec", @cls["0110000100110111"].pack("b16"))
+ assert_equal("\x86\xec", @cls["01100001", "00110111"].pack("b8b8"))
+ assert_equal("\x06", @cls["01100001"].pack("b4"))
+ assert_equal("\x02", @cls["01100001"].pack("b2"))
+
+ assert_equal("ABC", @cls[ 65, 66, 67 ].pack("C3"))
+ assert_equal("\377BC", @cls[ -1, 66, 67 ].pack("C*"))
+ assert_equal("ABC", @cls[ 65, 66, 67 ].pack("c3"))
+ assert_equal("\377BC", @cls[ -1, 66, 67 ].pack("c*"))
+
+
+ assert_equal("AB\n\x10", @cls["4142", "0a", "12"].pack("H4H2H1"))
+ assert_equal("AB\n\x02", @cls["1424", "a0", "21"].pack("h4h2h1"))
+
+ assert_equal("abc=02def=\ncat=\n=01=\n",
+ @cls["abc\002def", "cat", "\001"].pack("M9M3M4"))
+
+ assert_equal("aGVsbG8K\n", @cls["hello\n"].pack("m"))
+ assert_equal(",:&5L;&\\*:&5L;&\\*\n", @cls["hello\nhello\n"].pack("u"))
+
+ assert_equal("\u{a9 42 2260}", @cls[0xa9, 0x42, 0x2260].pack("U*"))
+
+
+ format = "c2x5CCxsdils_l_a6";
+ # Need the expression in here to force ary[5] to be numeric. This avoids
+ # test2 failing because ary2 goes str->numeric->str and ary does not.
+ ary = [1, -100, 127, 128, 32767, 987.654321098/100.0,
+ 12345, 123456, -32767, -123456, "abcdef"]
+ x = ary.pack(format)
+ ary2 = x.unpack(format)
+
+ assert_equal(ary.length, ary2.length)
+ assert_equal(ary.join(':'), ary2.join(':'))
+ assert_not_nil(x =~ /def/)
+
+=begin
+ skipping "Not tested:
+ D,d & double-precision float, native format\\
+ E & double-precision float, little-endian byte order\\
+ e & single-precision float, little-endian byte order\\
+ F,f & single-precision float, native format\\
+ G & double-precision float, network (big-endian) byte order\\
+ g & single-precision float, network (big-endian) byte order\\
+ I & unsigned integer\\
+ i & integer\\
+ L & unsigned long\\
+ l & long\\
+
+ N & long, network (big-endian) byte order\\
+ n & short, network (big-endian) byte-order\\
+ P & pointer to a structure (fixed-length string)\\
+ p & pointer to a null-terminated string\\
+ S & unsigned short\\
+ s & short\\
+ V & long, little-endian byte order\\
+ v & short, little-endian byte order\\
+ X & back up a byte\\
+ x & null byte\\
+ Z & ASCII string (null padded, count is width)\\
+"
+=end
+ end
+
+ def test_pop
+ a = @cls[ 'cat', 'dog' ]
+ assert_equal('dog', a.pop)
+ assert_equal(@cls['cat'], a)
+ assert_equal('cat', a.pop)
+ assert_equal(@cls[], a)
+ assert_nil(a.pop)
+ assert_equal(@cls[], a)
+ end
+
+ def test_push
+ a = @cls[1, 2, 3]
+ assert_equal(@cls[1, 2, 3, 4, 5], a.push(4, 5))
+ assert_equal(@cls[1, 2, 3, 4, 5, nil], a.push(nil))
+ # Ruby 1.8 feature:
+ # Array#push accepts any number of arguments.
+ #assert_raise(ArgumentError, "a.push()") { a.push() }
+ a.push
+ assert_equal @cls[1, 2, 3, 4, 5, nil], a
+ a.push 6, 7
+ assert_equal @cls[1, 2, 3, 4, 5, nil, 6, 7], a
+ end
+
+ def test_rassoc
+ a1 = @cls[*%w( cat feline )]
+ a2 = @cls[*%w( dog canine )]
+ a3 = @cls[*%w( mule asinine )]
+ a = @cls[ a1, a2, a3 ]
+
+ assert_equal(a1, a.rassoc('feline'))
+ assert_equal(a3, a.rassoc('asinine'))
+ assert_equal(nil, a.rassoc('dog'))
+ assert_equal(nil, a.rassoc('mule'))
+ assert_equal(nil, a.rassoc(1..2))
+ end
+
+ # also delete_if
+ def test_reject!
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(nil, a.reject! { false })
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.reject! { true })
+ assert_equal(@cls[], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.reject! { |i| i > 3 })
+ assert_equal(@cls[1, 2, 3], a)
+
+ bug2545 = '[ruby-core:27366]'
+ a = @cls[ 5, 6, 7, 8, 9, 10 ]
+ assert_equal(9, a.reject! {|i| break i if i > 8; assert_equal(a[0], i) || true if i < 7})
+ assert_equal(@cls[7, 8, 9, 10], a, bug2545)
+ end
+
+ def test_replace
+ a = @cls[ 1, 2, 3]
+ a_id = a.__id__
+ assert_equal(@cls[4, 5, 6], a.replace(@cls[4, 5, 6]))
+ assert_equal(@cls[4, 5, 6], a)
+ assert_equal(a_id, a.__id__)
+ assert_equal(@cls[], a.replace(@cls[]))
+
+ fa = a.dup.freeze
+ assert_nothing_raised(RuntimeError) { a.replace(a) }
+ assert_raise(RuntimeError) { fa.replace(fa) }
+ assert_raise(ArgumentError) { fa.replace() }
+ assert_raise(TypeError) { a.replace(42) }
+ assert_raise(RuntimeError) { fa.replace(42) }
+ end
+
+ def test_reverse
+ a = @cls[*%w( dog cat bee ant )]
+ assert_equal(@cls[*%w(ant bee cat dog)], a.reverse)
+ assert_equal(@cls[*%w(dog cat bee ant)], a)
+ assert_equal(@cls[], @cls[].reverse)
+ end
+
+ def test_reverse!
+ a = @cls[*%w( dog cat bee ant )]
+ assert_equal(@cls[*%w(ant bee cat dog)], a.reverse!)
+ assert_equal(@cls[*%w(ant bee cat dog)], a)
+ # Ruby 1.8 feature change:
+ # Array#reverse always returns self.
+ #assert_nil(@cls[].reverse!)
+ assert_equal @cls[], @cls[].reverse!
+ end
+
+ def test_reverse_each
+ a = @cls[*%w( dog cat bee ant )]
+ i = a.length
+ a.reverse_each { |e|
+ i -= 1
+ assert_equal(a[i], e)
+ }
+ assert_equal(0, i)
+
+ a = @cls[]
+ i = 0
+ a.reverse_each { |e|
+ i += 1
+ assert(false, "Never get here")
+ }
+ assert_equal(0, i)
+ end
+
+ def test_rindex
+ a = @cls[ 'cat', 99, /a/, 99, [ 1, 2, 3] ]
+ assert_equal(0, a.rindex('cat'))
+ assert_equal(3, a.rindex(99))
+ assert_equal(4, a.rindex([1,2,3]))
+ assert_nil(a.rindex('ca'))
+ assert_nil(a.rindex([1,2]))
+
+ assert_equal(3, a.rindex(99) {|x| x == [1,2,3] })
+ end
+
+ def test_shift
+ a = @cls[ 'cat', 'dog' ]
+ assert_equal('cat', a.shift)
+ assert_equal(@cls['dog'], a)
+ assert_equal('dog', a.shift)
+ assert_equal(@cls[], a)
+ assert_nil(a.shift)
+ assert_equal(@cls[], a)
+ end
+
+ def test_size
+ assert_equal(0, @cls[].size)
+ assert_equal(1, @cls[1].size)
+ assert_equal(100, @cls[*(0..99).to_a].size)
+ end
+
+ def test_slice
+ a = @cls[*(1..100).to_a]
+
+ assert_equal(1, a.slice(0))
+ assert_equal(100, a.slice(99))
+ assert_nil(a.slice(100))
+ assert_equal(100, a.slice(-1))
+ assert_equal(99, a.slice(-2))
+ assert_equal(1, a.slice(-100))
+ assert_nil(a.slice(-101))
+
+ assert_equal(@cls[1], a.slice(0,1))
+ assert_equal(@cls[100], a.slice(99,1))
+ assert_equal(@cls[], a.slice(100,1))
+ assert_equal(@cls[100], a.slice(99,100))
+ assert_equal(@cls[100], a.slice(-1,1))
+ assert_equal(@cls[99], a.slice(-2,1))
+
+ assert_equal(@cls[10, 11, 12], a.slice(9, 3))
+ assert_equal(@cls[10, 11, 12], a.slice(-91, 3))
+
+ assert_nil(a.slice(-101, 2))
+
+ assert_equal(@cls[1], a.slice(0..0))
+ assert_equal(@cls[100], a.slice(99..99))
+ assert_equal(@cls[], a.slice(100..100))
+ assert_equal(@cls[100], a.slice(99..200))
+ assert_equal(@cls[100], a.slice(-1..-1))
+ assert_equal(@cls[99], a.slice(-2..-2))
+
+ assert_equal(@cls[10, 11, 12], a.slice(9..11))
+ assert_equal(@cls[10, 11, 12], a.slice(-91..-89))
+
+ assert_nil(a.slice(-101..-1))
+
+ assert_nil(a.slice(10, -3))
+ # Ruby 1.8 feature change:
+ # Array#slice[size..x] always returns [].
+ #assert_nil(a.slice(10..7))
+ assert_equal @cls[], a.slice(10..7)
+ end
+
+ def test_slice!
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(3, a.slice!(2))
+ assert_equal(@cls[1, 2, 4, 5], a)
+
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(4, a.slice!(-2))
+ assert_equal(@cls[1, 2, 3, 5], a)
+
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(@cls[3,4], a.slice!(2,2))
+ assert_equal(@cls[1, 2, 5], a)
+
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(@cls[4,5], a.slice!(-2,2))
+ assert_equal(@cls[1, 2, 3], a)
+
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(@cls[3,4], a.slice!(2..3))
+ assert_equal(@cls[1, 2, 5], a)
+
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(nil, a.slice!(20))
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(nil, a.slice!(-6))
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(nil, a.slice!(-6..4))
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[1, 2, 3, 4, 5]
+ assert_equal(nil, a.slice!(-6,2))
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ assert_raise(ArgumentError) { @cls[1].slice! }
+ assert_raise(ArgumentError) { @cls[1].slice!(0, 0, 0) }
+ end
+
+ def test_sort
+ a = @cls[ 4, 1, 2, 3 ]
+ assert_equal(@cls[1, 2, 3, 4], a.sort)
+ assert_equal(@cls[4, 1, 2, 3], a)
+
+ assert_equal(@cls[4, 3, 2, 1], a.sort { |x, y| y <=> x} )
+ assert_equal(@cls[4, 1, 2, 3], a)
+
+ assert_equal(@cls[1, 2, 3, 4], a.sort { |x, y| (x - y) * (2**100) })
+
+ a.fill(1)
+ assert_equal(@cls[1, 1, 1, 1], a.sort)
+
+ assert_equal(@cls[], @cls[].sort)
+ end
+
+ def test_sort!
+ a = @cls[ 4, 1, 2, 3 ]
+ assert_equal(@cls[1, 2, 3, 4], a.sort!)
+ assert_equal(@cls[1, 2, 3, 4], a)
+
+ assert_equal(@cls[4, 3, 2, 1], a.sort! { |x, y| y <=> x} )
+ assert_equal(@cls[4, 3, 2, 1], a)
+
+ a.fill(1)
+ assert_equal(@cls[1, 1, 1, 1], a.sort!)
+
+ assert_equal(@cls[1], @cls[1].sort!)
+ assert_equal(@cls[], @cls[].sort!)
+
+ a = @cls[4, 3, 2, 1]
+ a.sort! {|m, n| a.replace([9, 8, 7, 6]); m <=> n }
+ assert_equal([1, 2, 3, 4], a)
+
+ a = @cls[4, 3, 2, 1]
+ a.sort! {|m, n| a.replace([9, 8, 7]); m <=> n }
+ assert_equal([1, 2, 3, 4], a)
+ end
+
+ def test_sort_with_callcc
+ need_continuation
+ n = 1000
+ cont = nil
+ ary = (1..100).to_a
+ begin
+ ary.sort! {|a,b|
+ callcc {|k| cont = k} unless cont
+ assert_equal(100, ary.size, '[ruby-core:16679]')
+ a <=> b
+ }
+ rescue => e
+ end
+ n -= 1
+ cont.call if 0 < n
+ assert_instance_of(RuntimeError, e, '[ruby-core:16679]')
+ assert_match(/reentered/, e.message, '[ruby-core:16679]')
+ end
+
+ def test_sort_with_replace
+ xary = (1..100).to_a
+ 100.times do
+ ary = (1..100).to_a
+ ary.sort! {|a,b| ary.replace(xary); a <=> b}
+ GC.start
+ assert_equal(xary, ary, '[ruby-dev:34732]')
+ end
+ end
+
+ def test_sort_bang_with_freeze
+ ary = []
+ o1 = Object.new
+ o1.singleton_class.class_eval {
+ define_method(:<=>) {|v|
+ ary.freeze
+ 1
+ }
+ }
+ o2 = o1.dup
+ ary << o1 << o2
+ orig = ary.dup
+ assert_raise(RuntimeError, "frozen during comparison") {ary.sort!}
+ assert_equal(orig, ary, "must not be modified once frozen")
+ end
+
+ def test_to_a
+ a = @cls[ 1, 2, 3 ]
+ a_id = a.__id__
+ assert_equal(a, a.to_a)
+ assert_equal(a_id, a.to_a.__id__)
+ end
+
+ def test_to_ary
+ a = [ 1, 2, 3 ]
+ b = @cls[*a]
+
+ a_id = a.__id__
+ assert_equal(a, b.to_ary)
+ if (@cls == Array)
+ assert_equal(a_id, a.to_ary.__id__)
+ end
+
+ o = Object.new
+ def o.to_ary
+ [4, 5]
+ end
+ assert_equal([1, 2, 3, 4, 5], a.concat(o))
+
+ o = Object.new
+ def o.to_ary
+ foo_bar()
+ end
+ assert_match(/foo_bar/, assert_raise(NoMethodError) {a.concat(o)}.message)
+ end
+
+ def test_to_s
+ $, = ""
+ a = @cls[]
+ assert_equal("[]", a.to_s)
+
+ $, = ""
+ a = @cls[1, 2]
+ assert_equal("[1, 2]", a.to_s)
+
+ $, = ""
+ a = @cls[1, 2, 3]
+ assert_equal("[1, 2, 3]", a.to_s)
+
+ $, = ":"
+ a = @cls[1, 2, 3]
+ assert_equal("[1, 2, 3]", a.to_s)
+ ensure
+ $, = nil
+ end
+
+ def test_to_h
+ kvp = Object.new
+ def kvp.to_ary
+ [:obtained, :via_to_ary]
+ end
+ array = [
+ [:key, :value],
+ kvp,
+ ]
+ assert_equal({key: :value, obtained: :via_to_ary}, array.to_h)
+
+ e = assert_raise(TypeError) {
+ [[:first_one, :ok], :not_ok].to_h
+ }
+ assert_equal "wrong element type Symbol at 1 (expected array)", e.message
+ e = assert_raise(ArgumentError) {
+ [[:first_one, :ok], [1, 2], [:not_ok]].to_h
+ }
+ assert_equal "wrong array length at 2 (expected 2, was 1)", e.message
+ end
+
+ def test_uniq
+ a = []
+ b = a.uniq
+ assert_equal([], a)
+ assert_equal([], b)
+ assert_not_same(a, b)
+
+ a = [1]
+ b = a.uniq
+ assert_equal([1], a)
+ assert_equal([1], b)
+ assert_not_same(a, b)
+
+ a = [1,1]
+ b = a.uniq
+ assert_equal([1,1], a)
+ assert_equal([1], b)
+ assert_not_same(a, b)
+
+ a = [1,2]
+ b = a.uniq
+ assert_equal([1,2], a)
+ assert_equal([1,2], b)
+ assert_not_same(a, b)
+
+ a = @cls[ 1, 2, 3, 2, 1, 2, 3, 4, nil ]
+ b = a.dup
+ assert_equal(@cls[1, 2, 3, 4, nil], a.uniq)
+ assert_equal(b, a)
+
+ c = @cls["a:def", "a:xyz", "b:abc", "b:xyz", "c:jkl"]
+ d = c.dup
+ assert_equal(@cls[ "a:def", "b:abc", "c:jkl" ], c.uniq {|s| s[/^\w+/]})
+ assert_equal(d, c)
+
+ assert_equal(@cls[1, 2, 3], @cls[1, 2, 3].uniq)
+
+ a = %w(a a)
+ b = a.uniq
+ assert_equal(%w(a a), a)
+ assert(a.none?(&:frozen?))
+ assert_equal(%w(a), b)
+ assert(b.none?(&:frozen?))
+
+ bug9340 = "[ruby-core:59457]"
+ ary = [bug9340, bug9340.dup, bug9340.dup]
+ assert_equal 1, ary.uniq.size
+ assert_same bug9340, ary.uniq[0]
+ end
+
+ def test_uniq_with_block
+ a = []
+ b = a.uniq {|v| v.even? }
+ assert_equal([], a)
+ assert_equal([], b)
+ assert_not_same(a, b)
+
+ a = [1]
+ b = a.uniq {|v| v.even? }
+ assert_equal([1], a)
+ assert_equal([1], b)
+ assert_not_same(a, b)
+
+ a = [1,3]
+ b = a.uniq {|v| v.even? }
+ assert_equal([1,3], a)
+ assert_equal([1], b)
+ assert_not_same(a, b)
+
+ a = %w(a a)
+ b = a.uniq {|v| v }
+ assert_equal(%w(a a), a)
+ assert(a.none?(&:frozen?))
+ assert_equal(%w(a), b)
+ assert(b.none?(&:frozen?))
+ end
+
+ def test_uniq!
+ a = []
+ b = a.uniq!
+ assert_equal(nil, b)
+
+ a = [1]
+ b = a.uniq!
+ assert_equal(nil, b)
+
+ a = [1,1]
+ b = a.uniq!
+ assert_equal([1], a)
+ assert_equal([1], b)
+ assert_same(a, b)
+
+ a = [1,2]
+ b = a.uniq!
+ assert_equal([1,2], a)
+ assert_equal(nil, b)
+
+ a = @cls[ 1, 2, 3, 2, 1, 2, 3, 4, nil ]
+ assert_equal(@cls[1, 2, 3, 4, nil], a.uniq!)
+ assert_equal(@cls[1, 2, 3, 4, nil], a)
+
+ c = @cls["a:def", "a:xyz", "b:abc", "b:xyz", "c:jkl"]
+ assert_equal(@cls[ "a:def", "b:abc", "c:jkl" ], c.uniq! {|s| s[/^\w+/]})
+ assert_equal(@cls[ "a:def", "b:abc", "c:jkl" ], c)
+
+ c = @cls["a:def", "b:abc", "c:jkl"]
+ assert_equal(nil, c.uniq! {|s| s[/^\w+/]})
+ assert_equal(@cls[ "a:def", "b:abc", "c:jkl" ], c)
+
+ assert_nil(@cls[1, 2, 3].uniq!)
+
+ f = a.dup.freeze
+ assert_raise(ArgumentError) { a.uniq!(1) }
+ assert_raise(ArgumentError) { f.uniq!(1) }
+ assert_raise(RuntimeError) { f.uniq! }
+
+ assert_nothing_raised do
+ a = [ {c: "b"}, {c: "r"}, {c: "w"}, {c: "g"}, {c: "g"} ]
+ a.sort_by!{|e| e[:c]}
+ a.uniq! {|e| e[:c]}
+ end
+
+ a = %w(a a)
+ b = a.uniq
+ assert_equal(%w(a a), a)
+ assert(a.none?(&:frozen?))
+ assert_equal(%w(a), b)
+ assert(b.none?(&:frozen?))
+ end
+
+ def test_uniq_bang_with_block
+ a = []
+ b = a.uniq! {|v| v.even? }
+ assert_equal(nil, b)
+
+ a = [1]
+ b = a.uniq! {|v| v.even? }
+ assert_equal(nil, b)
+
+ a = [1,3]
+ b = a.uniq! {|v| v.even? }
+ assert_equal([1], a)
+ assert_equal([1], b)
+ assert_same(a, b)
+
+ a = [1,2]
+ b = a.uniq! {|v| v.even? }
+ assert_equal([1,2], a)
+ assert_equal(nil, b)
+
+ a = %w(a a)
+ b = a.uniq! {|v| v }
+ assert_equal(%w(a), b)
+ assert_same(a, b)
+ assert b.none?(&:frozen?)
+ end
+
+ def test_uniq_bang_with_freeze
+ ary = [1,2]
+ orig = ary.dup
+ assert_raise(RuntimeError, "frozen during comparison") {
+ ary.uniq! {|v| ary.freeze; 1}
+ }
+ assert_equal(orig, ary, "must not be modified once frozen")
+ end
+
+ def test_unshift
+ a = @cls[]
+ assert_equal(@cls['cat'], a.unshift('cat'))
+ assert_equal(@cls['dog', 'cat'], a.unshift('dog'))
+ assert_equal(@cls[nil, 'dog', 'cat'], a.unshift(nil))
+ assert_equal(@cls[@cls[1,2], nil, 'dog', 'cat'], a.unshift(@cls[1, 2]))
+ end
+
+ def test_OR # '|'
+ assert_equal(@cls[], @cls[] | @cls[])
+ assert_equal(@cls[1], @cls[1] | @cls[])
+ assert_equal(@cls[1], @cls[] | @cls[1])
+ assert_equal(@cls[1], @cls[1] | @cls[1])
+
+ assert_equal(@cls[1,2], @cls[1] | @cls[2])
+ assert_equal(@cls[1,2], @cls[1, 1] | @cls[2, 2])
+ assert_equal(@cls[1,2], @cls[1, 2] | @cls[1, 2])
+
+ a = %w(a b c)
+ b = %w(a b c d e)
+ c = a | b
+ assert_equal(c, b)
+ assert_not_same(c, b)
+ assert_equal(%w(a b c), a)
+ assert_equal(%w(a b c d e), b)
+ assert(a.none?(&:frozen?))
+ assert(b.none?(&:frozen?))
+ assert(c.none?(&:frozen?))
+ end
+
+ def test_OR_in_order
+ obj1, obj2 = Class.new do
+ attr_reader :name
+ def initialize(name) @name = name; end
+ def inspect; "test_OR_in_order(#{@name})"; end
+ def hash; 0; end
+ def eql?(a) true; end
+ break [new("1"), new("2")]
+ end
+ assert_equal([obj1], [obj1]|[obj2])
+ end
+
+ def test_combination
+ assert_equal(@cls[[]], @cls[1,2,3,4].combination(0).to_a)
+ assert_equal(@cls[[1],[2],[3],[4]], @cls[1,2,3,4].combination(1).to_a)
+ assert_equal(@cls[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], @cls[1,2,3,4].combination(2).to_a)
+ assert_equal(@cls[[1,2,3],[1,2,4],[1,3,4],[2,3,4]], @cls[1,2,3,4].combination(3).to_a)
+ assert_equal(@cls[[1,2,3,4]], @cls[1,2,3,4].combination(4).to_a)
+ assert_equal(@cls[], @cls[1,2,3,4].combination(5).to_a)
+ end
+
+ def test_product
+ assert_equal(@cls[[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]],
+ @cls[1,2,3].product([4,5]))
+ assert_equal(@cls[[1,1],[1,2],[2,1],[2,2]], @cls[1,2].product([1,2]))
+
+ assert_equal(@cls[[1,3,5],[1,3,6],[1,4,5],[1,4,6],
+ [2,3,5],[2,3,6],[2,4,5],[2,4,6]],
+ @cls[1,2].product([3,4],[5,6]))
+ assert_equal(@cls[[1],[2]], @cls[1,2].product)
+ assert_equal(@cls[], @cls[1,2].product([]))
+
+ bug3394 = '[ruby-dev:41540]'
+ acc = []
+ EnvUtil.under_gc_stress {[1,2].product([3,4,5],[6,8]){|array| acc << array}}
+ assert_equal([[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, bug3394)
+
+ def (o = Object.new).to_ary; GC.start; [3,4] end
+ acc = [1,2].product(*[o]*10)
+ assert_equal([1,2].product([3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4]),
+ acc)
+
+ a = []
+ [1, 2].product([0, 1, 2, 3, 4][1, 4]) {|x| a << x }
+ a.all? {|x| assert_not_include(x, 0)}
+ end
+
+ def test_permutation
+ a = @cls[1,2,3]
+ assert_equal(@cls[[]], a.permutation(0).to_a)
+ assert_equal(@cls[[1],[2],[3]], a.permutation(1).to_a.sort)
+ assert_equal(@cls[[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]],
+ a.permutation(2).to_a.sort)
+ assert_equal(@cls[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]],
+ a.permutation(3).sort.to_a)
+ assert_equal(@cls[], a.permutation(4).to_a)
+ assert_equal(@cls[], a.permutation(-1).to_a)
+ assert_equal("abcde".each_char.to_a.permutation(5).sort,
+ "edcba".each_char.to_a.permutation(5).sort)
+ assert_equal(@cls[].permutation(0).to_a, @cls[[]])
+
+ a = @cls[1, 2, 3, 4]
+ b = @cls[]
+ a.permutation {|x| b << x; a.replace(@cls[9, 8, 7, 6]) }
+ assert_equal(@cls[9, 8, 7, 6], a)
+ assert_equal(@cls[1, 2, 3, 4].permutation.to_a, b)
+
+ bug3708 = '[ruby-dev:42067]'
+ assert_equal(b, @cls[0, 1, 2, 3, 4][1, 4].permutation.to_a, bug3708)
+
+ bug9932 = '[ruby-core:63103] [Bug #9932]'
+ assert_separately([], <<-"end;") # do
+ assert_nothing_raised(SystemStackError, "#{bug9932}") do
+ assert_equal(:ok, Array.new(100_000, nil).permutation {break :ok})
+ end
+ end;
+ end
+
+ def test_repeated_permutation
+ a = @cls[1,2]
+ assert_equal(@cls[[]], a.repeated_permutation(0).to_a)
+ assert_equal(@cls[[1],[2]], a.repeated_permutation(1).to_a.sort)
+ assert_equal(@cls[[1,1],[1,2],[2,1],[2,2]],
+ a.repeated_permutation(2).to_a.sort)
+ assert_equal(@cls[[1,1,1],[1,1,2],[1,2,1],[1,2,2],
+ [2,1,1],[2,1,2],[2,2,1],[2,2,2]],
+ a.repeated_permutation(3).to_a.sort)
+ assert_equal(@cls[], a.repeated_permutation(-1).to_a)
+ assert_equal("abcde".each_char.to_a.repeated_permutation(5).sort,
+ "edcba".each_char.to_a.repeated_permutation(5).sort)
+ assert_equal(@cls[].repeated_permutation(0).to_a, @cls[[]])
+ assert_equal(@cls[].repeated_permutation(1).to_a, @cls[])
+
+ a = @cls[1, 2, 3, 4]
+ b = @cls[]
+ a.repeated_permutation(4) {|x| b << x; a.replace(@cls[9, 8, 7, 6]) }
+ assert_equal(@cls[9, 8, 7, 6], a)
+ assert_equal(@cls[1, 2, 3, 4].repeated_permutation(4).to_a, b)
+
+ a = @cls[0, 1, 2, 3, 4][1, 4].repeated_permutation(2)
+ assert_empty(a.reject {|x| !x.include?(0)})
+
+ assert_separately([], <<-"end;") # do
+ assert_nothing_raised(SystemStackError) do
+ assert_equal(:ok, Array.new(100_000, nil).repeated_permutation(500_000) {break :ok})
+ end
+ end;
+ end
+
+ def test_repeated_combination
+ a = @cls[1,2,3]
+ assert_equal(@cls[[]], a.repeated_combination(0).to_a)
+ assert_equal(@cls[[1],[2],[3]], a.repeated_combination(1).to_a.sort)
+ assert_equal(@cls[[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]],
+ a.repeated_combination(2).to_a.sort)
+ assert_equal(@cls[[1,1,1],[1,1,2],[1,1,3],[1,2,2],[1,2,3],
+ [1,3,3],[2,2,2],[2,2,3],[2,3,3],[3,3,3]],
+ a.repeated_combination(3).to_a.sort)
+ assert_equal(@cls[[1,1,1,1],[1,1,1,2],[1,1,1,3],[1,1,2,2],[1,1,2,3],
+ [1,1,3,3],[1,2,2,2],[1,2,2,3],[1,2,3,3],[1,3,3,3],
+ [2,2,2,2],[2,2,2,3],[2,2,3,3],[2,3,3,3],[3,3,3,3]],
+ a.repeated_combination(4).to_a.sort)
+ assert_equal(@cls[], a.repeated_combination(-1).to_a)
+ assert_equal("abcde".each_char.to_a.repeated_combination(5).map{|e|e.sort}.sort,
+ "edcba".each_char.to_a.repeated_combination(5).map{|e|e.sort}.sort)
+ assert_equal(@cls[].repeated_combination(0).to_a, @cls[[]])
+ assert_equal(@cls[].repeated_combination(1).to_a, @cls[])
+
+ a = @cls[1, 2, 3, 4]
+ b = @cls[]
+ a.repeated_combination(4) {|x| b << x; a.replace(@cls[9, 8, 7, 6]) }
+ assert_equal(@cls[9, 8, 7, 6], a)
+ assert_equal(@cls[1, 2, 3, 4].repeated_combination(4).to_a, b)
+
+ a = @cls[0, 1, 2, 3, 4][1, 4].repeated_combination(2)
+ assert_empty(a.reject {|x| !x.include?(0)})
+
+ assert_separately([], <<-"end;") # do
+ assert_nothing_raised(SystemStackError) do
+ assert_equal(:ok, Array.new(100_000, nil).repeated_combination(500_000) {break :ok})
+ end
+ end;
+ end
+
+ def test_take
+ assert_equal([1,2,3], [1,2,3,4,5,0].take(3))
+ assert_raise(ArgumentError, '[ruby-dev:34123]') { [1,2].take(-1) }
+ assert_equal([1,2], [1,2].take(1000000000), '[ruby-dev:34123]')
+ end
+
+ def test_take_while
+ assert_equal([1,2], [1,2,3,4,5,0].take_while {|i| i < 3 })
+ end
+
+ def test_drop
+ assert_equal([4,5,0], [1,2,3,4,5,0].drop(3))
+ assert_raise(ArgumentError, '[ruby-dev:34123]') { [1,2].drop(-1) }
+ assert_equal([], [1,2].drop(1000000000), '[ruby-dev:34123]')
+ end
+
+ def test_drop_while
+ assert_equal([3,4,5,0], [1,2,3,4,5,0].drop_while {|i| i < 3 })
+ end
+
+ LONGP = [127, 63, 31, 15, 7].map {|x| 2**x-1 }.find do |x|
+ begin
+ [].first(x)
+ rescue ArgumentError
+ true
+ rescue RangeError
+ false
+ end
+ end
+
+ def test_ary_new
+ assert_raise(ArgumentError) { [].to_enum.first(-1) }
+ assert_raise(ArgumentError) { [].to_enum.first(LONGP) }
+ end
+
+ def test_try_convert
+ assert_equal([1], Array.try_convert([1]))
+ assert_equal(nil, Array.try_convert("1"))
+ end
+
+ def test_initialize
+ assert_nothing_raised { [].instance_eval { initialize } }
+ assert_nothing_raised { Array.new { } }
+ assert_equal([1, 2, 3], Array.new([1, 2, 3]))
+ assert_raise(ArgumentError) { Array.new(-1, 1) }
+ assert_raise(ArgumentError) { Array.new(LONGP, 1) }
+ assert_equal([1, 1, 1], Array.new(3, 1))
+ assert_equal([1, 1, 1], Array.new(3) { 1 })
+ assert_equal([1, 1, 1], Array.new(3, 1) { 1 })
+ end
+
+ def test_aset_error
+ assert_raise(IndexError) { [0][-2] = 1 }
+ assert_raise(IndexError) { [0][LONGP] = 2 }
+ assert_raise(IndexError) { [0][(LONGP + 1) / 2 - 1] = 2 }
+ assert_raise(IndexError) { [0][LONGP..-1] = 2 }
+ a = [0]
+ a[2] = 4
+ assert_equal([0, nil, 4], a)
+ assert_raise(ArgumentError) { [0][0, 0, 0] = 0 }
+ assert_raise(ArgumentError) { [0].freeze[0, 0, 0] = 0 }
+ assert_raise(TypeError) { [0][:foo] = 0 }
+ assert_raise(RuntimeError) { [0].freeze[:foo] = 0 }
+ end
+
+ def test_first2
+ assert_equal([0], [0].first(2))
+ assert_raise(ArgumentError) { [0].first(-1) }
+ end
+
+ def test_last2
+ assert_equal([0], [0].last(2))
+ assert_raise(ArgumentError) { [0].last(-1) }
+ end
+
+ def test_shift2
+ assert_equal(0, ([0] * 16).shift)
+ # check
+ a = [0, 1, 2]
+ a[3] = 3
+ a.shift(2)
+ assert_equal([2, 3], a)
+ end
+
+ def test_unshift_error
+ assert_raise(RuntimeError) { [].freeze.unshift('cat') }
+ assert_raise(RuntimeError) { [].freeze.unshift() }
+ end
+
+ def test_aref
+ assert_raise(ArgumentError) { [][0, 0, 0] }
+ end
+
+ def test_fetch
+ assert_equal(1, [].fetch(0, 0) { 1 })
+ assert_equal(1, [0, 1].fetch(-1))
+ assert_raise(IndexError) { [0, 1].fetch(2) }
+ assert_raise(IndexError) { [0, 1].fetch(-3) }
+ assert_equal(2, [0, 1].fetch(2, 2))
+ end
+
+ def test_index2
+ a = [0, 1, 2]
+ assert_equal(a, a.index.to_a)
+ assert_equal(1, a.index {|x| x == 1 })
+ end
+
+ def test_rindex2
+ a = [0, 1, 2]
+ assert_equal([2, 1, 0], a.rindex.to_a)
+ assert_equal(1, a.rindex {|x| x == 1 })
+
+ a = [0, 1]
+ e = a.rindex
+ assert_equal(1, e.next)
+ a.clear
+ assert_raise(StopIteration) { e.next }
+
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:==) {|x| a.clear; false }
+ end
+ a = [nil, o]
+ assert_equal(nil, a.rindex(0))
+ end
+
+ def test_ary_to_ary
+ o = Object.new
+ def o.to_ary; [1, 2, 3]; end
+ a, b, c = o
+ assert_equal([1, 2, 3], [a, b, c])
+ end
+
+ def test_splice
+ a = [0]
+ assert_raise(IndexError) { a[-2, 0] = nil }
+ end
+
+ def test_insert
+ a = [0]
+ assert_equal([0], a.insert(1))
+ assert_equal([0, 1], a.insert(1, 1))
+ assert_raise(ArgumentError) { a.insert }
+ assert_equal([0, 1, 2], a.insert(-1, 2))
+ assert_equal([0, 1, 3, 2], a.insert(-2, 3))
+ assert_raise(RuntimeError) { [0].freeze.insert(0)}
+ assert_raise(ArgumentError) { [0].freeze.insert }
+ end
+
+ def test_join2
+ a = []
+ a << a
+ assert_raise(ArgumentError){a.join}
+
+ def (a = Object.new).to_ary
+ [self]
+ end
+ assert_raise(ArgumentError, '[ruby-core:24150]'){[a].join}
+ assert_equal("12345", [1,[2,[3,4],5]].join)
+ end
+
+ def test_to_a2
+ klass = Class.new(Array)
+ a = klass.new.to_a
+ assert_equal([], a)
+ assert_equal(Array, a.class)
+ end
+
+ def test_values_at2
+ a = [0, 1, 2, 3, 4, 5]
+ assert_equal([1, 2, 3], a.values_at(1..3))
+ assert_equal([nil, nil], a.values_at(7..8))
+ bug6203 = '[ruby-core:43678]'
+ assert_equal([4, 5, nil, nil], a.values_at(4..7), bug6203)
+ assert_equal([nil], a.values_at(2**31-1))
+ end
+
+ def test_select
+ assert_equal([0, 2], [0, 1, 2, 3].select {|x| x % 2 == 0 })
+ end
+
+ # also keep_if
+ def test_select!
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(nil, a.select! { true })
+ assert_equal(a, a.keep_if { true })
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.select! { false })
+ assert_equal(@cls[], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.select! { |i| i > 3 })
+ assert_equal(@cls[4, 5], a)
+ end
+
+ def test_delete2
+ a = [0] * 1024 + [1] + [0] * 1024
+ a.delete(0)
+ assert_equal([1], a)
+ end
+
+ def test_reject
+ assert_equal([1, 3], [0, 1, 2, 3].reject {|x| x % 2 == 0 })
+ end
+
+ def test_reject_with_callcc
+ need_continuation
+ bug9727 = '[ruby-dev:48101] [Bug #9727]'
+ cont = nil
+ a = [*1..10].reject do |i|
+ callcc {|c| cont = c} if !cont and i == 10
+ false
+ end
+ if a.size < 1000
+ a.unshift(:x)
+ cont.call
+ end
+ assert_equal(1000, a.size, bug9727)
+ assert_equal([:x, *1..10], a.uniq, bug9727)
+ end
+
+ def test_zip
+ assert_equal([[1, :a, "a"], [2, :b, "b"], [3, nil, "c"]],
+ [1, 2, 3].zip([:a, :b], ["a", "b", "c", "d"]))
+ a = []
+ [1, 2, 3].zip([:a, :b], ["a", "b", "c", "d"]) {|x| a << x }
+ assert_equal([[1, :a, "a"], [2, :b, "b"], [3, nil, "c"]], a)
+
+ ary = Object.new
+ def ary.to_a; [1, 2]; end
+ assert_raise(TypeError) {%w(a b).zip(ary)}
+ def ary.each; [3, 4].each{|e|yield e}; end
+ assert_equal([['a', 3], ['b', 4]], %w(a b).zip(ary))
+ def ary.to_ary; [5, 6]; end
+ assert_equal([['a', 5], ['b', 6]], %w(a b).zip(ary))
+ end
+
+ def test_zip_bug
+ bug8153 = "ruby-core:53650"
+ r = 1..1
+ def r.respond_to?(*)
+ super
+ end
+ assert_equal [[42, 1]], [42].zip(r), bug8153
+ end
+
+ def test_transpose
+ assert_equal([[1, :a], [2, :b], [3, :c]],
+ [[1, 2, 3], [:a, :b, :c]].transpose)
+ assert_raise(IndexError) { [[1, 2, 3], [:a, :b]].transpose }
+ end
+
+ def test_clear2
+ assert_equal([], ([0] * 1024).clear)
+ end
+
+ def test_fill2
+ assert_raise(ArgumentError) { [].fill(0, 1, LONGP) }
+ end
+
+ def test_times
+ assert_raise(ArgumentError) { [0, 0, 0, 0] * ((LONGP + 1) / 4) }
+ end
+
+ def test_equal
+ o = Object.new
+ def o.to_ary; end
+ def o.==(x); :foo; end
+ assert_equal([0, 1, 2], o)
+ assert_not_equal([0, 1, 2], [0, 1, 3])
+ end
+
+ A = Array.new(3, &:to_s)
+ B = A.dup
+
+ def test_equal_resize
+ o = Object.new
+ def o.==(o)
+ A.clear
+ B.clear
+ true
+ end
+ A[1] = o
+ assert_equal(A, B)
+ end
+
+ def test_flatten_error
+ a = []
+ a << a
+ assert_raise(ArgumentError) { a.flatten }
+
+ f = [].freeze
+ assert_raise(ArgumentError) { a.flatten!(1, 2) }
+ assert_raise(TypeError) { a.flatten!(:foo) }
+ assert_raise(ArgumentError) { f.flatten!(1, 2) }
+ assert_raise(RuntimeError) { f.flatten! }
+ assert_raise(RuntimeError) { f.flatten!(:foo) }
+ end
+
+ def test_shuffle
+ 100.times do
+ assert_equal([0, 1, 2], [2, 1, 0].shuffle.sort)
+ end
+
+ gen = Random.new(0)
+ assert_raise(ArgumentError) {[1, 2, 3].shuffle(1, random: gen)}
+ srand(0)
+ 100.times do
+ assert_equal([0, 1, 2].shuffle, [0, 1, 2].shuffle(random: gen))
+ end
+
+ assert_raise_with_message(ArgumentError, /unknown keyword/) do
+ [0, 1, 2].shuffle(xawqij: "a")
+ end
+ assert_raise_with_message(ArgumentError, /unknown keyword/) do
+ [0, 1, 2].shuffle!(xawqij: "a")
+ end
+ end
+
+ def test_shuffle_random
+ gen = proc do
+ 10000000
+ end
+ class << gen
+ alias rand call
+ end
+ assert_raise(RangeError) {
+ [*0..2].shuffle(random: gen)
+ }
+
+ ary = (0...10000).to_a
+ gen = proc do
+ ary.replace([])
+ 0.5
+ end
+ class << gen
+ alias rand call
+ end
+ assert_raise(RuntimeError) {ary.shuffle!(random: gen)}
+
+ zero = Object.new
+ def zero.to_int
+ 0
+ end
+ gen_to_int = proc do |max|
+ zero
+ end
+ class << gen_to_int
+ alias rand call
+ end
+ ary = (0...10000).to_a
+ assert_equal(ary.rotate, ary.shuffle(random: gen_to_int))
+ end
+
+ def test_sample
+ 100.times do
+ assert_include([0, 1, 2], [2, 1, 0].sample)
+ samples = [2, 1, 0].sample(2)
+ samples.each{|sample|
+ assert_include([0, 1, 2], sample)
+ }
+ end
+
+ srand(0)
+ a = (1..18).to_a
+ (0..20).each do |n|
+ 100.times do
+ b = a.sample(n)
+ assert_equal([n, 18].min, b.size)
+ assert_equal(a, (a | b).sort)
+ assert_equal(b.sort, (a & b).sort)
+ end
+
+ h = Hash.new(0)
+ 1000.times do
+ a.sample(n).each {|x| h[x] += 1 }
+ end
+ assert_operator(h.values.min * 2, :>=, h.values.max) if n != 0
+ end
+
+ assert_raise(ArgumentError, '[ruby-core:23374]') {[1, 2].sample(-1)}
+
+ gen = Random.new(0)
+ srand(0)
+ a = (1..18).to_a
+ (0..20).each do |n|
+ 100.times do |i|
+ assert_equal(a.sample(n), a.sample(n, random: gen), "#{i}/#{n}")
+ end
+ end
+
+ assert_raise_with_message(ArgumentError, /unknown keyword/) do
+ [0, 1, 2].sample(xawqij: "a")
+ end
+ end
+
+ def test_sample_random
+ ary = (0...10000).to_a
+ assert_raise(ArgumentError) {ary.sample(1, 2, random: nil)}
+ gen0 = proc do |max|
+ max/2
+ end
+ class << gen0
+ alias rand call
+ end
+ gen1 = proc do |max|
+ ary.replace([])
+ max/2
+ end
+ class << gen1
+ alias rand call
+ end
+ assert_equal(5000, ary.sample(random: gen0))
+ assert_nil(ary.sample(random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000], ary.sample(1, random: gen0))
+ assert_equal([], ary.sample(1, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 4999], ary.sample(2, random: gen0))
+ assert_equal([], ary.sample(2, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 4999, 5001], ary.sample(3, random: gen0))
+ assert_equal([], ary.sample(3, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 4999, 5001, 4998], ary.sample(4, random: gen0))
+ assert_equal([], ary.sample(4, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 4999, 5001, 4998, 5002, 4997, 5003, 4996, 5004, 4995], ary.sample(10, random: gen0))
+ assert_equal([], ary.sample(10, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 0, 5001, 2, 5002, 4, 5003, 6, 5004, 8, 5005], ary.sample(11, random: gen0))
+ ary.sample(11, random: gen1) # implementation detail, may change in the future
+ assert_equal([], ary)
+
+ half = Object.new
+ def half.to_int
+ 5000
+ end
+ gen_to_int = proc do |max|
+ half
+ end
+ class << gen_to_int
+ alias rand call
+ end
+ ary = (0...10000).to_a
+ assert_equal(5000, ary.sample(random: gen_to_int))
+ end
+
+ def test_cycle
+ a = []
+ [0, 1, 2].cycle do |i|
+ a << i
+ break if a.size == 10
+ end
+ assert_equal([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], a)
+
+ a = [0, 1, 2]
+ assert_nil(a.cycle { a.clear })
+
+ a = []
+ [0, 1, 2].cycle(3) {|i| a << i }
+ assert_equal([0, 1, 2, 0, 1, 2, 0, 1, 2], a)
+ end
+
+ def test_reverse_each2
+ a = [0, 1, 2, 3, 4, 5]
+ r = []
+ a.reverse_each do |x|
+ r << x
+ a.pop
+ a.pop
+ end
+ assert_equal([5, 3, 1], r)
+ end
+
+ def test_combination2
+ assert_equal(:called, (0..100).to_a.combination(50) { break :called }, "[ruby-core:29240] ... must be yielded even if 100C50 > signed integer")
+ end
+
+ def test_combination_clear
+ bug9939 = '[ruby-core:63149] [Bug #9939]'
+ assert_separately([], <<-'end;')
+ 100_000.times {Array.new(1000)}
+ a = [*0..100]
+ a.combination(3) {|*,x| a.clear}
+ end;
+ end
+
+ def test_product2
+ a = (0..100).to_a
+ assert_raise(RangeError) do
+ a.product(a, a, a, a, a, a, a, a, a, a)
+ end
+ assert_nothing_raised(RangeError) do
+ a.product(a, a, a, a, a, a, a, a, a, a) { break }
+ end
+ end
+
+ class Array2 < Array
+ end
+
+ def test_array_subclass
+ assert_equal(Array2, Array2[1,2,3].uniq.class, "[ruby-dev:34581]")
+ assert_equal(Array2, Array2[1,2][0,1].class) # embeded
+ assert_equal(Array2, Array2[*(1..100)][1..99].class) #not embeded
+ end
+
+ def test_inspect
+ a = @cls[1, 2, 3]
+ a.taint
+ s = a.inspect
+ assert_equal(true, s.tainted?)
+ end
+
+ def test_initialize2
+ a = [1] * 1000
+ a.instance_eval { initialize }
+ assert_equal([], a)
+ end
+
+ def test_shift_shared_ary
+ a = (1..100).to_a
+ b = []
+ b.replace(a)
+ assert_equal((1..10).to_a, a.shift(10))
+ assert_equal((11..100).to_a, a)
+ end
+
+ def test_replace_shared_ary
+ a = [1] * 100
+ b = []
+ b.replace(a)
+ a.replace([1, 2, 3])
+ assert_equal([1, 2, 3], a)
+ assert_equal([1] * 100, b)
+ end
+
+ def test_fill_negative_length
+ a = (1..10).to_a
+ a.fill(:foo, 5, -3)
+ assert_equal((1..10).to_a, a)
+ end
+
+ def test_slice_frozen_array
+ a = [1,2,3,4,5].freeze
+ assert_equal([1,2,3,4], a[0,4])
+ assert_equal([2,3,4,5], a[1,4])
+ end
+
+ def test_sort_by!
+ a = [1,3,5,2,4]
+ a.sort_by! {|x| -x }
+ assert_equal([5,4,3,2,1], a)
+ end
+
+ def test_rotate
+ a = [1,2,3,4,5].freeze
+ assert_equal([2,3,4,5,1], a.rotate)
+ assert_equal([5,1,2,3,4], a.rotate(-1))
+ assert_equal([3,4,5,1,2], a.rotate(2))
+ assert_equal([4,5,1,2,3], a.rotate(-2))
+ assert_equal([4,5,1,2,3], a.rotate(13))
+ assert_equal([3,4,5,1,2], a.rotate(-13))
+ a = [1].freeze
+ assert_equal([1], a.rotate)
+ assert_equal([1], a.rotate(2))
+ assert_equal([1], a.rotate(-4))
+ assert_equal([1], a.rotate(13))
+ assert_equal([1], a.rotate(-13))
+ a = [].freeze
+ assert_equal([], a.rotate)
+ assert_equal([], a.rotate(2))
+ assert_equal([], a.rotate(-4))
+ assert_equal([], a.rotate(13))
+ assert_equal([], a.rotate(-13))
+ a = [1,2,3]
+ assert_raise(ArgumentError) { a.rotate(1, 1) }
+ assert_equal([1,2,3,4,5].rotate(2**31-1), [1,2,3,4,5].rotate(2**31-0.1))
+ assert_equal([1,2,3,4,5].rotate(-2**31), [1,2,3,4,5].rotate(-2**31-0.9))
+ end
+
+ def test_rotate!
+ a = [1,2,3,4,5]
+ assert_equal([2,3,4,5,1], a.rotate!)
+ assert_equal([2,3,4,5,1], a)
+ assert_equal([4,5,1,2,3], a.rotate!(2))
+ assert_equal([5,1,2,3,4], a.rotate!(-4))
+ assert_equal([3,4,5,1,2], a.rotate!(13))
+ assert_equal([5,1,2,3,4], a.rotate!(-13))
+ a = [1]
+ assert_equal([1], a.rotate!)
+ assert_equal([1], a.rotate!(2))
+ assert_equal([1], a.rotate!(-4))
+ assert_equal([1], a.rotate!(13))
+ assert_equal([1], a.rotate!(-13))
+ a = []
+ assert_equal([], a.rotate!)
+ assert_equal([], a.rotate!(2))
+ assert_equal([], a.rotate!(-4))
+ assert_equal([], a.rotate!(13))
+ assert_equal([], a.rotate!(-13))
+ a = [].freeze
+ assert_raise_with_message(RuntimeError, /can\'t modify frozen/) {a.rotate!}
+ a = [1,2,3]
+ assert_raise(ArgumentError) { a.rotate!(1, 1) }
+ end
+
+ def test_bsearch_typechecks_return_values
+ assert_raise(TypeError) do
+ [1, 2, 42, 100, 666].bsearch{ "not ok" }
+ end
+ assert_equal [1, 2, 42, 100, 666].bsearch{}, [1, 2, 42, 100, 666].bsearch{false}
+ end
+
+ def test_bsearch_with_no_block
+ enum = [1, 2, 42, 100, 666].bsearch
+ assert_nil enum.size
+ assert_equal 42, enum.each{|x| x >= 33 }
+ end
+
+ def test_bsearch_in_find_minimum_mode
+ a = [0, 4, 7, 10, 12]
+ assert_equal(4, a.bsearch {|x| x >= 4 })
+ assert_equal(7, a.bsearch {|x| x >= 6 })
+ assert_equal(0, a.bsearch {|x| x >= -1 })
+ assert_equal(nil, a.bsearch {|x| x >= 100 })
+ end
+
+ def test_bsearch_in_find_any_mode
+ a = [0, 4, 7, 10, 12]
+ assert_include([4, 7], a.bsearch {|x| 1 - x / 4 })
+ assert_equal(nil, a.bsearch {|x| 4 - x / 2 })
+ assert_equal(nil, a.bsearch {|x| 1 })
+ assert_equal(nil, a.bsearch {|x| -1 })
+
+ assert_include([4, 7], a.bsearch {|x| (1 - x / 4) * (2**100) })
+ assert_equal(nil, a.bsearch {|x| 1 * (2**100) })
+ assert_equal(nil, a.bsearch {|x| (-1) * (2**100) })
+
+ assert_include([4, 7], a.bsearch {|x| (2**100).coerce((1 - x / 4) * (2**100)).first })
+ end
+
+ def test_shared_marking
+ reduce = proc do |s|
+ s.gsub(/(verify_internal_consistency_reachable_i:\sWB\smiss\s\S+\s\(T_ARRAY\)\s->\s)\S+\s\((proc|T_NONE)\)\n
+ \K(?:\1\S+\s\(\2\)\n)*/x) do
+ "...(snip #{$&.count("\n")} lines)...\n"
+ end
+ end
+ begin
+ assert_normal_exit(<<-EOS, '[Bug #9718]', timeout: 5, stdout_filter: reduce)
+ queue = []
+ 50.times do
+ 10_000.times do
+ queue << lambda{}
+ end
+ GC.start(full_mark: false, immediate_sweep: true)
+ GC.verify_internal_consistency
+ queue.shift.call
+ end
+ EOS
+ rescue Timeout::Error => e
+ skip e.message
+ end
+ end
+
+ sizeof_long = [0].pack("l!").size
+ sizeof_voidp = [""].pack("p").size
+ if sizeof_long < sizeof_voidp
+ ARY_MAX = (1<<(8*sizeof_long-1)) / sizeof_voidp - 1
+ Bug11235 = '[ruby-dev:49043] [Bug #11235]'
+
+ def test_push_over_ary_max
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], <<-"end;")
+ a = Array.new(ARGV[0].to_i)
+ assert_raise(IndexError, ARGV[1]) {0x1000.times {a.push(1)}}
+ end;
+ end
+
+ def test_unshift_over_ary_max
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], <<-"end;")
+ a = Array.new(ARGV[0].to_i)
+ assert_raise(IndexError, ARGV[1]) {0x1000.times {a.unshift(1)}}
+ end;
+ end
+
+ def test_splice_over_ary_max
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], <<-"end;")
+ a = Array.new(ARGV[0].to_i)
+ assert_raise(IndexError, ARGV[1]) {a[0, 0] = Array.new(0x1000)}
+ end;
+ end
+ end
+
+ private
+ def need_continuation
+ unless respond_to?(:callcc, true)
+ EnvUtil.suppress_warning {require 'continuation'}
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_assignment.rb b/jni/ruby/test/ruby/test_assignment.rb
new file mode 100644
index 0000000..9c0fd48
--- /dev/null
+++ b/jni/ruby/test/ruby/test_assignment.rb
@@ -0,0 +1,760 @@
+require 'test/unit'
+
+class TestAssignment < Test::Unit::TestCase
+ def test_assign
+ a=[]; a[0] ||= "bar";
+ assert_equal("bar", a[0])
+ h={}; h["foo"] ||= "bar";
+ assert_equal("bar", h["foo"])
+
+ aa = 5
+ aa ||= 25
+ assert_equal(5, aa)
+ bb ||= 25
+ assert_equal(25, bb)
+ cc &&=33
+ assert_nil(cc)
+ cc = 5
+ cc &&=44
+ assert_equal(44, cc)
+ end
+
+ def test_assign_simple
+ a = nil; assert_nil(a)
+ a = 1; assert_equal(1, a)
+ a = []; assert_equal([], a)
+ a = [1]; assert_equal([1], a)
+ a = [nil]; assert_equal([nil], a)
+ a = [[]]; assert_equal([[]], a)
+ a = [1,2]; assert_equal([1,2], a)
+ a = [*[]]; assert_equal([], a)
+ a = [*[1]]; assert_equal([1], a)
+ a = [*[1,2]]; assert_equal([1,2], a)
+ end
+
+ def test_assign_splat
+ a = *[]; assert_equal([], a)
+ a = *[1]; assert_equal([1], a)
+ a = *[nil]; assert_equal([nil], a)
+ a = *[[]]; assert_equal([[]], a)
+ a = *[1,2]; assert_equal([1,2], a)
+ a = *[*[]]; assert_equal([], a)
+ a = *[*[1]]; assert_equal([1], a)
+ a = *[*[1,2]]; assert_equal([1,2], a)
+ end
+
+ def test_assign_ary
+ *a = nil; assert_equal([nil], a)
+ *a = 1; assert_equal([1], a)
+ *a = []; assert_equal([], a)
+ *a = [1]; assert_equal([1], a)
+ *a = [nil]; assert_equal([nil], a)
+ *a = [[]]; assert_equal([[]], a)
+ *a = [1,2]; assert_equal([1,2], a)
+ *a = [*[]]; assert_equal([], a)
+ *a = [*[1]]; assert_equal([1], a)
+ *a = [*[1,2]]; assert_equal([1,2], a)
+ end
+
+ def test_assign_ary_splat
+ *a = *[]; assert_equal([], a)
+ *a = *[1]; assert_equal([1], a)
+ *a = *[nil]; assert_equal([nil], a)
+ *a = *[[]]; assert_equal([[]], a)
+ *a = *[1,2]; assert_equal([1,2], a)
+ *a = *[*[]]; assert_equal([], a)
+ *a = *[*[1]]; assert_equal([1], a)
+ *a = *[*[1,2]]; assert_equal([1,2], a)
+ end
+
+ def test_massign_simple
+ a,b,*c = nil; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = 1; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = []; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = [1]; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = [nil]; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = [[]]; assert_equal([[],nil,[]], [a,b,c])
+ a,b,*c = [1,2]; assert_equal([1,2,[]], [a,b,c])
+ a,b,*c = [*[]]; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = [*[1]]; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = [*[1,2]]; assert_equal([1,2,[]], [a,b,c])
+ end
+
+ def test_massign_splat
+ a,b,*c = *[]; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = *[1]; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = *[nil]; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = *[[]]; assert_equal([[],nil,[]], [a,b,c])
+ a,b,*c = *[1,2]; assert_equal([1,2,[]], [a,b,c])
+ a,b,*c = *[*[]]; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = *[*[1]]; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = *[*[1,2]]; assert_equal([1,2,[]], [a,b,c])
+ end
+
+ def test_assign_abbreviated
+ bug2050 = '[ruby-core:25629]'
+ a = Hash.new {[]}
+ b = [1, 2]
+ assert_equal([1, 2, 3], a[:x] += [*b, 3], bug2050)
+ assert_equal([1, 2, 3], a[:x], bug2050)
+ assert_equal([1, 2, 3, [1, 2, 3]], a[:x] <<= [*b, 3], bug2050)
+ assert_equal([1, 2, 3, [1, 2, 3]], a[:x], bug2050)
+ end
+
+ def test_assign_private_self
+ bug9907 = '[ruby-core:62949] [Bug #9907]'
+
+ o = Object.new
+ class << o
+ private
+ def foo; 42; end
+ def [](i); 42; end
+ def foo=(a); 42; end
+ def []=(i, a); 42; end
+ end
+
+ assert_raise(NoMethodError) {
+ o.instance_eval {o.foo = 1}
+ }
+ assert_nothing_raised(NoMethodError) {
+ assert_equal(1, o.instance_eval {self.foo = 1})
+ }
+
+ assert_raise(NoMethodError) {
+ o.instance_eval {o[0] = 1}
+ }
+ assert_nothing_raised(NoMethodError) {
+ assert_equal(1, o.instance_eval {self[0] = 1})
+ }
+
+ assert_nothing_raised(NoMethodError, bug9907) {
+ assert_equal(43, o.instance_eval {self.foo += 1})
+ }
+ assert_nothing_raised(NoMethodError, bug9907) {
+ assert_equal(1, o.instance_eval {self.foo &&= 1})
+ }
+
+ assert_nothing_raised(NoMethodError, bug9907) {
+ assert_equal(43, o.instance_eval {self[0] += 1})
+ }
+ assert_nothing_raised(NoMethodError, bug9907) {
+ assert_equal(1, o.instance_eval {self[0] &&= 1})
+ }
+ end
+
+ def test_yield
+ def f; yield(nil); end; f {|a| assert_nil(a)}; undef f
+ def f; yield(1); end; f {|a| assert_equal(1, a)}; undef f
+ def f; yield([]); end; f {|a| assert_equal([], a)}; undef f
+ def f; yield([1]); end; f {|a| assert_equal([1], a)}; undef f
+ def f; yield([nil]); end; f {|a| assert_equal([nil], a)}; undef f
+ def f; yield([[]]); end; f {|a| assert_equal([[]], a)}; undef f
+ def f; yield([*[]]); end; f {|a| assert_equal([], a)}; undef f
+ def f; yield([*[1]]); end; f {|a| assert_equal([1], a)}; undef f
+ def f; yield([*[1,2]]); end; f {|a| assert_equal([1,2], a)}; undef f
+
+ def f; yield(*[1]); end; f {|a| assert_equal(1, a)}; undef f
+ def f; yield(*[nil]); end; f {|a| assert_equal(nil, a)}; undef f
+ def f; yield(*[[]]); end; f {|a| assert_equal([], a)}; undef f
+ def f; yield(*[*[1]]); end; f {|a| assert_equal(1, a)}; undef f
+
+ def f; yield; end; f {|*a| assert_equal([], a)}; undef f
+ def f; yield(nil); end; f {|*a| assert_equal([nil], a)}; undef f
+ def f; yield(1); end; f {|*a| assert_equal([1], a)}; undef f
+ def f; yield([]); end; f {|*a| assert_equal([[]], a)}; undef f
+ def f; yield([1]); end; f {|*a| assert_equal([[1]], a)}; undef f
+ def f; yield([nil]); end; f {|*a| assert_equal([[nil]], a)}; undef f
+ def f; yield([[]]); end; f {|*a| assert_equal([[[]]], a)}; undef f
+ def f; yield([1,2]); end; f {|*a| assert_equal([[1,2]], a)}; undef f
+ def f; yield([*[]]); end; f {|*a| assert_equal([[]], a)}; undef f
+ def f; yield([*[1]]); end; f {|*a| assert_equal([[1]], a)}; undef f
+ def f; yield([*[1,2]]); end; f {|*a| assert_equal([[1,2]], a)}; undef f
+
+ def f; yield(*[]); end; f {|*a| assert_equal([], a)}; undef f
+ def f; yield(*[1]); end; f {|*a| assert_equal([1], a)}; undef f
+ def f; yield(*[nil]); end; f {|*a| assert_equal([nil], a)}; undef f
+ def f; yield(*[[]]); end; f {|*a| assert_equal([[]], a)}; undef f
+ def f; yield(*[*[]]); end; f {|*a| assert_equal([], a)}; undef f
+ def f; yield(*[*[1]]); end; f {|*a| assert_equal([1], a)}; undef f
+ def f; yield(*[*[1,2]]); end; f {|*a| assert_equal([1,2], a)}; undef f
+
+ def f; yield; end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield(nil); end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield(1); end; f {|a,b,*c| assert_equal([1,nil,[]], [a,b,c])}; undef f
+ def f; yield([]); end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield([1]); end; f {|a,b,*c| assert_equal([1,nil,[]], [a,b,c])}; undef f
+ def f; yield([nil]); end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield([[]]); end; f {|a,b,*c| assert_equal([[],nil,[]], [a,b,c])}; undef f
+ def f; yield([*[]]); end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield([*[1]]); end; f {|a,b,*c| assert_equal([1,nil,[]], [a,b,c])}; undef f
+ def f; yield([*[1,2]]); end; f {|a,b,*c| assert_equal([1,2,[]], [a,b,c])}; undef f
+
+ def f; yield(*[]); end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield(*[1]); end; f {|a,b,*c| assert_equal([1,nil,[]], [a,b,c])}; undef f
+ def f; yield(*[nil]); end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield(*[[]]); end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield(*[*[]]); end; f {|a,b,*c| assert_equal([nil,nil,[]], [a,b,c])}; undef f
+ def f; yield(*[*[1]]); end; f {|a,b,*c| assert_equal([1,nil,[]], [a,b,c])}; undef f
+ def f; yield(*[*[1,2]]); end; f {|a,b,*c| assert_equal([1,2,[]], [a,b,c])}; undef f
+ end
+
+ def test_return
+ def r; return; end; a = r(); assert_nil(a); undef r
+ def r; return nil; end; a = r(); assert_nil(a); undef r
+ def r; return 1; end; a = r(); assert_equal(1, a); undef r
+ def r; return []; end; a = r(); assert_equal([], a); undef r
+ def r; return [1]; end; a = r(); assert_equal([1], a); undef r
+ def r; return [nil]; end; a = r(); assert_equal([nil], a); undef r
+ def r; return [[]]; end; a = r(); assert_equal([[]], a); undef r
+ def r; return [*[]]; end; a = r(); assert_equal([], a); undef r
+ def r; return [*[1]]; end; a = r(); assert_equal([1], a); undef r
+ def r; return [*[1,2]]; end; a = r(); assert_equal([1,2], a); undef r
+
+ def r; return *[]; end; a = r(); assert_equal([], a); undef r
+ def r; return *[1]; end; a = r(); assert_equal([1], a); undef r
+ def r; return *[nil]; end; a = r(); assert_equal([nil], a); undef r
+ def r; return *[[]]; end; a = r(); assert_equal([[]], a); undef r
+ def r; return *[*[]]; end; a = r(); assert_equal([], a); undef r
+ def r; return *[*[1]]; end; a = r(); assert_equal([1], a); undef r
+ def r; return *[*[1,2]]; end; a = r(); assert_equal([1,2], a); undef r
+
+ def r; return *[[]]; end; a = *r(); assert_equal([[]], a); undef r
+ def r; return *[*[1,2]]; end; a = *r(); assert_equal([1,2], a); undef r
+
+ def r; return; end; *a = r(); assert_equal([nil], a); undef r
+ def r; return nil; end; *a = r(); assert_equal([nil], a); undef r
+ def r; return 1; end; *a = r(); assert_equal([1], a); undef r
+ def r; return []; end; *a = r(); assert_equal([], a); undef r
+ def r; return [1]; end; *a = r(); assert_equal([1], a); undef r
+ def r; return [nil]; end; *a = r(); assert_equal([nil], a); undef r
+ def r; return [[]]; end; *a = r(); assert_equal([[]], a); undef r
+ def r; return [1,2]; end; *a = r(); assert_equal([1,2], a); undef r
+ def r; return [*[]]; end; *a = r(); assert_equal([], a); undef r
+ def r; return [*[1]]; end; *a = r(); assert_equal([1], a); undef r
+ def r; return [*[1,2]]; end; *a = r(); assert_equal([1,2], a); undef r
+
+ def r; return *[]; end; *a = r(); assert_equal([], a); undef r
+ def r; return *[1]; end; *a = r(); assert_equal([1], a); undef r
+ def r; return *[nil]; end; *a = r(); assert_equal([nil], a); undef r
+ def r; return *[[]]; end; *a = r(); assert_equal([[]], a); undef r
+ def r; return *[1,2]; end; *a = r(); assert_equal([1,2], a); undef r
+ def r; return *[*[]]; end; *a = r(); assert_equal([], a); undef r
+ def r; return *[*[1]]; end; *a = r(); assert_equal([1], a); undef r
+ def r; return *[*[1,2]]; end; *a = r(); assert_equal([1,2], a); undef r
+
+ def r; return *[[]]; end; *a = *r(); assert_equal([[]], a); undef r
+ def r; return *[1,2]; end; *a = *r(); assert_equal([1,2], a); undef r
+ def r; return *[*[1,2]]; end; *a = *r(); assert_equal([1,2], a); undef r
+
+ def r; return; end; a,b,*c = r(); assert_equal([nil,nil,[]], [a,b,c]); undef r
+ def r; return nil; end; a,b,*c = r(); assert_equal([nil,nil,[]], [a,b,c]); undef r
+ def r; return 1; end; a,b,*c = r(); assert_equal([1,nil,[]], [a,b,c]); undef r
+ def r; return []; end; a,b,*c = r(); assert_equal([nil,nil,[]], [a,b,c]); undef r
+ def r; return [1]; end; a,b,*c = r(); assert_equal([1,nil,[]], [a,b,c]); undef r
+ def r; return [nil]; end; a,b,*c = r(); assert_equal([nil,nil,[]], [a,b,c]); undef r
+ def r; return [[]]; end; a,b,*c = r(); assert_equal([[],nil,[]], [a,b,c]); undef r
+ def r; return [1,2]; end; a,b,*c = r(); assert_equal([1,2,[]], [a,b,c]); undef r
+ def r; return [*[]]; end; a,b,*c = r(); assert_equal([nil,nil,[]], [a,b,c]); undef r
+ def r; return [*[1]]; end; a,b,*c = r(); assert_equal([1,nil,[]], [a,b,c]); undef r
+ def r; return [*[1,2]]; end; a,b,*c = r(); assert_equal([1,2,[]], [a,b,c]); undef r
+
+ def r; return *[]; end; a,b,*c = r(); assert_equal([nil,nil,[]], [a,b,c]); undef r
+ def r; return *[1]; end; a,b,*c = r(); assert_equal([1,nil,[]], [a,b,c]); undef r
+ def r; return *[nil]; end; a,b,*c = r(); assert_equal([nil,nil,[]], [a,b,c]); undef r
+ def r; return *[[]]; end; a,b,*c = r(); assert_equal([[],nil,[]], [a,b,c]); undef r
+ def r; return *[1,2]; end; a,b,*c = r(); assert_equal([1,2,[]], [a,b,c]); undef r
+ def r; return *[*[]]; end; a,b,*c = r(); assert_equal([nil,nil,[]], [a,b,c]); undef r
+ def r; return *[*[1]]; end; a,b,*c = r(); assert_equal([1,nil,[]], [a,b,c]); undef r
+ def r; return *[*[1,2]]; end; a,b,*c = r(); assert_equal([1,2,[]], [a,b,c]); undef r
+
+ def r; return 1, *[]; end; a,b = r(); assert_equal([1,nil], [a,b]); undef r
+ def r; return 1,2,*[1]; end; a,b = r(); assert_equal([1,2], [a,b]); undef r
+ def r; return 1,2,3,*[1,2]; end; a,b = r(); assert_equal([1,2], [a,b]); undef r
+ end
+
+ def test_lambda
+ f = lambda {|r,| assert_equal([], r)}
+ f.call([], *[])
+
+ f = lambda {|r,*l| assert_equal([], r); assert_equal([1], l)}
+ f.call([], *[1])
+
+ f = lambda{|x| x}
+ assert_equal(42, f.call(42))
+ assert_equal([42], f.call([42]))
+ assert_equal([[42]], f.call([[42]]))
+ assert_equal([42,55], f.call([42,55]))
+
+ f = lambda{|x,| x}
+ assert_equal(42, f.call(42))
+ assert_equal([42], f.call([42]))
+ assert_equal([[42]], f.call([[42]]))
+ assert_equal([42,55], f.call([42,55]))
+
+ f = lambda{|*x| x}
+ assert_equal([42], f.call(42))
+ assert_equal([[42]], f.call([42]))
+ assert_equal([[[42]]], f.call([[42]]))
+ assert_equal([[42,55]], f.call([42,55]))
+ assert_equal([42,55], f.call(42,55))
+ end
+
+ def test_multi
+ a,=*[1]
+ assert_equal(1, a)
+ a,=*[[1]]
+ assert_equal([1], a)
+ a,=*[[[1]]]
+ assert_equal([[1]], a)
+
+ x, (y, z) = 1, 2, 3
+ assert_equal([1,2,nil], [x,y,z])
+ x, (y, z) = 1, [2,3]
+ assert_equal([1,2,3], [x,y,z])
+ x, (y, z) = 1, [2]
+ assert_equal([1,2,nil], [x,y,z])
+ end
+
+ def test_break
+ a = loop do break; end; assert_nil(a)
+ a = loop do break nil; end; assert_nil(a)
+ a = loop do break 1; end; assert_equal(1, a)
+ a = loop do break []; end; assert_equal([], a)
+ a = loop do break [1]; end; assert_equal([1], a)
+ a = loop do break [nil]; end; assert_equal([nil], a)
+ a = loop do break [[]]; end; assert_equal([[]], a)
+ a = loop do break [*[]]; end; assert_equal([], a)
+ a = loop do break [*[1]]; end; assert_equal([1], a)
+ a = loop do break [*[1,2]]; end; assert_equal([1,2], a)
+
+ a = loop do break *[]; end; assert_equal([], a)
+ a = loop do break *[1]; end; assert_equal([1], a)
+ a = loop do break *[nil]; end; assert_equal([nil], a)
+ a = loop do break *[[]]; end; assert_equal([[]], a)
+ a = loop do break *[*[]]; end; assert_equal([], a)
+ a = loop do break *[*[1]]; end; assert_equal([1], a)
+ a = loop do break *[*[1,2]]; end; assert_equal([1,2], a)
+
+ *a = loop do break; end; assert_equal([nil], a)
+ *a = loop do break nil; end; assert_equal([nil], a)
+ *a = loop do break 1; end; assert_equal([1], a)
+ *a = loop do break []; end; assert_equal([], a)
+ *a = loop do break [1]; end; assert_equal([1], a)
+ *a = loop do break [nil]; end; assert_equal([nil], a)
+ *a = loop do break [[]]; end; assert_equal([[]], a)
+ *a = loop do break [1,2]; end; assert_equal([1,2], a)
+ *a = loop do break [*[]]; end; assert_equal([], a)
+ *a = loop do break [*[1]]; end; assert_equal([1], a)
+ *a = loop do break [*[1,2]]; end; assert_equal([1,2], a)
+
+ *a = loop do break *[]; end; assert_equal([], a)
+ *a = loop do break *[1]; end; assert_equal([1], a)
+ *a = loop do break *[nil]; end; assert_equal([nil], a)
+ *a = loop do break *[[]]; end; assert_equal([[]], a)
+ *a = loop do break *[1,2]; end; assert_equal([1,2], a)
+ *a = loop do break *[*[]]; end; assert_equal([], a)
+ *a = loop do break *[*[1]]; end; assert_equal([1], a)
+ *a = loop do break *[*[1,2]]; end; assert_equal([1,2], a)
+
+ *a = *loop do break *[[]]; end; assert_equal([[]], a)
+ *a = *loop do break *[1,2]; end; assert_equal([1,2], a)
+ *a = *loop do break *[*[1,2]]; end; assert_equal([1,2], a)
+
+ a,b,*c = loop do break; end; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = loop do break nil; end; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = loop do break 1; end; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = loop do break []; end; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = loop do break [1]; end; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = loop do break [nil]; end; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = loop do break [[]]; end; assert_equal([[],nil,[]], [a,b,c])
+ a,b,*c = loop do break [1,2]; end; assert_equal([1,2,[]], [a,b,c])
+ a,b,*c = loop do break [*[]]; end; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = loop do break [*[1]]; end; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = loop do break [*[1,2]]; end; assert_equal([1,2,[]], [a,b,c])
+
+ a,b,*c = loop do break *[]; end; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = loop do break *[1]; end; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = loop do break *[nil]; end; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = loop do break *[[]]; end; assert_equal([[],nil,[]], [a,b,c])
+ a,b,*c = loop do break *[1,2]; end; assert_equal([1,2,[]], [a,b,c])
+ a,b,*c = loop do break *[*[]]; end; assert_equal([nil,nil,[]], [a,b,c])
+ a,b,*c = loop do break *[*[1]]; end; assert_equal([1,nil,[]], [a,b,c])
+ a,b,*c = loop do break *[*[1,2]]; end; assert_equal([1,2,[]], [a,b,c])
+ end
+
+ def test_next
+ def r(val); a = yield(); assert_equal(val, a); end
+ r(nil){next}
+ r(nil){next nil}
+ r(1){next 1}
+ r([]){next []}
+ r([1]){next [1]}
+ r([nil]){next [nil]}
+ r([[]]){next [[]]}
+ r([]){next [*[]]}
+ r([1]){next [*[1]]}
+ r([1,2]){next [*[1,2]]}
+
+ r([]){next *[]}
+ r([1]){next *[1]}
+ r([nil]){next *[nil]}
+ r([[]]){next *[[]]}
+ r([]){next *[*[]]}
+ r([1]){next *[*[1]]}
+ r([1,2]){next *[*[1,2]]}
+ undef r
+
+ def r(val); *a = yield(); assert_equal(val, a); end
+ r([nil]){next}
+ r([nil]){next nil}
+ r([1]){next 1}
+ r([]){next []}
+ r([1]){next [1]}
+ r([nil]){next [nil]}
+ r([[]]){next [[]]}
+ r([1,2]){next [1,2]}
+ r([]){next [*[]]}
+ r([1]){next [*[1]]}
+ r([1,2]){next [*[1,2]]}
+ undef r
+
+ def r(val); *a = *yield(); assert_equal(val, a); end
+ r([[]]){next *[[]]}
+ r([1,2]){next *[1,2]}
+ r([1,2]){next *[*[1,2]]}
+ undef r
+
+ def r(val); a,b,*c = yield(); assert_equal(val, [a,b,c]); end
+ r([nil,nil,[]]){next}
+ r([nil,nil,[]]){next nil}
+ r([1,nil,[]]){next 1}
+ r([nil,nil,[]]){next []}
+ r([1,nil,[]]){next [1]}
+ r([nil,nil,[]]){next [nil]}
+ r([[],nil,[]]){next [[]]}
+ r([1,2,[]]){next [1,2]}
+ r([nil,nil,[]]){next [*[]]}
+ r([1,nil,[]]){next [*[1]]}
+ r([1,2,[]]){next [*[1,2]]}
+ undef r
+
+ def r(val); a,b,*c = *yield(); assert_equal(val, [a,b,c]); end
+ r([[],nil,[]]){next *[[]]}
+ r([1,2,[]]){next *[1,2]}
+ r([1,2,[]]){next *[*[1,2]]}
+ undef r
+ end
+
+ def test_massign
+ a = nil
+ assert(defined?(a))
+ assert_nil(a)
+
+ # multiple asignment
+ a, b = 1, 2
+ assert_equal 1, a
+ assert_equal 2, b
+
+ a, b, c = 1, 2, 3
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+
+ a = 1
+ b = 2
+ a, b = b, a
+ assert_equal 2, a
+ assert_equal 1, b
+
+ a, = 1, 2
+ assert_equal 1, a
+
+ a, = 1, 2, 3
+ assert_equal 1, a
+
+ a, * = 1, 2, 3
+ assert_equal 1, a
+
+ a, *b = 1, 2, 3
+ assert_equal 1, a
+ assert_equal [2, 3], b
+
+ # not supported yet
+ #a, *b, c = 1, 2, 3, 4
+ #assert_equal 1, a
+ #assert_equal [2,3], b
+ #assert_equal 4, c
+
+ a = 1, 2
+ assert_equal [1, 2], a
+
+ a = [1, 2], [3, 4]
+ assert_equal [[1,2], [3,4]], a
+
+ a, (b, c), d = 1, [2, 3], 4
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+ assert_equal 4, d
+
+ *a = 1, 2, 3
+ assert_equal([1, 2, 3], a)
+
+ *a = 4
+ assert_equal([4], a)
+
+ *a = nil
+ assert_equal([nil], a)
+
+ a, b = 1
+ assert_equal 1, a
+ assert_nil b
+
+ a, b = [1, 2]
+ assert_equal 1, a
+ assert_equal 2, b
+ end
+
+ def test_nested_massign
+ (a, b), c = [[1, 2], 3]; assert_equal [1,2,3], [a,b,c]
+ a, (b, c) = [[1, 2], 3]; assert_equal [[1,2], 3, nil], [a,b,c]
+ a, (b, c) = [1, [2, 3]]; assert_equal [1,2,3], [a,b,c]
+ (a, b), *c = [[1, 2], 3]; assert_equal [1,2,[3]], [a,b,c]
+ (a,b),c,(d,e) = [[1,2],3,[4,5]]; assert_equal [1,2,3,4,5],[a,b,c,d,e]
+ (a,*b),c,(d,e,*) = [[1,2],3,[4,5]]; assert_equal [1,[2],3,4,5],[a,b,c,d,e]
+ (a,b),c,(d,*e) = [[1,2,3],4,[5,6,7,8]]; assert_equal [1,2,4,5,[6,7,8]],[a,b,c,d,e]
+ (a,(b1,b2)),c,(d,e) = [[1,2],3,[4,5]]; assert_equal [1,2,nil,3,4,5],[a,b1,b2,c,d,e]
+ (a,(b1,b2)),c,(d,e) = [[1,[21,22]],3,[4,5]]; assert_equal [1,21,22,3,4,5],[a,b1,b2,c,d,e]
+ end
+
+ class MyObj
+ def to_ary
+ [[1,2],[3,4]]
+ end
+ end
+
+ def test_to_ary_splat
+ a, b = MyObj.new
+ assert_equal [[1,2],[3,4]], [a,b]
+ end
+
+ A = 1
+ B = 2
+ X, Y = A, B
+ class Base
+ A = 3
+ B = 4
+ end
+
+ def test_const_massign
+ assert_equal [1,2], [X,Y]
+ a, b = Base::A, Base::B
+ assert_equal [3,4], [a,b]
+ end
+end
+
+require_relative 'sentence'
+class TestAssignmentGen < Test::Unit::TestCase
+ Syntax = {
+ :exp => [["0"],
+ ["nil"],
+ ["false"],
+ ["[]"],
+ ["[",:exps,"]"]],
+ :exps => [[:exp],
+ [:exp,",",:exps]],
+ :arg => [[:exp]],
+ :mrhs => [[:args,",",:arg],
+ [:args,",","*",:arg],
+ ["*",:arg]],
+ :args => [[:arg],
+ ["*",:arg],
+ [:args,",",:arg],
+ [:args,",","*",:arg]],
+ :mlhs => [[:mlhs_basic],
+ ["(",:mlhs_inner,")"]],
+ :mlhs_inner => [[:mlhs_basic],
+ ["(",:mlhs_inner,")"]],
+ :mlhs_basic => [[:mlhs_head],
+ [:mlhs_head,:mlhs_item],
+ [:mlhs_head,"*",:mlhs_node],
+ [:mlhs_head,"*",:mlhs_node,",",:mlhs_post],
+ [:mlhs_head,"*"],
+ [:mlhs_head,"*",",", :mlhs_post],
+ [ "*",:mlhs_node],
+ [ "*",:mlhs_node,",",:mlhs_post],
+ [ "*"],
+ [ "*",",", :mlhs_post]],
+ :mlhs_head => [[:mlhs_item,","],
+ [:mlhs_head,:mlhs_item,","]],
+ :mlhs_post => [[:mlhs_item],
+ [:mlhs_post,",",:mlhs_item]],
+ :mlhs_item => [[:mlhs_node],
+ ["(",:mlhs_inner,")"]],
+ :mlhs_node => [["var"]],
+ :xassign => [["var"," = ",:exp],
+ ["var"," = ",:mrhs],
+ [:mlhs," = ",:exp],
+ [:mlhs," = ",:mrhs]],
+ }
+
+ def rename_var(obj)
+ vars = []
+ r = obj.subst('var') {
+ var = "v#{vars.length}"
+ vars << var
+ var
+ }
+ return r, vars
+ end
+
+ def expand_except_paren(obj)
+ return obj if obj.respond_to? :to_str
+ obj.expand {|s|
+ !(s[0] == '(' && s[-1] == ')') &&
+ !(s[0] == '[' && s[-1] == ']')
+ }
+ end
+
+ def extract_single_element(ary)
+ raise "not a single element array: #{ary.inspect}" if ary.length != 1
+ ary[0]
+ end
+
+ def emu_assign_ary(lhs, rv, h)
+ rv = rv.respond_to?(:to_ary) ? rv : [rv]
+ rv = rv.dup
+ a = [[]]
+ lhs.each {|e|
+ if e == ','
+ a << []
+ else
+ a.last << e
+ end
+ }
+ a.pop if a.last == []
+ pre = []
+ star = post = nil
+ a.each {|e|
+ if post
+ post << e
+ elsif e[0] == '*'
+ star = e
+ post = []
+ else
+ pre << e
+ end
+ }
+ pre.map! {|e| extract_single_element(e) }
+ if star
+ if star == ['*']
+ star = nil
+ else
+ star = extract_single_element(star[1..-1])
+ end
+ end
+ post.map! {|e| extract_single_element(e) } if post
+
+ until pre.empty?
+ emu_assign_single(pre.shift, rv.shift, h)
+ end
+
+ if post
+ if rv.length < post.length
+ until post.empty?
+ emu_assign_single(post.shift, rv.shift, h)
+ end
+ else
+ until post.empty?
+ emu_assign_single(post.pop, rv.pop, h)
+ end
+ end
+ end
+
+ if star
+ emu_assign_single(star, rv, h)
+ end
+ end
+
+ def emu_assign_single(lhs, rv, h={})
+ if lhs.respond_to? :to_str
+ if /\A[a-z0-9]+\z/ =~ lhs
+ h[lhs] = rv
+ else
+ raise "unexpected lhs string: #{lhs.inspect}"
+ end
+ elsif Sentence === lhs
+ if lhs[0] == '(' && lhs[-1] == ')'
+ emu_assign_ary(lhs[1...-1], rv, h)
+ elsif lhs.length == 1 && String === lhs[0] && /\A[a-z0-9]+\z/ =~ lhs[0]
+ h[lhs[0]] = rv
+ else
+ raise "unexpected lhs sentence: #{lhs.inspect}"
+ end
+ else
+ raise "unexpected lhs: #{lhs.inspect}"
+ end
+ h
+ end
+
+ def emu_assign(assign)
+ lhs = expand_except_paren(assign[0])
+ rhs = expand_except_paren(assign[2])
+ lopen = Sentence === lhs && lhs[-1] != ')' && lhs.any? {|e| e == '*' || e == ',' }
+ ropen = Sentence === rhs && rhs[-1] != ']' && rhs.any? {|e| e == '*' || e == ',' }
+ lhs = Sentence.new(['(']+lhs.to_a+[')']) if lopen
+ begin
+ rv = eval((ropen ? ["[",assign[2],"]"] : assign[2]).join(''))
+ rescue Exception
+ rv = $!.message
+ end
+ emu_assign_single(lhs, rv)
+ end
+
+ def do_assign(assign, vars)
+ assign = assign.to_s
+ code1 = "#{assign}; [#{vars.join(",")}]"
+ assign.gsub!(/\bv\d+\b/, "o.a")
+ code2 = "o=[];class << o; self end.send(:define_method,:a=){|v|self << v};#{assign};o"
+ begin
+ vals1 = eval(code1)
+ rescue Exception
+ return {:ex=>$!.message}
+ end
+ begin
+ vals2 = eval(code2)
+ rescue Exception
+ return {:ex=>$!.message}
+ end
+ assert_equal(vals1, vals2, code1)
+ vals = vals1
+ h = {}
+ [vars, vals].transpose.each {|k,v| h[k] = v }
+ h
+ end
+
+ def check(assign)
+ assign, vars = rename_var(assign)
+ sent = assign.to_s
+ bruby = do_assign(assign, vars).to_a.sort
+ bemu = emu_assign(assign).to_a.sort
+ assert_equal(bemu, bruby, sent)
+ end
+
+ def test_assignment
+ syntax = Sentence.expand_syntax(Syntax)
+ Sentence.each(syntax, :xassign, 4) {|assign|
+ check(assign)
+ }
+ end
+
+ def test_optimized_aset
+ bug9448 = Class.new do
+ def []=(key, new_value)
+ '[ruby-core:60071] [Bug #9448]'
+ end
+ end
+ o = bug9448.new
+ assert_equal("ok", o['current'] = "ok")
+ end
+end
diff --git a/jni/ruby/test/ruby/test_autoload.rb b/jni/ruby/test/ruby/test_autoload.rb
new file mode 100644
index 0000000..da8855b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_autoload.rb
@@ -0,0 +1,207 @@
+require 'test/unit'
+require 'tempfile'
+require 'thread'
+
+class TestAutoload < Test::Unit::TestCase
+ def test_autoload_so
+ # Date is always available, unless excluded intentionally.
+ assert_in_out_err([], <<-INPUT, [], [])
+ autoload :Date, "date"
+ begin Date; rescue LoadError; end
+ INPUT
+ end
+
+ def test_non_realpath_in_loadpath
+ require 'tmpdir'
+ tmpdir = Dir.mktmpdir('autoload')
+ tmpdirs = [tmpdir]
+ tmpdirs.unshift(tmpdir + '/foo')
+ Dir.mkdir(tmpdirs[0])
+ tmpfiles = [tmpdir + '/foo.rb', tmpdir + '/foo/bar.rb']
+ open(tmpfiles[0] , 'w') do |f|
+ f.puts <<-INPUT
+$:.unshift(File.expand_path('..', __FILE__)+'/./foo')
+module Foo
+ autoload :Bar, 'bar'
+end
+p Foo::Bar
+ INPUT
+ end
+ open(tmpfiles[1], 'w') do |f|
+ f.puts 'class Foo::Bar; end'
+ end
+ assert_in_out_err([tmpfiles[0]], "", ["Foo::Bar"], [])
+ ensure
+ File.unlink(*tmpfiles) rescue nil if tmpfiles
+ tmpdirs.each {|dir| Dir.rmdir(dir)}
+ end
+
+ def test_autoload_p
+ bug4565 = '[ruby-core:35679]'
+
+ require 'tmpdir'
+ Dir.mktmpdir('autoload') {|tmpdir|
+ tmpfile = tmpdir + '/foo.rb'
+ a = Module.new do
+ autoload :X, tmpfile
+ end
+ b = Module.new do
+ include a
+ end
+ assert_equal(true, a.const_defined?(:X))
+ assert_equal(true, b.const_defined?(:X))
+ assert_equal(tmpfile, a.autoload?(:X), bug4565)
+ assert_equal(tmpfile, b.autoload?(:X), bug4565)
+ }
+ end
+
+ def test_autoload_with_unqualified_file_name # [ruby-core:69206]
+ lp = $LOAD_PATH.dup
+ lf = $LOADED_FEATURES.dup
+
+ Dir.mktmpdir('autoload') { |tmpdir|
+ $LOAD_PATH << tmpdir
+
+ Dir.chdir(tmpdir) do
+ eval <<-END
+ class ::Object
+ module A
+ autoload :C, 'b'
+ end
+ end
+ END
+
+ File.open('b.rb', 'w') {|file| file.puts 'module A; class C; end; end'}
+ assert_kind_of Class, ::A::C
+ end
+ }
+ ensure
+ $LOAD_PATH.replace lp
+ $LOADED_FEATURES.replace lf
+ Object.send(:remove_const, :A) if Object.const_defined?(:A)
+ end
+
+ def test_require_explicit
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'class Object; AutoloadTest = 1; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ assert_nothing_raised do
+ assert(require file.path)
+ assert_equal(1, ::AutoloadTest)
+ end
+ ensure
+ remove_autoload_constant
+ end
+ }
+ end
+
+ def test_threaded_accessing_constant
+ # Suppress "warning: loading in progress, circular require considered harmful"
+ EnvUtil.default_warning {
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'sleep 0.5; class AutoloadTest; X = 1; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ assert_nothing_raised do
+ t1 = Thread.new { ::AutoloadTest::X }
+ t2 = Thread.new { ::AutoloadTest::X }
+ [t1, t2].each(&:join)
+ end
+ ensure
+ remove_autoload_constant
+ end
+ }
+ }
+ end
+
+ def test_threaded_accessing_inner_constant
+ # Suppress "warning: loading in progress, circular require considered harmful"
+ EnvUtil.default_warning {
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'class AutoloadTest; sleep 0.5; X = 1; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ assert_nothing_raised do
+ t1 = Thread.new { ::AutoloadTest::X }
+ t2 = Thread.new { ::AutoloadTest::X }
+ [t1, t2].each(&:join)
+ end
+ ensure
+ remove_autoload_constant
+ end
+ }
+ }
+ end
+
+ def test_nameerror_when_autoload_did_not_define_the_constant
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts ''
+ file.close
+ add_autoload(file.path)
+ begin
+ assert_raise(NameError) do
+ AutoloadTest
+ end
+ ensure
+ remove_autoload_constant
+ end
+ }
+ end
+
+ def test_override_autoload
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts ''
+ file.close
+ add_autoload(file.path)
+ begin
+ eval %q(class AutoloadTest; end)
+ assert_equal(Class, AutoloadTest.class)
+ ensure
+ remove_autoload_constant
+ end
+ }
+ end
+
+ def test_override_while_autoloading
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'class AutoloadTest; sleep 0.5; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ # while autoloading...
+ t = Thread.new { AutoloadTest }
+ sleep 0.1
+ # override it
+ EnvUtil.suppress_warning {
+ eval %q(AutoloadTest = 1)
+ }
+ t.join
+ assert_equal(1, AutoloadTest)
+ ensure
+ remove_autoload_constant
+ end
+ }
+ end
+
+ def add_autoload(path)
+ (@autoload_paths ||= []) << path
+ eval <<-END
+ class ::Object
+ autoload :AutoloadTest, #{path.dump}
+ end
+ END
+ end
+
+ def remove_autoload_constant
+ $".replace($" - @autoload_paths)
+ eval <<-END
+ class ::Object
+ remove_const(:AutoloadTest)
+ end
+ END
+ end
+end
diff --git a/jni/ruby/test/ruby/test_backtrace.rb b/jni/ruby/test/ruby/test_backtrace.rb
new file mode 100644
index 0000000..ad1ea80
--- /dev/null
+++ b/jni/ruby/test/ruby/test_backtrace.rb
@@ -0,0 +1,246 @@
+require 'test/unit'
+require 'thread'
+
+class TestBacktrace < Test::Unit::TestCase
+ def test_exception
+ bt = Fiber.new{
+ begin
+ raise
+ rescue => e
+ e.backtrace
+ end
+ }.resume
+ assert_equal(1, bt.size)
+ assert_match(/.+:\d+:.+/, bt[0])
+ end
+
+ def helper_test_exception_backtrace_locations
+ raise
+ end
+
+ def test_exception_backtrace_locations
+ backtrace, backtrace_locations = Fiber.new{
+ begin
+ raise
+ rescue => e
+ [e.backtrace, e.backtrace_locations]
+ end
+ }.resume
+ assert_equal(backtrace, backtrace_locations.map{|e| e.to_s})
+
+ backtrace, backtrace_locations = Fiber.new{
+ begin
+ begin
+ helper_test_exception_backtrace_locations
+ rescue
+ raise
+ end
+ rescue => e
+ [e.backtrace, e.backtrace_locations]
+ end
+ }.resume
+ assert_equal(backtrace, backtrace_locations.map{|e| e.to_s})
+ end
+
+ def call_helper_test_exception_backtrace_locations
+ helper_test_exception_backtrace_locations(:bad_argument)
+ end
+
+ def test_argument_error_backtrace_locations
+ backtrace, backtrace_locations = Fiber.new{
+ begin
+ helper_test_exception_backtrace_locations(1)
+ rescue ArgumentError => e
+ [e.backtrace, e.backtrace_locations]
+ end
+ }.resume
+ assert_equal(backtrace, backtrace_locations.map{|e| e.to_s})
+
+ backtrace, backtrace_locations = Fiber.new{
+ begin
+ call_helper_test_exception_backtrace_locations
+ rescue ArgumentError => e
+ [e.backtrace, e.backtrace_locations]
+ end
+ }.resume
+ assert_equal(backtrace, backtrace_locations.map{|e| e.to_s})
+ end
+
+ def test_caller_lev
+ cs = []
+ Fiber.new{
+ Proc.new{
+ cs << caller(0)
+ cs << caller(1)
+ cs << caller(2)
+ cs << caller(3)
+ cs << caller(4)
+ cs << caller(5)
+ }.call
+ }.resume
+ assert_equal(3, cs[0].size)
+ assert_equal(2, cs[1].size)
+ assert_equal(1, cs[2].size)
+ assert_equal(0, cs[3].size)
+ assert_equal(nil, cs[4])
+
+ #
+ max = 7
+ rec = lambda{|n|
+ if n > 0
+ 1.times{
+ rec[n-1]
+ }
+ else
+ (max*3).times{|i|
+ total_size = caller(0).size
+ c = caller(i)
+ if c
+ assert_equal(total_size - i, caller(i).size, "[ruby-dev:45673]")
+ end
+ }
+ end
+ }
+ Fiber.new{
+ rec[max]
+ }.resume
+ end
+
+ def test_caller_lev_and_n
+ m = 10
+ rec = lambda{|n|
+ if n < 0
+ (m*6).times{|lev|
+ (m*6).times{|i|
+ t = caller(0).size
+ r = caller(lev, i)
+ r = r.size if r.respond_to? :size
+
+ # STDERR.puts [t, lev, i, r].inspect
+ if i == 0
+ assert_equal(0, r, [t, lev, i, r].inspect)
+ elsif t < lev
+ assert_equal(nil, r, [t, lev, i, r].inspect)
+ else
+ if t - lev > i
+ assert_equal(i, r, [t, lev, i, r].inspect)
+ else
+ assert_equal(t - lev, r, [t, lev, i, r].inspect)
+ end
+ end
+ }
+ }
+ else
+ rec[n-1]
+ end
+ }
+ rec[m]
+ end
+
+ def test_caller_with_nil_length
+ assert_equal caller(0), caller(0, nil)
+ end
+
+ def test_caller_locations
+ cs = caller(0); locs = caller_locations(0).map{|loc|
+ loc.to_s
+ }
+ assert_equal(cs, locs)
+ end
+
+ def test_caller_locations_with_range
+ cs = caller(0,2); locs = caller_locations(0..1).map { |loc|
+ loc.to_s
+ }
+ assert_equal(cs, locs)
+ end
+
+ def test_caller_locations_to_s_inspect
+ cs = caller(0); locs = caller_locations(0)
+ cs.zip(locs){|str, loc|
+ assert_equal(str, loc.to_s)
+ assert_equal(str.inspect, loc.inspect)
+ }
+ end
+
+ def th_rec q, n=10
+ if n > 1
+ th_rec q, n-1
+ else
+ q.pop
+ end
+ end
+
+ def test_thread_backtrace
+ begin
+ q = Queue.new
+ th = Thread.new{
+ th_rec q
+ }
+ sleep 0.5
+ th_backtrace = th.backtrace
+ th_locations = th.backtrace_locations
+
+ assert_equal(10, th_backtrace.count{|e| e =~ /th_rec/})
+ assert_equal(th_backtrace, th_locations.map{|e| e.to_s})
+ assert_equal(th_backtrace, th.backtrace(0))
+ assert_equal(th_locations.map{|e| e.to_s},
+ th.backtrace_locations(0).map{|e| e.to_s})
+ th_backtrace.size.times{|n|
+ assert_equal(n, th.backtrace(0, n).size)
+ assert_equal(n, th.backtrace_locations(0, n).size)
+ }
+ n = th_backtrace.size
+ assert_equal(n, th.backtrace(0, n + 1).size)
+ assert_equal(n, th.backtrace_locations(0, n + 1).size)
+ ensure
+ q << true
+ th.join
+ end
+ end
+
+ def test_thread_backtrace_locations_with_range
+ begin
+ q = Queue.new
+ th = Thread.new{
+ th_rec q
+ }
+ sleep 0.5
+ bt = th.backtrace(0,2)
+ locs = th.backtrace_locations(0..1).map { |loc|
+ loc.to_s
+ }
+ assert_equal(bt, locs)
+ ensure
+ q << true
+ th.join
+ end
+ end
+
+ def test_core_backtrace_alias
+ obj = BasicObject.new
+ e = assert_raise(NameError) do
+ class << obj
+ alias foo bar
+ end
+ end
+ assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label)
+ end
+
+ def test_core_backtrace_undef
+ obj = BasicObject.new
+ e = assert_raise(NameError) do
+ class << obj
+ undef foo
+ end
+ end
+ assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label)
+ end
+
+ def test_core_backtrace_hash_merge
+ e = assert_raise(TypeError) do
+ {**nil}
+ end
+ assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_basicinstructions.rb b/jni/ruby/test/ruby/test_basicinstructions.rb
new file mode 100644
index 0000000..4a1dc9c
--- /dev/null
+++ b/jni/ruby/test/ruby/test_basicinstructions.rb
@@ -0,0 +1,700 @@
+require 'test/unit'
+
+ConstTest = 3
+class Class
+ alias _remove_const remove_const
+ public :_remove_const
+end
+
+class TestBasicInstructions < Test::Unit::TestCase
+
+ def test_immediates
+ assert_equal((1==1), true)
+ assert_equal((1==2), false)
+ assert_equal [][0], nil
+ assert_equal "sym".intern, :sym
+ assert_equal "sym".intern, :"sym"
+ assert_equal 1234 + 0, 1234
+ assert_equal 1234, 1_2_3_4
+ assert_equal 41, 0b0101001
+ assert_equal 420, 0644
+ assert_equal 18, 0x12
+ assert_equal 123456789012345678901234567890 + 0,
+ 123456789012345678901234567890
+ assert_equal 1.234 + 0.0, 1.234
+ end
+
+ def test_self
+ assert_equal self, self
+ assert_equal false, (self == false) # Qfalse==0 in C
+ assert_equal false, (self == nil)
+ assert_equal false, (self == 0)
+ end
+
+ def test_string
+ expected = "str" + "ing"
+ assert_equal expected, 'string'
+ assert_equal expected, "string"
+ assert_equal expected, %q(string)
+ assert_equal expected, %Q(string)
+ assert_equal expected, %(string)
+ end
+
+ def test_dstring
+ assert_equal "2", "#{1+1}"
+ s = "OK"
+ assert_equal "OK", "#{s}"
+ assert_equal "OKx", "#{s}x"
+ assert_equal "xOK", "x#{s}"
+ assert_equal "xOKx", "x#{s}x"
+ end
+
+ def test_dsym
+ assert_equal :a3c, :"a#{1+2}c"
+ s = "sym"
+ assert_equal :sym, :"#{s}"
+ assert_equal :sym, :"#{"#{"#{s}"}"}"
+ end
+
+ def test_xstr
+ assert_equal 'hoge', `echo hoge`.chomp
+ assert_equal '3', `echo #{1 + 2}`.chomp
+ hoge = 'huga'
+ assert_equal 'huga', `echo #{hoge}`.chomp
+ end
+
+ def test_regexp
+ assert_equal(/test/, /test/)
+ assert_equal 'test', /test/.source
+ assert_equal 'TEST', /TEST/.source
+ assert_equal true, !!(/test/ =~ 'test')
+ assert_equal false, !!(/test/ =~ 'does not match')
+
+ re = /test/
+ assert_equal re, re
+ assert_equal 'test', re.source
+ assert_equal true, !!(re =~ 'test')
+ assert_equal false, !!(re =~ 'does not match')
+
+ assert_equal(/x#{1+1}x/, /x#{1+1}x/)
+ s = "OK"
+ assert_equal(/x#{s}x/, /x#{s}x/)
+ assert_equal true, !!(/x#{s}x/ =~ "xOKx")
+ assert_equal false, !!(/x#{s}x/ =~ "does not match")
+
+ s = "OK"
+ prev = nil
+ 3.times do
+ re = /#{s}/o
+ assert_same prev, re if prev
+ prev = re
+ end
+ end
+
+ def test_array
+ assert_equal [], []
+ assert_equal 0, [].size
+ assert_equal [1, 2, 3], [1, 2, 3]
+ assert_equal [3, 7, 11], [1+2, 3+4, 5+6]
+ assert_equal [[1], [2], [3]], [[1], [2], [3]]
+
+ a = [1, 2, 3]
+ assert_equal 1, a[0]
+ assert_equal 2, a[1]
+ assert_equal 3, a[2]
+ assert_nil a[3]
+
+ a = %w( a b c )
+ assert_equal 'a', a[0]
+ assert_equal 'b', a[1]
+ assert_equal 'c', a[2]
+ assert_nil a[3]
+ end
+
+ def test_hash
+ assert_equal({}, {})
+ assert_equal({1=>2}, {1=>2})
+ assert_equal({1=>2, 3=>4}, {1=>2, 3=>4})
+ assert_equal({1=>2, 3=>4}, {3=>4, 1=>2})
+ # assert_equal({1=>2, 3=>4}, {1,2, 3,4}) # 1.9 doesn't support
+ assert_equal({"key"=>"val"}, {"key"=>"val"})
+ end
+
+ def test_range
+ assert_equal((1..3), (1..3))
+ assert_equal((1...3), (1...3))
+ assert_not_equal((1...3), (1..3))
+ assert_not_equal((1..3), (1...3))
+ assert_equal((1..3), (1..2+1))
+ assert_equal((1...3), (1...2+1))
+ assert_equal(('a'..'z'), ('a'..'z'))
+ end
+
+ def test_not
+ assert_equal true, !nil
+ assert_equal true, !false
+ assert_equal false, !true
+ assert_equal false, !3
+ assert_equal false, !(1+1)
+
+ assert_equal false, !!nil
+ assert_equal false, !!false
+ assert_equal true, !!true
+ assert_equal true, !!3
+ assert_equal true, !!(1+1)
+
+ assert_equal true, (not nil)
+ assert_equal true, (not false)
+ assert_equal false, (not true)
+ assert_equal false, (not 3)
+ assert_equal false, (not (1 + 1))
+
+ assert_equal false, (not not nil)
+ assert_equal false, (not not false)
+ assert_equal true, (not not true)
+ assert_equal true, (not not 3)
+ assert_equal true, (not not (1+1))
+ end
+
+ def test_local_variable
+ a = 7
+ assert_equal 7, a
+ assert_equal a, a
+ b = 17
+ assert_equal 7, a
+ assert_equal 17, b
+ assert_equal a, a
+ assert_equal b, b
+ assert_not_equal a, b
+ assert_not_equal b, a
+ a = b
+ assert_equal 17, a
+ assert_equal 17, b
+ assert_equal a, a
+ assert_equal b, b
+ assert_equal a, b
+ assert_equal b, a
+ c = 28
+ assert_equal 17, a
+ assert_equal 17, b
+ assert_equal 28, c
+ assert_equal a, a
+ assert_equal b, b
+ assert_equal a, b
+ assert_equal c, c
+ assert_not_equal a, c
+ assert_not_equal b, c
+ a = b = c
+ assert_equal 28, a
+ assert_equal 28, b
+ assert_equal 28, c
+ assert_equal a, a
+ assert_equal b, b
+ assert_equal a, b
+ assert_equal b, a
+ assert_equal a, c
+ assert_equal c, a
+ assert_equal b, c
+ assert_equal c, b
+
+ a = 1
+ b = 2
+ c = 3
+ set_lvar_in_another_method
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+ end
+
+ def set_lvar_in_another_method
+ assert_raise(NameError) { a }
+ assert_raise(NameError) { b }
+ assert_raise(NameError) { c }
+ a = "NOT OK"
+ b = "NOT OK"
+ c = "NOT OK"
+ end
+
+ class Const
+ $Const = self
+ C = 'Const::C'
+ def self.c() C end
+ def c() C end
+
+ class A
+ C = 'Const::A::C'
+ def self.c() C end
+ def c() C end
+ CC = 'Const::A::CC'
+ def self.cc() CC end
+ def cc() CC end
+
+ class B
+ C = 'Const::A::B::C'
+ def self.c() C end
+ def c() C end
+ def self.cc() CC end
+ def cc() CC end
+ end
+ end
+
+ class AA < A
+ def self.cc() CC end
+ def cc() CC end
+ end
+
+ class AAA < AA
+ def self.cc() CC end
+ def cc() CC end
+ end
+ end
+ C = 0
+
+ def test_const_path
+ do_test_const_path
+ do_test_const_path
+ do_test_const_path
+ end
+
+ def do_test_const_path
+ assert_equal 0, C
+ assert_equal 0, C
+ assert_equal 3, ::ConstTest
+ assert_equal 3, ::ConstTest
+ assert_equal $Const, Const
+
+ assert_equal 'Const::C', Const::C
+ assert_equal 'Const::C', Const::C
+ assert_equal 'Const::A::C', Const::A::C
+ assert_equal 'Const::A::C', Const::A::C
+ assert_equal 'Const::A::B::C', Const::A::B::C
+ assert_equal 'Const::A::B::C', Const::A::B::C
+
+ Const::A::B._remove_const :C
+ assert_equal 'Const::C', Const::C
+ assert_equal 'Const::A::C', Const::A::C
+ assert_raise(NameError) { Const::A::B::C }
+
+ Const::A._remove_const :C
+ assert_equal 'Const::C', Const::C
+ assert_raise(NameError) { Const::A::C }
+ assert_raise(NameError) { Const::A::B::C }
+
+ Const._remove_const :C
+ assert_raise(NameError) { Const::C }
+ assert_raise(NameError) { Const::A::C }
+ assert_raise(NameError) { Const::A::B::C }
+
+ Const::A.const_set :C, 'Const::A::C'
+ assert_raise(NameError) { Const::C }
+ assert_equal 'Const::A::C', Const::A::C
+ assert_raise(NameError) { Const::A::B::C }
+
+ Const::A::B.const_set :C, 'Const::A::B::C'
+ assert_raise(NameError) { Const::C }
+ assert_equal 'Const::A::C', Const::A::C
+ assert_equal 'Const::A::B::C', Const::A::B::C
+
+ Const.const_set :C, 'Const::C'
+ assert_equal 'Const::C', Const::C
+ assert_equal 'Const::A::C', Const::A::C
+ assert_equal 'Const::A::B::C', Const::A::B::C
+ end
+
+ def test_const_cref
+ do_test_const_cref
+ do_test_const_cref
+ do_test_const_cref
+ end
+
+ def do_test_const_cref
+ assert_equal 'Const::C', Const.new.c
+ assert_equal 'Const::A::C', Const::A.new.c
+ assert_equal 'Const::A::B::C', Const::A::B.new.c
+
+ assert_equal 'Const::C', Const.c
+ assert_equal 'Const::A::C', Const::A.c
+ assert_equal 'Const::A::B::C', Const::A::B.c
+
+ Const::A::B._remove_const :C
+ assert_equal 'Const::C', Const.c
+ assert_equal 'Const::A::C', Const::A.c
+ assert_equal 'Const::A::C', Const::A::B.c
+ assert_equal 'Const::C', Const.new.c
+ assert_equal 'Const::A::C', Const::A.new.c
+ assert_equal 'Const::A::C', Const::A::B.new.c
+
+ Const::A._remove_const :C
+ assert_equal 'Const::C', Const.c
+ assert_equal 'Const::C', Const::A.c
+ assert_equal 'Const::C', Const::A::B.c
+ assert_equal 'Const::C', Const.new.c
+ assert_equal 'Const::C', Const::A.new.c
+ assert_equal 'Const::C', Const::A::B.new.c
+
+ Const::A::B.const_set :C, 'Const::A::B::C'
+ assert_equal 'Const::C', Const.c
+ assert_equal 'Const::C', Const::A.c
+ assert_equal 'Const::A::B::C', Const::A::B.c
+ assert_equal 'Const::C', Const.new.c
+ assert_equal 'Const::C', Const::A.new.c
+ assert_equal 'Const::A::B::C', Const::A::B.new.c
+
+ Const::A.const_set :C, 'Const::A::C'
+ assert_equal 'Const::C', Const.c
+ assert_equal 'Const::A::C', Const::A.c
+ assert_equal 'Const::A::B::C', Const::A::B.c
+ assert_equal 'Const::C', Const.new.c
+ assert_equal 'Const::A::C', Const::A.new.c
+ assert_equal 'Const::A::B::C', Const::A::B.new.c
+ ensure
+ # reset
+ Const.const_set :C, 'Const::C' unless Const.const_defined?(:C)
+ Const::A.const_set :C, 'Const::A::C' unless Const::A.const_defined?(:C)
+ Const::A::B.const_set :C, 'Const::A::B::C' unless Const::A::B.const_defined?(:C)
+ end
+
+ def test_const_inherit
+ do_test_const_inherit
+ do_test_const_inherit
+ do_test_const_inherit
+ end
+
+ def do_test_const_inherit
+ assert_equal 'Const::A::CC', Const::A.cc
+ assert_equal 'Const::A::CC', Const::AA.cc
+ assert_equal 'Const::A::CC', Const::AAA.cc
+ assert_equal 'Const::A::CC', Const::A.new.cc
+ assert_equal 'Const::A::CC', Const::AA.new.cc
+ assert_equal 'Const::A::CC', Const::AAA.new.cc
+
+ Const::AA.const_set :CC, 'Const::AA::CC'
+ assert_equal 'Const::A::CC', Const::A.cc
+ assert_equal 'Const::AA::CC', Const::AA.cc
+ assert_equal 'Const::AA::CC', Const::AAA.cc
+ assert_equal 'Const::A::CC', Const::A.new.cc
+ assert_equal 'Const::AA::CC', Const::AA.new.cc
+ assert_equal 'Const::AA::CC', Const::AAA.new.cc
+
+ Const::AAA.const_set :CC, 'Const::AAA::CC'
+ assert_equal 'Const::A::CC', Const::A.cc
+ assert_equal 'Const::AA::CC', Const::AA.cc
+ assert_equal 'Const::AAA::CC', Const::AAA.cc
+ assert_equal 'Const::A::CC', Const::A.new.cc
+ assert_equal 'Const::AA::CC', Const::AA.new.cc
+ assert_equal 'Const::AAA::CC', Const::AAA.new.cc
+
+ Const::AA._remove_const :CC
+ assert_equal 'Const::A::CC', Const::A.cc
+ assert_equal 'Const::A::CC', Const::AA.cc
+ assert_equal 'Const::AAA::CC', Const::AAA.cc
+ assert_equal 'Const::A::CC', Const::A.new.cc
+ assert_equal 'Const::A::CC', Const::AA.new.cc
+ assert_equal 'Const::AAA::CC', Const::AAA.new.cc
+
+ Const::AAA._remove_const :CC
+ assert_equal 'Const::A::CC', Const::A.cc
+ assert_equal 'Const::A::CC', Const::AA.cc
+ assert_equal 'Const::A::CC', Const::AAA.cc
+ assert_equal 'Const::A::CC', Const::A.new.cc
+ assert_equal 'Const::A::CC', Const::AA.new.cc
+ assert_equal 'Const::A::CC', Const::AAA.new.cc
+ end
+
+ def test_global_variable
+ $gvar1 = 1
+ assert_equal 1, $gvar1
+ $gvar1 = 2
+ assert_equal 2, $gvar1
+ $gvar2 = 77
+ assert_equal 2, $gvar1
+ assert_equal 77, $gvar2
+ $gvar2 = $gvar1
+ assert_equal 2, $gvar1
+ assert_equal 2, $gvar2
+ $gvar1 = 1
+ assert_equal 1, $gvar1
+ assert_equal 2, $gvar2
+ set_gvar_in_another_method
+ assert_equal "OK1", $gvar1
+ assert_equal "OK2", $gvar2
+ end
+
+ def set_gvar_in_another_method
+ assert_equal 1, $gvar1
+ assert_equal 2, $gvar2
+ $gvar1 = "OK1"
+ $gvar2 = "OK2"
+ end
+
+ class CVarA
+ @@cv = 'CVarA@@cv'
+ def self.cv() @@cv end
+ def self.cv=(v) @@cv = v end
+ class << self
+ def cv2() @@cv end
+ end
+ def cv() @@cv end
+ def cv=(v) @@cv = v end
+ end
+
+ class CVarB < CVarA
+ def self.cvB() @@cv end
+ def self.cvB=(v) @@cv = v end
+ class << self
+ def cvB2() @@cv end
+ end
+ def cvB() @@cv end
+ def cvB=(v) @@cv = v end
+ end
+
+ def test_class_variable
+ assert_equal 'CVarA@@cv', CVarA.cv
+ assert_equal 'CVarA@@cv', CVarA.cv2
+ assert_equal 'CVarA@@cv', CVarA.new.cv
+ CVarA.cv = 'singleton'
+ assert_equal 'singleton', CVarA.cv
+ assert_equal 'singleton', CVarA.cv2
+ assert_equal 'singleton', CVarA.new.cv
+ CVarA.new.cv = 'instance'
+ assert_equal 'instance', CVarA.cv
+ assert_equal 'instance', CVarA.cv2
+ assert_equal 'instance', CVarA.new.cv
+
+ CVarA.cv = 'CVarA@@cv'
+ CVarB.cv = 'B/singleton'
+ assert_equal 'B/singleton', CVarB.cv
+ assert_equal 'B/singleton', CVarB.cv2
+ assert_equal 'B/singleton', CVarB.new.cv
+ assert_equal 'B/singleton', CVarA.cv
+ assert_equal 'B/singleton', CVarA.cv2
+ assert_equal 'B/singleton', CVarA.new.cv
+ CVarB.new.cv = 'B/instance'
+ assert_equal 'B/instance', CVarB.cv
+ assert_equal 'B/instance', CVarB.cv2
+ assert_equal 'B/instance', CVarB.new.cv
+ assert_equal 'B/instance', CVarA.cv
+ assert_equal 'B/instance', CVarA.cv2
+ assert_equal 'B/instance', CVarA.new.cv
+
+ CVarA.cv = 'CVarA@@cv'
+ assert_equal('CVarA@@cv', CVarB.cvB)
+ assert_equal('CVarA@@cv', CVarB.cvB2)
+ assert_equal('CVarA@@cv', CVarB.new.cvB)
+ CVarB.cvB = 'B/singleton'
+ assert_equal 'B/singleton', CVarB.cvB
+ assert_equal 'B/singleton', CVarB.cvB2
+ assert_equal 'B/singleton', CVarB.new.cvB
+ assert_equal 'B/singleton', CVarA.cv
+ assert_equal 'B/singleton', CVarA.cv2
+ assert_equal 'B/singleton', CVarA.new.cv
+ CVarB.new.cvB = 'B/instance'
+ assert_equal 'B/instance', CVarB.cvB
+ assert_equal 'B/instance', CVarB.cvB2
+ assert_equal 'B/instance', CVarB.new.cvB
+ assert_equal 'B/instance', CVarA.cv
+ assert_equal 'B/instance', CVarA.cv2
+ assert_equal 'B/instance', CVarA.new.cv
+
+ CVarA.cv = 'CVarA@@cv'
+ CVarB.cvB = 'CVarB@@cv'
+ end
+
+ class OP
+ attr_reader :x
+ attr_accessor :foo
+ def x=(x)
+ @x = x
+ :Bug1996
+ end
+ Bug1996 = '[ruby-dev:39163], [ruby-core:25143]'
+ def [](i)
+ @x
+ end
+ def []=(i, x)
+ @x = x
+ :Bug2050
+ end
+ Bug2050 = '[ruby-core:25387]'
+ end
+
+ def test_opassign2_1
+ x = nil
+ assert_equal 1, x ||= 1
+ assert_equal 1, x
+ assert_equal 2, x &&= 2
+ assert_equal 2, x
+ assert_equal 2, x ||= 3
+ assert_equal 2, x
+ assert_equal 4, x &&= 4
+ assert_equal 4, x
+ assert_equal 5, x += 1
+ assert_equal 5, x
+ assert_equal 4, x -= 1
+ assert_equal 4, x
+ end
+
+ def test_opassign2_2
+ y = OP.new
+ y.x = nil
+ assert_equal 1, y.x ||= 1, OP::Bug1996
+ assert_equal 1, y.x
+ assert_equal 2, y.x &&= 2, OP::Bug1996
+ assert_equal 2, y.x
+ assert_equal 2, y.x ||= 3
+ assert_equal 2, y.x
+ assert_equal 4, y.x &&= 4, OP::Bug1996
+ assert_equal 4, y.x
+ assert_equal 5, y.x += 1, OP::Bug1996
+ assert_equal 5, y.x
+ assert_equal 4, y.x -= 1, OP::Bug1996
+ assert_equal 4, y.x
+ end
+
+ def test_opassign2_3
+ z = OP.new
+ z.x = OP.new
+ z.x.x = nil
+ assert_equal 1, z.x.x ||= 1, OP::Bug1996
+ assert_equal 1, z.x.x
+ assert_equal 2, z.x.x &&= 2, OP::Bug1996
+ assert_equal 2, z.x.x
+ assert_equal 2, z.x.x ||= 3
+ assert_equal 2, z.x.x
+ assert_equal 4, z.x.x &&= 4, OP::Bug1996
+ assert_equal 4, z.x.x
+ assert_equal 5, z.x.x += 1, OP::Bug1996
+ assert_equal 5, z.x.x
+ assert_equal 4, z.x.x -= 1, OP::Bug1996
+ assert_equal 4, z.x.x
+ end
+
+ def test_opassign1_1
+ a = []
+ a[0] = nil
+ assert_equal 1, a[0] ||= 1
+ assert_equal 1, a[0]
+ assert_equal 2, a[0] &&= 2
+ assert_equal 2, a[0]
+ assert_equal 2, a[0] ||= 3
+ assert_equal 2, a[0]
+ assert_equal 4, a[0] &&= 4
+ assert_equal 4, a[0]
+ assert_equal 5, a[0] += 1
+ assert_equal 5, a[0]
+ assert_equal 4, a[0] -= 1
+ assert_equal 4, a[0]
+ end
+
+ def test_opassign1_2
+ x = OP.new
+ x[0] = nil
+ assert_equal 1, x[0] ||= 1, OP::Bug2050
+ assert_equal 1, x[0]
+ assert_equal 2, x[0] &&= 2, OP::Bug2050
+ assert_equal 2, x[0]
+ assert_equal 2, x[0] ||= 3, OP::Bug2050
+ assert_equal 2, x[0]
+ assert_equal 4, x[0] &&= 4, OP::Bug2050
+ assert_equal 4, x[0]
+ assert_equal 5, x[0] += 1, OP::Bug2050
+ assert_equal 5, x[0]
+ assert_equal 4, x[0] -= 1, OP::Bug2050
+ assert_equal 4, x[0]
+ end
+
+ def test_send_opassign
+ return if defined?(RUBY_ENGINE) and RUBY_ENGINE != "ruby"
+
+ bug7773 = '[ruby-core:51821]'
+ x = OP.new
+ assert_equal 42, x.foo = 42, bug7773
+ assert_equal 42, x.foo, bug7773
+ assert_equal -6, x.send(:foo=, -6), bug7773
+ assert_equal -6, x.foo, bug7773
+ assert_equal :Bug1996, x.send(:x=, :case_when_setter_returns_other_value), bug7773
+ assert_equal :case_when_setter_returns_other_value, x.x, bug7773
+ end
+
+ def test_backref
+ /re/ =~ 'not match'
+ assert_nil $~
+ assert_nil $`
+ assert_nil $&
+ assert_nil $'
+ assert_nil $+
+ assert_nil $1
+ assert_nil $2
+ assert_nil $3
+ assert_nil $4
+ assert_nil $5
+ assert_nil $6
+ assert_nil $7
+ assert_nil $8
+ assert_nil $9
+
+ /(a)(b)(c)(d)(e)(f)(g)(h)(i)/ =~ 'xabcdefghiy'
+ assert_not_nil $~
+ assert_instance_of MatchData, $~
+ assert_equal 'abcdefghi', $~[0]
+ assert_equal 'a', $~[1]
+ assert_equal 'b', $~[2]
+ assert_equal 'c', $~[3]
+ assert_equal 'd', $~[4]
+ assert_equal 'e', $~[5]
+ assert_equal 'f', $~[6]
+ assert_equal 'g', $~[7]
+ assert_equal 'h', $~[8]
+ assert_equal 'i', $~[9]
+ assert_equal 'x', $`
+ assert_equal 'abcdefghi', $&
+ assert_equal "y", $'
+ assert_equal 'i', $+
+ assert_equal 'a', $1
+ assert_equal 'b', $2
+ assert_equal 'c', $3
+ assert_equal 'd', $4
+ assert_equal 'e', $5
+ assert_equal 'f', $6
+ assert_equal 'g', $7
+ assert_equal 'h', $8
+ assert_equal 'i', $9
+
+ /re/ =~ 'not match'
+ assert_nil $~
+ assert_nil $`
+ assert_nil $&
+ assert_nil $'
+ assert_nil $+
+ assert_nil $1
+ assert_nil $2
+ assert_nil $3
+ assert_nil $4
+ assert_nil $5
+ assert_nil $6
+ assert_nil $7
+ assert_nil $8
+ assert_nil $9
+ end
+
+ def test_array_splat
+ feature1125 = '[ruby-core:21901]'
+
+ a = []
+ assert_equal [], [*a]
+ assert_equal [1], [1, *a]
+ assert_not_same(a, [*a], feature1125)
+ a = [2]
+ assert_equal [2], [*a]
+ assert_equal [1, 2], [1, *a]
+ assert_not_same(a, [*a], feature1125)
+ a = [2, 3]
+ assert_equal [2, 3], [*a]
+ assert_equal [1, 2, 3], [1, *a]
+ assert_not_same(a, [*a], feature1125)
+
+ a = nil
+ assert_equal [], [*a]
+ assert_equal [1], [1, *a]
+ end
+end
diff --git a/jni/ruby/test/ruby/test_beginendblock.rb b/jni/ruby/test/ruby/test_beginendblock.rb
new file mode 100644
index 0000000..9c9d6b5
--- /dev/null
+++ b/jni/ruby/test/ruby/test_beginendblock.rb
@@ -0,0 +1,190 @@
+require 'test/unit'
+require 'tempfile'
+require 'timeout'
+
+class TestBeginEndBlock < Test::Unit::TestCase
+ DIR = File.dirname(File.expand_path(__FILE__))
+
+ def q(content)
+ "\"#{content}\""
+ end
+
+ def test_beginendblock
+ ruby = EnvUtil.rubybin
+ target = File.join(DIR, 'beginmainend.rb')
+ result = IO.popen([ruby, target]){|io|io.read}
+ assert_equal(%w(b1 b2-1 b2 main b3-1 b3 b4 e1 e1-1 e4 e4-2 e4-1 e4-1-1 e3 e2), result.split)
+
+ Tempfile.create(self.class.name) {|input|
+ inputpath = input.path
+ result = IO.popen([ruby, "-n", "-eBEGIN{p :begin}", "-eEND{p :end}", inputpath]){|io|io.read}
+ assert_equal(%w(:begin), result.split)
+ result = IO.popen([ruby, "-p", "-eBEGIN{p :begin}", "-eEND{p :end}", inputpath]){|io|io.read}
+ assert_equal(%w(:begin), result.split)
+ input.puts "foo\nbar"
+ input.close
+ result = IO.popen([ruby, "-n", "-eBEGIN{p :begin}", "-eEND{p :end}", inputpath]){|io|io.read}
+ assert_equal(%w(:begin :end), result.split)
+ result = IO.popen([ruby, "-p", "-eBEGIN{p :begin}", "-eEND{p :end}", inputpath]){|io|io.read}
+ assert_equal(%w(:begin foo bar :end), result.split)
+ }
+ end
+
+ def test_begininmethod
+ assert_raise_with_message(SyntaxError, /BEGIN is permitted only at toplevel/) do
+ eval("def foo; BEGIN {}; end")
+ end
+
+ assert_raise_with_message(SyntaxError, /BEGIN is permitted only at toplevel/) do
+ eval('eval("def foo; BEGIN {}; end")')
+ end
+ end
+
+ def test_begininclass
+ assert_raise_with_message(SyntaxError, /BEGIN is permitted only at toplevel/) do
+ eval("class TestBeginEndBlock; BEGIN {}; end")
+ end
+ end
+
+ def test_endblockwarn
+ ruby = EnvUtil.rubybin
+ # Use Tempfile to create temporary file path.
+ Tempfile.create(self.class.name) {|launcher|
+ Tempfile.create(self.class.name) {|errout|
+
+ launcher << <<EOF
+# -*- coding: #{ruby.encoding.name} -*-
+errout = ARGV.shift
+STDERR.reopen(File.open(errout, "w"))
+STDERR.sync = true
+Dir.chdir(#{q(DIR)})
+system("#{ruby}", "endblockwarn_rb")
+EOF
+ launcher.close
+ launcherpath = launcher.path
+ errout.close
+ erroutpath = errout.path
+ system(ruby, launcherpath, erroutpath)
+ expected = <<EOW
+endblockwarn_rb:2: warning: END in method; use at_exit
+(eval):2: warning: END in method; use at_exit
+EOW
+ assert_equal(expected, File.read(erroutpath))
+ }
+ }
+ end
+
+ def test_raise_in_at_exit
+ ruby = EnvUtil.rubybin
+ out = IO.popen([ruby, '-e', 'STDERR.reopen(STDOUT)',
+ '-e', 'at_exit{raise %[SomethingBad]}',
+ '-e', 'raise %[SomethingElse]']) {|f|
+ f.read
+ }
+ status = $?
+ assert_match(/SomethingBad/, out, "[ruby-core:9675]")
+ assert_match(/SomethingElse/, out, "[ruby-core:9675]")
+ assert_not_predicate(status, :success?)
+ end
+
+ def test_exitcode_in_at_exit
+ bug8501 = '[ruby-core:55365] [Bug #8501]'
+ ruby = EnvUtil.rubybin
+ out = IO.popen([ruby, '-e', 'STDERR.reopen(STDOUT)',
+ '-e', 'o = Object.new; def o.inspect; raise "[Bug #8501]"; end',
+ '-e', 'at_exit{o.nope}']) {|f|
+ f.read
+ }
+ status = $?
+ assert_match(/undefined method `nope'/, out, bug8501)
+ assert_not_predicate(status, :success?, bug8501)
+ end
+
+ def test_propagate_exit_code
+ ruby = EnvUtil.rubybin
+ assert_equal false, system(ruby, '-e', 'at_exit{exit 2}')
+ assert_equal 2, $?.exitstatus
+ assert_nil $?.termsig
+ end
+
+ def test_propagate_signaled
+ ruby = EnvUtil.rubybin
+ out = IO.popen(
+ [ruby,
+ '-e', 'trap(:INT, "DEFAULT")',
+ '-e', 'STDERR.reopen(STDOUT)',
+ '-e', 'at_exit{Process.kill(:INT, $$); sleep 5 }']) {|f|
+ timeout(10) {
+ f.read
+ }
+ }
+ assert_match(/Interrupt$/, out)
+ Process.kill(0, 0) rescue return # check if signal works
+ assert_nil $?.exitstatus
+ assert_equal Signal.list["INT"], $?.termsig
+ end
+
+ def test_endblock_raise
+ ruby = EnvUtil.rubybin
+ th = nil
+ out = IO.popen(
+ [ruby,
+ '-e', 'class C; def write(x); puts x; STDOUT.flush; sleep 0.01; end; end',
+ '-e', '$stderr = C.new',
+ '-e', 'END {raise "e1"}; END {puts "e2"}',
+ '-e', 'END {raise "e3"}; END {puts "e4"}',
+ '-e', 'END {raise "e5"}; END {puts "e6"}']) {|f|
+ th = Thread.new {sleep 5; Process.kill :KILL, f.pid}
+ f.read
+ }
+ assert_match(/e1/, out)
+ assert_match(/e6/, out)
+ ensure
+ th.kill.join if th.alive?
+ end
+
+ def test_nested_at_exit
+ Tempfile.create(["test_nested_at_exit_", ".rb"]) {|t|
+ t.puts "at_exit { puts :outer0 }"
+ t.puts "at_exit { puts :outer1_begin; at_exit { puts :inner1 }; puts :outer1_end }"
+ t.puts "at_exit { puts :outer2_begin; at_exit { puts :inner2 }; puts :outer2_end }"
+ t.puts "at_exit { puts :outer3 }"
+ t.flush
+
+ expected = [ "outer3",
+ "outer2_begin",
+ "outer2_end",
+ "inner2",
+ "outer1_begin",
+ "outer1_end",
+ "inner1",
+ "outer0" ]
+
+ assert_in_out_err(t.path, "", expected, [], "[ruby-core:35237]")
+ }
+ end
+
+ def test_rescue_at_exit
+ bug5218 = '[ruby-core:43173][Bug #5218]'
+ cmd = [
+ "raise 'X' rescue nil",
+ "nil",
+ "exit(42)",
+ ]
+ %w[at_exit END].each do |ex|
+ out, err, status = EnvUtil.invoke_ruby(cmd.map {|s|["-e", "#{ex} {#{s}}"]}.flatten, "", true, true)
+ assert_equal(["", "", 42], [out, err, status.exitstatus], "#{bug5218}: #{ex}")
+ end
+ end
+
+ def test_callcc_at_exit
+ bug9110 = '[ruby-core:58329][Bug #9110]'
+ script = <<EOS
+require "continuation"
+c = nil
+at_exit { c.call }
+at_exit { callcc {|_c| c = _c } }
+EOS
+ assert_normal_exit(script, bug9110)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_bignum.rb b/jni/ruby/test/ruby/test_bignum.rb
new file mode 100644
index 0000000..2c6eda0
--- /dev/null
+++ b/jni/ruby/test/ruby/test_bignum.rb
@@ -0,0 +1,711 @@
+require 'test/unit'
+
+class TestBignum < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ @fmax = Float::MAX.to_i
+ @fmax2 = @fmax * 2
+ @big = (1 << 63) - 1
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def fact(n)
+ return 1 if n == 0
+ f = 1
+ while n>0
+ f *= n
+ n -= 1
+ end
+ return f
+ end
+
+ def test_bignum
+ $x = fact(40)
+ assert_equal($x, $x)
+ assert_equal($x, fact(40))
+ assert_operator($x, :<, $x+2)
+ assert_operator($x, :>, $x-2)
+ assert_equal(815915283247897734345611269596115894272000000000, $x)
+ assert_not_equal(815915283247897734345611269596115894272000000001, $x)
+ assert_equal(815915283247897734345611269596115894272000000001, $x+1)
+ assert_equal(335367096786357081410764800000, $x/fact(20))
+ $x = -$x
+ assert_equal(-815915283247897734345611269596115894272000000000, $x)
+ assert_equal(2-(2**32), -(2**32-2))
+ assert_equal(2**32 - 5, (2**32-3)-2)
+
+ for i in 1000..1014
+ assert_equal(2 ** i, 1 << i)
+ end
+
+ n1 = 1 << 1000
+ for i in 1000..1014
+ assert_equal(n1, 1 << i)
+ n1 *= 2
+ end
+
+ n2=n1
+ for i in 1..10
+ n1 = n1 / 2
+ n2 = n2 >> 1
+ assert_equal(n1, n2)
+ end
+
+ for i in 4000..4096
+ n1 = 1 << i;
+ assert_equal(n1-1, (n1**2-1) / (n1+1))
+ end
+ end
+
+ def test_calc
+ b = 10**80
+ a = b * 9 + 7
+ assert_equal(7, a.modulo(b))
+ assert_equal(-b + 7, a.modulo(-b))
+ assert_equal(b + -7, (-a).modulo(b))
+ assert_equal(-7, (-a).modulo(-b))
+ assert_equal(7, a.remainder(b))
+ assert_equal(7, a.remainder(-b))
+ assert_equal(-7, (-a).remainder(b))
+ assert_equal(-7, (-a).remainder(-b))
+
+ assert_equal(10000000000000000000100000000000000000000, 10**40+10**20)
+ assert_equal(100000000000000000000, 10**40/10**20)
+
+ a = 677330545177305025495135714080
+ b = 14269972710765292560
+ assert_equal(0, a % b)
+ assert_equal(0, -a % b)
+ end
+
+ def shift_test(a)
+ b = a / (2 ** 32)
+ c = a >> 32
+ assert_equal(b, c)
+
+ b = a * (2 ** 32)
+ c = a << 32
+ assert_equal(b, c)
+ end
+
+ def test_shift
+ shift_test(-4518325415524767873)
+ shift_test(-0xfffffffffffffffff)
+ end
+
+ def test_to_s
+ assert_equal("fvvvvvvvvvvvv" ,18446744073709551615.to_s(32), "[ruby-core:10686]")
+ assert_equal("g000000000000" ,18446744073709551616.to_s(32), "[ruby-core:10686]")
+ assert_equal("3w5e11264sgsf" ,18446744073709551615.to_s(36), "[ruby-core:10686]")
+ assert_equal("3w5e11264sgsg" ,18446744073709551616.to_s(36), "[ruby-core:10686]")
+ assert_equal("nd075ib45k86f" ,18446744073709551615.to_s(31), "[ruby-core:10686]")
+ assert_equal("nd075ib45k86g" ,18446744073709551616.to_s(31), "[ruby-core:10686]")
+ assert_equal("1777777777777777777777" ,18446744073709551615.to_s(8))
+ assert_equal("-1777777777777777777777" ,-18446744073709551615.to_s(8))
+ assert_match(/\A10{99}1\z/, (10**100+1).to_s)
+ assert_match(/\A10{900}9{100}\z/, (10**1000+(10**100-1)).to_s)
+ end
+
+ b = 2**64
+ b *= b until Bignum === b
+
+ T_ZERO = b.coerce(0).first
+ T_ONE = b.coerce(1).first
+ T_MONE = b.coerce(-1).first
+ T31 = b.coerce(2**31).first # 2147483648
+ T31P = b.coerce(T31 - 1).first # 2147483647
+ T32 = b.coerce(2**32).first # 4294967296
+ T32P = b.coerce(T32 - 1).first # 4294967295
+ T64 = b.coerce(2**64).first # 18446744073709551616
+ T64P = b.coerce(T64 - 1).first # 18446744073709551615
+ T1024 = b.coerce(2**1024).first
+ T1024P = b.coerce(T1024 - 1).first
+
+ f = b
+ while Bignum === f-1
+ f = f >> 1
+ end
+ FIXNUM_MAX = f-1
+
+ def test_prepare
+ assert_instance_of(Bignum, T_ZERO)
+ assert_instance_of(Bignum, T_ONE)
+ assert_instance_of(Bignum, T_MONE)
+ assert_instance_of(Bignum, T31)
+ assert_instance_of(Bignum, T31P)
+ assert_instance_of(Bignum, T32)
+ assert_instance_of(Bignum, T32P)
+ assert_instance_of(Bignum, T64)
+ assert_instance_of(Bignum, T64P)
+ assert_instance_of(Bignum, T1024)
+ assert_instance_of(Bignum, T1024P)
+ end
+
+ def test_big_2comp
+ assert_equal("-4294967296", (~T32P).to_s)
+ assert_equal("..f00000000", "%x" % -T32)
+ end
+
+ def test_int2inum
+ assert_equal([T31P], [T31P].pack("I").unpack("I"))
+ assert_equal([T31P], [T31P].pack("i").unpack("i"))
+ end
+
+ def test_quad_pack
+ assert_equal([ 1], [ 1].pack("q").unpack("q"))
+ assert_equal([- 1], [- 1].pack("q").unpack("q"))
+ assert_equal([ T31P], [ T31P].pack("q").unpack("q"))
+ assert_equal([-T31P], [-T31P].pack("q").unpack("q"))
+ assert_equal([ T64P], [ T64P].pack("Q").unpack("Q"))
+ assert_equal([ 0], [ T64 ].pack("Q").unpack("Q"))
+ end
+
+ def test_str_to_inum
+ assert_equal(1, " +1".to_i)
+ assert_equal(-1, " -1".to_i)
+ assert_equal(0, "++1".to_i)
+ assert_equal(73, "111".oct)
+ assert_equal(273, "0x111".oct)
+ assert_equal(7, "0b111".oct)
+ assert_equal(73, "0o111".oct)
+ assert_equal(111, "0d111".oct)
+ assert_equal(73, "0111".oct)
+ assert_equal(111, Integer("111"))
+ assert_equal(13, "111".to_i(3))
+ assert_raise(ArgumentError) { "111".to_i(37) }
+ assert_equal(1333, "111".to_i(36))
+ assert_equal(1057, "111".to_i(32))
+ assert_equal(0, "00a".to_i)
+ assert_equal(1, Integer("1 "))
+ assert_raise(ArgumentError) { Integer("1_") }
+ assert_raise(ArgumentError) { Integer("1__") }
+ assert_raise(ArgumentError) { Integer("1_0 x") }
+ assert_equal(T31P, "1111111111111111111111111111111".to_i(2))
+ assert_equal(0_2, '0_2'.to_i)
+ assert_equal(00_2, '00_2'.to_i)
+ assert_equal(00_02, '00_02'.to_i)
+ end
+
+ def test_to_s2
+ assert_raise(ArgumentError) { T31P.to_s(37) }
+ assert_equal("9" * 32768, (10**32768-1).to_s)
+ assert_raise(RangeError) { Process.wait(1, T64P) }
+ assert_equal("0", T_ZERO.to_s)
+ assert_equal("1", T_ONE.to_s)
+ end
+
+ def test_to_f
+ assert_nothing_raised { T31P.to_f.to_i }
+ assert_raise(FloatDomainError) { (1024**1024).to_f.to_i }
+ assert_equal(1, (2**50000).to_f.infinite?)
+ assert_equal(-1, (-(2**50000)).to_f.infinite?)
+ end
+
+ def test_cmp
+ assert_operator(T31P, :>, 1)
+ assert_operator(T31P, :<, 2147483648.0)
+ assert_operator(T31P, :<, T64P)
+ assert_operator(T64P, :>, T31P)
+ assert_raise(ArgumentError) { T31P < "foo" }
+ assert_operator(T64, :<, (1.0/0.0))
+ assert_not_operator(T64, :>, (1.0/0.0))
+ end
+
+ def test_eq
+ assert_not_equal(T31P, 1)
+ assert_equal(T31P, 2147483647.0)
+ assert_not_equal(T31P, "foo")
+ assert_not_equal(2**77889, (1.0/0.0), '[ruby-core:31603]')
+ end
+
+ def test_eql
+ assert_send([T31P, :eql?, T31P])
+ end
+
+ def test_convert
+ assert_equal([255], [T_MONE].pack("C").unpack("C"))
+ assert_equal([0], [T32].pack("C").unpack("C"))
+ assert_raise(RangeError) { 0.to_s(T32) }
+ end
+
+ def test_sub
+ assert_equal(-T31, T32 - (T32 + T31))
+ x = 2**100
+ assert_equal(1, (x+2) - (x+1))
+ assert_equal(-1, (x+1) - (x+2))
+ assert_equal(0, (2**100) - (2.0**100))
+ o = Object.new
+ def o.coerce(x); [x, 2**100+2]; end
+ assert_equal(-1, (2**100+1) - o)
+ assert_equal(-1, T_ONE - 2)
+ end
+
+ def test_plus
+ assert_equal(T32.to_f, T32P + 1.0)
+ assert_raise(TypeError) { T32 + "foo" }
+ assert_equal(1267651809154049016125877911552, (2**100) + (2**80))
+ assert_equal(1267651809154049016125877911552, (2**80) + (2**100))
+ assert_equal(2**101, (2**100) + (2.0**100))
+ o = Object.new
+ def o.coerce(x); [x, 2**80]; end
+ assert_equal(1267651809154049016125877911552, (2**100) + o)
+ end
+
+ def test_minus
+ assert_equal(T32P.to_f, T32 - 1.0)
+ assert_raise(TypeError) { T32 - "foo" }
+ end
+
+ def test_mul
+ assert_equal(T32.to_f, T32 * 1.0)
+ assert_raise(TypeError) { T32 * "foo" }
+ o = Object.new
+ def o.coerce(x); [x, 2**100]; end
+ assert_equal(2**180, (2**80) * o)
+ end
+
+ def test_mul_balance
+ assert_equal(3**7000, (3**5000) * (3**2000))
+ end
+
+ def test_mul_large_numbers
+ a = %w[
+ 32580286268570032115047167942578356789222410206194227403993117616454027392
+ 62501901985861926098797067562795526004375784403965882943322008991129440928
+ 33855888840298794008677656280486901895499985197580043127115026675632969396
+ 55040226415022070581995493731570435346323030715226718346725312551631168110
+ 83966158581772380474470605428802018934282425947323171408377505151988776271
+ 85865548747366001752375899635539662017095652855537225416899242508164949615
+ 96848508410008685252121247181772953744297349638273854170932226446528911938
+ 03430429031094465344063914822790537339912760237589085026016396616506014081
+ 53557719631183538265614091691713138728177917059624255801026099255450058876
+ 97412698978242128457751836011774504753020608663272925708049430557191193188
+ 23212591809241860763625985763438355314593186083254640117460724730431447842
+ 15432124830037389073162094304199742919767272162759192882136828372588787906
+ 96027938532441670018954643423581446981760344524184231299785949158765352788
+ 38452309862972527623669323263424418781899966895996672291193305401609553502
+ 63893514163147729201340204483973131948541009975283778189609285614445485714
+ 63843850089417416331356938086609682943037801440660232801570877143192251897
+ 63026816485314923378023904237699794122181407920355722922555234540701118607
+ 37971417665315821995516986204709574657462370947443531049033704997194647442
+ 13711787319587466437795542850136751816475182349380345341647976135081955799
+ 56787050815348701001765730577514591032367920292271016649813170789854524395
+ 72571698998841196411826453893352760318867994518757872432266374568779920489
+ 55597104558927387008506485038236352630863481679853742412042588244086070827
+ 43705456833283086410967648483312972903432798923897357373793064381177468258
+ 69131640408147806442422254638590386673344704147156793990832671592488742473
+ 31524606724894164324227362735271650556732855509929890983919463699819116427
+ ].join.to_i
+ b = %w[
+ 31519454770031243652776765515030872050264386564379909299874378289835540661
+ 99756262835346828114038365624177182230027040172583473561802565238817167503
+ 85144159132462819032164726177606533272071955542237648482852154879445654746
+ 25061253606344846225905712926863168413666058602449408307586532461776530803
+ 56810626880722653177544008166119272373179841889454920521993413902672848145
+ 77974951972342194855267960390195830413354782136431833731467699250684103370
+ 98571305167189174270854698169136844578685346745340041520068176478277580590
+ 43810457765638903028049263788987034217272442328962400931269515791911786205
+ 15357047519615932249418012945178659435259428163356223753159488306813844040
+ 93609959555018799309373542926110109744437994067754004273450659607204900586
+ 28878103661124568217617766580438460505513654179249613168352070584906185237
+ 34829991855182473813233425492094534396541544295119674419522772382981982574
+ 64708442087451070125274285088681225122475041996116377707892328889948526913
+ 82239084041628877737628853240361038273348062246951097300286513836140601495
+ 63604611754185656404194406869925540477185577643853560887894081047256701731
+ 66884554460428760857958761948461476977864005799494946578017758268987123749
+ 85937011490156431231903167442071541493304390639100774497107347884381581049
+ 85451663323551635322518839895028929788021096587229364219084708576998525298
+ 39594168681411529110089531428721005176467479027585291807482375043729783455
+ 35827667428080449919778142400266842990117940984804919512360370451936835708
+ 76338722049621773169385978521438867493162717866679193103745711403152099047
+ 27294943901673885707639094215339506973982546487889199083181789561917985023
+ 82368442718514694400160954955539704757794969665555505203532944598698824542
+ 00599461848630034847211204029842422678421808487300084850702007663003230882
+ 16645745324467830796203354080471008809087072562876681588151822072260738003
+ ].join.to_i
+ c = %w[
+ 10269128594368631269792194698469828812223242061960065022209211719149714886
+ 03494742299892841188636314745174778237781513956755034582435818316155459882
+ 71422025990633195596790290038198841087091600598192959108790192789550336119
+ 13849937951116346796903163312950010689963716629093190601532313463306463573
+ 64436438673379454947908896258675634478867189655764364639888427350090856831
+ 84369949421175534994092429682748078316130135651006102162888937624830856951
+ 64818150356583421988135211585954838926347035741143424980258821170351244310
+ 33072045488402539147707418016613224788469923473310249137422855065567940804
+ 75231970365923936034328561426062696074717204901606475826224235014948198414
+ 19979210494282212322919438926816203585575357874850252052656098969732107129
+ 30639419804565653489687198910271702181183420960744232756057631336661646896
+ 48734093497394719644969417287962767186599484579769717220518657324467736902
+ 16947995288312851432262922140679347615046098863974141226499783975470926697
+ 95970415188661518504275964397022973192968233221707696639386238428211541334
+ 69925631385166494600401675904803418143232703594169525858261988389529181035
+ 06048776134746377586210180203524132714354779486439559392942733781343640971
+ 02430607931736785273011780813863748280091795277451796799961887248262211653
+ 38966967509803488282644299584920109534552889962877144862747797551711984992
+ 00726518175235286668236031649728858774545087668286506201943248842967749907
+ 05345423019480534625965140632428736051632750698608916592720742728646191514
+ 86268964807395494825321744802493138032936406889713953832376411900451422777
+ 06372983421062172556566901346288286168790235741528630664513209619789835729
+ 36999522461733403414326366959273556098219489572448083984779946889707480205
+ 42459898495081687425132939473146331452400120169525968892769310016015870148
+ 66821361032541586130017904207971120217385522074967066199941112154460026348
+ 07223950375610474071278649031647998546085807777970592429037128484222394216
+ 33776560239741740193444702279919018283324070210090106960567819910943036248
+ 16660475627526085805165023447934326510232828674828006752369603151390527384
+ 16810180735871644266726954590262010744712519045524839388305761859432443670
+ 05188791334908140831469790180096209292338569623252372975043915954675335333
+ 66614002146554533771788633057869340167604765688639181655208751680821446276
+ 75871494160208888666798836473728725968253820774671626436794492530356258709
+ 62318715778035246655925307167306434486713879511272648637608703497794724929
+ 54912261106702913491290913962825303534484477936036071463820553314826894581
+ 36951927032835690160443252405644718368516656317176848748544135126122940034
+ 68454782581240953957381976073459570718038035358630417744490242611126043987
+ 89191812971310096496208294948623403471433467614886863238916702384858514703
+ 24327715474804343531844042107910755966152655912676456945146277848606406879
+ 49724219295823540160221752189725460676360350860849986313532861445465771187
+ 86822806696323658053947125253562001971534265078959827450518368635828010637
+ 91977444206363529864361796188661941906329947840521598310396004328950804758
+ 79728679236044038853668859284513594307352133390781441610395116807369310560
+ 35193762565748328526426224069629084264376146174383444988110993194030351064
+ 29660536743256949099972314033972121470913480844652490838985461134989129492
+ 75577567064571716731774820127381261057956083604361635892088585967074514802
+ 51958582645785905276289980534832170529946494815794770854644518463332458915
+ 77572397432680871220602513555535017751714443325264019171753694163676670792
+ 04353584782364068773777058727187323211012094819929720407636607815292764459
+ 21851731257845562153822058534043916834839514338448582518847879059020959697
+ 90538105704766415685100946308842788321400392381169436435078204622400475281
+ ].join.to_i
+ assert_equal(c, a*b, '[ruby-core:48552]')
+ end
+
+ def test_divrem
+ assert_equal(0, T32 / T64)
+ end
+
+ def test_divide
+ bug5490 = '[ruby-core:40429]'
+ assert_raise(ZeroDivisionError, bug5490) {T1024./(0)}
+ assert_equal(Float::INFINITY, T1024./(0.0), bug5490)
+ end
+
+ def test_div
+ assert_equal(T32.to_f, T32 / 1.0)
+ assert_raise(TypeError) { T32 / "foo" }
+ assert_equal(0x20000000, 0x40000001.div(2.0), "[ruby-dev:34553]")
+ bug5490 = '[ruby-core:40429]'
+ assert_raise(ZeroDivisionError, bug5490) {T1024.div(0)}
+ assert_raise(ZeroDivisionError, bug5490) {T1024.div(0.0)}
+ end
+
+ def test_idiv
+ assert_equal(715827882, 1073741824.div(Rational(3,2)), ' [ruby-dev:34066]')
+ end
+
+ def test_modulo
+ assert_raise(TypeError) { T32 % "foo" }
+ end
+
+ def test_remainder
+ assert_equal(0, T32.remainder(1))
+ assert_raise(TypeError) { T32.remainder("foo") }
+ end
+
+ def test_divmod
+ assert_equal([T32, 0], T32.divmod(1))
+ assert_equal([2, 0], T32.divmod(T31))
+ assert_raise(TypeError) { T32.divmod("foo") }
+ end
+
+ def test_quo
+ assert_kind_of(Float, T32.quo(1.0))
+
+ assert_equal(T32.to_f, T32.quo(1))
+ assert_equal(T32.to_f, T32.quo(1.0))
+ assert_equal(T32.to_f, T32.quo(T_ONE))
+
+ assert_raise(TypeError) { T32.quo("foo") }
+
+ assert_equal(1024**1024, (1024**1024).quo(1))
+ assert_equal(Float::INFINITY, (1024**1024).quo(1.0))
+ assert_equal(1024**1024*2, (1024**1024*2).quo(1))
+ inf = 1 / 0.0; nan = inf / inf
+
+ assert_send([(1024**1024*2).quo(nan), :nan?])
+ end
+
+ def test_pow
+ assert_equal(1.0, T32 ** 0.0)
+ assert_equal(1.0 / T32, T32 ** -1)
+ assert_equal(1, (T32 ** T32).infinite?)
+ assert_equal(1, (T32 ** (2**30-1)).infinite?)
+
+ ### rational changes the behavior of Bignum#**
+ #assert_raise(TypeError) { T32**"foo" }
+ assert_raise(TypeError, ArgumentError) { T32**"foo" }
+
+ feature3429 = '[ruby-core:30735]'
+ assert_instance_of(Bignum, (2 ** 7830457), feature3429)
+ end
+
+ def test_and
+ assert_equal(0, T32 & 1)
+ assert_equal(-T32, (-T32) & (-T31))
+ assert_equal(0, T32 & T64)
+ end
+
+ def test_or
+ assert_equal(T32 + 1, T32 | 1)
+ assert_equal(T32 + T31, T32 | T31)
+ assert_equal(-T31, (-T32) | (-T31))
+ assert_equal(T64 + T32, T32 | T64)
+ assert_equal(FIXNUM_MAX, T_ZERO | FIXNUM_MAX)
+ end
+
+ def test_xor
+ assert_equal(T32 + 1, T32 ^ 1)
+ assert_equal(T32 + T31, T32 ^ T31)
+ assert_equal(T31, (-T32) ^ (-T31))
+ assert_equal(T64 + T32, T32 ^ T64)
+ end
+
+ class DummyNumeric < Numeric
+ def to_int
+ 1
+ end
+ end
+
+ def test_and_with_float
+ assert_raise(TypeError) { T1024 & 1.5 }
+ end
+
+ def test_and_with_rational
+ assert_raise(TypeError, "#1792") { T1024 & Rational(3, 2) }
+ end
+
+ def test_and_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { T1024 & DummyNumeric.new }
+ end
+
+ def test_or_with_float
+ assert_raise(TypeError) { T1024 | 1.5 }
+ end
+
+ def test_or_with_rational
+ assert_raise(TypeError, "#1792") { T1024 | Rational(3, 2) }
+ end
+
+ def test_or_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { T1024 | DummyNumeric.new }
+ end
+
+ def test_xor_with_float
+ assert_raise(TypeError) { T1024 ^ 1.5 }
+ end
+
+ def test_xor_with_rational
+ assert_raise(TypeError, "#1792") { T1024 ^ Rational(3, 2) }
+ end
+
+ def test_xor_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { T1024 ^ DummyNumeric.new }
+ end
+
+ def test_shift2
+ assert_equal(2**33, (2**32) << 1)
+ assert_equal(2**31, (2**32) << -1)
+ assert_equal(2**33, (2**32) << 1.0)
+ assert_equal(2**31, (2**32) << -1.0)
+ assert_equal(2**33, (2**32) << T_ONE)
+ assert_equal(2**31, (2**32) << T_MONE)
+ assert_equal(2**31, (2**32) >> 1)
+ assert_equal(2**33, (2**32) >> -1)
+ assert_equal(2**31, (2**32) >> 1.0)
+ assert_equal(2**33, (2**32) >> -1.0)
+ assert_equal(2**31, (2**32) >> T_ONE)
+ assert_equal(2**33, (2**32) >> T_MONE)
+ assert_equal( 0, (2**32) >> (2**32))
+ assert_equal(-1, -(2**32) >> (2**32))
+ assert_equal( 0, (2**32) >> 128)
+ assert_equal(-1, -(2**32) >> 128)
+ assert_equal( 0, (2**31) >> 32)
+ assert_equal(-1, -(2**31) >> 32)
+ end
+
+ def test_shift_bigshift
+ big = 2**300
+ assert_equal(2**65538 / (2**65537), 2**65538 >> big.coerce(65537).first)
+ end
+
+ def test_aref
+ assert_equal(0, (2**32)[0])
+ assert_equal(0, (2**32)[2**32])
+ assert_equal(0, (2**32)[-(2**32)])
+ assert_equal(0, (2**32)[T_ZERO])
+ assert_equal(0, (-(2**64))[0])
+ assert_equal(1, (-2**256)[256])
+ end
+
+ def test_hash
+ assert_nothing_raised { T31P.hash }
+ end
+
+ def test_coerce
+ assert_equal([T64P, T31P], T31P.coerce(T64P))
+ assert_raise(TypeError) { T31P.coerce(nil) }
+ end
+
+ def test_abs
+ assert_equal(T31P, (-T31P).abs)
+ end
+
+ def test_size
+ assert_kind_of(Integer, T31P.size)
+ end
+
+ def test_odd
+ assert_equal(true, (2**32+1).odd?)
+ assert_equal(false, (2**32).odd?)
+ end
+
+ def test_even
+ assert_equal(false, (2**32+1).even?)
+ assert_equal(true, (2**32).even?)
+ end
+
+ def test_interrupt_during_to_s
+ if defined?(Bignum::GMP_VERSION)
+ return # GMP doesn't support interrupt during an operation.
+ end
+ time = Time.now
+ start_flag = false
+ end_flag = false
+ num = (65536 ** 65536)
+ thread = Thread.new do
+ start_flag = true
+ num.to_s
+ end_flag = true
+ end
+ sleep 0.001 until start_flag
+ thread.raise
+ thread.join rescue nil
+ time = Time.now - time
+ skip "too fast cpu" if end_flag
+ assert_operator(time, :<, 10)
+ end
+
+ def test_interrupt_during_bigdivrem
+ if defined?(Bignum::GMP_VERSION)
+ return # GMP doesn't support interrupt during an operation.
+ end
+ return unless Process.respond_to?(:kill)
+ begin
+ trace = []
+ oldtrap = Signal.trap(:INT) {|sig| trace << :int }
+ a = 456 ** 100
+ b = 123 ** 100
+ c = nil
+ 100.times do |n|
+ a **= 3
+ b **= 3
+ trace.clear
+ th = Thread.new do
+ sleep 0.1; Process.kill :INT, $$
+ sleep 0.1; Process.kill :INT, $$
+ end
+ c = a / b
+ trace << :end
+ th.join
+ if trace == [:int, :int, :end]
+ assert_equal(a / b, c)
+ return
+ end
+ end
+ skip "cannot create suitable test case"
+ ensure
+ Signal.trap(:INT, oldtrap) if oldtrap
+ end
+ end
+
+ def test_too_big_to_s
+ if (big = 2**31-1).is_a?(Fixnum)
+ return
+ end
+ assert_raise_with_message(RangeError, /too big to convert/) {(1 << big).to_s}
+ end
+
+ def test_fix_fdiv
+ assert_not_equal(0, 1.fdiv(@fmax2))
+ assert_in_delta(0.5, 1.fdiv(@fmax2) * @fmax, 0.01)
+ end
+
+ def test_big_fdiv
+ assert_equal(1, @big.fdiv(@big))
+ assert_not_equal(0, @big.fdiv(@fmax2))
+ assert_not_equal(0, @fmax2.fdiv(@big))
+ assert_not_equal(0, @fmax2.fdiv(@fmax2))
+ assert_in_delta(0.5, @fmax.fdiv(@fmax2), 0.01)
+ assert_in_delta(1.0, @fmax2.fdiv(@fmax2), 0.01)
+ end
+
+ def test_float_fdiv
+ b = 1E+300.to_i
+ assert_equal(b, (b ** 2).fdiv(b))
+ assert_send([@big.fdiv(0.0 / 0.0), :nan?])
+ assert_in_delta(1E+300, (10**500).fdiv(1E+200), 1E+285)
+ end
+
+ def test_obj_fdiv
+ o = Object.new
+ def o.coerce(x); [x, 2**100]; end
+ assert_equal((2**200).to_f, (2**300).fdiv(o))
+ end
+
+ def test_singleton_method
+ # this test assumes 32bit/64bit platform
+ assert_raise(TypeError) { a = 1 << 64; def a.foo; end }
+ end
+
+ def test_frozen
+ assert_equal(true, (2**100).frozen?)
+ end
+
+ def test_bitwise_and_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { T1024 & obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(T1024 & 10, T1024 & obj)
+ end
+
+ def test_bitwise_or_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { T1024 | obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(T1024 | 10, T1024 | obj)
+ end
+
+ def test_bitwise_xor_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { T1024 ^ obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(T1024 ^ 10, T1024 ^ obj)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_call.rb b/jni/ruby/test/ruby/test_call.rb
new file mode 100644
index 0000000..5b81eb1
--- /dev/null
+++ b/jni/ruby/test/ruby/test_call.rb
@@ -0,0 +1,34 @@
+require 'test/unit'
+
+class TestCall < Test::Unit::TestCase
+ def aaa(a, b=100, *rest)
+ res = [a, b]
+ res += rest if rest
+ return res
+ end
+
+ def test_call
+ assert_raise(ArgumentError) {aaa()}
+ assert_raise(ArgumentError) {aaa}
+
+ assert_equal([1, 100], aaa(1))
+ assert_equal([1, 2], aaa(1, 2))
+ assert_equal([1, 2, 3, 4], aaa(1, 2, 3, 4))
+ assert_equal([1, 2, 3, 4], aaa(1, *[2, 3, 4]))
+ end
+
+ def test_callinfo
+ bug9622 = '[ruby-core:61422] [Bug #9622]'
+ o = Class.new do
+ def foo(*args)
+ bar(:foo, *args)
+ end
+ def bar(name)
+ name
+ end
+ end.new
+ e = assert_raise(ArgumentError) {o.foo(100)}
+ assert_nothing_raised(ArgumentError) {o.foo}
+ assert_raise_with_message(ArgumentError, e.message, bug9622) {o.foo(100)}
+ end
+end
diff --git a/jni/ruby/test/ruby/test_case.rb b/jni/ruby/test/ruby/test_case.rb
new file mode 100644
index 0000000..f20d1df
--- /dev/null
+++ b/jni/ruby/test/ruby/test_case.rb
@@ -0,0 +1,124 @@
+require 'test/unit'
+
+class TestCase < Test::Unit::TestCase
+ def test_case
+ case 5
+ when 1, 2, 3, 4, 6, 7, 8
+ assert(false)
+ when 5
+ assert(true)
+ end
+
+ case 5
+ when 5
+ assert(true)
+ when 1..10
+ assert(false)
+ end
+
+ case 5
+ when 1..10
+ assert(true)
+ else
+ assert(false)
+ end
+
+ case 5
+ when 5
+ assert(true)
+ else
+ assert(false)
+ end
+
+ case "foobar"
+ when /^f.*r$/
+ assert(true)
+ else
+ assert(false)
+ end
+
+ case
+ when true
+ assert(true)
+ when false, nil
+ assert(false)
+ else
+ assert(false)
+ end
+
+ case "+"
+ when *%w/. +/
+ assert(true)
+ else
+ assert(false)
+ end
+
+ case
+ when *[], false
+ assert(false)
+ else
+ assert(true)
+ end
+
+ case
+ when *false, []
+ assert(true)
+ else
+ assert(false)
+ end
+
+ assert_raise(NameError) do
+ case
+ when false, *x, false
+ end
+ end
+ end
+
+ def test_deoptimization
+ assert_in_out_err(['-e', <<-EOS], '', %w[42], [])
+ class Symbol; undef ===; def ===(o); p 42; true; end; end; case :foo; when :foo; end
+ EOS
+
+ assert_in_out_err(['-e', <<-EOS], '', %w[42], [])
+ class Fixnum; undef ===; def ===(o); p 42; true; end; end; case 1; when 1; end
+ EOS
+ end
+
+ def test_optimization
+ case 1
+ when 0.9, 1.1
+ assert(false)
+ when 1.0
+ assert(true)
+ else
+ assert(false)
+ end
+ case 536870912
+ when 536870911.9, 536870912.1
+ assert(false)
+ when 536870912.0
+ assert(true)
+ else
+ assert(false)
+ end
+ end
+
+ def test_method_missing
+ flag = false
+
+ case 1
+ when Class.new(BasicObject) { def method_missing(*) true end }.new
+ flag = true
+ end
+
+ assert(flag)
+ end
+
+ def test_nomethoderror
+ assert_raise(NoMethodError) {
+ case 1
+ when Class.new(BasicObject) { }.new
+ end
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_class.rb b/jni/ruby/test/ruby/test_class.rb
new file mode 100644
index 0000000..e17f56f
--- /dev/null
+++ b/jni/ruby/test/ruby/test_class.rb
@@ -0,0 +1,402 @@
+require 'test/unit'
+
+class TestClass < Test::Unit::TestCase
+ # ------------------
+ # Various test classes
+ # ------------------
+
+ class ClassOne
+ attr :num_args
+ @@subs = []
+ def initialize(*args)
+ @num_args = args.size
+ @args = args
+ end
+ def [](n)
+ @args[n]
+ end
+ def ClassOne.inherited(klass)
+ @@subs.push klass
+ end
+ def subs
+ @@subs
+ end
+ end
+
+ class ClassTwo < ClassOne
+ end
+
+ class ClassThree < ClassOne
+ end
+
+ class ClassFour < ClassThree
+ end
+
+ # ------------------
+ # Start of tests
+ # ------------------
+
+ def test_s_inherited
+ assert_equal([ClassTwo, ClassThree, ClassFour], ClassOne.new.subs)
+ end
+
+ def test_s_new
+ c = Class.new
+ assert_same(Class, c.class)
+ assert_same(Object, c.superclass)
+
+ c = Class.new(Fixnum)
+ assert_same(Class, c.class)
+ assert_same(Fixnum, c.superclass)
+ end
+
+ def test_00_new_basic
+ a = ClassOne.new
+ assert_equal(ClassOne, a.class)
+ assert_equal(0, a.num_args)
+
+ a = ClassOne.new(1, 2, 3)
+ assert_equal(3, a.num_args)
+ assert_equal(1, a[0])
+ end
+
+ def test_01_new_inherited
+ a = ClassTwo.new
+ assert_equal(ClassTwo, a.class)
+ assert_equal(0, a.num_args)
+
+ a = ClassTwo.new(1, 2, 3)
+ assert_equal(3, a.num_args)
+ assert_equal(1, a[0])
+ end
+
+ def test_superclass
+ assert_equal(ClassOne, ClassTwo.superclass)
+ assert_equal(Object, ClassTwo.superclass.superclass)
+ assert_equal(BasicObject, ClassTwo.superclass.superclass.superclass)
+ end
+
+ def test_class_cmp
+ assert_raise(TypeError) { Class.new <= 1 }
+ assert_raise(TypeError) { Class.new >= 1 }
+ assert_nil(Class.new <=> 1)
+ end
+
+ def test_class_initialize
+ assert_raise(TypeError) do
+ Class.new.instance_eval { initialize }
+ end
+ end
+
+ def test_instanciate_singleton_class
+ c = class << Object.new; self; end
+ assert_raise(TypeError) { c.new }
+ end
+
+ def test_superclass_of_basicobject
+ assert_equal(nil, BasicObject.superclass)
+ end
+
+ def test_module_function
+ c = Class.new
+ assert_raise(TypeError) do
+ Module.instance_method(:module_function).bind(c).call(:foo)
+ end
+ end
+
+ def test_extend_object
+ c = Class.new
+ assert_raise(TypeError) do
+ Module.instance_method(:extend_object).bind(c).call(Object.new)
+ end
+ end
+
+ def test_append_features
+ c = Class.new
+ assert_raise(TypeError) do
+ Module.instance_method(:append_features).bind(c).call(Module.new)
+ end
+ end
+
+ def test_prepend_features
+ c = Class.new
+ assert_raise(TypeError) do
+ Module.instance_method(:prepend_features).bind(c).call(Module.new)
+ end
+ end
+
+ def test_module_specific_methods
+ assert_empty(Class.private_instance_methods(true) &
+ [:module_function, :extend_object, :append_features, :prepend_features])
+ end
+
+ def test_method_redefinition
+ feature2155 = '[ruby-dev:39400]'
+
+ line = __LINE__+4
+ stderr = EnvUtil.verbose_warning do
+ Class.new do
+ def foo; end
+ def foo; end
+ end
+ end
+ assert_match(/:#{line}: warning: method redefined; discarding old foo/, stderr)
+ assert_match(/:#{line-1}: warning: previous definition of foo/, stderr, feature2155)
+
+ assert_warning '' do
+ Class.new do
+ def foo; end
+ alias bar foo
+ def foo; end
+ end
+ end
+
+ assert_warning '' do
+ Class.new do
+ def foo; end
+ alias bar foo
+ alias bar foo
+ end
+ end
+
+ line = __LINE__+4
+ stderr = EnvUtil.verbose_warning do
+ Class.new do
+ define_method(:foo) do end
+ def foo; end
+ end
+ end
+ assert_match(/:#{line}: warning: method redefined; discarding old foo/, stderr)
+ assert_match(/:#{line-1}: warning: previous definition of foo/, stderr, feature2155)
+
+ assert_warning '' do
+ Class.new do
+ define_method(:foo) do end
+ alias bar foo
+ alias bar foo
+ end
+ end
+
+ assert_warning '' do
+ Class.new do
+ def foo; end
+ undef foo
+ end
+ end
+ end
+
+ def test_check_inheritable
+ assert_raise(TypeError) { Class.new(Object.new) }
+
+ o = Object.new
+ c = class << o; self; end
+ assert_raise(TypeError) { Class.new(c) }
+ assert_raise(TypeError) { Class.new(Class) }
+ assert_raise(TypeError) { eval("class Foo < Class; end") }
+ end
+
+ def test_initialize_copy
+ c = Class.new
+ assert_raise(TypeError) { c.instance_eval { initialize_copy(1) } }
+
+ o = Object.new
+ c = class << o; self; end
+ assert_raise(TypeError) { c.dup }
+
+ assert_raise(TypeError) { BasicObject.dup }
+ end
+
+ def test_singleton_class
+ assert_raise(TypeError) { 1.extend(Module.new) }
+ assert_raise(TypeError) { 1.0.extend(Module.new) }
+ assert_raise(TypeError) { (2.0**1000).extend(Module.new) }
+ assert_raise(TypeError) { :foo.extend(Module.new) }
+
+ assert_in_out_err([], <<-INPUT, %w(:foo :foo true true), [])
+ module Foo; def foo; :foo; end; end
+ false.extend(Foo)
+ true.extend(Foo)
+ p false.foo
+ p true.foo
+ p FalseClass.include?(Foo)
+ p TrueClass.include?(Foo)
+ INPUT
+ end
+
+ def test_uninitialized
+ assert_raise(TypeError) { Class.allocate.new }
+ assert_raise(TypeError) { Class.allocate.superclass }
+ bug6863 = '[ruby-core:47148]'
+ assert_raise(TypeError, bug6863) { Class.new(Class.allocate) }
+ end
+
+ def test_nonascii_name
+ c = eval("class ::C\u{df}; self; end")
+ assert_equal("C\u{df}", c.name, '[ruby-core:24600]')
+ c = eval("class C\u{df}; self; end")
+ assert_equal("TestClass::C\u{df}", c.name, '[ruby-core:24600]')
+ end
+
+ def test_invalid_jump_from_class_definition
+ assert_raise(SyntaxError) { eval("class C; next; end") }
+ assert_raise(SyntaxError) { eval("class C; break; end") }
+ assert_raise(SyntaxError) { eval("class C; redo; end") }
+ assert_raise(SyntaxError) { eval("class C; retry; end") }
+ assert_raise(SyntaxError) { eval("class C; return; end") }
+ assert_raise(SyntaxError) { eval("class C; yield; end") }
+ end
+
+ def test_clone
+ original = Class.new {
+ def foo
+ return super()
+ end
+ }
+ mod = Module.new {
+ def foo
+ return "mod#foo"
+ end
+ }
+ copy = original.clone
+ copy.send(:include, mod)
+ assert_equal("mod#foo", copy.new.foo)
+ end
+
+ def test_nested_class_removal
+ assert_normal_exit('File.__send__(:remove_const, :Stat); at_exit{File.stat(".")}; GC.start')
+ end
+
+ class PrivateClass
+ end
+ private_constant :PrivateClass
+
+ def test_redefine_private_class
+ assert_raise(NameError) do
+ eval("class ::TestClass::PrivateClass; end")
+ end
+ eval <<-END
+ class ::TestClass
+ class PrivateClass
+ def foo; 42; end
+ end
+ end
+ END
+ assert_equal(42, PrivateClass.new.foo)
+ end
+
+ StrClone = String.clone
+ Class.new(StrClone)
+
+ def test_cloned_class
+ bug5274 = StrClone.new("[ruby-dev:44460]")
+ assert_equal(bug5274, Marshal.load(Marshal.dump(bug5274)))
+ end
+
+ def test_cannot_reinitialize_class_with_initialize_copy # [ruby-core:50869]
+ assert_in_out_err([], <<-'end;', ["Object"], [])
+ class Class
+ def initialize_copy(*); super; end
+ end
+
+ class A; end
+ class B; end
+
+ A.send(:initialize_copy, Class.new(B)) rescue nil
+
+ p A.superclass
+ end;
+ end
+
+ module M
+ C = 1
+
+ def self.m
+ C
+ end
+ end
+
+ def test_constant_access_from_method_in_cloned_module # [ruby-core:47834]
+ m = M.dup
+ assert_equal 1, m::C
+ assert_equal 1, m.m
+ end
+
+ def test_invalid_superclass
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < nil
+ end
+ end;
+ end
+
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < false
+ end
+ end;
+ end
+
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < true
+ end
+ end;
+ end
+
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < 0
+ end
+ end;
+ end
+
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < ""
+ end
+ end;
+ end
+ end
+
+ def test_cloned_singleton_method_added
+ bug5283 = '[ruby-dev:44477]'
+ added = []
+ c = Class.new
+ c.singleton_class.class_eval do
+ define_method(:singleton_method_added) {|mid| added << [self, mid]}
+ def foo; :foo; end
+ end
+ added.clear
+ d = c.clone
+ assert_empty(added.grep(->(k) {c == k[0]}), bug5283)
+ assert_equal(:foo, d.foo)
+ end
+
+ def test_singleton_class_p
+ feature7609 = '[ruby-core:51087] [Feature #7609]'
+ assert_predicate(self.singleton_class, :singleton_class?, feature7609)
+ assert_not_predicate(self.class, :singleton_class?, feature7609)
+ end
+
+ def test_freeze_to_s
+ assert_nothing_raised("[ruby-core:41858] [Bug #5828]") {
+ Class.new.freeze.clone.to_s
+ }
+ end
+
+ def test_singleton_class_of_frozen_object
+ obj = Object.new
+ c = obj.singleton_class
+ obj.freeze
+ assert_raise_with_message(RuntimeError, /frozen object/) {
+ c.class_eval {def f; end}
+ }
+ end
+
+ def test_singleton_class_message
+ c = Class.new.freeze
+ assert_raise_with_message(RuntimeError, /frozen Class/) {
+ def c.f; end
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_clone.rb b/jni/ruby/test/ruby/test_clone.rb
new file mode 100644
index 0000000..c5e2469
--- /dev/null
+++ b/jni/ruby/test/ruby/test_clone.rb
@@ -0,0 +1,28 @@
+require 'test/unit'
+
+class TestClone < Test::Unit::TestCase
+ module M001; end
+ module M002; end
+ module M003; include M002; end
+ module M002; include M001; end
+ module M003; include M002; end
+
+ def test_clone
+ foo = Object.new
+ def foo.test
+ "test"
+ end
+ bar = foo.clone
+ def bar.test2
+ "test2"
+ end
+
+ assert_equal("test2", bar.test2)
+ assert_equal("test", bar.test)
+ assert_equal("test", foo.test)
+
+ assert_raise(NoMethodError) {foo.test2}
+
+ assert_equal([M003, M002, M001], M003.ancestors)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_comparable.rb b/jni/ruby/test/ruby/test_comparable.rb
new file mode 100644
index 0000000..efa630f
--- /dev/null
+++ b/jni/ruby/test/ruby/test_comparable.rb
@@ -0,0 +1,94 @@
+require 'test/unit'
+
+class TestComparable < Test::Unit::TestCase
+ def setup
+ @o = Object.new
+ @o.extend(Comparable)
+ end
+ def cmp(b)
+ class << @o; self; end.class_eval {
+ undef :<=>
+ define_method(:<=>, b)
+ }
+ end
+
+ def test_equal
+ cmp->(x) do 0; end
+ assert_equal(true, @o == nil)
+ cmp->(x) do 1; end
+ assert_equal(false, @o == nil)
+ cmp->(x) do raise NotImplementedError, "Not a RuntimeError" end
+ assert_raise(NotImplementedError) { @o == nil }
+ bug7688 = '[ruby-core:51389] [Bug #7688]'
+ cmp->(x) do raise StandardError, "A standard error should be rescued"; end
+ warn = /Comparable#== will no more rescue exceptions .+ in the next release/
+ assert_warn(warn, bug7688) { @o == nil }
+ end
+
+ def test_gt
+ cmp->(x) do 1; end
+ assert_equal(true, @o > nil)
+ cmp->(x) do 0; end
+ assert_equal(false, @o > nil)
+ cmp->(x) do -1; end
+ assert_equal(false, @o > nil)
+ end
+
+ def test_ge
+ cmp->(x) do 1; end
+ assert_equal(true, @o >= nil)
+ cmp->(x) do 0; end
+ assert_equal(true, @o >= nil)
+ cmp->(x) do -1; end
+ assert_equal(false, @o >= nil)
+ end
+
+ def test_lt
+ cmp->(x) do 1; end
+ assert_equal(false, @o < nil)
+ cmp->(x) do 0; end
+ assert_equal(false, @o < nil)
+ cmp->(x) do -1; end
+ assert_equal(true, @o < nil)
+ end
+
+ def test_le
+ cmp->(x) do 1; end
+ assert_equal(false, @o <= nil)
+ cmp->(x) do 0; end
+ assert_equal(true, @o <= nil)
+ cmp->(x) do -1; end
+ assert_equal(true, @o <= nil)
+ end
+
+ def test_between
+ cmp->(x) do 0 <=> x end
+ assert_equal(false, @o.between?(1, 2))
+ assert_equal(false, @o.between?(-2, -1))
+ assert_equal(true, @o.between?(-1, 1))
+ assert_equal(true, @o.between?(0, 0))
+ end
+
+ def test_err
+ assert_raise(ArgumentError) { 1.0 < nil }
+ assert_raise(ArgumentError) { 1.0 < Object.new }
+ e = EnvUtil.labeled_class("E\u{30a8 30e9 30fc}")
+ assert_raise_with_message(ArgumentError, /E\u{30a8 30e9 30fc}/) {
+ 1.0 < e.new
+ }
+ end
+
+ def test_inversed_compare
+ bug7870 = '[ruby-core:52305] [Bug #7870]'
+ assert_nothing_raised(SystemStackError, bug7870) {
+ assert_nil(Time.new <=> "")
+ }
+ end
+
+ def test_no_cmp
+ bug9003 = '[ruby-core:57736] [Bug #9003]'
+ assert_nothing_raised(SystemStackError, bug9003) {
+ @o <=> @o.dup
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_complex.rb b/jni/ruby/test/ruby/test_complex.rb
new file mode 100644
index 0000000..1cbab2d
--- /dev/null
+++ b/jni/ruby/test/ruby/test_complex.rb
@@ -0,0 +1,1014 @@
+require 'test/unit'
+require 'cmath'
+
+class ComplexSub < Complex; end
+
+class Complex_Test < Test::Unit::TestCase
+
+ def test_rationalize
+ assert_equal(1.quo(3), Complex(1/3.0, 0).rationalize, '[ruby-core:38885]')
+ assert_equal(1.quo(5), Complex(0.2, 0).rationalize, '[ruby-core:38885]')
+ assert_equal(5.quo(2), Complex(2.5, 0).rationalize(0), '[ruby-core:40667]')
+ end
+
+ def test_compsub
+ c = ComplexSub.__send__(:convert, 1)
+
+ assert_kind_of(Numeric, c)
+
+ assert_instance_of(ComplexSub, c)
+
+ c2 = c + 1
+ assert_instance_of(ComplexSub, c2)
+ c2 = c - 1
+ assert_instance_of(ComplexSub, c2)
+
+ c3 = c - c2
+ assert_instance_of(ComplexSub, c3)
+
+ s = Marshal.dump(c)
+ c5 = Marshal.load(s)
+ assert_equal(c, c5)
+ assert_instance_of(ComplexSub, c5)
+
+ c1 = Complex(1)
+ assert_equal(c1.hash, c.hash, '[ruby-dev:38850]')
+ assert_equal([true, true], [c.eql?(c1), c1.eql?(c)])
+ end
+
+ def test_eql_p
+ c = Complex(0)
+ c2 = Complex(0)
+ c3 = Complex(1)
+
+ assert_equal(true, c.eql?(c2))
+ assert_equal(false, c.eql?(c3))
+
+ assert_equal(false, c.eql?(0))
+ end
+
+ def test_hash
+ assert_instance_of(Fixnum, Complex(1,2).hash)
+ assert_instance_of(Fixnum, Complex(1.0,2.0).hash)
+
+ h = {}
+ h[Complex(0)] = 0
+ h[Complex(0,1)] = 1
+ h[Complex(1,0)] = 2
+ h[Complex(1,1)] = 3
+
+ assert_equal(4, h.size)
+ assert_equal(2, h[Complex(1,0)])
+
+ h[Complex(0,0)] = 9
+ assert_equal(4, h.size)
+
+ h[Complex(0.0,0.0)] = 9.0
+ assert_equal(5, h.size)
+
+ if (0.0/0).nan? && !((0.0/0).eql?(0.0/0))
+ h = {}
+ 3.times{h[Complex(0.0/0)] = 1}
+ assert_equal(3, h.size)
+ end
+ end
+
+ def test_freeze
+ c = Complex(1)
+ c.freeze
+ assert_equal(true, c.frozen?)
+ assert_instance_of(String, c.to_s)
+ end
+
+ def test_conv
+ c = Complex(0,0)
+ assert_equal(Complex(0,0), c)
+
+ c = Complex(2**32, 2**32)
+ assert_equal(Complex(2**32,2**32), c)
+ assert_equal([2**32,2**32], [c.real,c.imag])
+
+ c = Complex(-2**32, 2**32)
+ assert_equal(Complex(-2**32,2**32), c)
+ assert_equal([-2**32,2**32], [c.real,c.imag])
+
+ c = Complex(2**32, -2**32)
+ assert_equal(Complex(2**32,-2**32), c)
+ assert_equal([2**32,-2**32], [c.real,c.imag])
+
+ c = Complex(-2**32, -2**32)
+ assert_equal(Complex(-2**32,-2**32), c)
+ assert_equal([-2**32,-2**32], [c.real,c.imag])
+
+ c = Complex(Complex(1,2),2)
+ assert_equal(Complex(1,4), c)
+
+ c = Complex(2,Complex(1,2))
+ assert_equal(Complex(0,1), c)
+
+ c = Complex(Complex(1,2),Complex(1,2))
+ assert_equal(Complex(-1,3), c)
+
+ c = Complex::I
+ assert_equal(Complex(0,1), c)
+
+ assert_equal(Complex(1),Complex(1))
+ assert_equal(Complex(1),Complex('1'))
+ assert_equal(Complex(3.0,3.0),Complex('3.0','3.0'))
+ assert_equal(Complex(1,1),Complex('3/3','3/3'))
+ assert_raise(TypeError){Complex(nil)}
+ assert_raise(TypeError){Complex(Object.new)}
+ assert_raise(ArgumentError){Complex()}
+ assert_raise(ArgumentError){Complex(1,2,3)}
+
+ if (0.0/0).nan?
+ assert_nothing_raised{Complex(0.0/0)}
+ end
+ if (1.0/0).infinite?
+ assert_nothing_raised{Complex(1.0/0)}
+ end
+ end
+
+ def test_attr
+ c = Complex(4)
+
+ assert_equal(4, c.real)
+ assert_equal(0, c.imag)
+
+ c = Complex(4,5)
+
+ assert_equal(4, c.real)
+ assert_equal(5, c.imag)
+
+ if -0.0.to_s == '-0.0'
+ c = Complex(-0.0,-0.0)
+
+ assert_equal('-0.0', c.real.to_s)
+ assert_equal('-0.0', c.imag.to_s)
+ end
+
+ c = Complex(4)
+
+ assert_equal(4, c.real)
+ assert_equal(0, c.imag)
+ assert_equal(c.imag, c.imaginary)
+
+ c = Complex(4,5)
+
+ assert_equal(4, c.real)
+ assert_equal(5, c.imag)
+ assert_equal(c.imag, c.imaginary)
+
+ if -0.0.to_s == '-0.0'
+ c = Complex(-0.0,-0.0)
+
+ assert_equal('-0.0', c.real.to_s)
+ assert_equal('-0.0', c.imag.to_s)
+ assert_equal(c.imag.to_s, c.imaginary.to_s)
+ end
+
+ c = Complex(4)
+
+ assert_equal(4, c.real)
+ assert_equal(c.imag, c.imaginary)
+ assert_equal(0, c.imag)
+
+ c = Complex(4,5)
+
+ assert_equal(4, c.real)
+ assert_equal(5, c.imag)
+ assert_equal(c.imag, c.imaginary)
+
+ c = Complex(-0.0,-0.0)
+
+ assert_equal('-0.0', c.real.to_s)
+ assert_equal('-0.0', c.imag.to_s)
+ assert_equal(c.imag.to_s, c.imaginary.to_s)
+ end
+
+ def test_attr2
+ c = Complex(1)
+
+ assert_equal(false, c.integer?)
+ assert_equal(false, c.real?)
+
+ assert_equal(true, Complex(0).zero?)
+ assert_equal(true, Complex(0,0).zero?)
+ assert_equal(false, Complex(1,0).zero?)
+ assert_equal(false, Complex(0,1).zero?)
+ assert_equal(false, Complex(1,1).zero?)
+
+ assert_equal(nil, Complex(0).nonzero?)
+ assert_equal(nil, Complex(0,0).nonzero?)
+ assert_equal(Complex(1,0), Complex(1,0).nonzero?)
+ assert_equal(Complex(0,1), Complex(0,1).nonzero?)
+ assert_equal(Complex(1,1), Complex(1,1).nonzero?)
+ end
+
+ def test_rect
+ assert_equal([1,2], Complex.rectangular(1,2).rectangular)
+ assert_equal([1,2], Complex.rect(1,2).rect)
+ end
+
+ def test_polar
+ assert_equal([1,2], Complex.polar(1,2).polar)
+ end
+
+ def test_uplus
+ assert_equal(Complex(1), +Complex(1))
+ assert_equal(Complex(-1), +Complex(-1))
+ assert_equal(Complex(1,1), +Complex(1,1))
+ assert_equal(Complex(-1,1), +Complex(-1,1))
+ assert_equal(Complex(1,-1), +Complex(1,-1))
+ assert_equal(Complex(-1,-1), +Complex(-1,-1))
+
+ if -0.0.to_s == '-0.0'
+ c = +Complex(0.0,0.0)
+ assert_equal('0.0', c.real.to_s)
+ assert_equal('0.0', c.imag.to_s)
+
+ c = +Complex(-0.0,-0.0)
+ assert_equal('-0.0', c.real.to_s)
+ assert_equal('-0.0', c.imag.to_s)
+ end
+ end
+
+ def test_negate
+ assert_equal(Complex(-1), -Complex(1))
+ assert_equal(Complex(1), -Complex(-1))
+ assert_equal(Complex(-1,-1), -Complex(1,1))
+ assert_equal(Complex(1,-1), -Complex(-1,1))
+ assert_equal(Complex(-1,1), -Complex(1,-1))
+ assert_equal(Complex(1,1), -Complex(-1,-1))
+
+ if -0.0.to_s == '-0.0'
+ c = -Complex(0.0,0.0)
+ assert_equal('-0.0', c.real.to_s)
+ assert_equal('-0.0', c.imag.to_s)
+
+ c = -Complex(-0.0,-0.0)
+ assert_equal('0.0', c.real.to_s)
+ assert_equal('0.0', c.imag.to_s)
+ end
+ end
+
+ def test_add
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ assert_equal(Complex(3,5), c + c2)
+
+ assert_equal(Complex(3,2), c + 2)
+ assert_equal(Complex(3.0,2), c + 2.0)
+
+ assert_equal(Complex(Rational(3,1),Rational(2)), c + Rational(2))
+ assert_equal(Complex(Rational(5,3),Rational(2)), c + Rational(2,3))
+ end
+
+ def test_sub
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ assert_equal(Complex(-1,-1), c - c2)
+
+ assert_equal(Complex(-1,2), c - 2)
+ assert_equal(Complex(-1.0,2), c - 2.0)
+
+ assert_equal(Complex(Rational(-1,1),Rational(2)), c - Rational(2))
+ assert_equal(Complex(Rational(1,3),Rational(2)), c - Rational(2,3))
+ end
+
+ def test_mul
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ assert_equal(Complex(-4,7), c * c2)
+
+ assert_equal(Complex(2,4), c * 2)
+ assert_equal(Complex(2.0,4.0), c * 2.0)
+
+ assert_equal(Complex(Rational(2,1),Rational(4)), c * Rational(2))
+ assert_equal(Complex(Rational(2,3),Rational(4,3)), c * Rational(2,3))
+ end
+
+ def test_div
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ assert_equal(Complex(Rational(8,13),Rational(1,13)), c / c2)
+
+ c = Complex(1.0,2.0)
+ c2 = Complex(2.0,3.0)
+
+ r = c / c2
+ assert_in_delta(0.615, r.real, 0.001)
+ assert_in_delta(0.076, r.imag, 0.001)
+
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ assert_equal(Complex(Rational(1,2),1), c / 2)
+ assert_equal(Complex(0.5,1.0), c / 2.0)
+
+ assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2))
+ assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3))
+ end
+
+ def test_quo
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ assert_equal(Complex(Rational(8,13),Rational(1,13)), c.quo(c2))
+
+ c = Complex(1.0,2.0)
+ c2 = Complex(2.0,3.0)
+
+ r = c.quo(c2)
+ assert_in_delta(0.615, r.real, 0.001)
+ assert_in_delta(0.076, r.imag, 0.001)
+
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ assert_equal(Complex(Rational(1,2),1), c.quo(2))
+ assert_equal(Complex(0.5,1.0), c.quo(2.0))
+
+ assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2))
+ assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3))
+ end
+
+ def test_fdiv
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ r = c.fdiv(c2)
+ assert_in_delta(0.615, r.real, 0.001)
+ assert_in_delta(0.076, r.imag, 0.001)
+
+ c = Complex(1.0,2.0)
+ c2 = Complex(2.0,3.0)
+
+ r = c.fdiv(c2)
+ assert_in_delta(0.615, r.real, 0.001)
+ assert_in_delta(0.076, r.imag, 0.001)
+
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ assert_equal(Complex(0.5,1.0), c.fdiv(2))
+ assert_equal(Complex(0.5,1.0), c.fdiv(2.0))
+ end
+
+ def test_expt
+ c = Complex(1,2)
+ c2 = Complex(2,3)
+
+ r = c ** c2
+ assert_in_delta(-0.015, r.real, 0.001)
+ assert_in_delta(-0.179, r.imag, 0.001)
+
+ assert_equal(Complex(-3,4), c ** 2)
+ assert_equal(Complex(Rational(-3,25),Rational(-4,25)), c ** -2)
+
+ r = c ** 2.0
+ assert_in_delta(-3.0, r.real, 0.001)
+ assert_in_delta(4.0, r.imag, 0.001)
+
+ r = c ** -2.0
+ assert_in_delta(-0.12, r.real, 0.001)
+ assert_in_delta(-0.16, r.imag, 0.001)
+
+ assert_equal(Complex(-3,4), c ** Rational(2))
+ assert_equal(Complex(Rational(-3,25),Rational(-4,25)),
+ c ** Rational(-2)) # why failed?
+
+ r = c ** Rational(2,3)
+ assert_in_delta(1.264, r.real, 0.001)
+ assert_in_delta(1.150, r.imag, 0.001)
+
+ r = c ** Rational(-2,3)
+ assert_in_delta(0.432, r.real, 0.001)
+ assert_in_delta(-0.393, r.imag, 0.001)
+ end
+
+ def test_cmp
+ assert_raise(NoMethodError){1 <=> Complex(1,1)}
+ assert_raise(NoMethodError){Complex(1,1) <=> 1}
+ assert_raise(NoMethodError){Complex(1,1) <=> Complex(1,1)}
+ end
+
+ def test_eqeq
+ assert_equal(Complex(1), Complex(1,0))
+ assert_equal(Complex(-1), Complex(-1,0))
+
+ assert_not_equal(Complex(1), Complex(2,1))
+ assert_operator(Complex(2,1), :!=, Complex(1))
+ assert_not_equal(nil, Complex(1))
+ assert_not_equal('', Complex(1))
+
+ nan = 0.0 / 0
+ if nan.nan? && nan != nan
+ assert_not_equal(Complex(nan, 0), Complex(nan, 0))
+ assert_not_equal(Complex(0, nan), Complex(0, nan))
+ assert_not_equal(Complex(nan, nan), Complex(nan, nan))
+ end
+ end
+
+ def test_coerce
+ assert_equal([Complex(2),Complex(1)], Complex(1).coerce(2))
+ assert_equal([Complex(2.2),Complex(1)], Complex(1).coerce(2.2))
+ assert_equal([Complex(Rational(2)),Complex(1)],
+ Complex(1).coerce(Rational(2)))
+ assert_equal([Complex(2),Complex(1)], Complex(1).coerce(Complex(2)))
+ end
+
+ class ObjectX
+ def + (x) Rational(1) end
+ alias - +
+ alias * +
+ alias / +
+ alias quo +
+ alias ** +
+ def coerce(x) [x, Complex(1)] end
+ end
+
+ def test_coerce2
+ x = ObjectX.new
+ %w(+ - * / quo **).each do |op|
+ assert_kind_of(Numeric, Complex(1).__send__(op, x))
+ end
+ end
+
+ def test_math
+ c = Complex(1,2)
+
+ assert_in_delta(2.236, c.abs, 0.001)
+ assert_in_delta(2.236, c.magnitude, 0.001)
+ assert_equal(5, c.abs2)
+
+ assert_equal(c.abs, Math.sqrt(c * c.conj))
+ assert_equal(c.abs, Math.sqrt(c.real**2 + c.imag**2))
+ assert_equal(c.abs2, c * c.conj)
+ assert_equal(c.abs2, c.real**2 + c.imag**2)
+
+ assert_in_delta(1.107, c.arg, 0.001)
+ assert_in_delta(1.107, c.angle, 0.001)
+ assert_in_delta(1.107, c.phase, 0.001)
+
+ r = c.polar
+ assert_in_delta(2.236, r[0], 0.001)
+ assert_in_delta(1.107, r[1], 0.001)
+ assert_equal(Complex(1,-2), c.conjugate)
+ assert_equal(Complex(1,-2), c.conj)
+
+ assert_equal(Complex(1,2), c.numerator)
+ assert_equal(1, c.denominator)
+ end
+
+ def test_to_s
+ c = Complex(1,2)
+
+ assert_instance_of(String, c.to_s)
+ assert_equal('1+2i', c.to_s)
+
+ assert_equal('0+2i', Complex(0,2).to_s)
+ assert_equal('0-2i', Complex(0,-2).to_s)
+ assert_equal('1+2i', Complex(1,2).to_s)
+ assert_equal('-1+2i', Complex(-1,2).to_s)
+ assert_equal('-1-2i', Complex(-1,-2).to_s)
+ assert_equal('1-2i', Complex(1,-2).to_s)
+ assert_equal('-1-2i', Complex(-1,-2).to_s)
+
+ assert_equal('0+2.0i', Complex(0,2.0).to_s)
+ assert_equal('0-2.0i', Complex(0,-2.0).to_s)
+ assert_equal('1.0+2.0i', Complex(1.0,2.0).to_s)
+ assert_equal('-1.0+2.0i', Complex(-1.0,2.0).to_s)
+ assert_equal('-1.0-2.0i', Complex(-1.0,-2.0).to_s)
+ assert_equal('1.0-2.0i', Complex(1.0,-2.0).to_s)
+ assert_equal('-1.0-2.0i', Complex(-1.0,-2.0).to_s)
+
+ assert_equal('0+2/1i', Complex(0,Rational(2)).to_s)
+ assert_equal('0-2/1i', Complex(0,Rational(-2)).to_s)
+ assert_equal('1+2/1i', Complex(1,Rational(2)).to_s)
+ assert_equal('-1+2/1i', Complex(-1,Rational(2)).to_s)
+ assert_equal('-1-2/1i', Complex(-1,Rational(-2)).to_s)
+ assert_equal('1-2/1i', Complex(1,Rational(-2)).to_s)
+ assert_equal('-1-2/1i', Complex(-1,Rational(-2)).to_s)
+
+ assert_equal('0+2/3i', Complex(0,Rational(2,3)).to_s)
+ assert_equal('0-2/3i', Complex(0,Rational(-2,3)).to_s)
+ assert_equal('1+2/3i', Complex(1,Rational(2,3)).to_s)
+ assert_equal('-1+2/3i', Complex(-1,Rational(2,3)).to_s)
+ assert_equal('-1-2/3i', Complex(-1,Rational(-2,3)).to_s)
+ assert_equal('1-2/3i', Complex(1,Rational(-2,3)).to_s)
+ assert_equal('-1-2/3i', Complex(-1,Rational(-2,3)).to_s)
+
+ nan = 0.0 / 0
+ inf = 1.0 / 0
+ if nan.nan?
+ assert_equal('NaN+NaN*i', Complex(nan,nan).to_s)
+ end
+ if inf.infinite?
+ assert_equal('Infinity+Infinity*i', Complex(inf,inf).to_s)
+ assert_equal('Infinity-Infinity*i', Complex(inf,-inf).to_s)
+ end
+ end
+
+ def test_inspect
+ c = Complex(1,2)
+
+ assert_instance_of(String, c.inspect)
+ assert_equal('(1+2i)', c.inspect)
+ end
+
+ def test_marshal
+ c = Complex(1,2)
+ c.instance_eval{@ivar = 9}
+
+ s = Marshal.dump(c)
+ c2 = Marshal.load(s)
+ assert_equal(c, c2)
+ assert_equal(9, c2.instance_variable_get(:@ivar))
+ assert_instance_of(Complex, c2)
+
+ c = Complex(Rational(1,2),Rational(2,3))
+
+ s = Marshal.dump(c)
+ c2 = Marshal.load(s)
+ assert_equal(c, c2)
+ assert_instance_of(Complex, c2)
+
+ bug3656 = '[ruby-core:31622]'
+ c = Complex(1,2)
+ c.freeze
+ assert_predicate(c, :frozen?)
+ result = c.marshal_load([2,3]) rescue :fail
+ assert_equal(:fail, result, bug3656)
+ assert_equal(Complex(1,2), c)
+ end
+
+ def test_marshal_compatibility
+ bug6625 = '[ruby-core:45775]'
+ dump = "\x04\x08o:\x0cComplex\x07:\x0a@reali\x06:\x0b@imagei\x07"
+ assert_nothing_raised(bug6625) do
+ assert_equal(Complex(1, 2), Marshal.load(dump), bug6625)
+ end
+ end
+
+ def test_parse
+ assert_equal(Complex(5), '5'.to_c)
+ assert_equal(Complex(-5), '-5'.to_c)
+ assert_equal(Complex(5,3), '5+3i'.to_c)
+ assert_equal(Complex(-5,3), '-5+3i'.to_c)
+ assert_equal(Complex(5,-3), '5-3i'.to_c)
+ assert_equal(Complex(-5,-3), '-5-3i'.to_c)
+ assert_equal(Complex(0,3), '3i'.to_c)
+ assert_equal(Complex(0,-3), '-3i'.to_c)
+ assert_equal(Complex(5,1), '5+i'.to_c)
+ assert_equal(Complex(0,1), 'i'.to_c)
+ assert_equal(Complex(0,1), '+i'.to_c)
+ assert_equal(Complex(0,-1), '-i'.to_c)
+
+ assert_equal(Complex(5,3), '5+3I'.to_c)
+ assert_equal(Complex(5,3), '5+3j'.to_c)
+ assert_equal(Complex(5,3), '5+3J'.to_c)
+ assert_equal(Complex(0,3), '3I'.to_c)
+ assert_equal(Complex(0,3), '3j'.to_c)
+ assert_equal(Complex(0,3), '3J'.to_c)
+ assert_equal(Complex(0,1), 'I'.to_c)
+ assert_equal(Complex(0,1), 'J'.to_c)
+
+ assert_equal(Complex(5.0), '5.0'.to_c)
+ assert_equal(Complex(-5.0), '-5.0'.to_c)
+ assert_equal(Complex(5.0,3.0), '5.0+3.0i'.to_c)
+ assert_equal(Complex(-5.0,3.0), '-5.0+3.0i'.to_c)
+ assert_equal(Complex(5.0,-3.0), '5.0-3.0i'.to_c)
+ assert_equal(Complex(-5.0,-3.0), '-5.0-3.0i'.to_c)
+ assert_equal(Complex(0.0,3.0), '3.0i'.to_c)
+ assert_equal(Complex(0.0,-3.0), '-3.0i'.to_c)
+
+ assert_equal(Complex(5.1), '5.1'.to_c)
+ assert_equal(Complex(-5.2), '-5.2'.to_c)
+ assert_equal(Complex(5.3,3.4), '5.3+3.4i'.to_c)
+ assert_equal(Complex(-5.5,3.6), '-5.5+3.6i'.to_c)
+ assert_equal(Complex(5.3,-3.4), '5.3-3.4i'.to_c)
+ assert_equal(Complex(-5.5,-3.6), '-5.5-3.6i'.to_c)
+ assert_equal(Complex(0.0,3.1), '3.1i'.to_c)
+ assert_equal(Complex(0.0,-3.2), '-3.2i'.to_c)
+
+ assert_equal(Complex(5.0), '5e0'.to_c)
+ assert_equal(Complex(-5.0), '-5e0'.to_c)
+ assert_equal(Complex(5.0,3.0), '5e0+3e0i'.to_c)
+ assert_equal(Complex(-5.0,3.0), '-5e0+3e0i'.to_c)
+ assert_equal(Complex(5.0,-3.0), '5e0-3e0i'.to_c)
+ assert_equal(Complex(-5.0,-3.0), '-5e0-3e0i'.to_c)
+ assert_equal(Complex(0.0,3.0), '3e0i'.to_c)
+ assert_equal(Complex(0.0,-3.0), '-3e0i'.to_c)
+
+ assert_equal(Complex(5e1), '5e1'.to_c)
+ assert_equal(Complex(-5e2), '-5e2'.to_c)
+ assert_equal(Complex(5e3,3e4), '5e003+3e4i'.to_c)
+ assert_equal(Complex(-5e5,3e6), '-5e5+3e006i'.to_c)
+ assert_equal(Complex(5e3,-3e4), '5e003-3e4i'.to_c)
+ assert_equal(Complex(-5e5,-3e6), '-5e5-3e006i'.to_c)
+ assert_equal(Complex(0.0,3e1), '3e1i'.to_c)
+ assert_equal(Complex(0.0,-3e2), '-3e2i'.to_c)
+
+ assert_equal(Complex(0.33), '.33'.to_c)
+ assert_equal(Complex(0.33), '0.33'.to_c)
+ assert_equal(Complex(-0.33), '-.33'.to_c)
+ assert_equal(Complex(-0.33), '-0.33'.to_c)
+ assert_equal(Complex(-0.33), '-0.3_3'.to_c)
+
+ assert_equal(Complex.polar(10,10), '10@10'.to_c)
+ assert_equal(Complex.polar(-10,-10), '-10@-10'.to_c)
+ assert_equal(Complex.polar(10.5,10.5), '10.5@10.5'.to_c)
+ assert_equal(Complex.polar(-10.5,-10.5), '-10.5@-10.5'.to_c)
+
+ assert_equal(Complex(5), Complex('5'))
+ assert_equal(Complex(-5), Complex('-5'))
+ assert_equal(Complex(5,3), Complex('5+3i'))
+ assert_equal(Complex(-5,3), Complex('-5+3i'))
+ assert_equal(Complex(5,-3), Complex('5-3i'))
+ assert_equal(Complex(-5,-3), Complex('-5-3i'))
+ assert_equal(Complex(0,3), Complex('3i'))
+ assert_equal(Complex(0,-3), Complex('-3i'))
+ assert_equal(Complex(5,1), Complex('5+i'))
+ assert_equal(Complex(0,1), Complex('i'))
+ assert_equal(Complex(0,1), Complex('+i'))
+ assert_equal(Complex(0,-1), Complex('-i'))
+
+ assert_equal(Complex(5,3), Complex('5+3I'))
+ assert_equal(Complex(5,3), Complex('5+3j'))
+ assert_equal(Complex(5,3), Complex('5+3J'))
+ assert_equal(Complex(0,3), Complex('3I'))
+ assert_equal(Complex(0,3), Complex('3j'))
+ assert_equal(Complex(0,3), Complex('3J'))
+ assert_equal(Complex(0,1), Complex('I'))
+ assert_equal(Complex(0,1), Complex('J'))
+
+ assert_equal(Complex(5.0), Complex('5.0'))
+ assert_equal(Complex(-5.0), Complex('-5.0'))
+ assert_equal(Complex(5.0,3.0), Complex('5.0+3.0i'))
+ assert_equal(Complex(-5.0,3.0), Complex('-5.0+3.0i'))
+ assert_equal(Complex(5.0,-3.0), Complex('5.0-3.0i'))
+ assert_equal(Complex(-5.0,-3.0), Complex('-5.0-3.0i'))
+ assert_equal(Complex(0.0,3.0), Complex('3.0i'))
+ assert_equal(Complex(0.0,-3.0), Complex('-3.0i'))
+
+ assert_equal(Complex(5.1), Complex('5.1'))
+ assert_equal(Complex(-5.2), Complex('-5.2'))
+ assert_equal(Complex(5.3,3.4), Complex('5.3+3.4i'))
+ assert_equal(Complex(-5.5,3.6), Complex('-5.5+3.6i'))
+ assert_equal(Complex(5.3,-3.4), Complex('5.3-3.4i'))
+ assert_equal(Complex(-5.5,-3.6), Complex('-5.5-3.6i'))
+ assert_equal(Complex(0.0,3.1), Complex('3.1i'))
+ assert_equal(Complex(0.0,-3.2), Complex('-3.2i'))
+
+ assert_equal(Complex(5.0), Complex('5e0'))
+ assert_equal(Complex(-5.0), Complex('-5e0'))
+ assert_equal(Complex(5.0,3.0), Complex('5e0+3e0i'))
+ assert_equal(Complex(-5.0,3.0), Complex('-5e0+3e0i'))
+ assert_equal(Complex(5.0,-3.0), Complex('5e0-3e0i'))
+ assert_equal(Complex(-5.0,-3.0), Complex('-5e0-3e0i'))
+ assert_equal(Complex(0.0,3.0), Complex('3e0i'))
+ assert_equal(Complex(0.0,-3.0), Complex('-3e0i'))
+
+ assert_equal(Complex(5e1), Complex('5e1'))
+ assert_equal(Complex(-5e2), Complex('-5e2'))
+ assert_equal(Complex(5e3,3e4), Complex('5e003+3e4i'))
+ assert_equal(Complex(-5e5,3e6), Complex('-5e5+3e006i'))
+ assert_equal(Complex(5e3,-3e4), Complex('5e003-3e4i'))
+ assert_equal(Complex(-5e5,-3e6), Complex('-5e5-3e006i'))
+ assert_equal(Complex(0.0,3e1), Complex('3e1i'))
+ assert_equal(Complex(0.0,-3e2), Complex('-3e2i'))
+
+ assert_equal(Complex(0.33), Complex('.33'))
+ assert_equal(Complex(0.33), Complex('0.33'))
+ assert_equal(Complex(-0.33), Complex('-.33'))
+ assert_equal(Complex(-0.33), Complex('-0.33'))
+ assert_equal(Complex(-0.33), Complex('-0.3_3'))
+
+ assert_equal(Complex.polar(10,10), Complex('10@10'))
+ assert_equal(Complex.polar(-10,-10), Complex('-10@-10'))
+ assert_equal(Complex.polar(10.5,10.5), Complex('10.5@10.5'))
+ assert_equal(Complex.polar(-10.5,-10.5), Complex('-10.5@-10.5'))
+
+ assert_equal(Complex(0), ''.to_c)
+ assert_equal(Complex(0), ' '.to_c)
+ assert_equal(Complex(5), "\f\n\r\t\v5\0".to_c)
+ assert_equal(Complex(0), '_'.to_c)
+ assert_equal(Complex(0), '_5'.to_c)
+ assert_equal(Complex(5), '5_'.to_c)
+ assert_equal(Complex(5), '5x'.to_c)
+ assert_equal(Complex(5), '5+_3i'.to_c)
+ assert_equal(Complex(5), '5+3_i'.to_c)
+ assert_equal(Complex(5,3), '5+3i_'.to_c)
+ assert_equal(Complex(5,3), '5+3ix'.to_c)
+ assert_raise(ArgumentError){ Complex('')}
+ assert_raise(ArgumentError){ Complex('_')}
+ assert_raise(ArgumentError){ Complex("\f\n\r\t\v5\0")}
+ assert_raise(ArgumentError){ Complex('_5')}
+ assert_raise(ArgumentError){ Complex('5_')}
+ assert_raise(ArgumentError){ Complex('5x')}
+ assert_raise(ArgumentError){ Complex('5+_3i')}
+ assert_raise(ArgumentError){ Complex('5+3_i')}
+ assert_raise(ArgumentError){ Complex('5+3i_')}
+ assert_raise(ArgumentError){ Complex('5+3ix')}
+
+ assert_equal(Complex(Rational(1,5)), '1/5'.to_c)
+ assert_equal(Complex(Rational(-1,5)), '-1/5'.to_c)
+ assert_equal(Complex(Rational(1,5),3), '1/5+3i'.to_c)
+ assert_equal(Complex(Rational(1,5),-3), '1/5-3i'.to_c)
+ assert_equal(Complex(Rational(-1,5),3), '-1/5+3i'.to_c)
+ assert_equal(Complex(Rational(-1,5),-3), '-1/5-3i'.to_c)
+ assert_equal(Complex(Rational(1,5),Rational(3,2)), '1/5+3/2i'.to_c)
+ assert_equal(Complex(Rational(1,5),Rational(-3,2)), '1/5-3/2i'.to_c)
+ assert_equal(Complex(Rational(-1,5),Rational(3,2)), '-1/5+3/2i'.to_c)
+ assert_equal(Complex(Rational(-1,5),Rational(-3,2)), '-1/5-3/2i'.to_c)
+ assert_equal(Complex(Rational(1,5),Rational(3,2)), '1/5+3/2i'.to_c)
+ assert_equal(Complex(Rational(1,5),Rational(-3,2)), '1/5-3/2i'.to_c)
+ assert_equal(Complex(Rational(-1,5),Rational(3,2)), '-1/5+3/2i'.to_c)
+ assert_equal(Complex(Rational(-1,5),Rational(-3,2)), '-1/5-3/2i'.to_c)
+ assert_equal(Complex.polar(Rational(1,5),Rational(3,2)), Complex('1/5@3/2'))
+ assert_equal(Complex.polar(Rational(-1,5),Rational(-3,2)), Complex('-1/5@-3/2'))
+
+ end
+
+ def test_respond
+ c = Complex(1,1)
+ assert_equal(false, c.respond_to?(:%))
+ assert_equal(false, c.respond_to?(:<))
+ assert_equal(false, c.respond_to?(:<=))
+ assert_equal(false, c.respond_to?(:<=>))
+ assert_equal(false, c.respond_to?(:>))
+ assert_equal(false, c.respond_to?(:>=))
+ assert_equal(false, c.respond_to?(:between?))
+ assert_equal(false, c.respond_to?(:div))
+ assert_equal(false, c.respond_to?(:divmod))
+ assert_equal(false, c.respond_to?(:floor))
+ assert_equal(false, c.respond_to?(:ceil))
+ assert_equal(false, c.respond_to?(:modulo))
+ assert_equal(false, c.respond_to?(:remainder))
+ assert_equal(false, c.respond_to?(:round))
+ assert_equal(false, c.respond_to?(:step))
+ assert_equal(false, c.respond_to?(:tunrcate))
+
+ assert_equal(false, c.respond_to?(:positive?))
+ assert_equal(false, c.respond_to?(:negative?))
+ assert_equal(false, c.respond_to?(:sign))
+
+ assert_equal(false, c.respond_to?(:quotient))
+ assert_equal(false, c.respond_to?(:quot))
+ assert_equal(false, c.respond_to?(:quotrem))
+
+ assert_equal(false, c.respond_to?(:gcd))
+ assert_equal(false, c.respond_to?(:lcm))
+ assert_equal(false, c.respond_to?(:gcdlcm))
+ end
+
+ def test_to_i
+ assert_equal(3, Complex(3).to_i)
+ assert_equal(3, Integer(Complex(3)))
+ assert_raise(RangeError){Complex(3,2).to_i}
+ assert_raise(RangeError){Integer(Complex(3,2))}
+ end
+
+ def test_to_f
+ assert_equal(3.0, Complex(3).to_f)
+ assert_equal(3.0, Float(Complex(3)))
+ assert_raise(RangeError){Complex(3,2).to_f}
+ assert_raise(RangeError){Float(Complex(3,2))}
+ end
+
+ def test_to_r
+ assert_equal(Rational(3), Complex(3).to_r)
+ assert_equal(Rational(3), Rational(Complex(3)))
+ assert_raise(RangeError){Complex(3,2).to_r}
+ assert_raise(RangeError){Rational(Complex(3,2))}
+ end
+
+ def test_to_c
+ c = nil.to_c
+ assert_equal([0,0], [c.real, c.imag])
+
+ c = 0.to_c
+ assert_equal([0,0], [c.real, c.imag])
+
+ c = 1.to_c
+ assert_equal([1,0], [c.real, c.imag])
+
+ c = 1.1.to_c
+ assert_equal([1.1, 0], [c.real, c.imag])
+
+ c = Rational(1,2).to_c
+ assert_equal([Rational(1,2), 0], [c.real, c.imag])
+
+ c = Complex(1,2).to_c
+ assert_equal([1, 2], [c.real, c.imag])
+
+ if (0.0/0).nan?
+ assert_nothing_raised{(0.0/0).to_c}
+ end
+ if (1.0/0).infinite?
+ assert_nothing_raised{(1.0/0).to_c}
+ end
+ end
+
+ def test_supp
+ assert_equal(true, 1.real?)
+ assert_equal(true, 1.1.real?)
+
+ assert_equal(1, 1.real)
+ assert_equal(0, 1.imag)
+ assert_equal(0, 1.imaginary)
+
+ assert_equal(1.1, 1.1.real)
+ assert_equal(0, 1.1.imag)
+ assert_equal(0, 1.1.imaginary)
+
+ assert_equal(1, 1.magnitude)
+ assert_equal(1, -1.magnitude)
+ assert_equal(1, 1.0.magnitude)
+ assert_equal(1, -1.0.magnitude)
+
+ assert_equal(4, 2.abs2)
+ assert_equal(4, -2.abs2)
+ assert_equal(4.0, 2.0.abs2)
+ assert_equal(4.0, -2.0.abs2)
+
+ assert_equal(0, 1.arg)
+ assert_equal(0, 1.angle)
+ assert_equal(0, 1.phase)
+
+ assert_equal(0, 1.0.arg)
+ assert_equal(0, 1.0.angle)
+ assert_equal(0, 1.0.phase)
+
+ if (0.0/0).nan?
+ nan = 0.0/0
+ assert_same(nan, nan.arg)
+ assert_same(nan, nan.angle)
+ assert_same(nan, nan.phase)
+ end
+
+ assert_equal(Math::PI, -1.arg)
+ assert_equal(Math::PI, -1.angle)
+ assert_equal(Math::PI, -1.phase)
+
+ assert_equal(Math::PI, -1.0.arg)
+ assert_equal(Math::PI, -1.0.angle)
+ assert_equal(Math::PI, -1.0.phase)
+
+ assert_equal([1,0], 1.rect)
+ assert_equal([-1,0], -1.rect)
+ assert_equal([1,0], 1.rectangular)
+ assert_equal([-1,0], -1.rectangular)
+
+ assert_equal([1.0,0], 1.0.rect)
+ assert_equal([-1.0,0], -1.0.rect)
+ assert_equal([1.0,0], 1.0.rectangular)
+ assert_equal([-1.0,0], -1.0.rectangular)
+
+ assert_equal([1,0], 1.polar)
+ assert_equal([1, Math::PI], -1.polar)
+
+ assert_equal([1.0,0], 1.0.polar)
+ assert_equal([1.0, Math::PI], -1.0.polar)
+
+ assert_equal(1, 1.conjugate)
+ assert_equal(-1, -1.conjugate)
+ assert_equal(1, 1.conj)
+ assert_equal(-1, -1.conj)
+
+ assert_equal(1.1, 1.1.conjugate)
+ assert_equal(-1.1, -1.1.conjugate)
+ assert_equal(1.1, 1.1.conj)
+ assert_equal(-1.1, -1.1.conj)
+
+ assert_equal(Complex(Rational(1,2),Rational(1)), Complex(1,2).quo(2))
+
+ assert_equal(0.5, 1.fdiv(2))
+ assert_equal(5000000000.0, 10000000000.fdiv(2))
+ assert_equal(0.5, 1.0.fdiv(2))
+ assert_equal(0.25, Rational(1,2).fdiv(2))
+ assert_equal(Complex(0.5,1.0), Complex(1,2).quo(2))
+
+ unless $".grep(/(?:\A|(?<!add)\/)complex/).empty?
+ assert_equal(Complex(0,2), CMath.sqrt(-4.0))
+ assert_equal(Complex(0,2), CMath.sqrt(-4))
+ assert_equal(Complex(0,2), CMath.sqrt(Rational(-4)))
+
+ assert_equal(Complex(0,3), CMath.sqrt(-9.0))
+ assert_equal(Complex(0,3), CMath.sqrt(-9))
+ assert_equal(Complex(0,3), CMath.sqrt(Rational(-9)))
+
+ c = CMath.sqrt(Complex(1, 2))
+ assert_in_delta(1.272, c.real, 0.001)
+ assert_in_delta(0.786, c.imag, 0.001)
+
+ c = CMath.sqrt(-9)
+ assert_in_delta(0.0, c.real, 0.001)
+ assert_in_delta(3.0, c.imag, 0.001)
+
+ c = CMath.exp(Complex(1, 2))
+ assert_in_delta(-1.131, c.real, 0.001)
+ assert_in_delta(2.471, c.imag, 0.001)
+
+ c = CMath.sin(Complex(1, 2))
+ assert_in_delta(3.165, c.real, 0.001)
+ assert_in_delta(1.959, c.imag, 0.001)
+
+ c = CMath.cos(Complex(1, 2))
+ assert_in_delta(2.032, c.real, 0.001)
+ assert_in_delta(-3.051, c.imag, 0.001)
+
+ c = CMath.tan(Complex(1, 2))
+ assert_in_delta(0.033, c.real, 0.001)
+ assert_in_delta(1.014, c.imag, 0.001)
+
+ c = CMath.sinh(Complex(1, 2))
+ assert_in_delta(-0.489, c.real, 0.001)
+ assert_in_delta(1.403, c.imag, 0.001)
+
+ c = CMath.cosh(Complex(1, 2))
+ assert_in_delta(-0.642, c.real, 0.001)
+ assert_in_delta(1.068, c.imag, 0.001)
+
+ c = CMath.tanh(Complex(1, 2))
+ assert_in_delta(1.166, c.real, 0.001)
+ assert_in_delta(-0.243, c.imag, 0.001)
+
+ c = CMath.log(Complex(1, 2))
+ assert_in_delta(0.804, c.real, 0.001)
+ assert_in_delta(1.107, c.imag, 0.001)
+
+ c = CMath.log(Complex(1, 2), Math::E)
+ assert_in_delta(0.804, c.real, 0.001)
+ assert_in_delta(1.107, c.imag, 0.001)
+
+ c = CMath.log(-1)
+ assert_in_delta(0.0, c.real, 0.001)
+ assert_in_delta(Math::PI, c.imag, 0.001)
+
+ c = CMath.log(8, 2)
+ assert_in_delta(3.0, c.real, 0.001)
+ assert_in_delta(0.0, c.imag, 0.001)
+
+ c = CMath.log(-8, -2)
+ assert_in_delta(1.092, c.real, 0.001)
+ assert_in_delta(-0.420, c.imag, 0.001)
+
+ c = CMath.log10(Complex(1, 2))
+ assert_in_delta(0.349, c.real, 0.001)
+ assert_in_delta(0.480, c.imag, 0.001)
+
+ c = CMath.asin(Complex(1, 2))
+ assert_in_delta(0.427, c.real, 0.001)
+ assert_in_delta(1.528, c.imag, 0.001)
+
+ c = CMath.acos(Complex(1, 2))
+ assert_in_delta(1.143, c.real, 0.001)
+ assert_in_delta(-1.528, c.imag, 0.001)
+
+ c = CMath.atan(Complex(1, 2))
+ assert_in_delta(1.338, c.real, 0.001)
+ assert_in_delta(0.402, c.imag, 0.001)
+
+ c = CMath.atan2(Complex(1, 2), 1)
+ assert_in_delta(1.338, c.real, 0.001)
+ assert_in_delta(0.402, c.imag, 0.001)
+
+ c = CMath.asinh(Complex(1, 2))
+ assert_in_delta(1.469, c.real, 0.001)
+ assert_in_delta(1.063, c.imag, 0.001)
+
+ c = CMath.acosh(Complex(1, 2))
+ assert_in_delta(1.528, c.real, 0.001)
+ assert_in_delta(1.143, c.imag, 0.001)
+
+ c = CMath.atanh(Complex(1, 2))
+ assert_in_delta(0.173, c.real, 0.001)
+ assert_in_delta(1.178, c.imag, 0.001)
+ end
+
+ end
+
+ def test_ruby19
+ assert_raise(NoMethodError){ Complex.new(1) }
+ assert_raise(NoMethodError){ Complex.new!(1) }
+ assert_raise(NoMethodError){ Complex.reduce(1) }
+ end
+
+ def test_fixed_bug
+ assert_equal(Complex(1), 1 ** Complex(1))
+ assert_equal('-1.0-0.0i', Complex(-1.0, -0.0).to_s)
+ assert_in_delta(Math::PI, Complex(-0.0).arg, 0.001)
+ assert_equal(Complex(2e3, 2e4), '2e3+2e4i'.to_c)
+ assert_raise(ArgumentError){ Complex('--8i')}
+ end
+
+ def test_known_bug
+ end
+
+end
diff --git a/jni/ruby/test/ruby/test_complex2.rb b/jni/ruby/test/ruby/test_complex2.rb
new file mode 100644
index 0000000..3ee7810
--- /dev/null
+++ b/jni/ruby/test/ruby/test_complex2.rb
@@ -0,0 +1,735 @@
+require 'test/unit'
+
+class Complex_Test2 < Test::Unit::TestCase
+
+ def test_kumi
+ skip unless defined?(Rational)
+
+ assert_equal(Complex(1, 0), +Complex(1, 0))
+ assert_equal(Complex(-1, 0), -Complex(1, 0))
+ assert_equal(Complex(2, 0),
+ Complex(1, 0) + Complex(1, 0))
+ assert_equal(Complex(0, 0),
+ Complex(1, 0) - Complex(1, 0))
+ assert_equal(Complex(1, 0),
+ Complex(1, 0) * Complex(1, 0))
+ assert_equal(Complex(1, 0),
+ Complex(1, 0) / Complex(1, 0))
+ assert_equal(Complex(1073741790, 0),
+ Complex(1, 0) + Complex(1073741789, 0))
+ assert_equal(Complex(-1073741788, 0),
+ Complex(1, 0) - Complex(1073741789, 0))
+ assert_equal(Complex(1073741789, 0),
+ Complex(1, 0) * Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1, 1073741789), 0),
+ Complex(1, 0) / Complex(1073741789, 0))
+ assert_equal(Complex(1073741828, 0),
+ Complex(1, 0) + Complex(1073741827, 0))
+ assert_equal(Complex(-1073741826, 0),
+ Complex(1, 0) - Complex(1073741827, 0))
+ assert_equal(Complex(1073741827, 0),
+ Complex(1, 0) * Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1, 1073741827), 0),
+ Complex(1, 0) / Complex(1073741827, 0))
+ assert_equal(Complex(1073741790, 1073741789),
+ Complex(1, 0) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(-1073741788, -1073741789),
+ Complex(1, 0) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(1073741789, 1073741789),
+ Complex(1, 0) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1, 2147483578), Rational(-1, 2147483578)),
+ Complex(1, 0) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(1073741790, 1073741827),
+ Complex(1, 0) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(-1073741788, -1073741827),
+ Complex(1, 0) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(1073741789, 1073741827),
+ Complex(1, 0) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1073741789, 2305842940494218450), Rational(-1073741827, 2305842940494218450)),
+ Complex(1, 0) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(1073741828, 1073741827),
+ Complex(1, 0) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(-1073741826, -1073741827),
+ Complex(1, 0) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(1073741827, 1073741827),
+ Complex(1, 0) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1, 2147483654), Rational(-1, 2147483654)),
+ Complex(1, 0) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(2147483616, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(1, 0) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(38, 1073741827), Rational(-1073741789, 1073741827)),
+ Complex(1, 0) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(1, 0) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1073741827, 2147483578), Rational(-1073741827, 2147483578)),
+ Complex(1, 0) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(2147483616, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(1, 0) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-38, 1073741789), Rational(-1073741827, 1073741789)),
+ Complex(1, 0) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(1, 0) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1073741789, 2147483654), Rational(-1073741789, 2147483654)),
+ Complex(1, 0) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(2147483616, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(1, 0) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(38, 1073741827), Rational(-1073741827, 1073741789)),
+ Complex(1, 0) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(1, 0) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1329227869515035739611240300898290063, 2658455833113515253509575011810600482), Rational(-1329227963598474519442525600436190287, 2658455833113515253509575011810600482)),
+ Complex(1, 0) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1073741789, 0), +Complex(1073741789, 0))
+ assert_equal(Complex(-1073741789, 0), -Complex(1073741789, 0))
+ assert_equal(Complex(1073741790, 0),
+ Complex(1073741789, 0) + Complex(1, 0))
+ assert_equal(Complex(1073741788, 0),
+ Complex(1073741789, 0) - Complex(1, 0))
+ assert_equal(Complex(1073741789, 0),
+ Complex(1073741789, 0) * Complex(1, 0))
+ assert_equal(Complex(1073741789, 0),
+ Complex(1073741789, 0) / Complex(1, 0))
+ assert_equal(Complex(2147483578, 0),
+ Complex(1073741789, 0) + Complex(1073741789, 0))
+ assert_equal(Complex(0, 0),
+ Complex(1073741789, 0) - Complex(1073741789, 0))
+ assert_equal(Complex(1152921429444920521, 0),
+ Complex(1073741789, 0) * Complex(1073741789, 0))
+ assert_equal(Complex(1, 0),
+ Complex(1073741789, 0) / Complex(1073741789, 0))
+ assert_equal(Complex(2147483616, 0),
+ Complex(1073741789, 0) + Complex(1073741827, 0))
+ assert_equal(Complex(-38, 0),
+ Complex(1073741789, 0) - Complex(1073741827, 0))
+ assert_equal(Complex(1152921470247108503, 0),
+ Complex(1073741789, 0) * Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1073741789, 1073741827), 0),
+ Complex(1073741789, 0) / Complex(1073741827, 0))
+ assert_equal(Complex(2147483578, 1073741789),
+ Complex(1073741789, 0) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(0, -1073741789),
+ Complex(1073741789, 0) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(1152921429444920521, 1152921429444920521),
+ Complex(1073741789, 0) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1, 2), Rational(-1, 2)),
+ Complex(1073741789, 0) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(2147483578, 1073741827),
+ Complex(1073741789, 0) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(0, -1073741827),
+ Complex(1073741789, 0) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(1152921429444920521, 1152921470247108503),
+ Complex(1073741789, 0) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921429444920521, 2305842940494218450), Rational(-1152921470247108503, 2305842940494218450)),
+ Complex(1073741789, 0) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(2147483616, 1073741827),
+ Complex(1073741789, 0) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(-38, -1073741827),
+ Complex(1073741789, 0) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(1152921470247108503, 1152921470247108503),
+ Complex(1073741789, 0) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1073741789, 2147483654), Rational(-1073741789, 2147483654)),
+ Complex(1073741789, 0) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(1073741789, 0) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921469173366714, 1073741827), Rational(-1073741789, 1073741827)),
+ Complex(1073741789, 0) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921429444920521, 1073741827), Rational(1152921429444920521, 1073741827)),
+ Complex(1073741789, 0) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1073741827, 2), Rational(-1073741827, 2)),
+ Complex(1073741789, 0) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921430518662348, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(1073741789, 0) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921428371178694, 1073741789), Rational(-1073741827, 1073741789)),
+ Complex(1073741789, 0) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1073741827, 1073741827),
+ Complex(1073741789, 0) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921429444920521, 2147483654), Rational(-1152921429444920521, 2147483654)),
+ Complex(1073741789, 0) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(1073741789, 0) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921469173366714, 1073741827), Rational(-1073741827, 1073741789)),
+ Complex(1073741789, 0) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921429444920521, 1073741827), 1073741827),
+ Complex(1073741789, 0) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1427247510601733037449111325195428279286542707, 2658455833113515253509575011810600482), Rational(-1427247611623052908177132720890654139107803443, 2658455833113515253509575011810600482)),
+ Complex(1073741789, 0) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1073741827, 0), +Complex(1073741827, 0))
+ assert_equal(Complex(-1073741827, 0), -Complex(1073741827, 0))
+ assert_equal(Complex(1073741828, 0),
+ Complex(1073741827, 0) + Complex(1, 0))
+ assert_equal(Complex(1073741826, 0),
+ Complex(1073741827, 0) - Complex(1, 0))
+ assert_equal(Complex(1073741827, 0),
+ Complex(1073741827, 0) * Complex(1, 0))
+ assert_equal(Complex(1073741827, 0),
+ Complex(1073741827, 0) / Complex(1, 0))
+ assert_equal(Complex(2147483616, 0),
+ Complex(1073741827, 0) + Complex(1073741789, 0))
+ assert_equal(Complex(38, 0),
+ Complex(1073741827, 0) - Complex(1073741789, 0))
+ assert_equal(Complex(1152921470247108503, 0),
+ Complex(1073741827, 0) * Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1073741827, 1073741789), 0),
+ Complex(1073741827, 0) / Complex(1073741789, 0))
+ assert_equal(Complex(2147483654, 0),
+ Complex(1073741827, 0) + Complex(1073741827, 0))
+ assert_equal(Complex(0, 0),
+ Complex(1073741827, 0) - Complex(1073741827, 0))
+ assert_equal(Complex(1152921511049297929, 0),
+ Complex(1073741827, 0) * Complex(1073741827, 0))
+ assert_equal(Complex(1, 0),
+ Complex(1073741827, 0) / Complex(1073741827, 0))
+ assert_equal(Complex(2147483616, 1073741789),
+ Complex(1073741827, 0) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(38, -1073741789),
+ Complex(1073741827, 0) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(1152921470247108503, 1152921470247108503),
+ Complex(1073741827, 0) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1073741827, 2147483578), Rational(-1073741827, 2147483578)),
+ Complex(1073741827, 0) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(2147483616, 1073741827),
+ Complex(1073741827, 0) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(38, -1073741827),
+ Complex(1073741827, 0) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(1152921470247108503, 1152921511049297929),
+ Complex(1073741827, 0) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921470247108503, 2305842940494218450), Rational(-1152921511049297929, 2305842940494218450)),
+ Complex(1073741827, 0) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(2147483654, 1073741827),
+ Complex(1073741827, 0) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(0, -1073741827),
+ Complex(1073741827, 0) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(1152921511049297929, 1152921511049297929),
+ Complex(1073741827, 0) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1, 2), Rational(-1, 2)),
+ Complex(1073741827, 0) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1152921512123039718, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(1073741827, 0) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921509975556140, 1073741827), Rational(-1073741789, 1073741827)),
+ Complex(1073741827, 0) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(1073741789, 1073741789),
+ Complex(1073741827, 0) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921511049297929, 2147483578), Rational(-1152921511049297929, 2147483578)),
+ Complex(1073741827, 0) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921471320850330, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(1073741827, 0) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921469173366676, 1073741789), Rational(-1073741827, 1073741789)),
+ Complex(1073741827, 0) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921511049297929, 1073741789), Rational(1152921511049297929, 1073741789)),
+ Complex(1073741827, 0) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1073741789, 2), Rational(-1073741789, 2)),
+ Complex(1073741827, 0) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921512123039718, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(1073741827, 0) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921509975556140, 1073741827), Rational(-1073741827, 1073741789)),
+ Complex(1073741827, 0) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1073741789, Rational(1152921511049297929, 1073741789)),
+ Complex(1073741827, 0) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1427247561112392079020469430422559713421565101, 2658455833113515253509575011810600482), Rational(-1427247662133715524919164459706626955683034349, 2658455833113515253509575011810600482)),
+ Complex(1073741827, 0) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1073741789, 1073741789), +Complex(1073741789, 1073741789))
+ assert_equal(Complex(-1073741789, -1073741789), -Complex(1073741789, 1073741789))
+ assert_equal(Complex(1073741790, 1073741789),
+ Complex(1073741789, 1073741789) + Complex(1, 0))
+ assert_equal(Complex(1073741788, 1073741789),
+ Complex(1073741789, 1073741789) - Complex(1, 0))
+ assert_equal(Complex(1073741789, 1073741789),
+ Complex(1073741789, 1073741789) * Complex(1, 0))
+ assert_equal(Complex(1073741789, 1073741789),
+ Complex(1073741789, 1073741789) / Complex(1, 0))
+ assert_equal(Complex(2147483578, 1073741789),
+ Complex(1073741789, 1073741789) + Complex(1073741789, 0))
+ assert_equal(Complex(0, 1073741789),
+ Complex(1073741789, 1073741789) - Complex(1073741789, 0))
+ assert_equal(Complex(1152921429444920521, 1152921429444920521),
+ Complex(1073741789, 1073741789) * Complex(1073741789, 0))
+ assert_equal(Complex(1, 1),
+ Complex(1073741789, 1073741789) / Complex(1073741789, 0))
+ assert_equal(Complex(2147483616, 1073741789),
+ Complex(1073741789, 1073741789) + Complex(1073741827, 0))
+ assert_equal(Complex(-38, 1073741789),
+ Complex(1073741789, 1073741789) - Complex(1073741827, 0))
+ assert_equal(Complex(1152921470247108503, 1152921470247108503),
+ Complex(1073741789, 1073741789) * Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(1073741789, 1073741789) / Complex(1073741827, 0))
+ assert_equal(Complex(2147483578, 2147483578),
+ Complex(1073741789, 1073741789) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(0, 0),
+ Complex(1073741789, 1073741789) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(0, 2305842858889841042),
+ Complex(1073741789, 1073741789) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(1, 0),
+ Complex(1073741789, 1073741789) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(2147483578, 2147483616),
+ Complex(1073741789, 1073741789) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(0, -38),
+ Complex(1073741789, 1073741789) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(-40802187982, 2305842899692029024),
+ Complex(1073741789, 1073741789) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921449846014512, 1152921470247109225), Rational(-20401093991, 1152921470247109225)),
+ Complex(1073741789, 1073741789) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(2147483616, 2147483616),
+ Complex(1073741789, 1073741789) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(-38, -38),
+ Complex(1073741789, 1073741789) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(0, 2305842940494217006),
+ Complex(1073741789, 1073741789) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1073741789, 1073741827), 0),
+ Complex(1073741789, 1073741789) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1152921471320850292, 1073741827)),
+ Complex(1073741789, 1073741789) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921469173366714, 1073741827), Rational(1152921469173366714, 1073741827)),
+ Complex(1073741789, 1073741789) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(0, Rational(2305842858889841042, 1073741827)),
+ Complex(1073741789, 1073741789) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(1073741827, 0),
+ Complex(1073741789, 1073741789) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921430518662348, 1073741789), Rational(1152921430518662348, 1073741789)),
+ Complex(1073741789, 1073741789) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921428371178694, 1073741789), Rational(1152921428371178694, 1073741789)),
+ Complex(1073741789, 1073741789) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(0, 2147483654),
+ Complex(1073741789, 1073741789) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921429444920521, 1073741827), 0),
+ Complex(1073741789, 1073741789) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1152921430518662348, 1073741789)),
+ Complex(1073741789, 1073741789) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921469173366714, 1073741827), Rational(1152921428371178694, 1073741789)),
+ Complex(1073741789, 1073741789) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-81604377408, 1073741827), Rational(2305842940494218450, 1073741827)),
+ Complex(1073741789, 1073741789) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1427247561112392972813122023043041209197173075, 1329227916556757626754787505905300241), Rational(-50510659935364010697847612929910630368, 1329227916556757626754787505905300241)),
+ Complex(1073741789, 1073741789) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1073741789, 1073741827), +Complex(1073741789, 1073741827))
+ assert_equal(Complex(-1073741789, -1073741827), -Complex(1073741789, 1073741827))
+ assert_equal(Complex(1073741790, 1073741827),
+ Complex(1073741789, 1073741827) + Complex(1, 0))
+ assert_equal(Complex(1073741788, 1073741827),
+ Complex(1073741789, 1073741827) - Complex(1, 0))
+ assert_equal(Complex(1073741789, 1073741827),
+ Complex(1073741789, 1073741827) * Complex(1, 0))
+ assert_equal(Complex(1073741789, 1073741827),
+ Complex(1073741789, 1073741827) / Complex(1, 0))
+ assert_equal(Complex(2147483578, 1073741827),
+ Complex(1073741789, 1073741827) + Complex(1073741789, 0))
+ assert_equal(Complex(0, 1073741827),
+ Complex(1073741789, 1073741827) - Complex(1073741789, 0))
+ assert_equal(Complex(1152921429444920521, 1152921470247108503),
+ Complex(1073741789, 1073741827) * Complex(1073741789, 0))
+ assert_equal(Complex(1, Rational(1073741827, 1073741789)),
+ Complex(1073741789, 1073741827) / Complex(1073741789, 0))
+ assert_equal(Complex(2147483616, 1073741827),
+ Complex(1073741789, 1073741827) + Complex(1073741827, 0))
+ assert_equal(Complex(-38, 1073741827),
+ Complex(1073741789, 1073741827) - Complex(1073741827, 0))
+ assert_equal(Complex(1152921470247108503, 1152921511049297929),
+ Complex(1073741789, 1073741827) * Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1073741789, 1073741827), 1),
+ Complex(1073741789, 1073741827) / Complex(1073741827, 0))
+ assert_equal(Complex(2147483578, 2147483616),
+ Complex(1073741789, 1073741827) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(0, 38),
+ Complex(1073741789, 1073741827) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(-40802187982, 2305842899692029024),
+ Complex(1073741789, 1073741827) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1073741808, 1073741789), Rational(19, 1073741789)),
+ Complex(1073741789, 1073741827) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(2147483578, 2147483654),
+ Complex(1073741789, 1073741827) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(0, 0),
+ Complex(1073741789, 1073741827) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(-81604377408, 2305842940494217006),
+ Complex(1073741789, 1073741827) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(1, 0),
+ Complex(1073741789, 1073741827) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(2147483616, 2147483654),
+ Complex(1073741789, 1073741827) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(-38, 0),
+ Complex(1073741789, 1073741827) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(-40802189426, 2305842981296406432),
+ Complex(1073741789, 1073741827) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1073741808, 1073741827), Rational(19, 1073741827)),
+ Complex(1073741789, 1073741827) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1152921512123039718, 1073741827)),
+ Complex(1073741789, 1073741827) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921469173366714, 1073741827), Rational(1152921509975556140, 1073741827)),
+ Complex(1073741789, 1073741827) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(-40802187982, 1073741827), Rational(2305842899692029024, 1073741827)),
+ Complex(1073741789, 1073741827) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921490648203216, 1073741789), Rational(20401094713, 1073741789)),
+ Complex(1073741789, 1073741827) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921430518662348, 1073741789), Rational(1152921471320850330, 1073741789)),
+ Complex(1073741789, 1073741827) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921428371178694, 1073741789), Rational(1152921469173366676, 1073741789)),
+ Complex(1073741789, 1073741827) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-40802189426, 1073741789), Rational(2305842981296406432, 1073741789)),
+ Complex(1073741789, 1073741827) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921449846014512, 1073741827), Rational(20401093991, 1073741827)),
+ Complex(1073741789, 1073741827) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1152921471320850330, 1073741789)),
+ Complex(1073741789, 1073741827) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921469173366714, 1073741827), Rational(1152921469173366676, 1073741789)),
+ Complex(1073741789, 1073741827) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-131433047608170424214, 1152921470247108503), 2147483616),
+ Complex(1073741789, 1073741827) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1427247586367724281184137892451027617484788528, 1329227916556757626754787505905300241), Rational(-25255330414578331645234047212843119171, 1329227916556757626754787505905300241)),
+ Complex(1073741789, 1073741827) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1073741827, 1073741827), +Complex(1073741827, 1073741827))
+ assert_equal(Complex(-1073741827, -1073741827), -Complex(1073741827, 1073741827))
+ assert_equal(Complex(1073741828, 1073741827),
+ Complex(1073741827, 1073741827) + Complex(1, 0))
+ assert_equal(Complex(1073741826, 1073741827),
+ Complex(1073741827, 1073741827) - Complex(1, 0))
+ assert_equal(Complex(1073741827, 1073741827),
+ Complex(1073741827, 1073741827) * Complex(1, 0))
+ assert_equal(Complex(1073741827, 1073741827),
+ Complex(1073741827, 1073741827) / Complex(1, 0))
+ assert_equal(Complex(2147483616, 1073741827),
+ Complex(1073741827, 1073741827) + Complex(1073741789, 0))
+ assert_equal(Complex(38, 1073741827),
+ Complex(1073741827, 1073741827) - Complex(1073741789, 0))
+ assert_equal(Complex(1152921470247108503, 1152921470247108503),
+ Complex(1073741827, 1073741827) * Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(1073741827, 1073741827) / Complex(1073741789, 0))
+ assert_equal(Complex(2147483654, 1073741827),
+ Complex(1073741827, 1073741827) + Complex(1073741827, 0))
+ assert_equal(Complex(0, 1073741827),
+ Complex(1073741827, 1073741827) - Complex(1073741827, 0))
+ assert_equal(Complex(1152921511049297929, 1152921511049297929),
+ Complex(1073741827, 1073741827) * Complex(1073741827, 0))
+ assert_equal(Complex(1, 1),
+ Complex(1073741827, 1073741827) / Complex(1073741827, 0))
+ assert_equal(Complex(2147483616, 2147483616),
+ Complex(1073741827, 1073741827) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(38, 38),
+ Complex(1073741827, 1073741827) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(0, 2305842940494217006),
+ Complex(1073741827, 1073741827) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1073741827, 1073741789), 0),
+ Complex(1073741827, 1073741827) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(2147483616, 2147483654),
+ Complex(1073741827, 1073741827) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(38, 0),
+ Complex(1073741827, 1073741827) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(-40802189426, 2305842981296406432),
+ Complex(1073741827, 1073741827) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921490648203216, 1152921470247109225), Rational(-20401094713, 1152921470247109225)),
+ Complex(1073741827, 1073741827) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(2147483654, 2147483654),
+ Complex(1073741827, 1073741827) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(0, 0),
+ Complex(1073741827, 1073741827) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(0, 2305843022098595858),
+ Complex(1073741827, 1073741827) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(1, 0),
+ Complex(1073741827, 1073741827) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1152921512123039718, 1073741827), Rational(1152921512123039718, 1073741827)),
+ Complex(1073741827, 1073741827) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921509975556140, 1073741827), Rational(1152921509975556140, 1073741827)),
+ Complex(1073741827, 1073741827) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(0, 2147483578),
+ Complex(1073741827, 1073741827) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921511049297929, 1073741789), 0),
+ Complex(1073741827, 1073741827) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921471320850330, 1073741789), Rational(1152921471320850330, 1073741789)),
+ Complex(1073741827, 1073741827) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921469173366676, 1073741789), Rational(1152921469173366676, 1073741789)),
+ Complex(1073741827, 1073741827) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(0, Rational(2305843022098595858, 1073741789)),
+ Complex(1073741827, 1073741827) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1073741789, 0),
+ Complex(1073741827, 1073741827) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921512123039718, 1073741827), Rational(1152921471320850330, 1073741789)),
+ Complex(1073741827, 1073741827) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921509975556140, 1073741827), Rational(1152921469173366676, 1073741789)),
+ Complex(1073741827, 1073741827) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-81604377408, 1073741789), Rational(2305842940494218450, 1073741789)),
+ Complex(1073741827, 1073741827) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1427247611623053801969816945064593334552299725, 1329227916556757626754787505905300241), Rational(-50510661722949347514642033621130734624, 1329227916556757626754787505905300241)),
+ Complex(1073741827, 1073741827) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)), +Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(-1073741789, 1073741827), Rational(-1073741789, 1073741827)), -Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(2147483616, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(1, 0))
+ assert_equal(Complex(Rational(-38, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(1, 0))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(1, 0))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(1, 0))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(1073741789, 0))
+ assert_equal(Complex(Rational(-1152921469173366714, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1152921429444920521, 1073741827), Rational(1152921429444920521, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1, 1073741827), Rational(1, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1152921512123039718, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(1073741827, 0))
+ assert_equal(Complex(Rational(-1152921509975556140, 1073741827), Rational(1073741789, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(1073741827, 0))
+ assert_equal(Complex(1073741789, 1073741789),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1073741789, 1152921511049297929), Rational(1073741789, 1152921511049297929)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1152921471320850292, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(-1152921469173366714, 1073741827), Rational(-1152921469173366714, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(0, Rational(2305842858889841042, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1, 1073741827), 0),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1152921512123039718, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(-1152921469173366714, 1073741827), Rational(-1152921509975556140, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(-40802187982, 1073741827), Rational(2305842899692029024, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921449846014512, 1237940005850657200720054075), Rational(-20401093991, 1237940005850657200720054075)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921512123039718, 1073741827), Rational(1152921512123039718, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(-1152921509975556140, 1073741827), Rational(-1152921509975556140, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(0, 2147483578),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1073741789, 1152921511049297929), 0),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(2147483578, 1073741827), Rational(2147483578, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(0, 0),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(0, Rational(2305842858889841042, 1152921511049297929)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(1, 0),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(2305842940494218450, 1152921470247108503), Rational(2305842940494218450, 1152921470247108503)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-81604377408, 1152921470247108503), Rational(-81604377408, 1152921470247108503)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(0, 2),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921429444920521, 1152921511049297929), 0),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(2147483578, 1073741827), Rational(2305842940494218450, 1152921470247108503)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(0, Rational(-81604377408, 1152921470247108503)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-81604377408, 1152921511049297929), Rational(2305842940494218450, 1152921511049297929)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1329227869515036572020512360130906225, 1329227916556757626754787505905300241), Rational(-47041717725097069072123994784, 1329227916556757626754787505905300241)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)), +Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-1073741827, 1073741789), Rational(-1073741827, 1073741789)), -Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(2147483616, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(1, 0))
+ assert_equal(Complex(Rational(38, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(1, 0))
+ assert_equal(Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(1, 0))
+ assert_equal(Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(1, 0))
+ assert_equal(Complex(Rational(1152921430518662348, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(1073741789, 0))
+ assert_equal(Complex(Rational(-1152921428371178694, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(1073741789, 0))
+ assert_equal(Complex(1073741827, 1073741827),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1073741827, 1152921429444920521), Rational(1073741827, 1152921429444920521)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1152921471320850330, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(1073741827, 0))
+ assert_equal(Complex(Rational(-1152921469173366676, 1073741789), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1152921511049297929, 1073741789), Rational(1152921511049297929, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1, 1073741789), Rational(1, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1152921430518662348, 1073741789), Rational(1152921430518662348, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(-1152921428371178694, 1073741789), Rational(-1152921428371178694, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(0, 2147483654),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1073741827, 1152921429444920521), 0),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1152921430518662348, 1073741789), Rational(1152921471320850330, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(-1152921428371178694, 1073741789), Rational(-1152921469173366676, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(-40802189426, 1073741789), Rational(2305842981296406432, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921490648203216, 1237939962039641331329903525), Rational(-20401094713, 1237939962039641331329903525)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921471320850330, 1073741789), Rational(1152921471320850330, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(-1152921469173366676, 1073741789), Rational(-1152921469173366676, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(0, Rational(2305843022098595858, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1, 1073741789), 0),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(2305842940494218450, 1152921470247108503), Rational(2305842940494218450, 1152921470247108503)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(81604377408, 1152921470247108503), Rational(81604377408, 1152921470247108503)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(0, 2),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921511049297929, 1152921429444920521), 0),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(2147483654, 1073741789), Rational(2147483654, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(0, 0),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(0, Rational(2305843022098595858, 1152921429444920521)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1, 0),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(2305842940494218450, 1152921470247108503), Rational(2147483654, 1073741789)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(81604377408, 1152921470247108503), 0),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-81604377408, 1152921429444920521), Rational(2305842940494218450, 1152921429444920521)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1329227963598475351851856578029295025, 1329227916556757626754787505905300241), Rational(-47041721054734275145774394016, 1329227916556757626754787505905300241)),
+ Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)), +Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-1073741789, 1073741827), Rational(-1073741827, 1073741789)), -Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(2147483616, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(1, 0))
+ assert_equal(Complex(Rational(-38, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(1, 0))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(1, 0))
+ assert_equal(Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(1, 0))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(1073741789, 0))
+ assert_equal(Complex(Rational(-1152921469173366714, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1152921429444920521, 1073741827), 1073741827),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1, 1073741827), Rational(1073741827, 1152921429444920521)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(1073741789, 0))
+ assert_equal(Complex(Rational(1152921512123039718, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(1073741827, 0))
+ assert_equal(Complex(Rational(-1152921509975556140, 1073741827), Rational(1073741827, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(1073741827, 0))
+ assert_equal(Complex(1073741789, Rational(1152921511049297929, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1073741789, 1152921511049297929), Rational(1, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(1073741827, 0))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1152921430518662348, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(-1152921469173366714, 1073741827), Rational(-1152921428371178694, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(-81604377408, 1073741827), Rational(2305842940494218450, 1073741827)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1152921470247109225, 1237939962039640556088331867), Rational(40802188704, 1237939962039640556088331867)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(1073741789, 1073741789))
+ assert_equal(Complex(Rational(1152921471320850292, 1073741827), Rational(1152921471320850330, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(-1152921469173366714, 1073741827), Rational(-1152921469173366676, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(-131433047608170424214, 1152921470247108503), 2147483616),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1237939983945150041266564176, 1329227916556755129526882950667240175), Rational(19, 1152921470247109225)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(1073741789, 1073741827))
+ assert_equal(Complex(Rational(1152921512123039718, 1073741827), Rational(1152921471320850330, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(-1152921509975556140, 1073741827), Rational(-1152921469173366676, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(-81604377408, 1073741789), Rational(2305842940494218450, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(1152921470247109225, 1237940005850656425478454981), Rational(40802188704, 1237940005850656425478454981)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(1073741827, 1073741827))
+ assert_equal(Complex(Rational(2147483578, 1073741827), Rational(2305842940494218450, 1152921470247108503)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(0, Rational(81604377408, 1152921470247108503)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(-81604377408, 1152921511049297929), Rational(2305842940494218450, 1152921511049297929)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(1152921470247109225, 1152921429444920521), Rational(40802188704, 1152921429444920521)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(Rational(1073741789, 1073741827), Rational(1073741789, 1073741827)))
+ assert_equal(Complex(Rational(2305842940494218450, 1152921470247108503), Rational(2147483654, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-81604377408, 1152921470247108503), 0),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-81604377408, 1152921429444920521), Rational(2305842940494218450, 1152921429444920521)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(1152921470247109225, 1152921511049297929), Rational(40802188704, 1152921511049297929)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(Rational(1073741827, 1073741789), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(2147483578, 1073741827), Rational(2147483654, 1073741789)),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) + Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(0, 0),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) - Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(Rational(-188166877559662688435796777600, 1329227916556754297117581432254901009), 2),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) * Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ assert_equal(Complex(1, 0),
+ Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)) / Complex(Rational(1073741789, 1073741827), Rational(1073741827, 1073741789)))
+ end
+
+ def test_kumi2
+ assert_equal('0.0+0.0i', (+Complex(+0.0, +0.0)).to_s)
+ assert_equal('-0.0-0.0i', (-Complex(+0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) + Complex(+0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) - Complex(+0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) * Complex(+0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) + Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) - Complex(-0.0, +0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(+0.0, +0.0) * Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) + Complex(+0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) - Complex(+0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) * Complex(+0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) + Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, +0.0) - Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(+0.0, +0.0) * Complex(-0.0, -0.0)).to_s)
+ assert_equal('-0.0+0.0i', (+Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0-0.0i', (-Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, +0.0) + Complex(+0.0, +0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(-0.0, +0.0) - Complex(+0.0, +0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(-0.0, +0.0) * Complex(+0.0, +0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(-0.0, +0.0) + Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, +0.0) - Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(-0.0, +0.0) * Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, +0.0) + Complex(+0.0, -0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(-0.0, +0.0) - Complex(+0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, +0.0) * Complex(+0.0, -0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(-0.0, +0.0) + Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, +0.0) - Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, +0.0) * Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0-0.0i', (+Complex(+0.0, -0.0)).to_s)
+ assert_equal('-0.0+0.0i', (-Complex(+0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, -0.0) + Complex(+0.0, +0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(+0.0, -0.0) - Complex(+0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, -0.0) * Complex(+0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, -0.0) + Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(+0.0, -0.0) - Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, -0.0) * Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(+0.0, -0.0) + Complex(+0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, -0.0) - Complex(+0.0, -0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(+0.0, -0.0) * Complex(+0.0, -0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(+0.0, -0.0) + Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(+0.0, -0.0) - Complex(-0.0, -0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(+0.0, -0.0) * Complex(-0.0, -0.0)).to_s)
+ assert_equal('-0.0-0.0i', (+Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (-Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, -0.0) + Complex(+0.0, +0.0)).to_s)
+ assert_equal('-0.0-0.0i', (Complex(-0.0, -0.0) - Complex(+0.0, +0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(-0.0, -0.0) * Complex(+0.0, +0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(-0.0, -0.0) + Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(-0.0, -0.0) - Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, -0.0) * Complex(-0.0, +0.0)).to_s)
+ assert_equal('0.0-0.0i', (Complex(-0.0, -0.0) + Complex(+0.0, -0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(-0.0, -0.0) - Complex(+0.0, -0.0)).to_s)
+ assert_equal('-0.0+0.0i', (Complex(-0.0, -0.0) * Complex(+0.0, -0.0)).to_s)
+ assert_equal('-0.0-0.0i', (Complex(-0.0, -0.0) + Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, -0.0) - Complex(-0.0, -0.0)).to_s)
+ assert_equal('0.0+0.0i', (Complex(-0.0, -0.0) * Complex(-0.0, -0.0)).to_s)
+ end
+
+end
diff --git a/jni/ruby/test/ruby/test_complexrational.rb b/jni/ruby/test/ruby/test_complexrational.rb
new file mode 100644
index 0000000..cef4074
--- /dev/null
+++ b/jni/ruby/test/ruby/test_complexrational.rb
@@ -0,0 +1,407 @@
+require 'test/unit'
+
+class ComplexRational_Test < Test::Unit::TestCase
+
+ def test_rat_srat
+ skip unless defined?(Rational)
+
+ c = SimpleRat(1,3)
+ cc = Rational(3,2)
+
+ assert_kind_of(Numeric, c)
+ assert_kind_of(Numeric, cc)
+
+ assert_instance_of(SimpleRat, c)
+ assert_instance_of(Rational, cc)
+
+ assert_equal(SimpleRat(1,3), +c)
+ assert_equal(SimpleRat(-1,3), -c)
+
+ assert_equal(SimpleRat(7,3), c + 2)
+ assert_equal(SimpleRat(-5,3), c - 2)
+ assert_equal(SimpleRat(2,3), c * 2)
+ assert_equal(SimpleRat(1,6), c / 2)
+ assert_equal(SimpleRat(1,9), c ** 2)
+ assert_equal(-1, c <=> 2)
+
+ assert_equal(SimpleRat(7,3), 2 + c)
+ assert_equal(SimpleRat(5,3), 2 - c)
+ assert_equal(SimpleRat(2,3), 2 * c)
+ assert_equal(SimpleRat(6,1), 2 / c)
+ assert_in_delta(1.2599, 2 ** c, 0.001)
+ assert_equal(1, 2 <=> c)
+
+ assert_equal(SimpleRat(11,6), c + cc)
+ assert_equal(SimpleRat(-7,6), c - cc)
+ assert_equal(SimpleRat(1,2), c * cc)
+ assert_equal(SimpleRat(2,9), c / cc)
+ assert_in_delta(0.1924, c ** cc, 0.001)
+ assert_equal(-1, c <=> cc)
+
+ assert_equal(SimpleRat(11,6), cc + c)
+ assert_equal(SimpleRat(7,6), cc - c)
+ assert_equal(SimpleRat(1,2), cc * c)
+ assert_equal(SimpleRat(9,2), cc / c)
+ assert_in_delta(1.1447, cc ** c, 0.001)
+ assert_equal(1, cc <=> c)
+
+ assert_equal(SimpleRat, (+c).class)
+ assert_equal(SimpleRat, (-c).class)
+
+ assert_equal(SimpleRat, (c + 2).class)
+ assert_equal(SimpleRat, (c - 2).class)
+ assert_equal(SimpleRat, (c * 2).class)
+ assert_equal(SimpleRat, (c / 2).class)
+ assert_equal(SimpleRat, (c ** 2).class)
+
+ assert_equal(SimpleRat, (2 + c).class)
+ assert_equal(SimpleRat, (2 - c).class)
+ assert_equal(SimpleRat, (2 * c).class)
+ assert_equal(SimpleRat, (2 / c).class)
+ assert_equal(Float, (2 ** c).class)
+
+ assert_equal(SimpleRat, (c + cc).class)
+ assert_equal(SimpleRat, (c - cc).class)
+ assert_equal(SimpleRat, (c * cc).class)
+ assert_equal(SimpleRat, (c / cc).class)
+ assert_equal(Float, (c ** cc).class)
+
+ assert_equal(SimpleRat, (cc + c).class)
+ assert_equal(SimpleRat, (cc - c).class)
+ assert_equal(SimpleRat, (cc * c).class)
+ assert_equal(SimpleRat, (cc / c).class)
+ assert_equal(Float, (cc ** c).class)
+
+ assert_equal(0, Rational(2,3) <=> SimpleRat(2,3))
+ assert_equal(0, SimpleRat(2,3) <=> Rational(2,3))
+ assert_equal(Rational(2,3), SimpleRat(2,3))
+ assert_equal(SimpleRat(2,3), Rational(2,3))
+
+ assert_equal(SimpleRat, (c + 0).class)
+ assert_equal(SimpleRat, (c - 0).class)
+ assert_equal(SimpleRat, (c * 0).class)
+ assert_equal(SimpleRat, (c * 1).class)
+ assert_equal(SimpleRat, (0 + c).class)
+ assert_equal(SimpleRat, (0 - c).class)
+ assert_equal(SimpleRat, (0 * c).class)
+ assert_equal(SimpleRat, (1 * c).class)
+ end
+
+ def test_comp_srat
+ skip unless defined?(Rational)
+
+ c = Complex(SimpleRat(2,3),SimpleRat(1,2))
+ cc = Complex(Rational(3,2),Rational(2,1))
+
+ assert_equal(Complex(SimpleRat(2,3),SimpleRat(1,2)), +c)
+ assert_equal(Complex(SimpleRat(-2,3),SimpleRat(-1,2)), -c)
+
+ assert_equal(Complex(SimpleRat(8,3),SimpleRat(1,2)), c + 2)
+ assert_equal(Complex(SimpleRat(-4,3),SimpleRat(1,2)), c - 2)
+ assert_equal(Complex(SimpleRat(4,3),SimpleRat(1,1)), c * 2)
+ assert_equal(Complex(SimpleRat(1,3),SimpleRat(1,4)), c / 2)
+ assert_equal(Complex(SimpleRat(7,36),SimpleRat(2,3)), c ** 2)
+ assert_raise(NoMethodError){c <=> 2}
+
+ assert_equal(Complex(SimpleRat(8,3),SimpleRat(1,2)), 2 + c)
+ assert_equal(Complex(SimpleRat(4,3),SimpleRat(-1,2)), 2 - c)
+ assert_equal(Complex(SimpleRat(4,3),SimpleRat(1,1)), 2 * c)
+ assert_equal(Complex(SimpleRat(48,25),SimpleRat(-36,25)), 2 / c)
+ r = 2 ** c
+ assert_in_delta(1.4940, r.real, 0.001)
+ assert_in_delta(0.5392, r.imag, 0.001)
+ assert_raise(NoMethodError){2 <=> c}
+
+ assert_equal(Complex(SimpleRat(13,6),SimpleRat(5,2)), c + cc)
+ assert_equal(Complex(SimpleRat(-5,6),SimpleRat(-3,2)), c - cc)
+ assert_equal(Complex(SimpleRat(0,1),SimpleRat(25,12)), c * cc)
+ assert_equal(Complex(SimpleRat(8,25),SimpleRat(-7,75)), c / cc)
+ r = c ** cc
+ assert_in_delta(0.1732, r.real, 0.001)
+ assert_in_delta(0.1186, r.imag, 0.001)
+ assert_raise(NoMethodError){c <=> cc}
+
+ assert_equal(Complex(SimpleRat(13,6),SimpleRat(5,2)), cc + c)
+ assert_equal(Complex(SimpleRat(5,6),SimpleRat(3,2)), cc - c)
+ assert_equal(Complex(SimpleRat(0,1),SimpleRat(25,12)), cc * c)
+ assert_equal(Complex(SimpleRat(72,25),SimpleRat(21,25)), cc / c)
+ r = cc ** c
+ assert_in_delta(0.5498, r.real, 0.001)
+ assert_in_delta(1.0198, r.imag, 0.001)
+ assert_raise(NoMethodError){cc <=> c}
+
+ assert_equal([SimpleRat,SimpleRat],
+ (+c).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (-c).instance_eval{[real.class, imag.class]})
+
+ assert_equal([SimpleRat,SimpleRat],
+ (c + 2).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c - 2).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c * 2).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c / 2).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c ** 2).instance_eval{[real.class, imag.class]})
+
+ assert_equal([SimpleRat,SimpleRat],
+ (c + cc).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c - cc).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c * cc).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c / cc).instance_eval{[real.class, imag.class]})
+ assert_equal([Float,Float],
+ (c ** cc).instance_eval{[real.class, imag.class]})
+
+ assert_equal([SimpleRat,SimpleRat],
+ (cc + c).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (cc - c).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (cc * c).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (cc / c).instance_eval{[real.class, imag.class]})
+ assert_equal([Float,Float],
+ (cc ** c).instance_eval{[real.class, imag.class]})
+
+ assert_equal(Complex(SimpleRat(2,3),SimpleRat(3,2)),
+ Complex(Rational(2,3),Rational(3,2)))
+ assert_equal(Complex(Rational(2,3),Rational(3,2)),
+ Complex(SimpleRat(2,3),SimpleRat(3,2)))
+
+ assert_equal([SimpleRat,SimpleRat],
+ (c + 0).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c - 0).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c * 0).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (c * 1).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (0 + c).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (0 - c).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (0 * c).instance_eval{[real.class, imag.class]})
+ assert_equal([SimpleRat,SimpleRat],
+ (1 * c).instance_eval{[real.class, imag.class]})
+ end
+
+end
+
+def SimpleRat(*a) SimpleRat.new(*a) end
+
+class SimpleRat < Numeric
+
+ def initialize(num, den = 1)
+ if den == 0
+ raise ZeroDivisionError, "divided by zero"
+ end
+ if den < 0
+ num = -num
+ den = -den
+ end
+ gcd = num.gcd(den)
+ @num = num.div(gcd)
+ @den = den.div(gcd)
+ end
+
+ def numerator() @num end
+ def denominator() @den end
+
+ def +@ () self end
+ def -@ () self.class.new(-@num, @den) end
+
+ def + (o)
+ case o
+ when SimpleRat, Rational
+ a = @num * o.denominator
+ b = o.numerator * @den
+ self.class.new(a + b, @den * o.denominator)
+ when Integer
+ self + self.class.new(o)
+ when Float
+ to_f + o
+ else
+ x, y = o.coerce(self)
+ x + y
+ end
+ end
+
+ def - (o)
+ case o
+ when SimpleRat, Rational
+ a = @num * o.denominator
+ b = o.numerator * @den
+ self.class.new(a - b, @den * o.denominator)
+ when Integer
+ self - self.class.new(o)
+ when Float
+ to_f - o
+ else
+ x, y = o.coerce(self)
+ x - y
+ end
+ end
+
+ def * (o)
+ case o
+ when SimpleRat, Rational
+ a = @num * o.numerator
+ b = @den * o.denominator
+ self.class.new(a, b)
+ when Integer
+ self * self.class.new(o)
+ when Float
+ to_f * o
+ else
+ x, y = o.coerce(self)
+ x * y
+ end
+ end
+
+ def quo(o)
+ case o
+ when SimpleRat, Rational
+ a = @num * o.denominator
+ b = @den * o.numerator
+ self.class.new(a, b)
+ when Integer
+ if o == 0
+ raise raise ZeroDivisionError, "divided by zero"
+ end
+ self.quo(self.class.new(o))
+ when Float
+ to_f.quo(o)
+ else
+ x, y = o.coerce(self)
+ x.quo(y)
+ end
+ end
+
+ alias / quo
+
+ def floor
+ @num.div(@den)
+ end
+
+ def ceil
+ -((-@num).div(@den))
+ end
+
+ def truncate
+ if @num < 0
+ return -((-@num).div(@den))
+ end
+ @num.div(@den)
+ end
+
+ alias to_i truncate
+
+ def round
+ if @num < 0
+ num = -@num
+ num = num * 2 + @den
+ den = @den * 2
+ -(num.div(den))
+ else
+ num = @num * 2 + @den
+ den = @den * 2
+ num.div(den)
+ end
+ end
+
+ def div(o) (self / o).floor end
+ def quot(o) (self / o).truncate end
+
+ def modulo(o)
+ q = div(o)
+ self - o * q
+ end
+
+ def remainder(o)
+ q = quot(o)
+ self - o * q
+ end
+
+ alias % modulo
+
+ def divmod(o) [div(o), modulo(o)] end
+ def quotrem(o) [quot(o), remainder(o)] end
+
+ def ** (o)
+ case o
+ when SimpleRat, Rational
+ Float(self) ** o
+ when Integer
+ if o > 0
+ a = @num ** o
+ b = @den ** o
+ elsif o < 0
+ a = @den ** -o
+ b = @num ** -o
+ else
+ a = b = 1
+ end
+ self.class.new(a, b)
+ when Float
+ to_f ** o
+ else
+ x, y = o.coerce(self)
+ x ** y
+ end
+ end
+
+ def <=> (o)
+ case o
+ when SimpleRat, Rational
+ a = @num * o.denominator
+ b = o.numerator * @den
+ return a <=> b
+ when Integer
+ self <=> self.class.new(o)
+ when Float
+ to_f <=> o
+ else
+ x, y = o.coerce(self)
+ x <=> y
+ end
+ end
+
+ def == (o)
+ begin
+ (self <=> o) == 0
+ rescue
+ false
+ end
+ end
+
+ def coerce(o)
+ case o
+ when Rational
+ [self.class.new(o.numerator, o.denominator), self]
+ when Integer
+ [self.class.new(o), self]
+ when Float
+ [o, self.to_f]
+ else
+ super
+ end
+ end
+
+ def hash() @num.hash ^ @den.hash end
+
+ def to_f() @num.to_f / @den.to_f end
+ def to_r() self end
+ def to_s() format('%s/%s', @num, @den) end
+
+ def inspect() format('#SR(%s)', to_s) end
+
+ def marshal_dump() [@num, @den] end
+ def marshal_load(a) @num, @den = a end
+
+end
diff --git a/jni/ruby/test/ruby/test_condition.rb b/jni/ruby/test/ruby/test_condition.rb
new file mode 100644
index 0000000..ba2e068
--- /dev/null
+++ b/jni/ruby/test/ruby/test_condition.rb
@@ -0,0 +1,16 @@
+require 'test/unit'
+
+class TestCondition < Test::Unit::TestCase
+
+ # [should] first test to see if we can run the tests.
+
+ def test_condition
+ $x = '0';
+
+ $x == $x && assert(true)
+ $x != $x && assert(false)
+ $x == $x || assert(false)
+ $x != $x || assert(true)
+
+ end
+end
diff --git a/jni/ruby/test/ruby/test_const.rb b/jni/ruby/test/ruby/test_const.rb
new file mode 100644
index 0000000..892d775
--- /dev/null
+++ b/jni/ruby/test/ruby/test_const.rb
@@ -0,0 +1,72 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+
+class TestConst < Test::Unit::TestCase
+ TEST1 = 1
+ TEST2 = 2
+
+ module Const
+ TEST3 = 3
+ TEST4 = 4
+ end
+
+ module Const2
+ TEST3 = 6
+ TEST4 = 8
+ end
+
+ def test_const
+ assert defined?(TEST1)
+ assert_equal 1, TEST1
+ assert defined?(TEST2)
+ assert_equal 2, TEST2
+
+ self.class.class_eval {
+ include Const
+ }
+ assert defined?(TEST1)
+ assert_equal 1, TEST1
+ assert defined?(TEST2)
+ assert_equal 2, TEST2
+ assert defined?(TEST3)
+ assert_equal 3, TEST3
+ assert defined?(TEST4)
+ assert_equal 4, TEST4
+
+ self.class.class_eval {
+ include Const2
+ }
+ STDERR.print "intentionally redefines TEST3, TEST4\n" if $VERBOSE
+ assert defined?(TEST1)
+ assert_equal 1, TEST1
+ assert defined?(TEST2)
+ assert_equal 2, TEST2
+ assert defined?(TEST3)
+ assert_equal 6, TEST3
+ assert defined?(TEST4)
+ assert_equal 8, TEST4
+ end
+
+ def test_redefinition
+ c = Class.new
+ name = "X\u{5b9a 6570}"
+ c.const_set(name, 1)
+ prev_line = __LINE__ - 1
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ assert_warning(<<-WARNING) {c.const_set(name, 2)}
+#{__FILE__}:#{__LINE__-1}: warning: already initialized constant #{c}::#{name}
+#{__FILE__}:#{prev_line}: warning: previous definition of #{name} was here
+WARNING
+ end
+ end
+
+ def test_redefinition_memory_leak
+ code = <<-PRE
+olderr = $stderr.dup
+$stderr.reopen(File::NULL, "wb")
+350000.times { FOO = :BAR }
+$stderr.reopen(olderr)
+PRE
+ assert_no_memory_leak([], '', code, 'redefined constant', timeout: 30)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_continuation.rb b/jni/ruby/test/ruby/test_continuation.rb
new file mode 100644
index 0000000..8d57b8b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_continuation.rb
@@ -0,0 +1,134 @@
+require 'test/unit'
+EnvUtil.suppress_warning {require 'continuation'}
+require 'fiber'
+
+class TestContinuation < Test::Unit::TestCase
+ def test_create
+ assert_equal(:ok, callcc{:ok})
+ assert_equal(:ok, callcc{|c| c.call :ok})
+ end
+
+ def test_call
+ assert_equal(:ok, callcc{|c| c.call :ok})
+
+ ary = []
+ ary << callcc{|c|
+ @cont = c
+ :a
+ }
+ @cont.call :b if ary.length < 3
+ assert_equal([:a, :b, :b], ary)
+ end
+
+ def test_check_localvars
+ vv = 0
+ @v = 0
+ @ary = []
+ [1, 2, 3].each{|i|
+ callcc {|k| @k = k}
+ @v += 1
+ vv += 1
+ }
+ @ary << [vv, @v]
+ @k.call if @v < 10
+ assert_equal((3..10).map{|e| [e, e]}, @ary)
+ end
+
+ def test_error
+ cont = callcc{|c| c}
+ assert_raise(RuntimeError){
+ Thread.new{cont.call}.join
+ }
+ assert_raise(LocalJumpError){
+ callcc
+ }
+ assert_raise(RuntimeError){
+ c = nil
+ Fiber.new do
+ callcc {|c2| c = c2 }
+ end.resume
+ c.call
+ }
+ end
+
+ def test_ary_flatten
+ assert_normal_exit %q{
+ require 'continuation'
+ n = 0
+ o = Object.new
+ def o.to_ary() callcc {|k| $k = k; [1,2,3]} end
+ [10,20,o,30,o,40].flatten.inspect
+ n += 1
+ $k.call if n < 100
+ }, '[ruby-dev:34798]'
+ end
+
+ def test_marshal_dump
+ assert_normal_exit %q{
+ require 'continuation'
+ n = 0
+ o = Object.new
+ def o.marshal_dump() callcc {|k| $k = k }; "fofof" end
+ a = [1,2,3,o,4,5,6]
+ Marshal.dump(a).inspect
+ n += 1
+ $k.call if n < 100
+ }, '[ruby-dev:34802]'
+ end
+
+ def tracing_with_set_trace_func
+ orig_thread = Thread.current
+ cont = nil
+ func = lambda do |*args|
+ if orig_thread == Thread.current
+ if cont
+ @memo += 1
+ c = cont
+ cont = nil
+ c.call(nil)
+ end
+ end
+ end
+ cont = callcc { |cc| cc }
+ if cont
+ set_trace_func(func)
+ else
+ set_trace_func(nil)
+ end
+ end
+
+ def test_tracing_with_set_trace_func
+ @memo = 0
+ tracing_with_set_trace_func
+ tracing_with_set_trace_func
+ tracing_with_set_trace_func
+ assert_equal 3, @memo
+ end
+
+ def tracing_with_thread_set_trace_func
+ cont = nil
+ func = lambda do |*args|
+ if cont
+ @memo += 1
+ c = cont
+ cont = nil
+ c.call(nil)
+ end
+ end
+ cont = callcc { |cc| cc }
+ if cont
+ Thread.current.set_trace_func(func)
+ else
+ Thread.current.set_trace_func(nil)
+ end
+ end
+
+ def test_tracing_with_thread_set_trace_func
+ @memo = 0
+ tracing_with_thread_set_trace_func
+ tracing_with_thread_set_trace_func
+ tracing_with_thread_set_trace_func
+ assert_equal 3, @memo
+ end
+end
+
diff --git a/jni/ruby/test/ruby/test_defined.rb b/jni/ruby/test/ruby/test_defined.rb
new file mode 100644
index 0000000..d5e8418
--- /dev/null
+++ b/jni/ruby/test/ruby/test_defined.rb
@@ -0,0 +1,211 @@
+require 'test/unit'
+
+class TestDefined < Test::Unit::TestCase
+ class Foo
+ def foo
+ p :foo
+ end
+ protected :foo
+ def bar(f)
+ yield(defined?(self.foo))
+ yield(defined?(f.foo))
+ end
+ def baz(f)
+ end
+ attr_accessor :attr
+ def attrasgn_test
+ yield(defined?(self.attr = 1))
+ end
+ end
+
+ def defined_test
+ return !defined?(yield)
+ end
+
+ def test_defined
+ $x = nil
+
+ assert(defined?($x)) # global variable
+ assert_equal('global-variable', defined?($x))# returns description
+
+ assert_nil(defined?(foo)) # undefined
+ foo=5
+ assert(defined?(foo)) # local variable
+
+ assert(defined?(Array)) # constant
+ assert(defined?(::Array)) # toplevel constant
+ assert(defined?(File::Constants)) # nested constant
+ assert(defined?(Object.new)) # method
+ assert(defined?(Object::new)) # method
+ assert(!defined?(Object.print)) # private method
+ assert(defined?(1 == 2)) # operator expression
+
+ f = Foo.new
+ assert_nil(defined?(f.foo)) # protected method
+ f.bar(f) { |v| assert(v) }
+ assert_nil(defined?(f.quux)) # undefined method
+ assert_nil(defined?(f.baz(x))) # undefined argument
+ x = 0
+ assert(defined?(f.baz(x)))
+ assert_nil(defined?(f.quux(x)))
+ assert(defined?(print(x)))
+ assert_nil(defined?(quux(x)))
+ assert(defined?(f.attr = 1))
+ f.attrasgn_test { |v| assert(v) }
+
+ assert(defined_test) # not iterator
+ assert(!defined_test{}) # called as iterator
+
+ /a/ =~ ''
+ assert_equal nil, defined?($&)
+ assert_equal nil, defined?($`)
+ assert_equal nil, defined?($')
+ assert_equal nil, defined?($+)
+ assert_equal nil, defined?($1)
+ assert_equal nil, defined?($2)
+ /a/ =~ 'a'
+ assert_equal 'global-variable', defined?($&)
+ assert_equal 'global-variable', defined?($`)
+ assert_equal 'global-variable', defined?($') # '
+ assert_equal nil, defined?($+)
+ assert_equal nil, defined?($1)
+ assert_equal nil, defined?($2)
+ /(a)/ =~ 'a'
+ assert_equal 'global-variable', defined?($&)
+ assert_equal 'global-variable', defined?($`)
+ assert_equal 'global-variable', defined?($') # '
+ assert_equal 'global-variable', defined?($+)
+ assert_equal 'global-variable', defined?($1)
+ assert_equal nil, defined?($2)
+ /(a)b/ =~ 'ab'
+ assert_equal 'global-variable', defined?($&)
+ assert_equal 'global-variable', defined?($`)
+ assert_equal 'global-variable', defined?($') # '
+ assert_equal 'global-variable', defined?($+)
+ assert_equal 'global-variable', defined?($1)
+ assert_equal nil, defined?($2)
+
+ assert_equal("nil", defined?(nil))
+ assert_equal("true", defined?(true))
+ assert_equal("false", defined?(false))
+ assert_equal("expression", defined?(1))
+
+ bug8224 = '[ruby-core:54024] [Bug #8224]'
+ (1..3).each do |level|
+ expr = "("*level+")"*level
+ assert_equal("nil", eval("defined? #{expr}"), "#{bug8224} defined? #{expr}")
+ assert_equal("nil", eval("defined?(#{expr})"), "#{bug8224} defined?(#{expr})")
+ end
+ end
+
+ def test_defined_impl_specific
+ feature7035 = '[ruby-core:47558]' # not spec
+ assert_predicate(defined?(Foo), :frozen?, feature7035)
+ assert_same(defined?(Foo), defined?(Array), feature7035)
+ end
+
+ class TestAutoloadedSuperclass
+ autoload :A, "a"
+ end
+
+ class TestAutoloadedSubclass < TestAutoloadedSuperclass
+ def a?
+ defined?(A)
+ end
+ end
+
+ def test_autoloaded_subclass
+ bug = "[ruby-core:35509]"
+
+ x = TestAutoloadedSuperclass.new
+ class << x
+ def a?; defined?(A); end
+ end
+ assert_equal("constant", x.a?, bug)
+
+ assert_equal("constant", TestAutoloadedSubclass.new.a?, bug)
+ end
+
+ class TestAutoloadedNoload
+ autoload :A, "a"
+ def a?
+ defined?(A)
+ end
+ def b?
+ defined?(A::B)
+ end
+ end
+
+ def test_autoloaded_noload
+ loaded = $".dup
+ $".clear
+ loadpath = $:.dup
+ $:.clear
+ x = TestAutoloadedNoload.new
+ assert_equal("constant", x.a?)
+ assert_nil(x.b?)
+ assert_equal([], $")
+ ensure
+ $".replace(loaded)
+ $:.replace(loadpath)
+ end
+
+ def test_exception
+ bug5786 = '[ruby-dev:45021]'
+ assert_nil(defined?(raise("[Bug#5786]")::A), bug5786)
+ end
+
+ def test_define_method
+ bug6644 = '[ruby-core:45831]'
+ a = Class.new do
+ def self.def_f!;
+ singleton_class.send(:define_method, :f) { defined? super }
+ end
+ end
+ aa = Class.new(a)
+ a.def_f!
+ assert_nil(a.f)
+ assert_nil(aa.f)
+ aa.def_f!
+ assert_equal("super", aa.f, bug6644)
+ assert_nil(a.f, bug6644)
+ end
+
+ def test_super_in_included_method
+ c0 = Class.new do
+ def m
+ end
+ end
+ m1 = Module.new do
+ def m
+ defined?(super)
+ end
+ end
+ c = Class.new(c0) do include m1
+ def m
+ super
+ end
+ end
+ assert_equal("super", c.new.m)
+ end
+
+ def test_super_in_block
+ bug8367 = '[ruby-core:54769] [Bug #8367]'
+ c = Class.new do
+ def x; end
+ end
+
+ m = Module.new do
+ def b; yield; end
+ def x; b {return defined?(super)}; end
+ end
+
+ o = c.new
+ o.extend(m)
+ assert_equal("super", o.x, bug8367)
+ end
+
+ def test_super_toplevel
+ assert_separately([], "assert_nil(defined?(super))")
+ end
+end
diff --git a/jni/ruby/test/ruby/test_dir.rb b/jni/ruby/test/ruby/test_dir.rb
new file mode 100644
index 0000000..85fdd16
--- /dev/null
+++ b/jni/ruby/test/ruby/test_dir.rb
@@ -0,0 +1,328 @@
+require 'test/unit'
+
+require 'tmpdir'
+require 'fileutils'
+
+class TestDir < Test::Unit::TestCase
+
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ @root = File.realpath(Dir.mktmpdir('__test_dir__'))
+ @nodir = File.join(@root, "dummy")
+ for i in ?a..?z
+ if i.ord % 2 == 0
+ FileUtils.touch(File.join(@root, i))
+ else
+ FileUtils.mkdir(File.join(@root, i))
+ end
+ end
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ FileUtils.remove_entry_secure @root if File.directory?(@root)
+ end
+
+ def test_seek
+ dir = Dir.open(@root)
+ begin
+ cache = []
+ loop do
+ pos = dir.tell
+ break unless name = dir.read
+ cache << [pos, name]
+ end
+ for x,y in cache.sort_by {|z| z[0] % 3 } # shuffle
+ dir.seek(x)
+ assert_equal(y, dir.read)
+ end
+ ensure
+ dir.close
+ end
+ end
+
+ def test_nodir
+ assert_raise(Errno::ENOENT) { Dir.open(@nodir) }
+ end
+
+ def test_inspect
+ d = Dir.open(@root)
+ assert_match(/^#<Dir:#{ Regexp.quote(@root) }>$/, d.inspect)
+ assert_match(/^#<Dir:.*>$/, Dir.allocate.inspect)
+ ensure
+ d.close
+ end
+
+ def test_path
+ d = Dir.open(@root)
+ assert_equal(@root, d.path)
+ assert_nil(Dir.allocate.path)
+ ensure
+ d.close
+ end
+
+ def test_set_pos
+ d = Dir.open(@root)
+ loop do
+ i = d.pos
+ break unless x = d.read
+ d.pos = i
+ assert_equal(x, d.read)
+ end
+ ensure
+ d.close
+ end
+
+ def test_rewind
+ d = Dir.open(@root)
+ a = (0..5).map { d.read }
+ d.rewind
+ b = (0..5).map { d.read }
+ assert_equal(a, b)
+ ensure
+ d.close
+ end
+
+ def test_chdir
+ @pwd = Dir.pwd
+ @env_home = ENV["HOME"]
+ @env_logdir = ENV["LOGDIR"]
+ ENV.delete("HOME")
+ ENV.delete("LOGDIR")
+
+ assert_raise(Errno::ENOENT) { Dir.chdir(@nodir) }
+ assert_raise(ArgumentError) { Dir.chdir }
+ ENV["HOME"] = @pwd
+ Dir.chdir do
+ assert_equal(@pwd, Dir.pwd)
+ Dir.chdir(@root)
+ assert_equal(@root, Dir.pwd)
+ end
+
+ ensure
+ begin
+ Dir.chdir(@pwd)
+ rescue
+ abort("cannot return the original directory: #{ @pwd }")
+ end
+ if @env_home
+ ENV["HOME"] = @env_home
+ else
+ ENV.delete("HOME")
+ end
+ if @env_logdir
+ ENV["LOGDIR"] = @env_logdir
+ else
+ ENV.delete("LOGDIR")
+ end
+ end
+
+ def test_chroot_nodir
+ assert_raise(NotImplementedError, Errno::ENOENT, Errno::EPERM
+ ) { Dir.chroot(File.join(@nodir, "")) }
+ end
+
+ def test_close
+ d = Dir.open(@root)
+ d.close
+ assert_raise(IOError) { d.read }
+ end
+
+ def test_glob
+ assert_equal((%w(. ..) + (?a..?z).to_a).map{|f| File.join(@root, f) },
+ Dir.glob(File.join(@root, "*"), File::FNM_DOTMATCH).sort)
+ assert_equal([@root] + (?a..?z).map {|f| File.join(@root, f) }.sort,
+ Dir.glob([@root, File.join(@root, "*")]).sort)
+ assert_equal([@root] + (?a..?z).map {|f| File.join(@root, f) }.sort,
+ Dir.glob(@root + "\0\0\0" + File.join(@root, "*")).sort)
+
+ assert_equal((?a..?z).step(2).map {|f| File.join(File.join(@root, f), "") }.sort,
+ Dir.glob(File.join(@root, "*/")).sort)
+ assert_equal([File.join(@root, '//a')], Dir.glob(@root + '//a'))
+
+ FileUtils.touch(File.join(@root, "{}"))
+ assert_equal(%w({} a).map{|f| File.join(@root, f) },
+ Dir.glob(File.join(@root, '{\{\},a}')))
+ assert_equal([], Dir.glob(File.join(@root, '[')))
+ assert_equal([], Dir.glob(File.join(@root, '[a-\\')))
+
+ assert_equal([File.join(@root, "a")], Dir.glob(File.join(@root, 'a\\')))
+ assert_equal((?a..?f).map {|f| File.join(@root, f) }.sort, Dir.glob(File.join(@root, '[abc/def]')).sort)
+ end
+
+ def test_glob_recursive
+ bug6977 = '[ruby-core:47418]'
+ bug8006 = '[ruby-core:53108] [Bug #8006]'
+ Dir.chdir(@root) do
+ assert_include(Dir.glob("a/**/*", File::FNM_DOTMATCH), "a/.", bug8006)
+
+ FileUtils.mkdir_p("a/b/c/d/e/f")
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/d/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/d/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/b/c/d/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/?/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/**/d/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/**/d/e/f"), bug6977)
+
+ bug8283 = '[ruby-core:54387] [Bug #8283]'
+ dirs = ["a/.x", "a/b/.y"]
+ FileUtils.mkdir_p(dirs)
+ dirs.map {|dir| open("#{dir}/z", "w") {}}
+ assert_equal([], Dir.glob("a/**/z").sort, bug8283)
+ assert_equal(["a/.x/z"], Dir.glob("a/**/.x/z"), bug8283)
+ assert_equal(["a/.x/z"], Dir.glob("a/.x/**/z"), bug8283)
+ assert_equal(["a/b/.y/z"], Dir.glob("a/**/.y/z"), bug8283)
+ end
+ end
+
+ def test_foreach
+ assert_equal(Dir.foreach(@root).to_a.sort, %w(. ..) + (?a..?z).to_a)
+ end
+
+ def test_dir_enc
+ dir = Dir.open(@root, encoding: "UTF-8")
+ begin
+ while name = dir.read
+ assert_equal(Encoding.find("UTF-8"), name.encoding)
+ end
+ ensure
+ dir.close
+ end
+
+ dir = Dir.open(@root, encoding: "ASCII-8BIT")
+ begin
+ while name = dir.read
+ assert_equal(Encoding.find("ASCII-8BIT"), name.encoding)
+ end
+ ensure
+ dir.close
+ end
+ end
+
+ def test_unknown_keywords
+ bug8060 = '[ruby-dev:47152] [Bug #8060]'
+ assert_raise_with_message(ArgumentError, /unknown keyword/, bug8060) do
+ Dir.open(@root, xawqij: "a") {}
+ end
+ end
+
+ def test_symlink
+ begin
+ ["dummy", *?a..?z].each do |f|
+ File.symlink(File.join(@root, f),
+ File.join(@root, "symlink-#{ f }"))
+ end
+ rescue NotImplementedError
+ return
+ end
+
+ assert_equal([*?a..?z, *"symlink-a".."symlink-z"].each_slice(2).map {|f, _| File.join(@root, f + "/") }.sort,
+ Dir.glob(File.join(@root, "*/")).sort)
+
+ Dir.glob(File.join(@root, "**/"))
+ end
+
+ def test_glob_metachar
+ bug8597 = '[ruby-core:55764] [Bug #8597]'
+ assert_empty(Dir.glob(File.join(@root, "<")), bug8597)
+ end
+
+ def test_glob_cases
+ feature5994 = "[ruby-core:42469] [Feature #5994]"
+ feature5994 << "\nDir.glob should return the filename with actual cases on the filesystem"
+ Dir.chdir(File.join(@root, "a")) do
+ open("FileWithCases", "w") {}
+ return unless File.exist?("filewithcases")
+ assert_equal(%w"FileWithCases", Dir.glob("filewithcases"), feature5994)
+ end
+ Dir.chdir(@root) do
+ assert_equal(%w"a/FileWithCases", Dir.glob("A/filewithcases"), feature5994)
+ end
+ end
+
+ def test_glob_super_root
+ bug9648 = '[ruby-core:61552] [Bug #9648]'
+ roots = Dir.glob("/*")
+ assert_equal(roots.map {|n| "/..#{n}"}, Dir.glob("/../*"), bug9648)
+ end
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_glob_legacy_short_name
+ bug10819 = '[ruby-core:67954] [Bug #10819]'
+ skip unless /\A\w:/ =~ ENV["ProgramFiles"]
+ short = "#$&/PROGRA~1"
+ skip unless File.directory?(short)
+ entries = Dir.glob("#{short}/Common*")
+ assert_not_empty(entries, bug10819)
+ assert_equal(Dir.glob("#{File.expand_path(short)}/Common*"), entries, bug10819)
+ end
+ end
+
+ def test_home
+ env_home = ENV["HOME"]
+ env_logdir = ENV["LOGDIR"]
+ ENV.delete("HOME")
+ ENV.delete("LOGDIR")
+
+ assert_raise(ArgumentError) { Dir.home }
+ assert_raise(ArgumentError) { Dir.home("") }
+ ENV["HOME"] = @nodir
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(@nodir, Dir.home)
+ assert_equal(@nodir, Dir.home(""))
+ }
+ %W[no:such:user \u{7559 5b88}:\u{756a}].each do |user|
+ assert_raise_with_message(ArgumentError, /#{user}/) {Dir.home(user)}
+ end
+ ensure
+ ENV["HOME"] = env_home
+ ENV["LOGDIR"] = env_logdir
+ end
+
+ def test_symlinks_not_resolved
+ Dir.mktmpdir do |dirname|
+ Dir.chdir(dirname) do
+ begin
+ File.symlink('some-dir', 'dir-symlink')
+ rescue NotImplementedError
+ return
+ end
+
+ Dir.mkdir('some-dir')
+ File.write('some-dir/foo', 'some content')
+
+ assert_equal [ 'dir-symlink', 'some-dir' ], Dir['*'].sort
+ assert_equal [ 'dir-symlink', 'some-dir', 'some-dir/foo' ], Dir['**/*'].sort
+ end
+ end
+ end
+
+ def test_fileno
+ Dir.open(".") {|d|
+ if d.respond_to? :fileno
+ assert_kind_of(Integer, d.fileno)
+ else
+ assert_raise(NotImplementedError) { d.fileno }
+ end
+ }
+ end
+
+ def test_insecure_chdir
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE=3
+ Dir.chdir("/")
+ end.call
+ end
+ m = "\u{79fb 52d5}"
+ d = Class.new(Dir) {singleton_class.class_eval {alias_method m, :chdir}}
+ assert_raise_with_message(SecurityError, /#{m}/) do
+ proc do
+ $SAFE=3
+ d.__send__(m, "/")
+ end.call
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_dir_m17n.rb b/jni/ruby/test/ruby/test_dir_m17n.rb
new file mode 100644
index 0000000..04186e5
--- /dev/null
+++ b/jni/ruby/test/ruby/test_dir_m17n.rb
@@ -0,0 +1,389 @@
+require 'test/unit'
+require 'tmpdir'
+
+class TestDir_M17N < Test::Unit::TestCase
+ def with_tmpdir
+ Dir.mktmpdir {|dir|
+ Dir.chdir(dir) {
+ yield dir
+ }
+ }
+ end
+
+ def assert_raw_file_name(code, encoding)
+ with_tmpdir { |dir|
+ assert_separately(["-E#{encoding}"], <<-EOS, :chdir=>dir)
+ filename = #{code}.chr('UTF-8').force_encoding("#{encoding}")
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ assert_include(ents, filename)
+ EOS
+
+ assert_separately(%w[-EASCII-8BIT], <<-EOS, :chdir=>dir)
+ filename = #{code}.chr('UTF-8').force_encoding("ASCII-8BIT")
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ expected_filename = #{code}.chr('UTF-8').encode(Encoding.find("filesystem")) rescue expected_filename = "?"
+ expected_filename = expected_filename.force_encoding("ASCII-8BIT")
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ case
+ when ents.include?(filename)
+ when ents.include?(expected_filename)
+ filename = expected_filename
+ else
+ ents = Dir.entries(".", {:encoding => Encoding.find("filesystem")})
+ filename = expected_filename
+ end
+ end
+ assert_include(ents, filename)
+ EOS
+ }
+ end
+
+ ## UTF-8 default_external, no default_internal
+
+ def test_filename_extutf8
+ with_tmpdir {|d|
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
+ filename = "\u3042"
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ assert_include(ents, filename)
+ EOS
+ }
+ end
+
+ def test_filename_extutf8_invalid
+ with_tmpdir {|d|
+ assert_separately(%w[-EASCII-8BIT], <<-'EOS', :chdir=>d)
+ filename = "\xff".force_encoding("ASCII-8BIT") # invalid byte sequence as UTF-8
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ filename = "%FF" if /darwin/ =~ RUBY_PLATFORM && ents.include?("%FF")
+ assert_include(ents, filename)
+ EOS
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
+ filename = "\xff".force_encoding("UTF-8") # invalid byte sequence as UTF-8
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ filename = "%FF" if /darwin/ =~ RUBY_PLATFORM && ents.include?("%FF")
+ assert_include(ents, filename)
+ EOS
+ }
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_filename_as_bytes_extutf8
+ with_tmpdir {|d|
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
+ filename = "\xc2\xa1".force_encoding("utf-8")
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ assert_include(ents, filename)
+ EOS
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
+ if /mswin|mingw|darwin/ =~ RUBY_PLATFORM
+ filename = "\x8f\xa2\xc2".force_encoding("euc-jp")
+ else
+ filename = "\xc2\xa1".force_encoding("euc-jp")
+ end
+ assert_nothing_raised(Errno::ENOENT) do
+ open(filename) {}
+ end
+ EOS
+ # no meaning test on windows
+ unless /mswin|mingw|darwin/ =~ RUBY_PLATFORM
+ assert_separately(%W[-EUTF-8], <<-'EOS', :chdir=>d)
+ filename1 = "\xc2\xa1".force_encoding("utf-8")
+ filename2 = "\xc2\xa1".force_encoding("euc-jp")
+ filename3 = filename1.encode("euc-jp")
+ filename4 = filename2.encode("utf-8")
+ assert_file.stat(filename1)
+ assert_file.stat(filename2)
+ assert_file.not_exist?(filename3)
+ assert_file.not_exist?(filename4)
+ EOS
+ end
+ }
+ end
+
+ ## UTF-8 default_external, EUC-JP default_internal
+
+ def test_filename_extutf8_inteucjp_representable
+ with_tmpdir {|d|
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
+ filename = "\u3042"
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ assert_include(ents, filename)
+ EOS
+ assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
+ filename = "\xA4\xA2".force_encoding("euc-jp")
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ assert_include(ents, filename)
+ EOS
+ assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
+ filename = "\xA4\xA2".force_encoding("euc-jp")
+ assert_nothing_raised(Errno::ENOENT) do
+ open(filename) {}
+ end
+ EOS
+ }
+ end
+
+ def test_filename_extutf8_inteucjp_unrepresentable
+ with_tmpdir {|d|
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
+ filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP
+ filename2 = "\u3042" # HIRAGANA LETTER A which is representable in EUC-JP
+ File.open(filename1, "w") {}
+ File.open(filename2, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ assert_include(ents, filename1)
+ assert_include(ents, filename2)
+ EOS
+ assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
+ filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP
+ filename2 = "\xA4\xA2".force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ assert_include(ents, filename1)
+ assert_include(ents, filename2)
+ EOS
+ assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
+ filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP
+ filename2 = "\u3042" # HIRAGANA LETTER A which is representable in EUC-JP
+ filename3 = "\xA4\xA2".force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP
+ assert_file.stat(filename1)
+ assert_file.stat(filename2)
+ assert_file.stat(filename3)
+ EOS
+ }
+ end
+
+ ## others
+
+ def test_filename_bytes_euc_jp
+ with_tmpdir {|d|
+ assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
+ filename = "\xA4\xA2".force_encoding("euc-jp")
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ ents.each {|e| e.force_encoding("ASCII-8BIT") }
+ if /darwin/ =~ RUBY_PLATFORM
+ filename = filename.encode("utf-8")
+ end
+ assert_include(ents, filename.force_encoding("ASCII-8BIT"))
+ EOS
+ }
+ end
+
+ def test_filename_euc_jp
+ with_tmpdir {|d|
+ assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
+ filename = "\xA4\xA2".force_encoding("euc-jp")
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ if /darwin/ =~ RUBY_PLATFORM
+ filename = filename.encode("utf-8").force_encoding("euc-jp")
+ end
+ assert_include(ents, filename)
+ EOS
+ assert_separately(%w[-EASCII-8BIT], <<-'EOS', :chdir=>d)
+ filename = "\xA4\xA2".force_encoding('ASCII-8BIT')
+ win_expected_filename = filename.encode(Encoding.find("filesystem"), "euc-jp") rescue "?"
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ unless ents.include?(filename)
+ case RUBY_PLATFORM
+ when /darwin/
+ filename = filename.encode("utf-8", "euc-jp").b
+ when /mswin|mingw/
+ if ents.include?(win_expected_filename.dup.force_encoding("ASCII-8BIT"))
+ ents = Dir.entries(".", {:encoding => Encoding.find("filesystem")})
+ filename = win_expected_filename
+ end
+ end
+ end
+ assert_include(ents, filename)
+ EOS
+ }
+ end
+
+ def test_filename_utf8_raw_jp_name
+ assert_raw_file_name(0x3042, "UTF-8")
+ end
+
+ def test_filename_utf8_raw_windows_1251_name
+ assert_raw_file_name(0x0424, "UTF-8")
+ end
+
+ def test_filename_utf8_raw_windows_1252_name
+ assert_raw_file_name(0x00c6, "UTF-8")
+ end
+
+ def test_filename_ext_euc_jp_and_int_utf_8
+ with_tmpdir {|d|
+ assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
+ filename = "\xA4\xA2".force_encoding("euc-jp")
+ File.open(filename, "w") {}
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ if /darwin/ =~ RUBY_PLATFORM
+ filename = filename.encode("utf-8", "euc-jp").force_encoding("euc-jp")
+ end
+ assert_include(ents, filename)
+ EOS
+ assert_separately(%w[-EEUC-JP:UTF-8], <<-'EOS', :chdir=>d)
+ filename = "\u3042"
+ opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
+ ents = Dir.entries(".", opts)
+ if /darwin/ =~ RUBY_PLATFORM
+ filename = filename.force_encoding("euc-jp")
+ end
+ assert_include(ents, filename)
+ EOS
+ }
+ end
+
+ def test_error_nonascii
+ bug6071 = '[ruby-dev:45279]'
+ paths = ["\u{3042}".encode("sjis"), "\u{ff}".encode("iso-8859-1")]
+ encs = with_tmpdir {
+ paths.map {|path|
+ Dir.open(path) rescue $!.message.encoding
+ }
+ }
+ assert_equal(paths.map(&:encoding), encs, bug6071)
+ end
+
+ def test_inspect_nonascii
+ bug6072 = '[ruby-dev:45280]'
+ paths = ["\u{3042}".encode("sjis"), "\u{ff}".encode("iso-8859-1")]
+ encs = with_tmpdir {
+ paths.map {|path|
+ Dir.mkdir(path)
+ Dir.open(path) {|d| d.inspect.encoding}
+ }
+ }
+ assert_equal(paths.map(&:encoding), encs, bug6072)
+ end
+
+ def test_glob_incompatible
+ d = "\u{3042}\u{3044}".encode("utf-16le")
+ assert_raise(Encoding::CompatibilityError) {Dir.glob(d)}
+ m = Class.new {define_method(:to_path) {d}}
+ assert_raise(Encoding::CompatibilityError) {Dir.glob(m.new)}
+ end
+
+ def test_glob_compose
+ bug7267 = '[ruby-core:48745] [Bug #7267]'
+
+ pp = Object.new.extend(Test::Unit::Assertions)
+ def pp.mu_pp(str) #:nodoc:
+ str.dump
+ end
+
+ with_tmpdir {|d|
+ orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}"
+ orig.each {|n| open(n, "w") {}}
+ orig.each do |o|
+ n = Dir.glob("#{o[0..0]}*")[0]
+ pp.assert_equal(o, n, bug7267)
+ end
+ }
+ end
+
+ def with_enc_path
+ with_tmpdir do |d|
+ names = %W"\u{391 392 393 394 395} \u{3042 3044 3046 3048 304a}"
+ names.each do |dir|
+ EnvUtil.with_default_external(Encoding::UTF_8) do
+ Dir.mkdir(dir) rescue next
+ begin
+ yield(dir)
+ ensure
+ File.chmod(0700, dir)
+ end
+ end
+ end
+ end
+ end
+
+ def test_glob_warning_opendir
+ with_enc_path do |dir|
+ open("#{dir}/x", "w") {}
+ File.chmod(0300, dir)
+ next if File.readable?(dir)
+ assert_warning(/#{dir}/) do
+ Dir.glob("#{dir}/*")
+ end
+ end
+ end
+
+ def test_glob_warning_match_all
+ with_enc_path do |dir|
+ open("#{dir}/x", "w") {}
+ File.chmod(0000, dir)
+ next if File.readable?(dir)
+ assert_warning(/#{dir}/) do
+ Dir.glob("#{dir}/x")
+ end
+ end
+ end
+
+ def test_glob_warning_match_dir
+ with_enc_path do |dir|
+ Dir.mkdir("#{dir}/x")
+ File.chmod(0000, dir)
+ next if File.readable?(dir)
+ assert_warning(/#{dir}/) do
+ Dir.glob("#{dir}/x/")
+ end
+ end
+ end
+
+ def test_glob_escape_multibyte
+ name = "\x81\\".force_encoding(Encoding::Shift_JIS)
+ with_tmpdir do
+ open(name, "w") {} rescue next
+ match, = Dir.glob("#{name}*")
+ next unless match and match.encoding == Encoding::Shift_JIS
+ assert_equal([name], Dir.glob("\\#{name}*"))
+ end
+ end
+
+ def test_entries_compose
+ bug7267 = '[ruby-core:48745] [Bug #7267]'
+
+ pp = Object.new.extend(Test::Unit::Assertions)
+ def pp.mu_pp(ary) #:nodoc:
+ '[' << ary.map {|str| "#{str.dump}(#{str.encoding})"}.join(', ') << ']'
+ end
+
+ with_tmpdir {|d|
+ orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}"
+ orig.each {|n| open(n, "w") {}}
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ opts = {:encoding => Encoding.default_external}
+ orig.map! {|o| o.encode(Encoding.find("filesystem")) rescue o.tr("^a-z", "?")}
+ else
+ enc = Encoding.find("filesystem")
+ enc = Encoding::ASCII_8BIT if enc == Encoding::US_ASCII
+ orig.each {|o| o.force_encoding(enc) }
+ end
+ ents = Dir.entries(".", opts).reject {|n| /\A\./ =~ n}
+ ents.sort!
+ pp.assert_equal(orig, ents, bug7267)
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_econv.rb b/jni/ruby/test/ruby/test_econv.rb
new file mode 100644
index 0000000..f423ebf
--- /dev/null
+++ b/jni/ruby/test/ruby/test_econv.rb
@@ -0,0 +1,926 @@
+require 'test/unit'
+
+class TestEncodingConverter < Test::Unit::TestCase
+ def check_ec(edst, esrc, eres, dst, src, ec, off, len, opts=nil)
+ res = ec.primitive_convert(src, dst, off, len, opts)
+ assert_equal([edst.dup.force_encoding("ASCII-8BIT"),
+ esrc.dup.force_encoding("ASCII-8BIT"),
+ eres],
+ [dst.dup.force_encoding("ASCII-8BIT"),
+ src.dup.force_encoding("ASCII-8BIT"),
+ res])
+ end
+
+ def assert_econv(converted, eres, obuf_bytesize, ec, consumed, rest, opts=nil)
+ ec = Encoding::Converter.new(*ec) if Array === ec
+ i = consumed + rest
+ o = ""
+ ret = ec.primitive_convert(i, o, 0, obuf_bytesize, opts)
+ assert_equal([converted, eres, rest],
+ [o, ret, i])
+ end
+
+ def assert_errinfo(e_res, e_enc1, e_enc2, e_error_bytes, e_readagain_bytes, ec)
+ assert_equal([e_res, e_enc1, e_enc2,
+ e_error_bytes && e_error_bytes.dup.force_encoding("ASCII-8BIT"),
+ e_readagain_bytes && e_readagain_bytes.dup.force_encoding("ASCII-8BIT")],
+ ec.primitive_errinfo)
+ end
+
+ def test_s_asciicompat_encoding
+ assert_equal(Encoding::STATELESS_ISO_2022_JP, Encoding::Converter.asciicompat_encoding("ISO-2022-JP"))
+ assert_equal(Encoding::STATELESS_ISO_2022_JP, Encoding::Converter.asciicompat_encoding(Encoding::ISO_2022_JP))
+ assert_equal(Encoding::UTF_8, Encoding::Converter.asciicompat_encoding("UTF-16BE"))
+ assert_equal(Encoding::UTF_8, Encoding::Converter.asciicompat_encoding("UTF-16LE"))
+ assert_equal(Encoding::UTF_8, Encoding::Converter.asciicompat_encoding("UTF-32BE"))
+ assert_equal(Encoding::UTF_8, Encoding::Converter.asciicompat_encoding("UTF-32LE"))
+ assert_nil(Encoding::Converter.asciicompat_encoding("EUC-JP"))
+ assert_nil(Encoding::Converter.asciicompat_encoding("UTF-8"))
+ assert_nil(Encoding::Converter.asciicompat_encoding(Encoding::UTF_8))
+ assert_nil(Encoding::Converter.asciicompat_encoding("xml_attr_escape"))
+ assert_nil(Encoding::Converter.asciicompat_encoding("encoding-not-exist"))
+ end
+
+ def test_asciicompat_encoding_iso2022jp
+ acenc = Encoding::Converter.asciicompat_encoding("ISO-2022-JP")
+ str = "\e$B~~\e(B".force_encoding("iso-2022-jp")
+ str2 = str.encode(acenc)
+ str3 = str2.encode("ISO-2022-JP")
+ assert_equal(str, str3)
+ end
+
+ def test_s_new
+ assert_kind_of(Encoding::Converter, Encoding::Converter.new("UTF-8", "EUC-JP"))
+ assert_kind_of(Encoding::Converter, Encoding::Converter.new(Encoding::UTF_8, Encoding::EUC_JP))
+ end
+
+ def test_s_new_convpath
+ assert_equal([], Encoding::Converter.new([]).convpath)
+ assert_equal([[Encoding::UTF_8, Encoding::EUC_JP]],
+ Encoding::Converter.new([["UTF-8", "EUC-JP"]]).convpath)
+ assert_equal([[Encoding::UTF_8, Encoding::WINDOWS_31J]],
+ Encoding::Converter.new([["utf-8", "cp932"]]).convpath)
+ assert_equal([[Encoding::UTF_8, Encoding::EUC_JP]],
+ Encoding::Converter.new([[Encoding::UTF_8, Encoding::EUC_JP]]).convpath)
+ assert_equal([[Encoding::ISO_8859_1, Encoding::UTF_8],
+ [Encoding::UTF_8, Encoding::EUC_JP]],
+ Encoding::Converter.new([["iso-8859-1", "euc-jp"]]).convpath)
+ assert_equal([[Encoding::ISO_8859_1, Encoding::UTF_8],
+ [Encoding::UTF_8, Encoding::EUC_JP],
+ "universal_newline"],
+ Encoding::Converter.new([["iso-8859-1", "euc-jp"], "universal_newline"]).convpath)
+ assert_equal(["universal_newline",
+ [Encoding::ISO_8859_1, Encoding::UTF_8],
+ [Encoding::UTF_8, Encoding::EUC_JP],
+ "universal_newline"],
+ Encoding::Converter.new(["universal_newline", ["iso-8859-1", "euc-jp"], "universal_newline"]).convpath)
+ end
+
+ def test_s_new_fail
+ name1 = "encoding-which-is-not-exist-1"
+ name2 = "encoding-which-is-not-exist-2"
+
+ assert_raise(Encoding::ConverterNotFoundError) {
+ Encoding::Converter.new(name1, name2)
+ }
+
+ encoding_list = Encoding.list.map {|e| e.name }
+ assert_not_include(encoding_list, name1)
+ assert_not_include(encoding_list, name2)
+ end
+
+ def test_newline_converter_with_ascii_incompatible
+ assert_nothing_raised {
+ Encoding::Converter.new("UTF-8", "UTF-16BE", Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR)
+ }
+ assert_nothing_raised {
+ Encoding::Converter.new("UTF-16BE", "UTF-8", Encoding::Converter::CRLF_NEWLINE_DECORATOR)
+ }
+ assert_nothing_raised {
+ Encoding::Converter.new("UTF-16BE", "UTF-8", Encoding::Converter::CR_NEWLINE_DECORATOR)
+ }
+
+ assert_nothing_raised {
+ Encoding::Converter.new("UTF-16BE", "UTF-8", Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR)
+ }
+ assert_nothing_raised {
+ Encoding::Converter.new("UTF-8", "UTF-16BE", Encoding::Converter::CRLF_NEWLINE_DECORATOR)
+ }
+ assert_nothing_raised {
+ Encoding::Converter.new("UTF-8", "UTF-16BE", Encoding::Converter::CR_NEWLINE_DECORATOR)
+ }
+ end
+
+ def test_get_encoding
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP")
+ assert_equal(Encoding::UTF_8, ec.source_encoding)
+ assert_equal(Encoding::EUC_JP, ec.destination_encoding)
+ end
+
+ def test_result_encoding
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP")
+ dst = "".force_encoding("ASCII-8BIT")
+ assert_equal(Encoding::ASCII_8BIT, dst.encoding)
+ ec.primitive_convert("\u{3042}", dst, nil, 10)
+ assert_equal(Encoding::EUC_JP, dst.encoding)
+ end
+
+ def test_output_region
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP")
+ ec.primitive_convert(src="a", dst="b", nil, 1, :partial_input=>true)
+ assert_equal("ba", dst)
+ ec.primitive_convert(src="a", dst="b", 0, 1, :partial_input=>true)
+ assert_equal("a", dst)
+ ec.primitive_convert(src="a", dst="b", 1, 1, :partial_input=>true)
+ assert_equal("ba", dst)
+ assert_raise(ArgumentError) {
+ ec.primitive_convert(src="a", dst="b", 2, 1, :partial_input=>true)
+ }
+ assert_raise(ArgumentError) {
+ ec.primitive_convert(src="a", dst="b", -1, 1, :partial_input=>true)
+ }
+ assert_raise(ArgumentError) {
+ ec.primitive_convert(src="a", dst="b", 1, -1, :partial_input=>true)
+ }
+ end
+
+ def test_nil_source_buffer
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP")
+ ret = ec.primitive_convert(nil, "", nil, 10)
+ assert_equal(:finished, ret)
+ end
+
+ def test_nil_destination_bytesize
+ ec = Encoding::Converter.new("Shift_JIS", "UTF-8")
+ n = 10000
+ src = "\xa1".force_encoding("Shift_JIS") * n
+ ret = ec.primitive_convert(src, dst="", nil, nil)
+ assert_equal(:finished, ret)
+ assert_equal("\xEF\xBD\xA1".force_encoding("UTF-8") * n, dst)
+ end
+
+ def test_nil_destination_bytesize2
+ ec = Encoding::Converter.new("Shift_JIS", "UTF-8")
+ n = 10000
+ src = "\xa1".force_encoding("Shift_JIS") * n
+ ret = ec.primitive_convert(src, dst="")
+ assert_equal(:finished, ret)
+ assert_equal("\xEF\xBD\xA1".force_encoding("UTF-8") * n, dst)
+ end
+
+ def test_nil_destination_bytesize_with_nonnil_byteoffset
+ ec = Encoding::Converter.new("Shift_JIS", "UTF-8")
+ n = 2000
+ src = "\xa1".force_encoding("Shift_JIS") * n
+ dst = "abcd" * 2000
+ ret = ec.primitive_convert(src, dst, 3, nil)
+ assert_equal(:finished, ret)
+ assert_equal("abc" + "\xEF\xBD\xA1".force_encoding("UTF-8") * n, dst)
+ end
+
+ def test_partial_input
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP")
+ ret = ec.primitive_convert(src="", dst="", nil, 10, :partial_input=>true)
+ assert_equal(:source_buffer_empty, ret)
+ ret = ec.primitive_convert(src="", dst="", nil, 10)
+ assert_equal(:finished, ret)
+ end
+
+ def test_accumulate_dst1
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP")
+ a = ["", "abc\u{3042}def", ec, nil, 1]
+ check_ec("a", "c\u{3042}def", :destination_buffer_full, *a)
+ check_ec("ab", "\u{3042}def", :destination_buffer_full, *a)
+ check_ec("abc", "def", :destination_buffer_full, *a)
+ check_ec("abc\xA4", "def", :destination_buffer_full, *a)
+ check_ec("abc\xA4\xA2", "ef", :destination_buffer_full, *a)
+ check_ec("abc\xA4\xA2d", "f", :destination_buffer_full, *a)
+ check_ec("abc\xA4\xA2de", "", :destination_buffer_full, *a)
+ check_ec("abc\xA4\xA2def", "", :finished, *a)
+ end
+
+ def test_accumulate_dst2
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP")
+ a = ["", "abc\u{3042}def", ec, nil, 2]
+ check_ec("ab", "\u{3042}def", :destination_buffer_full, *a)
+ check_ec("abc\xA4", "def", :destination_buffer_full, *a)
+ check_ec("abc\xA4\xA2d", "f", :destination_buffer_full, *a)
+ check_ec("abc\xA4\xA2def", "", :finished, *a)
+ end
+
+ def test_eucjp_to_utf8
+ assert_econv("", :finished, 100, ["UTF-8", "EUC-JP"], "", "")
+ assert_econv("a", :finished, 100, ["UTF-8", "EUC-JP"], "a", "")
+ end
+
+ def test_iso2022jp
+ assert_econv("", :finished, 100, ["Shift_JIS", "ISO-2022-JP"], "", "")
+ end
+
+ def test_iso2022jp_encode
+ ec = Encoding::Converter.new("EUC-JP", "ISO-2022-JP")
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "a"; check_ec("a", "", :source_buffer_empty, *a)
+ src << "\xA2"; check_ec("a", "", :source_buffer_empty, *a)
+ src << "\xA4"; check_ec("a\e$B\"$", "", :source_buffer_empty, *a)
+ src << "\xA1"; check_ec("a\e$B\"$", "", :source_buffer_empty, *a)
+ src << "\xA2"; check_ec("a\e$B\"$!\"", "", :source_buffer_empty, *a)
+ src << "b"; check_ec("a\e$B\"$!\"\e(Bb", "", :source_buffer_empty, *a)
+ src << "\xA2\xA6"; check_ec("a\e$B\"$!\"\e(Bb\e$B\"&", "", :source_buffer_empty, *a)
+ a[-1] = 0; check_ec("a\e$B\"$!\"\e(Bb\e$B\"&\e(B", "", :finished, *a)
+ end
+
+ def test_iso2022jp_decode
+ ec = Encoding::Converter.new("ISO-2022-JP", "EUC-JP")
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "a"; check_ec("a", "", :source_buffer_empty, *a)
+ src << "\e"; check_ec("a", "", :source_buffer_empty, *a)
+ src << "$"; check_ec("a", "", :source_buffer_empty, *a)
+ src << "B"; check_ec("a", "", :source_buffer_empty, *a)
+ src << "\x21"; check_ec("a", "", :source_buffer_empty, *a)
+ src << "\x22"; check_ec("a\xA1\xA2", "", :source_buffer_empty, *a)
+ src << "\n"; check_ec("a\xA1\xA2", "", :invalid_byte_sequence, *a)
+ src << "\x23"; check_ec("a\xA1\xA2", "", :source_buffer_empty, *a)
+ src << "\x24"; check_ec("a\xA1\xA2\xA3\xA4", "", :source_buffer_empty, *a)
+ src << "\e"; check_ec("a\xA1\xA2\xA3\xA4", "", :source_buffer_empty, *a)
+ src << "("; check_ec("a\xA1\xA2\xA3\xA4", "", :source_buffer_empty, *a)
+ src << "B"; check_ec("a\xA1\xA2\xA3\xA4", "", :source_buffer_empty, *a)
+ src << "c"; check_ec("a\xA1\xA2\xA3\xA4c", "", :source_buffer_empty, *a)
+ src << "\n"; check_ec("a\xA1\xA2\xA3\xA4c\n","", :source_buffer_empty, *a)
+ end
+
+ def test_invalid
+ assert_econv("", :invalid_byte_sequence, 100, ["UTF-8", "EUC-JP"], "\x80", "")
+ assert_econv("a", :invalid_byte_sequence, 100, ["UTF-8", "EUC-JP"], "a\x80", "")
+ assert_econv("a", :invalid_byte_sequence, 100, ["UTF-8", "EUC-JP"], "a\x80", "\x80")
+ assert_econv("abc", :invalid_byte_sequence, 100, ["UTF-8", "EUC-JP"], "abc\xFF", "def")
+ assert_econv("abc", :invalid_byte_sequence, 100, ["Shift_JIS", "EUC-JP"], "abc\xFF", "def")
+ assert_econv("abc", :invalid_byte_sequence, 100, ["ISO-2022-JP", "EUC-JP"], "abc\xFF", "def")
+ end
+
+ def test_invalid2
+ ec = Encoding::Converter.new("Shift_JIS", "EUC-JP")
+ a = ["", "abc\xFFdef", ec, nil, 1]
+ check_ec("a", "c\xFFdef", :destination_buffer_full, *a)
+ check_ec("ab", "\xFFdef", :destination_buffer_full, *a)
+ check_ec("abc", "def", :invalid_byte_sequence, *a)
+ check_ec("abcd", "f", :destination_buffer_full, *a)
+ check_ec("abcde", "", :destination_buffer_full, *a)
+ check_ec("abcdef", "", :finished, *a)
+ end
+
+ def test_invalid3
+ ec = Encoding::Converter.new("Shift_JIS", "EUC-JP")
+ a = ["", "abc\xFFdef", ec, nil, 10]
+ check_ec("abc", "def", :invalid_byte_sequence, *a)
+ check_ec("abcdef", "", :finished, *a)
+ end
+
+ def test_invalid4
+ ec = Encoding::Converter.new("Shift_JIS", "EUC-JP")
+ a = ["", "abc\xFFdef", ec, nil, 10, :after_output=>true]
+ check_ec("a", "bc\xFFdef", :after_output, *a)
+ check_ec("ab", "c\xFFdef", :after_output, *a)
+ check_ec("abc", "\xFFdef", :after_output, *a)
+ check_ec("abc", "def", :invalid_byte_sequence, *a)
+ check_ec("abcd", "ef", :after_output, *a)
+ check_ec("abcde", "f", :after_output, *a)
+ check_ec("abcdef", "", :after_output, *a)
+ check_ec("abcdef", "", :finished, *a)
+ end
+
+ def test_invalid_utf16le
+ ec = Encoding::Converter.new("UTF-16LE", "UTF-8")
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "A"; check_ec("", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\xd8"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x01"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x02"; check_ec("A", "", :invalid_byte_sequence, *a)
+ src << "\x03"; check_ec("A\u{0201}", "", :source_buffer_empty, *a)
+ src << "\x04"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\xd8"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\xd8"; check_ec("A\u{0201}\u{0403}", "", :invalid_byte_sequence, *a)
+ src << "\x00"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\xdc"; check_ec("A\u{0201}\u{0403}\u{10000}", "", :source_buffer_empty, *a)
+ end
+
+ def test_invalid_utf16be
+ ec = Encoding::Converter.new("UTF-16BE", "UTF-8")
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "\x00"; check_ec("", "", :source_buffer_empty, *a)
+ src << "A"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\xd8"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x02"; check_ec("A", "", :invalid_byte_sequence, *a)
+ src << "\x01"; check_ec("A\u{0201}", "", :source_buffer_empty, *a)
+ src << "\x04"; check_ec("A\u{0201}", "", :source_buffer_empty, *a)
+ src << "\x03"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\xd8"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\xd8"; check_ec("A\u{0201}\u{0403}", "", :invalid_byte_sequence, *a)
+ src << "\x00"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\xdc"; check_ec("A\u{0201}\u{0403}", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A\u{0201}\u{0403}\u{10000}", "", :source_buffer_empty, *a)
+ end
+
+ def test_invalid_utf32be
+ ec = Encoding::Converter.new("UTF-32BE", "UTF-8")
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "\x00"; check_ec("", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("", "", :source_buffer_empty, *a)
+ src << "A"; check_ec("A", "", :source_buffer_empty, *a)
+
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\xdc"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :invalid_byte_sequence, *a)
+
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "B"; check_ec("AB", "", :source_buffer_empty, *a)
+
+ src << "\x00"; check_ec("AB", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("AB", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("AB", "", :source_buffer_empty, *a)
+ src << "C"; check_ec("ABC", "", :source_buffer_empty, *a)
+ end
+
+ def test_invalid_utf32le
+ ec = Encoding::Converter.new("UTF-32LE", "UTF-8")
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "A"; check_ec("", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\xdc"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :invalid_byte_sequence, *a)
+
+ src << "B"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("A", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("AB", "", :source_buffer_empty, *a)
+
+ src << "C"; check_ec("AB", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("AB", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("AB", "", :source_buffer_empty, *a)
+ src << "\x00"; check_ec("ABC", "", :source_buffer_empty, *a)
+ end
+
+ def test_errors
+ ec = Encoding::Converter.new("UTF-16BE", "EUC-JP")
+ a = ["", "\xFF\xFE\x00A\xDC\x00\x00B", ec, nil, 10]
+ check_ec("", "\x00A\xDC\x00\x00B", :undefined_conversion, *a)
+ check_ec("A", "\x00B", :invalid_byte_sequence, *a) # \xDC\x00 is invalid as UTF-16BE
+ check_ec("AB", "", :finished, *a)
+ end
+
+ def test_errors2
+ ec = Encoding::Converter.new("UTF-16BE", "EUC-JP")
+ a = ["", "\xFF\xFE\x00A\xDC\x00\x00B", ec, nil, 10, :after_output=>true]
+ check_ec("", "\x00A\xDC\x00\x00B", :undefined_conversion, *a)
+ check_ec("A", "\xDC\x00\x00B", :after_output, *a)
+ check_ec("A", "\x00B", :invalid_byte_sequence, *a)
+ check_ec("AB", "", :after_output, *a)
+ check_ec("AB", "", :finished, *a)
+ end
+
+ def test_universal_newline
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", universal_newline: true)
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "abc\r\ndef"; check_ec("abc\ndef", "", :source_buffer_empty, *a)
+ src << "ghi\njkl"; check_ec("abc\ndefghi\njkl", "", :source_buffer_empty, *a)
+ src << "mno\rpqr"; check_ec("abc\ndefghi\njklmno\npqr", "", :source_buffer_empty, *a)
+ src << "stu\r"; check_ec("abc\ndefghi\njklmno\npqrstu", "", :source_buffer_empty, *a)
+ src << "\nvwx"; check_ec("abc\ndefghi\njklmno\npqrstu\nvwx", "", :source_buffer_empty, *a)
+ src << "\nyz"; check_ec("abc\ndefghi\njklmno\npqrstu\nvwx\nyz", "", :source_buffer_empty, *a)
+ end
+
+ def test_universal_newline2
+ ec = Encoding::Converter.new("", "", universal_newline: true)
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "abc\r\ndef"; check_ec("abc\ndef", "", :source_buffer_empty, *a)
+ src << "ghi\njkl"; check_ec("abc\ndefghi\njkl", "", :source_buffer_empty, *a)
+ src << "mno\rpqr"; check_ec("abc\ndefghi\njklmno\npqr", "", :source_buffer_empty, *a)
+ src << "stu\r"; check_ec("abc\ndefghi\njklmno\npqrstu", "", :source_buffer_empty, *a)
+ src << "\nvwx"; check_ec("abc\ndefghi\njklmno\npqrstu\nvwx", "", :source_buffer_empty, *a)
+ src << "\nyz"; check_ec("abc\ndefghi\njklmno\npqrstu\nvwx\nyz", "", :source_buffer_empty, *a)
+ end
+
+ def test_universal_newline3
+ ec = Encoding::Converter.new("", "", universal_newline: true)
+ a = ["", src="", ec, nil, 50, :partial_input=>true]
+ src << "abc\r\ndef"; check_ec("abc\ndef", "", :source_buffer_empty, *a)
+ src << "ghi\njkl"; check_ec("abc\ndefghi\njkl", "", :source_buffer_empty, *a)
+ src << "mno\rpqr"; check_ec("abc\ndefghi\njklmno\npqr", "", :source_buffer_empty, *a)
+ src << "stu\r"; check_ec("abc\ndefghi\njklmno\npqrstu", "", :source_buffer_empty, *a)
+ src << "\nvwx"; check_ec("abc\ndefghi\njklmno\npqrstu\nvwx", "", :source_buffer_empty, *a)
+ src << "\nyz"; check_ec("abc\ndefghi\njklmno\npqrstu\nvwx\nyz", "", :source_buffer_empty, *a)
+ src << "\r"; check_ec("abc\ndefghi\njklmno\npqrstu\nvwx\nyz", "", :source_buffer_empty, *a)
+ a[-1] = nil
+ src << ""; check_ec("abc\ndefghi\njklmno\npqrstu\nvwx\nyz\n", "", :finished, *a)
+ end
+
+ def test_crlf_newline
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", crlf_newline: true)
+ assert_econv("abc\r\ndef", :finished, 50, ec, "abc\ndef", "")
+ end
+
+ def test_crlf_newline2
+ ec = Encoding::Converter.new("", "", crlf_newline: true)
+ assert_econv("abc\r\ndef", :finished, 50, ec, "abc\ndef", "")
+ end
+
+ def test_cr_newline
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", cr_newline: true)
+ assert_econv("abc\rdef", :finished, 50, ec, "abc\ndef", "")
+ end
+
+ def test_cr_newline2
+ ec = Encoding::Converter.new("", "", cr_newline: true)
+ assert_econv("abc\rdef", :finished, 50, ec, "abc\ndef", "")
+ end
+
+ def test_no_universal_newline1
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", universal_newline: false)
+ assert_econv("abc\r\ndef", :finished, 50, ec, "abc\r\ndef", "")
+ end
+
+ def test_no_universal_newline2
+ ec = Encoding::Converter.new("", "", universal_newline: false)
+ assert_econv("abc\r\ndef", :finished, 50, ec, "abc\r\ndef", "")
+ end
+
+ def test_after_output
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP")
+ a = ["", "abc\u{3042}def", ec, nil, 100, :after_output=>true]
+ check_ec("a", "bc\u{3042}def", :after_output, *a)
+ check_ec("ab", "c\u{3042}def", :after_output, *a)
+ check_ec("abc", "\u{3042}def", :after_output, *a)
+ check_ec("abc\xA4\xA2", "def", :after_output, *a)
+ check_ec("abc\xA4\xA2d", "ef", :after_output, *a)
+ check_ec("abc\xA4\xA2de", "f", :after_output, *a)
+ check_ec("abc\xA4\xA2def", "", :after_output, *a)
+ check_ec("abc\xA4\xA2def", "", :finished, *a)
+ end
+
+ def test_errinfo_invalid_euc_jp
+ ec = Encoding::Converter.new("EUC-JP", "Shift_JIS")
+ ec.primitive_convert("\xff", "", nil, 10)
+ assert_errinfo(:invalid_byte_sequence, "EUC-JP", "Shift_JIS", "\xFF", "", ec)
+ end
+
+ def test_errinfo_invalid_euc_jp2
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xff", "", nil, 10)
+ assert_errinfo(:invalid_byte_sequence, "EUC-JP", "UTF-8", "\xFF", "", ec)
+ end
+
+ def test_errinfo_undefined_hiragana
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xa4\xa2", "", nil, 10)
+ assert_errinfo(:undefined_conversion, "UTF-8", "ISO-8859-1", "\xE3\x81\x82", "", ec)
+ end
+
+ def test_errinfo_invalid_partial_character
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xa4", "", nil, 10)
+ assert_errinfo(:incomplete_input, "EUC-JP", "UTF-8", "\xA4", "", ec)
+ end
+
+ def test_errinfo_valid_partial_character
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ec.primitive_convert("\xa4", "", nil, 10, :partial_input=>true)
+ assert_errinfo(:source_buffer_empty, nil, nil, nil, nil, ec)
+ end
+
+ def test_errinfo_invalid_utf16be
+ ec = Encoding::Converter.new("UTF-16BE", "UTF-8")
+ ec.primitive_convert(src="\xd8\x00\x00@", "", nil, 10)
+ assert_errinfo(:invalid_byte_sequence, "UTF-16BE", "UTF-8", "\xD8\x00", "\x00", ec)
+ assert_equal("@", src)
+ end
+
+ def test_errinfo_invalid_utf16le
+ ec = Encoding::Converter.new("UTF-16LE", "UTF-8")
+ ec.primitive_convert(src="\x00\xd8@\x00", "", nil, 10)
+ assert_errinfo(:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "@\x00", ec)
+ assert_equal("", src)
+ end
+
+ def test_output_iso2022jp
+ ec = Encoding::Converter.new("EUC-JP", "ISO-2022-JP")
+ ec.primitive_convert(src="\xa1\xa1", dst="", nil, 10, :partial_input=>true)
+ assert_equal("\e$B!!".force_encoding("ISO-2022-JP"), dst)
+ assert_equal(nil, ec.insert_output("???"))
+ ec.primitive_convert("", dst, nil, 10, :partial_input=>true)
+ assert_equal("\e$B!!\e(B???".force_encoding("ISO-2022-JP"), dst)
+ ec.primitive_convert(src="\xa1\xa2", dst, nil, 10, :partial_input=>true)
+ assert_equal("\e$B!!\e(B???\e$B!\"".force_encoding("ISO-2022-JP"), dst)
+
+ assert_equal(nil, ec.insert_output("\xA1\xA1".force_encoding("EUC-JP")))
+ ec.primitive_convert("", dst, nil, 10, :partial_input=>true)
+ assert_equal("\e$B!!\e(B???\e$B!\"!!".force_encoding("ISO-2022-JP"), dst)
+
+ ec.primitive_convert(src="\xa1\xa3", dst, nil, 10, :partial_input=>true)
+ assert_equal("\e$B!!\e(B???\e$B!\"!!!\#".force_encoding("ISO-2022-JP"), dst)
+
+ assert_equal(nil, ec.insert_output("\u3042"))
+ ec.primitive_convert("", dst, nil, 10, :partial_input=>true)
+ assert_equal("\e$B!!\e(B???\e$B!\"!!!\#$\"".force_encoding("ISO-2022-JP"), dst)
+
+ assert_raise(Encoding::UndefinedConversionError) {
+ ec.insert_output("\uFFFD")
+ }
+
+ assert_equal("\e$B!!\e(B???\e$B!\"!!!\#$\"".force_encoding("ISO-2022-JP"), dst)
+
+ ec.primitive_convert("", dst, nil, 10)
+ assert_equal("\e$B!!\e(B???\e$B!\"!!!\#$\"\e(B".force_encoding("ISO-2022-JP"), dst)
+ end
+
+ def test_exc_invalid
+ err = assert_raise(Encoding::InvalidByteSequenceError) {
+ "abc\xa4def".encode("ISO-8859-1", "EUC-JP")
+ }
+ assert_equal("EUC-JP", err.source_encoding_name)
+ assert_equal("UTF-8", err.destination_encoding_name)
+ assert_equal(Encoding::EUC_JP, err.source_encoding)
+ assert_equal(Encoding::UTF_8, err.destination_encoding)
+ assert_equal("\xA4".force_encoding("ASCII-8BIT"), err.error_bytes)
+ assert_equal("d", err.readagain_bytes)
+ assert_equal(false, err.incomplete_input?)
+ end
+
+ def test_exc_incomplete
+ err = assert_raise(Encoding::InvalidByteSequenceError) {
+ "abc\xa4".encode("ISO-8859-1", "EUC-JP")
+ }
+ assert_equal("EUC-JP", err.source_encoding_name)
+ assert_equal("UTF-8", err.destination_encoding_name)
+ assert_equal(Encoding::EUC_JP, err.source_encoding)
+ assert_equal(Encoding::UTF_8, err.destination_encoding)
+ assert_equal("\xA4".force_encoding("ASCII-8BIT"), err.error_bytes)
+ assert_equal(nil, err.readagain_bytes)
+ assert_equal(true, err.incomplete_input?)
+ end
+
+ def test_exc_undef
+ err = assert_raise(Encoding::UndefinedConversionError) {
+ "abc\xa4\xa2def".encode("ISO-8859-1", "EUC-JP")
+ }
+ assert_equal("UTF-8", err.source_encoding_name)
+ assert_equal("ISO-8859-1", err.destination_encoding_name)
+ assert_equal(Encoding::UTF_8, err.source_encoding)
+ assert_equal(Encoding::ISO_8859_1, err.destination_encoding)
+ assert_equal("\u{3042}", err.error_char)
+ end
+
+ def test_putback
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
+ ret = ec.primitive_convert(src="abc\xa1def", dst="", nil, 10)
+ assert_equal(:invalid_byte_sequence, ret)
+ assert_equal(["abc", "ef"], [dst, src])
+ src = ec.putback + src
+ assert_equal(["abc", "def"], [dst, src])
+ ret = ec.primitive_convert(src, dst, nil, 10)
+ assert_equal(:finished, ret)
+ assert_equal(["abcdef", ""], [dst, src])
+ end
+
+ def test_putback2
+ ec = Encoding::Converter.new("utf-16le", "euc-jp")
+ ret = ec.primitive_convert("\x00\xd8\x21\x00", "", nil, nil)
+ assert_equal(:invalid_byte_sequence, ret)
+ assert_equal("\x00".force_encoding("utf-16le"), ec.putback(1))
+ assert_equal("\x21".force_encoding("utf-16le"), ec.putback(1))
+ assert_equal("", ec.putback(1))
+ end
+
+ def test_invalid_replace
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", invalid: :replace)
+ ret = ec.primitive_convert(src="abc\x80def", dst="", nil, 100)
+ assert_equal(:finished, ret)
+ assert_equal("", src)
+ assert_equal("abc?def", dst)
+ end
+
+ def test_invalid_ignore
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", :invalid => :replace, :replace => "")
+ ret = ec.primitive_convert(src="abc\x80def", dst="", nil, 100)
+ assert_equal(:finished, ret)
+ assert_equal("", src)
+ assert_equal("abcdef", dst)
+ end
+
+ def test_undef_replace
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", :undef => :replace)
+ ret = ec.primitive_convert(src="abc\u{fffd}def", dst="", nil, 100)
+ assert_equal(:finished, ret)
+ assert_equal("", src)
+ assert_equal("abc?def", dst)
+ end
+
+ def test_undef_ignore
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", :undef => :replace, :replace => "")
+ ret = ec.primitive_convert(src="abc\u{fffd}def", dst="", nil, 100)
+ assert_equal(:finished, ret)
+ assert_equal("", src)
+ assert_equal("abcdef", dst)
+ end
+
+ def test_noconv
+ ec = Encoding::Converter.new("", "")
+ assert_equal(nil, ec.source_encoding)
+ assert_equal(nil, ec.destination_encoding)
+ assert_equal([:source_buffer_empty, nil, nil, nil, nil], ec.primitive_errinfo)
+ a = ["", "abcdefg", ec, nil, 2]
+ check_ec("ab", "cdefg", :destination_buffer_full, *a)
+ check_ec("abcd", "efg", :destination_buffer_full, *a)
+ check_ec("abcdef", "g", :destination_buffer_full, *a)
+ check_ec("abcdefg", "", :finished, *a)
+ end
+
+ def test_noconv_partial
+ ec = Encoding::Converter.new("", "")
+ a = ["", "abcdefg", ec, nil, 2, :partial_input=>true]
+ check_ec("ab", "cdefg", :destination_buffer_full, *a)
+ check_ec("abcd", "efg", :destination_buffer_full, *a)
+ check_ec("abcdef", "g", :destination_buffer_full, *a)
+ check_ec("abcdefg", "", :source_buffer_empty, *a)
+ end
+
+ def test_noconv_after_output
+ ec = Encoding::Converter.new("", "")
+ a = ["", "abcdefg", ec, nil, 2, :after_output=>true]
+ check_ec("a", "bcdefg", :after_output, *a)
+ check_ec("ab", "cdefg", :after_output, *a)
+ check_ec("abc", "defg", :after_output, *a)
+ check_ec("abcd", "efg", :after_output, *a)
+ check_ec("abcde", "fg", :after_output, *a)
+ check_ec("abcdef", "g", :after_output, *a)
+ check_ec("abcdefg", "", :after_output, *a)
+ check_ec("abcdefg", "", :finished, *a)
+ end
+
+ def test_noconv_insert_output
+ ec = Encoding::Converter.new("", "")
+ ec.insert_output("xyz")
+ ret = ec.primitive_convert(src="abc", dst="", nil, 20)
+ assert_equal(:finished, ret)
+ assert_equal(["xyzabc", ""], [dst, src])
+ end
+
+ def test_convert
+ ec = Encoding::Converter.new("utf-8", "euc-jp")
+ assert_raise(Encoding::InvalidByteSequenceError) { ec.convert("a\x80") }
+ assert_raise(Encoding::UndefinedConversionError) { ec.convert("\ufffd") }
+ ret = ec.primitive_convert(nil, "", nil, nil)
+ assert_equal(:finished, ret)
+ assert_raise(ArgumentError) { ec.convert("a") }
+ end
+
+ def test_finish_iso2022jp
+ ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
+ assert_equal("\e$B$\"".force_encoding("iso-2022-jp"), ec.convert("\u3042"))
+ assert_equal("\e(B".force_encoding("iso-2022-jp"), ec.finish)
+
+ end
+
+ def test_finish_incomplete_error
+ ec = Encoding::Converter.new("utf-8", "euc-jp")
+ ec.convert("\xEF")
+ assert_raise(Encoding::InvalidByteSequenceError) { ec.finish }
+ end
+
+ def test_last_error1
+ ec = Encoding::Converter.new("sjis", "euc-jp")
+ assert_equal(nil, ec.last_error)
+ assert_equal(:incomplete_input, ec.primitive_convert("fo\x81", "", nil, nil))
+ assert_kind_of(Encoding::InvalidByteSequenceError, ec.last_error)
+ end
+
+ def test_last_error2
+ ec = Encoding::Converter.new("sjis", "euc-jp")
+ assert_equal("fo", ec.convert("fo\x81"))
+ assert_raise(Encoding::InvalidByteSequenceError) { ec.finish }
+ assert_kind_of(Encoding::InvalidByteSequenceError, ec.last_error)
+ end
+
+ def test_us_ascii
+ ec = Encoding::Converter.new("UTF-8", "US-ASCII")
+ ec.primitive_convert("\u{3042}", "")
+ err = ec.last_error
+ assert_kind_of(Encoding::UndefinedConversionError, err)
+ assert_equal("\u{3042}", err.error_char)
+ end
+
+ def test_88591
+ ec = Encoding::Converter.new("UTF-8", "ISO-8859-1")
+ ec.primitive_convert("\u{3042}", "")
+ err = ec.last_error
+ assert_kind_of(Encoding::UndefinedConversionError, err)
+ assert_equal("\u{3042}", err.error_char)
+ end
+
+ def test_get_replacement
+ ec = Encoding::Converter.new("euc-jp", "iso-8859-1")
+ assert_equal("?", ec.replacement)
+
+ ec = Encoding::Converter.new("euc-jp", "utf-8")
+ assert_equal("\uFFFD", ec.replacement)
+ end
+
+ def test_set_replacement
+ ec = Encoding::Converter.new("utf-8", "us-ascii", :undef => :replace)
+ ec.replacement = "<undef>"
+ assert_equal("a <undef> b", ec.convert("a \u3042 b"))
+ end
+
+ def test_econv_new_hash
+ ec = Encoding::Converter.new("utf-8", "us-ascii", :undef => :replace)
+ assert_equal("a ? b", ec.convert("a \u3042 b"))
+ ec = Encoding::Converter.new("utf-8", "us-ascii", :undef => :replace, :replace => "X")
+ assert_equal("a X b", ec.convert("a \u3042 b"))
+ end
+
+ def test_hex_charref
+ ec = Encoding::Converter.new("UTF-8", "US-ASCII", Encoding::Converter::UNDEF_HEX_CHARREF)
+ assert_equal("&#x3042;", ec.convert("\u3042"))
+
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", Encoding::Converter::UNDEF_HEX_CHARREF)
+ assert_equal("\xa4\xcf\xa4\xa1\xa4\xa4&#x2665;\xa1\xa3".force_encoding("euc-jp"),
+ ec.convert("\u{306f 3041 3044 2665 3002}"))
+
+ ec = Encoding::Converter.new("UTF-8", "ISO-2022-JP", Encoding::Converter::UNDEF_HEX_CHARREF)
+ assert_equal("\e$B$O$!$$\e(B&#x2665;\e$B!#".force_encoding("ISO-2022-JP"),
+ ec.convert("\u{306f 3041 3044 2665 3002}"))
+ assert_equal("\e(B".force_encoding("ISO-2022-JP"),
+ ec.finish)
+
+ ec = Encoding::Converter.new("EUC-JP", "US-ASCII", Encoding::Converter::UNDEF_HEX_CHARREF)
+ assert_equal("&#x4EA4;&#x63DB;&#x6CD5;&#x5247;: n&#xD7;m=m&#xD7;n".force_encoding("ISO-8859-1"),
+ ec.convert("\xB8\xF2\xB4\xB9\xCB\xA1\xC2\xA7: n\xA1\xDFm=m\xA1\xDFn"))
+
+ ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1", Encoding::Converter::UNDEF_HEX_CHARREF)
+ assert_equal("&#x4EA4;&#x63DB;&#x6CD5;&#x5247;: n\xD7m=m\xD7n".force_encoding("ISO-8859-1"),
+ ec.convert("\xB8\xF2\xB4\xB9\xCB\xA1\xC2\xA7: n\xA1\xDFm=m\xA1\xDFn"))
+
+ ec = Encoding::Converter.new("UTF-8", "US-ASCII", Encoding::Converter::UNDEF_HEX_CHARREF)
+ assert_equal("&", ec.convert("&"))
+ end
+
+ def test_xml_escape_text
+ ec = Encoding::Converter.new("", "amp_escape")
+ assert_equal('&amp;<>"', ec.convert("&<>\""))
+ assert_equal('', ec.finish)
+
+ ec = Encoding::Converter.new("", "xml_text_escape")
+ assert_equal('&amp;&lt;&gt;"', ec.convert("&<>\""))
+ assert_equal('', ec.finish)
+ end
+
+ def test_xml_escape_attr_content
+ ec = Encoding::Converter.new("", "xml_attr_content_escape")
+ assert_equal('', ec.finish)
+
+ ec = Encoding::Converter.new("", "xml_attr_content_escape")
+ assert_equal('', ec.convert(""))
+ assert_equal('', ec.finish)
+
+ ec = Encoding::Converter.new("", "xml_attr_content_escape")
+ assert_equal('&quot;', ec.convert('"'))
+ assert_equal('', ec.finish)
+
+ ec = Encoding::Converter.new("", "xml_attr_content_escape")
+ assert_equal('&amp;&lt;&gt;&quot;', ec.convert("&<>\""))
+ assert_equal('', ec.finish)
+ end
+
+ def test_xml_escape_attr_quote
+ ec = Encoding::Converter.new("", "xml_attr_quote")
+ assert_equal('""', ec.finish)
+
+ ec = Encoding::Converter.new("", "xml_attr_quote")
+ assert_equal('', ec.convert(""))
+ assert_equal('""', ec.finish)
+
+ ec = Encoding::Converter.new("", "xml_attr_quote")
+ assert_equal('""', ec.convert('"'))
+ assert_equal('"', ec.finish)
+
+ ec = Encoding::Converter.new("", "xml_attr_quote")
+ assert_equal('"&<>"', ec.convert("&<>\""))
+ assert_equal('"', ec.finish)
+ end
+
+ def test_xml_escape_with_charref
+ ec = Encoding::Converter.new("utf-8", "euc-jp", Encoding::Converter::XML_TEXT_DECORATOR|Encoding::Converter::UNDEF_HEX_CHARREF)
+ assert_equal('&lt;&#x2665;&gt;&amp;"&#x2661;"', ec.convert("<\u2665>&\"\u2661\""))
+ assert_equal('', ec.finish)
+
+ ec = Encoding::Converter.new("utf-8", "euc-jp",
+ Encoding::Converter::XML_ATTR_CONTENT_DECORATOR|
+ Encoding::Converter::XML_ATTR_QUOTE_DECORATOR|
+ Encoding::Converter::UNDEF_HEX_CHARREF)
+ assert_equal('"&lt;&#x2665;&gt;&amp;&quot;&#x2661;&quot;', ec.convert("<\u2665>&\"\u2661\""))
+ assert_equal('"', ec.finish)
+
+ ec = Encoding::Converter.new("utf-8", "iso-2022-jp", Encoding::Converter::XML_TEXT_DECORATOR)
+ assert_equal("&amp;\e$B$&\e(B&amp;".force_encoding("iso-2022-jp"), ec.convert("&\u3046&"))
+ assert_equal('', ec.finish)
+ end
+
+ def test_xml_hasharg
+ assert_equal("&amp;\e$B$&\e(B&#x2665;&amp;\"'".force_encoding("iso-2022-jp"),
+ "&\u3046\u2665&\"'".encode("iso-2022-jp", xml: :text))
+ assert_equal("\"&amp;\e$B$&\e(B&#x2661;&amp;&quot;'\"".force_encoding("iso-2022-jp"),
+ "&\u3046\u2661&\"'".encode("iso-2022-jp", xml: :attr))
+
+ assert_equal("&amp;\u3046\u2661&amp;\"'".force_encoding("utf-8"),
+ "&\u3046\u2661&\"'".encode("utf-8", xml: :text))
+ end
+
+ def test_iso2022jp_invalid_replace
+ assert_equal("?x".force_encoding("iso-2022-jp"),
+ "\222\xA1x".encode("iso-2022-jp", "stateless-iso-2022-jp", :invalid => :replace))
+ end
+
+ def test_convpath
+ eucjp = Encoding::EUC_JP
+ utf8 = Encoding::UTF_8
+ utf16be = Encoding::UTF_16BE
+ utf16le = Encoding::UTF_16LE
+ iso88591 = Encoding::ISO_8859_1
+ iso2022jp = Encoding::ISO_2022_JP
+ siso2022jp = Encoding::STATELESS_ISO_2022_JP
+
+ assert_equal([], Encoding::Converter.new("", "").convpath)
+ assert_equal([[eucjp, utf8], [utf8, iso88591]],
+ Encoding::Converter.new(eucjp, iso88591).convpath)
+ assert_equal([[eucjp, siso2022jp], [siso2022jp, iso2022jp]],
+ Encoding::Converter.new(eucjp, iso2022jp).convpath)
+ assert_equal([[iso2022jp, siso2022jp],
+ [siso2022jp, eucjp],
+ [eucjp, utf8],
+ [utf8, iso88591]],
+ Encoding::Converter.new(iso2022jp, iso88591).convpath)
+ assert_equal(["universal_newline", [utf8, utf16be]],
+ Encoding::Converter.new(utf8, utf16be, universal_newline: true).convpath)
+ assert_equal([[utf16be, utf8], "universal_newline"],
+ Encoding::Converter.new(utf16be, utf8, universal_newline: true).convpath)
+ assert_equal([[utf16be, utf8], "universal_newline", [utf8, utf16le]],
+ Encoding::Converter.new(utf16be, utf16le, universal_newline: true).convpath)
+ end
+
+ def test_search_convpath
+ eucjp = Encoding::EUC_JP
+ utf8 = Encoding::UTF_8
+ utf32be = Encoding::UTF_32BE
+ iso88591 = Encoding::ISO_8859_1
+ assert_equal([[iso88591,utf8], [utf8,eucjp]],
+ Encoding::Converter.search_convpath("ISO-8859-1", "EUC-JP"))
+ assert_equal([[iso88591,utf8], [utf8,eucjp]],
+ Encoding::Converter.search_convpath(iso88591, eucjp))
+ assert_equal([[iso88591,utf8], [utf8,eucjp], "universal_newline"],
+ Encoding::Converter.search_convpath("ISO-8859-1", "EUC-JP", universal_newline: true))
+ assert_equal([[iso88591,utf8], "universal_newline", [utf8,utf32be]],
+ Encoding::Converter.search_convpath("ISO-8859-1", "UTF-32BE", universal_newline: true))
+ end
+
+ def test_invalid_replace2
+ assert_raise(ArgumentError) {
+ broken = "\x80".force_encoding("euc-jp")
+ "".encode("euc-jp", :undef => :replace, :replace => broken)
+ }
+ end
+
+ def test_newline_option
+ ec1 = Encoding::Converter.new("", "", universal_newline: true)
+ ec2 = Encoding::Converter.new("", "", newline: :universal)
+ assert_equal(ec1, ec2)
+ assert_raise_with_message(ArgumentError, /\u{3042}/) {
+ Encoding::Converter.new("", "", newline: "\u{3042}".to_sym)
+ }
+ end
+
+ def test_default_external
+ Encoding.list.grep(->(enc) {/\AISO-8859-\d+\z/i =~ enc.name}) do |enc|
+ assert_separately(%W[--disable=gems -d - #{enc.name}], <<-EOS, ignore_stderr: true)
+ Encoding.default_external = ext = ARGV[0]
+ Encoding.default_internal = int ='utf-8'
+ assert_nothing_raised do
+ Encoding::Converter.new(ext, int)
+ end
+ EOS
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_encoding.rb b/jni/ruby/test/ruby/test_encoding.rb
new file mode 100644
index 0000000..63c56e2
--- /dev/null
+++ b/jni/ruby/test/ruby/test_encoding.rb
@@ -0,0 +1,123 @@
+require 'test/unit'
+
+class TestEncoding < Test::Unit::TestCase
+
+ # Test basic encoding methods: list, find, name
+ def test_encoding
+ encodings = Encoding.list
+ assert_equal(encodings.empty?, false)
+
+ encodings.each do |e|
+ assert_equal(e, Encoding.find(e.name))
+ assert_equal(e, Encoding.find(e.name.upcase))
+ assert_equal(e, Encoding.find(e.name.capitalize))
+ assert_equal(e, Encoding.find(e.name.downcase))
+ assert_equal(e, Encoding.find(e))
+ end
+ end
+
+ def test_enc_names
+ aliases = Encoding.aliases
+ aliases.each do |a, en|
+ e = Encoding.find(a)
+ assert_equal(e.name, en)
+ assert_include(e.names, a)
+ end
+ end
+
+ # Test that Encoding objects can't be copied
+ # And that they can be compared by object_id
+ def test_singleton
+ encodings = Encoding.list
+ encodings.each do |e|
+ assert_raise(TypeError) { e.dup }
+ assert_raise(TypeError) { e.clone }
+ assert_equal(e.object_id, Marshal.load(Marshal.dump(e)).object_id)
+ end
+ end
+
+ def test_find
+ assert_raise(ArgumentError) { Encoding.find("foobarbazqux") }
+ assert_nothing_raised{Encoding.find("locale")}
+ assert_nothing_raised{Encoding.find("filesystem")}
+
+ if /(?:ms|dar)win|mingw/ !~ RUBY_PLATFORM
+ # Unix's filesystem encoding is default_external
+ assert_ruby_status(%w[-EUTF-8:EUC-JP], <<-'EOS')
+ exit Encoding.find("filesystem") == Encoding::UTF_8
+ Encoding.default_external = Encoding::EUC_JP
+ exit Encoding.find("filesystem") == Encoding::EUC_JP
+ EOS
+ end
+
+ bug5150 = '[ruby-dev:44327]'
+ assert_raise(TypeError, bug5150) {Encoding.find(1)}
+ end
+
+ def test_replicate
+ assert_instance_of(Encoding, Encoding::UTF_8.replicate('UTF-8-ANOTHER'))
+ assert_instance_of(Encoding, Encoding::ISO_2022_JP.replicate('ISO-2022-JP-ANOTHER'))
+ bug3127 = '[ruby-dev:40954]'
+ assert_raise(TypeError, bug3127) {Encoding::UTF_8.replicate(0)}
+ assert_raise(ArgumentError, bug3127) {Encoding::UTF_8.replicate("\0")}
+ end
+
+ def test_dummy_p
+ assert_equal(true, Encoding::ISO_2022_JP.dummy?)
+ assert_equal(false, Encoding::UTF_8.dummy?)
+ end
+
+ def test_ascii_compatible_p
+ assert_equal(true, Encoding::ASCII_8BIT.ascii_compatible?)
+ assert_equal(true, Encoding::UTF_8.ascii_compatible?)
+ assert_equal(false, Encoding::UTF_16BE.ascii_compatible?)
+ assert_equal(false, Encoding::ISO_2022_JP.ascii_compatible?)
+ end
+
+ def test_name_list
+ assert_instance_of(Array, Encoding.name_list)
+ Encoding.name_list.each do |x|
+ assert_instance_of(String, x)
+ end
+ end
+
+ def test_aliases
+ assert_instance_of(Hash, Encoding.aliases)
+ Encoding.aliases.each do |k, v|
+ assert_include(Encoding.name_list, k)
+ assert_include(Encoding.name_list, v)
+ assert_instance_of(String, k)
+ assert_instance_of(String, v)
+ end
+ end
+
+ def test_marshal
+ str = "".force_encoding("EUC-JP")
+ str2 = Marshal.load(Marshal.dump(str))
+ assert_equal(str, str2)
+ str2 = Marshal.load(Marshal.dump(str2))
+ assert_equal(str, str2, '[ruby-dev:38596]')
+ end
+
+ def test_compatible_p
+ ua = "abc".force_encoding(Encoding::UTF_8)
+ assert_equal(Encoding::UTF_8, Encoding.compatible?(ua, :abc))
+ assert_equal(nil, Encoding.compatible?(ua, 1))
+ bin = "a".force_encoding(Encoding::ASCII_8BIT)
+ asc = "b".force_encoding(Encoding::US_ASCII)
+ assert_equal(Encoding::ASCII_8BIT, Encoding.compatible?(bin, asc))
+ bin = "\xff".force_encoding(Encoding::ASCII_8BIT).to_sym
+ asc = "b".force_encoding(Encoding::ASCII_8BIT)
+ assert_equal(Encoding::ASCII_8BIT, Encoding.compatible?(bin, asc))
+ assert_equal(Encoding::UTF_8, Encoding.compatible?("\u{3042}".to_sym, ua.to_sym))
+ end
+
+ def test_errinfo_after_autoload
+ bug9038 = '[ruby-core:57949] [Bug #9038]'
+ assert_separately(%w[--disable=gems], <<-"end;")
+ assert_raise_with_message(SyntaxError, /unknown regexp option - Q/, #{bug9038.dump}) {
+ eval("/regexp/sQ")
+ }
+ end;
+ end
+end
diff --git a/jni/ruby/test/ruby/test_enum.rb b/jni/ruby/test/ruby/test_enum.rb
new file mode 100644
index 0000000..53e24fc
--- /dev/null
+++ b/jni/ruby/test/ruby/test_enum.rb
@@ -0,0 +1,733 @@
+require 'test/unit'
+EnvUtil.suppress_warning {require 'continuation'}
+require 'stringio'
+
+class TestEnumerable < Test::Unit::TestCase
+ def setup
+ @obj = Object.new
+ class << @obj
+ include Enumerable
+ def each
+ yield 1
+ yield 2
+ yield 3
+ yield 1
+ yield 2
+ self
+ end
+ end
+ @empty = Object.new
+ class << @empty
+ attr_reader :block
+ include Enumerable
+ def each(&block)
+ @block = block
+ self
+ end
+ end
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def assert_not_warn
+ begin
+ org_stderr = $stderr
+ v = $VERBOSE
+ $stderr = StringIO.new(warn = '')
+ $VERBOSE = true
+ yield
+ ensure
+ $stderr = org_stderr
+ $VERBOSE = v
+ end
+ assert_equal("", warn)
+ end
+
+ def test_grep
+ assert_equal([1, 2, 1, 2], @obj.grep(1..2))
+ a = []
+ @obj.grep(2) {|x| a << x }
+ assert_equal([2, 2], a)
+
+ bug5801 = '[ruby-dev:45041]'
+ @empty.grep(//)
+ block = @empty.block
+ assert_nothing_raised(bug5801) {100.times {block.call}}
+
+ a = []
+ lambda = ->(x, i) {a << [x, i]}
+ @obj.each_with_index.grep(proc{|x,i|x==2}, &lambda)
+ assert_equal([[2, 1], [2, 4]], a)
+ end
+
+ def test_count
+ assert_equal(5, @obj.count)
+ assert_equal(2, @obj.count(1))
+ assert_equal(3, @obj.count {|x| x % 2 == 1 })
+ assert_equal(2, @obj.count(1) {|x| x % 2 == 1 })
+ assert_raise(ArgumentError) { @obj.count(0, 1) }
+
+ if RUBY_ENGINE == "ruby"
+ en = Class.new {
+ include Enumerable
+ alias :size :count
+ def each
+ yield 1
+ end
+ }
+ assert_equal(1, en.new.count, '[ruby-core:24794]')
+ end
+ end
+
+ def test_find
+ assert_equal(2, @obj.find {|x| x % 2 == 0 })
+ assert_equal(nil, @obj.find {|x| false })
+ assert_equal(:foo, @obj.find(proc { :foo }) {|x| false })
+ cond = ->(x, i) { x % 2 == 0 }
+ assert_equal([2, 1], @obj.each_with_index.find(&cond))
+ end
+
+ def test_find_index
+ assert_equal(1, @obj.find_index(2))
+ assert_equal(1, @obj.find_index {|x| x % 2 == 0 })
+ assert_equal(nil, @obj.find_index {|x| false })
+ assert_raise(ArgumentError) { @obj.find_index(0, 1) }
+ assert_equal(1, @obj.find_index(2) {|x| x == 1 })
+ end
+
+ def test_find_all
+ assert_equal([1, 3, 1], @obj.find_all {|x| x % 2 == 1 })
+ cond = ->(x, i) { x % 2 == 1 }
+ assert_equal([[1, 0], [3, 2], [1, 3]], @obj.each_with_index.find_all(&cond))
+ end
+
+ def test_reject
+ assert_equal([2, 3, 2], @obj.reject {|x| x < 2 })
+ cond = ->(x, i) {x < 2}
+ assert_equal([[2, 1], [3, 2], [2, 4]], @obj.each_with_index.reject(&cond))
+ end
+
+ def test_to_a
+ assert_equal([1, 2, 3, 1, 2], @obj.to_a)
+ end
+
+ def test_to_h
+ obj = Object.new
+ def obj.each(*args)
+ yield(*args)
+ yield [:key, :value]
+ yield :other_key, :other_value
+ kvp = Object.new
+ def kvp.to_ary
+ [:obtained, :via_to_ary]
+ end
+ yield kvp
+ end
+ obj.extend Enumerable
+ assert_equal({
+ :hello => :world,
+ :key => :value,
+ :other_key => :other_value,
+ :obtained => :via_to_ary,
+ }, obj.to_h(:hello, :world))
+
+ e = assert_raise(TypeError) {
+ obj.to_h(:not_an_array)
+ }
+ assert_equal "wrong element type Symbol (expected array)", e.message
+
+ e = assert_raise(ArgumentError) {
+ obj.to_h([1])
+ }
+ assert_equal "element has wrong array length (expected 2, was 1)", e.message
+ end
+
+ def test_inject
+ assert_equal(12, @obj.inject {|z, x| z * x })
+ assert_equal(48, @obj.inject {|z, x| z * 2 + x })
+ assert_equal(12, @obj.inject(:*))
+ assert_equal(24, @obj.inject(2) {|z, x| z * x })
+ assert_equal(24, @obj.inject(2, :*) {|z, x| z * x })
+ assert_equal(nil, @empty.inject() {9})
+ end
+
+ def test_partition
+ assert_equal([[1, 3, 1], [2, 2]], @obj.partition {|x| x % 2 == 1 })
+ cond = ->(x, i) { x % 2 == 1 }
+ assert_equal([[[1, 0], [3, 2], [1, 3]], [[2, 1], [2, 4]]], @obj.each_with_index.partition(&cond))
+ end
+
+ def test_group_by
+ h = { 1 => [1, 1], 2 => [2, 2], 3 => [3] }
+ assert_equal(h, @obj.group_by {|x| x })
+
+ h = {1=>[[1, 0], [1, 3]], 2=>[[2, 1], [2, 4]], 3=>[[3, 2]]}
+ cond = ->(x, i) { x }
+ assert_equal(h, @obj.each_with_index.group_by(&cond))
+ end
+
+ def test_first
+ assert_equal(1, @obj.first)
+ assert_equal([1, 2, 3], @obj.first(3))
+ assert_nil(@empty.first)
+
+ bug5801 = '[ruby-dev:45041]'
+ assert_in_out_err([], <<-'end;', [], /unexpected break/)
+ empty = Object.new
+ class << empty
+ attr_reader :block
+ include Enumerable
+ def each(&block)
+ @block = block
+ self
+ end
+ end
+ empty.first
+ empty.block.call
+ end;
+ end
+
+ def test_sort
+ assert_equal([1, 1, 2, 2, 3], @obj.sort)
+ end
+
+ def test_sort_by
+ assert_equal([3, 2, 2, 1, 1], @obj.sort_by {|x| -x })
+ assert_equal((1..300).to_a.reverse, (1..300).sort_by {|x| -x })
+
+ cond = ->(x, i) { [-x, i] }
+ assert_equal([[3, 2], [2, 1], [2, 4], [1, 0], [1, 3]], @obj.each_with_index.sort_by(&cond))
+ end
+
+ def test_all
+ assert_equal(true, @obj.all? {|x| x <= 3 })
+ assert_equal(false, @obj.all? {|x| x < 3 })
+ assert_equal(true, @obj.all?)
+ assert_equal(false, [true, true, false].all?)
+ end
+
+ def test_any
+ assert_equal(true, @obj.any? {|x| x >= 3 })
+ assert_equal(false, @obj.any? {|x| x > 3 })
+ assert_equal(true, @obj.any?)
+ assert_equal(false, [false, false, false].any?)
+ end
+
+ def test_one
+ assert(@obj.one? {|x| x == 3 })
+ assert(!(@obj.one? {|x| x == 1 }))
+ assert(!(@obj.one? {|x| x == 4 }))
+ assert(%w{ant bear cat}.one? {|word| word.length == 4})
+ assert(!(%w{ant bear cat}.one? {|word| word.length > 4}))
+ assert(!(%w{ant bear cat}.one? {|word| word.length < 4}))
+ assert(!([ nil, true, 99 ].one?))
+ assert([ nil, true, false ].one?)
+ end
+
+ def test_none
+ assert(@obj.none? {|x| x == 4 })
+ assert(!(@obj.none? {|x| x == 1 }))
+ assert(!(@obj.none? {|x| x == 3 }))
+ assert(%w{ant bear cat}.none? {|word| word.length == 5})
+ assert(!(%w{ant bear cat}.none? {|word| word.length >= 4}))
+ assert([].none?)
+ assert([nil].none?)
+ assert([nil,false].none?)
+ end
+
+ def test_min
+ assert_equal(1, @obj.min)
+ assert_equal(3, @obj.min {|a,b| b <=> a })
+ cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
+ assert_equal([3, 2], @obj.each_with_index.min(&cond))
+ ary = %w(albatross dog horse)
+ assert_equal("albatross", ary.min)
+ assert_equal("dog", ary.min {|a,b| a.length <=> b.length })
+ assert_equal(1, [3,2,1].min)
+ assert_equal(%w[albatross dog], ary.min(2))
+ assert_equal(%w[dog horse],
+ ary.min(2) {|a,b| a.length <=> b.length })
+ end
+
+ def test_max
+ assert_equal(3, @obj.max)
+ assert_equal(1, @obj.max {|a,b| b <=> a })
+ cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
+ assert_equal([1, 3], @obj.each_with_index.max(&cond))
+ ary = %w(albatross dog horse)
+ assert_equal("horse", ary.max)
+ assert_equal("albatross", ary.max {|a,b| a.length <=> b.length })
+ assert_equal(1, [3,2,1].max{|a,b| b <=> a })
+ assert_equal(%w[horse dog], ary.max(2))
+ assert_equal(%w[albatross horse],
+ ary.max(2) {|a,b| a.length <=> b.length })
+ end
+
+ def test_minmax
+ assert_equal([1, 3], @obj.minmax)
+ assert_equal([3, 1], @obj.minmax {|a,b| b <=> a })
+ ary = %w(albatross dog horse)
+ assert_equal(["albatross", "horse"], ary.minmax)
+ assert_equal(["dog", "albatross"], ary.minmax {|a,b| a.length <=> b.length })
+ assert_equal([1, 3], [2,3,1].minmax)
+ assert_equal([3, 1], [2,3,1].minmax {|a,b| b <=> a })
+ assert_equal([1, 3], [2,2,3,3,1,1].minmax)
+ end
+
+ def test_min_by
+ assert_equal(3, @obj.min_by {|x| -x })
+ cond = ->(x, i) { -x }
+ assert_equal([3, 2], @obj.each_with_index.min_by(&cond))
+ a = %w(albatross dog horse)
+ assert_equal("dog", a.min_by {|x| x.length })
+ assert_equal(3, [2,3,1].min_by {|x| -x })
+ assert_equal(%w[dog horse], a.min_by(2) {|x| x.length })
+ end
+
+ def test_max_by
+ assert_equal(1, @obj.max_by {|x| -x })
+ cond = ->(x, i) { -x }
+ assert_equal([1, 0], @obj.each_with_index.max_by(&cond))
+ a = %w(albatross dog horse)
+ assert_equal("albatross", a.max_by {|x| x.length })
+ assert_equal(1, [2,3,1].max_by {|x| -x })
+ assert_equal(%w[albatross horse], a.max_by(2) {|x| x.length })
+ end
+
+ def test_minmax_by
+ assert_equal([3, 1], @obj.minmax_by {|x| -x })
+ cond = ->(x, i) { -x }
+ assert_equal([[3, 2], [1, 0]], @obj.each_with_index.minmax_by(&cond))
+ a = %w(albatross dog horse)
+ assert_equal(["dog", "albatross"], a.minmax_by {|x| x.length })
+ assert_equal([3, 1], [2,3,1].minmax_by {|x| -x })
+ end
+
+ def test_member
+ assert(@obj.member?(1))
+ assert(!(@obj.member?(4)))
+ assert([1,2,3].member?(1))
+ assert(!([1,2,3].member?(4)))
+ end
+
+ class Foo
+ include Enumerable
+ def each
+ yield 1
+ yield 1,2
+ end
+ end
+
+ def test_each_with_index
+ a = []
+ @obj.each_with_index {|x, i| a << [x, i] }
+ assert_equal([[1,0],[2,1],[3,2],[1,3],[2,4]], a)
+
+ hash = Hash.new
+ %w(cat dog wombat).each_with_index do |item, index|
+ hash[item] = index
+ end
+ assert_equal({"cat"=>0, "wombat"=>2, "dog"=>1}, hash)
+ assert_equal([[1, 0], [[1, 2], 1]], Foo.new.each_with_index.to_a)
+ end
+
+ def test_each_with_object
+ obj = [0, 1]
+ ret = (1..10).each_with_object(obj) {|i, memo|
+ memo[0] += i
+ memo[1] *= i
+ }
+ assert_same(obj, ret)
+ assert_equal([55, 3628800], ret)
+ assert_equal([[1, nil], [[1, 2], nil]], Foo.new.each_with_object(nil).to_a)
+ end
+
+ def test_each_entry
+ assert_equal([1, 2, 3], [1, 2, 3].each_entry.to_a)
+ assert_equal([1, [1, 2]], Foo.new.each_entry.to_a)
+ a = []
+ cond = ->(x, i) { a << x }
+ @obj.each_with_index.each_entry(&cond)
+ assert_equal([1, 2, 3, 1, 2], a)
+ end
+
+ def test_each_slice
+ ary = []
+ (1..10).each_slice(3) {|a| ary << a}
+ assert_equal([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], ary)
+
+ bug9749 = '[ruby-core:62060] [Bug #9749]'
+ ary.clear
+ (1..10).each_slice(3, &lambda {|a, *| ary << a})
+ assert_equal([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], ary, bug9749)
+ end
+
+ def test_each_cons
+ ary = []
+ (1..5).each_cons(3) {|a| ary << a}
+ assert_equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]], ary)
+
+ bug9749 = '[ruby-core:62060] [Bug #9749]'
+ ary.clear
+ (1..5).each_cons(3, &lambda {|a, *| ary << a})
+ assert_equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]], ary, bug9749)
+ end
+
+ def test_zip
+ assert_equal([[1,1],[2,2],[3,3],[1,1],[2,2]], @obj.zip(@obj))
+ a = []
+ @obj.zip([:a, :b, :c]) {|x,y| a << [x, y] }
+ assert_equal([[1,:a],[2,:b],[3,:c],[1,nil],[2,nil]], a)
+
+ a = []
+ cond = ->((x, i), y) { a << [x, y, i] }
+ @obj.each_with_index.zip([:a, :b, :c], &cond)
+ assert_equal([[1,:a,0],[2,:b,1],[3,:c,2],[1,nil,3],[2,nil,4]], a)
+
+ a = []
+ @obj.zip({a: "A", b: "B", c: "C"}) {|x,y| a << [x, y] }
+ assert_equal([[1,[:a,"A"]],[2,[:b,"B"]],[3,[:c,"C"]],[1,nil],[2,nil]], a)
+
+ ary = Object.new
+ def ary.to_a; [1, 2]; end
+ assert_raise(TypeError) {%w(a b).zip(ary)}
+ def ary.each; [3, 4].each{|e|yield e}; end
+ assert_equal([[1, 3], [2, 4], [3, nil], [1, nil], [2, nil]], @obj.zip(ary))
+ def ary.to_ary; [5, 6]; end
+ assert_equal([[1, 5], [2, 6], [3, nil], [1, nil], [2, nil]], @obj.zip(ary))
+ end
+
+ def test_take
+ assert_equal([1,2,3], @obj.take(3))
+ end
+
+ def test_take_while
+ assert_equal([1,2], @obj.take_while {|x| x <= 2})
+ cond = ->(x, i) {x <= 2}
+ assert_equal([[1, 0], [2, 1]], @obj.each_with_index.take_while(&cond))
+
+ bug5801 = '[ruby-dev:45040]'
+ @empty.take_while {true}
+ block = @empty.block
+ assert_nothing_raised(bug5801) {100.times {block.call}}
+ end
+
+ def test_drop
+ assert_equal([3,1,2], @obj.drop(2))
+ end
+
+ def test_drop_while
+ assert_equal([3,1,2], @obj.drop_while {|x| x <= 2})
+ cond = ->(x, i) {x <= 2}
+ assert_equal([[3, 2], [1, 3], [2, 4]], @obj.each_with_index.drop_while(&cond))
+ end
+
+ def test_cycle
+ assert_equal([1,2,3,1,2,1,2,3,1,2], @obj.cycle.take(10))
+ a = []
+ @obj.cycle(2) {|x| a << x}
+ assert_equal([1,2,3,1,2,1,2,3,1,2], a)
+ a = []
+ cond = ->(x, i) {a << x}
+ @obj.each_with_index.cycle(2, &cond)
+ assert_equal([1,2,3,1,2,1,2,3,1,2], a)
+ end
+
+ def test_callcc
+ assert_raise(RuntimeError) do
+ c = nil
+ @obj.sort_by {|x| callcc {|c2| c ||= c2 }; x }
+ c.call
+ end
+
+ assert_raise(RuntimeError) do
+ c = nil
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:<=>) do |x|
+ callcc {|c2| c ||= c2 }
+ 0
+ end
+ end
+ [o, o].sort_by {|x| x }
+ c.call
+ end
+
+ assert_raise(RuntimeError) do
+ c = nil
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:<=>) do |x|
+ callcc {|c2| c ||= c2 }
+ 0
+ end
+ end
+ [o, o, o].sort_by {|x| x }
+ c.call
+ end
+ end
+
+ def test_reverse_each
+ assert_equal([2,1,3,2,1], @obj.reverse_each.to_a)
+ end
+
+ def test_chunk
+ e = [].chunk {|elt| true }
+ assert_equal([], e.to_a)
+
+ e = @obj.chunk {|elt| elt & 2 == 0 ? false : true }
+ assert_equal([[false, [1]], [true, [2, 3]], [false, [1]], [true, [2]]], e.to_a)
+
+ e = @obj.chunk(acc: 0) {|elt, h| h[:acc] += elt; h[:acc].even? }
+ assert_equal([[false, [1,2]], [true, [3]], [false, [1,2]]], e.to_a)
+ assert_equal([[false, [1,2]], [true, [3]], [false, [1,2]]], e.to_a) # this tests h is duplicated.
+
+ hs = [{}]
+ e = [:foo].chunk(hs[0]) {|elt, h|
+ hs << h
+ true
+ }
+ assert_equal([[true, [:foo]]], e.to_a)
+ assert_equal([[true, [:foo]]], e.to_a)
+ assert_equal([{}, {}, {}], hs)
+ assert_not_same(hs[0], hs[1])
+ assert_not_same(hs[0], hs[2])
+ assert_not_same(hs[1], hs[2])
+
+ e = @obj.chunk {|elt| elt < 3 ? :_alone : true }
+ assert_equal([[:_alone, [1]],
+ [:_alone, [2]],
+ [true, [3]],
+ [:_alone, [1]],
+ [:_alone, [2]]], e.to_a)
+
+ e = @obj.chunk {|elt| elt == 3 ? :_separator : true }
+ assert_equal([[true, [1, 2]],
+ [true, [1, 2]]], e.to_a)
+
+ e = @obj.chunk {|elt| elt == 3 ? nil : true }
+ assert_equal([[true, [1, 2]],
+ [true, [1, 2]]], e.to_a)
+
+ e = @obj.chunk {|elt| :_foo }
+ assert_raise(RuntimeError) { e.to_a }
+ end
+
+ def test_slice_before
+ e = [].slice_before {|elt| true }
+ assert_equal([], e.to_a)
+
+ e = @obj.slice_before {|elt| elt.even? }
+ assert_equal([[1], [2,3,1], [2]], e.to_a)
+
+ e = @obj.slice_before {|elt| elt.odd? }
+ assert_equal([[1,2], [3], [1,2]], e.to_a)
+
+ e = @obj.slice_before(acc: 0) {|elt, h| h[:acc] += elt; h[:acc].even? }
+ assert_equal([[1,2], [3,1,2]], e.to_a)
+ assert_equal([[1,2], [3,1,2]], e.to_a) # this tests h is duplicated.
+
+ hs = [{}]
+ e = [:foo].slice_before(hs[0]) {|elt, h|
+ hs << h
+ true
+ }
+ assert_equal([[:foo]], e.to_a)
+ assert_equal([[:foo]], e.to_a)
+ assert_equal([{}, {}, {}], hs)
+ assert_not_same(hs[0], hs[1])
+ assert_not_same(hs[0], hs[2])
+ assert_not_same(hs[1], hs[2])
+
+ ss = %w[abc defg h ijk l mno pqr st u vw xy z]
+ assert_equal([%w[abc defg h], %w[ijk l], %w[mno], %w[pqr st u vw xy z]],
+ ss.slice_before(/\A...\z/).to_a)
+ assert_not_warn{ss.slice_before(/\A...\z/).to_a}
+ end
+
+ def test_slice_after0
+ assert_raise(ArgumentError) { [].slice_after }
+ end
+
+ def test_slice_after1
+ e = [].slice_after {|a| flunk "should not be called" }
+ assert_equal([], e.to_a)
+
+ e = [1,2].slice_after(1)
+ assert_equal([[1], [2]], e.to_a)
+
+ e = [1,2].slice_after(3)
+ assert_equal([[1, 2]], e.to_a)
+
+ [true, false].each {|b|
+ block_results = [true, b]
+ e = [1,2].slice_after {|a| block_results.shift }
+ assert_equal([[1], [2]], e.to_a)
+ assert_equal([], block_results)
+
+ block_results = [false, b]
+ e = [1,2].slice_after {|a| block_results.shift }
+ assert_equal([[1, 2]], e.to_a)
+ assert_equal([], block_results)
+ }
+ end
+
+ def test_slice_after_both_pattern_and_block
+ assert_raise(ArgumentError) { [].slice_after(1) {|a| true } }
+ end
+
+ def test_slice_after_continuation_lines
+ lines = ["foo\n", "bar\\\n", "baz\n", "\n", "qux\n"]
+ e = lines.slice_after(/[^\\]\n\z/)
+ assert_equal([["foo\n"], ["bar\\\n", "baz\n"], ["\n", "qux\n"]], e.to_a)
+ end
+
+ def test_slice_before_empty_line
+ lines = ["foo", "", "bar"]
+ e = lines.slice_after(/\A\s*\z/)
+ assert_equal([["foo", ""], ["bar"]], e.to_a)
+ end
+
+ def test_slice_when_0
+ e = [].slice_when {|a, b| flunk "should not be called" }
+ assert_equal([], e.to_a)
+ end
+
+ def test_slice_when_1
+ e = [1].slice_when {|a, b| flunk "should not be called" }
+ assert_equal([[1]], e.to_a)
+ end
+
+ def test_slice_when_2
+ e = [1,2].slice_when {|a,b|
+ assert_equal(1, a)
+ assert_equal(2, b)
+ true
+ }
+ assert_equal([[1], [2]], e.to_a)
+
+ e = [1,2].slice_when {|a,b|
+ assert_equal(1, a)
+ assert_equal(2, b)
+ false
+ }
+ assert_equal([[1, 2]], e.to_a)
+ end
+
+ def test_slice_when_3
+ block_invocations = [
+ lambda {|a, b|
+ assert_equal(1, a)
+ assert_equal(2, b)
+ true
+ },
+ lambda {|a, b|
+ assert_equal(2, a)
+ assert_equal(3, b)
+ false
+ }
+ ]
+ e = [1,2,3].slice_when {|a,b|
+ block_invocations.shift.call(a, b)
+ }
+ assert_equal([[1], [2, 3]], e.to_a)
+ assert_equal([], block_invocations)
+ end
+
+ def test_slice_when_noblock
+ assert_raise(ArgumentError) { [].slice_when }
+ end
+
+ def test_slice_when_contiguously_increasing_integers
+ e = [1,4,9,10,11,12,15,16,19,20,21].slice_when {|i, j| i+1 != j }
+ assert_equal([[1], [4], [9,10,11,12], [15,16], [19,20,21]], e.to_a)
+ end
+
+ def test_detect
+ @obj = ('a'..'z')
+ assert_equal('c', @obj.detect {|x| x == 'c' })
+
+ proc = Proc.new {|x| x == 'c' }
+ assert_equal('c', @obj.detect(&proc))
+
+ lambda = ->(x) { x == 'c' }
+ assert_equal('c', @obj.detect(&lambda))
+
+ assert_equal(['c',2], @obj.each_with_index.detect {|x, i| x == 'c' })
+
+ proc2 = Proc.new {|x, i| x == 'c' }
+ assert_equal(['c',2], @obj.each_with_index.detect(&proc2))
+
+ bug9605 = '[ruby-core:61340]'
+ lambda2 = ->(x, i) { x == 'c' }
+ assert_equal(['c',2], @obj.each_with_index.detect(&lambda2))
+ end
+
+ def test_select
+ @obj = ('a'..'z')
+ assert_equal(['c'], @obj.select {|x| x == 'c' })
+
+ proc = Proc.new {|x| x == 'c' }
+ assert_equal(['c'], @obj.select(&proc))
+
+ lambda = ->(x) { x == 'c' }
+ assert_equal(['c'], @obj.select(&lambda))
+
+ assert_equal([['c',2]], @obj.each_with_index.select {|x, i| x == 'c' })
+
+ proc2 = Proc.new {|x, i| x == 'c' }
+ assert_equal([['c',2]], @obj.each_with_index.select(&proc2))
+
+ bug9605 = '[ruby-core:61340]'
+ lambda2 = ->(x, i) { x == 'c' }
+ assert_equal([['c',2]], @obj.each_with_index.select(&lambda2))
+ end
+
+ def test_map
+ @obj = ('a'..'e')
+ assert_equal(['A', 'B', 'C', 'D', 'E'], @obj.map {|x| x.upcase })
+
+ proc = Proc.new {|x| x.upcase }
+ assert_equal(['A', 'B', 'C', 'D', 'E'], @obj.map(&proc))
+
+ lambda = ->(x) { x.upcase }
+ assert_equal(['A', 'B', 'C', 'D', 'E'], @obj.map(&lambda))
+
+ assert_equal([['A',0], ['B',1], ['C',2], ['D',3], ['E',4]],
+ @obj.each_with_index.map {|x, i| [x.upcase, i] })
+
+ proc2 = Proc.new {|x, i| [x.upcase, i] }
+ assert_equal([['A',0], ['B',1], ['C',2], ['D',3], ['E',4]],
+ @obj.each_with_index.map(&proc2))
+
+ lambda2 = ->(x, i) { [x.upcase, i] }
+ assert_equal([['A',0], ['B',1], ['C',2], ['D',3], ['E',4]],
+ @obj.each_with_index.map(&lambda2))
+ end
+
+ def test_flat_map
+ @obj = [[1,2], [3,4]]
+ assert_equal([2,4,6,8], @obj.flat_map {|i| i.map{|j| j*2} })
+
+ proc = Proc.new {|i| i.map{|j| j*2} }
+ assert_equal([2,4,6,8], @obj.flat_map(&proc))
+
+ lambda = ->(i) { i.map{|j| j*2} }
+ assert_equal([2,4,6,8], @obj.flat_map(&lambda))
+
+ assert_equal([[1,2],0,[3,4],1],
+ @obj.each_with_index.flat_map {|x, i| [x,i] })
+
+ proc2 = Proc.new {|x, i| [x,i] }
+ assert_equal([[1,2],0,[3,4],1],
+ @obj.each_with_index.flat_map(&proc2))
+
+ lambda2 = ->(x, i) { [x,i] }
+ assert_equal([[1,2],0,[3,4],1],
+ @obj.each_with_index.flat_map(&lambda2))
+ end
+end
diff --git a/jni/ruby/test/ruby/test_enumerator.rb b/jni/ruby/test/ruby/test_enumerator.rb
new file mode 100644
index 0000000..b5ced3b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_enumerator.rb
@@ -0,0 +1,628 @@
+require 'test/unit'
+
+class TestEnumerator < Test::Unit::TestCase
+ def setup
+ @obj = Object.new
+ class << @obj
+ include Enumerable
+ def foo(*a)
+ a.each {|x| yield x }
+ end
+ end
+ @sized = @obj.clone
+ def @sized.size
+ 42
+ end
+ end
+
+ def enum_test obj
+ obj.map{|e|
+ e
+ }.sort
+ end
+
+ def test_iterators
+ assert_equal [0, 1, 2], enum_test(3.times)
+ assert_equal [:x, :y, :z], enum_test([:x, :y, :z].each)
+ assert_equal [[:x, 1], [:y, 2]], enum_test({:x=>1, :y=>2}.each)
+ end
+
+ ## Enumerator as Iterator
+
+ def test_next
+ e = 3.times
+ 3.times{|i|
+ assert_equal i, e.next
+ }
+ assert_raise(StopIteration){e.next}
+ end
+
+ def test_loop
+ e = 3.times
+ i = 0
+ loop{
+ assert_equal(i, e.next)
+ i += 1
+ }
+ end
+
+ def test_nested_iteration
+ def (o = Object.new).each
+ yield :ok1
+ yield [:ok2, :x].each.next
+ end
+ e = o.to_enum
+ assert_equal :ok1, e.next
+ assert_equal :ok2, e.next
+ assert_raise(StopIteration){e.next}
+ end
+
+
+ def test_initialize
+ assert_equal([1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).to_a)
+ _, err = capture_io do
+ assert_equal([1, 2, 3], Enumerator.new(@obj, :foo, 1, 2, 3).to_a)
+ end
+ assert_match 'Enumerator.new without a block is deprecated', err
+ assert_equal([1, 2, 3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3))
+ assert_raise(ArgumentError) { Enumerator.new }
+
+ enum = @obj.to_enum
+ assert_raise(NoMethodError) { enum.each {} }
+ enum.freeze
+ assert_raise(RuntimeError) {
+ capture_io do
+ # warning: Enumerator.new without a block is deprecated; use Object#to_enum
+ enum.__send__(:initialize, @obj, :foo)
+ end
+ }
+ end
+
+ def test_initialize_copy
+ assert_equal([1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).dup.to_a)
+ e = @obj.to_enum(:foo, 1, 2, 3)
+ assert_nothing_raised { assert_equal(1, e.next) }
+ assert_raise(TypeError) { e.dup }
+
+ e = Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.dup
+ assert_nothing_raised { assert_equal(1, e.next) }
+ assert_raise(TypeError) { e.dup }
+ end
+
+ def test_gc
+ assert_nothing_raised do
+ 1.times do
+ foo = [1,2,3].to_enum
+ GC.start
+ end
+ GC.start
+ end
+ end
+
+ def test_slice
+ assert_equal([[1,2,3],[4,5,6],[7,8,9],[10]], (1..10).each_slice(3).to_a)
+ end
+
+ def test_cons
+ a = [[1,2,3], [2,3,4], [3,4,5], [4,5,6], [5,6,7], [6,7,8], [7,8,9], [8,9,10]]
+ assert_equal(a, (1..10).each_cons(3).to_a)
+ end
+
+ def test_with_index
+ assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a)
+ assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
+ end
+
+ def test_with_index_large_offset
+ bug8010 = '[ruby-dev:47131] [Bug #8010]'
+ s = 1 << (8*1.size-2)
+ assert_equal([[1,s],[2,s+1],[3,s+2]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a, bug8010)
+ s <<= 1
+ assert_equal([[1,s],[2,s+1],[3,s+2]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a, bug8010)
+ end
+
+ def test_with_index_nonnum_offset
+ bug8010 = '[ruby-dev:47131] [Bug #8010]'
+ s = Object.new
+ def s.to_int; 1 end
+ assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a, bug8010)
+ end
+
+ def test_with_index_string_offset
+ bug8010 = '[ruby-dev:47131] [Bug #8010]'
+ assert_raise(TypeError, bug8010){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
+ end
+
+ def test_with_index_dangling_memo
+ bug9178 = '[ruby-core:58692] [Bug #9178]'
+ assert_separately([], <<-"end;")
+ bug = "#{bug9178}"
+ e = [1].to_enum(:chunk).with_index {|c,i| i == 5}
+ assert_kind_of(Enumerator, e)
+ assert_equal([false, [1]], e.to_a[0], bug)
+ end;
+ end
+
+ def test_with_object
+ obj = [0, 1]
+ ret = (1..10).each.with_object(obj) {|i, memo|
+ memo[0] += i
+ memo[1] *= i
+ }
+ assert_same(obj, ret)
+ assert_equal([55, 3628800], ret)
+
+ a = [2,5,2,1,5,3,4,2,1,0]
+ obj = {}
+ ret = a.delete_if.with_object(obj) {|i, seen|
+ if seen.key?(i)
+ true
+ else
+ seen[i] = true
+ false
+ end
+ }
+ assert_same(obj, ret)
+ assert_equal([2, 5, 1, 3, 4, 0], a)
+ end
+
+ def test_next_rewind
+ e = @obj.to_enum(:foo, 1, 2, 3)
+ assert_equal(1, e.next)
+ assert_equal(2, e.next)
+ e.rewind
+ assert_equal(1, e.next)
+ assert_equal(2, e.next)
+ assert_equal(3, e.next)
+ assert_raise(StopIteration) { e.next }
+ end
+
+ def test_peek
+ a = [1]
+ e = a.each
+ assert_equal(1, e.peek)
+ assert_equal(1, e.peek)
+ assert_equal(1, e.next)
+ assert_raise(StopIteration) { e.peek }
+ assert_raise(StopIteration) { e.peek }
+ end
+
+ def test_peek_modify
+ o = Object.new
+ def o.each
+ yield 1,2
+ end
+ e = o.to_enum
+ a = e.peek
+ a << 3
+ assert_equal([1,2], e.peek)
+ end
+
+ def test_peek_values_modify
+ o = Object.new
+ def o.each
+ yield 1,2
+ end
+ e = o.to_enum
+ a = e.peek_values
+ a << 3
+ assert_equal([1,2], e.peek)
+ end
+
+ def test_next_after_stopiteration
+ a = [1]
+ e = a.each
+ assert_equal(1, e.next)
+ assert_raise(StopIteration) { e.next }
+ assert_raise(StopIteration) { e.next }
+ e.rewind
+ assert_equal(1, e.next)
+ assert_raise(StopIteration) { e.next }
+ assert_raise(StopIteration) { e.next }
+ end
+
+ def test_stop_result
+ a = [1]
+ res = a.each {}
+ e = a.each
+ assert_equal(1, e.next)
+ exc = assert_raise(StopIteration) { e.next }
+ assert_equal(res, exc.result)
+ end
+
+ def test_next_values
+ o = Object.new
+ def o.each
+ yield
+ yield 1
+ yield 1, 2
+ end
+ e = o.to_enum
+ assert_equal(nil, e.next)
+ assert_equal(1, e.next)
+ assert_equal([1,2], e.next)
+ e = o.to_enum
+ assert_equal([], e.next_values)
+ assert_equal([1], e.next_values)
+ assert_equal([1,2], e.next_values)
+ end
+
+ def test_peek_values
+ o = Object.new
+ def o.each
+ yield
+ yield 1
+ yield 1, 2
+ end
+ e = o.to_enum
+ assert_equal(nil, e.peek)
+ assert_equal(nil, e.next)
+ assert_equal(1, e.peek)
+ assert_equal(1, e.next)
+ assert_equal([1,2], e.peek)
+ assert_equal([1,2], e.next)
+ e = o.to_enum
+ assert_equal([], e.peek_values)
+ assert_equal([], e.next_values)
+ assert_equal([1], e.peek_values)
+ assert_equal([1], e.next_values)
+ assert_equal([1,2], e.peek_values)
+ assert_equal([1,2], e.next_values)
+ e = o.to_enum
+ assert_equal([], e.peek_values)
+ assert_equal(nil, e.next)
+ assert_equal([1], e.peek_values)
+ assert_equal(1, e.next)
+ assert_equal([1,2], e.peek_values)
+ assert_equal([1,2], e.next)
+ e = o.to_enum
+ assert_equal(nil, e.peek)
+ assert_equal([], e.next_values)
+ assert_equal(1, e.peek)
+ assert_equal([1], e.next_values)
+ assert_equal([1,2], e.peek)
+ assert_equal([1,2], e.next_values)
+ end
+
+ def test_each_arg
+ o = Object.new
+ def o.each(ary)
+ ary << 1
+ yield
+ end
+ ary = []
+ e = o.to_enum.each(ary)
+ e.next
+ assert_equal([1], ary)
+ end
+
+ def test_feed
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum(:each, ary)
+ e.next
+ e.feed 1
+ e.next
+ e.feed 2
+ e.next
+ e.feed 3
+ assert_raise(StopIteration) { e.next }
+ assert_equal([1,2,3], ary)
+ end
+
+ def test_feed_mixed
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum(:each, ary)
+ e.next
+ e.feed 1
+ e.next
+ e.next
+ e.feed 3
+ assert_raise(StopIteration) { e.next }
+ assert_equal([1,nil,3], ary)
+ end
+
+ def test_feed_twice
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum(:each, ary)
+ e.feed 1
+ assert_raise(TypeError) { e.feed 2 }
+ end
+
+ def test_feed_before_first_next
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum(:each, ary)
+ e.feed 1
+ e.next
+ e.next
+ assert_equal([1], ary)
+ end
+
+ def test_rewind_clear_feed
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum(:each, ary)
+ e.next
+ e.feed 1
+ e.next
+ e.feed 2
+ e.rewind
+ e.next
+ e.next
+ assert_equal([1,nil], ary)
+ end
+
+ def test_feed_yielder
+ x = nil
+ e = Enumerator.new {|y| x = y.yield; 10 }
+ e.next
+ e.feed 100
+ exc = assert_raise(StopIteration) { e.next }
+ assert_equal(100, x)
+ assert_equal(10, exc.result)
+ end
+
+ def test_inspect
+ e = (0..10).each_cons(2)
+ assert_equal("#<Enumerator: 0..10:each_cons(2)>", e.inspect)
+
+ e = Enumerator.new {|y| y.yield; 10 }
+ assert_match(/\A#<Enumerator: .*:each>/, e.inspect)
+
+ a = []
+ e = a.each_with_object(a)
+ a << e
+ assert_equal("#<Enumerator: [#<Enumerator: ...>]:each_with_object([#<Enumerator: ...>])>",
+ e.inspect)
+ end
+
+ def test_inspect_verbose
+ bug6214 = '[ruby-dev:45449]'
+ assert_warning("", bug6214) { "".bytes.inspect }
+ assert_warning("", bug6214) { [].lazy.inspect }
+ end
+
+ def test_inspect_encoding
+ c = Class.new{define_method("\u{3042}"){}}
+ e = c.new.enum_for("\u{3042}")
+ s = assert_nothing_raised(Encoding::CompatibilityError) {break e.inspect}
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_match(/\A#<Enumerator: .*:\u{3042}>\z/, s)
+ end
+
+ def test_generator
+ # note: Enumerator::Generator is a class just for internal
+ g = Enumerator::Generator.new {|y| y << 1 << 2 << 3; :foo }
+ g2 = g.dup
+ a = []
+ assert_equal(:foo, g.each {|x| a << x })
+ assert_equal([1, 2, 3], a)
+ a = []
+ assert_equal(:foo, g2.each {|x| a << x })
+ assert_equal([1, 2, 3], a)
+
+ g.freeze
+ assert_raise(RuntimeError) {
+ g.__send__ :initialize, proc { |y| y << 4 << 5 }
+ }
+ end
+
+ def test_generator_args
+ g = Enumerator::Generator.new {|y, x| y << 1 << 2 << 3; x }
+ a = []
+ assert_equal(:bar, g.each(:bar) {|x| a << x })
+ assert_equal([1, 2, 3], a)
+ end
+
+ def test_yielder
+ # note: Enumerator::Yielder is a class just for internal
+ a = []
+ y = Enumerator::Yielder.new {|x| a << x }
+ assert_equal(y, y << 1 << 2 << 3)
+ assert_equal([1, 2, 3], a)
+
+ a = []
+ y = Enumerator::Yielder.new {|x| a << x }
+ assert_equal([1], y.yield(1))
+ assert_equal([1, 2], y.yield(2))
+ assert_equal([1, 2, 3], y.yield(3))
+
+ assert_raise(LocalJumpError) { Enumerator::Yielder.new }
+ end
+
+ def test_size
+ assert_equal nil, Enumerator.new{}.size
+ assert_equal 42, Enumerator.new(->{42}){}.size
+ obj = Object.new
+ def obj.call; 42; end
+ assert_equal 42, Enumerator.new(obj){}.size
+ assert_equal 42, Enumerator.new(42){}.size
+ assert_equal 1 << 70, Enumerator.new(1 << 70){}.size
+ assert_equal Float::INFINITY, Enumerator.new(Float::INFINITY){}.size
+ assert_equal nil, Enumerator.new(nil){}.size
+ assert_raise(TypeError) { Enumerator.new("42"){} }
+
+ assert_equal nil, @obj.to_enum(:foo, 0, 1).size
+ assert_equal 2, @obj.to_enum(:foo, 0, 1){ 2 }.size
+ end
+
+ def test_size_for_enum_created_by_enumerators
+ enum = to_enum{ 42 }
+ assert_equal 42, enum.with_index.size
+ assert_equal 42, enum.with_object(:foo).size
+ end
+
+ def test_size_for_enum_created_from_array
+ arr = %w[hello world]
+ %i[each each_with_index reverse_each sort_by! sort_by map map!
+ keep_if reject! reject select! select delete_if].each do |method|
+ assert_equal arr.size, arr.send(method).size
+ end
+ end
+
+ def test_size_for_enum_created_from_enumerable
+ %i[find_all reject map flat_map partition group_by sort_by min_by max_by
+ minmax_by each_with_index reverse_each each_entry].each do |method|
+ assert_equal nil, @obj.send(method).size
+ assert_equal 42, @sized.send(method).size
+ end
+ assert_equal nil, @obj.each_with_object(nil).size
+ assert_equal 42, @sized.each_with_object(nil).size
+ end
+
+ def test_size_for_enum_created_from_hash
+ h = {a: 1, b: 2, c: 3}
+ methods = %i[delete_if reject reject! select select! keep_if each each_key each_pair]
+ enums = methods.map {|method| h.send(method)}
+ s = enums.group_by(&:size)
+ assert_equal([3], s.keys, ->{s.reject!{|k| k==3}.inspect})
+ h[:d] = 4
+ s = enums.group_by(&:size)
+ assert_equal([4], s.keys, ->{s.reject!{|k| k==4}.inspect})
+ end
+
+ def test_size_for_enum_created_from_env
+ %i[each_pair reject! delete_if select select! keep_if].each do |method|
+ assert_equal ENV.size, ENV.send(method).size
+ end
+ end
+
+ def test_size_for_enum_created_from_struct
+ s = Struct.new(:foo, :bar, :baz).new(1, 2)
+ %i[each each_pair select].each do |method|
+ assert_equal 3, s.send(method).size
+ end
+ end
+
+ def check_consistency_for_combinatorics(method)
+ [ [], [:a, :b, :c, :d, :e] ].product([-2, 0, 2, 5, 6]) do |array, arg|
+ assert_equal array.send(method, arg).to_a.size, array.send(method, arg).size,
+ "inconsistent size for #{array}.#{method}(#{arg})"
+ end
+ end
+
+ def test_size_for_array_combinatorics
+ check_consistency_for_combinatorics(:permutation)
+ assert_equal 24, [0, 1, 2, 4].permutation.size
+ assert_equal 2933197128679486453788761052665610240000000,
+ (1..42).to_a.permutation(30).size # 1.upto(42).inject(:*) / 1.upto(12).inject(:*)
+
+ check_consistency_for_combinatorics(:combination)
+ assert_equal 28258808871162574166368460400,
+ (1..100).to_a.combination(42).size
+ # 1.upto(100).inject(:*) / 1.upto(42).inject(:*) / 1.upto(58).inject(:*)
+
+ check_consistency_for_combinatorics(:repeated_permutation)
+ assert_equal 291733167875766667063796853374976,
+ (1..42).to_a.repeated_permutation(20).size # 42 ** 20
+
+ check_consistency_for_combinatorics(:repeated_combination)
+ assert_equal 28258808871162574166368460400,
+ (1..59).to_a.repeated_combination(42).size
+ # 1.upto(100).inject(:*) / 1.upto(42).inject(:*) / 1.upto(58).inject(:*)
+ end
+
+ def test_size_for_cycle
+ assert_equal Float::INFINITY, [:foo].cycle.size
+ assert_equal 10, [:foo, :bar].cycle(5).size
+ assert_equal 0, [:foo, :bar].cycle(-10).size
+ assert_equal 0, [].cycle.size
+ assert_equal 0, [].cycle(5).size
+
+ assert_equal nil, @obj.cycle.size
+ assert_equal nil, @obj.cycle(5).size
+ assert_equal Float::INFINITY, @sized.cycle.size
+ assert_equal 126, @sized.cycle(3).size
+ end
+
+ def test_size_for_loops
+ assert_equal Float::INFINITY, loop.size
+ assert_equal 42, 42.times.size
+ end
+
+ def test_size_for_each_slice
+ assert_equal nil, @obj.each_slice(3).size
+ assert_equal 6, @sized.each_slice(7).size
+ assert_equal 5, @sized.each_slice(10).size
+ assert_equal 1, @sized.each_slice(70).size
+ assert_raise(ArgumentError){ @obj.each_slice(0).size }
+ end
+
+ def test_size_for_each_cons
+ assert_equal nil, @obj.each_cons(3).size
+ assert_equal 33, @sized.each_cons(10).size
+ assert_equal 0, @sized.each_cons(70).size
+ assert_raise(ArgumentError){ @obj.each_cons(0).size }
+ end
+
+ def test_size_for_step
+ assert_equal 42, 5.step(46).size
+ assert_equal 4, 1.step(10, 3).size
+ assert_equal 3, 1.step(9, 3).size
+ assert_equal 0, 1.step(-11).size
+ assert_equal 0, 1.step(-11, 2).size
+ assert_equal 7, 1.step(-11, -2).size
+ assert_equal 7, 1.step(-11.1, -2).size
+ assert_equal 0, 42.step(Float::INFINITY, -2).size
+ assert_equal 1, 42.step(55, Float::INFINITY).size
+ assert_equal 1, 42.step(Float::INFINITY, Float::INFINITY).size
+ assert_equal 14, 0.1.step(4.2, 0.3).size
+ assert_equal Float::INFINITY, 42.step(Float::INFINITY, 2).size
+
+ assert_equal 10, (1..10).step.size
+ assert_equal 4, (1..10).step(3).size
+ assert_equal 3, (1...10).step(3).size
+ assert_equal Float::INFINITY, (42..Float::INFINITY).step(2).size
+ assert_raise(ArgumentError){ (1..10).step(-2).size }
+ end
+
+ def test_size_for_downup_to
+ assert_equal 0, 1.upto(-100).size
+ assert_equal 102, 1.downto(-100).size
+ assert_equal Float::INFINITY, 42.upto(Float::INFINITY).size
+ end
+
+ def test_size_for_string
+ assert_equal 5, 'hello'.each_byte.size
+ assert_equal 5, 'hello'.each_char.size
+ assert_equal 5, 'hello'.each_codepoint.size
+ end
+
+ def test_peek_for_enumerator_objects
+ e = 2.times
+ assert_equal(0, e.peek)
+ e.next
+ assert_equal(1, e.peek)
+ e.next
+ assert_raise(StopIteration) { e.peek }
+ end
+end
+
diff --git a/jni/ruby/test/ruby/test_env.rb b/jni/ruby/test/ruby/test_env.rb
new file mode 100644
index 0000000..88a7631
--- /dev/null
+++ b/jni/ruby/test/ruby/test_env.rb
@@ -0,0 +1,552 @@
+require 'test/unit'
+
+class TestEnv < Test::Unit::TestCase
+ IGNORE_CASE = /bccwin|mswin|mingw/ =~ RUBY_PLATFORM
+ PATH_ENV = "PATH"
+
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ @backup = ENV.to_hash
+ ENV.delete('test')
+ ENV.delete('TEST')
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ ENV.clear
+ @backup.each {|k, v| ENV[k] = v }
+ end
+
+ def test_bracket
+ assert_nil(ENV['test'])
+ assert_nil(ENV['TEST'])
+ ENV['test'] = 'foo'
+ assert_equal('foo', ENV['test'])
+ if IGNORE_CASE
+ assert_equal('foo', ENV['TEST'])
+ else
+ assert_nil(ENV['TEST'])
+ end
+ ENV['TEST'] = 'bar'
+ assert_equal('bar', ENV['TEST'])
+ if IGNORE_CASE
+ assert_equal('bar', ENV['test'])
+ else
+ assert_equal('foo', ENV['test'])
+ end
+
+ assert_raise(TypeError) {
+ tmp = ENV[1]
+ }
+ assert_raise(TypeError) {
+ ENV[1] = 'foo'
+ }
+ assert_raise(TypeError) {
+ ENV['test'] = 0
+ }
+ end
+
+ def test_has_value
+ val = 'a'
+ val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase)
+ ENV['test'] = val[0...-1]
+
+ assert_equal(false, ENV.has_value?(val))
+ assert_equal(false, ENV.has_value?(val.upcase))
+ ENV['test'] = val
+ assert_equal(true, ENV.has_value?(val))
+ assert_equal(false, ENV.has_value?(val.upcase))
+ ENV['test'] = val.upcase
+ assert_equal(false, ENV.has_value?(val))
+ assert_equal(true, ENV.has_value?(val.upcase))
+ end
+
+ def test_key
+ val = 'a'
+ val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase)
+ ENV['test'] = val[0...-1]
+
+ assert_nil(ENV.key(val))
+ assert_nil(ENV.index(val))
+ assert_nil(ENV.key(val.upcase))
+ ENV['test'] = val
+ if IGNORE_CASE
+ assert_equal('TEST', ENV.key(val).upcase)
+ else
+ assert_equal('test', ENV.key(val))
+ end
+ assert_nil(ENV.key(val.upcase))
+ ENV['test'] = val.upcase
+ assert_nil(ENV.key(val))
+ if IGNORE_CASE
+ assert_equal('TEST', ENV.key(val.upcase).upcase)
+ else
+ assert_equal('test', ENV.key(val.upcase))
+ end
+ end
+
+ def test_delete
+ assert_raise(ArgumentError) { ENV.delete("foo\0bar") }
+ assert_nil(ENV.delete("TEST"))
+ assert_nothing_raised { ENV.delete(PATH_ENV) }
+ end
+
+ def test_getenv
+ assert_raise(ArgumentError) { ENV["foo\0bar"] }
+ ENV[PATH_ENV] = ""
+ assert_equal("", ENV[PATH_ENV])
+ assert_nil(ENV[""])
+ end
+
+ def test_fetch
+ ENV["test"] = "foo"
+ assert_equal("foo", ENV.fetch("test"))
+ ENV.delete("test")
+ feature8649 = '[ruby-core:56062] [Feature #8649]'
+ assert_raise_with_message(KeyError, 'key not found: "test"', feature8649) do
+ ENV.fetch("test")
+ end
+ assert_equal("foo", ENV.fetch("test", "foo"))
+ assert_equal("bar", ENV.fetch("test") { "bar" })
+ assert_equal("bar", ENV.fetch("test", "foo") { "bar" })
+ assert_raise(ArgumentError) { ENV.fetch("foo\0bar") }
+ assert_nothing_raised { ENV.fetch(PATH_ENV, "foo") }
+ ENV[PATH_ENV] = ""
+ assert_equal("", ENV.fetch(PATH_ENV))
+ end
+
+ def test_aset
+ assert_nothing_raised { ENV["test"] = nil }
+ assert_equal(nil, ENV["test"])
+ assert_raise(ArgumentError) { ENV["foo\0bar"] = "test" }
+ assert_raise(ArgumentError) { ENV["test"] = "foo\0bar" }
+
+ begin
+ # setenv(3) allowed the name includes '=',
+ # but POSIX.1-2001 says it should fail with EINVAL.
+ # see also http://togetter.com/li/22380
+ ENV["foo=bar"] = "test"
+ assert_equal("test", ENV["foo=bar"])
+ assert_equal("test", ENV["foo"])
+ rescue Errno::EINVAL
+ end
+
+ ENV[PATH_ENV] = "/tmp/".taint
+ assert_equal("/tmp/", ENV[PATH_ENV])
+ end
+
+ def test_keys
+ a = ENV.keys
+ assert_kind_of(Array, a)
+ a.each {|k| assert_kind_of(String, k) }
+ end
+
+ def test_each_key
+ ENV.each_key {|k| assert_kind_of(String, k) }
+ end
+
+ def test_values
+ a = ENV.values
+ assert_kind_of(Array, a)
+ a.each {|k| assert_kind_of(String, k) }
+ end
+
+ def test_each_value
+ ENV.each_value {|k| assert_kind_of(String, k) }
+ end
+
+ def test_each_pair
+ ENV.each_pair do |k, v|
+ assert_kind_of(String, k)
+ assert_kind_of(String, v)
+ end
+ end
+
+ def test_reject_bang
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.reject! {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ assert_equal(h1, h2)
+
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.delete_if {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ assert_equal(h1, h2)
+ end
+
+ def test_select_bang
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.select! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ assert_equal(h1, h2)
+
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.keep_if {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ assert_equal(h1, h2)
+ end
+
+ def test_values_at
+ ENV["test"] = "foo"
+ assert_equal(["foo", "foo"], ENV.values_at("test", "test"))
+ end
+
+ def test_select
+ ENV["test"] = "foo"
+ h = ENV.select {|k| IGNORE_CASE ? k.upcase == "TEST" : k == "test" }
+ assert_equal(1, h.size)
+ k = h.keys.first
+ v = h.values.first
+ if IGNORE_CASE
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ end
+
+ def test_clear
+ ENV.clear
+ assert_equal(0, ENV.size)
+ end
+
+ def test_to_s
+ assert_equal("ENV", ENV.to_s)
+ end
+
+ def test_inspect
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ s = ENV.inspect
+ if IGNORE_CASE
+ s = s.upcase
+ assert(s == '{"FOO"=>"BAR", "BAZ"=>"QUX"}' || s == '{"BAZ"=>"QUX", "FOO"=>"BAR"}')
+ else
+ assert(s == '{"foo"=>"bar", "baz"=>"qux"}' || s == '{"baz"=>"qux", "foo"=>"bar"}')
+ end
+ end
+
+ def test_to_a
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ a = ENV.to_a
+ assert_equal(2, a.size)
+ if IGNORE_CASE
+ a = a.map {|x| x.map {|y| y.upcase } }
+ assert(a == [%w(FOO BAR), %w(BAZ QUX)] || a == [%w(BAZ QUX), %w(FOO BAR)])
+ else
+ assert(a == [%w(foo bar), %w(baz qux)] || a == [%w(baz qux), %w(foo bar)])
+ end
+ end
+
+ def test_rehash
+ assert_nil(ENV.rehash)
+ end
+
+ def test_size
+ s = ENV.size
+ ENV["test"] = "foo"
+ assert_equal(s + 1, ENV.size)
+ end
+
+ def test_empty_p
+ ENV.clear
+ assert_predicate(ENV, :empty?)
+ ENV["test"] = "foo"
+ assert_not_predicate(ENV, :empty?)
+ end
+
+ def test_has_key
+ assert_not_send([ENV, :has_key?, "test"])
+ ENV["test"] = "foo"
+ assert_send([ENV, :has_key?, "test"])
+ assert_raise(ArgumentError) { ENV.has_key?("foo\0bar") }
+ end
+
+ def test_assoc
+ assert_nil(ENV.assoc("test"))
+ ENV["test"] = "foo"
+ k, v = ENV.assoc("test")
+ if IGNORE_CASE
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ assert_raise(ArgumentError) { ENV.assoc("foo\0bar") }
+ end
+
+ def test_has_value2
+ ENV.clear
+ assert_not_send([ENV, :has_value?, "foo"])
+ ENV["test"] = "foo"
+ assert_send([ENV, :has_value?, "foo"])
+ end
+
+ def test_rassoc
+ ENV.clear
+ assert_nil(ENV.rassoc("foo"))
+ ENV["foo"] = "bar"
+ ENV["test"] = "foo"
+ ENV["baz"] = "qux"
+ k, v = ENV.rassoc("foo")
+ if IGNORE_CASE
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ end
+
+ def test_to_hash
+ h = {}
+ ENV.each {|k, v| h[k] = v }
+ assert_equal(h, ENV.to_hash)
+ end
+
+ def test_to_h
+ assert_equal(ENV.to_hash, ENV.to_h)
+ end
+
+ def test_reject
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ h2 = ENV.reject {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" }
+ assert_equal(h1, h2)
+ end
+
+ def check(as, bs)
+ if IGNORE_CASE
+ as = as.map {|xs| xs.map {|x| x.upcase } }
+ bs = bs.map {|xs| xs.map {|x| x.upcase } }
+ end
+ assert_equal(as.sort, bs.sort)
+ end
+
+ def test_shift
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ a = ENV.shift
+ b = ENV.shift
+ check([a, b], [%w(foo bar), %w(baz qux)])
+ assert_nil(ENV.shift)
+ end
+
+ def test_invert
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ check(ENV.invert.to_a, [%w(bar foo), %w(qux baz)])
+ end
+
+ def test_replace
+ ENV["foo"] = "xxx"
+ ENV.replace({"foo"=>"bar", "baz"=>"qux"})
+ check(ENV.to_hash.to_a, [%w(foo bar), %w(baz qux)])
+ end
+
+ def test_update
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV.update({"baz"=>"quux","a"=>"b"})
+ check(ENV.to_hash.to_a, [%w(foo bar), %w(baz quux), %w(a b)])
+
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV.update({"baz"=>"quux","a"=>"b"}) {|k, v1, v2| v1 ? k + "_" + v1 + "_" + v2 : v2 }
+ check(ENV.to_hash.to_a, [%w(foo bar), %w(baz baz_qux_quux), %w(a b)])
+ end
+
+ def test_huge_value
+ huge_value = "bar" * 40960
+ ENV["foo"] = "bar"
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ assert_raise(Errno::EINVAL) { ENV["foo"] = huge_value }
+ assert_equal("bar", ENV["foo"])
+ else
+ assert_nothing_raised { ENV["foo"] = huge_value }
+ assert_equal(huge_value, ENV["foo"])
+ end
+ end
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_win32_blocksize
+ keys = []
+ len = 32767 - ENV.to_a.flatten.inject(0) {|r,e| r + e.bytesize + 1}
+ val = "bar" * 1000
+ key = nil
+ while (len -= val.size + (key="foo#{len}").size + 2) > 0
+ keys << key
+ ENV[key] = val
+ end
+ 1.upto(12) {|i|
+ assert_raise(Errno::EINVAL) { ENV[key] = val }
+ }
+ ensure
+ keys.each {|k| ENV.delete(k)}
+ end
+ end
+
+ def test_frozen
+ ENV[PATH_ENV] = "/"
+ ENV.each do |k, v|
+ assert_predicate(k, :frozen?)
+ assert_predicate(v, :frozen?)
+ end
+ ENV.each_key do |k|
+ assert_predicate(k, :frozen?)
+ end
+ ENV.each_value do |v|
+ assert_predicate(v, :frozen?)
+ end
+ ENV.each_key do |k|
+ assert_predicate(ENV[k], :frozen?, "[#{k.dump}]")
+ assert_predicate(ENV.fetch(k), :frozen?, "fetch(#{k.dump})")
+ end
+ end
+
+ def test_taint_aref
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV["FOO".taint]
+ end.call
+ end
+ end
+
+ def test_taint_fetch
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV.fetch("FOO".taint)
+ end.call
+ end
+ end
+
+ def test_taint_assoc
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV.assoc("FOO".taint)
+ end.call
+ end
+ end
+
+ def test_taint_rassoc
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV.rassoc("FOO".taint)
+ end.call
+ end
+ end
+
+ def test_taint_key
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV.key("FOO".taint)
+ end.call
+ end
+ end
+
+ def test_taint_key_p
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV.key?("FOO".taint)
+ end.call
+ end
+ end
+
+ def test_taint_value_p
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV.value?("FOO".taint)
+ end.call
+ end
+ end
+
+ def test_taint_aset_value
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV["FOO"] = "BAR".taint
+ end.call
+ end
+ end
+
+ def test_taint_aset_key
+ assert_raise(SecurityError) do
+ proc do
+ $SAFE = 2
+ ENV["FOO".taint] = "BAR"
+ end.call
+ end
+ end
+
+ if RUBY_PLATFORM =~ /bccwin|mswin|mingw/
+ def test_memory_leak_aset
+ bug9977 = '[ruby-dev:48323] [Bug #9977]'
+ assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9977, limit: 2.0)
+ ENV.clear
+ k = 'FOO'
+ v = (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
+ doit = proc {ENV[k] = v}
+ 500.times(&doit)
+ end;
+ end
+
+ def test_memory_leak_select
+ bug9978 = '[ruby-dev:48325] [Bug #9978]'
+ assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9978, limit: 2.0)
+ ENV.clear
+ k = 'FOO'
+ (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
+ doit = proc {ENV.select {break}}
+ 500.times(&doit)
+ end;
+ end
+
+ def test_memory_crash_select
+ assert_normal_exit(<<-'end;')
+ 1000.times {ENV["FOO#{i}"] = 'bar'}
+ ENV.select {ENV.clear}
+ end;
+ end
+
+ def test_memory_leak_shift
+ bug9983 = '[ruby-dev:48332] [Bug #9983]'
+ assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9983, limit: 2.0)
+ ENV.clear
+ k = 'FOO'
+ v = (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
+ doit = proc {ENV[k] = v; ENV.shift}
+ 500.times(&doit)
+ end;
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_eval.rb b/jni/ruby/test/ruby/test_eval.rb
new file mode 100644
index 0000000..9d8db30
--- /dev/null
+++ b/jni/ruby/test/ruby/test_eval.rb
@@ -0,0 +1,510 @@
+require 'test/unit'
+
+class TestEval < Test::Unit::TestCase
+
+ @ivar = 12
+ @@cvar = 13
+ $gvar__eval = 14
+ Const = 15
+
+ def ruby(*args)
+ args = ['-e', '$>.write($<.read)'] if args.empty?
+ ruby = EnvUtil.rubybin
+ f = IO.popen([ruby] + args, 'r+')
+ yield(f)
+ ensure
+ f.close unless !f || f.closed?
+ end
+
+ def test_eval_basic
+ assert_equal nil, eval("nil")
+ assert_equal true, eval("true")
+ assert_equal false, eval("false")
+ assert_equal self, eval("self")
+ assert_equal 1, eval("1")
+ assert_equal :sym, eval(":sym")
+
+ assert_equal 11, eval("11")
+ @ivar = 12
+ assert_equal 12, eval("@ivar")
+ assert_equal 13, eval("@@cvar")
+ assert_equal 14, eval("$gvar__eval")
+ assert_equal 15, eval("Const")
+
+ assert_equal 16, eval("7 + 9")
+ assert_equal 17, eval("17.to_i")
+ assert_equal "18", eval(%q("18"))
+ assert_equal "19", eval(%q("1#{9}"))
+
+ 1.times {
+ assert_equal 12, eval("@ivar")
+ assert_equal 13, eval("@@cvar")
+ assert_equal 14, eval("$gvar__eval")
+ assert_equal 15, eval("Const")
+ }
+ end
+
+ def test_eval_binding_basic
+ assert_equal nil, eval("nil", binding())
+ assert_equal true, eval("true", binding())
+ assert_equal false, eval("false", binding())
+ assert_equal self, eval("self", binding())
+ assert_equal 1, eval("1", binding())
+ assert_equal :sym, eval(":sym", binding())
+
+ assert_equal 11, eval("11", binding())
+ @ivar = 12
+ assert_equal 12, eval("@ivar", binding())
+ assert_equal 13, eval("@@cvar", binding())
+ assert_equal 14, eval("$gvar__eval", binding())
+ assert_equal 15, eval("Const", binding())
+
+ assert_equal 16, eval("7 + 9", binding())
+ assert_equal 17, eval("17.to_i", binding())
+ assert_equal "18", eval(%q("18"), binding())
+ assert_equal "19", eval(%q("1#{9}"), binding())
+
+ 1.times {
+ assert_equal 12, eval("@ivar")
+ assert_equal 13, eval("@@cvar")
+ assert_equal 14, eval("$gvar__eval")
+ assert_equal 15, eval("Const")
+ }
+ end
+
+ def test_module_eval_string_basic
+ c = self.class
+ assert_equal nil, c.module_eval("nil")
+ assert_equal true, c.module_eval("true")
+ assert_equal false, c.module_eval("false")
+ assert_equal c, c.module_eval("self")
+ assert_equal :sym, c.module_eval(":sym")
+ assert_equal 11, c.module_eval("11")
+ @ivar = 12
+ assert_equal 12, c.module_eval("@ivar")
+ assert_equal 13, c.module_eval("@@cvar")
+ assert_equal 14, c.module_eval("$gvar__eval")
+ assert_equal 15, c.module_eval("Const")
+ assert_equal 16, c.module_eval("7 + 9")
+ assert_equal 17, c.module_eval("17.to_i")
+ assert_equal "18", c.module_eval(%q("18"))
+ assert_equal "19", c.module_eval(%q("1#{9}"))
+
+ @ivar = 12
+ 1.times {
+ assert_equal 12, c.module_eval("@ivar")
+ assert_equal 13, c.module_eval("@@cvar")
+ assert_equal 14, c.module_eval("$gvar__eval")
+ assert_equal 15, c.module_eval("Const")
+ }
+ end
+
+ def test_module_eval_block_basic
+ c = self.class
+ assert_equal nil, c.module_eval { nil }
+ assert_equal true, c.module_eval { true }
+ assert_equal false, c.module_eval { false }
+ assert_equal c, c.module_eval { self }
+ assert_equal :sym, c.module_eval { :sym }
+ assert_equal 11, c.module_eval { 11 }
+ @ivar = 12
+ assert_equal 12, c.module_eval { @ivar }
+ assert_equal 13, c.module_eval { @@cvar }
+ assert_equal 14, c.module_eval { $gvar__eval }
+ assert_equal 15, c.module_eval { Const }
+ assert_equal 16, c.module_eval { 7 + 9 }
+ assert_equal 17, c.module_eval { "17".to_i }
+ assert_equal "18", c.module_eval { "18" }
+ assert_equal "19", c.module_eval { "1#{9}" }
+
+ @ivar = 12
+ 1.times {
+ assert_equal 12, c.module_eval { @ivar }
+ assert_equal 13, c.module_eval { @@cvar }
+ assert_equal 14, c.module_eval { $gvar__eval }
+ assert_equal 15, c.module_eval { Const }
+ }
+ end
+
+ def forall_TYPE
+ objects = [Object.new, [], nil, true, false] # TODO: check
+ objects.each do |obj|
+ obj.instance_variable_set :@ivar, 12 unless obj.frozen?
+ yield obj
+ end
+ end
+
+ def test_instance_eval_string_basic
+ forall_TYPE do |o|
+ assert_equal nil, o.instance_eval("nil")
+ assert_equal true, o.instance_eval("true")
+ assert_equal false, o.instance_eval("false")
+ assert_equal o, o.instance_eval("self")
+ assert_equal 1, o.instance_eval("1")
+ assert_equal :sym, o.instance_eval(":sym")
+
+ assert_equal 11, o.instance_eval("11")
+ assert_equal 12, o.instance_eval("@ivar") unless o.frozen?
+ assert_equal 13, o.instance_eval("@@cvar")
+ assert_equal 14, o.instance_eval("$gvar__eval")
+ assert_equal 15, o.instance_eval("Const")
+ assert_equal 16, o.instance_eval("7 + 9")
+ assert_equal 17, o.instance_eval("17.to_i")
+ assert_equal "18", o.instance_eval(%q("18"))
+ assert_equal "19", o.instance_eval(%q("1#{9}"))
+
+ 1.times {
+ assert_equal 12, o.instance_eval("@ivar") unless o.frozen?
+ assert_equal 13, o.instance_eval("@@cvar")
+ assert_equal 14, o.instance_eval("$gvar__eval")
+ assert_equal 15, o.instance_eval("Const")
+ }
+ end
+ end
+
+ def test_instance_eval_block_basic
+ forall_TYPE do |o|
+ assert_equal nil, o.instance_eval { nil }
+ assert_equal true, o.instance_eval { true }
+ assert_equal false, o.instance_eval { false }
+ assert_equal o, o.instance_eval { self }
+ assert_equal 1, o.instance_eval { 1 }
+ assert_equal :sym, o.instance_eval { :sym }
+
+ assert_equal 11, o.instance_eval { 11 }
+ assert_equal 12, o.instance_eval { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_eval { @@cvar }
+ assert_equal 14, o.instance_eval { $gvar__eval }
+ assert_equal 15, o.instance_eval { Const }
+ assert_equal 16, o.instance_eval { 7 + 9 }
+ assert_equal 17, o.instance_eval { 17.to_i }
+ assert_equal "18", o.instance_eval { "18" }
+ assert_equal "19", o.instance_eval { "1#{9}" }
+
+ 1.times {
+ assert_equal 12, o.instance_eval { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_eval { @@cvar }
+ assert_equal 14, o.instance_eval { $gvar__eval }
+ assert_equal 15, o.instance_eval { Const }
+ }
+ end
+ end
+
+ def test_instance_eval_block_self
+ # instance_eval(&block)'s self must not be sticky (jruby/jruby#2060)
+ pr = proc { self }
+ assert_equal self, pr.call
+ o = Object.new
+ assert_equal o, o.instance_eval(&pr)
+ assert_equal self, pr.call
+ end
+
+ def test_instance_eval_cvar
+ [Object.new, [], 7, :sym, true, false, nil].each do |obj|
+ assert_equal(13, obj.instance_eval("@@cvar"))
+ assert_equal(13, obj.instance_eval{@@cvar})
+ # assert_raise(NameError){obj.instance_eval("@@cvar")}
+ # assert_raise(NameError){obj.instance_eval{@@cvar}}
+ end
+ end
+
+ def test_instance_eval_method
+ bug2788 = '[ruby-core:28324]'
+ [Object.new, [], nil, true, false].each do |o|
+ assert_nothing_raised(TypeError, "#{bug2788} (#{o.inspect})") do
+ o.instance_eval {
+ def defd_using_instance_eval() :ok end
+ }
+ end
+ assert_equal(:ok, o.defd_using_instance_eval)
+ class << o
+ remove_method :defd_using_instance_eval
+ end
+ end
+ end
+
+ def test_instance_eval_on_argf_singleton_class
+ bug8188 = '[ruby-core:53839] [Bug #8188]'
+ assert_warning('', bug8188) do
+ ARGF.singleton_class.instance_eval{}
+ end
+ end
+
+ class Foo
+ Bar = 2
+ end
+
+ def test_instance_eval_const
+ bar = nil
+ assert_nothing_raised(NameError) do
+ bar = Foo.new.instance_eval("Bar")
+ end
+ assert_equal(2, bar)
+ end
+
+ #
+ # From ruby/test/ruby/test_eval.rb
+ #
+
+ def make_test_binding
+ local1 = "local1"
+ lambda {
+ local2 = "local2"
+ return binding
+ }.call
+ end
+
+ def test_eval_orig
+ assert_nil(eval(""))
+ bad=false
+ eval 'while false; bad = true; print "foo\n" end'
+ assert(!bad)
+
+ assert(eval('TRUE'))
+ assert(eval('true'))
+ assert(!eval('NIL'))
+ assert(!eval('nil'))
+ assert(!eval('FALSE'))
+ assert(!eval('false'))
+
+ $foo = 'assert(true)'
+ begin
+ eval $foo
+ rescue
+ assert(false)
+ end
+
+ assert_equal('assert(true)', eval("$foo"))
+ assert_equal(true, eval("true"))
+ i = 5
+ assert(eval("i == 5"))
+ assert_equal(5, eval("i"))
+ assert(eval("defined? i"))
+
+ x = make_test_binding
+ assert_equal("local1", eval("local1", x)) # normal local var
+ assert_equal("local2", eval("local2", x)) # nested local var
+ bad = true
+ begin
+ p eval("local1")
+ rescue NameError # must raise error
+ bad = false
+ end
+ assert(!bad)
+
+ # !! use class_eval to avoid nested definition
+ x = self.class.class_eval %q(
+ module EvTest
+ EVTEST1 = 25
+ evtest2 = 125
+ binding
+ end
+ )
+ assert_equal(25, eval("EVTEST1", x)) # constant in module
+ assert_equal(125, eval("evtest2", x)) # local var in module
+ bad = true
+ begin
+ eval("EVTEST1")
+ rescue NameError # must raise error
+ bad = false
+ end
+ assert(!bad)
+
+ if false
+ # Ruby 2.0 doesn't see Proc as Binding
+ x = proc{}
+ eval "i4 = 1", x
+ assert_equal(1, eval("i4", x))
+ x = proc{proc{}}.call
+ eval "i4 = 22", x
+ assert_equal(22, eval("i4", x))
+ t = []
+ x = proc{proc{}}.call
+ eval "(0..9).each{|i5| t[i5] = proc{i5*2}}", x
+ assert_equal(8, t[4].call)
+ end
+
+ x = binding
+ eval "i = 1", x
+ assert_equal(1, eval("i", x))
+ x = proc{binding}.call
+ eval "i = 22", x
+ assert_equal(22, eval("i", x))
+ t = []
+ x = proc{binding}.call
+ eval "(0..9).each{|i5| t[i5] = proc{i5*2}}", x
+ assert_equal(8, t[4].call)
+ x = proc{binding}.call
+ eval "for i6 in 1..1; j6=i6; end", x
+ assert(eval("defined? i6", x))
+ assert(eval("defined? j6", x))
+
+ proc {
+ p = binding
+ eval "foo11 = 1", p
+ foo22 = 5
+ proc{foo11=22}.call
+ proc{foo22=55}.call
+ # assert_equal(eval("foo11"), eval("foo11", p))
+ # assert_equal(1, eval("foo11"))
+ assert_equal(eval("foo22"), eval("foo22", p))
+ assert_equal(55, eval("foo22"))
+ }.call
+
+ if false
+ # Ruby 2.0 doesn't see Proc as Binding
+ p1 = proc{i7 = 0; proc{i7}}.call
+ assert_equal(0, p1.call)
+ eval "i7=5", p1
+ assert_equal(5, p1.call)
+ assert(!defined?(i7))
+ end
+
+ if false
+ # Ruby 2.0 doesn't see Proc as Binding
+ p1 = proc{i7 = 0; proc{i7}}.call
+ i7 = nil
+ assert_equal(0, p1.call)
+ eval "i7=1", p1
+ assert_equal(1, p1.call)
+ eval "i7=5", p1
+ assert_equal(5, p1.call)
+ assert_nil(i7)
+ end
+ end
+
+ def test_nil_instance_eval_cvar
+ def nil.test_binding
+ binding
+ end
+ bb = eval("nil.instance_eval \"binding\"", nil.test_binding)
+ assert_raise(NameError, "[ruby-dev:24103]") { eval("@@a", bb) }
+ class << nil
+ remove_method :test_binding
+ end
+ end
+
+ def test_fixnum_instance_eval_cvar
+ assert_raise(NameError, "[ruby-dev:24213]") { 1.instance_eval "@@a" }
+ end
+
+ def test_cvar_scope_with_instance_eval
+ # TODO: check
+ Fixnum.class_eval "@@test_cvar_scope_with_instance_eval = 1" # depends on [ruby-dev:24229]
+ @@test_cvar_scope_with_instance_eval = 4
+ assert_equal(4, 1.instance_eval("@@test_cvar_scope_with_instance_eval"), "[ruby-dev:24223]")
+ Fixnum.__send__(:remove_class_variable, :@@test_cvar_scope_with_instance_eval)
+ end
+
+ def test_eval_and_define_method
+ assert_nothing_raised("[ruby-dev:24228]") {
+ def temporally_method_for_test_eval_and_define_method(&block)
+ lambda {
+ class << Object.new; self end.send(:define_method, :zzz, &block)
+ }
+ end
+ v = eval("temporally_method_for_test_eval_and_define_method {}")
+ {}[0] = {}
+ v.call
+ }
+ end
+
+ def test_define_method_block
+ cc = Class.new do
+ define_method(:foo) {|&block|
+ block.call if block
+ }
+ end
+
+ c = cc.new
+ x = "ng"
+ c.foo() {x = "ok"}
+ assert_equal("ok", x)
+ end
+
+ def test_define_method_toplevel
+ feature6609 = '[ruby-core:45715]'
+ main = eval("self", TOPLEVEL_BINDING)
+ assert_nothing_raised(NoMethodError, feature6609) do
+ main.instance_eval do
+ define_method("feature6609_block") {feature6609}
+ end
+ end
+ assert_equal(feature6609, feature6609_block)
+
+ assert_nothing_raised(NoMethodError, feature6609) do
+ main.instance_eval do
+ define_method("feature6609_method", Object.instance_method(:feature6609_block))
+ end
+ end
+ assert_equal(feature6609, feature6609_method)
+ end
+
+ def test_eval_using_integer_as_binding
+ assert_raise(TypeError) { eval("", 1) }
+ end
+
+ def test_eval_raise
+ assert_raise(RuntimeError) { eval("raise ''") }
+ end
+
+ def test_eval_with_toplevel_binding # [ruby-dev:37142]
+ ruby("-e", "x = 0; eval('p x', TOPLEVEL_BINDING)") do |f|
+ f.close_write
+ assert_equal("0", f.read.chomp)
+ end
+ end
+
+ def test_eval_ascii_incompatible
+ assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-16be"))}
+ assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-16le"))}
+ assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-32be"))}
+ assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-32le"))}
+ end
+
+ def test_instance_eval_method_proc
+ bug3860 = Class.new do
+ def initialize(a);
+ @a=a
+ end
+ def get(*args)
+ @a
+ end
+ end
+ foo = bug3860.new 1
+ foo_pr = foo.method(:get).to_proc
+ result = foo.instance_eval(&foo_pr)
+ assert_equal(1, result, 'Bug #3786, Bug #3860, [ruby-core:32501]')
+ end
+
+ def test_file_encoding
+ fname = "\u{3042}".encode("euc-jp")
+ assert_equal(fname, eval("__FILE__", nil, fname, 1))
+ end
+
+ def test_eval_location_fstring
+ o = Object.new
+ o.instance_eval "def foo() end", "generated code"
+ o.instance_eval "def bar() end", "generated code"
+
+ a, b = o.method(:foo).source_location[0],
+ o.method(:bar).source_location[0]
+
+ assert_same a, b
+ end
+
+ def test_gced_binding_block
+ assert_normal_exit %q{
+ def m
+ binding
+ end
+ GC.stress = true
+ b = nil
+ tap do
+ b = m {}
+ end
+ 0.times.to_a
+ b.eval('yield')
+ }, '[Bug #10368]'
+ end
+end
diff --git a/jni/ruby/test/ruby/test_exception.rb b/jni/ruby/test/ruby/test_exception.rb
new file mode 100644
index 0000000..c68d226
--- /dev/null
+++ b/jni/ruby/test/ruby/test_exception.rb
@@ -0,0 +1,701 @@
+require 'test/unit'
+require 'tempfile'
+
+class TestException < Test::Unit::TestCase
+ def test_exception_rescued
+ begin
+ raise "this must be handled"
+ assert(false)
+ rescue
+ assert(true)
+ end
+ end
+
+ def test_exception_retry
+ bad = true
+ begin
+ raise "this must be handled no.2"
+ rescue
+ if bad
+ bad = false
+ retry
+ assert(false)
+ end
+ end
+ assert(true)
+ end
+
+ def test_exception_in_rescue
+ string = "this must be handled no.3"
+ assert_raise_with_message(RuntimeError, string) do
+ begin
+ raise "exception in rescue clause"
+ rescue
+ raise string
+ end
+ assert(false)
+ end
+ end
+
+ def test_exception_in_ensure
+ string = "exception in ensure clause"
+ assert_raise_with_message(RuntimeError, string) do
+ begin
+ raise "this must be handled no.4"
+ ensure
+ assert_instance_of(RuntimeError, $!)
+ assert_equal("this must be handled no.4", $!.message)
+ raise "exception in ensure clause"
+ end
+ assert(false)
+ end
+ end
+
+ def test_exception_ensure
+ bad = true
+ begin
+ begin
+ raise "this must be handled no.5"
+ ensure
+ bad = false
+ end
+ rescue
+ end
+ assert(!bad)
+ end
+
+ def test_exception_ensure_2 # just duplication?
+ bad = true
+ begin
+ begin
+ raise "this must be handled no.6"
+ ensure
+ bad = false
+ end
+ rescue
+ end
+ assert(!bad)
+ end
+
+ def test_errinfo_in_debug
+ bug9568 = EnvUtil.labeled_class("[ruby-core:61091] [Bug #9568]", RuntimeError) do
+ def to_s
+ require '\0'
+ rescue LoadError
+ self.class.to_s
+ end
+ end
+
+ err = EnvUtil.verbose_warning do
+ assert_raise(bug9568) do
+ $DEBUG, debug = true, $DEBUG
+ begin
+ raise bug9568
+ ensure
+ $DEBUG = debug
+ end
+ end
+ end
+ assert_include(err, bug9568.to_s)
+ end
+
+ def test_errinfo_encoding_in_debug
+ exc = Module.new {break class_eval("class C\u{30a8 30e9 30fc} < RuntimeError; self; end".encode(Encoding::EUC_JP))}
+ exc.inspect
+
+ err = EnvUtil.verbose_warning do
+ assert_raise(exc) do
+ $DEBUG, debug = true, $DEBUG
+ begin
+ raise exc
+ ensure
+ $DEBUG = debug
+ end
+ end
+ end
+ assert_include(err, exc.to_s)
+ end
+
+ def test_break_ensure
+ bad = true
+ while true
+ begin
+ break
+ ensure
+ bad = false
+ end
+ end
+ assert(!bad)
+ end
+
+ def test_catch_no_throw
+ assert_equal(:foo, catch {:foo})
+ end
+
+ def test_catch_throw
+ result = catch(:foo) {
+ loop do
+ loop do
+ throw :foo, true
+ break
+ end
+ assert(false, "should not reach here")
+ end
+ false
+ }
+ assert(result)
+ end
+
+ def test_catch_throw_noarg
+ assert_nothing_raised(UncaughtThrowError) {
+ result = catch {|obj|
+ throw obj, :ok
+ assert(false, "should not reach here")
+ }
+ assert_equal(:ok, result)
+ }
+ end
+
+ def test_uncaught_throw
+ tag = nil
+ e = assert_raise_with_message(UncaughtThrowError, /uncaught throw/) {
+ catch("foo") {|obj|
+ tag = obj.dup
+ throw tag, :ok
+ assert(false, "should not reach here")
+ }
+ assert(false, "should not reach here")
+ }
+ assert_not_nil(tag)
+ assert_same(tag, e.tag)
+ assert_equal(:ok, e.value)
+ end
+
+ def test_catch_throw_in_require
+ bug7185 = '[ruby-dev:46234]'
+ Tempfile.create(["dep", ".rb"]) {|t|
+ t.puts("throw :extdep, 42")
+ t.close
+ assert_equal(42, assert_throw(:extdep, bug7185) {require t.path}, bug7185)
+ }
+ end
+
+ def test_else_no_exception
+ begin
+ assert(true)
+ rescue
+ assert(false)
+ else
+ assert(true)
+ end
+ end
+
+ def test_else_raised
+ begin
+ assert(true)
+ raise
+ assert(false)
+ rescue
+ assert(true)
+ else
+ assert(false)
+ end
+ end
+
+ def test_else_nested_no_exception
+ begin
+ assert(true)
+ begin
+ assert(true)
+ rescue
+ assert(false)
+ else
+ assert(true)
+ end
+ assert(true)
+ rescue
+ assert(false)
+ else
+ assert(true)
+ end
+ end
+
+ def test_else_nested_rescued
+ begin
+ assert(true)
+ begin
+ assert(true)
+ raise
+ assert(false)
+ rescue
+ assert(true)
+ else
+ assert(false)
+ end
+ assert(true)
+ rescue
+ assert(false)
+ else
+ assert(true)
+ end
+ end
+
+ def test_else_nested_unrescued
+ begin
+ assert(true)
+ begin
+ assert(true)
+ rescue
+ assert(false)
+ else
+ assert(true)
+ end
+ assert(true)
+ raise
+ assert(false)
+ rescue
+ assert(true)
+ else
+ assert(false)
+ end
+ end
+
+ def test_else_nested_rescued_reraise
+ begin
+ assert(true)
+ begin
+ assert(true)
+ raise
+ assert(false)
+ rescue
+ assert(true)
+ else
+ assert(false)
+ end
+ assert(true)
+ raise
+ assert(false)
+ rescue
+ assert(true)
+ else
+ assert(false)
+ end
+ end
+
+ def test_raise_with_wrong_number_of_arguments
+ assert_raise(TypeError) { raise nil }
+ assert_raise(TypeError) { raise 1, 1 }
+ assert_raise(ArgumentError) { raise 1, 1, 1, 1 }
+ end
+
+ def test_type_error_message_encoding
+ c = eval("Module.new do break class C\u{4032}; self; end; end")
+ o = c.new
+ assert_raise_with_message(TypeError, /C\u{4032}/) do
+ ""[o]
+ end
+ c.class_eval {def to_int; self; end}
+ assert_raise_with_message(TypeError, /C\u{4032}/) do
+ ""[o]
+ end
+ c.class_eval {def to_a; self; end}
+ assert_raise_with_message(TypeError, /C\u{4032}/) do
+ [*o]
+ end
+ end
+
+ def test_errat
+ assert_in_out_err([], "p $@", %w(nil), [])
+
+ assert_in_out_err([], "$@ = 1", [], /\$! not set \(ArgumentError\)$/)
+
+ assert_in_out_err([], <<-INPUT, [], /backtrace must be Array of String \(TypeError\)$/)
+ begin
+ raise
+ rescue
+ $@ = 1
+ end
+ INPUT
+
+ assert_in_out_err([], <<-INPUT, [], /^foo: unhandled exception$/)
+ begin
+ raise
+ rescue
+ $@ = 'foo'
+ raise
+ end
+ INPUT
+
+ assert_in_out_err([], <<-INPUT, [], /^foo: unhandled exception\s+from bar\s+from baz$/)
+ begin
+ raise
+ rescue
+ $@ = %w(foo bar baz)
+ raise
+ end
+ INPUT
+ end
+
+ def test_thread_signal_location
+ _, stderr, _ = EnvUtil.invoke_ruby("--disable-gems -d", <<-RUBY, false, true)
+Thread.start do
+ begin
+ Process.kill(:INT, $$)
+ ensure
+ raise "in ensure"
+ end
+end.join
+ RUBY
+ assert_not_match(/:0/, stderr, "[ruby-dev:39116]")
+ end
+
+ def test_errinfo
+ begin
+ raise "foo"
+ assert(false)
+ rescue => e
+ assert_equal(e, $!)
+ 1.times { assert_equal(e, $!) }
+ end
+
+ assert_equal(nil, $!)
+ end
+
+ def test_inspect
+ assert_equal("#<Exception: Exception>", Exception.new.inspect)
+
+ e = Class.new(Exception)
+ e.class_eval do
+ def to_s; ""; end
+ end
+ assert_equal(e.inspect, e.new.inspect)
+ end
+
+ def test_to_s
+ e = StandardError.new("foo")
+ assert_equal("foo", e.to_s)
+
+ def (s = Object.new).to_s
+ "bar"
+ end
+ e = StandardError.new(s)
+ assert_equal("bar", e.to_s)
+ end
+
+ def test_set_backtrace
+ e = Exception.new
+
+ e.set_backtrace("foo")
+ assert_equal(["foo"], e.backtrace)
+
+ e.set_backtrace(%w(foo bar baz))
+ assert_equal(%w(foo bar baz), e.backtrace)
+
+ assert_raise(TypeError) { e.set_backtrace(1) }
+ assert_raise(TypeError) { e.set_backtrace([1]) }
+ end
+
+ def test_exit_success_p
+ begin
+ exit
+ rescue SystemExit => e
+ end
+ assert_send([e, :success?], "success by default")
+
+ begin
+ exit(true)
+ rescue SystemExit => e
+ end
+ assert_send([e, :success?], "true means success")
+
+ begin
+ exit(false)
+ rescue SystemExit => e
+ end
+ assert_not_send([e, :success?], "false means failure")
+
+ begin
+ abort
+ rescue SystemExit => e
+ end
+ assert_not_send([e, :success?], "abort means failure")
+ end
+
+ def test_nomethoderror
+ bug3237 = '[ruby-core:29948]'
+ str = "\u2600"
+ id = :"\u2604"
+ msg = "undefined method `#{id}' for #{str.inspect}:String"
+ assert_raise_with_message(NoMethodError, msg, bug3237) do
+ str.__send__(id)
+ end
+ end
+
+ def test_errno
+ assert_equal(Encoding.find("locale"), Errno::EINVAL.new.message.encoding)
+ end
+
+ def test_too_many_args_in_eval
+ bug5720 = '[ruby-core:41520]'
+ arg_string = (0...140000).to_a.join(", ")
+ assert_raise(SystemStackError, bug5720) {eval "raise(#{arg_string})"}
+ end
+
+ def test_systemexit_new
+ e0 = SystemExit.new
+ assert_equal(0, e0.status)
+ assert_equal("SystemExit", e0.message)
+ ei = SystemExit.new(3)
+ assert_equal(3, ei.status)
+ assert_equal("SystemExit", ei.message)
+ es = SystemExit.new("msg")
+ assert_equal(0, es.status)
+ assert_equal("msg", es.message)
+ eis = SystemExit.new(7, "msg")
+ assert_equal(7, eis.status)
+ assert_equal("msg", eis.message)
+
+ bug5728 = '[ruby-dev:44951]'
+ et = SystemExit.new(true)
+ assert_equal(true, et.success?, bug5728)
+ assert_equal("SystemExit", et.message, bug5728)
+ ef = SystemExit.new(false)
+ assert_equal(false, ef.success?, bug5728)
+ assert_equal("SystemExit", ef.message, bug5728)
+ ets = SystemExit.new(true, "msg")
+ assert_equal(true, ets.success?, bug5728)
+ assert_equal("msg", ets.message, bug5728)
+ efs = SystemExit.new(false, "msg")
+ assert_equal(false, efs.success?, bug5728)
+ assert_equal("msg", efs.message, bug5728)
+ end
+
+ def test_exception_in_name_error_to_str
+ bug5575 = '[ruby-core:41612]'
+ Tempfile.create(["test_exception_in_name_error_to_str", ".rb"]) do |t|
+ t.puts <<-EOC
+ begin
+ BasicObject.new.inspect
+ rescue
+ $!.inspect
+ end
+ EOC
+ t.close
+ assert_nothing_raised(NameError, bug5575) do
+ load(t.path)
+ end
+ end
+ end
+
+ def test_equal
+ bug5865 = '[ruby-core:41979]'
+ assert_equal(RuntimeError.new("a"), RuntimeError.new("a"), bug5865)
+ assert_not_equal(RuntimeError.new("a"), StandardError.new("a"), bug5865)
+ end
+
+ def test_exception_in_exception_equal
+ bug5865 = '[ruby-core:41979]'
+ Tempfile.create(["test_exception_in_exception_equal", ".rb"]) do |t|
+ t.puts <<-EOC
+ o = Object.new
+ def o.exception(arg)
+ end
+ _ = RuntimeError.new("a") == o
+ EOC
+ t.close
+ assert_nothing_raised(ArgumentError, bug5865) do
+ load(t.path)
+ end
+ end
+ end
+
+ Bug4438 = '[ruby-core:35364]'
+
+ def test_rescue_single_argument
+ assert_raise(TypeError, Bug4438) do
+ begin
+ raise
+ rescue 1
+ end
+ end
+ end
+
+ def test_rescue_splat_argument
+ assert_raise(TypeError, Bug4438) do
+ begin
+ raise
+ rescue *Array(1)
+ end
+ end
+ end
+
+ def test_to_s_taintness_propagation
+ for exc in [Exception, NameError]
+ m = "abcdefg"
+ e = exc.new(m)
+ e.taint
+ s = e.to_s
+ assert_equal(false, m.tainted?,
+ "#{exc}#to_s should not propagate taintness")
+ assert_equal(false, s.tainted?,
+ "#{exc}#to_s should not propagate taintness")
+ end
+
+ o = Object.new
+ def o.to_str
+ "foo"
+ end
+ o.taint
+ e = NameError.new(o)
+ s = e.to_s
+ assert_equal(false, s.tainted?)
+ end
+
+ def m
+ m(&->{return 0})
+ 42
+ end
+
+ def test_stackoverflow
+ feature6216 = '[ruby-core:43794] [Feature #6216]'
+ e = assert_raise(SystemStackError, feature6216) {m}
+ level = e.backtrace.size
+ assert_operator(level, :>, 10, feature6216)
+
+ feature6216 = '[ruby-core:63377] [Feature #6216]'
+ e = assert_raise(SystemStackError, feature6216) {raise e}
+ assert_equal(level, e.backtrace.size, feature6216)
+ end
+
+ def test_machine_stackoverflow
+ bug9109 = '[ruby-dev:47804] [Bug #9109]'
+ assert_separately(%w[--disable-gem], <<-SRC)
+ assert_raise(SystemStackError, #{bug9109.dump}) {
+ h = {a: ->{h[:a].call}}
+ h[:a].call
+ }
+ SRC
+ rescue SystemStackError
+ end
+
+ def test_machine_stackoverflow_by_define_method
+ bug9454 = '[ruby-core:60113] [Bug #9454]'
+ assert_separately(%w[--disable-gem], <<-SRC)
+ assert_raise(SystemStackError, #{bug9454.dump}) {
+ define_method(:foo) {self.foo}
+ self.foo
+ }
+ SRC
+ rescue SystemStackError
+ end
+
+ def test_cause
+ msg = "[Feature #8257]"
+ cause = nil
+ e = assert_raise(StandardError) {
+ begin
+ raise msg
+ rescue => e
+ cause = e.cause
+ raise StandardError
+ end
+ }
+ assert_nil(cause, msg)
+ cause = e.cause
+ assert_instance_of(RuntimeError, cause, msg)
+ assert_equal(msg, cause.message, msg)
+ end
+
+ def test_cause_reraised
+ msg = "[Feature #8257]"
+ e = assert_raise(RuntimeError) {
+ begin
+ raise msg
+ rescue => e
+ raise e
+ end
+ }
+ assert_not_same(e, e.cause, "#{msg}: should not be recursive")
+ end
+
+ def test_raise_with_cause
+ msg = "[Feature #8257]"
+ cause = ArgumentError.new("foobar")
+ e = assert_raise(RuntimeError) {raise msg, cause: cause}
+ assert_same(cause, e.cause)
+ end
+
+ def test_cause_with_no_arguments
+ cause = ArgumentError.new("foobar")
+ assert_raise_with_message(ArgumentError, /with no arguments/) do
+ raise cause: cause
+ end
+ end
+
+ def test_unknown_option
+ bug = '[ruby-core:63203] [Feature #8257] should pass unknown options'
+
+ exc = Class.new(RuntimeError) do
+ attr_reader :arg
+ def initialize(msg = nil)
+ @arg = msg
+ super(msg)
+ end
+ end
+
+ e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar"}
+ assert_equal({"foo" => "bar", foo: "bar"}, e.arg, bug)
+
+ e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar", cause: "zzz"}
+ assert_equal({"foo" => "bar", foo: "bar"}, e.arg, bug)
+
+ e = assert_raise(exc, bug) {raise exc, {}}
+ assert_equal({}, e.arg, bug)
+ end
+
+ def test_anonymous_message
+ assert_in_out_err([], "raise Class.new(RuntimeError), 'foo'", [], /foo\n/)
+ end
+
+ def test_name_error_info
+ obj = BasicObject.new
+ e = assert_raise(NameError) {
+ obj.instance_eval("Object")
+ }
+ assert_equal(:Object, e.name)
+ e = assert_raise(NameError) {
+ obj.instance_eval {foo}
+ }
+ assert_equal(:foo, e.name)
+ e = assert_raise(NoMethodError) {
+ obj.foo(1, 2)
+ }
+ assert_equal(:foo, e.name)
+ assert_equal([1, 2], e.args)
+ end
+
+ def test_output_string_encoding
+ # "\x82\xa0" in cp932 is "\u3042" (Japanese hiragana 'a')
+ # change $stderr to force calling rb_io_write() instead of fwrite()
+ assert_in_out_err(["-Eutf-8:cp932"], '# coding: cp932
+$stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
+ assert_equal 1, outs.size
+ assert_equal 0, errs.size
+ err = outs.first.force_encoding('utf-8')
+ assert err.valid_encoding?, 'must be valid encoding'
+ assert_match /\u3042/, err
+ end
+ end
+
+ def test_multibyte_and_newline
+ bug10727 = '[ruby-core:67473] [Bug #10727]'
+ assert_in_out_err([], <<-'end;', [], /\u{306b 307b 3093 3054} \(E\)\n\u{6539 884c}/, bug10727, encoding: "UTF-8")
+ class E < StandardError
+ def initialize
+ super("\u{306b 307b 3093 3054}\n\u{6539 884c}")
+ end
+ end
+ raise E
+ end;
+ end
+end
diff --git a/jni/ruby/test/ruby/test_fiber.rb b/jni/ruby/test/ruby/test_fiber.rb
new file mode 100644
index 0000000..4684a65
--- /dev/null
+++ b/jni/ruby/test/ruby/test_fiber.rb
@@ -0,0 +1,347 @@
+require 'test/unit'
+require 'fiber'
+EnvUtil.suppress_warning {require 'continuation'}
+require 'tmpdir'
+
+class TestFiber < Test::Unit::TestCase
+ def test_normal
+ assert_equal(:ok2,
+ Fiber.new{|e|
+ assert_equal(:ok1, e)
+ Fiber.yield :ok2
+ }.resume(:ok1)
+ )
+ assert_equal([:a, :b], Fiber.new{|a, b| [a, b]}.resume(:a, :b))
+ end
+
+ def test_argument
+ assert_equal(4, Fiber.new {|i=4| i}.resume)
+ end
+
+ def test_term
+ assert_equal(:ok, Fiber.new{:ok}.resume)
+ assert_equal([:a, :b, :c, :d, :e],
+ Fiber.new{
+ Fiber.new{
+ Fiber.new{
+ Fiber.new{
+ [:a]
+ }.resume + [:b]
+ }.resume + [:c]
+ }.resume + [:d]
+ }.resume + [:e])
+ end
+
+ def test_many_fibers
+ max = 10_000
+ assert_equal(max, max.times{
+ Fiber.new{}
+ })
+ GC.start # force collect created fibers
+ assert_equal(max,
+ max.times{|i|
+ Fiber.new{
+ }.resume
+ }
+ )
+ GC.start # force collect created fibers
+ end
+
+ def test_many_fibers_with_threads
+ assert_normal_exit <<-SRC, timeout: 60
+ max = 1000
+ @cnt = 0
+ (1..100).map{|ti|
+ Thread.new{
+ max.times{|i|
+ Fiber.new{
+ @cnt += 1
+ }.resume
+ }
+ }
+ }.each{|t|
+ t.join
+ }
+ SRC
+ end
+
+ def test_error
+ assert_raise(ArgumentError){
+ Fiber.new # Fiber without block
+ }
+ assert_raise(FiberError){
+ f = Fiber.new{}
+ Thread.new{f.resume}.join # Fiber yielding across thread
+ }
+ assert_raise(FiberError){
+ f = Fiber.new{}
+ f.resume
+ f.resume
+ }
+ assert_raise(RuntimeError){
+ Fiber.new{
+ @c = callcc{|c| @c = c}
+ }.resume
+ @c.call # cross fiber callcc
+ }
+ assert_raise(RuntimeError){
+ Fiber.new{
+ raise
+ }.resume
+ }
+ assert_raise(FiberError){
+ Fiber.yield
+ }
+ assert_raise(FiberError){
+ fib = Fiber.new{
+ fib.resume
+ }
+ fib.resume
+ }
+ assert_raise(FiberError){
+ fib = Fiber.new{
+ Fiber.new{
+ fib.resume
+ }.resume
+ }
+ fib.resume
+ }
+ end
+
+ def test_return
+ assert_raise(LocalJumpError){
+ Fiber.new do
+ return
+ end.resume
+ }
+ end
+
+ def test_throw
+ assert_raise(UncaughtThrowError){
+ Fiber.new do
+ throw :a
+ end.resume
+ }
+ end
+
+ def test_transfer
+ ary = []
+ f2 = nil
+ f1 = Fiber.new{
+ ary << f2.transfer(:foo)
+ :ok
+ }
+ f2 = Fiber.new{
+ ary << f1.transfer(:baz)
+ :ng
+ }
+ assert_equal(:ok, f1.transfer)
+ assert_equal([:baz], ary)
+ end
+
+ def test_tls
+ #
+ def tvar(var, val)
+ old = Thread.current[var]
+ begin
+ Thread.current[var] = val
+ yield
+ ensure
+ Thread.current[var] = old
+ end
+ end
+
+ fb = Fiber.new {
+ assert_equal(nil, Thread.current[:v]); tvar(:v, :x) {
+ assert_equal(:x, Thread.current[:v]); Fiber.yield
+ assert_equal(:x, Thread.current[:v]); }
+ assert_equal(nil, Thread.current[:v]); Fiber.yield
+ raise # unreachable
+ }
+
+ assert_equal(nil, Thread.current[:v]); tvar(:v,1) {
+ assert_equal(1, Thread.current[:v]); tvar(:v,3) {
+ assert_equal(3, Thread.current[:v]); fb.resume
+ assert_equal(3, Thread.current[:v]); }
+ assert_equal(1, Thread.current[:v]); }
+ assert_equal(nil, Thread.current[:v]); fb.resume
+ assert_equal(nil, Thread.current[:v]);
+ end
+
+ def test_alive
+ fib = Fiber.new{Fiber.yield}
+ assert_equal(true, fib.alive?)
+ fib.resume
+ assert_equal(true, fib.alive?)
+ fib.resume
+ assert_equal(false, fib.alive?)
+ end
+
+ def test_resume_self
+ f = Fiber.new {f.resume}
+ assert_raise(FiberError, '[ruby-core:23651]') {f.transfer}
+ end
+
+ def test_fiber_transfer_segv
+ assert_normal_exit %q{
+ require 'fiber'
+ f2 = nil
+ f1 = Fiber.new{ f2.resume }
+ f2 = Fiber.new{ f1.resume }
+ f1.transfer
+ }, '[ruby-dev:40833]'
+ assert_normal_exit %q{
+ require 'fiber'
+ Fiber.new{}.resume
+ 1.times{Fiber.current.transfer}
+ }
+ end
+
+ def test_resume_root_fiber
+ assert_raise(FiberError) do
+ Thread.new do
+ Fiber.current.resume
+ end.join
+ end
+ end
+
+ def test_gc_root_fiber
+ bug4612 = '[ruby-core:35891]'
+
+ assert_normal_exit %q{
+ require 'fiber'
+ GC.stress = true
+ Thread.start{ Fiber.current; nil }.join
+ GC.start
+ }, bug4612
+ end
+
+ def test_no_valid_cfp
+ bug5083 = '[ruby-dev:44208]'
+ assert_equal([], Fiber.new(&Module.method(:nesting)).resume, bug5083)
+ assert_instance_of(Class, Fiber.new(&Class.new.method(:undef_method)).resume(:to_s), bug5083)
+ end
+
+ def test_prohibit_resume_transfered_fiber
+ assert_raise(FiberError){
+ root_fiber = Fiber.current
+ f = Fiber.new{
+ root_fiber.transfer
+ }
+ f.transfer
+ f.resume
+ }
+ assert_raise(FiberError){
+ g=nil
+ f=Fiber.new{
+ g.resume
+ g.resume
+ }
+ g=Fiber.new{
+ f.resume
+ f.resume
+ }
+ f.transfer
+ }
+ end
+
+ def test_fork_from_fiber
+ begin
+ pid = Process.fork{}
+ rescue NotImplementedError
+ return
+ else
+ Process.wait(pid)
+ end
+ bug5700 = '[ruby-core:41456]'
+ assert_nothing_raised(bug5700) do
+ Fiber.new{ pid = fork {} }.resume
+ end
+ pid, status = Process.waitpid2(pid)
+ assert_equal(0, status.exitstatus, bug5700)
+ assert_equal(false, status.signaled?, bug5700)
+ end
+
+ def test_exit_in_fiber
+ bug5993 = '[ruby-dev:45218]'
+ assert_nothing_raised(bug5993) do
+ Thread.new{ Fiber.new{ Thread.exit }.resume; raise "unreachable" }.join
+ end
+ end
+
+ def test_fatal_in_fiber
+ assert_in_out_err(["-r-test-/fatal/rb_fatal", "-e", <<-EOS], "", [], /ok/)
+ Fiber.new{
+ rb_fatal "ok"
+ }.resume
+ puts :ng # unreachable.
+ EOS
+ end
+
+ def invoke_rec script, vm_stack_size, machine_stack_size, use_length = true
+ env = {}
+ env['RUBY_FIBER_VM_STACK_SIZE'] = vm_stack_size.to_s if vm_stack_size
+ env['RUBY_FIBER_MACHINE_STACK_SIZE'] = machine_stack_size.to_s if machine_stack_size
+ out, _ = Dir.mktmpdir("test_fiber") {|tmpdir|
+ EnvUtil.invoke_ruby([env, '-e', script], '', true, true, chdir: tmpdir)
+ }
+ use_length ? out.length : out
+ end
+
+ def test_stack_size
+ h_default = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', nil, nil, false))
+ h_0 = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', 0, 0, false))
+ h_large = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', 1024 * 1024 * 10, 1024 * 1024 * 10, false))
+
+ assert_operator(h_default[:fiber_vm_stack_size], :>, h_0[:fiber_vm_stack_size])
+ assert_operator(h_default[:fiber_vm_stack_size], :<, h_large[:fiber_vm_stack_size])
+ assert_operator(h_default[:fiber_machine_stack_size], :>=, h_0[:fiber_machine_stack_size])
+ assert_operator(h_default[:fiber_machine_stack_size], :<=, h_large[:fiber_machine_stack_size])
+
+ # check VM machine stack size
+ script = '$stdout.sync=true; def rec; print "."; rec; end; Fiber.new{rec}.resume'
+ size_default = invoke_rec script, nil, nil
+ assert_operator(size_default, :>, 0)
+ size_0 = invoke_rec script, 0, nil
+ assert_operator(size_default, :>, size_0)
+ size_large = invoke_rec script, 1024 * 1024 * 10, nil
+ assert_operator(size_default, :<, size_large)
+
+ return if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ # check machine stack size
+ # Note that machine stack size may not change size (depend on OSs)
+ script = '$stdout.sync=true; def rec; print "."; 1.times{1.times{1.times{rec}}}; end; Fiber.new{rec}.resume'
+ vm_stack_size = 1024 * 1024
+ size_default = invoke_rec script, vm_stack_size, nil
+ size_0 = invoke_rec script, vm_stack_size, 0
+ assert_operator(size_default, :>=, size_0)
+ size_large = invoke_rec script, vm_stack_size, 1024 * 1024 * 10
+ assert_operator(size_default, :<=, size_large)
+ end
+
+ def test_separate_lastmatch
+ bug7678 = '[ruby-core:51331]'
+ /a/ =~ "a"
+ m1 = $~
+ m2 = nil
+ Fiber.new do
+ /b/ =~ "b"
+ m2 = $~
+ end.resume
+ assert_equal("b", m2[0])
+ assert_equal(m1, $~, bug7678)
+ end
+
+ def test_separate_lastline
+ bug7678 = '[ruby-core:51331]'
+ $_ = s1 = "outer"
+ s2 = nil
+ Fiber.new do
+ s2 = "inner"
+ end.resume
+ assert_equal("inner", s2)
+ assert_equal(s1, $_, bug7678)
+ end
+end
+
diff --git a/jni/ruby/test/ruby/test_file.rb b/jni/ruby/test/ruby/test_file.rb
new file mode 100644
index 0000000..601d19e
--- /dev/null
+++ b/jni/ruby/test/ruby/test_file.rb
@@ -0,0 +1,423 @@
+require 'test/unit'
+require 'tempfile'
+require "thread"
+require_relative 'ut_eof'
+
+class TestFile < Test::Unit::TestCase
+
+ # I don't know Ruby's spec about "unlink-before-close" exactly.
+ # This test asserts current behaviour.
+ def test_unlink_before_close
+ Dir.mktmpdir('rubytest-file') {|tmpdir|
+ filename = tmpdir + '/' + File.basename(__FILE__) + ".#{$$}"
+ w = File.open(filename, "w")
+ w << "foo"
+ w.close
+ r = File.open(filename, "r")
+ begin
+ if /(mswin|bccwin|mingw|emx)/ =~ RUBY_PLATFORM
+ assert_raise(Errno::EACCES) {File.unlink(filename)}
+ else
+ assert_nothing_raised {File.unlink(filename)}
+ end
+ ensure
+ r.close
+ File.unlink(filename) if File.exist?(filename)
+ end
+ }
+ end
+
+ include TestEOF
+ def open_file(content)
+ Tempfile.create("test-eof") {|f|
+ f << content
+ f.rewind
+ yield f
+ }
+ end
+ alias open_file_rw open_file
+
+ include TestEOF::Seek
+
+ def test_empty_file_bom
+ bug6487 = '[ruby-core:45203]'
+ Tempfile.create(__method__.to_s) {|f|
+ assert_file.exist?(f.path)
+ assert_nothing_raised(bug6487) {File.read(f.path, mode: 'r:utf-8')}
+ assert_nothing_raised(bug6487) {File.read(f.path, mode: 'r:bom|utf-8')}
+ }
+ end
+
+ def assert_bom(bytes, name)
+ bug6487 = '[ruby-core:45203]'
+
+ Tempfile.create(name.to_s) {|f|
+ f.sync = true
+ expected = ""
+ result = nil
+ bytes[0...-1].each do |x|
+ f.write x
+ f.write ' '
+ f.pos -= 1
+ expected << x
+ assert_nothing_raised(bug6487) {result = File.read(f.path, mode: 'rb:bom|utf-8')}
+ assert_equal("#{expected} ".force_encoding("utf-8"), result)
+ end
+ f.write bytes[-1]
+ assert_nothing_raised(bug6487) {result = File.read(f.path, mode: 'rb:bom|utf-8')}
+ assert_equal '', result, "valid bom"
+ }
+ end
+
+ def test_bom_8
+ assert_bom(["\xEF", "\xBB", "\xBF"], __method__)
+ end
+
+ def test_bom_16be
+ assert_bom(["\xFE", "\xFF"], __method__)
+ end
+
+ def test_bom_16le
+ assert_bom(["\xFF", "\xFE"], __method__)
+ end
+
+ def test_bom_32be
+ assert_bom(["\0", "\0", "\xFE", "\xFF"], __method__)
+ end
+
+ def test_bom_32le
+ assert_bom(["\xFF\xFE\0", "\0"], __method__)
+ end
+
+ def test_truncate_wbuf
+ Tempfile.create("test-truncate") {|f|
+ f.print "abc"
+ f.truncate(0)
+ f.print "def"
+ f.flush
+ assert_equal("\0\0\0def", File.read(f.path), "[ruby-dev:24191]")
+ }
+ end
+
+ def test_truncate_rbuf
+ Tempfile.create("test-truncate") {|f|
+ f.puts "abc"
+ f.puts "def"
+ f.rewind
+ assert_equal("abc\n", f.gets)
+ f.truncate(3)
+ assert_equal(nil, f.gets, "[ruby-dev:24197]")
+ }
+ end
+
+ def test_truncate_beyond_eof
+ Tempfile.create("test-truncate") {|f|
+ f.print "abc"
+ f.truncate 10
+ assert_equal("\0" * 7, f.read(100), "[ruby-dev:24532]")
+ }
+ end
+
+ def test_truncate_size
+ Tempfile.create("test-truncate") do |f|
+ q1 = Queue.new
+ q2 = Queue.new
+
+ th = Thread.new do
+ data = ''
+ 64.times do |i|
+ data << i.to_s
+ f.rewind
+ f.print data
+ f.truncate(data.bytesize)
+ q1.push data.bytesize
+ q2.pop
+ end
+ q1.push nil
+ end
+
+ while size = q1.pop
+ assert_equal size, File.size(f.path)
+ assert_equal size, f.size
+ q2.push true
+ end
+ th.join
+ end
+ end
+
+ def test_read_all_extended_file
+ [nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ assert_equal("a", f.read, "mode = <#{mode}>")
+ }
+ end
+ end
+
+ def test_gets_extended_file
+ [nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ assert_equal("a", f.gets("a"), "mode = <#{mode}>")
+ }
+ end
+ end
+
+ def test_gets_para_extended_file
+ [nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "\na"
+ f.rewind
+ assert_equal("a", f.gets(""), "mode = <#{mode}>")
+ }
+ end
+ end
+
+ def test_each_char_extended_file
+ [nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ result = []
+ f.each_char {|b| result << b }
+ assert_equal([?a], result, "mode = <#{mode}>")
+ }
+ end
+ end
+
+ def test_each_byte_extended_file
+ [nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ result = []
+ f.each_byte {|b| result << b.chr }
+ assert_equal([?a], result, "mode = <#{mode}>")
+ }
+ end
+ end
+
+ def test_getc_extended_file
+ [nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ assert_equal(?a, f.getc, "mode = <#{mode}>")
+ }
+ end
+ end
+
+ def test_getbyte_extended_file
+ [nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ assert_equal(?a, f.getbyte.chr, "mode = <#{mode}>")
+ }
+ end
+ end
+
+ def test_s_chown
+ assert_nothing_raised { File.chown(-1, -1) }
+ assert_nothing_raised { File.chown nil, nil }
+ end
+
+ def test_chown
+ assert_nothing_raised {
+ File.open(__FILE__) {|f| f.chown(-1, -1) }
+ }
+ assert_nothing_raised("[ruby-dev:27140]") {
+ File.open(__FILE__) {|f| f.chown nil, nil }
+ }
+ end
+
+ def test_uninitialized
+ assert_raise(TypeError) { File::Stat.allocate.readable? }
+ assert_nothing_raised { File::Stat.allocate.inspect }
+ end
+
+ def test_realpath
+ Dir.mktmpdir('rubytest-realpath') {|tmpdir|
+ realdir = File.realpath(tmpdir)
+ tst = realdir + (File::SEPARATOR*3 + ".")
+ assert_equal(realdir, File.realpath(tst))
+ assert_equal(realdir, File.realpath(".", tst))
+ if File::ALT_SEPARATOR
+ bug2961 = '[ruby-core:28653]'
+ assert_equal(realdir, File.realpath(realdir.tr(File::SEPARATOR, File::ALT_SEPARATOR)), bug2961)
+ end
+ }
+ end
+
+ def test_realdirpath
+ Dir.mktmpdir('rubytest-realdirpath') {|tmpdir|
+ realdir = File.realpath(tmpdir)
+ tst = realdir + (File::SEPARATOR*3 + ".")
+ assert_equal(realdir, File.realdirpath(tst))
+ assert_equal(realdir, File.realdirpath(".", tst))
+ assert_equal(File.join(realdir, "foo"), File.realdirpath("foo", tst))
+ }
+ begin
+ result = File.realdirpath("bar", "//:/foo")
+ rescue SystemCallError
+ else
+ if result.start_with?("//")
+ assert_equal("//:/foo/bar", result)
+ end
+ end
+ end
+
+ def test_utime_with_minus_time_segv
+ bug5596 = '[ruby-dev:44838]'
+ assert_in_out_err([], <<-EOS, [bug5596], [])
+ require "tempfile"
+ t = Time.at(-1)
+ begin
+ Tempfile.create('test_utime_with_minus_time_segv') {|f|
+ File.utime(t, t, f)
+ }
+ rescue
+ end
+ puts '#{bug5596}'
+ EOS
+ end
+
+ def test_utime
+ bug6385 = '[ruby-core:44776]'
+
+ mod_time_contents = Time.at 1306527039
+
+ file = Tempfile.new("utime")
+ file.close
+ path = file.path
+
+ File.utime(File.atime(path), mod_time_contents, path)
+ stats = File.stat(path)
+
+ file.open
+ file_mtime = file.mtime
+ file.close(true)
+
+ assert_equal(mod_time_contents, file_mtime, bug6385)
+ assert_equal(mod_time_contents, stats.mtime, bug6385)
+ end
+
+ def test_stat
+ tb = Process.clock_gettime(Process::CLOCK_REALTIME)
+ Tempfile.create("stat") {|file|
+ tb = (tb + Process.clock_gettime(Process::CLOCK_REALTIME)) / 2
+ file.close
+ path = file.path
+
+ t0 = Process.clock_gettime(Process::CLOCK_REALTIME)
+ File.write(path, "foo")
+ sleep 2
+ File.write(path, "bar")
+ sleep 2
+ File.chmod(0644, path)
+ sleep 2
+ File.read(path)
+
+ delta = 1
+ stat = File.stat(path)
+ assert_in_delta tb, stat.birthtime.to_f, delta
+ assert_in_delta t0+2, stat.mtime.to_f, delta
+ if stat.birthtime != stat.ctime
+ assert_in_delta t0+4, stat.ctime.to_f, delta
+ end
+ unless /mswin|mingw/ =~ RUBY_PLATFORM
+ # Windows delays updating atime
+ assert_in_delta t0+6, stat.atime.to_f, delta
+ end
+ }
+ rescue NotImplementedError
+ end
+
+ def test_chmod_m17n
+ bug5671 = '[ruby-dev:44898]'
+ Dir.mktmpdir('test-file-chmod-m17n-') do |tmpdir|
+ file = File.join(tmpdir, "\u3042")
+ File.open(file, 'w'){}
+ assert_equal(File.chmod(0666, file), 1, bug5671)
+ end
+ end
+
+ def test_file_open_permissions
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ tmp = File.join(tmpdir, 'x')
+ File.open(tmp, :mode => IO::RDWR | IO::CREAT | IO::BINARY,
+ :encoding => Encoding::ASCII_8BIT) do |x|
+
+ assert_predicate(x, :autoclose?)
+ assert_equal Encoding::ASCII_8BIT, x.external_encoding
+ x.write 'hello'
+
+ x.seek 0, IO::SEEK_SET
+
+ assert_equal 'hello', x.read
+
+ end
+ end
+ end
+
+ def test_file_open_double_mode
+ assert_raise_with_message(ArgumentError, 'mode specified twice') {
+ File.open("a", 'w', :mode => 'rw+')
+ }
+ end
+
+ def test_conflicting_encodings
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ tmp = File.join(tmpdir, 'x')
+ File.open(tmp, 'wb', :encoding => Encoding::EUC_JP) do |x|
+ assert_equal Encoding::EUC_JP, x.external_encoding
+ end
+ end
+ end
+
+ def test_untainted_path
+ bug5374 = '[ruby-core:39745]'
+ cwd = ("./"*40+".".taint).dup.untaint
+ in_safe = proc {|safe| $SAFE = safe; File.stat(cwd)}
+ assert_not_send([cwd, :tainted?])
+ (0..1).each do |level|
+ assert_nothing_raised(SecurityError, bug5374) {in_safe[level]}
+ end
+ def (s = Object.new).to_path; "".taint; end
+ m = "\u{691c 67fb}"
+ (c = Class.new(File)).singleton_class.class_eval {alias_method m, :stat}
+ assert_raise_with_message(SecurityError, /#{m}/) {
+ proc {$SAFE = 3; c.__send__(m, s)}.call
+ }
+ end
+
+ if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM
+ def test_long_unc
+ feature3399 = '[ruby-core:30623]'
+ path = File.expand_path(__FILE__)
+ path.sub!(%r'\A//', 'UNC/')
+ assert_nothing_raised(Errno::ENOENT, feature3399) do
+ File.stat("//?/#{path}")
+ end
+ end
+ end
+
+ def test_open_nul
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ path = File.join(tmpdir, "foo")
+ assert_raise(ArgumentError) do
+ open(path + "\0bar", "w") {}
+ end
+ assert_file.not_exist?(path)
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_file_exhaustive.rb b/jni/ruby/test/ruby/test_file_exhaustive.rb
new file mode 100644
index 0000000..b347e53
--- /dev/null
+++ b/jni/ruby/test/ruby/test_file_exhaustive.rb
@@ -0,0 +1,1238 @@
+require "test/unit"
+require "fileutils"
+require "tmpdir"
+
+class TestFileExhaustive < Test::Unit::TestCase
+ DRIVE = Dir.pwd[%r'\A(?:[a-z]:|//[^/]+/[^/]+)'i]
+
+ def assert_incompatible_encoding
+ d = "\u{3042}\u{3044}".encode("utf-16le")
+ assert_raise(Encoding::CompatibilityError) {yield d}
+ m = Class.new {define_method(:to_path) {d}}
+ assert_raise(Encoding::CompatibilityError) {yield m.new}
+ end
+
+ def setup
+ @dir = Dir.mktmpdir("rubytest-file")
+ @rootdir = "#{DRIVE}/"
+ File.chown(-1, Process.gid, @dir)
+ @file = make_tmp_filename("file")
+ @zerofile = make_tmp_filename("zerofile")
+ @nofile = make_tmp_filename("nofile")
+ @symlinkfile = make_tmp_filename("symlinkfile")
+ @hardlinkfile = make_tmp_filename("hardlinkfile")
+ make_file("foo", @file)
+ make_file("", @zerofile)
+ @time = Time.now
+ begin
+ File.symlink(@file, @symlinkfile)
+ rescue NotImplementedError
+ @symlinkfile = nil
+ end
+ begin
+ File.link(@file, @hardlinkfile)
+ rescue NotImplementedError, Errno::EINVAL # EINVAL for Windows Vista
+ @hardlinkfile = nil
+ end
+ end
+
+ def teardown
+ GC.start
+ FileUtils.remove_entry_secure @dir
+ end
+
+ def make_file(content, file = @file)
+ open(file, "w") {|fh| fh << content }
+ end
+
+ def make_tmp_filename(prefix)
+ "#{@dir}/#{prefix}#{File.basename(__FILE__)}.#{$$}.test"
+ end
+
+ def test_path
+ file = @file
+
+ assert_equal(file, File.open(file) {|f| f.path})
+ assert_equal(file, File.path(file))
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:to_path) { file }
+ end
+ assert_equal(file, File.path(o))
+ end
+
+ def assert_integer(n)
+ assert_kind_of(Integer, n)
+ end
+
+ def assert_integer_or_nil(n)
+ msg = ->{"#{n.inspect} is neither Fixnum nor nil."}
+ if n
+ assert_kind_of(Integer, n, msg)
+ else
+ assert_nil(n, msg)
+ end
+ end
+
+ def test_stat
+ sleep(@time - Time.now + 1.1)
+ make_file("foo", @file + "2")
+ fs1, fs2 = File.stat(@file), File.stat(@file + "2")
+ assert_nothing_raised do
+ assert_equal(0, fs1 <=> fs1)
+ assert_equal(-1, fs1 <=> fs2)
+ assert_equal(1, fs2 <=> fs1)
+ assert_nil(fs1 <=> nil)
+ assert_integer(fs1.dev)
+ assert_integer_or_nil(fs1.rdev)
+ assert_integer_or_nil(fs1.dev_major)
+ assert_integer_or_nil(fs1.dev_minor)
+ assert_integer_or_nil(fs1.rdev_major)
+ assert_integer_or_nil(fs1.rdev_minor)
+ assert_integer(fs1.ino)
+ assert_integer(fs1.mode)
+ unless /emx|mswin|mingw/ =~ RUBY_PLATFORM
+ # on Windows, nlink is always 1. but this behavior will be changed
+ # in the future.
+ assert_equal(@hardlinkfile ? 2 : 1, fs1.nlink)
+ end
+ assert_integer(fs1.uid)
+ assert_integer(fs1.gid)
+ assert_equal(3, fs1.size)
+ assert_integer_or_nil(fs1.blksize)
+ assert_integer_or_nil(fs1.blocks)
+ assert_kind_of(Time, fs1.atime)
+ assert_kind_of(Time, fs1.mtime)
+ assert_kind_of(Time, fs1.ctime)
+ assert_kind_of(String, fs1.inspect)
+ end
+ assert_raise(Errno::ENOENT) { File.stat(@nofile) }
+ assert_kind_of(File::Stat, File.open(@file) {|f| f.stat})
+ assert_raise(Errno::ENOENT) { File.lstat(@nofile) }
+ assert_kind_of(File::Stat, File.open(@file) {|f| f.lstat})
+ end
+
+ def test_stat_drive_root
+ assert_nothing_raised { File.stat(DRIVE + "/") }
+ assert_nothing_raised { File.stat(DRIVE + "/.") }
+ assert_nothing_raised { File.stat(DRIVE + "/..") }
+ assert_raise(Errno::ENOENT) { File.stat(DRIVE + "/...") }
+ # want to test the root of empty drive, but there is no method to test it...
+ end if DRIVE
+
+ def test_stat_dotted_prefix
+ Dir.mktmpdir do |dir|
+ prefix = File.join(dir, "...a")
+ Dir.mkdir(prefix)
+ assert_file.exist?(prefix)
+
+ assert_nothing_raised { File.stat(prefix) }
+
+ Dir.chdir(dir) do
+ assert_nothing_raised { File.stat(File.basename(prefix)) }
+ end
+ end
+ end if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM
+
+ def test_directory_p
+ assert_file.directory?(@dir)
+ assert_file.not_directory?(@dir+"/...")
+ assert_file.not_directory?(@file)
+ assert_file.not_directory?(@nofile)
+ end
+
+ def test_pipe_p ## xxx
+ assert_file.not_pipe?(@dir)
+ assert_file.not_pipe?(@file)
+ assert_file.not_pipe?(@nofile)
+ end
+
+ def test_symlink_p
+ assert_file.not_symlink?(@dir)
+ assert_file.not_symlink?(@file)
+ assert_file.symlink?(@symlinkfile) if @symlinkfile
+ assert_file.not_symlink?(@hardlinkfile) if @hardlinkfile
+ assert_file.not_symlink?(@nofile)
+ end
+
+ def test_socket_p ## xxx
+ assert_file.not_socket?(@dir)
+ assert_file.not_socket?(@file)
+ assert_file.not_socket?(@nofile)
+ end
+
+ def test_blockdev_p ## xxx
+ assert_file.not_blockdev?(@dir)
+ assert_file.not_blockdev?(@file)
+ assert_file.not_blockdev?(@nofile)
+ end
+
+ def test_chardev_p ## xxx
+ assert_file.not_chardev?(@dir)
+ assert_file.not_chardev?(@file)
+ assert_file.not_chardev?(@nofile)
+ end
+
+ def test_exist_p
+ assert_file.exist?(@dir)
+ assert_file.exist?(@file)
+ assert_file.not_exist?(@nofile)
+ end
+
+ def test_readable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ return if Process.euid == 0
+ File.chmod(0200, @file)
+ assert_file.not_readable?(@file)
+ File.chmod(0600, @file)
+ assert_file.readable?(@file)
+ assert_file.not_readable?(@nofile)
+ end
+
+ def test_readable_real_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ return if Process.euid == 0
+ File.chmod(0200, @file)
+ assert_file.not_readable_real?(@file)
+ File.chmod(0600, @file)
+ assert_file.readable_real?(@file)
+ assert_file.not_readable_real?(@nofile)
+ end
+
+ def test_world_readable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ File.chmod(0006, @file)
+ assert_file.world_readable?(@file)
+ File.chmod(0060, @file)
+ assert_file.not_world_readable?(@file)
+ File.chmod(0600, @file)
+ assert_file.not_world_readable?(@file)
+ assert_file.not_world_readable?(@nofile)
+ end
+
+ def test_writable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ return if Process.euid == 0
+ File.chmod(0400, @file)
+ assert_file.not_writable?(@file)
+ File.chmod(0600, @file)
+ assert_file.writable?(@file)
+ assert_file.not_writable?(@nofile)
+ end
+
+ def test_writable_real_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ return if Process.euid == 0
+ File.chmod(0400, @file)
+ assert_file.not_writable_real?(@file)
+ File.chmod(0600, @file)
+ assert_file.writable_real?(@file)
+ assert_file.not_writable_real?(@nofile)
+ end
+
+ def test_world_writable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ File.chmod(0006, @file)
+ assert_file.world_writable?(@file)
+ File.chmod(0060, @file)
+ assert_file.not_world_writable?(@file)
+ File.chmod(0600, @file)
+ assert_file.not_world_writable?(@file)
+ assert_file.not_world_writable?(@nofile)
+ end
+
+ def test_executable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ File.chmod(0100, @file)
+ assert_file.executable?(@file)
+ File.chmod(0600, @file)
+ assert_file.not_executable?(@file)
+ assert_file.not_executable?(@nofile)
+ end
+
+ def test_executable_real_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ File.chmod(0100, @file)
+ assert_file.executable_real?(@file)
+ File.chmod(0600, @file)
+ assert_file.not_executable_real?(@file)
+ assert_file.not_executable_real?(@nofile)
+ end
+
+ def test_file_p
+ assert_file.not_file?(@dir)
+ assert_file.file?(@file)
+ assert_file.not_file?(@nofile)
+ end
+
+ def test_zero_p
+ assert_nothing_raised { File.zero?(@dir) }
+ assert_file.not_zero?(@file)
+ assert_file.zero?(@zerofile)
+ assert_file.not_zero?(@nofile)
+ end
+
+ def test_size_p
+ assert_nothing_raised { File.size?(@dir) }
+ assert_equal(3, File.size?(@file))
+ assert_file.not_size?(@zerofile)
+ assert_file.not_size?(@nofile)
+ end
+
+ def test_owned_p ## xxx
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ assert_file.owned?(@file)
+ assert_file.grpowned?(@file)
+ end
+
+ def test_suid_sgid_sticky ## xxx
+ assert_file.not_setuid?(@file)
+ assert_file.not_setgid?(@file)
+ assert_file.not_sticky?(@file)
+ end
+
+ def test_path_identical_p
+ assert_file.identical?(@file, @file)
+ assert_file.not_identical?(@file, @zerofile)
+ assert_file.not_identical?(@file, @nofile)
+ assert_file.not_identical?(@nofile, @file)
+ end
+
+ def test_io_identical_p
+ open(@file) {|f|
+ assert_file.identical?(f, f)
+ assert_file.identical?(@file, f)
+ assert_file.identical?(f, @file)
+ }
+ end
+
+ def test_closed_io_identical_p
+ io = open(@file) {|f| f}
+ assert_raise(IOError) {
+ File.identical?(@file, io)
+ }
+ File.unlink(@file)
+ assert_file.not_exist?(@file)
+ end
+
+ def test_s_size
+ assert_integer(File.size(@dir))
+ assert_equal(3, File.size(@file))
+ assert_equal(0, File.size(@zerofile))
+ assert_raise(Errno::ENOENT) { File.size(@nofile) }
+ end
+
+ def test_ftype
+ assert_equal("directory", File.ftype(@dir))
+ assert_equal("file", File.ftype(@file))
+ assert_equal("link", File.ftype(@symlinkfile)) if @symlinkfile
+ assert_equal("file", File.ftype(@hardlinkfile)) if @hardlinkfile
+ assert_raise(Errno::ENOENT) { File.ftype(@nofile) }
+ end
+
+ def test_atime
+ t1 = File.atime(@file)
+ t2 = File.open(@file) {|f| f.atime}
+ assert_kind_of(Time, t1)
+ assert_kind_of(Time, t2)
+ assert_equal(t1, t2)
+ assert_raise(Errno::ENOENT) { File.atime(@nofile) }
+ end
+
+ def test_mtime
+ t1 = File.mtime(@file)
+ t2 = File.open(@file) {|f| f.mtime}
+ assert_kind_of(Time, t1)
+ assert_kind_of(Time, t2)
+ assert_equal(t1, t2)
+ assert_raise(Errno::ENOENT) { File.mtime(@nofile) }
+ end
+
+ def test_ctime
+ t1 = File.ctime(@file)
+ t2 = File.open(@file) {|f| f.ctime}
+ assert_kind_of(Time, t1)
+ assert_kind_of(Time, t2)
+ assert_equal(t1, t2)
+ assert_raise(Errno::ENOENT) { File.ctime(@nofile) }
+ end
+
+ def test_chmod
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ assert_equal(1, File.chmod(0444, @file))
+ assert_equal(0444, File.stat(@file).mode % 01000)
+ assert_equal(0, File.open(@file) {|f| f.chmod(0222)})
+ assert_equal(0222, File.stat(@file).mode % 01000)
+ File.chmod(0600, @file)
+ assert_raise(Errno::ENOENT) { File.chmod(0600, @nofile) }
+ end
+
+ def test_lchmod
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ assert_equal(1, File.lchmod(0444, @file))
+ assert_equal(0444, File.stat(@file).mode % 01000)
+ File.lchmod(0600, @file)
+ assert_raise(Errno::ENOENT) { File.lchmod(0600, @nofile) }
+ rescue NotImplementedError
+ end
+
+ def test_chown ## xxx
+ end
+
+ def test_lchown ## xxx
+ end
+
+ def test_symlink
+ return unless @symlinkfile
+ assert_equal("link", File.ftype(@symlinkfile))
+ assert_raise(Errno::EEXIST) { File.symlink(@file, @file) }
+ end
+
+ def test_utime
+ t = Time.local(2000)
+ File.utime(t + 1, t + 2, @zerofile)
+ assert_equal(t + 1, File.atime(@zerofile))
+ assert_equal(t + 2, File.mtime(@zerofile))
+ end
+
+ def test_hardlink
+ return unless @hardlinkfile
+ assert_equal("file", File.ftype(@hardlinkfile))
+ assert_raise(Errno::EEXIST) { File.link(@file, @file) }
+ end
+
+ def test_readlink
+ return unless @symlinkfile
+ assert_equal(@file, File.readlink(@symlinkfile))
+ assert_raise(Errno::EINVAL) { File.readlink(@file) }
+ assert_raise(Errno::ENOENT) { File.readlink(@nofile) }
+ if fs = Encoding.find("filesystem")
+ assert_equal(fs, File.readlink(@symlinkfile).encoding)
+ end
+ rescue NotImplementedError
+ end
+
+ def test_readlink_long_path
+ return unless @symlinkfile
+ bug9157 = '[ruby-core:58592] [Bug #9157]'
+ assert_separately(["-", @symlinkfile, bug9157], <<-"end;")
+ symlinkfile, bug9157 = *ARGV
+ 100.step(1000, 100) do |n|
+ File.unlink(symlinkfile)
+ link = "foo"*n
+ begin
+ File.symlink(link, symlinkfile)
+ rescue Errno::ENAMETOOLONG
+ break
+ end
+ assert_equal(link, File.readlink(symlinkfile), bug9157)
+ end
+ end;
+ end
+
+ def test_unlink
+ assert_equal(1, File.unlink(@file))
+ make_file("foo", @file)
+ assert_raise(Errno::ENOENT) { File.unlink(@nofile) }
+ end
+
+ def test_rename
+ assert_equal(0, File.rename(@file, @nofile))
+ assert_file.not_exist?(@file)
+ assert_file.exist?(@nofile)
+ assert_equal(0, File.rename(@nofile, @file))
+ assert_raise(Errno::ENOENT) { File.rename(@nofile, @file) }
+ end
+
+ def test_umask
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ prev = File.umask(0777)
+ assert_equal(0777, File.umask)
+ open(@nofile, "w") { }
+ assert_equal(0, File.stat(@nofile).mode % 01000)
+ File.unlink(@nofile)
+ assert_equal(0777, File.umask(prev))
+ assert_raise(ArgumentError) { File.umask(0, 1, 2) }
+ end
+
+ def test_expand_path
+ assert_equal(@file, File.expand_path(File.basename(@file), File.dirname(@file)))
+ case RUBY_PLATFORM
+ when /cygwin|mingw|mswin|bccwin/
+ assert_equal(@file, File.expand_path(@file + " "))
+ assert_equal(@file, File.expand_path(@file + "."))
+ assert_equal(@file, File.expand_path(@file + "::$DATA"))
+ assert_match(/\Ac:\//i, File.expand_path('c:'), '[ruby-core:31591]')
+ assert_match(/\Ac:\//i, File.expand_path('c:foo', 'd:/bar'))
+ assert_match(/\Ae:\//i, File.expand_path('e:foo', 'd:/bar'))
+ assert_match(%r'\Ac:/bar/foo\z'i, File.expand_path('c:foo', 'c:/bar'))
+ when /darwin/
+ ["\u{feff}", *"\u{2000}"..."\u{2100}"].each do |c|
+ file = @file + c
+ begin
+ open(file) {}
+ rescue
+ assert_equal(file, File.expand_path(file), c.dump)
+ else
+ assert_equal(@file, File.expand_path(file), c.dump)
+ end
+ end
+ end
+ if DRIVE
+ assert_match(%r"\Az:/foo\z"i, File.expand_path('/foo', "z:/bar"))
+ assert_match(%r"\A//host/share/foo\z"i, File.expand_path('/foo', "//host/share/bar"))
+ assert_match(%r"\A#{DRIVE}/foo\z"i, File.expand_path('/foo'))
+ else
+ assert_equal("/foo", File.expand_path('/foo'))
+ end
+ end
+
+ def test_expand_path_memsize
+ bug9934 = '[ruby-core:63114] [Bug #9934]'
+ require "objspace"
+ path = File.expand_path("/foo")
+ assert_operator(ObjectSpace.memsize_of(path), :<=, path.bytesize + GC::INTERNAL_CONSTANTS[:RVALUE_SIZE], bug9934)
+ path = File.expand_path("/a"*25)
+ assert_equal(path.bytesize+1 + GC::INTERNAL_CONSTANTS[:RVALUE_SIZE], ObjectSpace.memsize_of(path), bug9934)
+ end
+
+ def test_expand_path_encoding
+ drive = (DRIVE ? 'C:' : '')
+ if Encoding.find("filesystem") == Encoding::CP1251
+ a = "#{drive}/\u3042\u3044\u3046\u3048\u304a".encode("cp932")
+ else
+ a = "#{drive}/\u043f\u0440\u0438\u0432\u0435\u0442".encode("cp1251")
+ end
+ assert_equal(a, File.expand_path(a))
+ a = "#{drive}/\225\\\\"
+ if File::ALT_SEPARATOR == '\\'
+ [%W"cp437 #{drive}/\225", %W"cp932 #{drive}/\225\\"]
+ else
+ [["cp437", a], ["cp932", a]]
+ end.each do |cp, expected|
+ assert_equal(expected.force_encoding(cp), File.expand_path(a.dup.force_encoding(cp)), cp)
+ end
+
+ path = "\u3042\u3044\u3046\u3048\u304a".encode("EUC-JP")
+ assert_equal("#{Dir.pwd}/#{path}".encode("CP932"), File.expand_path(path).encode("CP932"))
+
+ path = "\u3042\u3044\u3046\u3048\u304a".encode("CP51932")
+ assert_equal("#{Dir.pwd}/#{path}", File.expand_path(path))
+
+ assert_incompatible_encoding {|d| File.expand_path(d)}
+ end
+
+ def test_expand_path_encoding_filesystem
+ home = ENV["HOME"]
+ ENV["HOME"] = "#{DRIVE}/UserHome"
+
+ path = "~".encode("US-ASCII")
+ dir = "C:/".encode("IBM437")
+ fs = Encoding.find("filesystem")
+
+ assert_equal fs, File.expand_path(path).encoding
+ assert_equal fs, File.expand_path(path, dir).encoding
+ ensure
+ ENV["HOME"] = home
+ end
+
+ UnknownUserHome = "~foo_bar_baz_unknown_user_wahaha".freeze
+
+ def test_expand_path_home
+ assert_kind_of(String, File.expand_path("~")) if ENV["HOME"]
+ assert_raise(ArgumentError) { File.expand_path(UnknownUserHome) }
+ assert_raise(ArgumentError) { File.expand_path(UnknownUserHome, "/") }
+ begin
+ bug3630 = '[ruby-core:31537]'
+ home = ENV["HOME"]
+ home_drive = ENV["HOMEDRIVE"]
+ home_path = ENV["HOMEPATH"]
+ user_profile = ENV["USERPROFILE"]
+ ENV["HOME"] = nil
+ ENV["HOMEDRIVE"] = nil
+ ENV["HOMEPATH"] = nil
+ ENV["USERPROFILE"] = nil
+ assert_raise(ArgumentError) { File.expand_path("~") }
+ ENV["HOME"] = "~"
+ assert_raise(ArgumentError, bug3630) { File.expand_path("~") }
+ ENV["HOME"] = "."
+ assert_raise(ArgumentError, bug3630) { File.expand_path("~") }
+ ensure
+ ENV["HOME"] = home
+ ENV["HOMEDRIVE"] = home_drive
+ ENV["HOMEPATH"] = home_path
+ ENV["USERPROFILE"] = user_profile
+ end
+ end
+
+ def test_expand_path_home_dir_string
+ home = ENV["HOME"]
+ new_home = "#{DRIVE}/UserHome"
+ ENV["HOME"] = new_home
+ bug8034 = "[ruby-core:53168]"
+
+ assert_equal File.join(new_home, "foo"), File.expand_path("foo", "~"), bug8034
+ assert_equal File.join(new_home, "bar", "foo"), File.expand_path("foo", "~/bar"), bug8034
+
+ assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) }
+ assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) }
+ ensure
+ ENV["HOME"] = home
+ end
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_expand_path_home_memory_leak_in_path
+ assert_no_memory_leak_at_expand_path_home('', 'in path')
+ end
+
+ def test_expand_path_home_memory_leak_in_base
+ assert_no_memory_leak_at_expand_path_home('".",', 'in base')
+ end
+
+ def assert_no_memory_leak_at_expand_path_home(arg, message)
+ prep = 'ENV["HOME"] = "foo"*100'
+ assert_no_memory_leak([], prep, <<-TRY, "memory leaked at non-absolute home #{message}")
+ 10000.times do
+ begin
+ File.expand_path(#{arg}"~/a")
+ rescue ArgumentError => e
+ next
+ ensure
+ abort("ArgumentError (non-absolute home) expected") unless e
+ end
+ end
+ GC.start
+ TRY
+ end
+ end
+
+
+ def test_expand_path_remove_trailing_alternative_data
+ assert_equal File.join(@rootdir, "aaa"), File.expand_path("#{@rootdir}/aaa::$DATA")
+ assert_equal File.join(@rootdir, "aa:a"), File.expand_path("#{@rootdir}/aa:a:$DATA")
+ assert_equal File.join(@rootdir, "aaa:$DATA"), File.expand_path("#{@rootdir}/aaa:$DATA")
+ end if DRIVE
+
+ def test_expand_path_resolve_empty_string_current_directory
+ assert_equal(Dir.pwd, File.expand_path(""))
+ end
+
+ def test_expand_path_resolve_dot_current_directory
+ assert_equal(Dir.pwd, File.expand_path("."))
+ end
+
+ def test_expand_path_resolve_file_name_relative_current_directory
+ assert_equal(File.join(Dir.pwd, "foo"), File.expand_path("foo"))
+ end
+
+ def test_ignore_nil_dir_string
+ assert_equal(File.join(Dir.pwd, "foo"), File.expand_path("foo", nil))
+ end
+
+ def test_expand_path_resolve_file_name_and_dir_string_relative
+ assert_equal(File.join(Dir.pwd, "bar", "foo"),
+ File.expand_path("foo", "bar"))
+ end
+
+ def test_expand_path_cleanup_dots_file_name
+ bug = "[ruby-talk:18512]"
+
+ assert_equal(File.join(Dir.pwd, ".a"), File.expand_path(".a"), bug)
+ assert_equal(File.join(Dir.pwd, "..a"), File.expand_path("..a"), bug)
+
+ if DRIVE
+ # cleanup dots only on Windows
+ assert_equal(File.join(Dir.pwd, "a"), File.expand_path("a."), bug)
+ assert_equal(File.join(Dir.pwd, "a"), File.expand_path("a.."), bug)
+ else
+ assert_equal(File.join(Dir.pwd, "a."), File.expand_path("a."), bug)
+ assert_equal(File.join(Dir.pwd, "a.."), File.expand_path("a.."), bug)
+ end
+ end
+
+ def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_a_complete_path
+ assert_equal(@dir, File.expand_path("", "#{@dir}"))
+ assert_equal(File.join(@dir, "a"), File.expand_path("a", "#{@dir}"))
+ assert_equal(File.join(@dir, "a"), File.expand_path("../a", "#{@dir}/xxx"))
+ assert_equal(@rootdir, File.expand_path(".", "#{@rootdir}"))
+ end
+
+ def test_expand_path_ignores_supplied_dir_if_path_contains_a_drive_letter
+ assert_equal(@rootdir, File.expand_path(@rootdir, "D:/"))
+ end if DRIVE
+
+ def test_expand_path_removes_trailing_slashes_from_absolute_path
+ assert_equal(File.join(@rootdir, "foo"), File.expand_path("#{@rootdir}foo/"))
+ assert_equal(File.join(@rootdir, "foo.rb"), File.expand_path("#{@rootdir}foo.rb/"))
+ end
+
+ def test_expand_path_removes_trailing_spaces_from_absolute_path
+ assert_equal(File.join(@rootdir, "a"), File.expand_path("#{@rootdir}a "))
+ end if DRIVE
+
+ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_dir_s_drive
+ assert_match(%r"\Az:/foo\z"i, File.expand_path('/foo', "z:/bar"))
+ end if DRIVE
+
+ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_and_unc_pathname
+ assert_equal("//foo", File.expand_path('//foo', "//bar"))
+ assert_equal("//bar/foo", File.expand_path('/foo', "//bar"))
+ assert_equal("//foo", File.expand_path('//foo', "/bar"))
+ end if DRIVE
+
+ def test_expand_path_converts_a_dot_with_unc_dir
+ assert_equal("//", File.expand_path('.', "//"))
+ end
+
+ def test_expand_path_preserves_unc_path_root
+ assert_equal("//", File.expand_path("//"))
+ assert_equal("//", File.expand_path("//."))
+ assert_equal("//", File.expand_path("//.."))
+ end
+
+ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_host_share
+ assert_match(%r"\A//host/share/foo\z"i, File.expand_path('/foo', "//host/share/bar"))
+ end if DRIVE
+
+ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_a_current_drive
+ assert_match(%r"\A#{DRIVE}/foo\z"i, File.expand_path('/foo'))
+ end
+
+ def test_expand_path_returns_tainted_strings_or_not
+ assert_equal(true, File.expand_path('foo').tainted?)
+ assert_equal(true, File.expand_path('foo'.taint).tainted?)
+ assert_equal(true, File.expand_path('/foo'.taint).tainted?)
+ assert_equal(true, File.expand_path('foo', 'bar').tainted?)
+ assert_equal(true, File.expand_path('foo', '/bar'.taint).tainted?)
+ assert_equal(true, File.expand_path('foo'.taint, '/bar').tainted?)
+ assert_equal(true, File.expand_path('~').tainted?) if ENV["HOME"]
+
+ if DRIVE
+ assert_equal(true, File.expand_path('/foo').tainted?)
+ assert_equal(false, File.expand_path('//foo').tainted?)
+ assert_equal(true, File.expand_path('C:/foo'.taint).tainted?)
+ assert_equal(false, File.expand_path('C:/foo').tainted?)
+ assert_equal(true, File.expand_path('foo', '/bar').tainted?)
+ assert_equal(true, File.expand_path('foo', 'C:/bar'.taint).tainted?)
+ assert_equal(true, File.expand_path('foo'.taint, 'C:/bar').tainted?)
+ assert_equal(false, File.expand_path('foo', 'C:/bar').tainted?)
+ assert_equal(false, File.expand_path('C:/foo/../bar').tainted?)
+ assert_equal(false, File.expand_path('foo', '//bar').tainted?)
+ else
+ assert_equal(false, File.expand_path('/foo').tainted?)
+ assert_equal(false, File.expand_path('foo', '/bar').tainted?)
+ end
+ end
+
+ def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_home_as_base
+ old_home = ENV["HOME"]
+ home = ENV["HOME"] = "#{DRIVE}/UserHome"
+ assert_equal(home, File.expand_path("~"))
+ assert_equal(home, File.expand_path("~", "C:/FooBar"))
+ assert_equal(File.join(home, "a"), File.expand_path("~/a", "C:/FooBar"))
+ ensure
+ ENV["HOME"] = old_home
+ end
+
+ def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home
+ old_home = ENV["HOME"]
+ unc_home = ENV["HOME"] = "//UserHome"
+ assert_equal(unc_home, File.expand_path("~"))
+ ensure
+ ENV["HOME"] = old_home
+ end if DRIVE
+
+ def test_expand_path_does_not_modify_a_home_string_argument
+ old_home = ENV["HOME"]
+ home = ENV["HOME"] = "#{DRIVE}/UserHome"
+ str = "~/a"
+ assert_equal("#{home}/a", File.expand_path(str))
+ assert_equal("~/a", str)
+ ensure
+ ENV["HOME"] = old_home
+ end
+
+ def test_expand_path_raises_argument_error_for_any_supplied_username
+ bug = '[ruby-core:39597]'
+ assert_raise(ArgumentError, bug) { File.expand_path("~anything") }
+ end if DRIVE
+
+ def test_expand_path_for_existent_username
+ user = ENV['USER']
+ skip "ENV['USER'] is not set" unless user
+ assert_equal(ENV['HOME'], File.expand_path("~#{user}"))
+ end unless DRIVE
+
+ def test_expand_path_error_for_nonexistent_username
+ user = "\u{3086 3046 3066 3044}:\u{307F 3084 304A 3046}"
+ assert_raise_with_message(ArgumentError, /#{user}/) {File.expand_path("~#{user}")}
+ end unless DRIVE
+
+ def test_expand_path_error_for_non_absolute_home
+ old_home = ENV["HOME"]
+ ENV["HOME"] = "./UserHome"
+ assert_raise_with_message(ArgumentError, /non-absolute home/) {File.expand_path("~")}
+ ensure
+ ENV["HOME"] = old_home
+ end
+
+ def test_expand_path_raises_a_type_error_if_not_passed_a_string_type
+ assert_raise(TypeError) { File.expand_path(1) }
+ assert_raise(TypeError) { File.expand_path(nil) }
+ assert_raise(TypeError) { File.expand_path(true) }
+ end
+
+ def test_expand_path_expands_dot_dir
+ assert_equal("#{DRIVE}/dir", File.expand_path("#{DRIVE}/./dir"))
+ end
+
+ def test_expand_path_does_not_expand_wildcards
+ assert_equal("#{DRIVE}/*", File.expand_path("./*", "#{DRIVE}/"))
+ assert_equal("#{Dir.pwd}/*", File.expand_path("./*", Dir.pwd))
+ assert_equal("#{DRIVE}/?", File.expand_path("./?", "#{DRIVE}/"))
+ assert_equal("#{Dir.pwd}/?", File.expand_path("./?", Dir.pwd))
+ end if DRIVE
+
+ def test_expand_path_does_not_modify_the_string_argument
+ str = "./a/b/../c"
+ assert_equal("#{Dir.pwd}/a/c", File.expand_path(str, Dir.pwd))
+ assert_equal("./a/b/../c", str)
+ end
+
+ def test_expand_path_returns_a_string_when_passed_a_string_subclass
+ sub = Class.new(String)
+ str = sub.new "./a/b/../c"
+ path = File.expand_path(str, Dir.pwd)
+ assert_equal("#{Dir.pwd}/a/c", path)
+ assert_instance_of(String, path)
+ end
+
+ def test_expand_path_accepts_objects_that_have_a_to_path_method
+ klass = Class.new { def to_path; "a/b/c"; end }
+ obj = klass.new
+ assert_equal("#{Dir.pwd}/a/b/c", File.expand_path(obj))
+ end
+
+ def test_expand_path_with_drive_letter
+ bug10858 = '[ruby-core:68130] [Bug #10858]'
+ assert_match(%r'/bar/foo\z'i, File.expand_path('z:foo', 'bar'), bug10858)
+ assert_equal('z:/bar/foo', File.expand_path('z:foo', '/bar'), bug10858)
+ end if DRIVE
+
+ def test_basename
+ assert_equal(File.basename(@file).sub(/\.test$/, ""), File.basename(@file, ".test"))
+ assert_equal("", s = File.basename(""))
+ assert(!s.frozen?, '[ruby-core:24199]')
+ assert_equal("foo", s = File.basename("foo"))
+ assert(!s.frozen?, '[ruby-core:24199]')
+ assert_equal("foo", File.basename("foo", ".ext"))
+ assert_equal("foo", File.basename("foo.ext", ".ext"))
+ assert_equal("foo", File.basename("foo.ext", ".*"))
+ if /cygwin|mingw|mswin|bccwin/ =~ RUBY_PLATFORM
+ basename = File.basename(@file)
+ assert_equal(basename, File.basename(@file + " "))
+ assert_equal(basename, File.basename(@file + "."))
+ assert_equal(basename, File.basename(@file + "::$DATA"))
+ basename.chomp!(".test")
+ assert_equal(basename, File.basename(@file + " ", ".test"))
+ assert_equal(basename, File.basename(@file + ".", ".test"))
+ assert_equal(basename, File.basename(@file + "::$DATA", ".test"))
+ assert_equal(basename, File.basename(@file + " ", ".*"))
+ assert_equal(basename, File.basename(@file + ".", ".*"))
+ assert_equal(basename, File.basename(@file + "::$DATA", ".*"))
+ end
+ if File::ALT_SEPARATOR == '\\'
+ a = "foo/\225\\\\"
+ [%W"cp437 \225", %W"cp932 \225\\"].each do |cp, expected|
+ assert_equal(expected.force_encoding(cp), File.basename(a.dup.force_encoding(cp)), cp)
+ end
+ end
+
+ assert_incompatible_encoding {|d| File.basename(d)}
+ assert_incompatible_encoding {|d| File.basename(d, ".*")}
+ assert_raise(Encoding::CompatibilityError) {File.basename("foo.ext", ".*".encode("utf-16le"))}
+
+ s = "foo\x93_a".force_encoding("cp932")
+ assert_equal(s, File.basename(s, "_a"))
+
+ s = "\u4032.\u3024"
+ assert_equal(s, File.basename(s, ".\x95\\".force_encoding("cp932")))
+ end
+
+ def test_dirname
+ assert(@file.start_with?(File.dirname(@file)))
+ assert_equal(".", File.dirname(""))
+ assert_incompatible_encoding {|d| File.dirname(d)}
+ if File::ALT_SEPARATOR == '\\'
+ a = "\225\\\\foo"
+ [%W"cp437 \225", %W"cp932 \225\\"].each do |cp, expected|
+ assert_equal(expected.force_encoding(cp), File.dirname(a.dup.force_encoding(cp)), cp)
+ end
+ end
+ end
+
+ def test_extname
+ assert_equal(".test", File.extname(@file))
+ prefixes = ["", "/", ".", "/.", "bar/.", "/bar/."]
+ infixes = ["", " ", "."]
+ infixes2 = infixes + [".ext "]
+ appendixes = [""]
+ if /cygwin|mingw|mswin|bccwin/ =~ RUBY_PLATFORM
+ appendixes << " " << "." << "::$DATA" << "::$DATA.bar"
+ end
+ prefixes.each do |prefix|
+ appendixes.each do |appendix|
+ infixes.each do |infix|
+ path = "#{prefix}foo#{infix}#{appendix}"
+ assert_equal("", File.extname(path), "File.extname(#{path.inspect})")
+ end
+ infixes2.each do |infix|
+ path = "#{prefix}foo#{infix}.ext#{appendix}"
+ assert_equal(".ext", File.extname(path), "File.extname(#{path.inspect})")
+ end
+ end
+ end
+ bug3175 = '[ruby-core:29627]'
+ assert_equal(".rb", File.extname("/tmp//bla.rb"), bug3175)
+
+ assert_incompatible_encoding {|d| File.extname(d)}
+ end
+
+ def test_split
+ d, b = File.split(@file)
+ assert_equal(File.dirname(@file), d)
+ assert_equal(File.basename(@file), b)
+ end
+
+ def test_join
+ s = "foo" + File::SEPARATOR + "bar" + File::SEPARATOR + "baz"
+ assert_equal(s, File.join("foo", "bar", "baz"))
+ assert_equal(s, File.join(["foo", "bar", "baz"]))
+
+ o = Object.new
+ def o.to_path; "foo"; end
+ assert_equal(s, File.join(o, "bar", "baz"))
+ assert_equal(s, File.join("foo" + File::SEPARATOR, "bar", File::SEPARATOR + "baz"))
+ end
+
+ def test_join_alt_separator
+ if File::ALT_SEPARATOR == '\\'
+ a = "\225\\"
+ b = "foo"
+ [%W"cp437 \225\\foo", %W"cp932 \225\\/foo"].each do |cp, expected|
+ assert_equal(expected.force_encoding(cp), File.join(a.dup.force_encoding(cp), b.dup.force_encoding(cp)), cp)
+ end
+ end
+ end
+
+ def test_join_ascii_incompatible
+ bug7168 = '[ruby-core:48012]'
+ names = %w"a b".map {|s| s.encode(Encoding::UTF_16LE)}
+ assert_raise(Encoding::CompatibilityError, bug7168) {File.join(*names)}
+ assert_raise(Encoding::CompatibilityError, bug7168) {File.join(names)}
+
+ a = Object.new
+ b = names[1]
+ names = [a, "b"]
+ a.singleton_class.class_eval do
+ define_method(:to_path) do
+ names[1] = b
+ "a"
+ end
+ end
+ assert_raise(Encoding::CompatibilityError, bug7168) {File.join(names)}
+ end
+
+ def test_truncate
+ assert_equal(0, File.truncate(@file, 1))
+ assert_file.exist?(@file)
+ assert_equal(1, File.size(@file))
+ assert_equal(0, File.truncate(@file, 0))
+ assert_file.exist?(@file)
+ assert_file.zero?(@file)
+ make_file("foo", @file)
+ assert_raise(Errno::ENOENT) { File.truncate(@nofile, 0) }
+
+ f = File.new(@file, "w")
+ assert_equal(0, f.truncate(2))
+ assert_file.exist?(@file)
+ assert_equal(2, File.size(@file))
+ assert_equal(0, f.truncate(0))
+ assert_file.exist?(@file)
+ assert_file.zero?(@file)
+ f.close
+ make_file("foo", @file)
+
+ assert_raise(IOError) { File.open(@file) {|ff| ff.truncate(0)} }
+ rescue NotImplementedError
+ end
+
+ def test_flock ## xxx
+ f = File.new(@file, "r+")
+ f.flock(File::LOCK_EX)
+ f.flock(File::LOCK_SH)
+ f.flock(File::LOCK_UN)
+ f.close
+ rescue NotImplementedError
+ end
+
+ def test_test
+ sleep(@time - Time.now + 1.1)
+ make_file("foo", @file + "2")
+ [@dir, @file, @zerofile, @symlinkfile, @hardlinkfile].compact.each do |f|
+ assert_equal(File.atime(f), test(?A, f))
+ assert_equal(File.ctime(f), test(?C, f))
+ assert_equal(File.mtime(f), test(?M, f))
+ assert_equal(File.blockdev?(f), test(?b, f))
+ assert_equal(File.chardev?(f), test(?c, f))
+ assert_equal(File.directory?(f), test(?d, f))
+ assert_equal(File.exist?(f), test(?e, f))
+ assert_equal(File.file?(f), test(?f, f))
+ assert_equal(File.setgid?(f), test(?g, f))
+ assert_equal(File.grpowned?(f), test(?G, f))
+ assert_equal(File.sticky?(f), test(?k, f))
+ assert_equal(File.symlink?(f), test(?l, f))
+ assert_equal(File.owned?(f), test(?o, f))
+ assert_nothing_raised { test(?O, f) }
+ assert_equal(File.pipe?(f), test(?p, f))
+ assert_equal(File.readable?(f), test(?r, f))
+ assert_equal(File.readable_real?(f), test(?R, f))
+ assert_equal(File.size?(f), test(?s, f))
+ assert_equal(File.socket?(f), test(?S, f))
+ assert_equal(File.setuid?(f), test(?u, f))
+ assert_equal(File.writable?(f), test(?w, f))
+ assert_equal(File.writable_real?(f), test(?W, f))
+ assert_equal(File.executable?(f), test(?x, f))
+ assert_equal(File.executable_real?(f), test(?X, f))
+ assert_equal(File.zero?(f), test(?z, f))
+ end
+ assert_equal(false, test(?-, @dir, @file))
+ assert_equal(true, test(?-, @file, @file))
+ assert_equal(true, test(?=, @file, @file))
+ assert_equal(false, test(?>, @file, @file))
+ assert_equal(false, test(?<, @file, @file))
+ unless /cygwin/ =~ RUBY_PLATFORM
+ assert_equal(false, test(?=, @file, @file + "2"))
+ assert_equal(false, test(?>, @file, @file + "2"))
+ assert_equal(true, test(?>, @file + "2", @file))
+ assert_equal(true, test(?<, @file, @file + "2"))
+ assert_equal(false, test(?<, @file + "2", @file))
+ end
+ assert_raise(ArgumentError) { test }
+ assert_raise(Errno::ENOENT) { test(?A, @nofile) }
+ assert_raise(ArgumentError) { test(?a) }
+ assert_raise(ArgumentError) { test("\0".ord) }
+ end
+
+ def test_stat_init
+ sleep(@time - Time.now + 1.1)
+ make_file("foo", @file + "2")
+ fs1, fs2 = File::Stat.new(@file), File::Stat.new(@file + "2")
+ assert_nothing_raised do
+ assert_equal(0, fs1 <=> fs1)
+ assert_equal(-1, fs1 <=> fs2)
+ assert_equal(1, fs2 <=> fs1)
+ assert_nil(fs1 <=> nil)
+ assert_integer(fs1.dev)
+ assert_integer_or_nil(fs1.rdev)
+ assert_integer_or_nil(fs1.dev_major)
+ assert_integer_or_nil(fs1.dev_minor)
+ assert_integer_or_nil(fs1.rdev_major)
+ assert_integer_or_nil(fs1.rdev_minor)
+ assert_integer(fs1.ino)
+ assert_integer(fs1.mode)
+ unless /emx|mswin|mingw/ =~ RUBY_PLATFORM
+ # on Windows, nlink is always 1. but this behavior will be changed
+ # in the future.
+ assert_equal(@hardlinkfile ? 2 : 1, fs1.nlink)
+ end
+ assert_integer(fs1.uid)
+ assert_integer(fs1.gid)
+ assert_equal(3, fs1.size)
+ assert_integer_or_nil(fs1.blksize)
+ assert_integer_or_nil(fs1.blocks)
+ assert_kind_of(Time, fs1.atime)
+ assert_kind_of(Time, fs1.mtime)
+ assert_kind_of(Time, fs1.ctime)
+ assert_kind_of(String, fs1.inspect)
+ end
+ assert_raise(Errno::ENOENT) { File::Stat.new(@nofile) }
+ assert_kind_of(File::Stat, File::Stat.new(@file).dup)
+ assert_raise(TypeError) do
+ File::Stat.new(@file).instance_eval { initialize_copy(0) }
+ end
+ end
+
+ def test_stat_ftype
+ assert_equal("directory", File::Stat.new(@dir).ftype)
+ assert_equal("file", File::Stat.new(@file).ftype)
+ # File::Stat uses stat
+ assert_equal("file", File::Stat.new(@symlinkfile).ftype) if @symlinkfile
+ assert_equal("file", File::Stat.new(@hardlinkfile).ftype) if @hardlinkfile
+ end
+
+ def test_stat_directory_p
+ assert(File::Stat.new(@dir).directory?)
+ assert(!(File::Stat.new(@file).directory?))
+ end
+
+ def test_stat_pipe_p ## xxx
+ assert(!(File::Stat.new(@dir).pipe?))
+ assert(!(File::Stat.new(@file).pipe?))
+ end
+
+ def test_stat_symlink_p
+ assert(!(File::Stat.new(@dir).symlink?))
+ assert(!(File::Stat.new(@file).symlink?))
+ # File::Stat uses stat
+ assert(!(File::Stat.new(@symlinkfile).symlink?)) if @symlinkfile
+ assert(!(File::Stat.new(@hardlinkfile).symlink?)) if @hardlinkfile
+ end
+
+ def test_stat_socket_p ## xxx
+ assert(!(File::Stat.new(@dir).socket?))
+ assert(!(File::Stat.new(@file).socket?))
+ end
+
+ def test_stat_blockdev_p ## xxx
+ assert(!(File::Stat.new(@dir).blockdev?))
+ assert(!(File::Stat.new(@file).blockdev?))
+ end
+
+ def test_stat_chardev_p ## xxx
+ assert(!(File::Stat.new(@dir).chardev?))
+ assert(!(File::Stat.new(@file).chardev?))
+ end
+
+ def test_stat_readable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ return if Process.euid == 0
+ File.chmod(0200, @file)
+ assert(!(File::Stat.new(@file).readable?))
+ File.chmod(0600, @file)
+ assert(File::Stat.new(@file).readable?)
+ end
+
+ def test_stat_readable_real_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ return if Process.euid == 0
+ File.chmod(0200, @file)
+ assert(!(File::Stat.new(@file).readable_real?))
+ File.chmod(0600, @file)
+ assert(File::Stat.new(@file).readable_real?)
+ end
+
+ def test_stat_world_readable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ File.chmod(0006, @file)
+ assert(File::Stat.new(@file).world_readable?)
+ File.chmod(0060, @file)
+ assert(!(File::Stat.new(@file).world_readable?))
+ File.chmod(0600, @file)
+ assert(!(File::Stat.new(@file).world_readable?))
+ end
+
+ def test_stat_writable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ return if Process.euid == 0
+ File.chmod(0400, @file)
+ assert(!(File::Stat.new(@file).writable?))
+ File.chmod(0600, @file)
+ assert(File::Stat.new(@file).writable?)
+ end
+
+ def test_stat_writable_real_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ return if Process.euid == 0
+ File.chmod(0400, @file)
+ assert(!(File::Stat.new(@file).writable_real?))
+ File.chmod(0600, @file)
+ assert(File::Stat.new(@file).writable_real?)
+ end
+
+ def test_stat_world_writable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ File.chmod(0006, @file)
+ assert(File::Stat.new(@file).world_writable?)
+ File.chmod(0060, @file)
+ assert(!(File::Stat.new(@file).world_writable?))
+ File.chmod(0600, @file)
+ assert(!(File::Stat.new(@file).world_writable?))
+ end
+
+ def test_stat_executable_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ File.chmod(0100, @file)
+ assert(File::Stat.new(@file).executable?)
+ File.chmod(0600, @file)
+ assert(!(File::Stat.new(@file).executable?))
+ end
+
+ def test_stat_executable_real_p
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ File.chmod(0100, @file)
+ assert(File::Stat.new(@file).executable_real?)
+ File.chmod(0600, @file)
+ assert(!(File::Stat.new(@file).executable_real?))
+ end
+
+ def test_stat_file_p
+ assert(!(File::Stat.new(@dir).file?))
+ assert(File::Stat.new(@file).file?)
+ end
+
+ def test_stat_zero_p
+ assert_nothing_raised { File::Stat.new(@dir).zero? }
+ assert(!(File::Stat.new(@file).zero?))
+ assert(File::Stat.new(@zerofile).zero?)
+ end
+
+ def test_stat_size_p
+ assert_nothing_raised { File::Stat.new(@dir).size? }
+ assert_equal(3, File::Stat.new(@file).size?)
+ assert(!(File::Stat.new(@zerofile).size?))
+ end
+
+ def test_stat_owned_p ## xxx
+ return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
+ assert(File::Stat.new(@file).owned?)
+ assert(File::Stat.new(@file).grpowned?)
+ end
+
+ def test_stat_suid_sgid_sticky ## xxx
+ assert(!(File::Stat.new(@file).setuid?))
+ assert(!(File::Stat.new(@file).setgid?))
+ assert(!(File::Stat.new(@file).sticky?))
+ end
+
+ def test_stat_size
+ assert_integer(File::Stat.new(@dir).size)
+ assert_equal(3, File::Stat.new(@file).size)
+ assert_equal(0, File::Stat.new(@zerofile).size)
+ end
+
+ def test_stat_special_file
+ # test for special files such as pagefile.sys on Windows
+ assert_nothing_raised do
+ Dir::glob("C:/*.sys") {|f| File::Stat.new(f) }
+ end
+ end if DRIVE
+
+ def test_path_check
+ assert_nothing_raised { ENV["PATH"] }
+ end
+
+ def test_size
+ assert_equal(3, File.open(@file) {|f| f.size })
+ File.open(@file, "a") do |f|
+ f.write("bar")
+ assert_equal(6, f.size)
+ end
+ end
+
+ def test_absolute_path
+ assert_equal(File.join(Dir.pwd, "~foo"), File.absolute_path("~foo"))
+ dir = File.expand_path("/bar")
+ assert_equal(File.join(dir, "~foo"), File.absolute_path("~foo", dir))
+ end
+end
diff --git a/jni/ruby/test/ruby/test_fixnum.rb b/jni/ruby/test/ruby/test_fixnum.rb
new file mode 100644
index 0000000..8b2cf2e
--- /dev/null
+++ b/jni/ruby/test/ruby/test_fixnum.rb
@@ -0,0 +1,315 @@
+require 'test/unit'
+
+class TestFixnum < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def test_pow
+ [1, 2, 2**64, 2**63*3, 2**64*3].each do |y|
+ [-1, 0, 1].each do |x|
+ z1 = x**y
+ z2 = (-x)**y
+ if y % 2 == 1
+ assert_equal(z2, -z1)
+ else
+ assert_equal(z2, z1)
+ end
+ end
+ end
+ end
+
+ def test_succ
+ assert_equal(0x40000000, 0x3fffffff.succ, "[ruby-dev:31189]")
+ assert_equal(0x4000000000000000, 0x3fffffffffffffff.succ, "[ruby-dev:31190]")
+ end
+
+ def test_pred
+ assert_equal(-0x40000001, (-0x40000000).pred)
+ assert_equal(-0x4000000000000001, (-0x4000000000000000).pred)
+ end
+
+ def test_plus
+ assert_equal(0x40000000, 0x3fffffff+1)
+ assert_equal(0x4000000000000000, 0x3fffffffffffffff+1)
+ assert_equal(-0x40000001, (-0x40000000)+(-1))
+ assert_equal(-0x4000000000000001, (-0x4000000000000000)+(-1))
+ assert_equal(-0x80000000, (-0x40000000)+(-0x40000000))
+ end
+
+ def test_sub
+ assert_equal(0x40000000, 0x3fffffff-(-1))
+ assert_equal(0x4000000000000000, 0x3fffffffffffffff-(-1))
+ assert_equal(-0x40000001, (-0x40000000)-1)
+ assert_equal(-0x4000000000000001, (-0x4000000000000000)-1)
+ assert_equal(-0x80000000, (-0x40000000)-0x40000000)
+ end
+
+ def test_mult
+ assert_equal(0x40000000, 0x20000000*2)
+ assert_equal(0x4000000000000000, 0x2000000000000000*2)
+ assert_equal(-0x40000001, 33025*(-32513))
+ assert_equal(-0x4000000000000001, 1380655685*(-3340214413))
+ assert_equal(0x40000000, (-0x40000000)*(-1))
+ end
+
+ def test_div
+ assert_equal(2, 5/2)
+ assert_equal(0, 1/2)
+ assert_equal(-1, -1/2)
+ assert_equal(0, -(1/2))
+ assert_equal(-1, (-1)/2)
+ assert_equal(0, (-1)/(-2))
+ assert_equal(-1, 1/(-2))
+ assert_equal(1, -(1/(-2)))
+ assert_equal(0x3fffffff, 0xbffffffd/3)
+ assert_equal(0x40000000, 0xc0000000/3)
+ assert_equal(0x4000000000000000, 0xc000000000000000/3)
+ assert_equal(-0x40000001, 0xc0000003/(-3))
+ assert_equal(-0x4000000000000001, 0xc000000000000003/(-3))
+ assert_equal(0x40000000, (-0x40000000)/(-1), "[ruby-dev:31210]")
+ assert_equal(0x4000000000000000, (-0x4000000000000000)/(-1))
+ assert_raise(FloatDomainError) { 2.div(Float::NAN).nan? }
+ end
+
+ def test_mod
+ assert_equal(2, (-0x40000000) % 3)
+ assert_equal(0, (-0x40000000) % (-1))
+ end
+
+ def test_divmod
+ (-5).upto(5) {|a|
+ (-5).upto(5) {|b|
+ next if b == 0
+ q, r = a.divmod(b)
+ assert_equal(a, b*q+r)
+ assert_operator(r.abs, :<, b.abs)
+ if 0 < b
+ assert_operator(r, :>=, 0)
+ assert_operator(r, :<, b)
+ else
+ assert_operator(r, :>, b)
+ assert_operator(r, :<=, 0)
+ end
+ assert_equal(q, a/b)
+ assert_equal(q, a.div(b))
+ assert_equal(r, a%b)
+ assert_equal(r, a.modulo(b))
+ }
+ }
+ assert_raise(FloatDomainError) { 2.divmod(Float::NAN) }
+ end
+
+ def test_not
+ assert_equal(-0x40000000, ~0x3fffffff)
+ assert_equal(0x3fffffff, ~-0x40000000)
+ end
+
+ def test_lshift
+ assert_equal(0x40000000, 0x20000000 << 1)
+ assert_equal(-0x40000000, (-0x20000000) << 1)
+ assert_equal(-0x80000000, (-0x40000000) << 1)
+ end
+
+ def test_rshift
+ assert_equal(0x20000000, 0x40000000 >> 1)
+ assert_equal(-0x20000000, (-0x40000000) >> 1)
+ assert_equal(-0x40000000, (-0x80000000) >> 1)
+ end
+
+ def test_abs
+ assert_equal(0x40000000, (-0x40000000).abs)
+ assert_equal(0x4000000000000000, (-0x4000000000000000).abs)
+ end
+
+ def test_to_s
+ assert_equal("1010", 10.to_s(2))
+ assert_equal("a", 10.to_s(36))
+ assert_raise(ArgumentError) { 10.to_s(1) }
+ end
+
+ def test_plus2
+ assert_equal(2, 1 + 1)
+ assert_equal(4294967297, 1 + 2**32)
+ assert_equal(2.0, 1 + 1.0)
+ assert_raise(TypeError) { 1 + nil }
+ end
+
+ def test_minus
+ assert_equal(0, 1 - 1)
+ assert_equal(-4294967295, 1 - 2**32)
+ assert_equal(0.0, 1 - 1.0)
+ assert_raise(TypeError) { 1 - nil }
+ end
+
+ def test_mul
+ assert_equal(6, 2.send(:*, 3))
+ a = 2**30-1
+ assert_equal(1152921502459363329, a.send(:*, a))
+
+ assert_equal(6.0, 2 * 3.0)
+ assert_raise(TypeError) { 2 * nil }
+ end
+
+ def test_divide
+ assert_equal(2.0, 4.quo(2))
+ assert_equal(2.0, 4 / 2)
+ assert_equal(2.0, 4.div(2))
+
+ assert_equal(0.5**32, 1.quo(2**32))
+ assert_equal(0, 1 / (2**32))
+ assert_equal(0, 1.div(2**32))
+
+ assert_kind_of(Float, 1.quo(2.0))
+ assert_equal(0.5, 1.quo(2.0))
+ assert_equal(0.5, 1 / 2.0)
+ assert_equal(0, 1.div(2.0))
+
+ ### rational changes the behavior of Fixnum#quo
+ #assert_raise(TypeError) { 2.quo(nil) }
+ assert_raise(TypeError, NoMethodError) { 2.quo(nil) }
+ assert_raise(TypeError) { 2 / nil }
+ assert_raise(TypeError) { 2.div(nil) }
+
+ assert_equal(0, 4.modulo(2))
+ assert_equal(1, 1.modulo(2**32))
+ assert_equal(1, 1.modulo(2.0))
+ assert_raise(TypeError) { 2.modulo(nil) }
+
+ assert_equal([2, 0], 4.divmod(2))
+ assert_equal([0, 1], 1.divmod(2**32))
+ assert_equal([0, 1], 1.divmod(2.0))
+ assert_raise(TypeError) { 2.divmod(nil) }
+ end
+
+ def test_pow2
+ assert_equal(65536, 2**16)
+ assert_equal(4294967296, 2**32)
+ assert_equal(0.5**16, 2**-16)
+ assert_equal(1, (-1)**4294967296)
+ assert_equal(-1, (-1)**4294967295)
+ assert_equal(4, 2**((2**32).coerce(2).first))
+ assert_equal(2, 4**0.5)
+ assert_equal(0, 0**0.5)
+ assert_equal(1, (0**-1.0).infinite?)
+ ### rational changes the behavior of Fixnum#**
+ #assert_raise(TypeError) { 1 ** nil }
+ assert_raise(TypeError, NoMethodError) { 1 ** nil }
+ end
+
+ def test_cmp
+ assert_operator(1, :!=, nil)
+
+ assert_equal(0, 1 <=> 1)
+ assert_equal(-1, 1 <=> 4294967296)
+ assert_equal(0, 1 <=> 1.0)
+ assert_nil(1 <=> nil)
+
+ assert_operator(1, :>, 0)
+ assert_not_operator(1, :>, 1)
+ assert_not_operator(1, :>, 2)
+ assert_not_operator(1, :>, 4294967296)
+ assert_operator(1, :>, 0.0)
+ assert_raise(ArgumentError) { 1 > nil }
+
+ assert_operator(1, :>=, 0)
+ assert_operator(1, :>=, 1)
+ assert_not_operator(1, :>=, 2)
+ assert_not_operator(1, :>=, 4294967296)
+ assert_operator(1, :>=, 0.0)
+ assert_raise(ArgumentError) { 1 >= nil }
+
+ assert_not_operator(1, :<, 0)
+ assert_not_operator(1, :<, 1)
+ assert_operator(1, :<, 2)
+ assert_operator(1, :<, 4294967296)
+ assert_not_operator(1, :<, 0.0)
+ assert_raise(ArgumentError) { 1 < nil }
+
+ assert_not_operator(1, :<=, 0)
+ assert_operator(1, :<=, 1)
+ assert_operator(1, :<=, 2)
+ assert_operator(1, :<=, 4294967296)
+ assert_not_operator(1, :<=, 0.0)
+ assert_raise(ArgumentError) { 1 <= nil }
+ end
+
+ class DummyNumeric < Numeric
+ def to_int
+ 1
+ end
+ end
+
+ def test_and_with_float
+ assert_raise(TypeError) { 1 & 1.5 }
+ end
+
+ def test_and_with_rational
+ assert_raise(TypeError, "#1792") { 1 & Rational(3, 2) }
+ end
+
+ def test_and_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { 1 & DummyNumeric.new }
+ end
+
+ def test_or_with_float
+ assert_raise(TypeError) { 1 | 1.5 }
+ end
+
+ def test_or_with_rational
+ assert_raise(TypeError, "#1792") { 1 | Rational(3, 2) }
+ end
+
+ def test_or_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { 1 | DummyNumeric.new }
+ end
+
+ def test_xor_with_float
+ assert_raise(TypeError) { 1 ^ 1.5 }
+ end
+
+ def test_xor_with_rational
+ assert_raise(TypeError, "#1792") { 1 ^ Rational(3, 2) }
+ end
+
+ def test_xor_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { 1 ^ DummyNumeric.new }
+ end
+
+ def test_singleton_method
+ assert_raise(TypeError) { a = 1; def a.foo; end }
+ end
+
+ def test_frozen
+ assert_equal(true, 1.frozen?)
+ end
+
+ def assert_eql(a, b, mess)
+ assert a.eql?(b), "expected #{a} & #{b} to be eql? #{mess}"
+ end
+
+ def test_power_of_1_and_minus_1
+ bug5715 = '[ruby-core:41498]'
+ big = 1 << 66
+ assert_eql 1, 1 ** -big , bug5715
+ assert_eql 1, (-1) ** -big , bug5715
+ assert_eql -1, (-1) ** -(big+1) , bug5715
+ end
+
+ def test_power_of_0
+ bug5713 = '[ruby-core:41494]'
+ big = 1 << 66
+ assert_raise(ZeroDivisionError, bug5713) { 0 ** -big }
+ assert_raise(ZeroDivisionError, bug5713) { 0 ** Rational(-2,3) }
+ end
+
+ def test_remainder
+ assert_equal(1, 5.remainder(4))
+ assert_predicate(4.remainder(Float::NAN), :nan?)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_flip.rb b/jni/ruby/test/ruby/test_flip.rb
new file mode 100644
index 0000000..84f7bf0
--- /dev/null
+++ b/jni/ruby/test/ruby/test_flip.rb
@@ -0,0 +1,41 @@
+require 'test/unit'
+
+class TestFlip < Test::Unit::TestCase
+ def test_hidden_key
+ bug6899 = '[ruby-core:47253]'
+ foo = "foor"
+ bar = "bar"
+ assert_nothing_raised(NotImplementedError, bug6899) do
+ 2000.times {eval %[(foo..bar) ? 1 : 2]}
+ end
+ end
+
+ def test_shared_eval
+ bug7671 = '[ruby-core:51296]'
+ vs = (1..9).to_a
+ vs.select {|n| if n==2..n==16 then 1 end}
+ v = eval("vs.select {|n| if n==3..n==6 then 1 end}")
+ assert_equal([*3..6], v, bug7671)
+ end
+
+ def test_shared_thread
+ ff = proc {|n| true if n==3..n==5}
+ v = 1..9
+ a = true
+ th = Thread.new {
+ v.select {|i|
+ Thread.pass while a
+ ff[i].tap {a = true}
+ }
+ }
+ v1 = v.select {|i|
+ Thread.pass until a
+ ff[i].tap {a = false}
+ }
+ v2 = th.value
+ expected = [3, 4, 5]
+ mesg = 'flip-flop should be separated per threads'
+ assert_equal(expected, v1, mesg)
+ assert_equal(expected, v2, mesg)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_float.rb b/jni/ruby/test/ruby/test_float.rb
new file mode 100644
index 0000000..eab4d12
--- /dev/null
+++ b/jni/ruby/test/ruby/test_float.rb
@@ -0,0 +1,683 @@
+require 'test/unit'
+
+class TestFloat < Test::Unit::TestCase
+ include EnvUtil
+
+ def test_float
+ assert_equal(2, 2.6.floor)
+ assert_equal(-3, (-2.6).floor)
+ assert_equal(3, 2.6.ceil)
+ assert_equal(-2, (-2.6).ceil)
+ assert_equal(2, 2.6.truncate)
+ assert_equal(-2, (-2.6).truncate)
+ assert_equal(3, 2.6.round)
+ assert_equal(-2, (-2.4).truncate)
+ assert_in_delta(13.4 % 1, 0.4, 0.0001)
+ assert_equal(36893488147419111424,
+ 36893488147419107329.0.to_i)
+ end
+
+ def nan_test(x,y)
+ extend Test::Unit::Assertions
+ assert_operator(x, :!=, y)
+ assert_not_operator(x, :<, y)
+ assert_not_operator(x, :>, y)
+ assert_not_operator(x, :<=, y)
+ assert_not_operator(x, :>=, y)
+ end
+ def test_nan
+ nan = Float::NAN
+ nan_test(nan, nan)
+ nan_test(nan, 0)
+ nan_test(nan, 1)
+ nan_test(nan, -1)
+ nan_test(nan, 1000)
+ nan_test(nan, -1000)
+ nan_test(nan, 1_000_000_000_000)
+ nan_test(nan, -1_000_000_000_000)
+ nan_test(nan, 100.0);
+ nan_test(nan, -100.0);
+ nan_test(nan, 0.001);
+ nan_test(nan, -0.001);
+ nan_test(nan, 1.0/0);
+ nan_test(nan, -1.0/0);
+ end
+
+ def test_precision
+ u = 3.7517675036461267e+17
+ v = sprintf("%.16e", u).to_f
+ assert_in_delta(u, v, u.abs * Float::EPSILON)
+ assert_in_delta(u, v, v.abs * Float::EPSILON)
+ end
+
+ def test_symmetry_bignum # [ruby-bugs-ja:118]
+ a = 100000000000000000000000
+ b = 100000000000000000000000.0
+ assert_equal(a == b, b == a)
+ end
+
+ def test_cmp_int
+ 100.times {|i|
+ int0 = 1 << i
+ [int0, -int0].each {|int|
+ flt = int.to_f
+ bigger = int + 1
+ smaller = int - 1
+ assert_operator(flt, :==, int)
+ assert_operator(flt, :>, smaller)
+ assert_operator(flt, :>=, smaller)
+ assert_operator(flt, :<, bigger)
+ assert_operator(flt, :<=, bigger)
+ assert_equal(0, flt <=> int)
+ assert_equal(-1, flt <=> bigger)
+ assert_equal(1, flt <=> smaller)
+ assert_operator(int, :==, flt)
+ assert_operator(bigger, :>, flt)
+ assert_operator(bigger, :>=, flt)
+ assert_operator(smaller, :<, flt)
+ assert_operator(smaller, :<=, flt)
+ assert_equal(0, int <=> flt)
+ assert_equal(-1, smaller <=> flt)
+ assert_equal(1, bigger <=> flt)
+ [
+ [int, flt + 0.5, bigger],
+ [smaller, flt - 0.5, int]
+ ].each {|smaller2, flt2, bigger2|
+ next if flt2 == flt2.round
+ assert_operator(flt2, :!=, smaller2)
+ assert_operator(flt2, :!=, bigger2)
+ assert_operator(flt2, :>, smaller2)
+ assert_operator(flt2, :>=, smaller2)
+ assert_operator(flt2, :<, bigger2)
+ assert_operator(flt2, :<=, bigger2)
+ assert_equal(-1, flt2 <=> bigger2)
+ assert_equal(1, flt2 <=> smaller2)
+ assert_operator(smaller2, :!=, flt2)
+ assert_operator(bigger2, :!=, flt2)
+ assert_operator(bigger2, :>, flt2)
+ assert_operator(bigger2, :>=, flt2)
+ assert_operator(smaller2, :<, flt2)
+ assert_operator(smaller2, :<=, flt2)
+ assert_equal(-1, smaller2 <=> flt2)
+ assert_equal(1, bigger2 <=> flt2)
+ }
+ }
+ }
+ end
+
+ def test_strtod
+ a = Float("0")
+ assert_in_delta(a, 0, Float::EPSILON)
+ a = Float("0.0")
+ assert_in_delta(a, 0, Float::EPSILON)
+ a = Float("+0.0")
+ assert_in_delta(a, 0, Float::EPSILON)
+ a = Float("-0.0")
+ assert_in_delta(a, 0, Float::EPSILON)
+ a = Float("0.0000000000000000001")
+ assert_not_equal(0.0, a)
+ a = Float("+0.0000000000000000001")
+ assert_not_equal(0.0, a)
+ a = Float("-0.0000000000000000001")
+ assert_not_equal(0.0, a)
+ a = Float(".0")
+ assert_in_delta(a, 0, Float::EPSILON)
+ a = Float("+.0")
+ assert_in_delta(a, 0, Float::EPSILON)
+ a = Float("-.0")
+ assert_in_delta(a, 0, Float::EPSILON)
+ assert_raise(ArgumentError){Float("0.")}
+ assert_raise(ArgumentError){Float("+0.")}
+ assert_raise(ArgumentError){Float("-0.")}
+ assert_raise(ArgumentError){Float(".")}
+ assert_raise(ArgumentError){Float("+")}
+ assert_raise(ArgumentError){Float("+.")}
+ assert_raise(ArgumentError){Float("-")}
+ assert_raise(ArgumentError){Float("-.")}
+ assert_raise(ArgumentError){Float("1e")}
+ assert_raise(ArgumentError){Float("1__1")}
+ assert_raise(ArgumentError){Float("1.")}
+ assert_raise(ArgumentError){Float("1.e+00")}
+ assert_raise(ArgumentError){Float("0x1.p+0")}
+ # add expected behaviour here.
+ assert_equal(10, Float("1_0"))
+
+ assert_equal([ 0.0].pack('G'), [Float(" 0x0p+0").to_f].pack('G'))
+ assert_equal([-0.0].pack('G'), [Float("-0x0p+0").to_f].pack('G'))
+ assert_equal(255.0, Float("0Xff"))
+ assert_equal(1024.0, Float("0x1p10"))
+ assert_equal(1024.0, Float("0x1p+10"))
+ assert_equal(0.0009765625, Float("0x1p-10"))
+ assert_equal(2.6881171418161356e+43, Float("0x1.3494a9b171bf5p+144"))
+ assert_equal(-3.720075976020836e-44, Float("-0x1.a8c1f14e2af5dp-145"))
+ assert_equal(31.0*2**1019, Float("0x0."+("0"*268)+"1fp2099"))
+ assert_equal(31.0*2**1019, Float("0x0."+("0"*600)+"1fp3427"))
+ assert_equal(-31.0*2**1019, Float("-0x0."+("0"*268)+"1fp2099"))
+ assert_equal(-31.0*2**1019, Float("-0x0."+("0"*600)+"1fp3427"))
+ suppress_warning do
+ assert_equal(31.0*2**-1027, Float("0x1f"+("0"*268)+".0p-2099"))
+ assert_equal(31.0*2**-1027, Float("0x1f"+("0"*600)+".0p-3427"))
+ assert_equal(-31.0*2**-1027, Float("-0x1f"+("0"*268)+".0p-2099"))
+ assert_equal(-31.0*2**-1027, Float("-0x1f"+("0"*600)+".0p-3427"))
+ end
+ end
+
+ def test_divmod
+ assert_equal([2, 3.5], 11.5.divmod(4))
+ assert_equal([-3, -0.5], 11.5.divmod(-4))
+ assert_equal([-3, 0.5], (-11.5).divmod(4))
+ assert_equal([2, -3.5], (-11.5).divmod(-4))
+ assert_raise(FloatDomainError) { Float::NAN.divmod(2) }
+ assert_raise(FloatDomainError) { Float::INFINITY.divmod(2) }
+ end
+
+ def test_div
+ assert_equal(2, 11.5.div(4))
+ assert_equal(-3, 11.5.div(-4))
+ assert_equal(-3, (-11.5).div(4))
+ assert_equal(2, (-11.5).div(-4))
+ assert_raise(FloatDomainError) { 11.5.div(Float::NAN).nan? }
+ assert_raise(FloatDomainError) { Float::NAN.div(2).nan? }
+ assert_raise(FloatDomainError) { Float::NAN.div(11.5).nan? }
+ end
+
+ def test_modulo
+ assert_equal(3.5, 11.5.modulo(4))
+ assert_equal(-0.5, 11.5.modulo(-4))
+ assert_equal(0.5, (-11.5).modulo(4))
+ assert_equal(-3.5, (-11.5).modulo(-4))
+ end
+
+ def test_remainder
+ assert_equal(3.5, 11.5.remainder(4))
+ assert_equal(3.5, 11.5.remainder(-4))
+ assert_equal(-3.5, (-11.5).remainder(4))
+ assert_equal(-3.5, (-11.5).remainder(-4))
+ assert_predicate(Float::NAN.remainder(4), :nan?)
+ assert_predicate(4.remainder(Float::NAN), :nan?)
+ end
+
+ def test_to_s
+ inf = Float::INFINITY
+ assert_equal("Infinity", inf.to_s)
+ assert_equal("-Infinity", (-inf).to_s)
+ assert_equal("NaN", (inf / inf).to_s)
+
+ assert_equal("1.0e+18", 1000_00000_00000_00000.0.to_s)
+
+ bug3273 = '[ruby-core:30145]'
+ [0.21611564636388508, 0.56].each do |f|
+ s = f.to_s
+ assert_equal(f, s.to_f, bug3273)
+ assert_not_equal(f, s.chop.to_f, bug3273)
+ end
+ end
+
+ def test_coerce
+ assert_equal(Float, 1.0.coerce(1).first.class)
+ end
+
+ def test_plus
+ assert_equal(4.0, 2.0.send(:+, 2))
+ assert_equal(4.0, 2.0.send(:+, (2**32).coerce(2).first))
+ assert_equal(4.0, 2.0.send(:+, 2.0))
+ assert_equal(Float::INFINITY, 2.0.send(:+, Float::INFINITY))
+ assert_predicate(2.0.send(:+, Float::NAN), :nan?)
+ assert_raise(TypeError) { 2.0.send(:+, nil) }
+ end
+
+ def test_minus
+ assert_equal(0.0, 2.0.send(:-, 2))
+ assert_equal(0.0, 2.0.send(:-, (2**32).coerce(2).first))
+ assert_equal(0.0, 2.0.send(:-, 2.0))
+ assert_equal(-Float::INFINITY, 2.0.send(:-, Float::INFINITY))
+ assert_predicate(2.0.send(:-, Float::NAN), :nan?)
+ assert_raise(TypeError) { 2.0.send(:-, nil) }
+ end
+
+ def test_mul
+ assert_equal(4.0, 2.0.send(:*, 2))
+ assert_equal(4.0, 2.0.send(:*, (2**32).coerce(2).first))
+ assert_equal(4.0, 2.0.send(:*, 2.0))
+ assert_equal(Float::INFINITY, 2.0.send(:*, Float::INFINITY))
+ assert_raise(TypeError) { 2.0.send(:*, nil) }
+ end
+
+ def test_div2
+ assert_equal(1.0, 2.0.send(:/, 2))
+ assert_equal(1.0, 2.0.send(:/, (2**32).coerce(2).first))
+ assert_equal(1.0, 2.0.send(:/, 2.0))
+ assert_equal(0.0, 2.0.send(:/, Float::INFINITY))
+ assert_raise(TypeError) { 2.0.send(:/, nil) }
+ end
+
+ def test_modulo2
+ assert_equal(0.0, 2.0.send(:%, 2))
+ assert_equal(0.0, 2.0.send(:%, (2**32).coerce(2).first))
+ assert_equal(0.0, 2.0.send(:%, 2.0))
+ assert_raise(TypeError) { 2.0.send(:%, nil) }
+ end
+
+ def test_modulo3
+ bug6048 = '[ruby-core:42726]'
+ assert_equal(4.2, 4.2.send(:%, Float::INFINITY), bug6048)
+ assert_equal(4.2, 4.2 % Float::INFINITY, bug6048)
+ assert_is_minus_zero(-0.0 % 4.2)
+ assert_is_minus_zero(-0.0.send :%, 4.2)
+ assert_raise(ZeroDivisionError, bug6048) { 4.2.send(:%, 0.0) }
+ assert_raise(ZeroDivisionError, bug6048) { 4.2 % 0.0 }
+ assert_raise(ZeroDivisionError, bug6048) { 42.send(:%, 0) }
+ assert_raise(ZeroDivisionError, bug6048) { 42 % 0 }
+ end
+
+ def test_modulo4
+ assert_predicate((0.0).modulo(Float::NAN), :nan?)
+ assert_predicate((1.0).modulo(Float::NAN), :nan?)
+ assert_predicate(Float::INFINITY.modulo(1), :nan?)
+ end
+
+ def test_divmod2
+ assert_equal([1.0, 0.0], 2.0.divmod(2))
+ assert_equal([1.0, 0.0], 2.0.divmod((2**32).coerce(2).first))
+ assert_equal([1.0, 0.0], 2.0.divmod(2.0))
+ assert_raise(TypeError) { 2.0.divmod(nil) }
+
+ inf = Float::INFINITY
+ assert_raise(ZeroDivisionError) {inf.divmod(0)}
+
+ a, b = (2.0**32).divmod(1.0)
+ assert_equal(2**32, a)
+ assert_equal(0, b)
+ end
+
+ def test_pow
+ assert_equal(1.0, 1.0 ** (2**32))
+ assert_equal(1.0, 1.0 ** 1.0)
+ assert_raise(TypeError) { 1.0 ** nil }
+ end
+
+ def test_eql
+ inf = Float::INFINITY
+ nan = Float::NAN
+ assert_operator(1.0, :eql?, 1.0)
+ assert_operator(inf, :eql?, inf)
+ assert_not_operator(nan, :eql?, nan)
+ assert_not_operator(1.0, :eql?, nil)
+
+ assert_equal(1.0, 1)
+ assert_not_equal(1.0, 2**32)
+ assert_not_equal(1.0, nan)
+ assert_not_equal(1.0, nil)
+ end
+
+ def test_cmp
+ inf = Float::INFINITY
+ nan = Float::NAN
+ assert_equal(0, 1.0 <=> 1.0)
+ assert_equal(1, 1.0 <=> 0.0)
+ assert_equal(-1, 1.0 <=> 2.0)
+ assert_nil(1.0 <=> nil)
+ assert_nil(1.0 <=> nan)
+ assert_nil(nan <=> 1.0)
+
+ assert_equal(0, 1.0 <=> 1)
+ assert_equal(1, 1.0 <=> 0)
+ assert_equal(-1, 1.0 <=> 2)
+
+ assert_equal(-1, 1.0 <=> 2**32)
+
+ assert_equal(1, inf <=> (Float::MAX.to_i*2))
+ assert_equal(-1, -inf <=> (-Float::MAX.to_i*2))
+ assert_equal(-1, (Float::MAX.to_i*2) <=> inf)
+ assert_equal(1, (-Float::MAX.to_i*2) <=> -inf)
+
+ bug3609 = '[ruby-core:31470]'
+ def (pinf = Object.new).infinite?; +1 end
+ def (ninf = Object.new).infinite?; -1 end
+ def (fin = Object.new).infinite?; nil end
+ nonum = Object.new
+ assert_equal(0, inf <=> pinf, bug3609)
+ assert_equal(1, inf <=> fin, bug3609)
+ assert_equal(1, inf <=> ninf, bug3609)
+ assert_nil(inf <=> nonum, bug3609)
+ assert_equal(-1, -inf <=> pinf, bug3609)
+ assert_equal(-1, -inf <=> fin, bug3609)
+ assert_equal(0, -inf <=> ninf, bug3609)
+ assert_nil(-inf <=> nonum, bug3609)
+
+ assert_raise(ArgumentError) { 1.0 > nil }
+ assert_raise(ArgumentError) { 1.0 >= nil }
+ assert_raise(ArgumentError) { 1.0 < nil }
+ assert_raise(ArgumentError) { 1.0 <= nil }
+ end
+
+ def test_zero_p
+ assert_predicate(0.0, :zero?)
+ assert_not_predicate(1.0, :zero?)
+ end
+
+ def test_infinite_p
+ inf = Float::INFINITY
+ assert_equal(1, inf.infinite?)
+ assert_equal(-1, (-inf).infinite?)
+ assert_nil(1.0.infinite?)
+ end
+
+ def test_finite_p
+ inf = Float::INFINITY
+ assert_not_predicate(inf, :finite?)
+ assert_not_predicate(-inf, :finite?)
+ assert_predicate(1.0, :finite?)
+ end
+
+ def test_floor_ceil_round_truncate
+ assert_equal(1, 1.5.floor)
+ assert_equal(2, 1.5.ceil)
+ assert_equal(2, 1.5.round)
+ assert_equal(1, 1.5.truncate)
+
+ assert_equal(2, 2.0.floor)
+ assert_equal(2, 2.0.ceil)
+ assert_equal(2, 2.0.round)
+ assert_equal(2, 2.0.truncate)
+
+ assert_equal(-2, (-1.5).floor)
+ assert_equal(-1, (-1.5).ceil)
+ assert_equal(-2, (-1.5).round)
+ assert_equal(-1, (-1.5).truncate)
+
+ assert_equal(-2, (-2.0).floor)
+ assert_equal(-2, (-2.0).ceil)
+ assert_equal(-2, (-2.0).round)
+ assert_equal(-2, (-2.0).truncate)
+
+ inf = Float::INFINITY
+ assert_raise(FloatDomainError) { inf.floor }
+ assert_raise(FloatDomainError) { inf.ceil }
+ assert_raise(FloatDomainError) { inf.round }
+ assert_raise(FloatDomainError) { inf.truncate }
+ end
+
+ def test_round_with_precision
+ assert_equal(1.100, 1.111.round(1))
+ assert_equal(1.110, 1.111.round(2))
+ assert_equal(11110.0, 11111.1.round(-1))
+ assert_equal(11100.0, 11111.1.round(-2))
+
+ assert_equal(10**300, 1.1e300.round(-300))
+ assert_equal(-10**300, -1.1e300.round(-300))
+ assert_equal(1.0e-300, 1.1e-300.round(300))
+ assert_equal(-1.0e-300, -1.1e-300.round(300))
+
+ bug5227 = '[ruby-core:39093]'
+ assert_equal(42.0, 42.0.round(308), bug5227)
+ assert_equal(1.0e307, 1.0e307.round(2), bug5227)
+
+ assert_raise(TypeError) {1.0.round("4")}
+ assert_raise(TypeError) {1.0.round(nil)}
+ def (prec = Object.new).to_int; 2; end
+ assert_equal(1.0, 0.998.round(prec))
+ end
+
+ VS = [
+ 18446744073709551617.0,
+ 18446744073709551616.0,
+ 18446744073709551615.8,
+ 18446744073709551615.5,
+ 18446744073709551615.2,
+ 18446744073709551615.0,
+ 18446744073709551614.0,
+
+ 4611686018427387905.0,
+ 4611686018427387904.0,
+ 4611686018427387903.8,
+ 4611686018427387903.5,
+ 4611686018427387903.2,
+ 4611686018427387903.0,
+ 4611686018427387902.0,
+
+ 4294967297.0,
+ 4294967296.0,
+ 4294967295.8,
+ 4294967295.5,
+ 4294967295.2,
+ 4294967295.0,
+ 4294967294.0,
+
+ 1073741825.0,
+ 1073741824.0,
+ 1073741823.8,
+ 1073741823.5,
+ 1073741823.2,
+ 1073741823.0,
+ 1073741822.0,
+
+ -1073741823.0,
+ -1073741824.0,
+ -1073741824.2,
+ -1073741824.5,
+ -1073741824.8,
+ -1073741825.0,
+ -1073741826.0,
+
+ -4294967295.0,
+ -4294967296.0,
+ -4294967296.2,
+ -4294967296.5,
+ -4294967296.8,
+ -4294967297.0,
+ -4294967298.0,
+
+ -4611686018427387903.0,
+ -4611686018427387904.0,
+ -4611686018427387904.2,
+ -4611686018427387904.5,
+ -4611686018427387904.8,
+ -4611686018427387905.0,
+ -4611686018427387906.0,
+
+ -18446744073709551615.0,
+ -18446744073709551616.0,
+ -18446744073709551616.2,
+ -18446744073709551616.5,
+ -18446744073709551616.8,
+ -18446744073709551617.0,
+ -18446744073709551618.0,
+ ]
+
+ def test_truncate
+ VS.each {|f|
+ i = f.truncate
+ assert_equal(i, f.to_i)
+ if f < 0
+ assert_operator(i, :<, 0)
+ else
+ assert_operator(i, :>, 0)
+ end
+ assert_operator(i.abs, :<=, f.abs)
+ d = f.abs - i.abs
+ assert_operator(0, :<=, d)
+ assert_operator(d, :<, 1)
+ }
+ end
+
+ def test_ceil
+ VS.each {|f|
+ i = f.ceil
+ if f < 0
+ assert_operator(i, :<, 0)
+ else
+ assert_operator(i, :>, 0)
+ end
+ assert_operator(i, :>=, f)
+ d = f - i
+ assert_operator(-1, :<, d)
+ assert_operator(d, :<=, 0)
+ }
+ end
+
+ def test_floor
+ VS.each {|f|
+ i = f.floor
+ if f < 0
+ assert_operator(i, :<, 0)
+ else
+ assert_operator(i, :>, 0)
+ end
+ assert_operator(i, :<=, f)
+ d = f - i
+ assert_operator(0, :<=, d)
+ assert_operator(d, :<, 1)
+ }
+ end
+
+ def test_round
+ VS.each {|f|
+ msg = "round(#{f})"
+ i = f.round
+ if f < 0
+ assert_operator(i, :<, 0, msg)
+ else
+ assert_operator(i, :>, 0, msg)
+ end
+ d = f - i
+ assert_operator(-0.5, :<=, d, msg)
+ assert_operator(d, :<=, 0.5, msg)
+ }
+ end
+
+ def test_Float
+ assert_in_delta(0.125, Float("0.1_2_5"), 0.00001)
+ assert_in_delta(0.125, "0.1_2_5__".to_f, 0.00001)
+ assert_equal(1, suppress_warning {Float(([1] * 10000).join)}.infinite?)
+ assert_not_predicate(Float(([1] * 10000).join("_")), :infinite?) # is it really OK?
+ assert_raise(ArgumentError) { Float("1.0\x001") }
+ assert_equal(15.9375, Float('0xf.fp0'))
+ assert_raise(ArgumentError) { Float('0x') }
+ assert_equal(15, Float('0xf'))
+ assert_equal(15, Float('0xfp0'))
+ assert_raise(ArgumentError) { Float('0xfp') }
+ assert_raise(ArgumentError) { Float('0xf.') }
+ assert_raise(ArgumentError) { Float('0xf.p') }
+ assert_raise(ArgumentError) { Float('0xf.p0') }
+ assert_raise(ArgumentError) { Float('0xf.f') }
+ assert_raise(ArgumentError) { Float('0xf.fp') }
+ assert_equal(Float::INFINITY, Float('0xf.fp1000000000000000'))
+ assert_equal(1, suppress_warning {Float("1e10_00")}.infinite?)
+ assert_raise(TypeError) { Float(nil) }
+ o = Object.new
+ def o.to_f; inf = Float::INFINITY; inf/inf; end
+ assert_predicate(Float(o), :nan?)
+ end
+
+ def test_invalid_str
+ bug4310 = '[ruby-core:34820]'
+ assert_raise(ArgumentError, bug4310) {under_gc_stress {Float('a'*10000)}}
+ end
+
+ def test_num2dbl
+ assert_raise(ArgumentError) do
+ 1.0.step(2.0, "0.5") {}
+ end
+ assert_raise(TypeError) do
+ 1.0.step(2.0, nil) {}
+ end
+ end
+
+ def test_sleep_with_Float
+ assert_nothing_raised("[ruby-core:23282]") do
+ sleep(0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1)
+ end
+ end
+
+ def test_step
+ 1000.times do
+ a = rand
+ b = a+rand*1000
+ s = (b - a) / 10
+ assert_equal(11, (a..b).step(s).to_a.length)
+ end
+
+ (1.0..12.7).step(1.3).each do |n|
+ assert_operator(n, :<=, 12.7)
+ end
+
+ assert_equal([5.0, 4.0, 3.0, 2.0], 5.0.step(1.5, -1).to_a)
+ end
+
+ def test_step2
+ assert_equal([0.0], 0.0.step(1.0, Float::INFINITY).to_a)
+ end
+
+ def test_step_excl
+ 1000.times do
+ a = rand
+ b = a+rand*1000
+ s = (b - a) / 10
+ assert_equal(10, (a...b).step(s).to_a.length)
+ end
+
+ assert_equal([1.0, 2.9, 4.8, 6.699999999999999], (1.0...6.8).step(1.9).to_a)
+
+ e = 1+1E-12
+ (1.0 ... e).step(1E-16) do |n|
+ assert_operator(n, :<=, e)
+ end
+ end
+
+ def test_singleton_method
+ # flonum on 64bit platform
+ assert_raise(TypeError) { a = 1.0; def a.foo; end }
+ # always not flonum
+ assert_raise(TypeError) { a = Float::INFINITY; def a.foo; end }
+ end
+
+ def test_long_string
+ assert_separately([], <<-'end;')
+ assert_in_epsilon(10.0, ("1."+"1"*300000).to_f*9)
+ end;
+ end
+
+ def test_next_float
+ smallest = 0.0.next_float
+ assert_equal(-Float::MAX, (-Float::INFINITY).next_float)
+ assert_operator(-Float::MAX, :<, (-Float::MAX).next_float)
+ assert_equal(Float::EPSILON/2, (-1.0).next_float + 1.0)
+ assert_operator(0.0, :<, smallest)
+ assert_operator([0.0, smallest], :include?, smallest/2)
+ assert_equal(Float::EPSILON, 1.0.next_float - 1.0)
+ assert_equal(Float::INFINITY, Float::MAX.next_float)
+ assert_equal(Float::INFINITY, Float::INFINITY.next_float)
+ assert_predicate(Float::NAN.next_float, :nan?)
+ end
+
+ def test_prev_float
+ smallest = 0.0.next_float
+ assert_equal(-Float::INFINITY, (-Float::INFINITY).prev_float)
+ assert_equal(-Float::INFINITY, (-Float::MAX).prev_float)
+ assert_equal(-Float::EPSILON, (-1.0).prev_float + 1.0)
+ assert_equal(-smallest, 0.0.prev_float)
+ assert_operator([0.0, 0.0.prev_float], :include?, 0.0.prev_float/2)
+ assert_equal(-Float::EPSILON/2, 1.0.prev_float - 1.0)
+ assert_operator(Float::MAX, :>, Float::MAX.prev_float)
+ assert_equal(Float::MAX, Float::INFINITY.prev_float)
+ assert_predicate(Float::NAN.prev_float, :nan?)
+ end
+
+ def test_next_prev_float_zero
+ z = 0.0.next_float.prev_float
+ assert_equal(0.0, z)
+ assert_equal(Float::INFINITY, 1.0/z)
+ z = 0.0.prev_float.next_float
+ assert_equal(0.0, z)
+ assert_equal(-Float::INFINITY, 1.0/z)
+ end
+
+ def test_hash_0
+ bug10979 = '[ruby-core:68541] [Bug #10979]'
+ assert_equal(+0.0.hash, -0.0.hash)
+ assert_operator(+0.0, :eql?, -0.0)
+ h = {0.0 => bug10979}
+ assert_equal(bug10979, h[-0.0])
+ end
+end
diff --git a/jni/ruby/test/ruby/test_fnmatch.rb b/jni/ruby/test/ruby/test_fnmatch.rb
new file mode 100644
index 0000000..a0eab54
--- /dev/null
+++ b/jni/ruby/test/ruby/test_fnmatch.rb
@@ -0,0 +1,131 @@
+require 'test/unit'
+
+class TestFnmatch < Test::Unit::TestCase
+
+ def bracket_test(s, t) # `s' should start with neither '!' nor '^'
+ 0x21.upto(0x7E) do |i|
+ assert_equal(t.include?(i.chr), File.fnmatch("[#{s}]", i.chr, File::FNM_DOTMATCH))
+ assert_equal(t.include?(i.chr), !File.fnmatch("[^#{s}]", i.chr, File::FNM_DOTMATCH))
+ assert_equal(t.include?(i.chr), !File.fnmatch("[!#{s}]", i.chr, File::FNM_DOTMATCH))
+ end
+ end
+ def test_fnmatch
+ assert_file.for("[ruby-dev:22819]").fnmatch('\[1\]' , '[1]')
+ assert_file.for("[ruby-dev:22815]").fnmatch('*?', 'a')
+ assert_file.fnmatch('*/', 'a/')
+ assert_file.fnmatch('\[1\]' , '[1]', File::FNM_PATHNAME)
+ assert_file.fnmatch('*?', 'a', File::FNM_PATHNAME)
+ assert_file.fnmatch('*/', 'a/', File::FNM_PATHNAME)
+ # text
+ assert_file.fnmatch('cat', 'cat')
+ assert_file.not_fnmatch('cat', 'category')
+ assert_file.not_fnmatch('cat', 'wildcat')
+ # '?' matches any one character
+ assert_file.fnmatch('?at', 'cat')
+ assert_file.fnmatch('c?t', 'cat')
+ assert_file.fnmatch('ca?', 'cat')
+ assert_file.fnmatch('?a?', 'cat')
+ assert_file.not_fnmatch('c??t', 'cat')
+ assert_file.not_fnmatch('??at', 'cat')
+ assert_file.not_fnmatch('ca??', 'cat')
+ # '*' matches any number (including 0) of any characters
+ assert_file.fnmatch('c*', 'cats')
+ assert_file.fnmatch('c*ts', 'cats')
+ assert_file.fnmatch('*ts', 'cats')
+ assert_file.fnmatch('*c*a*t*s*', 'cats')
+ assert_file.not_fnmatch('c*t', 'cats')
+ assert_file.not_fnmatch('*abc', 'abcabz')
+ assert_file.fnmatch('*abz', 'abcabz')
+ assert_file.not_fnmatch('a*abc', 'abc')
+ assert_file.fnmatch('a*bc', 'abc')
+ assert_file.not_fnmatch('a*bc', 'abcd')
+ # [seq] : matches any character listed between bracket
+ # [!seq] or [^seq] : matches any character except those listed between bracket
+ bracket_test("bd-gikl-mosv-x", "bdefgiklmosvwx")
+ # escaping character
+ assert_file.fnmatch('\?', '?')
+ assert_file.not_fnmatch('\?', '\?')
+ assert_file.not_fnmatch('\?', 'a')
+ assert_file.not_fnmatch('\?', '\a')
+ assert_file.fnmatch('\*', '*')
+ assert_file.not_fnmatch('\*', '\*')
+ assert_file.not_fnmatch('\*', 'cats')
+ assert_file.not_fnmatch('\*', '\cats')
+ assert_file.fnmatch('\a', 'a')
+ assert_file.not_fnmatch('\a', '\a')
+ assert_file.fnmatch('[a\-c]', 'a')
+ assert_file.fnmatch('[a\-c]', '-')
+ assert_file.fnmatch('[a\-c]', 'c')
+ assert_file.not_fnmatch('[a\-c]', 'b')
+ assert_file.not_fnmatch('[a\-c]', '\\')
+ # escaping character loses its meaning if FNM_NOESCAPE is set
+ assert_file.not_fnmatch('\?', '?', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\?', '\?', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('\?', 'a', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\?', '\a', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('\*', '*', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\*', '\*', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('\*', 'cats', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\*', '\cats', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('\a', 'a', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\a', '\a', File::FNM_NOESCAPE)
+ assert_file.fnmatch('[a\-c]', 'a', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('[a\-c]', '-', File::FNM_NOESCAPE)
+ assert_file.fnmatch('[a\-c]', 'c', File::FNM_NOESCAPE)
+ assert_file.fnmatch('[a\-c]', 'b', File::FNM_NOESCAPE) # '\\' < 'b' < 'c'
+ assert_file.fnmatch('[a\-c]', '\\', File::FNM_NOESCAPE)
+ # case is ignored if FNM_CASEFOLD is set
+ assert_file.not_fnmatch('cat', 'CAT')
+ assert_file.fnmatch('cat', 'CAT', File::FNM_CASEFOLD)
+ assert_file.not_fnmatch('[a-z]', 'D')
+ assert_file.fnmatch('[a-z]', 'D', File::FNM_CASEFOLD)
+ assert_file.not_fnmatch('[abc]', 'B')
+ assert_file.fnmatch('[abc]', 'B', File::FNM_CASEFOLD)
+ # wildcard doesn't match '/' if FNM_PATHNAME is set
+ assert_file.fnmatch('foo?boo', 'foo/boo')
+ assert_file.fnmatch('foo*', 'foo/boo')
+ assert_file.not_fnmatch('foo?boo', 'foo/boo', File::FNM_PATHNAME)
+ assert_file.not_fnmatch('foo*', 'foo/boo', File::FNM_PATHNAME)
+ # wildcard matches leading period if FNM_DOTMATCH is set
+ assert_file.not_fnmatch('*', '.profile')
+ assert_file.fnmatch('*', '.profile', File::FNM_DOTMATCH)
+ assert_file.fnmatch('.*', '.profile')
+ assert_file.fnmatch('*', 'dave/.profile')
+ assert_file.fnmatch('*/*', 'dave/.profile')
+ assert_file.not_fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME)
+ assert_file.fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH)
+ # recursive matching
+ assert_file.fnmatch('**/foo', 'a/b/c/foo', File::FNM_PATHNAME)
+ assert_file.fnmatch('**/foo', '/foo', File::FNM_PATHNAME)
+ assert_file.not_fnmatch('**/foo', 'a/.b/c/foo', File::FNM_PATHNAME)
+ assert_file.fnmatch('**/foo', 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH)
+ assert_file.fnmatch('**/foo', '/root/foo', File::FNM_PATHNAME)
+ assert_file.fnmatch('**/foo', 'c:/root/foo', File::FNM_PATHNAME)
+ end
+
+ def test_extglob
+ feature5422 = '[ruby-core:40037]'
+ assert_file.for(feature5422).not_fnmatch?( "{.g,t}*", ".gem")
+ assert_file.for(feature5422).fnmatch?("{.g,t}*", ".gem", File::FNM_EXTGLOB)
+ end
+
+ def test_unmatched_encoding
+ bug7911 = '[ruby-dev:47069] [Bug #7911]'
+ path = "\u{3042}"
+ pattern_ascii = 'a'.encode('US-ASCII')
+ pattern_eucjp = path.encode('EUC-JP')
+ assert_nothing_raised(ArgumentError, bug7911) do
+ assert_file.not_fnmatch(pattern_ascii, path)
+ assert_file.not_fnmatch(pattern_eucjp, path)
+ assert_file.not_fnmatch(pattern_ascii, path, File::FNM_CASEFOLD)
+ assert_file.not_fnmatch(pattern_eucjp, path, File::FNM_CASEFOLD)
+ assert_file.fnmatch("{*,#{pattern_ascii}}", path, File::FNM_EXTGLOB)
+ assert_file.fnmatch("{*,#{pattern_eucjp}}", path, File::FNM_EXTGLOB)
+ end
+ end
+
+ def test_unicode
+ assert_file.fnmatch("[a-\u3042]*", "\u3042")
+ assert_file.not_fnmatch("[a-\u3042]*", "\u3043")
+ end
+end
diff --git a/jni/ruby/test/ruby/test_gc.rb b/jni/ruby/test/ruby/test_gc.rb
new file mode 100644
index 0000000..e9e4bdc
--- /dev/null
+++ b/jni/ruby/test/ruby/test_gc.rb
@@ -0,0 +1,379 @@
+require 'test/unit'
+
+class TestGc < Test::Unit::TestCase
+ class S
+ def initialize(a)
+ @a = a
+ end
+ end
+
+ def test_gc
+ prev_stress = GC.stress
+ GC.stress = false
+
+ assert_nothing_raised do
+ 1.upto(10000) {
+ tmp = [0,1,2,3,4,5,6,7,8,9]
+ }
+ tmp = nil
+ end
+ l=nil
+ 100000.times {
+ l = S.new(l)
+ }
+ GC.start
+ assert true # reach here or dumps core
+ l = []
+ 100000.times {
+ l.push([l])
+ }
+ GC.start
+ assert true # reach here or dumps core
+
+ GC.stress = prev_stress
+ end
+
+ def use_rgengc?
+ GC::OPTS.include? 'USE_RGENGC'.freeze
+ end
+
+ def test_enable_disable
+ GC.enable
+ assert_equal(false, GC.enable)
+ assert_equal(false, GC.disable)
+ assert_equal(true, GC.disable)
+ assert_equal(true, GC.disable)
+ assert_nil(GC.start)
+ assert_equal(true, GC.enable)
+ assert_equal(false, GC.enable)
+ ensure
+ GC.enable
+ end
+
+ def test_start_full_mark
+ return unless use_rgengc?
+
+ GC.start(full_mark: false)
+ assert_nil GC.latest_gc_info(:major_by)
+
+ GC.start(full_mark: true)
+ assert_not_nil GC.latest_gc_info(:major_by)
+ end
+
+ def test_start_immediate_sweep
+ GC.start(immediate_sweep: false)
+ assert_equal false, GC.latest_gc_info(:immediate_sweep)
+
+ GC.start(immediate_sweep: true)
+ assert_equal true, GC.latest_gc_info(:immediate_sweep)
+ end
+
+ def test_count
+ c = GC.count
+ GC.start
+ assert_operator(c, :<, GC.count)
+ end
+
+ def test_stat
+ res = GC.stat
+ assert_equal(false, res.empty?)
+ assert_kind_of(Integer, res[:count])
+
+ arg = Hash.new
+ res = GC.stat(arg)
+ assert_equal(arg, res)
+ assert_equal(false, res.empty?)
+ assert_kind_of(Integer, res[:count])
+
+ stat, count = {}, {}
+ GC.start
+ GC.stat(stat)
+ ObjectSpace.count_objects(count)
+ assert_equal(count[:TOTAL]-count[:FREE], stat[:heap_live_slots])
+ assert_equal(count[:FREE], stat[:heap_free_slots])
+
+ # measure again without GC.start
+ 1000.times{ "a" + "b" }
+ GC.stat(stat)
+ ObjectSpace.count_objects(count)
+ assert_equal(count[:FREE], stat[:heap_free_slots])
+ end
+
+ def test_stat_argument
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.stat(:"\u{30eb 30d3 30fc}")}
+ end
+
+ def test_stat_single
+ stat = GC.stat
+ assert_equal stat[:count], GC.stat(:count)
+ assert_raise(ArgumentError){ GC.stat(:invalid) }
+ end
+
+ def test_stat_constraints
+ stat = GC.stat
+ assert_equal stat[:total_allocated_pages], stat[:heap_allocated_pages] + stat[:total_freed_pages]
+ assert_operator stat[:heap_sorted_length], :>=, stat[:heap_eden_pages] + stat[:heap_allocatable_pages], "stat is: " + stat.inspect
+ assert_equal stat[:heap_available_slots], stat[:heap_live_slots] + stat[:heap_free_slots] + stat[:heap_final_slots]
+ assert_equal stat[:heap_live_slots], stat[:total_allocated_objects] - stat[:total_freed_objects] - stat[:heap_final_slots]
+ assert_equal stat[:heap_allocated_pages], stat[:heap_eden_pages] + stat[:heap_tomb_pages]
+
+ if use_rgengc?
+ assert_equal stat[:count], stat[:major_gc_count] + stat[:minor_gc_count]
+ end
+ end
+
+ def test_latest_gc_info
+ assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
+ GC.start
+ count = GC.stat(:heap_free_slots) + GC.stat(:heap_allocatable_pages) * GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT]
+ count.times{ "a" + "b" }
+ assert_equal :newobj, GC.latest_gc_info[:gc_by]
+ eom
+
+ GC.start
+ assert_equal :force, GC.latest_gc_info[:major_by] if use_rgengc?
+ assert_equal :method, GC.latest_gc_info[:gc_by]
+ assert_equal true, GC.latest_gc_info[:immediate_sweep]
+
+ GC.stress = true
+ assert_equal :force, GC.latest_gc_info[:major_by]
+ ensure
+ GC.stress = false
+ end
+
+ def test_latest_gc_info_argument
+ info = {}
+ GC.latest_gc_info(info)
+
+ assert_not_empty info
+ assert_equal info[:gc_by], GC.latest_gc_info(:gc_by)
+ assert_raises(ArgumentError){ GC.latest_gc_info(:invalid) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.latest_gc_info(:"\u{30eb 30d3 30fc}")}
+ end
+
+ def test_singleton_method
+ assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:42832]")
+ GC.stress = true
+ 10.times do
+ obj = Object.new
+ def obj.foo() end
+ def obj.bar() raise "obj.foo is called, but this is obj.bar" end
+ obj.foo
+ end
+ EOS
+ end
+
+ def test_singleton_method_added
+ assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:44436]")
+ class BasicObject
+ undef singleton_method_added
+ def singleton_method_added(mid)
+ raise
+ end
+ end
+ b = proc {}
+ class << b; end
+ b.clone rescue nil
+ GC.start
+ EOS
+ end
+
+ def test_gc_parameter
+ env = {
+ "RUBY_GC_MALLOC_LIMIT" => "60000000",
+ "RUBY_GC_HEAP_INIT_SLOTS" => "100000"
+ }
+ assert_normal_exit("exit", "[ruby-core:39777]", :child_env => env)
+
+ env = {
+ "RUBYOPT" => "",
+ "RUBY_GC_HEAP_INIT_SLOTS" => "100000"
+ }
+ assert_in_out_err([env, "-e", "exit"], "", [], [], "[ruby-core:39795]")
+ assert_in_out_err([env, "-W0", "-e", "exit"], "", [], [], "[ruby-core:39795]")
+ assert_in_out_err([env, "-W1", "-e", "exit"], "", [], [], "[ruby-core:39795]")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_INIT_SLOTS=100000/, "[ruby-core:39795]")
+
+ env = {
+ "RUBY_GC_HEAP_GROWTH_FACTOR" => "2.0",
+ "RUBY_GC_HEAP_GROWTH_MAX_SLOTS" => "10000"
+ }
+ assert_normal_exit("exit", "", :child_env => env)
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_FACTOR=2.0/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_MAX_SLOTS=10000/, "[ruby-core:57928]")
+
+ env = {
+ "RUBY_GC_HEAP_INIT_SLOTS" => "100000",
+ "RUBY_GC_HEAP_FREE_SLOTS" => "10000",
+ "RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR" => "0.9",
+ }
+ assert_normal_exit("exit", "", :child_env => env)
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=0\.9/, "")
+
+ # always full GC when RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR < 1.0
+ assert_in_out_err([env, "-e", "1000_000.times{Object.new}; p(GC.stat[:minor_gc_count] < GC.stat[:major_gc_count])"], "", ['true'], //, "") if use_rgengc?
+
+ # check obsolete
+ assert_in_out_err([{'RUBY_FREE_MIN' => '100'}, '-w', '-eexit'], '', [],
+ /RUBY_FREE_MIN is obsolete. Use RUBY_GC_HEAP_FREE_SLOTS instead/)
+ assert_in_out_err([{'RUBY_HEAP_MIN_SLOTS' => '100'}, '-w', '-eexit'], '', [],
+ /RUBY_HEAP_MIN_SLOTS is obsolete. Use RUBY_GC_HEAP_INIT_SLOTS instead/)
+
+ env = {
+ "RUBY_GC_MALLOC_LIMIT" => "60000000",
+ "RUBY_GC_MALLOC_LIMIT_MAX" => "160000000",
+ "RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR" => "2.0"
+ }
+ assert_normal_exit("exit", "", :child_env => env)
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT=6000000/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT_MAX=16000000/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=2.0/, "")
+
+ if use_rgengc?
+ env = {
+ "RUBY_GC_OLDMALLOC_LIMIT" => "60000000",
+ "RUBY_GC_OLDMALLOC_LIMIT_MAX" => "160000000",
+ "RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR" => "2.0"
+ }
+ assert_normal_exit("exit", "", :child_env => env)
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT=6000000/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT_MAX=16000000/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=2.0/, "")
+ end
+ end
+
+ def test_profiler_enabled
+ GC::Profiler.enable
+ assert_equal(true, GC::Profiler.enabled?)
+ GC::Profiler.disable
+ assert_equal(false, GC::Profiler.enabled?)
+ ensure
+ GC::Profiler.disable
+ end
+
+ def test_profiler_clear
+ assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
+ GC::Profiler.enable
+
+ GC.start
+ assert_equal(1, GC::Profiler.raw_data.size)
+ GC::Profiler.clear
+ assert_equal(0, GC::Profiler.raw_data.size)
+
+ 200.times{ GC.start }
+ assert_equal(200, GC::Profiler.raw_data.size)
+ GC::Profiler.clear
+ assert_equal(0, GC::Profiler.raw_data.size)
+ eom
+ end
+
+ def test_profiler_total_time
+ GC::Profiler.enable
+ GC::Profiler.clear
+
+ GC.start
+ assert_operator(GC::Profiler.total_time, :>=, 0)
+ ensure
+ GC::Profiler.disable
+ end
+
+ def test_finalizing_main_thread
+ assert_in_out_err(%w[--disable-gems], <<-EOS, ["\"finalize\""], [], "[ruby-dev:46647]")
+ ObjectSpace.define_finalizer(Thread.main) { p 'finalize' }
+ EOS
+ end
+
+ def test_expand_heap
+ assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
+ GC.start
+ base_length = GC.stat[:heap_eden_pages]
+ (base_length * 500).times{ 'a' }
+ GC.start
+ assert_in_delta base_length, (v = GC.stat[:heap_eden_pages]), 1,
+ "invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_pages]: #{v})"
+
+ a = []
+ (base_length * 500).times{ a << 'a'; nil }
+ GC.start
+ assert_operator base_length, :<, GC.stat[:heap_eden_pages] + 1
+ eom
+ end
+
+ def test_gc_internals
+ assert_not_nil GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT]
+ assert_not_nil GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
+ end
+
+ def test_sweep_in_finalizer
+ bug9205 = '[ruby-core:58833] [Bug #9205]'
+ 2.times do
+ assert_ruby_status([], <<-'end;', bug9205, timeout: 60)
+ raise_proc = proc do |id|
+ GC.start
+ end
+ 1000.times do
+ ObjectSpace.define_finalizer(Object.new, raise_proc)
+ end
+ end;
+ end
+ end
+
+ def test_exception_in_finalizer
+ bug9168 = '[ruby-core:58652] [Bug #9168]'
+ assert_normal_exit(<<-'end;', bug9168)
+ raise_proc = proc {raise}
+ 10000.times do
+ ObjectSpace.define_finalizer(Object.new, raise_proc)
+ Thread.handle_interrupt(RuntimeError => :immediate) {break}
+ Thread.handle_interrupt(RuntimeError => :on_blocking) {break}
+ Thread.handle_interrupt(RuntimeError => :never) {break}
+ end
+ end;
+ end
+
+ def test_interrupt_in_finalizer
+ bug10595 = '[ruby-core:66825] [Bug #10595]'
+ src = <<-'end;'
+ pid = $$
+ Thread.start do
+ 10.times {
+ sleep 0.1
+ Process.kill("INT", pid) rescue break
+ }
+ sleep 5
+ Process.kill("SEGV", pid) rescue nil
+ Process.kill("KILL", pid) rescue nil
+ end
+ f = proc {1000.times {}}
+ loop do
+ ObjectSpace.define_finalizer(Object.new, f)
+ end
+ end;
+ status = assert_in_out_err(["-e", src], "", [], /Interrupt/, bug10595)
+ unless /mswin|mingw/ =~ RUBY_PLATFORM
+ assert_equal("INT", Signal.signame(status.termsig))
+ end
+ end
+
+ def test_verify_internal_consistency
+ assert_nil(GC.verify_internal_consistency)
+ end
+
+ def test_gc_stress_on_realloc
+ assert_normal_exit(<<-'end;', '[Bug #9859]')
+ class C
+ def initialize
+ @a = nil
+ @b = nil
+ @c = nil
+ @d = nil
+ @e = nil
+ @f = nil
+ end
+ end
+
+ GC.stress = true
+ C.new
+ end;
+ end
+end
diff --git a/jni/ruby/test/ruby/test_hash.rb b/jni/ruby/test/ruby/test_hash.rb
new file mode 100644
index 0000000..1bb32f6
--- /dev/null
+++ b/jni/ruby/test/ruby/test_hash.rb
@@ -0,0 +1,1300 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+EnvUtil.suppress_warning {require 'continuation'}
+
+class TestHash < Test::Unit::TestCase
+
+ def test_hash
+ x = @cls[1=>2, 2=>4, 3=>6]
+ y = @cls[1=>2, 2=>4, 3=>6] # y = {1, 2, 2, 4, 3, 6} # 1.9 doesn't support
+
+ assert_equal(2, x[1])
+
+ assert(begin
+ for k,v in y
+ raise if k*2 != v
+ end
+ true
+ rescue
+ false
+ end)
+
+ assert_equal(3, x.length)
+ assert_send([x, :has_key?, 1])
+ assert_send([x, :has_value?, 4])
+ assert_equal([4,6], x.values_at(2,3))
+ assert_equal({1=>2, 2=>4, 3=>6}, x)
+
+ z = y.keys.join(":")
+ assert_equal("1:2:3", z)
+
+ z = y.values.join(":")
+ assert_equal("2:4:6", z)
+ assert_equal(x, y)
+
+ y.shift
+ assert_equal(2, y.length)
+
+ z = [1,2]
+ y[z] = 256
+ assert_equal(256, y[z])
+
+ x = Hash.new(0)
+ x[1] = 1
+ assert_equal(1, x[1])
+ assert_equal(0, x[2])
+
+ x = Hash.new([])
+ assert_equal([], x[22])
+ assert_same(x[22], x[22])
+
+ x = Hash.new{[]}
+ assert_equal([], x[22])
+ assert_not_same(x[22], x[22])
+
+ x = Hash.new{|h,kk| z = kk; h[kk] = kk*2}
+ z = 0
+ assert_equal(44, x[22])
+ assert_equal(22, z)
+ z = 0
+ assert_equal(44, x[22])
+ assert_equal(0, z)
+ x.default = 5
+ assert_equal(5, x[23])
+
+ x = Hash.new
+ def x.default(k)
+ $z = k
+ self[k] = k*2
+ end
+ $z = 0
+ assert_equal(44, x[22])
+ assert_equal(22, $z)
+ $z = 0
+ assert_equal(44, x[22])
+ assert_equal(0, $z)
+ end
+
+ # From rubicon
+
+ def setup
+ @cls ||= Hash
+ @h = @cls[
+ 1 => 'one', 2 => 'two', 3 => 'three',
+ self => 'self', true => 'true', nil => 'nil',
+ 'nil' => nil
+ ]
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def test_bad_initialize_copy
+ h = Class.new(Hash) {
+ def initialize_copy(h)
+ super(Object.new)
+ end
+ }.new
+ assert_raise(TypeError) { h.dup }
+ end
+
+ def test_clear_initialize_copy
+ h = @cls[1=>2]
+ h.instance_eval {initialize_copy({})}
+ assert_empty(h)
+ end
+
+ def test_self_initialize_copy
+ h = @cls[1=>2]
+ h.instance_eval {initialize_copy(h)}
+ assert_equal(2, h[1])
+ end
+
+ def test_dup_will_rehash
+ set1 = @cls[]
+ set2 = @cls[set1 => true]
+
+ set1[set1] = true
+
+ assert_equal set2, set2.dup
+ end
+
+ def test_s_AREF
+ h = @cls["a" => 100, "b" => 200]
+ assert_equal(100, h['a'])
+ assert_equal(200, h['b'])
+ assert_nil(h['c'])
+
+ h = @cls.[]("a" => 100, "b" => 200)
+ assert_equal(100, h['a'])
+ assert_equal(200, h['b'])
+ assert_nil(h['c'])
+ end
+
+ def test_s_new
+ h = @cls.new
+ assert_instance_of(@cls, h)
+ assert_nil(h.default)
+ assert_nil(h['spurious'])
+
+ h = @cls.new('default')
+ assert_instance_of(@cls, h)
+ assert_equal('default', h.default)
+ assert_equal('default', h['spurious'])
+
+ end
+
+ def test_AREF # '[]'
+ t = Time.now
+ h = @cls[
+ 1 => 'one', 2 => 'two', 3 => 'three',
+ self => 'self', t => 'time', nil => 'nil',
+ 'nil' => nil
+ ]
+
+ assert_equal('one', h[1])
+ assert_equal('two', h[2])
+ assert_equal('three', h[3])
+ assert_equal('self', h[self])
+ assert_equal('time', h[t])
+ assert_equal('nil', h[nil])
+ assert_equal(nil, h['nil'])
+ assert_equal(nil, h['koala'])
+
+ h1 = h.dup
+ h1.default = :default
+
+ assert_equal('one', h1[1])
+ assert_equal('two', h1[2])
+ assert_equal('three', h1[3])
+ assert_equal('self', h1[self])
+ assert_equal('time', h1[t])
+ assert_equal('nil', h1[nil])
+ assert_equal(nil, h1['nil'])
+ assert_equal(:default, h1['koala'])
+
+
+ end
+
+ def test_ASET # '[]='
+ t = Time.now
+ h = @cls.new
+ h[1] = 'one'
+ h[2] = 'two'
+ h[3] = 'three'
+ h[self] = 'self'
+ h[t] = 'time'
+ h[nil] = 'nil'
+ h['nil'] = nil
+ assert_equal('one', h[1])
+ assert_equal('two', h[2])
+ assert_equal('three', h[3])
+ assert_equal('self', h[self])
+ assert_equal('time', h[t])
+ assert_equal('nil', h[nil])
+ assert_equal(nil, h['nil'])
+ assert_equal(nil, h['koala'])
+
+ h[1] = 1
+ h[nil] = 99
+ h['nil'] = nil
+ z = [1,2]
+ h[z] = 256
+ assert_equal(1, h[1])
+ assert_equal('two', h[2])
+ assert_equal('three', h[3])
+ assert_equal('self', h[self])
+ assert_equal('time', h[t])
+ assert_equal(99, h[nil])
+ assert_equal(nil, h['nil'])
+ assert_equal(nil, h['koala'])
+ assert_equal(256, h[z])
+ end
+
+ def test_AREF_fstring_key
+ h = {"abc" => 1}
+ before = GC.stat(:total_allocated_objects)
+ 5.times{ h["abc"] }
+ assert_equal before, GC.stat(:total_allocated_objects)
+ end
+
+ def test_ASET_fstring_key
+ a, b = {}, {}
+ assert_equal 1, a["abc"] = 1
+ assert_equal 1, b["abc"] = 1
+ assert_same a.keys[0], b.keys[0]
+ end
+
+ def test_NEWHASH_fstring_key
+ a = {"ABC" => :t}
+ b = {"ABC" => :t}
+ assert_same a.keys[0], b.keys[0]
+ assert_same "ABC".freeze, a.keys[0]
+ end
+
+ def test_EQUAL # '=='
+ h1 = @cls[ "a" => 1, "c" => 2 ]
+ h2 = @cls[ "a" => 1, "c" => 2, 7 => 35 ]
+ h3 = @cls[ "a" => 1, "c" => 2, 7 => 35 ]
+ h4 = @cls[ ]
+ assert_equal(h1, h1)
+ assert_equal(h2, h2)
+ assert_equal(h3, h3)
+ assert_equal(h4, h4)
+ assert_not_equal(h1, h2)
+ assert_equal(h2, h3)
+ assert_not_equal(h3, h4)
+ end
+
+ def test_clear
+ assert_operator(@h.size, :>, 0)
+ @h.clear
+ assert_equal(0, @h.size)
+ assert_nil(@h[1])
+ end
+
+ def test_clone
+ for taint in [ false, true ]
+ for frozen in [ false, true ]
+ a = @h.clone
+ a.taint if taint
+ a.freeze if frozen
+ b = a.clone
+
+ assert_equal(a, b)
+ assert_not_same(a, b)
+ assert_equal(a.frozen?, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
+ end
+ end
+ end
+
+ def test_default
+ assert_nil(@h.default)
+ h = @cls.new(:xyzzy)
+ assert_equal(:xyzzy, h.default)
+ end
+
+ def test_default=
+ assert_nil(@h.default)
+ @h.default = :xyzzy
+ assert_equal(:xyzzy, @h.default)
+ end
+
+ def test_delete
+ h1 = @cls[ 1 => 'one', 2 => 'two', true => 'true' ]
+ h2 = @cls[ 1 => 'one', 2 => 'two' ]
+ h3 = @cls[ 2 => 'two' ]
+
+ assert_equal('true', h1.delete(true))
+ assert_equal(h2, h1)
+
+ assert_equal('one', h1.delete(1))
+ assert_equal(h3, h1)
+
+ assert_equal('two', h1.delete(2))
+ assert_equal(@cls[], h1)
+
+ assert_nil(h1.delete(99))
+ assert_equal(@cls[], h1)
+
+ assert_equal('default 99', h1.delete(99) {|i| "default #{i}" })
+ end
+
+ def test_delete_if
+ base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
+ h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
+ h2 = @cls[ 2 => false, 'cat' => 99 ]
+ h3 = @cls[ 2 => false ]
+
+ h = base.dup
+ assert_equal(h, h.delete_if { false })
+ assert_equal(@cls[], h.delete_if { true })
+
+ h = base.dup
+ assert_equal(h1, h.delete_if {|k,v| k.instance_of?(String) })
+ assert_equal(h1, h)
+
+ h = base.dup
+ assert_equal(h2, h.delete_if {|k,v| v.instance_of?(String) })
+ assert_equal(h2, h)
+
+ h = base.dup
+ assert_equal(h3, h.delete_if {|k,v| v })
+ assert_equal(h3, h)
+
+ h = base.dup
+ n = 0
+ h.delete_if {|*a|
+ n += 1
+ assert_equal(2, a.size)
+ assert_equal(base[a[0]], a[1])
+ h.shift
+ true
+ }
+ assert_equal(base.size, n)
+ end
+
+ def test_keep_if
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 })
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal({1=>2,3=>4,5=>6}, h.keep_if{true})
+ end
+
+ def test_dup
+ for taint in [ false, true ]
+ for frozen in [ false, true ]
+ a = @h.dup
+ a.taint if taint
+ a.freeze if frozen
+ b = a.dup
+
+ assert_equal(a, b)
+ assert_not_same(a, b)
+ assert_equal(false, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
+ end
+ end
+ end
+
+ def test_dup_equality
+ h = @cls['k' => 'v']
+ assert_equal(h, h.dup)
+ h1 = @cls[h => 1]
+ assert_equal(h1, h1.dup)
+ h[1] = 2
+ assert_equal(h1, h1.dup)
+ end
+
+ def test_each
+ count = 0
+ @cls[].each { |k, v| count + 1 }
+ assert_equal(0, count)
+
+ h = @h
+ h.each do |k, v|
+ assert_equal(v, h.delete(k))
+ end
+ assert_equal(@cls[], h)
+
+ h = @cls[]
+ h[1] = 1
+ h[2] = 2
+ assert_equal([[1,1],[2,2]], h.each.to_a)
+ end
+
+ def test_each_key
+ count = 0
+ @cls[].each_key { |k| count + 1 }
+ assert_equal(0, count)
+
+ h = @h
+ h.each_key do |k|
+ h.delete(k)
+ end
+ assert_equal(@cls[], h)
+ end
+
+ def test_each_pair
+ count = 0
+ @cls[].each_pair { |k, v| count + 1 }
+ assert_equal(0, count)
+
+ h = @h
+ h.each_pair do |k, v|
+ assert_equal(v, h.delete(k))
+ end
+ assert_equal(@cls[], h)
+ end
+
+ def test_each_value
+ res = []
+ @cls[].each_value { |v| res << v }
+ assert_equal(0, [].length)
+
+ @h.each_value { |v| res << v }
+ assert_equal(0, [].length)
+
+ expected = []
+ @h.each { |k, v| expected << v }
+
+ assert_equal([], expected - res)
+ assert_equal([], res - expected)
+ end
+
+ def test_empty?
+ assert_empty(@cls[])
+ assert_not_empty(@h)
+ end
+
+ def test_fetch
+ assert_equal('gumbygumby', @h.fetch('gumby') {|k| k * 2 })
+ assert_equal('pokey', @h.fetch('gumby', 'pokey'))
+
+ assert_equal('one', @h.fetch(1))
+ assert_equal(nil, @h.fetch('nil'))
+ assert_equal('nil', @h.fetch(nil))
+ end
+
+ def test_fetch_error
+ assert_raise(KeyError) { @cls[].fetch(1) }
+ assert_raise(KeyError) { @h.fetch('gumby') }
+ e = assert_raise(KeyError) { @h.fetch('gumby'*20) }
+ assert_match(/key not found: "gumbygumby/, e.message)
+ assert_match(/\.\.\.\z/, e.message)
+ end
+
+ def test_key2?
+ assert_not_send([@cls[], :key?, 1])
+ assert_not_send([@cls[], :key?, nil])
+ assert_send([@h, :key?, nil])
+ assert_send([@h, :key?, 1])
+ assert_not_send([@h, :key?, 'gumby'])
+ end
+
+ def test_value?
+ assert_not_send([@cls[], :value?, 1])
+ assert_not_send([@cls[], :value?, nil])
+ assert_send([@h, :value?, 'one'])
+ assert_send([@h, :value?, nil])
+ assert_not_send([@h, :value?, 'gumby'])
+ end
+
+ def test_include?
+ assert_not_send([@cls[], :include?, 1])
+ assert_not_send([@cls[], :include?, nil])
+ assert_send([@h, :include?, nil])
+ assert_send([@h, :include?, 1])
+ assert_not_send([@h, :include?, 'gumby'])
+ end
+
+ def test_key
+ assert_equal(1, @h.key('one'))
+ assert_equal(nil, @h.key('nil'))
+ assert_equal('nil', @h.key(nil))
+
+ assert_equal(nil, @h.key('gumby'))
+ assert_equal(nil, @cls[].key('gumby'))
+ end
+
+ def test_values_at
+ res = @h.values_at('dog', 'cat', 'horse')
+ assert_equal(3, res.length)
+ assert_equal([nil, nil, nil], res)
+
+ res = @h.values_at
+ assert_equal(0, res.length)
+
+ res = @h.values_at(3, 2, 1, nil)
+ assert_equal 4, res.length
+ assert_equal %w( three two one nil ), res
+
+ res = @h.values_at(3, 99, 1, nil)
+ assert_equal 4, res.length
+ assert_equal ['three', nil, 'one', 'nil'], res
+ end
+
+
+ def test_invert
+ h = @h.invert
+ assert_equal(1, h['one'])
+ assert_equal(true, h['true'])
+ assert_equal(nil, h['nil'])
+
+ h.each do |k, v|
+ assert_send([@h, :key?, v]) # not true in general, but works here
+ end
+
+ h = @cls[ 'a' => 1, 'b' => 2, 'c' => 1].invert
+ assert_equal(2, h.length)
+ assert_include(%w[a c], h[1])
+ assert_equal('b', h[2])
+ end
+
+ def test_key?
+ assert_not_send([@cls[], :key?, 1])
+ assert_not_send([@cls[], :key?, nil])
+ assert_send([@h, :key?, nil])
+ assert_send([@h, :key?, 1])
+ assert_not_send([@h, :key?, 'gumby'])
+ end
+
+ def test_keys
+ assert_equal([], @cls[].keys)
+
+ keys = @h.keys
+ expected = []
+ @h.each { |k, v| expected << k }
+ assert_equal([], keys - expected)
+ assert_equal([], expected - keys)
+ end
+
+ def test_length
+ assert_equal(0, @cls[].length)
+ assert_equal(7, @h.length)
+ end
+
+ def test_member?
+ assert_not_send([@cls[], :member?, 1])
+ assert_not_send([@cls[], :member?, nil])
+ assert_send([@h, :member?, nil])
+ assert_send([@h, :member?, 1])
+ assert_not_send([@h, :member?, 'gumby'])
+ end
+
+ def test_rehash
+ a = [ "a", "b" ]
+ c = [ "c", "d" ]
+ h = @cls[ a => 100, c => 300 ]
+ assert_equal(100, h[a])
+ a[0] = "z"
+ assert_nil(h[a])
+ h.rehash
+ assert_equal(100, h[a])
+ end
+
+ def test_reject
+ assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].reject {|k, v| k + v < 7 })
+
+ base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
+ h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
+ h2 = @cls[ 2 => false, 'cat' => 99 ]
+ h3 = @cls[ 2 => false ]
+
+ h = base.dup
+ assert_equal(h, h.reject { false })
+ assert_equal(@cls[], h.reject { true })
+
+ h = base.dup
+ assert_equal(h1, h.reject {|k,v| k.instance_of?(String) })
+
+ assert_equal(h2, h.reject {|k,v| v.instance_of?(String) })
+
+ assert_equal(h3, h.reject {|k,v| v })
+ assert_equal(base, h)
+
+ h.instance_variable_set(:@foo, :foo)
+ h.default = 42
+ h.taint
+ h = EnvUtil.suppress_warning {h.reject {false}}
+ assert_instance_of(Hash, h)
+ assert_not_predicate(h, :tainted?)
+ assert_nil(h.default)
+ assert_not_send([h, :instance_variable_defined?, :@foo])
+ end
+
+ def test_reject!
+ base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
+ h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
+ h2 = @cls[ 2 => false, 'cat' => 99 ]
+ h3 = @cls[ 2 => false ]
+
+ h = base.dup
+ assert_equal(nil, h.reject! { false })
+ assert_equal(@cls[], h.reject! { true })
+
+ h = base.dup
+ assert_equal(h1, h.reject! {|k,v| k.instance_of?(String) })
+ assert_equal(h1, h)
+
+ h = base.dup
+ assert_equal(h2, h.reject! {|k,v| v.instance_of?(String) })
+ assert_equal(h2, h)
+
+ h = base.dup
+ assert_equal(h3, h.reject! {|k,v| v })
+ assert_equal(h3, h)
+ end
+
+ def test_replace
+ h = @cls[ 1 => 2, 3 => 4 ]
+ h1 = h.replace(@cls[ 9 => 8, 7 => 6 ])
+ assert_equal(h, h1)
+ assert_equal(8, h[9])
+ assert_equal(6, h[7])
+ assert_nil(h[1])
+ assert_nil(h[2])
+ end
+
+ def test_replace_bug9230
+ h = @cls[]
+ h.replace(@cls[])
+ assert_empty h
+
+ h = @cls[]
+ h.replace(@cls[].compare_by_identity)
+ assert_predicate(h, :compare_by_identity?)
+ end
+
+ def test_shift
+ h = @h.dup
+
+ @h.length.times {
+ k, v = h.shift
+ assert_send([@h, :key?, k])
+ assert_equal(@h[k], v)
+ }
+
+ assert_equal(0, h.length)
+ end
+
+ def test_size
+ assert_equal(0, @cls[].length)
+ assert_equal(7, @h.length)
+ end
+
+ def test_sort
+ h = @cls[].sort
+ assert_equal([], h)
+
+ h = @cls[ 1 => 1, 2 => 1 ].sort
+ assert_equal([[1,1], [2,1]], h)
+
+ h = @cls[ 'cat' => 'feline', 'ass' => 'asinine', 'bee' => 'beeline' ]
+ h1 = h.sort
+ assert_equal([ %w(ass asinine), %w(bee beeline), %w(cat feline)], h1)
+ end
+
+ def test_store
+ t = Time.now
+ h = @cls.new
+ h.store(1, 'one')
+ h.store(2, 'two')
+ h.store(3, 'three')
+ h.store(self, 'self')
+ h.store(t, 'time')
+ h.store(nil, 'nil')
+ h.store('nil', nil)
+ assert_equal('one', h[1])
+ assert_equal('two', h[2])
+ assert_equal('three', h[3])
+ assert_equal('self', h[self])
+ assert_equal('time', h[t])
+ assert_equal('nil', h[nil])
+ assert_equal(nil, h['nil'])
+ assert_equal(nil, h['koala'])
+
+ h.store(1, 1)
+ h.store(nil, 99)
+ h.store('nil', nil)
+ assert_equal(1, h[1])
+ assert_equal('two', h[2])
+ assert_equal('three', h[3])
+ assert_equal('self', h[self])
+ assert_equal('time', h[t])
+ assert_equal(99, h[nil])
+ assert_equal(nil, h['nil'])
+ assert_equal(nil, h['koala'])
+ end
+
+ def test_to_a
+ assert_equal([], @cls[].to_a)
+ assert_equal([[1,2]], @cls[ 1=>2 ].to_a)
+ a = @cls[ 1=>2, 3=>4, 5=>6 ].to_a
+ assert_equal([1,2], a.delete([1,2]))
+ assert_equal([3,4], a.delete([3,4]))
+ assert_equal([5,6], a.delete([5,6]))
+ assert_equal(0, a.length)
+
+ h = @cls[ 1=>2, 3=>4, 5=>6 ]
+ h.taint
+ a = h.to_a
+ assert_equal(true, a.tainted?)
+ end
+
+ def test_to_hash
+ h = @h.to_hash
+ assert_equal(@h, h)
+ assert_instance_of(@cls, h)
+ end
+
+ def test_to_h
+ h = @h.to_h
+ assert_equal(@h, h)
+ assert_instance_of(Hash, h)
+ end
+
+ def test_nil_to_h
+ h = nil.to_h
+ assert_equal({}, h)
+ assert_nil(h.default)
+ assert_nil(h.default_proc)
+ end
+
+ def test_to_s
+ h = @cls[ 1 => 2, "cat" => "dog", 1.5 => :fred ]
+ assert_equal(h.inspect, h.to_s)
+ $, = ":"
+ assert_equal(h.inspect, h.to_s)
+ h = @cls[]
+ assert_equal(h.inspect, h.to_s)
+ ensure
+ $, = nil
+ end
+
+ def test_update
+ h1 = @cls[ 1 => 2, 2 => 3, 3 => 4 ]
+ h2 = @cls[ 2 => 'two', 4 => 'four' ]
+
+ ha = @cls[ 1 => 2, 2 => 'two', 3 => 4, 4 => 'four' ]
+ hb = @cls[ 1 => 2, 2 => 3, 3 => 4, 4 => 'four' ]
+
+ assert_equal(ha, h1.update(h2))
+ assert_equal(ha, h1)
+
+ h1 = @cls[ 1 => 2, 2 => 3, 3 => 4 ]
+ h2 = @cls[ 2 => 'two', 4 => 'four' ]
+
+ assert_equal(hb, h2.update(h1))
+ assert_equal(hb, h2)
+ end
+
+ def test_value2?
+ assert_not_send([@cls[], :value?, 1])
+ assert_not_send([@cls[], :value?, nil])
+ assert_send([@h, :value?, nil])
+ assert_send([@h, :value?, 'one'])
+ assert_not_send([@h, :value?, 'gumby'])
+ end
+
+ def test_values
+ assert_equal([], @cls[].values)
+
+ vals = @h.values
+ expected = []
+ @h.each { |k, v| expected << v }
+ assert_equal([], vals - expected)
+ assert_equal([], expected - vals)
+ end
+
+ def test_intialize_wrong_arguments
+ assert_raise(ArgumentError) do
+ Hash.new(0) { }
+ end
+ end
+
+ def test_create
+ assert_equal({1=>2, 3=>4}, @cls[[[1,2],[3,4]]])
+ assert_raise(ArgumentError) { Hash[0, 1, 2] }
+ assert_warning(/wrong element type Fixnum at 1 /) {@cls[[[1, 2], 3]]}
+ bug5406 = '[ruby-core:39945]'
+ assert_raise(ArgumentError, bug5406) { @cls[[[1, 2], [3, 4, 5]]] }
+ assert_equal({1=>2, 3=>4}, @cls[1,2,3,4])
+ o = Object.new
+ def o.to_hash() {1=>2} end
+ assert_equal({1=>2}, @cls[o], "[ruby-dev:34555]")
+ end
+
+ def test_rehash2
+ h = @cls[1 => 2, 3 => 4]
+ assert_equal(h.dup, h.rehash)
+ assert_raise(RuntimeError) { h.each { h.rehash } }
+ assert_equal({}, @cls[].rehash)
+ end
+
+ def test_fetch2
+ assert_equal(:bar, @h.fetch(0, :foo) { :bar })
+ end
+
+ def test_default_proc
+ h = @cls.new {|hh, k| hh + k + "baz" }
+ assert_equal("foobarbaz", h.default_proc.call("foo", "bar"))
+ assert_nil(h.default_proc = nil)
+ assert_nil(h.default_proc)
+ h.default_proc = ->(_,_){ true }
+ assert_equal(true, h[:nope])
+ h = @cls[]
+ assert_nil(h.default_proc)
+ end
+
+ def test_shift2
+ h = @cls.new {|hh, k| :foo }
+ h[1] = 2
+ assert_equal([1, 2], h.shift)
+ assert_equal(:foo, h.shift)
+ assert_equal(:foo, h.shift)
+
+ h = @cls.new(:foo)
+ h[1] = 2
+ assert_equal([1, 2], h.shift)
+ assert_equal(:foo, h.shift)
+ assert_equal(:foo, h.shift)
+
+ h =@cls[1=>2]
+ h.each { assert_equal([1, 2], h.shift) }
+ end
+
+ def test_shift_none
+ h = @cls.new {|hh, k| "foo"}
+ def h.default(k = nil)
+ super.upcase
+ end
+ assert_equal("FOO", h.shift)
+ end
+
+ def test_reject_bang2
+ assert_equal({1=>2}, @cls[1=>2,3=>4].reject! {|k, v| k + v == 7 })
+ assert_nil(@cls[1=>2,3=>4].reject! {|k, v| k == 5 })
+ assert_nil(@cls[].reject! { })
+ end
+
+ def test_select
+ assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].select {|k, v| k + v >= 7 })
+
+ base = @cls[ 1 => 'one', '2' => false, true => 'true', 'cat' => 99 ]
+ h1 = @cls[ '2' => false, 'cat' => 99 ]
+ h2 = @cls[ 1 => 'one', true => 'true' ]
+ h3 = @cls[ 1 => 'one', true => 'true', 'cat' => 99 ]
+
+ h = base.dup
+ assert_equal(h, h.select { true })
+ assert_equal(@cls[], h.select { false })
+
+ h = base.dup
+ assert_equal(h1, h.select {|k,v| k.instance_of?(String) })
+
+ assert_equal(h2, h.select {|k,v| v.instance_of?(String) })
+
+ assert_equal(h3, h.select {|k,v| v })
+ assert_equal(base, h)
+
+ h.instance_variable_set(:@foo, :foo)
+ h.default = 42
+ h.taint
+ h = h.select {true}
+ assert_instance_of(Hash, h)
+ assert_not_predicate(h, :tainted?)
+ assert_nil(h.default)
+ assert_not_send([h, :instance_variable_defined?, :@foo])
+ end
+
+ def test_select!
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal(h, h.select! {|k, v| k + v >= 7 })
+ assert_equal({3=>4,5=>6}, h)
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal(nil, h.select!{true})
+ end
+
+ def test_clear2
+ assert_equal({}, @cls[1=>2,3=>4,5=>6].clear)
+ h = @cls[1=>2,3=>4,5=>6]
+ h.each { h.clear }
+ assert_equal({}, h)
+ end
+
+ def test_replace2
+ h1 = @cls.new { :foo }
+ h2 = @cls.new
+ h2.replace h1
+ assert_equal(:foo, h2[0])
+
+ assert_raise(ArgumentError) { h2.replace() }
+ assert_raise(TypeError) { h2.replace(1) }
+ h2.freeze
+ assert_raise(ArgumentError) { h2.replace() }
+ assert_raise(RuntimeError) { h2.replace(h1) }
+ assert_raise(RuntimeError) { h2.replace(42) }
+ end
+
+ def test_size2
+ assert_equal(0, @cls[].size)
+ end
+
+ def test_equal2
+ assert_not_equal(0, @cls[])
+ o = Object.new
+ o.instance_variable_set(:@cls, @cls)
+ def o.to_hash; @cls[]; end
+ def o.==(x); true; end
+ assert_equal({}, o)
+ def o.==(x); false; end
+ assert_not_equal({}, o)
+
+ h1 = @cls[1=>2]; h2 = @cls[3=>4]
+ assert_not_equal(h1, h2)
+ h1 = @cls[1=>2]; h2 = @cls[1=>4]
+ assert_not_equal(h1, h2)
+ end
+
+ def test_eql
+ assert_not_send([@cls[], :eql?, 0])
+ o = Object.new
+ o.instance_variable_set(:@cls, @cls)
+ def o.to_hash; @cls[]; end
+ def o.eql?(x); true; end
+ assert_send([@cls[], :eql?, o])
+ def o.eql?(x); false; end
+ assert_not_send([@cls[], :eql?, o])
+ end
+
+ def test_hash2
+ assert_kind_of(Integer, @cls[].hash)
+ h = @cls[1=>2]
+ h.shift
+ assert_equal({}.hash, h.hash, '[ruby-core:38650]')
+ bug9231 = '[ruby-core:58993] [Bug #9231]'
+ assert_not_equal(0, @cls[].hash, bug9231)
+ end
+
+ def test_update2
+ h1 = @cls[1=>2, 3=>4]
+ h2 = {1=>3, 5=>7}
+ h1.update(h2) {|k, v1, v2| k + v1 + v2 }
+ assert_equal({1=>6, 3=>4, 5=>7}, h1)
+ end
+
+ def test_merge
+ h1 = @cls[1=>2, 3=>4]
+ h2 = {1=>3, 5=>7}
+ assert_equal({1=>3, 3=>4, 5=>7}, h1.merge(h2))
+ assert_equal({1=>6, 3=>4, 5=>7}, h1.merge(h2) {|k, v1, v2| k + v1 + v2 })
+ end
+
+ def test_assoc
+ assert_equal([3,4], @cls[1=>2, 3=>4, 5=>6].assoc(3))
+ assert_nil(@cls[1=>2, 3=>4, 5=>6].assoc(4))
+ assert_equal([1.0,1], @cls[1.0=>1].assoc(1))
+ end
+
+ def test_assoc_compare_by_identity
+ h = @cls[]
+ h.compare_by_identity
+ h["a"] = 1
+ h["a".dup] = 2
+ assert_equal(["a",1], h.assoc("a"))
+ end
+
+ def test_rassoc
+ assert_equal([3,4], @cls[1=>2, 3=>4, 5=>6].rassoc(4))
+ assert_nil({1=>2, 3=>4, 5=>6}.rassoc(3))
+ end
+
+ def test_flatten
+ assert_equal([[1], [2]], @cls[[1] => [2]].flatten)
+
+ a = @cls[1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]]
+ assert_equal([1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten)
+ assert_equal([[1, "one"], [2, [2, "two"]], [3, [3, ["three"]]]], a.flatten(0))
+ assert_equal([1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten(1))
+ assert_equal([1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2))
+ assert_equal([1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3))
+ assert_equal([1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(-1))
+ assert_raise(TypeError){ a.flatten(Object) }
+ end
+
+ def test_callcc
+ h = @cls[1=>2]
+ c = nil
+ f = false
+ h.each { callcc {|c2| c = c2 } }
+ unless f
+ f = true
+ c.call
+ end
+ assert_raise(RuntimeError) { h.each { h.rehash } }
+
+ h = @cls[1=>2]
+ c = nil
+ assert_raise(RuntimeError) do
+ h.each { callcc {|c2| c = c2 } }
+ h.clear
+ c.call
+ end
+ end
+
+ def test_callcc_iter_level
+ bug9105 = '[ruby-dev:47803] [Bug #9105]'
+ h = @cls[1=>2, 3=>4]
+ c = nil
+ f = false
+ h.each {callcc {|c2| c = c2}}
+ unless f
+ f = true
+ c.call
+ end
+ assert_nothing_raised(RuntimeError, bug9105) do
+ h.each {|i, j|
+ h.delete(i);
+ assert_not_equal(false, i, bug9105)
+ }
+ end
+ end
+
+ def test_callcc_escape
+ bug9105 = '[ruby-dev:47803] [Bug #9105]'
+ assert_nothing_raised(RuntimeError, bug9105) do
+ h=@cls[]
+ cnt=0
+ c = callcc {|cc|cc}
+ h[cnt] = true
+ h.each{|i|
+ cnt+=1
+ c.call if cnt == 1
+ }
+ end
+ end
+
+ def test_callcc_reenter
+ bug9105 = '[ruby-dev:47803] [Bug #9105]'
+ assert_nothing_raised(RuntimeError, bug9105) do
+ h = @cls[1=>2,3=>4]
+ c = nil
+ f = false
+ h.each { |i|
+ callcc {|c2| c = c2 } unless c
+ h.delete(1) if f
+ }
+ unless f
+ f = true
+ c.call
+ end
+ end
+ end
+
+ def test_threaded_iter_level
+ bug9105 = '[ruby-dev:47807] [Bug #9105]'
+ h = @cls[1=>2]
+ 2.times.map {
+ f = false
+ th = Thread.start {h.each {f = true; sleep}}
+ Thread.pass until f
+ Thread.pass until th.stop?
+ th
+ }.each {|th| th.run; th.join}
+ assert_nothing_raised(RuntimeError, bug9105) do
+ h[5] = 6
+ end
+ assert_equal(6, h[5], bug9105)
+ end
+
+ def test_compare_by_identity
+ a = "foo"
+ assert_not_predicate(@cls[], :compare_by_identity?)
+ h = @cls[a => "bar"]
+ assert_not_predicate(h, :compare_by_identity?)
+ h.compare_by_identity
+ assert_predicate(h, :compare_by_identity?)
+ #assert_equal("bar", h[a])
+ assert_nil(h["foo"])
+
+ bug8703 = '[ruby-core:56256] [Bug #8703] copied identhash'
+ h.clear
+ assert_predicate(h.dup, :compare_by_identity?, bug8703)
+ end
+
+ def test_same_key
+ bug9646 = '[ruby-dev:48047] [Bug #9646] Infinite loop at Hash#each'
+ h = @cls[a=[], 1]
+ a << 1
+ h[[]] = 2
+ a.clear
+ cnt = 0
+ r = h.each{ break nil if (cnt+=1) > 100 }
+ assert_not_nil(r,bug9646)
+ end
+
+ class ObjWithHash
+ def initialize(value, hash)
+ @value = value
+ @hash = hash
+ end
+ attr_reader :value, :hash
+
+ def eql?(other)
+ @value == other.value
+ end
+ end
+
+ def test_hash_hash
+ assert_equal({0=>2,11=>1}.hash, @cls[11=>1,0=>2].hash)
+ o1 = ObjWithHash.new(0,1)
+ o2 = ObjWithHash.new(11,1)
+ assert_equal({o1=>1,o2=>2}.hash, @cls[o2=>2,o1=>1].hash)
+ end
+
+ def test_hash_bignum_hash
+ x = 2<<(32-3)-1
+ assert_equal({x=>1}.hash, @cls[x=>1].hash)
+ x = 2<<(64-3)-1
+ assert_equal({x=>1}.hash, @cls[x=>1].hash)
+
+ o = Object.new
+ def o.hash; 2 << 100; end
+ assert_equal({o=>1}.hash, @cls[o=>1].hash)
+ end
+
+ def test_hash_poped
+ assert_nothing_raised { eval("a = 1; @cls[a => a]; a") }
+ end
+
+ def test_recursive_key
+ h = @cls[]
+ assert_nothing_raised { h[h] = :foo }
+ h.rehash
+ assert_equal(:foo, h[h])
+ end
+
+ def test_inverse_hash
+ feature4262 = '[ruby-core:34334]'
+ [@cls[1=>2], @cls[123=>"abc"]].each do |h|
+ assert_not_equal(h.hash, h.invert.hash, feature4262)
+ end
+ end
+
+ def test_recursive_hash_value_struct
+ bug9151 = '[ruby-core:58567] [Bug #9151]'
+
+ s = Struct.new(:x) {def hash; [x,""].hash; end}
+ a = s.new
+ b = s.new
+ a.x = b
+ b.x = a
+ assert_nothing_raised(SystemStackError, bug9151) {a.hash}
+ assert_nothing_raised(SystemStackError, bug9151) {b.hash}
+
+ h = @cls[]
+ h[[a,"hello"]] = 1
+ assert_equal(1, h.size)
+ h[[b,"world"]] = 2
+ assert_equal(2, h.size)
+
+ obj = Object.new
+ h = @cls[a => obj]
+ assert_same(obj, h[b])
+ end
+
+ def test_recursive_hash_value_array
+ h = @cls[]
+ h[[[1]]] = 1
+ assert_equal(1, h.size)
+ h[[[2]]] = 1
+ assert_equal(2, h.size)
+
+ a = []
+ a << a
+
+ h = @cls[]
+ h[[a, 1]] = 1
+ assert_equal(1, h.size)
+ h[[a, 2]] = 2
+ assert_equal(2, h.size)
+ h[[a, a]] = 3
+ assert_equal(3, h.size)
+
+ obj = Object.new
+ h = @cls[a => obj]
+ assert_same(obj, h[[[a]]])
+ end
+
+ def test_recursive_hash_value_array_hash
+ h = @cls[]
+ rec = [h]
+ h[:x] = rec
+
+ obj = Object.new
+ h2 = {rec => obj}
+ [h, {x: rec}].each do |k|
+ k = [k]
+ assert_same(obj, h2[k], ->{k.inspect})
+ end
+ end
+
+ def test_recursive_hash_value_hash_array
+ h = @cls[]
+ rec = [h]
+ h[:x] = rec
+
+ obj = Object.new
+ h2 = {h => obj}
+ [rec, [h]].each do |k|
+ k = {x: k}
+ assert_same(obj, h2[k], ->{k.inspect})
+ end
+ end
+
+ def test_exception_in_rehash
+ return unless @cls == Hash
+
+ bug9187 = '[ruby-core:58728] [Bug #9187]'
+
+ prepare = <<-EOS
+ class Foo
+ def initialize
+ @raise = false
+ end
+
+ def hash
+ raise if @raise
+ @raise = true
+ return 0
+ end
+ end
+ h = {Foo.new => true}
+ EOS
+
+ code = <<-EOS
+ 10_0000.times do
+ h.rehash rescue nil
+ end
+ GC.start
+ EOS
+
+ assert_no_memory_leak([], prepare, code, bug9187)
+ end
+
+ def test_wrapper_of_special_const
+ bug9381 = '[ruby-core:59638] [Bug #9381]'
+
+ wrapper = Class.new do
+ def initialize(obj)
+ @obj = obj
+ end
+
+ def hash
+ @obj.hash
+ end
+
+ def eql?(other)
+ @obj.eql?(other)
+ end
+ end
+
+ bad = [
+ 5, true, false, nil,
+ 0.0, 1.72723e-77,
+ ].select do |x|
+ hash = {x => bug9381}
+ hash[wrapper.new(x)] != bug9381
+ end
+ assert_empty(bad, bug9381)
+ end
+
+ def test_label_syntax
+ return unless @cls == Hash
+
+ feature4935 = '[ruby-core:37553] [Feature #4935]'
+ x = 'world'
+ hash = assert_nothing_raised(SyntaxError) do
+ break eval(%q({foo: 1, "foo-bar": 2, "hello-#{x}": 3, 'hello-#{x}': 4, 'bar': {}}))
+ end
+ assert_equal({:foo => 1, :'foo-bar' => 2, :'hello-world' => 3, :'hello-#{x}' => 4, :bar => {}}, hash)
+ end
+
+ class TestSubHash < TestHash
+ class SubHash < Hash
+ def reject(*)
+ super
+ end
+ end
+
+ def setup
+ @cls = SubHash
+ super
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_ifunless.rb b/jni/ruby/test/ruby/test_ifunless.rb
new file mode 100644
index 0000000..e144ff8
--- /dev/null
+++ b/jni/ruby/test/ruby/test_ifunless.rb
@@ -0,0 +1,14 @@
+require 'test/unit'
+
+class TestIfunless < Test::Unit::TestCase
+ def test_if_unless
+ x = 'test';
+ assert(if x == x then true else false end)
+ bad = false
+ unless x == x
+ bad = true
+ end
+ assert(!bad)
+ assert(unless x != x then true else false end)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_integer.rb b/jni/ruby/test/ruby/test_integer.rb
new file mode 100644
index 0000000..3dbf365
--- /dev/null
+++ b/jni/ruby/test/ruby/test_integer.rb
@@ -0,0 +1,280 @@
+require 'test/unit'
+
+class TestInteger < Test::Unit::TestCase
+ BDSIZE = 0x4000000000000000.coerce(0)[0].size
+ def self.bdsize(x)
+ ((x + 1) / 8 + BDSIZE) / BDSIZE * BDSIZE
+ end
+ def bdsize(x)
+ self.class.bdsize(x)
+ end
+
+ def test_aref
+ # assert_equal(1, (1 << 0x40000000)[0x40000000], "[ruby-dev:31271]")
+ # assert_equal(0, (-1 << 0x40000001)[0x40000000], "[ruby-dev:31271]")
+ big_zero = 0x40000000.coerce(0)[0]
+ assert_equal(0, (-0x40000002)[big_zero], "[ruby-dev:31271]")
+ assert_equal(1, 0x400000001[big_zero], "[ruby-dev:31271]")
+ end
+
+ def test_pow
+ assert_not_equal(0, begin
+ 0**-1
+ rescue
+ nil
+ end, "[ruby-dev:32084] [ruby-dev:34547]")
+ end
+
+ def test_lshift
+ assert_equal(0, 1 << -0x40000000)
+ assert_equal(0, 1 << -0x40000001)
+ assert_equal(0, 1 << -0x80000000)
+ assert_equal(0, 1 << -0x80000001)
+ # assert_equal(bdsize(0x80000000), (1 << 0x80000000).size)
+ end
+
+ def test_rshift
+ # assert_equal(bdsize(0x40000001), (1 >> -0x40000001).size)
+ assert_predicate((1 >> 0x80000000), :zero?)
+ assert_predicate((1 >> 0xffffffff), :zero?)
+ assert_predicate((1 >> 0x100000000), :zero?)
+ # assert_equal((1 << 0x40000000), (1 >> -0x40000000))
+ # assert_equal((1 << 0x40000001), (1 >> -0x40000001))
+ end
+
+ def test_Integer
+ assert_raise(ArgumentError) {Integer("0x-1")}
+ assert_raise(ArgumentError) {Integer("-0x-1")}
+ assert_raise(ArgumentError) {Integer("0x 123")}
+ assert_raise(ArgumentError) {Integer("0x 123")}
+ assert_raise(ArgumentError) {Integer("0x0x5")}
+ assert_raise(ArgumentError) {Integer("0x0x000000005")}
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(1540841, "0x0x5".to_i(36))
+ }
+ assert_raise(ArgumentError) { Integer("--0") }
+ assert_raise(ArgumentError) { Integer("-+0") }
+ assert_raise(ArgumentError) { Integer("++1") }
+ assert_raise(ArgumentError) { Integer("") }
+ assert_raise(ArgumentError) { Integer("10 x") }
+ assert_raise(ArgumentError) { Integer("1__2") }
+ assert_raise(ArgumentError) { Integer("1z") }
+ assert_raise(ArgumentError) { Integer("46116860184273__87904") }
+ assert_raise(ArgumentError) { Integer("4611686018427387904_") }
+ assert_raise(ArgumentError) { Integer("4611686018427387904 :") }
+ assert_equal(0x4000000000000000, Integer("46_11_686_0184273_87904"))
+ assert_raise(ArgumentError) { Integer("\0") }
+ assert_nothing_raised(ArgumentError, "[ruby-core:13873]") {
+ assert_equal(0, Integer("0 "))
+ }
+ assert_nothing_raised(ArgumentError, "[ruby-core:14139]") {
+ assert_equal(0377, Integer("0_3_7_7"))
+ }
+ assert_raise(ArgumentError, "[ruby-core:14139]") {Integer("0__3_7_7")}
+ assert_equal(1234, Integer(1234))
+ assert_equal(1, Integer(1.234))
+
+ # base argument
+ assert_equal(1234, Integer("1234", 10))
+ assert_equal(668, Integer("1234", 8))
+ assert_equal(4660, Integer("1234", 16))
+ assert_equal(49360, Integer("1234", 36))
+ # decimal, not octal
+ assert_equal(1234, Integer("01234", 10))
+ assert_raise(ArgumentError) { Integer("0x123", 10) }
+ assert_raise(ArgumentError) { Integer(1234, 10) }
+ assert_raise(ArgumentError) { Integer(12.34, 10) }
+ assert_raise(ArgumentError) { Integer(Object.new, 1) }
+
+ assert_raise(ArgumentError) { Integer(1, 1, 1) }
+
+ assert_equal(2 ** 50, Integer(2.0 ** 50))
+ assert_raise(TypeError) { Integer(nil) }
+
+ bug6192 = '[ruby-core:43566]'
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-16be"))}
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-16le"))}
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-32be"))}
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-32le"))}
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("iso-2022-jp"))}
+ end
+
+ def test_int_p
+ assert_not_predicate(1.0, :integer?)
+ assert_predicate(1, :integer?)
+ end
+
+ def test_odd_p_even_p
+ Fixnum.class_eval do
+ alias odd_bak odd?
+ alias even_bak even?
+ remove_method :odd?, :even?
+ end
+
+ assert_predicate(1, :odd?)
+ assert_not_predicate(2, :odd?)
+ assert_not_predicate(1, :even?)
+ assert_predicate(2, :even?)
+
+ ensure
+ Fixnum.class_eval do
+ alias odd? odd_bak
+ alias even? even_bak
+ remove_method :odd_bak, :even_bak
+ end
+ end
+
+ def test_succ
+ assert_equal(2, 1.send(:succ))
+
+ Fixnum.class_eval do
+ alias succ_bak succ
+ remove_method :succ
+ end
+
+ assert_equal(2, 1.succ)
+ assert_equal(4294967297, 4294967296.succ)
+
+ ensure
+ Fixnum.class_eval do
+ alias succ succ_bak
+ remove_method :succ_bak
+ end
+ end
+
+ def test_chr
+ assert_equal("a", "a".ord.chr)
+ assert_raise(RangeError) { (-1).chr }
+ assert_raise(RangeError) { 0x100.chr }
+ end
+
+ def test_upto
+ a = []
+ 1.upto(3) {|x| a << x }
+ assert_equal([1, 2, 3], a)
+
+ a = []
+ 1.upto(0) {|x| a << x }
+ assert_equal([], a)
+
+ y = 2**30 - 1
+ a = []
+ y.upto(y+2) {|x| a << x }
+ assert_equal([y, y+1, y+2], a)
+ end
+
+ def test_downto
+ a = []
+ -1.downto(-3) {|x| a << x }
+ assert_equal([-1, -2, -3], a)
+
+ a = []
+ 1.downto(2) {|x| a << x }
+ assert_equal([], a)
+
+ y = -(2**30)
+ a = []
+ y.downto(y-2) {|x| a << x }
+ assert_equal([y, y-1, y-2], a)
+ end
+
+ def test_times
+ (2**32).times do |i|
+ break if i == 2
+ end
+ end
+
+ def test_round
+ assert_equal(11111, 11111.round)
+ assert_equal(Fixnum, 11111.round.class)
+ assert_equal(11111, 11111.round(0))
+ assert_equal(Fixnum, 11111.round(0).class)
+
+ assert_equal(11111.0, 11111.round(1))
+ assert_equal(Float, 11111.round(1).class)
+ assert_equal(11111.0, 11111.round(2))
+ assert_equal(Float, 11111.round(2).class)
+
+ assert_equal(11110, 11111.round(-1))
+ assert_equal(Fixnum, 11111.round(-1).class)
+ assert_equal(11100, 11111.round(-2))
+ assert_equal(Fixnum, 11111.round(-2).class)
+
+ assert_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.round(-1))
+ assert_equal(Bignum, 1111_1111_1111_1111_1111_1111_1111_1111.round(-1).class)
+ assert_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1))
+ assert_equal(Bignum, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1).class)
+ end
+
+ def test_bitwise_and_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { 3 & obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(3 & 10, 3 & obj)
+ end
+
+ def test_bitwise_or_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { 3 | obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(3 | 10, 3 | obj)
+ end
+
+ def test_bitwise_xor_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { 3 ^ obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(3 ^ 10, 3 ^ obj)
+ end
+
+ def test_bit_length
+ assert_equal(13, (-2**12-1).bit_length)
+ assert_equal(12, (-2**12).bit_length)
+ assert_equal(12, (-2**12+1).bit_length)
+ assert_equal(9, -0x101.bit_length)
+ assert_equal(8, -0x100.bit_length)
+ assert_equal(8, -0xff.bit_length)
+ assert_equal(1, -2.bit_length)
+ assert_equal(0, -1.bit_length)
+ assert_equal(0, 0.bit_length)
+ assert_equal(1, 1.bit_length)
+ assert_equal(8, 0xff.bit_length)
+ assert_equal(9, 0x100.bit_length)
+ assert_equal(9, 0x101.bit_length)
+ assert_equal(12, (2**12-1).bit_length)
+ assert_equal(13, (2**12).bit_length)
+ assert_equal(13, (2**12+1).bit_length)
+
+ assert_equal(10001, (-2**10000-1).bit_length)
+ assert_equal(10000, (-2**10000).bit_length)
+ assert_equal(10000, (-2**10000+1).bit_length)
+ assert_equal(10000, (2**10000-1).bit_length)
+ assert_equal(10001, (2**10000).bit_length)
+ assert_equal(10001, (2**10000+1).bit_length)
+
+ 2.upto(1000) {|i|
+ n = 2**i
+ assert_equal(i+1, (-n-1).bit_length, "(#{-n-1}).bit_length")
+ assert_equal(i, (-n).bit_length, "(#{-n}).bit_length")
+ assert_equal(i, (-n+1).bit_length, "(#{-n+1}).bit_length")
+ assert_equal(i, (n-1).bit_length, "#{n-1}.bit_length")
+ assert_equal(i+1, (n).bit_length, "#{n}.bit_length")
+ assert_equal(i+1, (n+1).bit_length, "#{n+1}.bit_length")
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_integer_comb.rb b/jni/ruby/test/ruby/test_integer_comb.rb
new file mode 100644
index 0000000..e00e758
--- /dev/null
+++ b/jni/ruby/test/ruby/test_integer_comb.rb
@@ -0,0 +1,631 @@
+require 'test/unit'
+
+class TestIntegerComb < Test::Unit::TestCase
+ VS = [
+ -0x1000000000000000000000000000000000000000000000002,
+ -0x1000000000000000000000000000000000000000000000001,
+ -0x1000000000000000000000000000000000000000000000000,
+ -0xffffffffffffffffffffffffffffffffffffffffffffffff,
+ -0x1000000000000000000000002,
+ -0x1000000000000000000000001,
+ -0x1000000000000000000000000,
+ -0xffffffffffffffffffffffff,
+ -0x10000000000000002,
+ -0x10000000000000001,
+ -0x10000000000000000,
+ -0xffffffffffffffff,
+ -0x4000000000000002,
+ -0x4000000000000001,
+ -0x4000000000000000,
+ -0x3fffffffffffffff,
+ -0x100000002,
+ -0x100000001,
+ -0x100000000,
+ -0xffffffff,
+ -0xc717a08d, # 0xc717a08d * 0x524b2245 = 0x4000000000000001
+ -0x80000002,
+ -0x80000001,
+ -0x80000000,
+ -0x7fffffff,
+ -0x524b2245,
+ -0x40000002,
+ -0x40000001,
+ -0x40000000,
+ -0x3fffffff,
+ -0x10002,
+ -0x10001,
+ -0x10000,
+ -0xffff,
+ -0x8101, # 0x8101 * 0x7f01 = 0x40000001
+ -0x8002,
+ -0x8001,
+ -0x8000,
+ -0x7fff,
+ -0x7f01,
+ -65,
+ -64,
+ -63,
+ -62,
+ -33,
+ -32,
+ -31,
+ -30,
+ -3,
+ -2,
+ -1,
+ 0,
+ 1,
+ 2,
+ 3,
+ 30,
+ 31,
+ 32,
+ 33,
+ 62,
+ 63,
+ 64,
+ 65,
+ 0x7f01,
+ 0x7ffe,
+ 0x7fff,
+ 0x8000,
+ 0x8001,
+ 0x8101,
+ 0xfffe,
+ 0xffff,
+ 0x10000,
+ 0x10001,
+ 0x3ffffffe,
+ 0x3fffffff,
+ 0x40000000,
+ 0x40000001,
+ 0x524b2245,
+ 0x7ffffffe,
+ 0x7fffffff,
+ 0x80000000,
+ 0x80000001,
+ 0xc717a08d,
+ 0xfffffffe,
+ 0xffffffff,
+ 0x100000000,
+ 0x100000001,
+ 0x3ffffffffffffffe,
+ 0x3fffffffffffffff,
+ 0x4000000000000000,
+ 0x4000000000000001,
+ 0xfffffffffffffffe,
+ 0xffffffffffffffff,
+ 0x10000000000000000,
+ 0x10000000000000001,
+ 0xffffffffffffffffffffffff,
+ 0x1000000000000000000000000,
+ 0x1000000000000000000000001,
+ 0xffffffffffffffffffffffffffffffffffffffffffffffff,
+ 0x1000000000000000000000000000000000000000000000000,
+ 0x1000000000000000000000000000000000000000000000001
+ ]
+
+ #VS.map! {|v| 0x4000000000000000.coerce(v)[0] }
+ #VS.concat VS.find_all {|v| Fixnum === v }.map {|v| 0x4000000000000000.coerce(v)[0] }
+ #VS.sort! {|a, b| a.abs <=> b.abs }
+
+ min = -1
+ min *= 2 while min.class == Fixnum
+ FIXNUM_MIN = min/2
+ max = 1
+ max *= 2 while (max-1).class == Fixnum
+ FIXNUM_MAX = max/2-1
+
+ def test_fixnum_range
+ assert_instance_of(Bignum, FIXNUM_MIN-1)
+ assert_instance_of(Fixnum, FIXNUM_MIN)
+ assert_instance_of(Fixnum, FIXNUM_MAX)
+ assert_instance_of(Bignum, FIXNUM_MAX+1)
+ end
+
+ def check_class(n)
+ if FIXNUM_MIN <= n && n <= FIXNUM_MAX
+ assert_instance_of(Fixnum, n)
+ else
+ assert_instance_of(Bignum, n)
+ end
+ end
+
+ def test_aref
+ VS.each {|a|
+ 100.times {|i|
+ assert_equal((a >> i).odd? ? 1 : 0, a[i], "(#{a})[#{i}]")
+ }
+ }
+ VS.each {|a|
+ VS.each {|b|
+ c = nil
+ assert_nothing_raised("(#{a})[#{b}]") { c = a[b] }
+ check_class(c)
+ if b < 0
+ assert_equal(0, c, "(#{a})[#{b}]")
+ else
+ assert_equal((a >> b).odd? ? 1 : 0, c, "(#{a})[#{b}]")
+ end
+ }
+ }
+ end
+
+ def test_plus
+ VS.each {|a|
+ VS.each {|b|
+ c = a + b
+ check_class(c)
+ assert_equal(b + a, c, "#{a} + #{b}")
+ assert_equal(a, c - b, "(#{a} + #{b}) - #{b}")
+ assert_equal(a-~b-1, c, "#{a} + #{b}") # Hacker's Delight
+ assert_equal((a^b)+2*(a&b), c, "#{a} + #{b}") # Hacker's Delight
+ assert_equal((a|b)+(a&b), c, "#{a} + #{b}") # Hacker's Delight
+ assert_equal(2*(a|b)-(a^b), c, "#{a} + #{b}") # Hacker's Delight
+ }
+ }
+ end
+
+ def test_minus
+ VS.each {|a|
+ VS.each {|b|
+ c = a - b
+ check_class(c)
+ assert_equal(a, c + b, "(#{a} - #{b}) + #{b}")
+ assert_equal(-b, c - a, "(#{a} - #{b}) - #{a}")
+ assert_equal(a+~b+1, c, "#{a} - #{b}") # Hacker's Delight
+ assert_equal((a^b)-2*(b&~a), c, "#{a} - #{b}") # Hacker's Delight
+ assert_equal((a&~b)-(b&~a), c, "#{a} - #{b}") # Hacker's Delight
+ assert_equal(2*(a&~b)-(a^b), c, "#{a} - #{b}") # Hacker's Delight
+ }
+ }
+ end
+
+ def test_mult
+ VS.each {|a|
+ VS.each {|b|
+ c = a * b
+ check_class(c)
+ assert_equal(b * a, c, "#{a} * #{b}")
+ assert_equal(b.send(:*, a), c, "#{a} * #{b}")
+ assert_equal(b, c / a, "(#{a} * #{b}) / #{a}") if a != 0
+ assert_equal(a.abs * b.abs, (a * b).abs, "(#{a} * #{b}).abs")
+ assert_equal((a-100)*(b-100)+(a-100)*100+(b-100)*100+10000, c, "#{a} * #{b}")
+ assert_equal((a+100)*(b+100)-(a+100)*100-(b+100)*100+10000, c, "#{a} * #{b}")
+ }
+ }
+ end
+
+ def test_divmod
+ VS.each {|a|
+ VS.each {|b|
+ if b == 0
+ assert_raise(ZeroDivisionError) { a.divmod(b) }
+ else
+ q, r = a.divmod(b)
+ check_class(q)
+ check_class(r)
+ assert_equal(a, b*q+r)
+ assert_operator(r.abs, :<, b.abs)
+ if 0 < b
+ assert_operator(r, :>=, 0)
+ assert_operator(r, :<, b)
+ else
+ assert_operator(r, :>, b)
+ assert_operator(r, :<=, 0)
+ end
+ assert_equal(q, a/b)
+ assert_equal(q, a.div(b))
+ assert_equal(r, a%b)
+ assert_equal(r, a.modulo(b))
+ end
+ }
+ }
+ end
+
+ def test_pow
+ small_values = VS.find_all {|v| 0 <= v && v < 1000 }
+ VS.each {|a|
+ small_values.each {|b|
+ c = a ** b
+ check_class(c)
+ d = 1
+ b.times { d *= a }
+ assert_equal(d, c, "(#{a}) ** #{b}")
+ if a != 0
+ d = c
+ b.times { d /= a }
+ assert_equal(1, d, "((#{a}) ** #{b}) / #{a} / ...(#{b} times)...")
+ end
+ }
+ }
+ end
+
+ def test_not
+ VS.each {|a|
+ b = ~a
+ check_class(b)
+ assert_equal(-1 ^ a, b, "~#{a}")
+ assert_equal(-a-1, b, "~#{a}") # Hacker's Delight
+ assert_equal(0, a & b, "#{a} & ~#{a}")
+ assert_equal(-1, a | b, "#{a} | ~#{a}")
+ }
+ end
+
+ def test_or
+ VS.each {|a|
+ VS.each {|b|
+ c = a | b
+ check_class(c)
+ assert_equal(b | a, c, "#{a} | #{b}")
+ assert_equal(a + b - (a&b), c, "#{a} | #{b}")
+ assert_equal((a & ~b) + b, c, "#{a} | #{b}") # Hacker's Delight
+ assert_equal(-1, c | ~a, "(#{a} | #{b}) | ~#{a})")
+ }
+ }
+ end
+
+ def test_and
+ VS.each {|a|
+ VS.each {|b|
+ c = a & b
+ check_class(c)
+ assert_equal(b & a, c, "#{a} & #{b}")
+ assert_equal(a + b - (a|b), c, "#{a} & #{b}")
+ assert_equal((~a | b) - ~a, c, "#{a} & #{b}") # Hacker's Delight
+ assert_equal(0, c & ~a, "(#{a} & #{b}) & ~#{a}")
+ }
+ }
+ end
+
+ def test_xor
+ VS.each {|a|
+ VS.each {|b|
+ c = a ^ b
+ check_class(c)
+ assert_equal(b ^ a, c, "#{a} ^ #{b}")
+ assert_equal((a|b)-(a&b), c, "#{a} ^ #{b}") # Hacker's Delight
+ assert_equal(b, c ^ a, "(#{a} ^ #{b}) ^ #{a}")
+ }
+ }
+ end
+
+ def test_lshift
+ small_values = VS.find_all {|v| v < 8000 }
+ VS.each {|a|
+ small_values.each {|b|
+ c = a << b
+ check_class(c)
+ if 0 <= b
+ assert_equal(a, c >> b, "(#{a} << #{b}) >> #{b}")
+ assert_equal(a * 2**b, c, "#{a} << #{b}")
+ end
+ 0.upto(c.size*8+10) {|nth|
+ assert_equal(a[nth-b], c[nth], "(#{a} << #{b})[#{nth}]")
+ }
+ }
+ }
+ end
+
+ def test_rshift
+ small_values = VS.find_all {|v| -8000 < v }
+ VS.each {|a|
+ small_values.each {|b|
+ c = a >> b
+ check_class(c)
+ if b <= 0
+ assert_equal(a, c << b, "(#{a} >> #{b}) << #{b}")
+ assert_equal(a * 2**(-b), c, "#{a} >> #{b}")
+ end
+ 0.upto(c.size*8+10) {|nth|
+ assert_equal(a[nth+b], c[nth], "(#{a} >> #{b})[#{nth}]")
+ }
+ }
+ }
+ end
+
+ def test_succ
+ VS.each {|a|
+ b = a.succ
+ check_class(b)
+ assert_equal(a+1, b, "(#{a}).succ")
+ assert_equal(a, b.pred, "(#{a}).succ.pred")
+ assert_equal(a, b-1, "(#{a}).succ - 1")
+ }
+ end
+
+ def test_pred
+ VS.each {|a|
+ b = a.pred
+ check_class(b)
+ assert_equal(a-1, b, "(#{a}).pred")
+ assert_equal(a, b.succ, "(#{a}).pred.succ")
+ assert_equal(a, b + 1, "(#{a}).pred + 1")
+ }
+ end
+
+ def test_unary_plus
+ VS.each {|a|
+ b = +a
+ check_class(b)
+ assert_equal(a, b, "+(#{a})")
+ }
+ end
+
+ def test_unary_minus
+ VS.each {|a|
+ b = -a
+ check_class(b)
+ assert_equal(0-a, b, "-(#{a})")
+ assert_equal(~a+1, b, "-(#{a})")
+ assert_equal(0, a+b, "#{a}+(-(#{a}))")
+ }
+ end
+
+ def test_cmp
+ VS.each_with_index {|a, i|
+ VS.each_with_index {|b, j|
+ assert_equal(i <=> j, a <=> b, "#{a} <=> #{b}")
+ assert_equal(i < j, a < b, "#{a} < #{b}")
+ assert_equal(i <= j, a <= b, "#{a} <= #{b}")
+ assert_equal(i > j, a > b, "#{a} > #{b}")
+ assert_equal(i >= j, a >= b, "#{a} >= #{b}")
+ }
+ }
+ end
+
+ def test_eq
+ VS.each_with_index {|a, i|
+ VS.each_with_index {|b, j|
+ c = a == b
+ assert_equal(b == a, c, "#{a} == #{b}")
+ assert_equal(i == j, c, "#{a} == #{b}")
+ }
+ }
+ end
+
+ def test_abs
+ VS.each {|a|
+ b = a.abs
+ check_class(b)
+ if a < 0
+ assert_equal(-a, b, "(#{a}).abs")
+ else
+ assert_equal(a, b, "(#{a}).abs")
+ end
+ }
+ end
+
+ def test_ceil
+ VS.each {|a|
+ b = a.ceil
+ check_class(b)
+ assert_equal(a, b, "(#{a}).ceil")
+ }
+ end
+
+ def test_floor
+ VS.each {|a|
+ b = a.floor
+ check_class(b)
+ assert_equal(a, b, "(#{a}).floor")
+ }
+ end
+
+ def test_round
+ VS.each {|a|
+ b = a.round
+ check_class(b)
+ assert_equal(a, b, "(#{a}).round")
+ }
+ end
+
+ def test_truncate
+ VS.each {|a|
+ b = a.truncate
+ check_class(b)
+ assert_equal(a, b, "(#{a}).truncate")
+ }
+ end
+
+ def test_remainder
+ VS.each {|a|
+ VS.each {|b|
+ if b == 0
+ assert_raise(ZeroDivisionError) { a.divmod(b) }
+ else
+ r = a.remainder(b)
+ check_class(r)
+ if a < 0
+ assert_operator(-b.abs, :<, r, "#{a}.remainder(#{b})")
+ assert_operator(0, :>=, r, "#{a}.remainder(#{b})")
+ elsif 0 < a
+ assert_operator(0, :<=, r, "#{a}.remainder(#{b})")
+ assert_operator(b.abs, :>, r, "#{a}.remainder(#{b})")
+ else
+ assert_equal(0, r, "#{a}.remainder(#{b})")
+ end
+ end
+ }
+ }
+ end
+
+ def test_zero_nonzero
+ VS.each {|a|
+ z = a.zero?
+ n = a.nonzero?
+ if a == 0
+ assert_equal(true, z, "(#{a}).zero?")
+ assert_equal(nil, n, "(#{a}).nonzero?")
+ else
+ assert_equal(false, z, "(#{a}).zero?")
+ assert_equal(a, n, "(#{a}).nonzero?")
+ check_class(n)
+ end
+ assert(z ^ n, "(#{a}).zero? ^ (#{a}).nonzero?")
+ }
+ end
+
+ def test_even_odd
+ VS.each {|a|
+ e = a.even?
+ o = a.odd?
+ assert_equal((a % 2) == 0, e, "(#{a}).even?")
+ assert_equal((a % 2) == 1, o, "(#{a}).odd")
+ assert_equal((a & 1) == 0, e, "(#{a}).even?")
+ assert_equal((a & 1) == 1, o, "(#{a}).odd")
+ assert(e ^ o, "(#{a}).even? ^ (#{a}).odd?")
+ }
+ end
+
+ def test_to_s
+ 2.upto(36) {|radix|
+ VS.each {|a|
+ s = a.to_s(radix)
+ b = s.to_i(radix)
+ assert_equal(a, b, "(#{a}).to_s(#{radix}).to_i(#{radix})")
+ }
+ }
+ end
+
+ def test_printf_x
+ VS.reverse_each {|a|
+ s = sprintf("%x", a)
+ if /\A\.\./ =~ s
+ b = -($'.tr('0123456789abcdef', 'fedcba9876543210').to_i(16) + 1)
+ else
+ b = s.to_i(16)
+ end
+ assert_equal(a, b, "sprintf('%x', #{a}) = #{s.inspect}")
+ }
+ end
+
+ def test_printf_x_sign
+ VS.reverse_each {|a|
+ s = sprintf("%+x", a)
+ b = s.to_i(16)
+ assert_equal(a, b, "sprintf('%+x', #{a}) = #{s.inspect}")
+ s = sprintf("% x", a)
+ b = s.to_i(16)
+ assert_equal(a, b, "sprintf('% x', #{a}) = #{s.inspect}")
+ }
+ end
+
+ def test_printf_o
+ VS.reverse_each {|a|
+ s = sprintf("%o", a)
+ if /\A\.\./ =~ s
+ b = -($'.tr('01234567', '76543210').to_i(8) + 1)
+ else
+ b = s.to_i(8)
+ end
+ assert_equal(a, b, "sprintf('%o', #{a}) = #{s.inspect}")
+ }
+ end
+
+ def test_printf_o_sign
+ VS.reverse_each {|a|
+ s = sprintf("%+o", a)
+ b = s.to_i(8)
+ assert_equal(a, b, "sprintf('%+o', #{a}) = #{s.inspect}")
+ s = sprintf("% o", a)
+ b = s.to_i(8)
+ assert_equal(a, b, "sprintf('% o', #{a}) = #{s.inspect}")
+ }
+ end
+
+ def test_printf_b
+ VS.reverse_each {|a|
+ s = sprintf("%b", a)
+ if /\A\.\./ =~ s
+ b = -($'.tr('01', '10').to_i(2) + 1)
+ else
+ b = s.to_i(2)
+ end
+ assert_equal(a, b, "sprintf('%b', #{a}) = #{s.inspect}")
+ }
+ end
+
+ def test_printf_b_sign
+ VS.reverse_each {|a|
+ s = sprintf("%+b", a)
+ b = s.to_i(2)
+ assert_equal(a, b, "sprintf('%+b', #{a}) = #{s.inspect}")
+ s = sprintf("% b", a)
+ b = s.to_i(2)
+ assert_equal(a, b, "sprintf('% b', #{a}) = #{s.inspect}")
+ }
+ end
+
+ def test_printf_diu
+ VS.reverse_each {|a|
+ s = sprintf("%d", a)
+ b = s.to_i
+ assert_equal(a, b, "sprintf('%d', #{a}) = #{s.inspect}")
+ s = sprintf("%i", a)
+ b = s.to_i
+ assert_equal(a, b, "sprintf('%i', #{a}) = #{s.inspect}")
+ s = sprintf("%u", a)
+ b = s.to_i
+ assert_equal(a, b, "sprintf('%u', #{a}) = #{s.inspect}")
+ }
+ end
+
+ def test_marshal
+ VS.reverse_each {|a|
+ s = Marshal.dump(a)
+ b = Marshal.load(s)
+ assert_equal(a, b, "Marshal.load(Marshal.dump(#{a}))")
+ }
+ end
+
+ def test_pack
+ %w[c C s S s! S! i I i! I! l L l! L! q Q n N v V].each {|template|
+ size = [0].pack(template).size
+ mask = (1 << (size * 8)) - 1
+ if /[A-Znv]/ =~ template
+ min = 0
+ max = (1 << (size * 8))-1
+ else
+ min = -(1 << (size * 8 - 1))
+ max = (1 << (size * 8 - 1)) - 1
+ end
+ VS.reverse_each {|a|
+ s = [a].pack(template)
+ b = s.unpack(template)[0]
+ if min <= a && a <= max
+ assert_equal(a, b, "[#{a}].pack(#{template.dump}).unpack(#{template.dump})[0]")
+ end
+ assert_operator(min, :<=, b)
+ assert_operator(b, :<=, max)
+ assert_equal(a & mask, b & mask, "[#{a}].pack(#{template.dump}).unpack(#{template.dump})[0] & #{mask}")
+ }
+ }
+ end
+
+ def test_pack_ber
+ template = "w"
+ VS.reverse_each {|a|
+ if a < 0
+ assert_raise(ArgumentError) { [a].pack(template) }
+ else
+ s = [a].pack(template)
+ b = s.unpack(template)[0]
+ assert_equal(a, b, "[#{a}].pack(#{template.dump}).unpack(#{template.dump})")
+ end
+ }
+ end
+
+ def test_pack_utf8
+ template = "U"
+ VS.reverse_each {|a|
+ if a < 0 || 0x7fffffff < a
+ assert_raise(RangeError) { [a].pack(template) }
+ else
+ s = [a].pack(template)
+ b = s.unpack(template)[0]
+ assert_equal(a, b, "[#{a}].pack(#{template.dump}).unpack(#{template.dump})")
+ end
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_io.rb b/jni/ruby/test/ruby/test_io.rb
new file mode 100644
index 0000000..d0ca5e6
--- /dev/null
+++ b/jni/ruby/test/ruby/test_io.rb
@@ -0,0 +1,3171 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'tmpdir'
+require "fcntl"
+require 'io/nonblock'
+require 'socket'
+require 'stringio'
+require 'timeout'
+require 'tempfile'
+require 'weakref'
+
+class TestIO < Test::Unit::TestCase
+ module Feature
+ def have_close_on_exec?
+ $stdin.close_on_exec?
+ true
+ rescue NotImplementedError
+ false
+ end
+
+ def have_nonblock?
+ IO.method_defined?("nonblock=")
+ end
+ end
+
+ include Feature
+ extend Feature
+
+ def pipe(wp, rp)
+ re, we = nil, nil
+ r, w = IO.pipe
+ rt = Thread.new do
+ begin
+ rp.call(r)
+ rescue Exception
+ r.close
+ re = $!
+ end
+ end
+ wt = Thread.new do
+ begin
+ wp.call(w)
+ rescue Exception
+ w.close
+ we = $!
+ end
+ end
+ flunk("timeout") unless wt.join(10) && rt.join(10)
+ ensure
+ w.close unless !w || w.closed?
+ r.close unless !r || r.closed?
+ (wt.kill; wt.join) if wt
+ (rt.kill; rt.join) if rt
+ raise we if we
+ raise re if re
+ end
+
+ def with_pipe
+ r, w = IO.pipe
+ begin
+ yield r, w
+ ensure
+ r.close unless r.closed?
+ w.close unless w.closed?
+ end
+ end
+
+ def with_read_pipe(content)
+ pipe(proc do |w|
+ w << content
+ w.close
+ end, proc do |r|
+ yield r
+ end)
+ end
+
+ def mkcdtmpdir
+ Dir.mktmpdir {|d|
+ Dir.chdir(d) {
+ yield
+ }
+ }
+ end
+
+ def trapping_usr1
+ @usr1_rcvd = 0
+ trap(:USR1) { @usr1_rcvd += 1 }
+ yield
+ ensure
+ trap(:USR1, "DEFAULT")
+ end
+
+ def test_pipe
+ r, w = IO.pipe
+ assert_instance_of(IO, r)
+ assert_instance_of(IO, w)
+ [
+ Thread.start{
+ w.print "abc"
+ w.close
+ },
+ Thread.start{
+ assert_equal("abc", r.read)
+ r.close
+ }
+ ].each{|thr| thr.join}
+ end
+
+ def test_pipe_block
+ x = nil
+ ret = IO.pipe {|r, w|
+ x = [r,w]
+ assert_instance_of(IO, r)
+ assert_instance_of(IO, w)
+ [
+ Thread.start do
+ w.print "abc"
+ w.close
+ end,
+ Thread.start do
+ assert_equal("abc", r.read)
+ end
+ ].each{|thr| thr.join}
+ assert_not_predicate(r, :closed?)
+ assert_predicate(w, :closed?)
+ :foooo
+ }
+ assert_equal(:foooo, ret)
+ assert_predicate(x[0], :closed?)
+ assert_predicate(x[1], :closed?)
+ end
+
+ def test_pipe_block_close
+ 4.times {|i|
+ x = nil
+ IO.pipe {|r, w|
+ x = [r,w]
+ r.close if (i&1) == 0
+ w.close if (i&2) == 0
+ }
+ assert_predicate(x[0], :closed?)
+ assert_predicate(x[1], :closed?)
+ }
+ end
+
+ def test_gets_rs
+ # default_rs
+ pipe(proc do |w|
+ w.print "aaa\nbbb\n"
+ w.close
+ end, proc do |r|
+ assert_equal "aaa\n", r.gets
+ assert_equal "bbb\n", r.gets
+ assert_nil r.gets
+ r.close
+ end)
+
+ # nil
+ pipe(proc do |w|
+ w.print "a\n\nb\n\n"
+ w.close
+ end, proc do |r|
+ assert_equal "a\n\nb\n\n", r.gets(nil)
+ assert_nil r.gets("")
+ r.close
+ end)
+
+ # "\377"
+ pipe(proc do |w|
+ w.print "\377xyz"
+ w.close
+ end, proc do |r|
+ r.binmode
+ assert_equal("\377", r.gets("\377"), "[ruby-dev:24460]")
+ r.close
+ end)
+
+ # ""
+ pipe(proc do |w|
+ w.print "a\n\nb\n\n"
+ w.close
+ end, proc do |r|
+ assert_equal "a\n\n", r.gets(""), "[ruby-core:03771]"
+ assert_equal "b\n\n", r.gets("")
+ assert_nil r.gets("")
+ r.close
+ end)
+ end
+
+ def test_gets_limit_extra_arg
+ pipe(proc do |w|
+ w << "0123456789\n0123456789"
+ w.close
+ end, proc do |r|
+ assert_equal("0123456789\n0", r.gets(nil, 12))
+ assert_raise(TypeError) { r.gets(3,nil) }
+ end)
+ end
+
+ # This test cause SEGV.
+ def test_ungetc
+ pipe(proc do |w|
+ w.close
+ end, proc do |r|
+ s = "a" * 1000
+ assert_raise(IOError, "[ruby-dev:31650]") { 200.times { r.ungetc s } }
+ end)
+ end
+
+ def test_ungetbyte
+ make_tempfile {|t|
+ t.open
+ t.binmode
+ t.ungetbyte(0x41)
+ assert_equal(-1, t.pos)
+ assert_equal(0x41, t.getbyte)
+ t.rewind
+ assert_equal(0, t.pos)
+ t.ungetbyte("qux")
+ assert_equal(-3, t.pos)
+ assert_equal("quxfoo\n", t.gets)
+ assert_equal(4, t.pos)
+ t.set_encoding("utf-8")
+ t.ungetbyte(0x89)
+ t.ungetbyte(0x8e)
+ t.ungetbyte("\xe7")
+ t.ungetbyte("\xe7\xb4\x85")
+ assert_equal(-2, t.pos)
+ assert_equal("\u7d05\u7389bar\n", t.gets)
+ }
+ end
+
+ def test_each_byte
+ pipe(proc do |w|
+ w << "abc def"
+ w.close
+ end, proc do |r|
+ r.each_byte {|byte| break if byte == 32 }
+ assert_equal("def", r.read, "[ruby-dev:31659]")
+ end)
+ end
+
+ def test_each_byte_with_seek
+ make_tempfile {|t|
+ bug5119 = '[ruby-core:38609]'
+ i = 0
+ open(t.path) do |f|
+ f.each_byte {i = f.pos}
+ end
+ assert_equal(12, i, bug5119)
+ }
+ end
+
+ def test_each_codepoint
+ make_tempfile {|t|
+ bug2959 = '[ruby-core:28650]'
+ a = ""
+ File.open(t, 'rt') {|f|
+ f.each_codepoint {|c| a << c}
+ }
+ assert_equal("foo\nbar\nbaz\n", a, bug2959)
+ }
+ end
+
+ def test_codepoints
+ make_tempfile {|t|
+ bug2959 = '[ruby-core:28650]'
+ a = ""
+ File.open(t, 'rt') {|f|
+ assert_warn(/deprecated/) {
+ f.codepoints {|c| a << c}
+ }
+ }
+ assert_equal("foo\nbar\nbaz\n", a, bug2959)
+ }
+ end
+
+ def test_rubydev33072
+ t = make_tempfile
+ path = t.path
+ t.close!
+ assert_raise(Errno::ENOENT, "[ruby-dev:33072]") do
+ File.read(path, nil, nil, {})
+ end
+ end
+
+ def with_srccontent(content = "baz")
+ src = "src"
+ mkcdtmpdir {
+ File.open(src, "w") {|f| f << content }
+ yield src, content
+ }
+ end
+
+ def test_copy_stream_small
+ with_srccontent("foobar") {|src, content|
+ ret = IO.copy_stream(src, "dst")
+ assert_equal(content.bytesize, ret)
+ assert_equal(content, File.read("dst"))
+ }
+ end
+
+ def test_copy_stream_smaller
+ with_srccontent {|src, content|
+
+ # overwrite by smaller file.
+ dst = "dst"
+ File.open(dst, "w") {|f| f << "foobar"}
+
+ ret = IO.copy_stream(src, dst)
+ assert_equal(content.bytesize, ret)
+ assert_equal(content, File.read(dst))
+
+ ret = IO.copy_stream(src, dst, 2)
+ assert_equal(2, ret)
+ assert_equal(content[0,2], File.read(dst))
+
+ ret = IO.copy_stream(src, dst, 0)
+ assert_equal(0, ret)
+ assert_equal("", File.read(dst))
+
+ ret = IO.copy_stream(src, dst, nil, 1)
+ assert_equal(content.bytesize-1, ret)
+ assert_equal(content[1..-1], File.read(dst))
+ }
+ end
+
+ def test_copy_stream_noent
+ with_srccontent {|src, content|
+ assert_raise(Errno::ENOENT) {
+ IO.copy_stream("nodir/foo", "dst")
+ }
+
+ assert_raise(Errno::ENOENT) {
+ IO.copy_stream(src, "nodir/bar")
+ }
+ }
+ end
+
+ def test_copy_stream_pipe
+ with_srccontent {|src, content|
+ pipe(proc do |w|
+ ret = IO.copy_stream(src, w)
+ assert_equal(content.bytesize, ret)
+ w.close
+ end, proc do |r|
+ assert_equal(content, r.read)
+ end)
+ }
+ end
+
+ def test_copy_stream_write_pipe
+ with_srccontent {|src, content|
+ with_pipe {|r, w|
+ w.close
+ assert_raise(IOError) { IO.copy_stream(src, w) }
+ }
+ }
+ end
+
+ def with_pipecontent
+ mkcdtmpdir {
+ yield "abc"
+ }
+ end
+
+ def test_copy_stream_pipe_to_file
+ with_pipecontent {|pipe_content|
+ dst = "dst"
+ with_read_pipe(pipe_content) {|r|
+ ret = IO.copy_stream(r, dst)
+ assert_equal(pipe_content.bytesize, ret)
+ assert_equal(pipe_content, File.read(dst))
+ }
+ }
+ end
+
+ def test_copy_stream_read_pipe
+ with_pipecontent {|pipe_content|
+ with_read_pipe(pipe_content) {|r1|
+ assert_equal("a", r1.getc)
+ pipe(proc do |w2|
+ w2.sync = false
+ w2 << "def"
+ ret = IO.copy_stream(r1, w2)
+ assert_equal(2, ret)
+ w2.close
+ end, proc do |r2|
+ assert_equal("defbc", r2.read)
+ end)
+ }
+
+ with_read_pipe(pipe_content) {|r1|
+ assert_equal("a", r1.getc)
+ pipe(proc do |w2|
+ w2.sync = false
+ w2 << "def"
+ ret = IO.copy_stream(r1, w2, 1)
+ assert_equal(1, ret)
+ w2.close
+ end, proc do |r2|
+ assert_equal("defb", r2.read)
+ end)
+ }
+
+ with_read_pipe(pipe_content) {|r1|
+ assert_equal("a", r1.getc)
+ pipe(proc do |w2|
+ ret = IO.copy_stream(r1, w2)
+ assert_equal(2, ret)
+ w2.close
+ end, proc do |r2|
+ assert_equal("bc", r2.read)
+ end)
+ }
+
+ with_read_pipe(pipe_content) {|r1|
+ assert_equal("a", r1.getc)
+ pipe(proc do |w2|
+ ret = IO.copy_stream(r1, w2, 1)
+ assert_equal(1, ret)
+ w2.close
+ end, proc do |r2|
+ assert_equal("b", r2.read)
+ end)
+ }
+
+ with_read_pipe(pipe_content) {|r1|
+ assert_equal("a", r1.getc)
+ pipe(proc do |w2|
+ ret = IO.copy_stream(r1, w2, 0)
+ assert_equal(0, ret)
+ w2.close
+ end, proc do |r2|
+ assert_equal("", r2.read)
+ end)
+ }
+
+ pipe(proc do |w1|
+ w1 << "abc"
+ w1 << "def"
+ w1.close
+ end, proc do |r1|
+ assert_equal("a", r1.getc)
+ pipe(proc do |w2|
+ ret = IO.copy_stream(r1, w2)
+ assert_equal(5, ret)
+ w2.close
+ end, proc do |r2|
+ assert_equal("bcdef", r2.read)
+ end)
+ end)
+ }
+ end
+
+ def test_copy_stream_file_to_pipe
+ with_srccontent {|src, content|
+ pipe(proc do |w|
+ ret = IO.copy_stream(src, w, 1, 1)
+ assert_equal(1, ret)
+ w.close
+ end, proc do |r|
+ assert_equal(content[1,1], r.read)
+ end)
+ }
+ end
+
+ if have_nonblock?
+ def test_copy_stream_pipe_nonblock
+ mkcdtmpdir {
+ with_read_pipe("abc") {|r1|
+ assert_equal("a", r1.getc)
+ with_pipe {|r2, w2|
+ begin
+ w2.nonblock = true
+ rescue Errno::EBADF
+ skip "nonblocking IO for pipe is not implemented"
+ break
+ end
+ s = w2.syswrite("a" * 100000)
+ t = Thread.new { sleep 0.1; r2.read }
+ ret = IO.copy_stream(r1, w2)
+ w2.close
+ assert_equal(2, ret)
+ assert_equal("a" * s + "bc", t.value)
+ }
+ }
+ }
+ end
+ end
+
+ def with_bigcontent
+ yield "abc" * 123456
+ end
+
+ def with_bigsrc
+ mkcdtmpdir {
+ with_bigcontent {|bigcontent|
+ bigsrc = "bigsrc"
+ File.open("bigsrc", "w") {|f| f << bigcontent }
+ yield bigsrc, bigcontent
+ }
+ }
+ end
+
+ def test_copy_stream_bigcontent
+ with_bigsrc {|bigsrc, bigcontent|
+ ret = IO.copy_stream(bigsrc, "bigdst")
+ assert_equal(bigcontent.bytesize, ret)
+ assert_equal(bigcontent, File.read("bigdst"))
+ }
+ end
+
+ def test_copy_stream_bigcontent_chop
+ with_bigsrc {|bigsrc, bigcontent|
+ ret = IO.copy_stream(bigsrc, "bigdst", nil, 100)
+ assert_equal(bigcontent.bytesize-100, ret)
+ assert_equal(bigcontent[100..-1], File.read("bigdst"))
+ }
+ end
+
+ def test_copy_stream_bigcontent_mid
+ with_bigsrc {|bigsrc, bigcontent|
+ ret = IO.copy_stream(bigsrc, "bigdst", 30000, 100)
+ assert_equal(30000, ret)
+ assert_equal(bigcontent[100, 30000], File.read("bigdst"))
+ }
+ end
+
+ def test_copy_stream_bigcontent_fpos
+ with_bigsrc {|bigsrc, bigcontent|
+ File.open(bigsrc) {|f|
+ begin
+ assert_equal(0, f.pos)
+ ret = IO.copy_stream(f, "bigdst", nil, 10)
+ assert_equal(bigcontent.bytesize-10, ret)
+ assert_equal(bigcontent[10..-1], File.read("bigdst"))
+ assert_equal(0, f.pos)
+ ret = IO.copy_stream(f, "bigdst", 40, 30)
+ assert_equal(40, ret)
+ assert_equal(bigcontent[30, 40], File.read("bigdst"))
+ assert_equal(0, f.pos)
+ rescue NotImplementedError
+ #skip "pread(2) is not implemtented."
+ end
+ }
+ }
+ end
+
+ def test_copy_stream_closed_pipe
+ with_srccontent {|src,|
+ with_pipe {|r, w|
+ w.close
+ assert_raise(IOError) { IO.copy_stream(src, w) }
+ }
+ }
+ end
+
+ def with_megacontent
+ yield "abc" * 1234567
+ end
+
+ def with_megasrc
+ mkcdtmpdir {
+ with_megacontent {|megacontent|
+ megasrc = "megasrc"
+ File.open(megasrc, "w") {|f| f << megacontent }
+ yield megasrc, megacontent
+ }
+ }
+ end
+
+ if have_nonblock?
+ def test_copy_stream_megacontent_nonblock
+ with_megacontent {|megacontent|
+ with_pipe {|r1, w1|
+ with_pipe {|r2, w2|
+ begin
+ r1.nonblock = true
+ w2.nonblock = true
+ rescue Errno::EBADF
+ skip "nonblocking IO for pipe is not implemented"
+ end
+ t1 = Thread.new { w1 << megacontent; w1.close }
+ t2 = Thread.new { r2.read }
+ t3 = Thread.new {
+ ret = IO.copy_stream(r1, w2)
+ assert_equal(megacontent.bytesize, ret)
+ w2.close
+ }
+ _, t2_value, _ = assert_join_threads([t1, t2, t3])
+ assert_equal(megacontent, t2_value)
+ }
+ }
+ }
+ end
+ end
+
+ def test_copy_stream_megacontent_pipe_to_file
+ with_megasrc {|megasrc, megacontent|
+ with_pipe {|r1, w1|
+ with_pipe {|r2, w2|
+ t1 = Thread.new { w1 << megacontent; w1.close }
+ t2 = Thread.new { r2.read }
+ t3 = Thread.new {
+ ret = IO.copy_stream(r1, w2)
+ assert_equal(megacontent.bytesize, ret)
+ w2.close
+ }
+ _, t2_value, _ = assert_join_threads([t1, t2, t3])
+ assert_equal(megacontent, t2_value)
+ }
+ }
+ }
+ end
+
+ def test_copy_stream_megacontent_file_to_pipe
+ with_megasrc {|megasrc, megacontent|
+ with_pipe {|r, w|
+ t1 = Thread.new { r.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(megasrc, w)
+ assert_equal(megacontent.bytesize, ret)
+ w.close
+ }
+ t1_value, _ = assert_join_threads([t1, t2])
+ assert_equal(megacontent, t1_value)
+ }
+ }
+ end
+
+ def test_copy_stream_rbuf
+ mkcdtmpdir {
+ begin
+ pipe(proc do |w|
+ File.open("foo", "w") {|f| f << "abcd" }
+ File.open("foo") {|f|
+ f.read(1)
+ assert_equal(3, IO.copy_stream(f, w, 10, 1))
+ }
+ w.close
+ end, proc do |r|
+ assert_equal("bcd", r.read)
+ end)
+ rescue NotImplementedError
+ skip "pread(2) is not implemtented."
+ end
+ }
+ end
+
+ def with_socketpair
+ s1, s2 = UNIXSocket.pair
+ begin
+ yield s1, s2
+ ensure
+ s1.close unless s1.closed?
+ s2.close unless s2.closed?
+ end
+ end
+
+ def test_copy_stream_socket1
+ with_srccontent("foobar") {|src, content|
+ with_socketpair {|s1, s2|
+ ret = IO.copy_stream(src, s1)
+ assert_equal(content.bytesize, ret)
+ s1.close
+ assert_equal(content, s2.read)
+ }
+ }
+ end if defined? UNIXSocket
+
+ def test_copy_stream_socket2
+ with_bigsrc {|bigsrc, bigcontent|
+ with_socketpair {|s1, s2|
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(bigsrc, s1)
+ assert_equal(bigcontent.bytesize, ret)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
+ assert_equal(bigcontent, result)
+ }
+ }
+ end if defined? UNIXSocket
+
+ def test_copy_stream_socket3
+ with_bigsrc {|bigsrc, bigcontent|
+ with_socketpair {|s1, s2|
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(bigsrc, s1, 10000)
+ assert_equal(10000, ret)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
+ assert_equal(bigcontent[0,10000], result)
+ }
+ }
+ end if defined? UNIXSocket
+
+ def test_copy_stream_socket4
+ with_bigsrc {|bigsrc, bigcontent|
+ File.open(bigsrc) {|f|
+ assert_equal(0, f.pos)
+ with_socketpair {|s1, s2|
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(f, s1, nil, 100)
+ assert_equal(bigcontent.bytesize-100, ret)
+ assert_equal(0, f.pos)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
+ assert_equal(bigcontent[100..-1], result)
+ }
+ }
+ }
+ end if defined? UNIXSocket
+
+ def test_copy_stream_socket5
+ with_bigsrc {|bigsrc, bigcontent|
+ File.open(bigsrc) {|f|
+ assert_equal(bigcontent[0,100], f.read(100))
+ assert_equal(100, f.pos)
+ with_socketpair {|s1, s2|
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(f, s1)
+ assert_equal(bigcontent.bytesize-100, ret)
+ assert_equal(bigcontent.length, f.pos)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
+ assert_equal(bigcontent[100..-1], result)
+ }
+ }
+ }
+ end if defined? UNIXSocket
+
+ def test_copy_stream_socket6
+ mkcdtmpdir {
+ megacontent = "abc" * 1234567
+ File.open("megasrc", "w") {|f| f << megacontent }
+
+ with_socketpair {|s1, s2|
+ begin
+ s1.nonblock = true
+ rescue Errno::EBADF
+ skip "nonblocking IO for pipe is not implemented"
+ end
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream("megasrc", s1)
+ assert_equal(megacontent.bytesize, ret)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
+ assert_equal(megacontent, result)
+ }
+ }
+ end if defined? UNIXSocket
+
+ def test_copy_stream_socket7
+ GC.start
+ mkcdtmpdir {
+ megacontent = "abc" * 1234567
+ File.open("megasrc", "w") {|f| f << megacontent }
+
+ with_socketpair {|s1, s2|
+ begin
+ s1.nonblock = true
+ rescue Errno::EBADF
+ skip "nonblocking IO for pipe is not implemented"
+ end
+ trapping_usr1 do
+ nr = 30
+ begin
+ pid = fork do
+ s1.close
+ IO.select([s2])
+ Process.kill(:USR1, Process.ppid)
+ s2.read
+ end
+ s2.close
+ nr.times do
+ assert_equal megacontent.bytesize, IO.copy_stream("megasrc", s1)
+ end
+ assert_equal(1, @usr1_rcvd)
+ ensure
+ s1.close
+ _, status = Process.waitpid2(pid) if pid
+ end
+ assert_predicate(status, :success?)
+ end
+ }
+ }
+ end if defined? UNIXSocket and IO.method_defined?("nonblock=")
+
+ def test_copy_stream_strio
+ src = StringIO.new("abcd")
+ dst = StringIO.new
+ ret = IO.copy_stream(src, dst)
+ assert_equal(4, ret)
+ assert_equal("abcd", dst.string)
+ assert_equal(4, src.pos)
+ end
+
+ def test_copy_stream_strio_len
+ src = StringIO.new("abcd")
+ dst = StringIO.new
+ ret = IO.copy_stream(src, dst, 3)
+ assert_equal(3, ret)
+ assert_equal("abc", dst.string)
+ assert_equal(3, src.pos)
+ end
+
+ def test_copy_stream_strio_off
+ src = StringIO.new("abcd")
+ with_pipe {|r, w|
+ assert_raise(ArgumentError) {
+ IO.copy_stream(src, w, 3, 1)
+ }
+ }
+ end
+
+ def test_copy_stream_fname_to_strio
+ mkcdtmpdir {
+ File.open("foo", "w") {|f| f << "abcd" }
+ src = "foo"
+ dst = StringIO.new
+ ret = IO.copy_stream(src, dst, 3)
+ assert_equal(3, ret)
+ assert_equal("abc", dst.string)
+ }
+ end
+
+ def test_copy_stream_strio_to_fname
+ mkcdtmpdir {
+ # StringIO to filename
+ src = StringIO.new("abcd")
+ ret = IO.copy_stream(src, "fooo", 3)
+ assert_equal(3, ret)
+ assert_equal("abc", File.read("fooo"))
+ assert_equal(3, src.pos)
+ }
+ end
+
+ def test_copy_stream_io_to_strio
+ mkcdtmpdir {
+ # IO to StringIO
+ File.open("bar", "w") {|f| f << "abcd" }
+ File.open("bar") {|src|
+ dst = StringIO.new
+ ret = IO.copy_stream(src, dst, 3)
+ assert_equal(3, ret)
+ assert_equal("abc", dst.string)
+ assert_equal(3, src.pos)
+ }
+ }
+ end
+
+ def test_copy_stream_strio_to_io
+ mkcdtmpdir {
+ # StringIO to IO
+ src = StringIO.new("abcd")
+ ret = File.open("baz", "w") {|dst|
+ IO.copy_stream(src, dst, 3)
+ }
+ assert_equal(3, ret)
+ assert_equal("abc", File.read("baz"))
+ assert_equal(3, src.pos)
+ }
+ end
+
+ def test_copy_stream_write_in_binmode
+ bug8767 = '[ruby-core:56518] [Bug #8767]'
+ mkcdtmpdir {
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ # StringIO to object with to_path
+ bytes = "\xDE\xAD\xBE\xEF".force_encoding(Encoding::ASCII_8BIT)
+ src = StringIO.new(bytes)
+ dst = Object.new
+ def dst.to_path
+ "qux"
+ end
+ assert_nothing_raised(bug8767) {
+ IO.copy_stream(src, dst)
+ }
+ assert_equal(bytes, File.binread("qux"), bug8767)
+ assert_equal(4, src.pos, bug8767)
+ end
+ }
+ end
+
+ def test_copy_stream_read_in_binmode
+ bug8767 = '[ruby-core:56518] [Bug #8767]'
+ mkcdtmpdir {
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ # StringIO to object with to_path
+ bytes = "\xDE\xAD\xBE\xEF".force_encoding(Encoding::ASCII_8BIT)
+ File.binwrite("qux", bytes)
+ dst = StringIO.new
+ src = Object.new
+ def src.to_path
+ "qux"
+ end
+ assert_nothing_raised(bug8767) {
+ IO.copy_stream(src, dst)
+ }
+ assert_equal(bytes, dst.string.b, bug8767)
+ assert_equal(4, dst.pos, bug8767)
+ end
+ }
+ end
+
+ class Rot13IO
+ def initialize(io)
+ @io = io
+ end
+
+ def readpartial(*args)
+ ret = @io.readpartial(*args)
+ ret.tr!('a-zA-Z', 'n-za-mN-ZA-M')
+ ret
+ end
+
+ def write(str)
+ @io.write(str.tr('a-zA-Z', 'n-za-mN-ZA-M'))
+ end
+
+ def to_io
+ @io
+ end
+ end
+
+ def test_copy_stream_io_to_rot13
+ mkcdtmpdir {
+ File.open("bar", "w") {|f| f << "vex" }
+ File.open("bar") {|src|
+ File.open("baz", "w") {|dst0|
+ dst = Rot13IO.new(dst0)
+ ret = IO.copy_stream(src, dst, 3)
+ assert_equal(3, ret)
+ }
+ assert_equal("irk", File.read("baz"))
+ }
+ }
+ end
+
+ def test_copy_stream_rot13_to_io
+ mkcdtmpdir {
+ File.open("bar", "w") {|f| f << "flap" }
+ File.open("bar") {|src0|
+ src = Rot13IO.new(src0)
+ File.open("baz", "w") {|dst|
+ ret = IO.copy_stream(src, dst, 4)
+ assert_equal(4, ret)
+ }
+ }
+ assert_equal("sync", File.read("baz"))
+ }
+ end
+
+ def test_copy_stream_rot13_to_rot13
+ mkcdtmpdir {
+ File.open("bar", "w") {|f| f << "bin" }
+ File.open("bar") {|src0|
+ src = Rot13IO.new(src0)
+ File.open("baz", "w") {|dst0|
+ dst = Rot13IO.new(dst0)
+ ret = IO.copy_stream(src, dst, 3)
+ assert_equal(3, ret)
+ }
+ }
+ assert_equal("bin", File.read("baz"))
+ }
+ end
+
+ def test_copy_stream_strio_flush
+ with_pipe {|r, w|
+ w.sync = false
+ w.write "zz"
+ src = StringIO.new("abcd")
+ IO.copy_stream(src, w)
+ t1 = Thread.new {
+ w.close
+ }
+ t2 = Thread.new { r.read }
+ _, result = assert_join_threads([t1, t2])
+ assert_equal("zzabcd", result)
+ }
+ end
+
+ def test_copy_stream_strio_rbuf
+ pipe(proc do |w|
+ w << "abcd"
+ w.close
+ end, proc do |r|
+ assert_equal("a", r.read(1))
+ sio = StringIO.new
+ IO.copy_stream(r, sio)
+ assert_equal("bcd", sio.string)
+ end)
+ end
+
+ def test_copy_stream_src_wbuf
+ mkcdtmpdir {
+ pipe(proc do |w|
+ File.open("foe", "w+") {|f|
+ f.write "abcd\n"
+ f.rewind
+ f.write "xy"
+ IO.copy_stream(f, w)
+ }
+ assert_equal("xycd\n", File.read("foe"))
+ w.close
+ end, proc do |r|
+ assert_equal("cd\n", r.read)
+ r.close
+ end)
+ }
+ end
+
+ class Bug5237
+ attr_reader :count
+ def initialize
+ @count = 0
+ end
+
+ def read(bytes, buffer)
+ @count += 1
+ buffer.replace "this is a test"
+ nil
+ end
+ end
+
+ def test_copy_stream_broken_src_read_eof
+ src = Bug5237.new
+ dst = StringIO.new
+ assert_equal 0, src.count
+ th = Thread.new { IO.copy_stream(src, dst) }
+ flunk("timeout") unless th.join(10)
+ assert_equal 1, src.count
+ end
+
+ def test_copy_stream_dst_rbuf
+ mkcdtmpdir {
+ pipe(proc do |w|
+ w << "xyz"
+ w.close
+ end, proc do |r|
+ File.open("fom", "w+b") {|f|
+ f.write "abcd\n"
+ f.rewind
+ assert_equal("abc", f.read(3))
+ f.ungetc "c"
+ IO.copy_stream(r, f)
+ }
+ assert_equal("abxyz", File.read("fom"))
+ end)
+ }
+ end
+
+ def ruby(*args)
+ args = ['-e', '$>.write($<.read)'] if args.empty?
+ ruby = EnvUtil.rubybin
+ f = IO.popen([ruby] + args, 'r+')
+ pid = f.pid
+ yield(f)
+ ensure
+ f.close unless !f || f.closed?
+ begin
+ Process.wait(pid)
+ rescue Errno::ECHILD, Errno::ESRCH
+ end
+ end
+
+ def test_try_convert
+ assert_equal(STDOUT, IO.try_convert(STDOUT))
+ assert_equal(nil, IO.try_convert("STDOUT"))
+ end
+
+ def test_ungetc2
+ f = false
+ pipe(proc do |w|
+ Thread.pass until f
+ w.write("1" * 10000)
+ w.close
+ end, proc do |r|
+ r.ungetc("0" * 10000)
+ f = true
+ assert_equal("0" * 10000 + "1" * 10000, r.read)
+ end)
+ end
+
+ def test_write_non_writable
+ with_pipe do |r, w|
+ assert_raise(IOError) do
+ r.write "foobarbaz"
+ end
+ end
+ end
+
+ def test_dup
+ ruby do |f|
+ begin
+ f2 = f.dup
+ f.puts "foo"
+ f2.puts "bar"
+ f.close_write
+ f2.close_write
+ assert_equal("foo\nbar\n", f.read)
+ assert_equal("", f2.read)
+ ensure
+ f2.close
+ end
+ end
+ end
+
+ def test_dup_many
+ ruby('-e', <<-'End') {|f|
+ ok = 0
+ a = []
+ begin
+ loop {a << IO.pipe}
+ rescue Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM
+ ok += 1
+ end
+ print "no" if ok != 1
+ begin
+ loop {a << [a[-1][0].dup, a[-1][1].dup]}
+ rescue Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM
+ ok += 1
+ end
+ print "no" if ok != 2
+ print "ok"
+ End
+ assert_equal("ok", f.read)
+ }
+ end
+
+ def test_inspect
+ with_pipe do |r, w|
+ assert_match(/^#<IO:fd \d+>$/, r.inspect)
+ r.freeze
+ assert_match(/^#<IO:fd \d+>$/, r.inspect)
+ end
+ end
+
+ def test_readpartial
+ pipe(proc do |w|
+ w.write "foobarbaz"
+ w.close
+ end, proc do |r|
+ assert_raise(ArgumentError) { r.readpartial(-1) }
+ assert_equal("fooba", r.readpartial(5))
+ r.readpartial(5, s = "")
+ assert_equal("rbaz", s)
+ end)
+ end
+
+ def test_readpartial_lock
+ with_pipe do |r, w|
+ s = ""
+ t = Thread.new { r.readpartial(5, s) }
+ Thread.pass until t.stop?
+ assert_raise(RuntimeError) { s.clear }
+ w.write "foobarbaz"
+ w.close
+ assert_equal("fooba", t.value)
+ end
+ end
+
+ def test_readpartial_pos
+ mkcdtmpdir {
+ open("foo", "w") {|f| f << "abc" }
+ open("foo") {|f|
+ f.seek(0)
+ assert_equal("ab", f.readpartial(2))
+ assert_equal(2, f.pos)
+ }
+ }
+ end
+
+ def test_readpartial_with_not_empty_buffer
+ pipe(proc do |w|
+ w.write "foob"
+ w.close
+ end, proc do |r|
+ r.readpartial(5, s = "01234567")
+ assert_equal("foob", s)
+ end)
+ end
+
+ def test_readpartial_buffer_error
+ with_pipe do |r, w|
+ s = ""
+ t = Thread.new { r.readpartial(5, s) }
+ Thread.pass until t.stop?
+ t.kill
+ t.value
+ assert_equal("", s)
+ end
+ end
+
+ def test_read
+ pipe(proc do |w|
+ w.write "foobarbaz"
+ w.close
+ end, proc do |r|
+ assert_raise(ArgumentError) { r.read(-1) }
+ assert_equal("fooba", r.read(5))
+ r.read(nil, s = "")
+ assert_equal("rbaz", s)
+ end)
+ end
+
+ def test_read_lock
+ with_pipe do |r, w|
+ s = ""
+ t = Thread.new { r.read(5, s) }
+ Thread.pass until t.stop?
+ assert_raise(RuntimeError) { s.clear }
+ w.write "foobarbaz"
+ w.close
+ assert_equal("fooba", t.value)
+ end
+ end
+
+ def test_read_with_not_empty_buffer
+ pipe(proc do |w|
+ w.write "foob"
+ w.close
+ end, proc do |r|
+ r.read(nil, s = "01234567")
+ assert_equal("foob", s)
+ end)
+ end
+
+ def test_read_buffer_error
+ with_pipe do |r, w|
+ s = ""
+ t = Thread.new { r.read(5, s) }
+ Thread.pass until t.stop?
+ t.kill
+ t.value
+ assert_equal("", s)
+ end
+ with_pipe do |r, w|
+ s = "xxx"
+ t = Thread.new {r.read(2, s)}
+ Thread.pass until t.stop?
+ t.kill
+ t.value
+ assert_equal("xxx", s)
+ end
+ end
+
+ def test_write_nonblock
+ pipe(proc do |w|
+ w.write_nonblock(1)
+ w.close
+ end, proc do |r|
+ assert_equal("1", r.read)
+ end)
+ end
+
+ def test_read_nonblock_with_not_empty_buffer
+ with_pipe {|r, w|
+ w.write "foob"
+ w.close
+ r.read_nonblock(5, s = "01234567")
+ assert_equal("foob", s)
+ }
+ end
+
+ def test_write_nonblock_simple_no_exceptions
+ pipe(proc do |w|
+ w.write_nonblock('1', exception: false)
+ w.close
+ end, proc do |r|
+ assert_equal("1", r.read)
+ end)
+ end
+
+ def test_read_nonblock_error
+ return if !have_nonblock?
+ with_pipe {|r, w|
+ begin
+ r.read_nonblock 4096
+ rescue Errno::EWOULDBLOCK
+ assert_kind_of(IO::WaitReadable, $!)
+ end
+ }
+
+ with_pipe {|r, w|
+ begin
+ r.read_nonblock 4096, ""
+ rescue Errno::EWOULDBLOCK
+ assert_kind_of(IO::WaitReadable, $!)
+ end
+ }
+ end
+
+ def test_read_nonblock_no_exceptions
+ return if !have_nonblock?
+ with_pipe {|r, w|
+ assert_equal :wait_readable, r.read_nonblock(4096, exception: false)
+ w.puts "HI!"
+ assert_equal "HI!\n", r.read_nonblock(4096, exception: false)
+ w.close
+ assert_equal nil, r.read_nonblock(4096, exception: false)
+ }
+ end
+
+ def test_read_nonblock_with_buffer_no_exceptions
+ return if !have_nonblock?
+ with_pipe {|r, w|
+ assert_equal :wait_readable, r.read_nonblock(4096, "", exception: false)
+ w.puts "HI!"
+ buf = "buf"
+ value = r.read_nonblock(4096, buf, exception: false)
+ assert_equal value, "HI!\n"
+ assert_same(buf, value)
+ w.close
+ assert_equal nil, r.read_nonblock(4096, "", exception: false)
+ }
+ end
+
+ def test_write_nonblock_error
+ return if !have_nonblock?
+ with_pipe {|r, w|
+ begin
+ loop {
+ w.write_nonblock "a"*100000
+ }
+ rescue Errno::EWOULDBLOCK
+ assert_kind_of(IO::WaitWritable, $!)
+ end
+ }
+ end
+
+ def test_write_nonblock_no_exceptions
+ return if !have_nonblock?
+ with_pipe {|r, w|
+ loop {
+ ret = w.write_nonblock("a"*100000, exception: false)
+ if ret.is_a?(Symbol)
+ assert_equal :wait_writable, ret
+ break
+ end
+ }
+ }
+ end
+
+ def test_gets
+ pipe(proc do |w|
+ w.write "foobarbaz"
+ w.close
+ end, proc do |r|
+ assert_equal("", r.gets(0))
+ assert_equal("foobarbaz", r.gets(9))
+ end)
+ end
+
+ def test_close_read
+ ruby do |f|
+ f.close_read
+ f.write "foobarbaz"
+ assert_raise(IOError) { f.read }
+ end
+ end
+
+ def test_close_read_pipe
+ with_pipe do |r, w|
+ r.close_read
+ assert_raise(Errno::EPIPE) { w.write "foobarbaz" }
+ end
+ end
+
+ def test_write_epipe_nosync
+ assert_separately([], <<-"end;")
+ r, w = IO.pipe
+ r.close
+ w.sync = false
+ assert_raise(Errno::EPIPE) {
+ loop { w.write "a" }
+ }
+ end;
+ end
+
+ def test_close_read_non_readable
+ with_pipe do |r, w|
+ assert_raise(IOError) do
+ w.close_read
+ end
+ end
+ end
+
+ def test_close_write
+ ruby do |f|
+ f.write "foobarbaz"
+ f.close_write
+ assert_equal("foobarbaz", f.read)
+ end
+ end
+
+ def test_close_write_non_readable
+ with_pipe do |r, w|
+ assert_raise(IOError) do
+ r.close_write
+ end
+ end
+ end
+
+ def test_close_read_write_separately
+ bug = '[ruby-list:49598]'
+ (1..10).each do |i|
+ assert_nothing_raised(IOError, "#{bug} trying ##{i}") do
+ IO.popen(EnvUtil.rubybin, "r+") {|f|
+ th = Thread.new {f.close_write}
+ f.close_read
+ th.join
+ }
+ end
+ end
+ end
+
+ def test_pid
+ IO.pipe {|r, w|
+ assert_equal(nil, r.pid)
+ assert_equal(nil, w.pid)
+ }
+
+ begin
+ pipe = IO.popen(EnvUtil.rubybin, "r+")
+ pid1 = pipe.pid
+ pipe.puts "p $$"
+ pipe.close_write
+ pid2 = pipe.read.chomp.to_i
+ assert_equal(pid2, pid1)
+ assert_equal(pid2, pipe.pid)
+ ensure
+ pipe.close
+ end
+ assert_raise(IOError) { pipe.pid }
+ end
+
+ def test_pid_after_close_read
+ pid1 = pid2 = nil
+ IO.popen("exit ;", "r+") do |io|
+ pid1 = io.pid
+ io.close_read
+ pid2 = io.pid
+ end
+ assert_not_nil(pid1)
+ assert_equal(pid1, pid2)
+ end
+
+ def make_tempfile
+ t = Tempfile.new("test_io")
+ t.binmode
+ t.puts "foo"
+ t.puts "bar"
+ t.puts "baz"
+ t.close
+ if block_given?
+ begin
+ yield t
+ ensure
+ t.close(true)
+ end
+ else
+ t
+ end
+ end
+
+ def test_set_lineno
+ make_tempfile {|t|
+ ruby("-e", <<-SRC, t.path) do |f|
+ open(ARGV[0]) do |f|
+ p $.
+ f.gets; p $.
+ f.gets; p $.
+ f.lineno = 1000; p $.
+ f.gets; p $.
+ f.gets; p $.
+ f.rewind; p $.
+ f.gets; p $.
+ f.gets; p $.
+ f.gets; p $.
+ f.gets; p $.
+ end
+ SRC
+ assert_equal("0,1,2,2,1001,1001,1001,1,2,3,3", f.read.chomp.gsub("\n", ","))
+ end
+
+ pipe(proc do |w|
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ r.gets; assert_equal(1, $.)
+ r.gets; assert_equal(2, $.)
+ r.lineno = 1000; assert_equal(2, $.)
+ r.gets; assert_equal(1001, $.)
+ r.gets; assert_equal(1001, $.)
+ end)
+ }
+ end
+
+ def test_readline
+ pipe(proc do |w|
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ r.readline; assert_equal(1, $.)
+ r.readline; assert_equal(2, $.)
+ r.lineno = 1000; assert_equal(2, $.)
+ r.readline; assert_equal(1001, $.)
+ assert_raise(EOFError) { r.readline }
+ end)
+ end
+
+ def test_each_char
+ pipe(proc do |w|
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ a = []
+ r.each_char {|c| a << c }
+ assert_equal(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"], a)
+ end)
+ end
+
+ def test_lines
+ verbose, $VERBOSE = $VERBOSE, nil
+ pipe(proc do |w|
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ e = nil
+ assert_warn(/deprecated/) {
+ e = r.lines
+ }
+ assert_equal("foo\n", e.next)
+ assert_equal("bar\n", e.next)
+ assert_equal("baz\n", e.next)
+ assert_raise(StopIteration) { e.next }
+ end)
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def test_bytes
+ verbose, $VERBOSE = $VERBOSE, nil
+ pipe(proc do |w|
+ w.binmode
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ e = nil
+ assert_warn(/deprecated/) {
+ e = r.bytes
+ }
+ (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
+ assert_equal(c.ord, e.next)
+ end
+ assert_raise(StopIteration) { e.next }
+ end)
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def test_chars
+ verbose, $VERBOSE = $VERBOSE, nil
+ pipe(proc do |w|
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ e = nil
+ assert_warn(/deprecated/) {
+ e = r.chars
+ }
+ (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
+ assert_equal(c, e.next)
+ end
+ assert_raise(StopIteration) { e.next }
+ end)
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def test_readbyte
+ pipe(proc do |w|
+ w.binmode
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ r.binmode
+ (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
+ assert_equal(c.ord, r.readbyte)
+ end
+ assert_raise(EOFError) { r.readbyte }
+ end)
+ end
+
+ def test_readchar
+ pipe(proc do |w|
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
+ assert_equal(c, r.readchar)
+ end
+ assert_raise(EOFError) { r.readchar }
+ end)
+ end
+
+ def test_close_on_exec
+ skip "IO\#close_on_exec is not implemented." unless have_close_on_exec?
+ ruby do |f|
+ assert_equal(true, f.close_on_exec?)
+ f.close_on_exec = false
+ assert_equal(false, f.close_on_exec?)
+ f.close_on_exec = true
+ assert_equal(true, f.close_on_exec?)
+ f.close_on_exec = false
+ assert_equal(false, f.close_on_exec?)
+ end
+
+ with_pipe do |r, w|
+ assert_equal(true, r.close_on_exec?)
+ r.close_on_exec = false
+ assert_equal(false, r.close_on_exec?)
+ r.close_on_exec = true
+ assert_equal(true, r.close_on_exec?)
+ r.close_on_exec = false
+ assert_equal(false, r.close_on_exec?)
+
+ assert_equal(true, w.close_on_exec?)
+ w.close_on_exec = false
+ assert_equal(false, w.close_on_exec?)
+ w.close_on_exec = true
+ assert_equal(true, w.close_on_exec?)
+ w.close_on_exec = false
+ assert_equal(false, w.close_on_exec?)
+ end
+ end
+
+ def test_pos
+ make_tempfile {|t|
+
+ open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
+ f.write "Hello"
+ assert_equal(5, f.pos)
+ end
+ open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
+ f.sync = true
+ f.read
+ f.write "Hello"
+ assert_equal(5, f.pos)
+ end
+ }
+ end
+
+ def test_pos_with_getc
+ _bug6179 = '[ruby-core:43497]'
+ make_tempfile {|t|
+ ["", "t", "b"].each do |mode|
+ open(t.path, "w#{mode}") do |f|
+ f.write "0123456789\n"
+ end
+
+ open(t.path, "r#{mode}") do |f|
+ assert_equal 0, f.pos, "mode=r#{mode}"
+ assert_equal '0', f.getc, "mode=r#{mode}"
+ assert_equal 1, f.pos, "mode=r#{mode}"
+ assert_equal '1', f.getc, "mode=r#{mode}"
+ assert_equal 2, f.pos, "mode=r#{mode}"
+ assert_equal '2', f.getc, "mode=r#{mode}"
+ assert_equal 3, f.pos, "mode=r#{mode}"
+ assert_equal '3', f.getc, "mode=r#{mode}"
+ assert_equal 4, f.pos, "mode=r#{mode}"
+ assert_equal '4', f.getc, "mode=r#{mode}"
+ end
+ end
+ }
+ end
+
+ def can_seek_data(f)
+ if /linux/ =~ RUBY_PLATFORM
+ require "-test-/file"
+ # lseek(2)
+ case Bug::File::Fs.fsname(f.path)
+ when "btrfs"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,1]) >= 0
+ when "ocfs"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,2]) >= 0
+ when "xfs"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,5]) >= 0
+ when "ext4"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,8]) >= 0
+ when "tmpfs"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,8]) >= 0
+ end
+ end
+ false
+ end
+
+ def test_seek
+ make_tempfile {|t|
+ open(t.path) { |f|
+ f.seek(9)
+ assert_equal("az\n", f.read)
+ }
+
+ open(t.path) { |f|
+ f.seek(9, IO::SEEK_SET)
+ assert_equal("az\n", f.read)
+ }
+
+ open(t.path) { |f|
+ f.seek(-4, IO::SEEK_END)
+ assert_equal("baz\n", f.read)
+ }
+
+ open(t.path) { |f|
+ assert_equal("foo\n", f.gets)
+ f.seek(2, IO::SEEK_CUR)
+ assert_equal("r\nbaz\n", f.read)
+ }
+
+ if defined?(IO::SEEK_DATA)
+ open(t.path) { |f|
+ break unless can_seek_data(f)
+ assert_equal("foo\n", f.gets)
+ f.seek(0, IO::SEEK_DATA)
+ assert_equal("foo\nbar\nbaz\n", f.read)
+ }
+ open(t.path, 'r+') { |f|
+ break unless can_seek_data(f)
+ f.seek(100*1024, IO::SEEK_SET)
+ f.print("zot\n")
+ f.seek(50*1024, IO::SEEK_DATA)
+ assert_operator(f.pos, :>=, 50*1024)
+ assert_match(/\A\0*zot\n\z/, f.read)
+ }
+ end
+
+ if defined?(IO::SEEK_HOLE)
+ open(t.path) { |f|
+ break unless can_seek_data(f)
+ assert_equal("foo\n", f.gets)
+ f.seek(0, IO::SEEK_HOLE)
+ assert_operator(f.pos, :>, 20)
+ f.seek(100*1024, IO::SEEK_HOLE)
+ assert_equal("", f.read)
+ }
+ end
+ }
+ end
+
+ def test_seek_symwhence
+ make_tempfile {|t|
+ open(t.path) { |f|
+ f.seek(9, :SET)
+ assert_equal("az\n", f.read)
+ }
+
+ open(t.path) { |f|
+ f.seek(-4, :END)
+ assert_equal("baz\n", f.read)
+ }
+
+ open(t.path) { |f|
+ assert_equal("foo\n", f.gets)
+ f.seek(2, :CUR)
+ assert_equal("r\nbaz\n", f.read)
+ }
+
+ if defined?(IO::SEEK_DATA)
+ open(t.path) { |f|
+ break unless can_seek_data(f)
+ assert_equal("foo\n", f.gets)
+ f.seek(0, :DATA)
+ assert_equal("foo\nbar\nbaz\n", f.read)
+ }
+ open(t.path, 'r+') { |f|
+ break unless can_seek_data(f)
+ f.seek(100*1024, :SET)
+ f.print("zot\n")
+ f.seek(50*1024, :DATA)
+ assert_operator(f.pos, :>=, 50*1024)
+ assert_match(/\A\0*zot\n\z/, f.read)
+ }
+ end
+
+ if defined?(IO::SEEK_HOLE)
+ open(t.path) { |f|
+ break unless can_seek_data(f)
+ assert_equal("foo\n", f.gets)
+ f.seek(0, :HOLE)
+ assert_operator(f.pos, :>, 20)
+ f.seek(100*1024, :HOLE)
+ assert_equal("", f.read)
+ }
+ end
+ }
+ end
+
+ def test_sysseek
+ make_tempfile {|t|
+ open(t.path) do |f|
+ f.sysseek(-4, IO::SEEK_END)
+ assert_equal("baz\n", f.read)
+ end
+
+ open(t.path) do |f|
+ a = [f.getc, f.getc, f.getc]
+ a.reverse_each {|c| f.ungetc c }
+ assert_raise(IOError) { f.sysseek(1) }
+ end
+ }
+ end
+
+ def test_syswrite
+ make_tempfile {|t|
+ open(t.path, "w") do |f|
+ o = Object.new
+ def o.to_s; "FOO\n"; end
+ f.syswrite(o)
+ end
+ assert_equal("FOO\n", File.read(t.path))
+ }
+ end
+
+ def test_sysread
+ make_tempfile {|t|
+ open(t.path) do |f|
+ a = [f.getc, f.getc, f.getc]
+ a.reverse_each {|c| f.ungetc c }
+ assert_raise(IOError) { f.sysread(1) }
+ end
+ }
+ end
+
+ def test_sysread_with_not_empty_buffer
+ pipe(proc do |w|
+ w.write "foob"
+ w.close
+ end, proc do |r|
+ r.sysread( 5, s = "01234567" )
+ assert_equal( "foob", s )
+ end)
+ end
+
+ def test_flag
+ make_tempfile {|t|
+ assert_raise(ArgumentError) do
+ open(t.path, "z") { }
+ end
+
+ assert_raise(ArgumentError) do
+ open(t.path, "rr") { }
+ end
+ }
+ end
+
+ def test_sysopen
+ make_tempfile {|t|
+ fd = IO.sysopen(t.path)
+ assert_kind_of(Integer, fd)
+ f = IO.for_fd(fd)
+ assert_equal("foo\nbar\nbaz\n", f.read)
+ f.close
+
+ fd = IO.sysopen(t.path, "w", 0666)
+ assert_kind_of(Integer, fd)
+ if defined?(Fcntl::F_GETFL)
+ f = IO.for_fd(fd)
+ else
+ f = IO.for_fd(fd, 0666)
+ end
+ f.write("FOO\n")
+ f.close
+
+ fd = IO.sysopen(t.path, "r")
+ assert_kind_of(Integer, fd)
+ f = IO.for_fd(fd)
+ assert_equal("FOO\n", f.read)
+ f.close
+ }
+ end
+
+ def try_fdopen(fd, autoclose = true, level = 50)
+ if level > 0
+ begin
+ 1.times {return try_fdopen(fd, autoclose, level - 1)}
+ ensure
+ GC.start
+ end
+ else
+ WeakRef.new(IO.for_fd(fd, autoclose: autoclose))
+ end
+ end
+
+ def test_autoclose
+ feature2250 = '[ruby-core:26222]'
+ pre = 'ft2250'
+
+ Dir.mktmpdir {|d|
+ t = open("#{d}/#{pre}", "w")
+ f = IO.for_fd(t.fileno)
+ assert_equal(true, f.autoclose?)
+ f.autoclose = false
+ assert_equal(false, f.autoclose?)
+ f.close
+ assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
+
+ t = open("#{d}/#{pre}", "w")
+ f = IO.for_fd(t.fileno, autoclose: false)
+ assert_equal(false, f.autoclose?)
+ f.autoclose = true
+ assert_equal(true, f.autoclose?)
+ f.close
+ assert_raise(Errno::EBADF, feature2250) {t.close}
+ }
+ end
+
+ def test_autoclose_true_closed_by_finalizer
+ feature2250 = '[ruby-core:26222]'
+ pre = 'ft2250'
+ t = Tempfile.new(pre)
+ w = try_fdopen(t.fileno)
+ begin
+ w.close
+ begin
+ t.close
+ rescue Errno::EBADF
+ end
+ skip "expect IO object was GC'ed but not recycled yet"
+ rescue WeakRef::RefError
+ assert_raise(Errno::EBADF, feature2250) {t.close}
+ end
+ ensure
+ t.close!
+ end
+
+ def test_autoclose_false_closed_by_finalizer
+ feature2250 = '[ruby-core:26222]'
+ pre = 'ft2250'
+ t = Tempfile.new(pre)
+ w = try_fdopen(t.fileno, false)
+ begin
+ w.close
+ t.close
+ skip "expect IO object was GC'ed but not recycled yet"
+ rescue WeakRef::RefError
+ assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
+ end
+ ensure
+ t.close!
+ end
+
+ def test_open_redirect
+ o = Object.new
+ def o.to_open; self; end
+ assert_equal(o, open(o))
+ o2 = nil
+ open(o) do |f|
+ o2 = f
+ end
+ assert_equal(o, o2)
+ end
+
+ def test_open_pipe
+ open("|" + EnvUtil.rubybin, "r+") do |f|
+ f.puts "puts 'foo'"
+ f.close_write
+ assert_equal("foo\n", f.read)
+ end
+ end
+
+ def test_reopen
+ make_tempfile {|t|
+ open(__FILE__) do |f|
+ f.gets
+ assert_nothing_raised {
+ f.reopen(t.path)
+ assert_equal("foo\n", f.gets)
+ }
+ end
+
+ open(__FILE__) do |f|
+ f.gets
+ f2 = open(t.path)
+ begin
+ f2.gets
+ assert_nothing_raised {
+ f.reopen(f2)
+ assert_equal("bar\n", f.gets, '[ruby-core:24240]')
+ }
+ ensure
+ f2.close
+ end
+ end
+
+ open(__FILE__) do |f|
+ f2 = open(t.path)
+ begin
+ f.reopen(f2)
+ assert_equal("foo\n", f.gets)
+ assert_equal("bar\n", f.gets)
+ f.reopen(f2)
+ assert_equal("baz\n", f.gets, '[ruby-dev:39479]')
+ ensure
+ f2.close
+ end
+ end
+ }
+ end
+
+ def test_reopen_inherit
+ mkcdtmpdir {
+ system(EnvUtil.rubybin, '-e', <<"End")
+ f = open("out", "w")
+ STDOUT.reopen(f)
+ STDERR.reopen(f)
+ system(#{EnvUtil.rubybin.dump}, '-e', 'STDOUT.print "out"')
+ system(#{EnvUtil.rubybin.dump}, '-e', 'STDERR.print "err"')
+End
+ assert_equal("outerr", File.read("out"))
+ }
+ end
+
+ def test_reopen_stdio
+ mkcdtmpdir {
+ fname = 'bug11319'
+ File.write(fname, 'hello')
+ system(EnvUtil.rubybin, '-e', "STDOUT.reopen('#{fname}', 'w+')")
+ assert_equal('', File.read(fname))
+ }
+ end
+
+ def test_reopen_mode
+ feature7067 = '[ruby-core:47694]'
+ make_tempfile {|t|
+ open(__FILE__) do |f|
+ assert_nothing_raised {
+ f.reopen(t.path, "r")
+ assert_equal("foo\n", f.gets)
+ }
+ end
+
+ open(__FILE__) do |f|
+ assert_nothing_raised(feature7067) {
+ f.reopen(t.path, File::RDONLY)
+ assert_equal("foo\n", f.gets)
+ }
+ end
+ }
+ end
+
+ def test_reopen_opt
+ feature7103 = '[ruby-core:47806]'
+ make_tempfile {|t|
+ open(__FILE__) do |f|
+ assert_nothing_raised(feature7103) {
+ f.reopen(t.path, "r", binmode: true)
+ }
+ assert_equal("foo\n", f.gets)
+ end
+
+ open(__FILE__) do |f|
+ assert_nothing_raised(feature7103) {
+ f.reopen(t.path, autoclose: false)
+ }
+ assert_equal("foo\n", f.gets)
+ end
+ }
+ end
+
+ def make_tempfile_for_encoding
+ t = make_tempfile
+ open(t.path, "rb+:utf-8") {|f| f.puts "\u7d05\u7389bar\n"}
+ if block_given?
+ yield t
+ else
+ t
+ end
+ ensure
+ t.close(true) if t and block_given?
+ end
+
+ def test_reopen_encoding
+ make_tempfile_for_encoding {|t|
+ open(__FILE__) {|f|
+ f.reopen(t.path, "r:utf-8")
+ s = f.gets
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_equal("\u7d05\u7389bar\n", s)
+ }
+
+ open(__FILE__) {|f|
+ f.reopen(t.path, "r:UTF-8:EUC-JP")
+ s = f.gets
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s)
+ }
+ }
+ end
+
+ def test_reopen_opt_encoding
+ feature7103 = '[ruby-core:47806]'
+ make_tempfile_for_encoding {|t|
+ open(__FILE__) {|f|
+ assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "ASCII-8BIT")}
+ s = f.gets
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xe7\xb4\x85\xe7\x8e\x89bar\n", s)
+ }
+
+ open(__FILE__) {|f|
+ assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "UTF-8:EUC-JP")}
+ s = f.gets
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s)
+ }
+ }
+ end
+
+ def test_foreach
+ a = []
+ IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+
+ make_tempfile {|t|
+ a = []
+ IO.foreach(t.path) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+
+ a = []
+ IO.foreach(t.path, {:mode => "r" }) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+
+ a = []
+ IO.foreach(t.path, {:open_args => [] }) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+
+ a = []
+ IO.foreach(t.path, {:open_args => ["r"] }) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+
+ a = []
+ IO.foreach(t.path, "b") {|x| a << x }
+ assert_equal(["foo\nb", "ar\nb", "az\n"], a)
+
+ a = []
+ IO.foreach(t.path, 3) {|x| a << x }
+ assert_equal(["foo", "\n", "bar", "\n", "baz", "\n"], a)
+
+ a = []
+ IO.foreach(t.path, "b", 3) {|x| a << x }
+ assert_equal(["foo", "\nb", "ar\n", "b", "az\n"], a)
+
+ bug = '[ruby-dev:31525]'
+ assert_raise(ArgumentError, bug) {IO.foreach}
+
+ a = nil
+ assert_nothing_raised(ArgumentError, bug) {a = IO.foreach(t.path).to_a}
+ assert_equal(["foo\n", "bar\n", "baz\n"], a, bug)
+
+ bug6054 = '[ruby-dev:45267]'
+ assert_raise_with_message(IOError, /not opened for reading/, bug6054) do
+ IO.foreach(t.path, mode:"w").next
+ end
+ }
+ end
+
+ def test_s_readlines
+ make_tempfile {|t|
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
+ assert_equal(["foo\nb", "ar\nb", "az\n"], IO.readlines(t.path, "b"))
+ assert_equal(["fo", "o\n", "ba", "r\n", "ba", "z\n"], IO.readlines(t.path, 2))
+ assert_equal(["fo", "o\n", "b", "ar", "\nb", "az", "\n"], IO.readlines(t.path, "b", 2))
+ }
+ end
+
+ def test_printf
+ pipe(proc do |w|
+ printf(w, "foo %s baz\n", "bar")
+ w.close_write
+ end, proc do |r|
+ assert_equal("foo bar baz\n", r.read)
+ end)
+ end
+
+ def test_print
+ make_tempfile {|t|
+ assert_in_out_err(["-", t.path],
+ "print while $<.gets",
+ %w(foo bar baz), [])
+ }
+ end
+
+ def test_print_separators
+ $, = ':'
+ $\ = "\n"
+ pipe(proc do |w|
+ w.print('a')
+ w.print('a','b','c')
+ w.close
+ end, proc do |r|
+ assert_equal("a\n", r.gets)
+ assert_equal("a:b:c\n", r.gets)
+ assert_nil r.gets
+ r.close
+ end)
+ ensure
+ $, = nil
+ $\ = nil
+ end
+
+ def test_putc
+ pipe(proc do |w|
+ w.putc "A"
+ w.putc "BC"
+ w.putc 68
+ w.close_write
+ end, proc do |r|
+ assert_equal("ABD", r.read)
+ end)
+
+ assert_in_out_err([], "putc 65", %w(A), [])
+ end
+
+ def test_puts_recursive_array
+ a = ["foo"]
+ a << a
+ pipe(proc do |w|
+ w.puts a
+ w.close
+ end, proc do |r|
+ assert_equal("foo\n[...]\n", r.read)
+ end)
+ end
+
+ def test_display
+ pipe(proc do |w|
+ "foo".display(w)
+ w.close
+ end, proc do |r|
+ assert_equal("foo", r.read)
+ end)
+
+ assert_in_out_err([], "'foo'.display", %w(foo), [])
+ end
+
+ def test_set_stdout
+ assert_raise(TypeError) { $> = Object.new }
+
+ assert_in_out_err([], "$> = $stderr\nputs 'foo'", [], %w(foo))
+
+ assert_separately(%w[-Eutf-8], <<-"end;") # do
+ alias $\u{6a19 6e96 51fa 529b} $stdout
+ x = eval("class X\u{307b 3052}; self; end".encode("euc-jp"))
+ assert_raise_with_message(TypeError, /\\$\u{6a19 6e96 51fa 529b} must.*, X\u{307b 3052} given/) do
+ $\u{6a19 6e96 51fa 529b} = x.new
+ end
+ end;
+ end
+
+ def test_initialize
+ return unless defined?(Fcntl::F_GETFL)
+
+ make_tempfile {|t|
+
+ fd = IO.sysopen(t.path, "w")
+ assert_kind_of(Integer, fd)
+ %w[r r+ w+ a+].each do |mode|
+ assert_raise(Errno::EINVAL, "#{mode} [ruby-dev:38571]") {IO.new(fd, mode)}
+ end
+ f = IO.new(fd, "w")
+ f.write("FOO\n")
+ f.close
+
+ assert_equal("FOO\n", File.read(t.path))
+ }
+ end
+
+ def test_reinitialize
+ make_tempfile {|t|
+ f = open(t.path)
+ begin
+ assert_raise(RuntimeError) do
+ f.instance_eval { initialize }
+ end
+ ensure
+ f.close
+ end
+ }
+ end
+
+ def test_new_with_block
+ assert_in_out_err([], "r, w = IO.pipe; r.autoclose=false; IO.new(r.fileno) {}.close", [], /^.+$/)
+ n = "IO\u{5165 51fa 529b}"
+ c = eval("class #{n} < IO; self; end")
+ IO.pipe do |r, w|
+ assert_warning(/#{n}/) {
+ r.autoclose=false
+ io = c.new(r.fileno) {}
+ io.close
+ }
+ end
+ end
+
+ def test_readline2
+ assert_in_out_err(["-e", <<-SRC], "foo\nbar\nbaz\n", %w(foo bar baz end), [])
+ puts readline
+ puts readline
+ puts readline
+ begin
+ puts readline
+ rescue EOFError
+ puts "end"
+ end
+ SRC
+ end
+
+ def test_readlines
+ assert_in_out_err(["-e", "p readlines"], "foo\nbar\nbaz\n",
+ ["[\"foo\\n\", \"bar\\n\", \"baz\\n\"]"], [])
+ end
+
+ def test_s_read
+ make_tempfile {|t|
+ assert_equal("foo\nbar\nbaz\n", File.read(t.path))
+ assert_equal("foo\nba", File.read(t.path, 6))
+ assert_equal("bar\n", File.read(t.path, 4, 4))
+ }
+ end
+
+ def test_uninitialized
+ assert_raise(IOError) { IO.allocate.print "" }
+ end
+
+ def test_nofollow
+ # O_NOFOLLOW is not standard.
+ return if /freebsd|linux/ !~ RUBY_PLATFORM
+ return unless defined? File::NOFOLLOW
+ mkcdtmpdir {
+ open("file", "w") {|f| f << "content" }
+ begin
+ File.symlink("file", "slnk")
+ rescue NotImplementedError
+ return
+ end
+ assert_raise(Errno::EMLINK, Errno::ELOOP) {
+ open("slnk", File::RDONLY|File::NOFOLLOW) {}
+ }
+ assert_raise(Errno::EMLINK, Errno::ELOOP) {
+ File.foreach("slnk", :open_args=>[File::RDONLY|File::NOFOLLOW]) {}
+ }
+ }
+ end
+
+ def test_tainted
+ make_tempfile {|t|
+ assert_predicate(File.read(t.path, 4), :tainted?, '[ruby-dev:38826]')
+ assert_predicate(File.open(t.path) {|f| f.read(4)}, :tainted?, '[ruby-dev:38826]')
+ }
+ end
+
+ def test_binmode_after_closed
+ make_tempfile {|t|
+ assert_raise(IOError) {t.binmode}
+ }
+ end
+
+ def test_threaded_flush
+ bug3585 = '[ruby-core:31348]'
+ src = %q{\
+ t = Thread.new { sleep 3 }
+ Thread.new {sleep 1; t.kill; p 'hi!'}
+ t.join
+ }.gsub(/^\s+/, '')
+ 10.times.map do
+ Thread.start do
+ assert_in_out_err([], src) {|stdout, stderr|
+ assert_no_match(/hi.*hi/, stderr.join, bug3585)
+ }
+ end
+ end.each {|th| th.join}
+ end
+
+ def test_flush_in_finalizer1
+ require 'tempfile'
+ bug3910 = '[ruby-dev:42341]'
+ t = Tempfile.open("bug3910") {|t|
+ path = t.path
+ t.close
+ fds = []
+ assert_nothing_raised(TypeError, bug3910) do
+ 500.times {
+ f = File.open(path, "w")
+ f.instance_variable_set(:@test_flush_in_finalizer1, true)
+ fds << f.fileno
+ f.print "hoge"
+ }
+ end
+ t
+ }
+ ensure
+ ObjectSpace.each_object(File) {|f|
+ if f.instance_variables.include?(:@test_flush_in_finalizer1)
+ f.close
+ end
+ }
+ t.close!
+ end
+
+ def test_flush_in_finalizer2
+ require 'tempfile'
+ bug3910 = '[ruby-dev:42341]'
+ Tempfile.open("bug3910") {|t|
+ path = t.path
+ t.close
+ begin
+ 1.times do
+ io = open(path,"w")
+ io.instance_variable_set(:@test_flush_in_finalizer2, true)
+ io.print "hoge"
+ end
+ assert_nothing_raised(TypeError, bug3910) do
+ GC.start
+ end
+ ensure
+ ObjectSpace.each_object(File) {|f|
+ if f.instance_variables.include?(:@test_flush_in_finalizer2)
+ f.close
+ end
+ }
+ end
+ t.close!
+ }
+ end
+
+ def test_readlines_limit_0
+ bug4024 = '[ruby-dev:42538]'
+ make_tempfile {|t|
+ open(t.path, "r") do |io|
+ assert_raise(ArgumentError, bug4024) do
+ io.readlines(0)
+ end
+ end
+ }
+ end
+
+ def test_each_line_limit_0
+ bug4024 = '[ruby-dev:42538]'
+ make_tempfile {|t|
+ open(t.path, "r") do |io|
+ assert_raise(ArgumentError, bug4024) do
+ io.each_line(0).next
+ end
+ end
+ }
+ end
+
+ def os_and_fs(path)
+ uname = Etc.uname
+ os = "#{uname[:sysname]} #{uname[:release]}"
+
+ fs = nil
+ if uname[:sysname] == 'Linux'
+ # [ruby-dev:45703] Old Linux's fadvice() doesn't work on tmpfs.
+ mount = `mount`
+ mountpoints = []
+ mount.scan(/ on (\S+) type (\S+) /) {
+ mountpoints << [$1, $2]
+ }
+ mountpoints.sort_by {|mountpoint, fstype| mountpoint.length }.reverse_each {|mountpoint, fstype|
+ if path == mountpoint
+ fs = fstype
+ break
+ end
+ mountpoint += "/" if %r{/\z} !~ mountpoint
+ if path.start_with?(mountpoint)
+ fs = fstype
+ break
+ end
+ }
+ end
+
+ if fs
+ "#{fs} on #{os}"
+ else
+ os
+ end
+ end
+
+ def test_advise
+ make_tempfile {|tf|
+ assert_raise(ArgumentError, "no arguments") { tf.advise }
+ %w{normal random sequential willneed dontneed noreuse}.map(&:to_sym).each do |adv|
+ [[0,0], [0, 20], [400, 2]].each do |offset, len|
+ open(tf.path) do |t|
+ ret = assert_nothing_raised(lambda { os_and_fs(tf.path) }) {
+ begin
+ t.advise(adv, offset, len)
+ rescue Errno::EINVAL => e
+ if /linux/ =~ RUBY_PLATFORM && (Etc.uname[:release].split('.').map(&:to_i) <=> [3,6]) < 0
+ next # [ruby-core:65355] tmpfs is not supported
+ else
+ raise e
+ end
+ end
+ }
+ assert_nil(ret)
+ assert_raise(ArgumentError, "superfluous arguments") do
+ t.advise(adv, offset, len, offset)
+ end
+ assert_raise(TypeError, "wrong type for first argument") do
+ t.advise(adv.to_s, offset, len)
+ end
+ assert_raise(TypeError, "wrong type for last argument") do
+ t.advise(adv, offset, Array(len))
+ end
+ assert_raise(RangeError, "last argument too big") do
+ t.advise(adv, offset, 9999e99)
+ end
+ end
+ assert_raise(IOError, "closed file") do
+ make_tempfile {|tf2|
+ tf2.advise(adv.to_sym, offset, len)
+ }
+ end
+ end
+ end
+ }
+ end
+
+ def test_invalid_advise
+ feature4204 = '[ruby-dev:42887]'
+ make_tempfile {|tf|
+ %W{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |adv|
+ [[0,0], [0, 20], [400, 2]].each do |offset, len|
+ open(tf.path) do |t|
+ assert_raise_with_message(NotImplementedError, /#{Regexp.quote(adv.inspect)}/, feature4204) { t.advise(adv, offset, len) }
+ end
+ end
+ end
+ }
+ end
+
+ def test_fcntl_lock_linux
+ return if /x86_64-linux/ !~ RUBY_PLATFORM # A binary form of struct flock depend on platform
+ return if [nil].pack("p").bytesize != 8 # Return if x32 platform.
+
+ pad=0
+ Tempfile.create(self.class.name) do |f|
+ r, w = IO.pipe
+ pid = fork do
+ r.close
+ lock = [Fcntl::F_WRLCK, IO::SEEK_SET, pad, 12, 34, 0].pack("s!s!i!L!L!i!")
+ f.fcntl Fcntl::F_SETLKW, lock
+ w.syswrite "."
+ sleep
+ end
+ w.close
+ assert_equal ".", r.read(1)
+ r.close
+ pad = 0
+ getlock = [Fcntl::F_WRLCK, 0, pad, 0, 0, 0].pack("s!s!i!L!L!i!")
+ f.fcntl Fcntl::F_GETLK, getlock
+
+ ptype, whence, pad, start, len, lockpid = getlock.unpack("s!s!i!L!L!i!")
+
+ assert_equal(ptype, Fcntl::F_WRLCK)
+ assert_equal(whence, IO::SEEK_SET)
+ assert_equal(start, 12)
+ assert_equal(len, 34)
+ assert_equal(pid, lockpid)
+
+ Process.kill :TERM, pid
+ Process.waitpid2(pid)
+ end
+ end
+
+ def test_fcntl_lock_freebsd
+ return if /freebsd/ !~ RUBY_PLATFORM # A binary form of struct flock depend on platform
+
+ start = 12
+ len = 34
+ sysid = 0
+ Tempfile.create(self.class.name) do |f|
+ r, w = IO.pipe
+ pid = fork do
+ r.close
+ lock = [start, len, 0, Fcntl::F_WRLCK, IO::SEEK_SET, sysid].pack("qqis!s!i!")
+ f.fcntl Fcntl::F_SETLKW, lock
+ w.syswrite "."
+ sleep
+ end
+ w.close
+ assert_equal ".", r.read(1)
+ r.close
+
+ getlock = [0, 0, 0, Fcntl::F_WRLCK, 0, 0].pack("qqis!s!i!")
+ f.fcntl Fcntl::F_GETLK, getlock
+
+ start, len, lockpid, ptype, whence, sysid = getlock.unpack("qqis!s!i!")
+
+ assert_equal(ptype, Fcntl::F_WRLCK)
+ assert_equal(whence, IO::SEEK_SET)
+ assert_equal(start, 12)
+ assert_equal(len, 34)
+ assert_equal(pid, lockpid)
+
+ Process.kill :TERM, pid
+ Process.waitpid2(pid)
+ end
+ end
+
+ def test_fcntl_dupfd
+ Tempfile.create(self.class.name) do |f|
+ fd = f.fcntl(Fcntl::F_DUPFD, 63)
+ begin
+ assert_operator(fd, :>=, 63)
+ ensure
+ IO.for_fd(fd).close
+ end
+ end
+ end
+
+ def test_cross_thread_close_fd
+ with_pipe do |r,w|
+ read_thread = Thread.new do
+ begin
+ r.read(1)
+ rescue => e
+ e
+ end
+ end
+
+ sleep(0.1) until read_thread.stop?
+ r.close
+ read_thread.join
+ assert_kind_of(IOError, read_thread.value)
+ end
+ end
+
+ def test_cross_thread_close_stdio
+ assert_separately([], <<-'end;')
+ IO.pipe do |r,w|
+ $stdin.reopen(r)
+ r.close
+ read_thread = Thread.new do
+ $stdin.read(1)
+ end
+ sleep(0.1) until read_thread.stop?
+ $stdin.close
+ assert_raise(IOError) {read_thread.join}
+ end
+ end;
+ end
+
+ def test_open_mode
+ feature4742 = "[ruby-core:36338]"
+ bug6055 = '[ruby-dev:45268]'
+
+ mkcdtmpdir do
+ assert_not_nil(f = File.open('symbolic', 'w'))
+ f.close
+ assert_not_nil(f = File.open('numeric', File::WRONLY|File::TRUNC|File::CREAT))
+ f.close
+ assert_not_nil(f = File.open('hash-symbolic', :mode => 'w'))
+ f.close
+ assert_not_nil(f = File.open('hash-numeric', :mode => File::WRONLY|File::TRUNC|File::CREAT), feature4742)
+ f.close
+ assert_nothing_raised(bug6055) {f = File.open('hash-symbolic', binmode: true)}
+ f.close
+ end
+ end
+
+ def test_s_write
+ mkcdtmpdir do
+ path = "test_s_write"
+ File.write(path, "foo\nbar\nbaz")
+ assert_equal("foo\nbar\nbaz", File.read(path))
+ File.write(path, "FOO", 0)
+ assert_equal("FOO\nbar\nbaz", File.read(path))
+ File.write(path, "BAR")
+ assert_equal("BAR", File.read(path))
+ File.write(path, "\u{3042}", mode: "w", encoding: "EUC-JP")
+ assert_equal("\u{3042}".encode("EUC-JP"), File.read(path, encoding: "EUC-JP"))
+ File.delete path
+ assert_equal(6, File.write(path, 'string', 2))
+ File.delete path
+ assert_raise(Errno::EINVAL) { File.write('nonexisting','string', -2) }
+ assert_equal(6, File.write(path, 'string'))
+ assert_equal(3, File.write(path, 'sub', 1))
+ assert_equal("ssubng", File.read(path))
+ File.delete path
+ assert_equal(3, File.write(path, "foo", encoding: "UTF-8"))
+ File.delete path
+ assert_equal(3, File.write(path, "foo", 0, encoding: "UTF-8"))
+ assert_equal("foo", File.read(path))
+ assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
+ assert_equal("ffo", File.read(path))
+ File.delete path
+ assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
+ assert_equal("\00f", File.read(path))
+ assert_equal(1, File.write(path, "f", 0, encoding: "UTF-8"))
+ assert_equal("ff", File.read(path))
+ assert_raise(TypeError) {
+ File.write(path, "foo", Object.new => Object.new)
+ }
+ end
+ end
+
+ def test_s_binwrite
+ mkcdtmpdir do
+ path = "test_s_binwrite"
+ File.binwrite(path, "foo\nbar\nbaz")
+ assert_equal("foo\nbar\nbaz", File.read(path))
+ File.binwrite(path, "FOO", 0)
+ assert_equal("FOO\nbar\nbaz", File.read(path))
+ File.binwrite(path, "BAR")
+ assert_equal("BAR", File.read(path))
+ File.binwrite(path, "\u{3042}")
+ assert_equal("\u{3042}".force_encoding("ASCII-8BIT"), File.binread(path))
+ File.delete path
+ assert_equal(6, File.binwrite(path, 'string', 2))
+ File.delete path
+ assert_equal(6, File.binwrite(path, 'string'))
+ assert_equal(3, File.binwrite(path, 'sub', 1))
+ assert_equal("ssubng", File.binread(path))
+ assert_equal(6, File.size(path))
+ assert_raise(Errno::EINVAL) { File.binwrite('nonexisting', 'string', -2) }
+ assert_nothing_raised(TypeError) { File.binwrite(path, "string", mode: "w", encoding: "EUC-JP") }
+ end
+ end
+
+ def test_race_between_read
+ Tempfile.create("test") {|file|
+ begin
+ path = file.path
+ file.close
+ write_file = File.open(path, "wt")
+ read_file = File.open(path, "rt")
+
+ threads = []
+ 10.times do |i|
+ threads << Thread.new {write_file.print(i)}
+ threads << Thread.new {read_file.read}
+ end
+ assert_join_threads(threads)
+ assert(true, "[ruby-core:37197]")
+ ensure
+ read_file.close
+ write_file.close
+ end
+ }
+ end
+
+ def test_warn
+ assert_warning "warning\n" do
+ warn "warning"
+ end
+
+ assert_warning '' do
+ warn
+ end
+
+ assert_warning "[Feature #5029]\n[ruby-core:38070]\n" do
+ warn "[Feature #5029]", "[ruby-core:38070]"
+ end
+ end
+
+ def test_cloexec
+ return unless defined? Fcntl::FD_CLOEXEC
+ open(__FILE__) {|f|
+ assert_predicate(f, :close_on_exec?)
+ g = f.dup
+ begin
+ assert_predicate(g, :close_on_exec?)
+ f.reopen(g)
+ assert_predicate(f, :close_on_exec?)
+ ensure
+ g.close
+ end
+ g = IO.new(f.fcntl(Fcntl::F_DUPFD))
+ begin
+ assert_predicate(g, :close_on_exec?)
+ ensure
+ g.close
+ end
+ }
+ IO.pipe {|r,w|
+ assert_predicate(r, :close_on_exec?)
+ assert_predicate(w, :close_on_exec?)
+ }
+ end
+
+ def test_ioctl_linux
+ return if /linux/ !~ RUBY_PLATFORM
+ # Alpha, mips, sparc and ppc have an another ioctl request number scheme.
+ # So, hardcoded 0x80045200 may fail.
+ return if /^i.?86|^x86_64/ !~ RUBY_PLATFORM
+
+ assert_nothing_raised do
+ File.open('/dev/urandom'){|f1|
+ entropy_count = ""
+ # RNDGETENTCNT(0x80045200) mean "get entropy count".
+ f1.ioctl(0x80045200, entropy_count)
+ }
+ end
+
+ buf = ''
+ assert_nothing_raised do
+ fionread = 0x541B
+ File.open(__FILE__){|f1|
+ f1.ioctl(fionread, buf)
+ }
+ end
+ assert_equal(File.size(__FILE__), buf.unpack('i!')[0])
+ end
+
+ def test_ioctl_linux2
+ return if /linux/ !~ RUBY_PLATFORM
+ return if /^i.?86|^x86_64/ !~ RUBY_PLATFORM
+
+ return unless system('tty', '-s') # stdin is not a terminal
+ File.open('/dev/tty') { |f|
+ tiocgwinsz=0x5413
+ winsize=""
+ assert_nothing_raised {
+ f.ioctl(tiocgwinsz, winsize)
+ }
+ }
+ end
+
+ def test_setpos
+ mkcdtmpdir {
+ File.open("tmp.txt", "wb") {|f|
+ f.puts "a"
+ f.puts "bc"
+ f.puts "def"
+ }
+ pos1 = pos2 = pos3 = nil
+ File.open("tmp.txt", "rb") {|f|
+ assert_equal("a\n", f.gets)
+ pos1 = f.pos
+ assert_equal("bc\n", f.gets)
+ pos2 = f.pos
+ assert_equal("def\n", f.gets)
+ pos3 = f.pos
+ assert_equal(nil, f.gets)
+ }
+ File.open("tmp.txt", "rb") {|f|
+ f.pos = pos1
+ assert_equal("bc\n", f.gets)
+ assert_equal("def\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+ File.open("tmp.txt", "rb") {|f|
+ f.pos = pos2
+ assert_equal("def\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+ File.open("tmp.txt", "rb") {|f|
+ f.pos = pos3
+ assert_equal(nil, f.gets)
+ }
+ File.open("tmp.txt", "rb") {|f|
+ f.pos = File.size("tmp.txt")
+ s = "not empty string "
+ assert_equal("", f.read(0,s))
+ }
+ }
+ end
+
+ def test_std_fileno
+ assert_equal(0, STDIN.fileno)
+ assert_equal(1, STDOUT.fileno)
+ assert_equal(2, STDERR.fileno)
+ assert_equal(0, $stdin.fileno)
+ assert_equal(1, $stdout.fileno)
+ assert_equal(2, $stderr.fileno)
+ end
+
+ def test_frozen_fileno
+ bug9865 = '[ruby-dev:48241] [Bug #9865]'
+ with_pipe do |r,w|
+ fd = r.fileno
+ assert_equal(fd, r.freeze.fileno, bug9865)
+ end
+ end
+
+ def test_frozen_autoclose
+ with_pipe do |r,w|
+ fd = r.fileno
+ assert_equal(true, r.freeze.autoclose?)
+ end
+ end
+
+ def test_sysread_locktmp
+ bug6099 = '[ruby-dev:45297]'
+ buf = " " * 100
+ data = "a" * 100
+ with_pipe do |r,w|
+ th = Thread.new {r.sysread(100, buf)}
+ Thread.pass until th.stop?
+ buf.replace("")
+ assert_empty(buf, bug6099)
+ w.write(data)
+ Thread.pass while th.alive?
+ th.join
+ end
+ assert_equal(data, buf, bug6099)
+ end
+
+ def test_readpartial_locktmp
+ bug6099 = '[ruby-dev:45297]'
+ buf = " " * 100
+ data = "a" * 100
+ th = nil
+ with_pipe do |r,w|
+ r.nonblock = true
+ th = Thread.new {r.readpartial(100, buf)}
+
+ Thread.pass until th.stop?
+
+ assert_equal 100, buf.bytesize
+
+ begin
+ buf.replace("")
+ rescue RuntimeError => e
+ assert_match(/can't modify string; temporarily locked/, e.message)
+ Thread.pass
+ end until buf.empty?
+
+ assert_empty(buf, bug6099)
+ assert_predicate(th, :alive?)
+ w.write(data)
+ Thread.pass while th.alive?
+ th.join
+ end
+ assert_equal(data, buf, bug6099)
+ end
+
+ def test_advise_pipe
+ # we don't know if other platforms have a real posix_fadvise()
+ return if /linux/ !~ RUBY_PLATFORM
+ with_pipe do |r,w|
+ # Linux 2.6.15 and earlier returned EINVAL instead of ESPIPE
+ assert_raise(Errno::ESPIPE, Errno::EINVAL) { r.advise(:willneed) }
+ assert_raise(Errno::ESPIPE, Errno::EINVAL) { w.advise(:willneed) }
+ end
+ end
+
+ def assert_buffer_not_raise_shared_string_error
+ bug6764 = '[ruby-core:46586]'
+ bug9847 = '[ruby-core:62643] [Bug #9847]'
+ size = 28
+ data = [*"a".."z", *"A".."Z"].shuffle.join("")
+ t = Tempfile.new("test_io")
+ t.write(data)
+ t.close
+ w = []
+ assert_nothing_raised(RuntimeError, bug6764) do
+ buf = ''
+ File.open(t.path, "r") do |r|
+ while yield(r, size, buf)
+ w << buf.dup
+ end
+ end
+ end
+ assert_equal(data, w.join(""), bug9847)
+ ensure
+ t.close!
+ end
+
+ def test_read_buffer_not_raise_shared_string_error
+ assert_buffer_not_raise_shared_string_error do |r, size, buf|
+ r.read(size, buf)
+ end
+ end
+
+ def test_sysread_buffer_not_raise_shared_string_error
+ assert_buffer_not_raise_shared_string_error do |r, size, buf|
+ begin
+ r.sysread(size, buf)
+ rescue EOFError
+ nil
+ end
+ end
+ end
+
+ def test_readpartial_buffer_not_raise_shared_string_error
+ assert_buffer_not_raise_shared_string_error do |r, size, buf|
+ begin
+ r.readpartial(size, buf)
+ rescue EOFError
+ nil
+ end
+ end
+ end
+
+ def test_puts_recursive_ary
+ bug5986 = '[ruby-core:42444]'
+ c = Class.new {
+ def to_ary
+ [self]
+ end
+ }
+ s = StringIO.new
+ s.puts(c.new)
+ assert_equal("[...]\n", s.string, bug5986)
+ end
+
+ def test_io_select_with_many_files
+ bug8080 = '[ruby-core:53349]'
+
+ assert_normal_exit %q{
+ require "tempfile"
+
+ # try to raise RLIM_NOFILE to >FD_SETSIZE
+ # Unfortunately, ruby export FD_SETSIZE. then we assume it's 1024.
+ fd_setsize = 1024
+
+ begin
+ Process.setrlimit(Process::RLIMIT_NOFILE, fd_setsize+10)
+ rescue =>e
+ # Process::RLIMIT_NOFILE couldn't be raised. skip the test
+ exit 0
+ end
+
+ tempfiles = []
+ (0..fd_setsize+1).map {|i|
+ tempfiles << Tempfile.open("test_io_select_with_many_files")
+ }
+
+ IO.select(tempfiles)
+ }, bug8080, timeout: 30
+ end
+
+ def test_read_32bit_boundary
+ bug8431 = '[ruby-core:55098] [Bug #8431]'
+ make_tempfile {|t|
+ assert_separately(["-", bug8431, t.path], <<-"end;")
+ msg = ARGV.shift
+ f = open(ARGV[0], "rb")
+ f.seek(0xffff_ffff)
+ assert_nil(f.read(1), msg)
+ end;
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_write_32bit_boundary
+ bug8431 = '[ruby-core:55098] [Bug #8431]'
+ make_tempfile {|t|
+ def t.close(unlink_now = false)
+ # TODO: Tempfile should deal with this delay on Windows?
+ # NOTE: re-opening with O_TEMPORARY does not work.
+ path = self.path
+ ret = super
+ if unlink_now
+ begin
+ File.unlink(path)
+ rescue Errno::ENOENT
+ rescue Errno::EACCES
+ sleep(2)
+ retry
+ end
+ end
+ ret
+ end
+
+ begin
+ assert_separately(["-", bug8431, t.path], <<-"end;", timeout: 30)
+ msg = ARGV.shift
+ f = open(ARGV[0], "wb")
+ f.seek(0xffff_ffff)
+ begin
+ # this will consume very long time or fail by ENOSPC on a
+ # filesystem which sparse file is not supported
+ f.write('1')
+ pos = f.tell
+ rescue Errno::ENOSPC
+ skip "non-sparse file system"
+ rescue SystemCallError
+ else
+ assert_equal(0x1_0000_0000, pos, msg)
+ end
+ end;
+ rescue Timeout::Error
+ skip "Timeout because of slow file writing"
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_unlocktmp_ensure
+ bug8669 = '[ruby-core:56121] [Bug #8669]'
+
+ str = ""
+ IO.pipe {|r,|
+ t = Thread.new { r.read(nil, str) }
+ sleep 0.1 until t.stop?
+ t.raise
+ sleep 0.1 while t.alive?
+ assert_nothing_raised(RuntimeError, bug8669) { str.clear }
+ assert_raise(RuntimeError) { t.join }
+ }
+ end
+
+ def test_readpartial_unlocktmp_ensure
+ bug8669 = '[ruby-core:56121] [Bug #8669]'
+
+ str = ""
+ IO.pipe {|r, w|
+ t = Thread.new { r.readpartial(4096, str) }
+ sleep 0.1 until t.stop?
+ t.raise
+ sleep 0.1 while t.alive?
+ assert_nothing_raised(RuntimeError, bug8669) { str.clear }
+ assert_raise(RuntimeError) { t.join }
+ }
+ end
+
+ def test_sysread_unlocktmp_ensure
+ bug8669 = '[ruby-core:56121] [Bug #8669]'
+
+ str = ""
+ IO.pipe {|r, w|
+ t = Thread.new { r.sysread(4096, str) }
+ sleep 0.1 until t.stop?
+ t.raise
+ sleep 0.1 while t.alive?
+ assert_nothing_raised(RuntimeError, bug8669) { str.clear }
+ assert_raise(RuntimeError) { t.join }
+ }
+ end
+
+ def test_exception_at_close
+ bug10153 = '[ruby-core:64463] [Bug #10153] exception in close at the end of block'
+ assert_raise(Errno::EBADF, bug10153) do
+ IO.pipe do |r, w|
+ assert_nothing_raised {IO.open(w.fileno) {}}
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_io_m17n.rb b/jni/ruby/test/ruby/test_io_m17n.rb
new file mode 100644
index 0000000..734b017
--- /dev/null
+++ b/jni/ruby/test/ruby/test_io_m17n.rb
@@ -0,0 +1,2566 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'tmpdir'
+require 'timeout'
+
+class TestIO_M17N < Test::Unit::TestCase
+ ENCS = [
+ Encoding::ASCII_8BIT,
+ Encoding::EUC_JP,
+ Encoding::Shift_JIS,
+ Encoding::UTF_8
+ ]
+
+ def with_tmpdir
+ Dir.mktmpdir {|dir|
+ Dir.chdir(dir) {
+ yield dir
+ }
+ }
+ end
+
+ def pipe(*args, wp, rp)
+ re, we = nil, nil
+ r, w = IO.pipe(*args)
+ rt = Thread.new do
+ begin
+ rp.call(r)
+ rescue Exception
+ r.close
+ re = $!
+ end
+ end
+ wt = Thread.new do
+ begin
+ wp.call(w)
+ rescue Exception
+ w.close
+ we = $!
+ end
+ end
+ flunk("timeout") unless wt.join(10) && rt.join(10)
+ ensure
+ w.close unless !w || w.closed?
+ r.close unless !r || r.closed?
+ (wt.kill; wt.join) if wt
+ (rt.kill; rt.join) if rt
+ raise we if we
+ raise re if re
+ end
+
+ def with_pipe(*args)
+ r, w = IO.pipe(*args)
+ begin
+ yield r, w
+ ensure
+ r.close if !r.closed?
+ w.close if !w.closed?
+ end
+ end
+
+ def generate_file(path, content)
+ open(path, "wb") {|f| f.write content }
+ end
+
+ def encdump(str)
+ "#{str.dump}.force_encoding(#{str.encoding.name.dump})"
+ end
+
+ def assert_str_equal(expected, actual, message=nil)
+ full_message = build_message(message, <<EOT)
+#{encdump expected} expected but not equal to
+#{encdump actual}.
+EOT
+ assert_equal(expected, actual, full_message)
+ end
+
+ def test_open_r
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r") {|f|
+ assert_equal(Encoding.default_external, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_rb
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "rb") {|f|
+ assert_equal(Encoding.find("ASCII-8BIT"), f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_enc
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r:euc-jp") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_ascii8bit
+ with_tmpdir {
+ generate_file('tmp', "")
+ EnvUtil.with_default_external(Encoding::ASCII_8BIT) do
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ open("tmp", "r") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ open("tmp", "r:ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ open("tmp", "r:ascii-8bit:utf-16") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ end
+ EnvUtil.with_default_internal(nil) do
+ open("tmp", "r") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ open("tmp", "r:ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ open("tmp", "r:ascii-8bit:utf-16") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ end
+ end
+ }
+ end
+
+ def test_open_r_enc_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", encoding: "euc-jp") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_encname_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", encoding: Encoding::EUC_JP) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_ext_enc_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", external_encoding: Encoding::EUC_JP) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_ext_encname_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", external_encoding: "euc-jp") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_enc_enc
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", external_encoding: Encoding::EUC_JP, internal_encoding: Encoding::UTF_8) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_encname_encname
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r:euc-jp:utf-8") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_encname_encname_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", encoding: "euc-jp:utf-8") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_enc_enc_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", external_encoding: Encoding::EUC_JP, internal_encoding: Encoding::UTF_8) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_externalencname_internalencname_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", external_encoding: "euc-jp", internal_encoding: "utf-8") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_w
+ with_tmpdir {
+ open("tmp", "w") {|f|
+ assert_equal(nil, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_wb
+ with_tmpdir {
+ open("tmp", "wb") {|f|
+ assert_equal(Encoding.find("ASCII-8BIT"), f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_w_enc
+ with_tmpdir {
+ open("tmp", "w:euc-jp") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_w_enc_in_opt
+ with_tmpdir {
+ open("tmp", "w", encoding: "euc-jp") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_w_enc_in_opt2
+ with_tmpdir {
+ open("tmp", "w", external_encoding: "euc-jp") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_w_enc_enc
+ with_tmpdir {
+ open("tmp", "w:euc-jp:utf-8") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_w_enc_enc_in_opt
+ with_tmpdir {
+ open("tmp", "w", encoding: "euc-jp:utf-8") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_w_enc_enc_in_opt2
+ with_tmpdir {
+ open("tmp", "w", external_encoding: "euc-jp", internal_encoding: "utf-8") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_w_enc_enc_perm
+ with_tmpdir {
+ open("tmp", "w:euc-jp:utf-8", 0600) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_ignored_encoding_option
+ enc = "\u{30a8 30f3 30b3 30fc 30c7 30a3 30f3 30b0}"
+ pattern = /#{enc}/
+ assert_warning(pattern) {
+ open(IO::NULL, external_encoding: "us-ascii", encoding: enc) {}
+ }
+ assert_warning(pattern) {
+ open(IO::NULL, internal_encoding: "us-ascii", encoding: enc) {}
+ }
+ end
+
+ def test_io_new_enc
+ with_tmpdir {
+ generate_file("tmp", "\xa1")
+ fd = IO.sysopen("tmp")
+ f = IO.new(fd, "r:sjis")
+ begin
+ assert_equal(Encoding::Windows_31J, f.read.encoding)
+ ensure
+ f.close
+ end
+ }
+ end
+
+ def test_s_pipe_invalid
+ pipe("utf-8", "euc-jp", { :invalid=>:replace },
+ proc do |w|
+ w << "\x80"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("?", r.read)
+ end)
+ end
+
+ def test_s_pipe_undef
+ pipe("utf-8:euc-jp", { :undef=>:replace },
+ proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("?", r.read)
+ end)
+ end
+
+ def test_s_pipe_undef_replace_string
+ pipe("utf-8:euc-jp", { :undef=>:replace, :replace=>"X" },
+ proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("X", r.read)
+ end)
+ end
+
+ def test_dup
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << "\u3042"
+ w.close
+ end,
+ proc do |r|
+ r2 = r.dup
+ begin
+ assert_equal("\xA4\xA2".force_encoding("euc-jp"), r2.read)
+ ensure
+ r2.close
+ end
+ end)
+ end
+
+ def test_dup_undef
+ pipe("utf-8:euc-jp", { :undef=>:replace },
+ proc do |w|
+ w << "\uFFFD"
+ w.close
+ end,
+ proc do |r|
+ r2 = r.dup
+ begin
+ assert_equal("?", r2.read)
+ ensure
+ r2.close
+ end
+ end)
+ end
+
+ def test_stdin
+ assert_equal(Encoding.default_external, STDIN.external_encoding)
+ assert_equal(nil, STDIN.internal_encoding)
+ end
+
+ def test_stdout
+ assert_equal(nil, STDOUT.external_encoding)
+ assert_equal(nil, STDOUT.internal_encoding)
+ end
+
+ def test_stderr
+ assert_equal(nil, STDERR.external_encoding)
+ assert_equal(nil, STDERR.internal_encoding)
+ end
+
+ def test_terminator_conversion
+ with_tmpdir {
+ generate_file('tmp', "before \u00FF after")
+ s = open("tmp", "r:utf-8:iso-8859-1") {|f|
+ f.gets("\xFF".force_encoding("iso-8859-1"))
+ }
+ assert_equal(Encoding.find("iso-8859-1"), s.encoding)
+ assert_str_equal("before \xFF".force_encoding("iso-8859-1"), s, '[ruby-core:14288]')
+ }
+ end
+
+ def test_terminator_conversion2
+ with_tmpdir {
+ generate_file('tmp', "before \xA1\xA2\xA2\xA3 after")
+ s = open("tmp", "r:euc-jp:utf-8") {|f|
+ f.gets("\xA2\xA2".force_encoding("euc-jp").encode("utf-8"))
+ }
+ assert_equal(Encoding.find("utf-8"), s.encoding)
+ assert_str_equal("before \xA1\xA2\xA2\xA3 after".force_encoding("euc-jp").encode("utf-8"), s, '[ruby-core:14319]')
+ }
+ end
+
+ def test_terminator_stateful_conversion
+ with_tmpdir {
+ src = "before \e$B\x23\x30\x23\x31\e(B after".force_encoding("iso-2022-jp")
+ generate_file('tmp', src)
+ s = open("tmp", "r:iso-2022-jp:euc-jp") {|f|
+ f.gets("0".force_encoding("euc-jp"))
+ }
+ assert_equal(Encoding.find("euc-jp"), s.encoding)
+ assert_str_equal(src.encode("euc-jp"), s)
+ }
+ end
+
+ def test_nonascii_terminator
+ with_tmpdir {
+ generate_file('tmp', "before \xA2\xA2 after")
+ open("tmp", "r:euc-jp") {|f|
+ assert_raise(ArgumentError) {
+ f.gets("\xA2\xA2".force_encoding("utf-8"))
+ }
+ }
+ }
+ end
+
+ def test_pipe_terminator_conversion
+ rs = "\xA2\xA2".encode("utf-8", "euc-jp")
+ pipe("euc-jp:utf-8",
+ proc do |w|
+ w.write "before \xa2\xa2 after"
+ w.close
+ end,
+ proc do |r|
+ timeout(1) {
+ assert_equal("before \xa2\xa2".encode("utf-8", "euc-jp"),
+ r.gets(rs))
+ }
+ end)
+ end
+
+ def test_pipe_conversion
+ pipe("euc-jp:utf-8",
+ proc do |w|
+ w.write "\xa1\xa1"
+ end,
+ proc do |r|
+ assert_equal("\xa1\xa1".encode("utf-8", "euc-jp"), r.getc)
+ end)
+ end
+
+ def test_pipe_convert_partial_read
+ pipe("euc-jp:utf-8",
+ proc do |w|
+ w.write "\xa1"
+ sleep 0.1
+ w.write "\xa1"
+ end,
+ proc do |r|
+ assert_equal("\xa1\xa1".encode("utf-8", "euc-jp"), r.getc)
+ end)
+ end
+
+ def test_getc_invalid
+ pipe("euc-jp:utf-8",
+ proc do |w|
+ w << "\xa1xyz"
+ w.close
+ end,
+ proc do |r|
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
+ assert_equal("\xA1".force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal("xyz", r.read(10))
+ end)
+ end
+
+ def test_getc_stateful_conversion
+ with_tmpdir {
+ src = "\e$B\x23\x30\x23\x31\e(B".force_encoding("iso-2022-jp")
+ generate_file('tmp', src)
+ open("tmp", "r:iso-2022-jp:euc-jp") {|f|
+ assert_equal("\xa3\xb0".force_encoding("euc-jp"), f.getc)
+ assert_equal("\xa3\xb1".force_encoding("euc-jp"), f.getc)
+ }
+ }
+ end
+
+ def test_getc_newlineconv
+ with_tmpdir {
+ src = "\u3042"
+ generate_file('tmp', src)
+ EnvUtil.with_default_external(Encoding::UTF_8) do
+ open("tmp", "rt") {|f|
+ s = f.getc
+ assert_equal(true, s.valid_encoding?)
+ assert_equal("\u3042", s)
+ }
+ end
+ }
+ end
+
+ def test_getc_newlineconv_invalid
+ with_tmpdir {
+ src = "\xE3\x81"
+ generate_file('tmp', src)
+ EnvUtil.with_default_external(Encoding::UTF_8) do
+ open("tmp", "rt") {|f|
+ s = f.getc
+ assert_equal(false, s.valid_encoding?)
+ assert_equal("\xE3".force_encoding("UTF-8"), s)
+ s = f.getc
+ assert_equal(false, s.valid_encoding?)
+ assert_equal("\x81".force_encoding("UTF-8"), s)
+ }
+ end
+ }
+ end
+
+ def test_ungetc_int
+ with_tmpdir {
+ generate_file('tmp', "A")
+ s = open("tmp", "r:GB18030") {|f|
+ f.ungetc(0x8431A439)
+ f.read
+ }
+ assert_equal(Encoding::GB18030, s.encoding)
+ assert_str_equal(0x8431A439.chr("GB18030")+"A", s)
+ }
+ end
+
+ def test_ungetc_str
+ with_tmpdir {
+ generate_file('tmp', "A")
+ s = open("tmp", "r:GB18030") {|f|
+ f.ungetc(0x8431A439.chr("GB18030"))
+ f.read
+ }
+ assert_equal(Encoding::GB18030, s.encoding)
+ assert_str_equal(0x8431A439.chr("GB18030")+"A", s)
+ }
+ end
+
+ def test_ungetc_stateful_conversion
+ with_tmpdir {
+ src = "before \e$B\x23\x30\x23\x31\e(B after".force_encoding("iso-2022-jp")
+ generate_file('tmp', src)
+ s = open("tmp", "r:iso-2022-jp:euc-jp") {|f|
+ f.ungetc("0".force_encoding("euc-jp"))
+ f.read
+ }
+ assert_equal(Encoding.find("euc-jp"), s.encoding)
+ assert_str_equal("0" + src.encode("euc-jp"), s)
+ }
+ end
+
+ def test_ungetc_stateful_conversion2
+ with_tmpdir {
+ src = "before \e$B\x23\x30\x23\x31\e(B after".force_encoding("iso-2022-jp")
+ former = "before \e$B\x23\x30\e(B".force_encoding("iso-2022-jp")
+ rs = "\e$B\x23\x30\e(B".force_encoding("iso-2022-jp")
+ latter = "\e$B\x23\x31\e(B after".force_encoding("iso-2022-jp")
+ generate_file('tmp', src)
+ s = open("tmp", "r:iso-2022-jp:euc-jp") {|f|
+ assert_equal(former.encode("euc-jp", "iso-2022-jp"),
+ f.gets(rs.encode("euc-jp", "iso-2022-jp")))
+ f.ungetc("0")
+ f.read
+ }
+ assert_equal(Encoding.find("euc-jp"), s.encoding)
+ assert_str_equal("0" + latter.encode("euc-jp"), s)
+ }
+ end
+
+ def test_open_ascii
+ with_tmpdir {
+ src = "abc\n"
+ generate_file('tmp', "abc\n")
+ ENCS.each {|enc|
+ s = open('tmp', "r:#{enc}") {|f| f.gets }
+ assert_equal(enc, s.encoding)
+ assert_str_equal(src, s)
+ }
+ }
+ end
+
+ def test_open_nonascii
+ with_tmpdir {
+ src = "\xc2\xa1\n"
+ generate_file('tmp', src)
+ ENCS.each {|enc|
+ content = src.dup.force_encoding(enc)
+ s = open('tmp', "r:#{enc}") {|f| f.gets }
+ assert_equal(enc, s.encoding)
+ assert_str_equal(content, s)
+ }
+ }
+ end
+
+ def test_read_encoding
+ with_tmpdir {
+ src = "\xc2\xa1\n".force_encoding("ASCII-8BIT")
+ generate_file('tmp', "\xc2\xa1\n")
+ ENCS.each {|enc|
+ content = src.dup.force_encoding(enc)
+ open('tmp', "r:#{enc}") {|f|
+ s = f.getc
+ assert_equal(enc, s.encoding)
+ assert_str_equal(content[0], s)
+ }
+ open('tmp', "r:#{enc}") {|f|
+ s = f.readchar
+ assert_equal(enc, s.encoding)
+ assert_str_equal(content[0], s)
+ }
+ open('tmp', "r:#{enc}") {|f|
+ s = f.gets
+ assert_equal(enc, s.encoding)
+ assert_str_equal(content, s)
+ }
+ open('tmp', "r:#{enc}") {|f|
+ s = f.readline
+ assert_equal(enc, s.encoding)
+ assert_str_equal(content, s)
+ }
+ open('tmp', "r:#{enc}") {|f|
+ lines = f.readlines
+ assert_equal(1, lines.length)
+ s = lines[0]
+ assert_equal(enc, s.encoding)
+ assert_str_equal(content, s)
+ }
+ open('tmp', "r:#{enc}") {|f|
+ f.each_line {|s|
+ assert_equal(enc, s.encoding)
+ assert_str_equal(content, s)
+ }
+ }
+ open('tmp', "r:#{enc}") {|f|
+ s = f.read
+ assert_equal(enc, s.encoding)
+ assert_str_equal(content, s)
+ }
+ open('tmp', "r:#{enc}") {|f|
+ s = f.read(1)
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_str_equal(src[0], s)
+ }
+ open('tmp', "r:#{enc}") {|f|
+ s = f.readpartial(1)
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_str_equal(src[0], s)
+ }
+ open('tmp', "r:#{enc}") {|f|
+ s = f.sysread(1)
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_str_equal(src[0], s)
+ }
+ }
+ }
+ end
+
+ def test_write_noenc
+ src = "\xc2\xa1\n".force_encoding("ascii-8bit")
+ with_tmpdir {
+ open('tmp', "w") {|f|
+ ENCS.each {|enc|
+ f.write src.dup.force_encoding(enc)
+ }
+ }
+ open('tmp', 'r:ascii-8bit') {|f|
+ assert_equal(src*ENCS.length, f.read)
+ }
+ }
+ end
+
+ def test_write_conversion
+ utf8 = "\u6666"
+ eucjp = "\xb3\xa2".force_encoding("EUC-JP")
+ with_tmpdir {
+ open('tmp', "w:EUC-JP") {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ f.print utf8
+ }
+ assert_equal(eucjp, File.read('tmp').force_encoding("EUC-JP"))
+ open('tmp', 'r:EUC-JP:UTF-8') {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ assert_equal(utf8, f.read)
+ }
+ }
+ end
+
+ def test_pipe
+ utf8 = "\u6666"
+ eucjp = "\xb3\xa2".force_encoding("EUC-JP")
+
+ pipe(proc do |w|
+ w << utf8
+ w.close
+ end, proc do |r|
+ assert_equal(Encoding.default_external, r.external_encoding)
+ assert_equal(nil, r.internal_encoding)
+ s = r.read
+ assert_equal(Encoding.default_external, s.encoding)
+ assert_str_equal(utf8.dup.force_encoding(Encoding.default_external), s)
+ end)
+
+ pipe("EUC-JP",
+ proc do |w|
+ w << eucjp
+ w.close
+ end,
+ proc do |r|
+ assert_equal(Encoding::EUC_JP, r.external_encoding)
+ assert_equal(nil, r.internal_encoding)
+ assert_equal(eucjp, r.read)
+ end)
+
+ pipe("UTF-8",
+ proc do |w|
+ w << "a" * 1023 + "\u3042" + "a" * 1022
+ w.close
+ end,
+ proc do |r|
+ assert_equal(true, r.read.valid_encoding?)
+ end)
+
+ pipe("UTF-8:EUC-JP",
+ proc do |w|
+ w << utf8
+ w.close
+ end,
+ proc do |r|
+ assert_equal(Encoding::UTF_8, r.external_encoding)
+ assert_equal(Encoding::EUC_JP, r.internal_encoding)
+ assert_equal(eucjp, r.read)
+ end)
+
+ assert_raise_with_message(ArgumentError, /invalid name encoding/) do
+ with_pipe("UTF-8", "UTF-8".encode("UTF-32BE")) {}
+ end
+ assert_raise_with_message(ArgumentError, /invalid name encoding/) do
+ with_pipe("UTF-8".encode("UTF-32BE")) {}
+ end
+
+ ENCS.each {|enc|
+ pipe(enc,
+ proc do |w|
+ w << "\xc2\xa1"
+ w.close
+ end,
+ proc do |r|
+ s = r.getc
+ assert_equal(enc, s.encoding)
+ end)
+ }
+
+ ENCS.each {|enc|
+ next if enc == Encoding::ASCII_8BIT
+ next if enc == Encoding::UTF_8
+ pipe("#{enc}:UTF-8",
+ proc do |w|
+ w << "\xc2\xa1"
+ w.close
+ end,
+ proc do |r|
+ s = r.read
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_equal(s.encode("UTF-8"), s)
+ end)
+ }
+
+ end
+
+ def test_marshal
+ data = 56225
+ pipe("EUC-JP",
+ proc do |w|
+ Marshal.dump(data, w)
+ w.close
+ end,
+ proc do |r|
+ result = nil
+ assert_nothing_raised("[ruby-dev:33264]") { result = Marshal.load(r) }
+ assert_equal(data, result)
+ end)
+ end
+
+ def test_gets_nil
+ pipe("UTF-8:EUC-JP",
+ proc do |w|
+ w << "\u{3042}"
+ w.close
+ end,
+ proc do |r|
+ result = r.gets(nil)
+ assert_equal("\u{3042}".encode("euc-jp"), result)
+ end)
+ end
+
+ def test_gets_limit
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.gets(1)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.gets(2)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4".force_encoding("euc-jp"), r.gets(3)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4".force_encoding("euc-jp"), r.gets(4)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6".force_encoding("euc-jp"), r.gets(5)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6".force_encoding("euc-jp"), r.gets(6)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(7)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(8)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(9)) })
+ end
+
+ def test_gets_invalid
+ before = "\u{3042}\u{3044}"
+ invalid = "\x80".force_encoding("utf-8")
+ after = "\u{3046}\u{3048}"
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << before + invalid + after
+ w.close
+ end,
+ proc do |r|
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.gets }
+ assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal(after.encode("euc-jp"), r.gets)
+ end)
+ end
+
+ def test_getc_invalid2
+ before1 = "\u{3042}"
+ before2 = "\u{3044}"
+ invalid = "\x80".force_encoding("utf-8")
+ after1 = "\u{3046}"
+ after2 = "\u{3048}"
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << before1 + before2 + invalid + after1 + after2
+ w.close
+ end,
+ proc do |r|
+ assert_equal(before1.encode("euc-jp"), r.getc)
+ assert_equal(before2.encode("euc-jp"), r.getc)
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
+ assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal(after1.encode("euc-jp"), r.getc)
+ assert_equal(after2.encode("euc-jp"), r.getc)
+ end)
+ end
+
+ def test_getc_invalid3
+ before1 = "\x42\x30".force_encoding("utf-16le")
+ before2 = "\x44\x30".force_encoding("utf-16le")
+ invalid = "\x00\xd8".force_encoding("utf-16le")
+ after1 = "\x46\x30".force_encoding("utf-16le")
+ after2 = "\x48\x30".force_encoding("utf-16le")
+ pipe("utf-16le:euc-jp", { :binmode => true },
+ proc do |w|
+ w << before1 + before2 + invalid + after1 + after2
+ w.close
+ end,
+ proc do |r|
+ assert_equal(before1.encode("euc-jp"), r.getc)
+ assert_equal(before2.encode("euc-jp"), r.getc)
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
+ assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal(after1.encode("euc-jp"), r.getc)
+ assert_equal(after2.encode("euc-jp"), r.getc)
+ end)
+ end
+
+ def test_read_all
+ str = "\u3042\u3044"
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << str
+ w.close
+ end,
+ proc do |r|
+ assert_equal(str.encode("euc-jp"), r.read)
+ end)
+ end
+
+ def test_read_all_invalid
+ before = "\u{3042}\u{3044}"
+ invalid = "\x80".force_encoding("utf-8")
+ after = "\u{3046}\u{3048}"
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << before + invalid + after
+ w.close
+ end,
+ proc do |r|
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.read }
+ assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal(after.encode("euc-jp"), r.read)
+ end)
+ end
+
+ def test_file_foreach
+ with_tmpdir {
+ generate_file('tst', 'a' * 8191 + "\xa1\xa1")
+ assert_nothing_raised {
+ File.foreach('tst', :encoding=>"euc-jp") {|line| line.inspect }
+ }
+ }
+ end
+
+ def test_set_encoding
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ s = "\u3042".force_encoding("ascii-8bit")
+ s << "\x82\xa0".force_encoding("ascii-8bit")
+ w << s
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ r.set_encoding("shift_jis:euc-jp")
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ end)
+ end
+
+ def test_set_encoding2
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ s = "\u3042".force_encoding("ascii-8bit")
+ s << "\x82\xa0".force_encoding("ascii-8bit")
+ w << s
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ r.set_encoding("shift_jis", "euc-jp")
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ end)
+ end
+
+ def test_set_encoding_nil
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ s = "\u3042".force_encoding("ascii-8bit")
+ s << "\x82\xa0".force_encoding("ascii-8bit")
+ w << s
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ r.set_encoding(nil)
+ assert_equal("\x82\xa0".force_encoding(Encoding.default_external), r.read)
+ end)
+ end
+
+ def test_set_encoding_enc
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ s = "\u3042".force_encoding("ascii-8bit")
+ s << "\x82\xa0".force_encoding("ascii-8bit")
+ w << s
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ r.set_encoding(Encoding::Shift_JIS)
+ assert_equal("\x82\xa0".force_encoding(Encoding::Shift_JIS), r.getc)
+ end)
+ end
+
+ def test_set_encoding_invalid
+ pipe(proc do |w|
+ w << "\x80"
+ w.close
+ end,
+ proc do |r|
+ r.set_encoding("utf-8:euc-jp", :invalid=>:replace)
+ assert_equal("?", r.read)
+ end)
+ end
+
+ def test_set_encoding_identical
+ #bug5568 = '[ruby-core:40727]'
+ bug6324 = '[ruby-core:44455]'
+ open(__FILE__, "r") do |f|
+ assert_warning('', bug6324) {
+ f.set_encoding("eucjp:euc-jp")
+ }
+ assert_warning('', bug6324) {
+ f.set_encoding("eucjp", "euc-jp")
+ }
+ assert_warning('', bug6324) {
+ f.set_encoding(Encoding::EUC_JP, "euc-jp")
+ }
+ assert_warning('', bug6324) {
+ f.set_encoding("eucjp", Encoding::EUC_JP)
+ }
+ assert_warning('', bug6324) {
+ f.set_encoding(Encoding::EUC_JP, Encoding::EUC_JP)
+ }
+ nonstr = Object.new
+ def nonstr.to_str; "eucjp"; end
+ assert_warning('', bug6324) {
+ f.set_encoding(nonstr, nonstr)
+ }
+ end
+ end
+
+ def test_set_encoding_undef
+ pipe(proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ r.set_encoding("utf-8", "euc-jp", :undef=>:replace)
+ assert_equal("?", r.read)
+ end)
+ end
+
+ def test_set_encoding_undef_replace
+ pipe(proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ r.set_encoding("utf-8", "euc-jp", :undef=>:replace, :replace=>"ZZZ")
+ assert_equal("ZZZ", r.read)
+ end)
+ pipe(proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ r.set_encoding("utf-8:euc-jp", :undef=>:replace, :replace=>"ZZZ")
+ assert_equal("ZZZ", r.read)
+ end)
+ end
+
+ def test_set_encoding_binmode
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rt") {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_raise(ArgumentError) {
+ open(__FILE__, "r") {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_nothing_raised {
+ open(__FILE__, "rb") {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_nothing_raised {
+ open(__FILE__, "r") {|f|
+ f.binmode
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_nothing_raised {
+ open(__FILE__, "rt") {|f|
+ f.binmode
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_nothing_raised {
+ open(__FILE__, "r", binmode: true) {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rb", binmode: true) {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rb", binmode: false) {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ end
+
+ def test_set_encoding_unsupported
+ bug5567 = '[ruby-core:40726]'
+ IO.pipe do |r, w|
+ assert_nothing_raised(bug5567) do
+ assert_warning(/Unsupported/, bug5567) {r.set_encoding("fffffffffffxx")}
+ assert_warning(/Unsupported/, bug5567) {r.set_encoding("fffffffffffxx", "us-ascii")}
+ assert_warning(/Unsupported/, bug5567) {r.set_encoding("us-ascii", "fffffffffffxx")}
+ end
+ end
+ end
+
+ def test_textmode_twice
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rt", textmode: true) {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rt", textmode: false) {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ end
+
+ def test_write_conversion_fixenc
+ pipe(proc do |w|
+ w.set_encoding("iso-2022-jp:utf-8")
+ w << "\u3042"
+ w << "\u3044"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"),
+ r.read.force_encoding("ascii-8bit"))
+ end)
+ end
+
+ def test_write_conversion_anyenc_stateful
+ pipe(proc do |w|
+ w.set_encoding("iso-2022-jp")
+ w << "\u3042"
+ w << "\x82\xa2".force_encoding("sjis")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"),
+ r.read.force_encoding("ascii-8bit"))
+ end)
+ end
+
+ def test_write_conversion_anyenc_stateless
+ pipe(proc do |w|
+ w.set_encoding("euc-jp")
+ w << "\u3042"
+ w << "\x82\xa2".force_encoding("sjis")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2\xa4\xa4".force_encoding("ascii-8bit"),
+ r.read.force_encoding("ascii-8bit"))
+ end)
+ end
+
+ def test_write_conversion_anyenc_stateful_nosync
+ pipe(proc do |w|
+ w.sync = false
+ w.set_encoding("iso-2022-jp")
+ w << "\u3042"
+ w << "\x82\xa2".force_encoding("sjis")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"),
+ r.read.force_encoding("ascii-8bit"))
+ end)
+ end
+
+ def test_read_stateful
+ pipe("euc-jp:iso-2022-jp",
+ proc do |w|
+ w << "\xA4\xA2"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\e$B$\"\e(B".force_encoding("iso-2022-jp"), r.read)
+ end)
+ end
+
+ def test_stdin_external_encoding_with_reopen
+ skip "passing non-stdio fds is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
+ with_tmpdir {
+ open("tst", "w+") {|f|
+ pid = spawn(EnvUtil.rubybin, '-e', <<-'End', 10=>f)
+ io = IO.new(10, "r+")
+ STDIN.reopen(io)
+ STDIN.external_encoding
+ STDIN.write "\u3042"
+ STDIN.flush
+ End
+ Process.wait pid
+ f.rewind
+ result = f.read.force_encoding("ascii-8bit")
+ assert_equal("\u3042".force_encoding("ascii-8bit"), result)
+ }
+ }
+ end
+
+ def test_popen_r_enc
+ IO.popen("#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ end
+
+ def test_popen_r_enc_in_opt
+ IO.popen("#{EnvUtil.rubybin} -e 'putc 255'", "r", encoding: "ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ end
+
+ def test_popen_r_enc_in_opt2
+ IO.popen("#{EnvUtil.rubybin} -e 'putc 255'", "r", external_encoding: "ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ end
+
+ def test_popen_r_enc_enc
+ IO.popen("#{EnvUtil.rubybin} -e 'putc 0xa1'", "r:shift_jis:euc-jp") {|f|
+ assert_equal(Encoding::Shift_JIS, f.external_encoding)
+ assert_equal(Encoding::EUC_JP, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\x8e\xa1".force_encoding("euc-jp"), s)
+ }
+ end
+
+ def test_popen_r_enc_enc_in_opt
+ IO.popen("#{EnvUtil.rubybin} -e 'putc 0xa1'", "r", encoding: "shift_jis:euc-jp") {|f|
+ assert_equal(Encoding::Shift_JIS, f.external_encoding)
+ assert_equal(Encoding::EUC_JP, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\x8e\xa1".force_encoding("euc-jp"), s)
+ }
+ end
+
+ def test_popen_r_enc_enc_in_opt2
+ IO.popen("#{EnvUtil.rubybin} -e 'putc 0xa1'", "r", external_encoding: "shift_jis", internal_encoding: "euc-jp") {|f|
+ assert_equal(Encoding::Shift_JIS, f.external_encoding)
+ assert_equal(Encoding::EUC_JP, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\x8e\xa1".force_encoding("euc-jp"), s)
+ }
+ end
+
+ def test_popenv_r_enc_enc_in_opt2
+ IO.popen([EnvUtil.rubybin, "-e", "putc 0xa1"], "r", external_encoding: "shift_jis", internal_encoding: "euc-jp") {|f|
+ assert_equal(Encoding::Shift_JIS, f.external_encoding)
+ assert_equal(Encoding::EUC_JP, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\x8e\xa1".force_encoding("euc-jp"), s)
+ }
+ end
+
+ def test_open_pipe_r_enc
+ open("|#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ end
+
+ def test_open_pipe_r_enc2
+ open("|#{EnvUtil.rubybin} -e 'putc \"\\u3042\"'", "r:UTF-8") {|f|
+ assert_equal(Encoding::UTF_8, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_equal("\u3042", s)
+ }
+ end
+
+ def test_s_foreach_enc
+ with_tmpdir {
+ generate_file("t", "\xff")
+ IO.foreach("t", :mode => "r:ascii-8bit") {|s|
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_enc_in_opt
+ with_tmpdir {
+ generate_file("t", "\xff")
+ IO.foreach("t", :encoding => "ascii-8bit") {|s|
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_enc_in_opt2
+ with_tmpdir {
+ generate_file("t", "\xff")
+ IO.foreach("t", :external_encoding => "ascii-8bit") {|s|
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_enc_enc
+ with_tmpdir {
+ generate_file("t", "\u3042")
+ IO.foreach("t", :mode => "r:utf-8:euc-jp") {|s|
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_enc_enc_in_opt
+ with_tmpdir {
+ generate_file("t", "\u3042")
+ IO.foreach("t", :mode => "r", :encoding => "utf-8:euc-jp") {|s|
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_enc_enc_in_opt2
+ with_tmpdir {
+ generate_file("t", "\u3042")
+ IO.foreach("t", :mode => "r", :external_encoding => "utf-8", :internal_encoding => "euc-jp") {|s|
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_open_args_enc
+ with_tmpdir {
+ generate_file("t", "\xff")
+ IO.foreach("t", :open_args => ["r:ascii-8bit"]) {|s|
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_open_args_enc_in_opt
+ with_tmpdir {
+ generate_file("t", "\xff")
+ IO.foreach("t", :open_args => ["r", encoding: "ascii-8bit"]) {|s|
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_open_args_enc_in_opt2
+ with_tmpdir {
+ generate_file("t", "\xff")
+ IO.foreach("t", :open_args => ["r", external_encoding: "ascii-8bit"]) {|s|
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xff".force_encoding("ascii-8bit"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_open_args_enc_enc
+ with_tmpdir {
+ generate_file("t", "\u3042")
+ IO.foreach("t", :open_args => ["r:utf-8:euc-jp"]) {|s|
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_open_args_enc_enc_in_opt
+ with_tmpdir {
+ generate_file("t", "\u3042")
+ IO.foreach("t", :open_args => ["r", encoding: "utf-8:euc-jp"]) {|s|
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), s)
+ }
+ }
+ end
+
+ def test_s_foreach_open_args_enc_enc_in_opt2
+ with_tmpdir {
+ generate_file("t", "\u3042")
+ IO.foreach("t", :open_args => ["r", external_encoding: "utf-8", internal_encoding: "euc-jp"]) {|s|
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), s)
+ }
+ }
+ end
+
+ def test_both_textmode_binmode
+ bug5918 = '[ruby-core:42199]'
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "r", :textmode=>true, :binmode=>true) }
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "rt", :binmode=>true) }
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "rt", :binmode=>false) }
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "rb", :textmode=>true) }
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "rb", :textmode=>false) }
+ end
+
+ def test_textmode_decode_universal_newline_read
+ with_tmpdir {
+ generate_file("t.crlf", "a\r\nb\r\nc\r\n")
+ assert_equal("a\nb\nc\n", File.read("t.crlf", mode:"rt:euc-jp:utf-8"))
+ assert_equal("a\nb\nc\n", File.read("t.crlf", mode:"rt"))
+ open("t.crlf", "rt:euc-jp:utf-8") {|f| assert_equal("a\nb\nc\n", f.read) }
+ open("t.crlf", "rt") {|f| assert_equal("a\nb\nc\n", f.read) }
+ open("t.crlf", "r", :textmode=>true) {|f| assert_equal("a\nb\nc\n", f.read) }
+ open("t.crlf", "r", textmode: true, universal_newline: false) {|f|
+ assert_equal("a\r\nb\r\nc\r\n", f.read)
+ }
+
+ generate_file("t.cr", "a\rb\rc\r")
+ assert_equal("a\nb\nc\n", File.read("t.cr", mode:"rt:euc-jp:utf-8"))
+ assert_equal("a\nb\nc\n", File.read("t.cr", mode:"rt"))
+
+ generate_file("t.lf", "a\nb\nc\n")
+ assert_equal("a\nb\nc\n", File.read("t.cr", mode:"rt:euc-jp:utf-8"))
+ assert_equal("a\nb\nc\n", File.read("t.cr", mode:"rt"))
+ }
+ end
+
+ def test_textmode_decode_universal_newline_getc
+ with_tmpdir {
+ generate_file("t.crlf", "a\r\nb\r\nc\r\n")
+ open("t.crlf", "rt") {|f|
+ assert_equal("a", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal("b", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal("c", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal(nil, f.getc)
+ }
+
+ generate_file("t.cr", "a\rb\rc\r")
+ open("t.cr", "rt") {|f|
+ assert_equal("a", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal("b", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal("c", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal(nil, f.getc)
+ }
+
+ generate_file("t.lf", "a\nb\nc\n")
+ open("t.lf", "rt") {|f|
+ assert_equal("a", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal("b", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal("c", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal(nil, f.getc)
+ }
+ }
+ end
+
+ def test_textmode_decode_universal_newline_gets
+ with_tmpdir {
+ generate_file("t.crlf", "a\r\nb\r\nc\r\n")
+ open("t.crlf", "rt") {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+
+ generate_file("t.cr", "a\rb\rc\r")
+ open("t.cr", "rt") {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+
+ generate_file("t.lf", "a\nb\nc\n")
+ open("t.lf", "rt") {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+ }
+ end
+
+ def test_textmode_decode_universal_newline_utf16
+ with_tmpdir {
+ generate_file("t.utf16be.crlf", "\0a\0\r\0\n\0b\0\r\0\n\0c\0\r\0\n")
+ assert_equal("a\nb\nc\n", File.read("t.utf16be.crlf", mode:"rt:utf-16be:utf-8"))
+
+ generate_file("t.utf16le.crlf", "a\0\r\0\n\0b\0\r\0\n\0c\0\r\0\n\0")
+ assert_equal("a\nb\nc\n", File.read("t.utf16le.crlf", mode:"rt:utf-16le:utf-8"))
+
+ generate_file("t.utf16be.cr", "\0a\0\r\0b\0\r\0c\0\r")
+ assert_equal("a\nb\nc\n", File.read("t.utf16be.cr", mode:"rt:utf-16be:utf-8"))
+
+ generate_file("t.utf16le.cr", "a\0\r\0b\0\r\0c\0\r\0")
+ assert_equal("a\nb\nc\n", File.read("t.utf16le.cr", mode:"rt:utf-16le:utf-8"))
+
+ generate_file("t.utf16be.lf", "\0a\0\n\0b\0\n\0c\0\n")
+ assert_equal("a\nb\nc\n", File.read("t.utf16be.lf", mode:"rt:utf-16be:utf-8"))
+
+ generate_file("t.utf16le.lf", "a\0\n\0b\0\n\0c\0\n\0")
+ assert_equal("a\nb\nc\n", File.read("t.utf16le.lf", mode:"rt:utf-16le:utf-8"))
+ }
+ end
+
+ SYSTEM_NEWLINE = []
+ def system_newline
+ return SYSTEM_NEWLINE.first if !SYSTEM_NEWLINE.empty?
+ with_tmpdir {
+ open("newline", "wt") {|f|
+ f.print "\n"
+ }
+ open("newline", "rb") {|f|
+ SYSTEM_NEWLINE << f.read
+ }
+ }
+ SYSTEM_NEWLINE.first
+ end
+
+ def test_textmode_encode_newline
+ with_tmpdir {
+ open("t.txt", "wt") {|f|
+ f.puts "abc"
+ f.puts "def"
+ }
+ content = File.read("t.txt", :mode=>"rb")
+ nl = system_newline
+ assert_equal("abc#{nl}def#{nl}", content)
+ }
+ end
+
+ def test_textmode_encode_newline_enc
+ with_tmpdir {
+ open("t.txt", "wt:euc-jp") {|f|
+ f.puts "abc\u3042"
+ f.puts "def\u3044"
+ }
+ content = File.read("t.txt", :mode=>"rb:ascii-8bit")
+ nl = system_newline
+ assert_equal("abc\xA4\xA2#{nl}def\xA4\xA4#{nl}", content)
+ }
+ end
+
+ def test_read_newline_conversion_with_encoding_conversion
+ with_tmpdir {
+ generate_file("t.utf8.crlf", "a\r\nb\r\n")
+ open("t.utf8.crlf", "rb:utf-8:utf-16be") {|f|
+ content = f.read
+ assert_equal("\0a\0\r\0\n\0b\0\r\0\n".force_encoding("UTF-16BE"), content)
+ }
+ open("t.utf8.crlf", "rt:utf-8:utf-16be") {|f|
+ content = f.read
+ assert_equal("\0a\0\n\0b\0\n".force_encoding("UTF-16BE"), content)
+ }
+ open("t.utf8.crlf", "r:utf-8:utf-16be") {|f|
+ content = f.read
+ if system_newline == "\n"
+ assert_equal("\0a\0\r\0\n\0b\0\r\0\n".force_encoding("UTF-16BE"), content)
+ else
+ assert_equal("\0a\0\n\0b\0\n".force_encoding("UTF-16BE"), content)
+ end
+ }
+ }
+ end
+
+ def test_read_newline_conversion_without_encoding_conversion
+ with_tmpdir {
+ generate_file("t.utf16.crlf", "\0a\0\r\0\n\0b\0\r\0\n")
+ open("t.utf16.crlf", "rb:utf-16be") {|f|
+ content = f.read
+ assert_equal("\0a\0\r\0\n\0b\0\r\0\n".force_encoding("UTF-16BE"),
+ content)
+ }
+ }
+ end
+
+ def test_read_newline_conversion_error
+ with_tmpdir {
+ generate_file("empty.txt", "")
+ # ascii incompatible encoding without conversion needs binmode.
+ assert_raise(ArgumentError) {
+ open("empty.txt", "rt:utf-16be") {|f| }
+ }
+ assert_raise(ArgumentError) {
+ open("empty.txt", "r:utf-16be") {|f| }
+ }
+ }
+ end
+
+ def test_read_mode
+ with_tmpdir {
+ generate_file("t", "a\rb\r\nc\n\xc2\xa2")
+ generate_file("ie", "a\rb\r\nc\n\e$B\x42\x22\e(B")
+ generate_file("iu", "a\rb\r\nc\n\e$B\x21\x71\e(B")
+ generate_file("be", "\0a\0\r\0b\0\r\0\n\0c\0\n\x85\x35")
+ generate_file("bu", "\0a\0\r\0b\0\r\0\n\0c\0\n\0\xa2")
+ # "\xc2\xa2" is valid as EUC-JP and UTF-8
+ # EUC-JP UTF-8 Unicode
+ # 0xC2A2 0xE894B5 U+8535
+ # 0xA1F1 0xC2A2 U+00A2
+
+ open("t","rt") {|f| assert_equal("a\nb\nc\n\xc2\xa2".force_encoding(Encoding.default_external), f.read) }
+ open("t","rb") {|f| assert_equal("a\rb\r\nc\n\xc2\xa2".force_encoding(Encoding::ASCII_8BIT), f.read) }
+
+ open("t","rt:euc-jp") {|f| assert_equal("a\nb\nc\n\xc2\xa2".force_encoding("EUC-JP"), f.read) }
+ open("t","rb:euc-jp") {|f| assert_equal("a\rb\r\nc\n\xc2\xa2".force_encoding("EUC-JP"), f.read) }
+ open("t","rt:utf-8") {|f| assert_equal("a\nb\nc\n\xc2\xa2".force_encoding("UTF-8"), f.read) }
+ open("t","rb:utf-8") {|f| assert_equal("a\rb\r\nc\n\xc2\xa2".force_encoding("UTF-8"), f.read) }
+ assert_raise(ArgumentError) { open("t", "rt:iso-2022-jp") {|f| } }
+ open("t","rb:iso-2022-jp") {|f| assert_equal("a\rb\r\nc\n\xc2\xa2".force_encoding("ISO-2022-JP"), f.read) }
+
+ open("t","rt:euc-jp:utf-8") {|f| assert_equal("a\nb\nc\n\u8535", f.read) }
+ open("t","rt:utf-8:euc-jp") {|f| assert_equal("a\nb\nc\n\xa1\xf1".force_encoding("EUC-JP"), f.read) }
+ open("t","rb:euc-jp:utf-8") {|f| assert_equal("a\rb\r\nc\n\u8535", f.read) }
+ open("t","rb:utf-8:euc-jp") {|f| assert_equal("a\rb\r\nc\n\xa1\xf1".force_encoding("EUC-JP"), f.read) }
+
+ open("t","rt:euc-jp:iso-2022-jp"){|f| assert_equal("a\nb\nc\n\e$B\x42\x22\e(B".force_encoding("ISO-2022-JP"), f.read) }
+ open("t","rt:utf-8:iso-2022-jp"){|f| assert_equal("a\nb\nc\n\e$B\x21\x71\e(B".force_encoding("ISO-2022-JP"), f.read) }
+ open("t","rt:euc-jp:utf-16be"){|f| assert_equal("\0a\0\n\0b\0\n\0c\0\n\x85\x35".force_encoding("UTF-16BE"), f.read) }
+ open("t","rt:utf-8:utf-16be"){|f| assert_equal("\0a\0\n\0b\0\n\0c\0\n\0\xa2".force_encoding("UTF-16BE"), f.read) }
+ open("t","rb:euc-jp:iso-2022-jp"){|f|assert_equal("a\rb\r\nc\n\e$B\x42\x22\e(B".force_encoding("ISO-2022-JP"),f.read)}
+ open("t","rb:utf-8:iso-2022-jp"){|f|assert_equal("a\rb\r\nc\n\e$B\x21\x71\e(B".force_encoding("ISO-2022-JP"),f.read)}
+ open("t","rb:euc-jp:utf-16be"){|f|assert_equal("\0a\0\r\0b\0\r\0\n\0c\0\n\x85\x35".force_encoding("UTF-16BE"),f.read)}
+ open("t","rb:utf-8:utf-16be"){|f|assert_equal("\0a\0\r\0b\0\r\0\n\0c\0\n\0\xa2".force_encoding("UTF-16BE"),f.read)}
+
+ open("ie","rt:iso-2022-jp:euc-jp"){|f| assert_equal("a\nb\nc\n\xc2\xa2".force_encoding("EUC-JP"), f.read) }
+ open("iu","rt:iso-2022-jp:utf-8"){|f| assert_equal("a\nb\nc\n\xc2\xa2".force_encoding("UTF-8"), f.read) }
+ open("be","rt:utf-16be:euc-jp"){|f| assert_equal("a\nb\nc\n\xc2\xa2".force_encoding("EUC-JP"), f.read) }
+ open("bu","rt:utf-16be:utf-8"){|f| assert_equal("a\nb\nc\n\xc2\xa2".force_encoding("UTF-8"), f.read) }
+ open("ie","rb:iso-2022-jp:euc-jp"){|f|assert_equal("a\rb\r\nc\n\xc2\xa2".force_encoding("EUC-JP"),f.read)}
+ open("iu","rb:iso-2022-jp:utf-8"){|f|assert_equal("a\rb\r\nc\n\xc2\xa2".force_encoding("UTF-8"),f.read)}
+ open("be","rb:utf-16be:euc-jp"){|f|assert_equal("a\rb\r\nc\n\xc2\xa2".force_encoding("EUC-JP"),f.read)}
+ open("bu","rb:utf-16be:utf-8"){|f|assert_equal("a\rb\r\nc\n\xc2\xa2".force_encoding("UTF-8"),f.read)}
+
+ open("ie","rt:iso-2022-jp:utf-16be"){|f|assert_equal("\0a\0\n\0b\0\n\0c\0\n\x85\x35".force_encoding("UTF-16BE"),f.read)}
+ open("be","rt:utf-16be:iso-2022-jp"){|f|assert_equal("a\nb\nc\n\e$B\x42\x22\e(B".force_encoding("ISO-2022-JP"),f.read)}
+ open("ie","rb:iso-2022-jp:utf-16be"){|f|assert_equal("\0a\0\r\0b\0\r\0\n\0c\0\n\x85\x35".force_encoding("UTF-16BE"),f.read)}
+ open("be","rb:utf-16be:iso-2022-jp"){|f|assert_equal("a\rb\r\nc\n\e$B\x42\x22\e(B".force_encoding("ISO-2022-JP"),f.read)}
+ }
+ end
+
+ def assert_write(expected, mode, *args)
+ with_tmpdir {
+ open("t", mode) {|f|
+ args.each {|arg| f.print arg }
+ }
+ content = File.read("t", :mode=>"rb:ascii-8bit")
+ assert_equal(expected.dup.force_encoding("ascii-8bit"),
+ content.force_encoding("ascii-8bit"))
+ }
+ end
+
+ def test_write_mode
+ # "\xc2\xa2" is valid as EUC-JP and UTF-8
+ # EUC-JP UTF-8 Unicode
+ # 0xC2A2 0xE894B5 U+8535
+ # 0xA1F1 0xC2A2 U+00A2
+ a = "a\rb\r\nc\n"
+ e = "\xc2\xa2".force_encoding("euc-jp")
+ u8 = "\xc2\xa2".force_encoding("utf-8")
+ u16 = "\x85\x35\0\r\x00\xa2\0\r\0\n\0\n".force_encoding("utf-16be")
+ i = "\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r\n\n".force_encoding("iso-2022-jp")
+ n = system_newline
+ n.encode("utf-16be").force_encoding("ascii-8bit")
+
+ assert_write("a\rb\r#{n}c#{n}", "wt", a)
+ assert_write("\xc2\xa2", "wt", e)
+ assert_write("\xc2\xa2", "wt", u8)
+
+ assert_write("a\rb\r\nc\n", "wb", a)
+ assert_write("\xc2\xa2", "wb", e)
+ assert_write("\xc2\xa2", "wb", u8)
+
+ #assert_write("\x85\x35\0\r\x00\xa2\0\r\0\n\0\n", "wt", u16) should raise
+ #assert_write("\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r\n\n", "wt", i) should raise
+ assert_write("\x85\x35\0\r\x00\xa2\0\r\0\n\0\n", "wb", u16)
+ assert_write("\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r\n\n", "wb", i)
+
+ t_write_mode_enc
+ t_write_mode_enc(":utf-8")
+ end
+
+ def t_write_mode_enc(enc="")
+ # "\xc2\xa2" is valid as EUC-JP and UTF-8
+ # EUC-JP UTF-8 Unicode
+ # 0xC2A2 0xE894B5 U+8535
+ # 0xA1F1 0xC2A2 U+00A2
+ a = "a\rb\r\nc\n"
+ e = "\xc2\xa2".force_encoding("euc-jp")
+ u8 = "\xc2\xa2".force_encoding("utf-8")
+ u16 = "\x85\x35\0\r\x00\xa2\0\r\0\n\0\n".force_encoding("utf-16be")
+ i = "\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r\n\n".force_encoding("iso-2022-jp")
+ n = system_newline
+ un = n.encode("utf-16be").force_encoding("ascii-8bit")
+
+ assert_write("a\rb\r#{n}c#{n}", "wt:euc-jp#{enc}", a)
+ assert_write("\xc2\xa2", "wt:euc-jp#{enc}", e)
+ assert_write("\xa1\xf1", "wt:euc-jp#{enc}", u8)
+
+ assert_write("a\rb\r\nc\n", "wb:euc-jp#{enc}", a)
+ assert_write("\xc2\xa2", "wb:euc-jp#{enc}", e)
+ assert_write("\xa1\xf1", "wb:euc-jp#{enc}", u8)
+
+ assert_write("\xc2\xa2\r\xa1\xf1\r#{n}#{n}", "wt:euc-jp#{enc}", u16)
+ assert_write("\xc2\xa2\r\xa1\xf1\r#{n}#{n}", "wt:euc-jp#{enc}", i)
+ assert_write("\xc2\xa2\r\xa1\xf1\r\n\n", "wb:euc-jp#{enc}", u16)
+ assert_write("\xc2\xa2\r\xa1\xf1\r\n\n", "wb:euc-jp#{enc}", i)
+
+ assert_write("\0a\0\r\0b\0\r#{un}\0c#{un}", "wt:utf-16be#{enc}", a)
+ assert_write("\x85\x35", "wt:utf-16be#{enc}", e)
+ assert_write("\x00\xa2", "wt:utf-16be#{enc}", u8)
+ assert_write("a\rb\r#{n}c#{n}", "wt:iso-2022-jp#{enc}", a)
+ assert_write("\e$B\x42\x22\e(B", "wt:iso-2022-jp#{enc}", e)
+ assert_write("\e$B\x21\x71\e(B", "wt:iso-2022-jp#{enc}", u8)
+
+ assert_write("\0a\0\r\0b\0\r\0\n\0c\0\n", "wb:utf-16be#{enc}", a)
+ assert_write("\x85\x35", "wb:utf-16be#{enc}", e)
+ assert_write("\x00\xa2", "wb:utf-16be#{enc}", u8)
+ assert_write("a\rb\r\nc\n", "wb:iso-2022-jp#{enc}", a)
+ assert_write("\e$B\x42\x22\e(B", "wb:iso-2022-jp#{enc}", e)
+ assert_write("\e$B\x21\x71\e(B", "wb:iso-2022-jp#{enc}", u8)
+
+ assert_write("\x85\x35\0\r\x00\xa2\0\r#{un}#{un}", "wt:utf-16be#{enc}", u16)
+ assert_write("\x85\x35\0\r\x00\xa2\0\r#{un}#{un}", "wt:utf-16be#{enc}", i)
+ assert_write("\x85\x35\0\r\x00\xa2\0\r\0\n\0\n", "wb:utf-16be#{enc}", u16)
+ assert_write("\x85\x35\0\r\x00\xa2\0\r\0\n\0\n", "wb:utf-16be#{enc}", i)
+ assert_write("\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r#{n}#{n}", "wt:iso-2022-jp#{enc}", u16)
+ assert_write("\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r#{n}#{n}", "wt:iso-2022-jp#{enc}", i)
+ assert_write("\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r\n\n", "wb:iso-2022-jp#{enc}", u16)
+ assert_write("\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r\n\n", "wb:iso-2022-jp#{enc}", i)
+ end
+
+ def test_write_mode_fail
+ return if system_newline == "\n"
+ with_tmpdir {
+ open("t", "wt") {|f|
+ assert_raise(ArgumentError) { f.print "\0\r\0\r\0\n\0\n".force_encoding("utf-16be") }
+ }
+ }
+ end
+
+ def test_write_ascii_incompat
+ with_tmpdir {
+ open("t.utf8", "wb:utf-8:utf-16be") {|f| }
+ open("t.utf8", "wt:utf-8:utf-16be") {|f| }
+ open("t.utf8", "w:utf-8:utf-16be") {|f| }
+ open("t.utf16", "wb:utf-16be") {|f| }
+ open("t.utf16", "wt:utf-16be") {|f| }
+ open("t.utf16", "w:utf-16be") {|f| }
+ }
+ end
+
+ def test_binmode_write_ascii_incompat_internal
+ with_tmpdir {
+ open("t.utf8.lf", "wb:utf-8:utf-16be") {|f|
+ f.print "\0a\0\n\0b\0\n".force_encoding("UTF-16BE")
+ }
+ content = File.read("t.utf8.lf", :mode=>"rb:ascii-8bit")
+ assert_equal("a\nb\n", content)
+
+ open("t.utf8.lf", "wb:utf-16be") {|f|
+ f.print "\0a\0\n\0b\0\n".force_encoding("UTF-16BE")
+ }
+ content = File.read("t.utf8.lf", :mode=>"rb:ascii-8bit")
+ assert_equal("\0a\0\n\0b\0\n", content)
+ }
+ end
+
+ def test_binary
+ with_tmpdir {
+ src = "a\nb\rc\r\nd\n"
+ generate_file("t.txt", src)
+ open("t.txt", "rb") {|f|
+ assert_equal(src, f.read)
+ }
+ open("t.txt", "r", :binmode=>true) {|f|
+ assert_equal(src, f.read)
+ }
+ if system_newline == "\n"
+ open("t.txt", "r") {|f|
+ assert_equal(src, f.read)
+ }
+ end
+ }
+ end
+
+ def test_binmode
+ with_tmpdir {
+ src = "a\r\nb\r\nc\r\n"
+ generate_file("t.txt", src)
+ open("t.txt", "rt") {|f|
+ assert_equal("a", f.getc)
+ assert_equal("\n", f.getc)
+ f.binmode
+ assert_equal("b", f.getc)
+ assert_equal("\r", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal("c", f.getc)
+ assert_equal("\r", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal(nil, f.getc)
+ }
+ }
+ end
+
+ def test_binmode2
+ with_tmpdir {
+ src = "a\r\nb\r\nc\r\n"
+ generate_file("t.txt", src)
+ open("t.txt", "rt:euc-jp:utf-8") {|f|
+ assert_equal("a", f.getc)
+ assert_equal("\n", f.getc)
+ f.binmode
+ assert_equal("b", f.getc)
+ assert_equal("\r", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal("c", f.getc)
+ assert_equal("\r", f.getc)
+ assert_equal("\n", f.getc)
+ assert_equal(nil, f.getc)
+ }
+ }
+ end
+
+ def test_binmode3
+ with_tmpdir {
+ src = "\u3042\r\n"
+ generate_file("t.txt", src)
+ srcbin = src.dup.force_encoding("ascii-8bit")
+ open("t.txt", "rt:utf-8:euc-jp") {|f|
+ f.binmode
+ result = f.read
+ assert_str_equal(srcbin, result)
+ assert_equal(Encoding::ASCII_8BIT, result.encoding)
+ }
+ }
+ end
+
+ def test_invalid_r
+ with_tmpdir {
+ generate_file("t.txt", "a\x80b")
+ open("t.txt", "r:utf-8:euc-jp", :invalid => :replace) {|f|
+ assert_equal("a?b", f.read)
+ }
+ open("t.txt", "r:utf-8:euc-jp", :invalid => :replace, :replace => "") {|f|
+ assert_equal("ab", f.read)
+ }
+ open("t.txt", "r:utf-8:euc-jp", :undef => :replace) {|f|
+ assert_raise(Encoding::InvalidByteSequenceError) { f.read }
+ assert_equal("b", f.read)
+ }
+ open("t.txt", "r:utf-8:euc-jp", :undef => :replace, :replace => "") {|f|
+ assert_raise(Encoding::InvalidByteSequenceError) { f.read }
+ assert_equal("b", f.read)
+ }
+ }
+ end
+
+ def test_undef_r
+ with_tmpdir {
+ generate_file("t.txt", "a\uFFFDb")
+ open("t.txt", "r:utf-8:euc-jp", :undef => :replace) {|f|
+ assert_equal("a?b", f.read)
+ }
+ open("t.txt", "r:utf-8:euc-jp", :undef => :replace, :replace => "") {|f|
+ assert_equal("ab", f.read)
+ }
+ open("t.txt", "r:utf-8:euc-jp", :invalid => :replace) {|f|
+ assert_raise(Encoding::UndefinedConversionError) { f.read }
+ assert_equal("b", f.read)
+ }
+ open("t.txt", "r:utf-8:euc-jp", :invalid => :replace, :replace => "") {|f|
+ assert_raise(Encoding::UndefinedConversionError) { f.read }
+ assert_equal("b", f.read)
+ }
+ }
+ end
+
+ def test_invalid_w
+ with_tmpdir {
+ invalid_utf8 = "a\x80b".force_encoding("utf-8")
+ open("t.txt", "w:euc-jp", :invalid => :replace) {|f|
+ assert_nothing_raised { f.write invalid_utf8 }
+ }
+ assert_equal("a?b", File.read("t.txt"))
+
+ open("t.txt", "w:euc-jp", :invalid => :replace, :replace => "") {|f|
+ assert_nothing_raised { f.write invalid_utf8 }
+ }
+ assert_equal("ab", File.read("t.txt"))
+
+ open("t.txt", "w:euc-jp", :undef => :replace) {|f|
+ assert_raise(Encoding::InvalidByteSequenceError) { f.write invalid_utf8 }
+ }
+ open("t.txt", "w:euc-jp", :undef => :replace, :replace => "") {|f|
+ assert_raise(Encoding::InvalidByteSequenceError) { f.write invalid_utf8 }
+ }
+ }
+ end
+
+ def test_undef_w_stateless
+ with_tmpdir {
+ generate_file("t.txt", "a\uFFFDb")
+ open("t.txt", "w:euc-jp:utf-8", :undef => :replace) {|f|
+ assert_nothing_raised { f.write "a\uFFFDb" }
+ }
+ assert_equal("a?b", File.read("t.txt"))
+ open("t.txt", "w:euc-jp:utf-8", :undef => :replace, :replace => "") {|f|
+ assert_nothing_raised { f.write "a\uFFFDb" }
+ }
+ assert_equal("ab", File.read("t.txt"))
+ open("t.txt", "w:euc-jp:utf-8", :invalid => :replace) {|f|
+ assert_raise(Encoding::UndefinedConversionError) { f.write "a\uFFFDb" }
+ }
+ open("t.txt", "w:euc-jp:utf-8", :invalid => :replace, :replace => "") {|f|
+ assert_raise(Encoding::UndefinedConversionError) { f.write "a\uFFFDb" }
+ }
+ }
+ end
+
+ def test_undef_w_stateful
+ with_tmpdir {
+ generate_file("t.txt", "a\uFFFDb")
+ open("t.txt", "w:iso-2022-jp:utf-8", :undef => :replace) {|f|
+ assert_nothing_raised { f.write "a\uFFFDb" }
+ }
+ assert_equal("a?b", File.read("t.txt"))
+ open("t.txt", "w:iso-2022-jp:utf-8", :undef => :replace, :replace => "") {|f|
+ assert_nothing_raised { f.write "a\uFFFDb" }
+ }
+ assert_equal("ab", File.read("t.txt"))
+ open("t.txt", "w:iso-2022-jp:utf-8", :invalid => :replace) {|f|
+ assert_raise(Encoding::UndefinedConversionError) { f.write "a\uFFFDb" }
+ }
+ open("t.txt", "w:iso-2022-jp:utf-8", :invalid => :replace, :replace => "") {|f|
+ assert_raise(Encoding::UndefinedConversionError) { f.write "a\uFFFDb" }
+ }
+ }
+ end
+
+ def test_w_xml_attr
+ with_tmpdir {
+ open("raw.txt", "wb", xml: :attr) {|f| f.print '&<>"\''; f.puts "\u4E02\u3042" }
+ content = File.read("raw.txt", :mode=>"rb:ascii-8bit")
+ assert_equal("\"&amp;&lt;&gt;&quot;'\u4E02\u3042\n\"".force_encoding("ascii-8bit"), content)
+
+ open("ascii.txt", "wb:us-ascii", xml: :attr) {|f| f.print '&<>"\''; f.puts "\u4E02\u3042" }
+ content = File.read("ascii.txt", :mode=>"rb:ascii-8bit")
+ assert_equal("\"&amp;&lt;&gt;&quot;'&#x4E02;&#x3042;\n\"".force_encoding("ascii-8bit"), content)
+
+ open("iso-2022-jp.txt", "wb:iso-2022-jp", xml: :attr) {|f| f.print '&<>"\''; f.puts "\u4E02\u3042" }
+ content = File.read("iso-2022-jp.txt", :mode=>"rb:ascii-8bit")
+ assert_equal("\"&amp;&lt;&gt;&quot;'&#x4E02;\e$B$\"\e(B\n\"".force_encoding("ascii-8bit"), content)
+
+ open("utf-16be.txt", "wb:utf-16be", xml: :attr) {|f| f.print '&<>"\''; f.puts "\u4E02\u3042" }
+ content = File.read("utf-16be.txt", :mode=>"rb:ascii-8bit")
+ assert_equal("\0\"\0&\0a\0m\0p\0;\0&\0l\0t\0;\0&\0g\0t\0;\0&\0q\0u\0o\0t\0;\0'\x4E\x02\x30\x42\0\n\0\"".force_encoding("ascii-8bit"), content)
+
+ open("eucjp.txt", "w:euc-jp:utf-8", xml: :attr) {|f|
+ f.print "\u4E02" # U+4E02 is 0x3021 in JIS X 0212
+ }
+ content = File.read("eucjp.txt", :mode=>"rb:ascii-8bit")
+ assert_equal("\"\x8F\xB0\xA1\"".force_encoding("ascii-8bit"), content)
+
+ open("sjis.txt", "w:sjis:utf-8", xml: :attr) {|f|
+ f.print "\u4E02" # U+4E02 is 0x3021 in JIS X 0212
+ }
+ content = File.read("sjis.txt", :mode=>"rb:ascii-8bit")
+ assert_equal("\"&#x4E02;\"".force_encoding("ascii-8bit"), content)
+
+ open("iso-2022-jp.txt", "w:iso-2022-jp:utf-8", xml: :attr) {|f|
+ f.print "\u4E02" # U+4E02 is 0x3021 in JIS X 0212
+ }
+ content = File.read("iso-2022-jp.txt", :mode=>"rb:ascii-8bit")
+ assert_equal("\"&#x4E02;\"".force_encoding("ascii-8bit"), content)
+ }
+ end
+
+ def test_strip_bom
+ with_tmpdir {
+ text = "\uFEFFa"
+ stripped = "a"
+ %w/UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE/.each do |name|
+ path = '%s-bom.txt' % name
+ content = text.encode(name)
+ generate_file(path, content)
+ result = File.read(path, mode: 'rb:BOM|UTF-8')
+ assert_equal(content[1].force_encoding("ascii-8bit"),
+ result.force_encoding("ascii-8bit"))
+ result = File.read(path, mode: 'rb:BOM|UTF-8:UTF-8')
+ assert_equal(Encoding::UTF_8, result.encoding)
+ assert_equal(stripped, result)
+ end
+
+ bug3407 = '[ruby-core:30641]'
+ path = 'UTF-8-bom.txt'
+ result = File.read(path, encoding: 'BOM|UTF-8')
+ assert_equal("a", result.force_encoding("ascii-8bit"), bug3407)
+
+ bug8323 = '[ruby-core:54563] [Bug #8323]'
+ expected = "a\xff".force_encoding("utf-8")
+ open(path, 'ab') {|f| f.write("\xff")}
+ result = File.read(path, encoding: 'BOM|UTF-8')
+ assert_not_predicate(result, :valid_encoding?, bug8323)
+ assert_equal(expected, result, bug8323)
+ result = File.read(path, encoding: 'BOM|UTF-8:UTF-8')
+ assert_not_predicate(result, :valid_encoding?, bug8323)
+ assert_equal(expected, result, bug8323)
+
+ path = 'ascii.txt'
+ generate_file(path, stripped)
+ result = File.read(path, encoding: 'BOM|UTF-8')
+ assert_equal(stripped, result, bug8323)
+ result = File.read(path, encoding: 'BOM|UTF-8:UTF-8')
+ assert_equal(stripped, result, bug8323)
+ }
+ end
+
+ def test_cbuf
+ with_tmpdir {
+ fn = "tst"
+ open(fn, "w") {|f| f.print "foo" }
+ open(fn, "r+t") {|f|
+ f.ungetc(f.getc)
+ assert_raise(IOError, "[ruby-dev:40493]") { f.readpartial(2) }
+ assert_raise(IOError) { f.read(2) }
+ assert_raise(IOError) { f.each_byte {|c| } }
+ assert_raise(IOError) { f.getbyte }
+ assert_raise(IOError) { f.ungetbyte(0) }
+ assert_raise(IOError) { f.sysread(2) }
+ assert_raise(IOError) { IO.copy_stream(f, "tmpout") }
+ assert_raise(IOError) { f.sysseek(2) }
+ }
+ open(fn, "r+t") {|f|
+ f.ungetc(f.getc)
+ assert_equal("foo", f.read)
+ }
+ }
+ end
+
+ def test_text_mode_ungetc_eof
+ with_tmpdir {
+ open("ff", "w") {|f| }
+ open("ff", "rt") {|f|
+ f.ungetc "a"
+ assert_not_predicate(f, :eof?, "[ruby-dev:40506] (3)")
+ }
+ }
+ end
+
+ def test_cbuf_select
+ pipe("US-ASCII:UTF-8", { :universal_newline => true },
+ proc do |w|
+ w << "\r\n"
+ end,
+ proc do |r|
+ r.ungetc(r.getc)
+ assert_equal([[r],[],[]], IO.select([r], nil, nil, 1))
+ end)
+ end
+
+ def test_textmode_paragraphmode
+ pipe("US-ASCII:UTF-8", { :universal_newline => true },
+ proc do |w|
+ w << "a\n\n\nc".gsub(/\n/, "\r\n")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("a\n\n", r.gets(""))
+ assert_equal("c", r.gets(""), "[ruby-core:23723] (18)")
+ end)
+ end
+
+ def test_textmode_paragraph_binaryread
+ pipe("US-ASCII:UTF-8", { :universal_newline => true },
+ proc do |w|
+ w << "a\n\n\ncdefgh".gsub(/\n/, "\r\n")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("a\n\n", r.gets(""))
+ assert_equal("c", r.getc)
+ assert_equal("defgh", r.readpartial(10))
+ end)
+ end
+
+ def test_textmode_paragraph_nonasciicompat
+ bug3534 = ['[ruby-dev:41803]', '[Bug #3534]']
+ IO.pipe {|r, w|
+ [Encoding::UTF_32BE, Encoding::UTF_32LE,
+ Encoding::UTF_16BE, Encoding::UTF_16LE,
+ Encoding::UTF_8].each do |e|
+ r.set_encoding(Encoding::US_ASCII, e)
+ wthr = Thread.new{ w.print(bug3534[0], "\n\n\n\n", bug3534[1], "\n") }
+ assert_equal((bug3534[0]+"\n\n").encode(e), r.gets(""), bug3534[0])
+ assert_equal((bug3534[1]+"\n").encode(e), r.gets(), bug3534[1])
+ wthr.join
+ end
+ }
+ end
+
+ def test_binmode_paragraph_nonasciicompat
+ bug3534 = ['[ruby-dev:41803]', '[Bug #3534]']
+ IO.pipe {|r, w|
+ r.binmode
+ w.binmode
+ [Encoding::UTF_32BE, Encoding::UTF_32LE,
+ Encoding::UTF_16BE, Encoding::UTF_16LE,
+ Encoding::UTF_8].each do |e|
+ r.set_encoding(Encoding::US_ASCII, e)
+ wthr = Thread.new{ w.print(bug3534[0], "\n\n\n\n", bug3534[1], "\n") }
+ assert_equal((bug3534[0]+"\n\n").encode(e), r.gets(""), bug3534[0])
+ assert_equal((bug3534[1]+"\n").encode(e), r.gets(), bug3534[1])
+ wthr.join
+ end
+ }
+ end
+
+ def test_puts_widechar
+ bug = '[ruby-dev:42212]'
+ pipe(Encoding::ASCII_8BIT,
+ proc do |w|
+ w.binmode
+ w.puts(0x010a.chr(Encoding::UTF_32BE))
+ w.puts(0x010a.chr(Encoding::UTF_16BE))
+ w.puts(0x0a010000.chr(Encoding::UTF_32LE))
+ w.puts(0x0a01.chr(Encoding::UTF_16LE))
+ w.close
+ end,
+ proc do |r|
+ r.binmode
+ assert_equal("\x00\x00\x01\x0a\n", r.read(5), bug)
+ assert_equal("\x01\x0a\n", r.read(3), bug)
+ assert_equal("\x00\x00\x01\x0a\n", r.read(5), bug)
+ assert_equal("\x01\x0a\n", r.read(3), bug)
+ assert_equal("", r.read, bug)
+ r.close
+ end)
+ end
+
+ def test_getc_ascii_only
+ bug4557 = '[ruby-core:35630]'
+ c = with_tmpdir {
+ open("a", "wb") {|f| f.puts "a"}
+ open("a", "rt") {|f| f.getc}
+ }
+ assert_predicate(c, :ascii_only?, bug4557)
+ end
+
+ def test_getc_conversion
+ bug8516 = '[ruby-core:55444] [Bug #8516]'
+ c = with_tmpdir {
+ open("a", "wb") {|f| f.putc "\xe1"}
+ open("a", "r:iso-8859-1:utf-8") {|f| f.getc}
+ }
+ assert_not_predicate(c, :ascii_only?, bug8516)
+ assert_equal(1, c.size, bug8516)
+ end
+
+ def test_default_mode_on_dosish
+ with_tmpdir {
+ open("a", "w") {|f| f.write "\n"}
+ assert_equal("\r\n", IO.binread("a"))
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_default_mode_on_unix
+ with_tmpdir {
+ open("a", "w") {|f| f.write "\n"}
+ assert_equal("\n", IO.binread("a"))
+ }
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_text_mode
+ with_tmpdir {
+ open("a", "wb") {|f| f.write "\r\n"}
+ assert_equal("\n", open("a", "rt"){|f| f.read})
+ }
+ end
+
+ def test_binary_mode
+ with_tmpdir {
+ open("a", "wb") {|f| f.write "\r\n"}
+ assert_equal("\r\n", open("a", "rb"){|f| f.read})
+ }
+ end
+
+ def test_default_stdout_stderr_mode
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, in: in_r, out: out_w, err: out_w)
+ in_r.close
+ out_w.close
+ in_w.write <<-EOS
+ STDOUT.puts "abc"
+ STDOUT.flush
+ STDERR.puts "def"
+ STDERR.flush
+ EOS
+ in_w.close
+ Process.wait pid
+ assert_equal "abc\r\ndef\r\n", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_cr_decorator_on_stdout
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, in: in_r, out: out_w)
+ in_r.close
+ out_w.close
+ in_w.write <<-EOS
+ STDOUT.set_encoding('locale', nil, newline: :cr)
+ STDOUT.puts "abc"
+ STDOUT.flush
+ EOS
+ in_w.close
+ Process.wait pid
+ assert_equal "abc\r", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end
+
+ def test_lf_decorator_on_stdout
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, in: in_r, out: out_w)
+ in_r.close
+ out_w.close
+ in_w.write <<-EOS
+ STDOUT.set_encoding('locale', nil, newline: :lf)
+ STDOUT.puts "abc"
+ STDOUT.flush
+ EOS
+ in_w.close
+ Process.wait pid
+ assert_equal "abc\n", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end
+
+ def test_crlf_decorator_on_stdout
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, in: in_r, out: out_w)
+ in_r.close
+ out_w.close
+ in_w.write <<-EOS
+ STDOUT.set_encoding('locale', nil, newline: :crlf)
+ STDOUT.puts "abc"
+ STDOUT.flush
+ EOS
+ in_w.close
+ Process.wait pid
+ assert_equal "abc\r\n", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end
+
+ def test_binmode_with_pipe
+ with_pipe do |r, w|
+ src = "a\r\nb\r\nc\r\n"
+ w.binmode.write src
+ w.close
+
+ assert_equal("a", r.getc)
+ assert_equal("\n", r.getc)
+ r.binmode
+ assert_equal("b", r.getc)
+ assert_equal("\r", r.getc)
+ assert_equal("\n", r.getc)
+ assert_equal("c", r.getc)
+ assert_equal("\r", r.getc)
+ assert_equal("\n", r.getc)
+ assert_equal(nil, r.getc)
+ r.close
+ end
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_stdin_binmode
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, '-e', <<-'End', in: in_r, out: out_w)
+ STDOUT.binmode
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDIN.binmode
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ End
+ in_r.close
+ out_w.close
+ src = "a\r\nb\r\nc\r\n"
+ in_w.binmode.write src
+ in_w.close
+ Process.wait pid
+ assert_equal "a\nb\r\nc\r\n", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_length
+ with_tmpdir {
+ str = "a\nb"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal(str, f.read(3))
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_length_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ # read with length should be binary mode
+ assert_equal("a\r\n", f.read(3)) # binary
+ assert_equal("b\nc\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_gets_and_read_with_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\n\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("a\n", f.gets) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c\r\n", f.read(3)) # binary
+ assert_equal("\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_getc_and_read_with_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\n\n\r\n\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c\n\n\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_binmode_and_gets
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ open("tmp", "wb") { |f| f.write str }
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c\n", f.gets) # text
+ assert_equal("\n", f.gets) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_binmode_and_getc
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ open("tmp", "wb") { |f| f.write str }
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_write_with_binmode
+ with_tmpdir {
+ str = "a\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r+") do |f|
+ assert_equal("a\r\n", f.read(3)) # binary
+ f.write("b\n\n"); # text
+ f.rewind
+ assert_equal("a\nb\n\n", f.read) # text
+ f.rewind
+ assert_equal("a\r\nb\r\n\r\n", f.binmode.read) # binary
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_seek_with_setting_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n\n\n\n\n\n\n\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("a\n", f.gets) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_error_nonascii
+ bug6071 = '[ruby-dev:45279]'
+ paths = ["\u{3042}".encode("sjis"), "\u{ff}".encode("iso-8859-1")]
+ encs = with_tmpdir {
+ paths.map {|path|
+ open(path) rescue $!.message.encoding
+ }
+ }
+ assert_equal(paths.map(&:encoding), encs, bug6071)
+ end
+
+ def test_inspect_nonascii
+ bug6072 = '[ruby-dev:45280]'
+ paths = ["\u{3042}".encode("sjis"), "\u{ff}".encode("iso-8859-1")]
+ encs = with_tmpdir {
+ paths.map {|path|
+ open(path, "wb") {|f| f.inspect.encoding}
+ }
+ }
+ assert_equal(paths.map(&:encoding), encs, bug6072)
+ end
+
+ def test_pos_dont_move_cursor_position
+ bug6179 = '[ruby-core:43497]'
+ with_tmpdir {
+ str = "line one\r\nline two\r\nline three\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("line one\n", f.readline)
+ assert_equal(10, f.pos, bug6179)
+ assert_equal("line two\n", f.readline, bug6179)
+ assert_equal(20, f.pos, bug6179)
+ assert_equal("line three\n", f.readline, bug6179)
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_pos_with_buffer_end_cr
+ bug6401 = '[ruby-core:44874]'
+ with_tmpdir {
+ # Read buffer size is 8191. This generates '\r' at 8191.
+ lines = ["X" * 8187, "X"]
+ generate_file("tmp", lines.join("\r\n") + "\r\n")
+
+ open("tmp", "r") do |f|
+ lines.each do |line|
+ f.pos
+ assert_equal(line, f.readline.chomp, bug6401)
+ end
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_crlf_and_eof
+ bug6271 = '[ruby-core:44189]'
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ i = 0
+ until f.eof?
+ assert_equal(str[i], f.read(1), bug6271)
+ i += 1
+ end
+ assert_equal(str.size, i, bug6271)
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_buf_broken_ascii_only
+ a, b = IO.pipe
+ a.binmode
+ b.binmode
+ b.write("\xE2\x9C\x93")
+ b.close
+
+ buf = "".force_encoding("binary")
+ assert buf.ascii_only?, "should have been ascii_only?"
+ a.read(1, buf)
+ assert !buf.ascii_only?, "should not have been ascii_only?"
+ ensure
+ a.close rescue nil
+ b.close rescue nil
+ end
+end
diff --git a/jni/ruby/test/ruby/test_iseq.rb b/jni/ruby/test/ruby/test_iseq.rb
new file mode 100644
index 0000000..686646d
--- /dev/null
+++ b/jni/ruby/test/ruby/test_iseq.rb
@@ -0,0 +1,144 @@
+require 'test/unit'
+
+class TestISeq < Test::Unit::TestCase
+ ISeq = RubyVM::InstructionSequence
+
+ def test_no_linenum
+ bug5894 = '[ruby-dev:45130]'
+ assert_normal_exit('p RubyVM::InstructionSequence.compile("1", "mac", "", 0).to_a', bug5894)
+ end
+
+ def lines src
+ body = RubyVM::InstructionSequence.new(src).to_a[13]
+ body.find_all{|e| e.kind_of? Fixnum}
+ end
+
+ def test_to_a_lines
+ src = <<-EOS
+ p __LINE__ # 1
+ p __LINE__ # 2
+ # 3
+ p __LINE__ # 4
+ EOS
+ assert_equal [1, 2, 4], lines(src)
+
+ src = <<-EOS
+ # 1
+ p __LINE__ # 2
+ # 3
+ p __LINE__ # 4
+ # 5
+ EOS
+ assert_equal [2, 4], lines(src)
+
+ src = <<-EOS
+ 1 # should be optimized out
+ 2 # should be optimized out
+ p __LINE__ # 3
+ p __LINE__ # 4
+ 5 # should be optimized out
+ 6 # should be optimized out
+ p __LINE__ # 7
+ 8 # should be optimized out
+ 9
+ EOS
+ assert_equal [3, 4, 7, 9], lines(src)
+ end
+
+ def test_unsupport_type
+ ary = RubyVM::InstructionSequence.compile("p").to_a
+ ary[9] = :foobar
+ assert_raise_with_message(TypeError, /:foobar/) {RubyVM::InstructionSequence.load(ary)}
+ end if defined?(RubyVM::InstructionSequence.load)
+
+ def test_disasm_encoding
+ src = "\u{3042} = 1; \u{3042}; \u{3043}"
+ asm = RubyVM::InstructionSequence.compile(src).disasm
+ assert_equal(src.encoding, asm.encoding)
+ assert_predicate(asm, :valid_encoding?)
+ src.encode!(Encoding::Shift_JIS)
+ asm = RubyVM::InstructionSequence.compile(src).disasm
+ assert_equal(src.encoding, asm.encoding)
+ assert_predicate(asm, :valid_encoding?)
+ end
+
+ LINE_BEFORE_METHOD = __LINE__
+ def method_test_line_trace
+
+ a = 1
+
+ b = 2
+
+ end
+
+ def test_line_trace
+ iseq = ISeq.compile \
+ %q{ a = 1
+ b = 2
+ c = 3
+ # d = 4
+ e = 5
+ # f = 6
+ g = 7
+
+ }
+ assert_equal([1, 2, 3, 5, 7], iseq.line_trace_all)
+ iseq.line_trace_specify(1, true) # line 2
+ iseq.line_trace_specify(3, true) # line 5
+
+ result = []
+ TracePoint.new(:specified_line){|tp|
+ result << tp.lineno
+ }.enable{
+ iseq.eval
+ }
+ assert_equal([2, 5], result)
+
+ iseq = ISeq.of(self.class.instance_method(:method_test_line_trace))
+ assert_equal([LINE_BEFORE_METHOD + 3, LINE_BEFORE_METHOD + 5], iseq.line_trace_all)
+ end if false # TODO: now, it is only for C APIs.
+
+ LINE_OF_HERE = __LINE__
+ def test_location
+ iseq = ISeq.of(method(:test_location))
+
+ assert_equal(__FILE__, iseq.path)
+ assert_match(/#{__FILE__}/, iseq.absolute_path)
+ assert_equal("test_location", iseq.label)
+ assert_equal("test_location", iseq.base_label)
+ assert_equal(LINE_OF_HERE+1, iseq.first_lineno)
+
+ line = __LINE__
+ iseq = ISeq.of(Proc.new{})
+ assert_equal(__FILE__, iseq.path)
+ assert_match(/#{__FILE__}/, iseq.absolute_path)
+ assert_equal("test_location", iseq.base_label)
+ assert_equal("block in test_location", iseq.label)
+ assert_equal(line+1, iseq.first_lineno)
+ end
+
+ def test_label_fstring
+ c = Class.new{ def foobar() end }
+
+ a, b = eval("# encoding: us-ascii\n'foobar'.freeze"),
+ ISeq.of(c.instance_method(:foobar)).label
+ assert_same a, b
+ end
+
+ def test_disable_opt
+ src = "a['foo'] = a['bar']; 'a'.freeze"
+ _,_,_,_,_,_,_,_,_,_,_,_,_,body= RubyVM::InstructionSequence.compile(src, __FILE__, __FILE__, __LINE__, false).to_a
+ body.each{|insn|
+ next if Integer === insn
+ op = insn.first
+ assert(!op.to_s.match(/^opt_/), "#{op}")
+ }
+ end
+
+ def test_invalid_source
+ bug11159 = '[ruby-core:69219] [Bug #11159]'
+ assert_raise(TypeError, bug11159) {ISeq.compile(nil)}
+ assert_raise(TypeError, bug11159) {ISeq.compile(:foo)}
+ assert_raise(TypeError, bug11159) {ISeq.compile(1)}
+ end
+end
diff --git a/jni/ruby/test/ruby/test_iterator.rb b/jni/ruby/test/ruby/test_iterator.rb
new file mode 100644
index 0000000..34652db
--- /dev/null
+++ b/jni/ruby/test/ruby/test_iterator.rb
@@ -0,0 +1,497 @@
+require 'test/unit'
+
+class Array
+ def iter_test1
+ collect{|e| [e, yield(e)]}.sort{|a,b|a[1]<=>b[1]}
+ end
+ def iter_test2
+ ary = collect{|e| [e, yield(e)]}
+ ary.sort{|a,b|a[1]<=>b[1]}
+ end
+end
+
+class TestIterator < Test::Unit::TestCase
+ def ttt
+ assert(iterator?)
+ end
+
+ def test_iterator
+ assert(!iterator?)
+
+ ttt{}
+
+ # yield at top level !! here's not toplevel
+ assert(!defined?(yield))
+ end
+
+ def test_array
+ x = [1, 2, 3, 4]
+ y = []
+
+ # iterator over array
+ for i in x
+ y.push i
+ end
+ assert_equal(x, y)
+ end
+
+ def tt
+ 1.upto(10) {|i|
+ yield i
+ }
+ end
+
+ def tt2(dummy)
+ yield 1
+ end
+
+ def tt3(&block)
+ tt2(raise(ArgumentError,""),&block)
+ end
+
+ def test_nested_iterator
+ i = 0
+ tt{|j| break if j == 5}
+ assert_equal(0, i)
+
+ assert_raise(ArgumentError) do
+ tt3{}
+ end
+ end
+
+ def tt4 &block
+ tt2(raise(ArgumentError,""),&block)
+ end
+
+ def test_block_argument_without_paren
+ assert_raise(ArgumentError) do
+ tt4{}
+ end
+ end
+
+ # iterator break/redo/next
+ def test_break
+ done = true
+ loop{
+ break
+ done = false # should not reach here
+ }
+ assert(done)
+
+ done = false
+ bad = false
+ loop {
+ break if done
+ done = true
+ next
+ bad = true # should not reach here
+ }
+ assert(!bad)
+
+ done = false
+ bad = false
+ loop {
+ break if done
+ done = true
+ redo
+ bad = true # should not reach here
+ }
+ assert(!bad)
+
+ x = []
+ for i in 1 .. 7
+ x.push i
+ end
+ assert_equal(7, x.size)
+ assert_equal([1, 2, 3, 4, 5, 6, 7], x)
+ end
+
+ def test_append_method_to_built_in_class
+ x = [[1,2],[3,4],[5,6]]
+ assert_equal(x.iter_test1{|x|x}, x.iter_test2{|x|x})
+ end
+
+ class IterTest
+ def initialize(e); @body = e; end
+
+ def each0(&block); @body.each(&block); end
+ def each1(&block); @body.each {|*x| block.call(*x) } end
+ def each2(&block); @body.each {|*x| block.call(x) } end
+ def each3(&block); @body.each {|x| block.call(*x) } end
+ def each4(&block); @body.each {|x| block.call(x) } end
+ def each5; @body.each {|*x| yield(*x) } end
+ def each6; @body.each {|*x| yield(x) } end
+ def each7; @body.each {|x| yield(*x) } end
+ def each8; @body.each {|x| yield(x) } end
+
+ def f(a)
+ a
+ end
+ end
+
+ def test_itertest
+ assert_equal([1], IterTest.new(nil).method(:f).to_proc.call([1]))
+ m = /\w+/.match("abc")
+ assert_equal([m], IterTest.new(nil).method(:f).to_proc.call([m]))
+
+ IterTest.new([0]).each0 {|x| assert_equal(0, x)}
+ IterTest.new([1]).each1 {|x| assert_equal(1, x)}
+ IterTest.new([2]).each2 {|x| assert_equal([2], x)}
+ IterTest.new([4]).each4 {|x| assert_equal(4, x)}
+ IterTest.new([5]).each5 {|x| assert_equal(5, x)}
+ IterTest.new([6]).each6 {|x| assert_equal([6], x)}
+ IterTest.new([8]).each8 {|x| assert_equal(8, x)}
+
+ IterTest.new([[0]]).each0 {|x| assert_equal([0], x)}
+ IterTest.new([[1]]).each1 {|x| assert_equal([1], x)}
+ IterTest.new([[2]]).each2 {|x| assert_equal([[2]], x)}
+ IterTest.new([[3]]).each3 {|x| assert_equal(3, x)}
+ IterTest.new([[4]]).each4 {|x| assert_equal([4], x)}
+ IterTest.new([[5]]).each5 {|x| assert_equal([5], x)}
+ IterTest.new([[6]]).each6 {|x| assert_equal([[6]], x)}
+ IterTest.new([[7]]).each7 {|x| assert_equal(7, x)}
+ IterTest.new([[8]]).each8 {|x| assert_equal([8], x)}
+
+ IterTest.new([[0,0]]).each0 {|*x| assert_equal([[0,0]], x)}
+ IterTest.new([[8,8]]).each8 {|*x| assert_equal([[8,8]], x)}
+ end
+
+ def m(var)
+ var
+ end
+
+ def m1
+ m(block_given?)
+ end
+
+ def m2
+ m(block_given?,&proc{})
+ end
+
+ def test_block_given
+ assert(m1{p 'test'})
+ assert(m2{p 'test'})
+ assert(!m1())
+ assert(!m2())
+ end
+
+ def m3(var, &block)
+ m(yield(var), &block)
+ end
+
+ def m4(&block)
+ m(m1(), &block)
+ end
+
+ def test_block_passing
+ assert(!m4())
+ assert(!m4 {})
+ assert_equal(100, m3(10) {|x|x*x})
+ end
+
+ class C
+ include Enumerable
+ def initialize
+ @a = [1,2,3]
+ end
+ def each(&block)
+ @a.each(&block)
+ end
+ end
+
+ def test_collect
+ assert_equal([1,2,3], C.new.collect{|n| n})
+ end
+
+ def test_proc
+ assert_instance_of(Proc, lambda{})
+ assert_instance_of(Proc, Proc.new{})
+ lambda{|a|assert_equal(a, 1)}.call(1)
+ end
+
+ def test_block
+ assert_instance_of(NilClass, get_block)
+ assert_instance_of(Proc, get_block{})
+ end
+
+ def test_argument
+ assert_nothing_raised {lambda{||}.call}
+ assert_raise(ArgumentError) {lambda{||}.call(1)}
+ assert_nothing_raised {lambda{|a,|}.call(1)}
+ assert_raise(ArgumentError) {lambda{|a,|}.call()}
+ assert_raise(ArgumentError) {lambda{|a,|}.call(1,2)}
+ end
+
+ def get_block(&block)
+ block
+ end
+
+ def test_get_block
+ assert_instance_of(Proc, get_block{})
+ assert_nothing_raised {get_block{||}.call()}
+ assert_nothing_raised {get_block{||}.call(1)}
+ assert_nothing_raised {get_block{|a,|}.call(1)}
+ assert_nothing_raised {get_block{|a,|}.call()}
+ assert_nothing_raised {get_block{|a,|}.call(1,2)}
+
+ assert_nothing_raised {get_block(&lambda{||}).call()}
+ assert_raise(ArgumentError) {get_block(&lambda{||}).call(1)}
+ assert_nothing_raised {get_block(&lambda{|a,|}).call(1)}
+ assert_raise(ArgumentError) {get_block(&lambda{|a,|}).call(1,2)}
+
+ block = get_block{11}
+ assert_instance_of(Proc, block)
+ assert_instance_of(Proc, block.to_proc)
+ assert_equal(block.clone.call, 11)
+ assert_instance_of(Proc, get_block(&block))
+
+ lmd = lambda{44}
+ assert_instance_of(Proc, lmd)
+ assert_instance_of(Proc, lmd.to_proc)
+ assert_equal(lmd.clone.call, 44)
+ assert_instance_of(Proc, get_block(&lmd))
+
+ assert_equal(1, Proc.new{|a,| a}.call(1,2,3))
+ assert_nothing_raised {Proc.new{|a,|}.call(1,2)}
+ end
+
+ def return1_test
+ Proc.new {
+ return 55
+ }.call + 5
+ end
+
+ def test_return1
+ assert_equal(55, return1_test())
+ end
+
+ def return2_test
+ lambda {
+ return 55
+ }.call + 5
+ end
+
+ def test_return2
+ assert_equal(60, return2_test())
+ end
+
+ def proc_call(&b)
+ b.call
+ end
+ def proc_yield()
+ yield
+ end
+ def proc_return1
+ proc_call{return 42}+1
+ end
+
+ def test_proc_return1
+ assert_equal(42, proc_return1())
+ end
+
+ def proc_return2
+ proc_yield{return 42}+1
+ end
+
+ def test_proc_return2
+ assert_equal(42, proc_return2())
+ end
+
+ def test_ljump
+ assert_raise(LocalJumpError) {get_block{break}.call}
+
+ # cannot use assert_nothing_raised due to passing block.
+ begin
+ val = lambda{break 11}.call
+ rescue LocalJumpError
+ assert(false, "LocalJumpError occurred from break in lambda")
+ else
+ assert_equal(11, val)
+ end
+
+ block = get_block{11}
+ lmd = lambda{44}
+ assert_equal(0, block.arity)
+ assert_equal(0, lmd.arity)
+ assert_equal(0, lambda{||}.arity)
+ assert_equal(1, lambda{|a|}.arity)
+ assert_equal(1, lambda{|a,|}.arity)
+ assert_equal(2, lambda{|a,b|}.arity)
+ end
+
+ def marity_test(m)
+ mobj = method(m)
+ assert_equal(mobj.arity, mobj.to_proc.arity)
+ end
+
+ def test_marity
+ marity_test(:assert)
+ marity_test(:marity_test)
+ marity_test(:p)
+
+ lambda(&method(:assert)).call(true)
+ lambda(&get_block{|a,n| assert(a,n)}).call(true, "marity")
+ end
+
+ def foo
+ yield(:key, :value)
+ end
+ def bar(&blk)
+ blk.call(:key, :value)
+ end
+
+ def test_yield_vs_call
+ foo{|k,v| assert_equal([:key, :value], [k,v])}
+ bar{|k,v| assert_equal([:key, :value], [k,v])}
+ end
+
+ class H
+ def each
+ yield [:key, :value]
+ end
+ alias each_pair each
+ end
+
+ def test_assoc_yield
+ [{:key=>:value}, H.new].each {|h|
+ h.each{|a| assert_equal([:key, :value], a)}
+ h.each{|a,| assert_equal(:key, a)}
+ h.each{|*a| assert_equal([[:key, :value]], a)}
+ h.each{|k,v| assert_equal([:key, :value], [k,v])}
+ h.each_pair{|a| assert_equal([:key, :value], a)}
+ h.each_pair{|a,| assert_equal(:key, a)}
+ h.each_pair{|*a| assert_equal([[:key, :value]], a)}
+ h.each_pair{|k,v| assert_equal([:key, :value], [k,v])}
+ }
+ end
+
+ class ITER_TEST1
+ def a
+ block_given?
+ end
+ end
+
+ class ITER_TEST2 < ITER_TEST1
+ include Test::Unit::Assertions
+ def a
+ assert(super)
+ super
+ end
+ end
+
+ def test_iter_test2
+ assert(ITER_TEST2.new.a {})
+ end
+
+ class ITER_TEST3
+ def foo x
+ return yield if block_given?
+ x
+ end
+ end
+
+ class ITER_TEST4 < ITER_TEST3
+ include Test::Unit::Assertions
+ def foo x
+ assert_equal(super, yield)
+ assert_equal(x, super(x, &nil))
+ end
+ end
+
+ def test_iter4
+ ITER_TEST4.new.foo(44){55}
+ end
+
+ def test_break__nested_loop1
+ _test_break__nested_loop1 do
+ break
+ end
+ end
+
+ def _test_break__nested_loop1
+ while true
+ yield
+ end
+ assert(false, "must not reach here")
+ end
+
+ def test_break__nested_loop2
+ _test_break__nested_loop2 do
+ break
+ end
+ end
+
+ def _test_break__nested_loop2
+ until false
+ yield
+ end
+ assert(false, "must not reach here")
+ end
+
+ def test_break__nested_loop3
+ _test_break__nested_loop3 do
+ break
+ end
+ end
+
+ def _test_break__nested_loop3
+ loop do
+ yield
+ end
+ assert(false, "must not reach here")
+ end
+
+ def test_break_from_enum
+ result = ["a"].inject("ng") {|x,y| break "ok"}
+ assert_equal("ok", result)
+ end
+
+ def _test_return_trace_func(x)
+ set_trace_func(proc {})
+ [].fetch(2) {return x}
+ ensure
+ set_trace_func(nil)
+ end
+
+ def test_return_trace_func
+ ok = "returned gracefully"
+ result = "skipped"
+ result = _test_return_trace_func(ok)
+ ensure
+ assert_equal(ok, result)
+ return
+ end
+
+ class IterString < ::String
+ def ===(other)
+ super if !block_given?
+ end
+ end
+
+ # Check that the block passed to an iterator
+ # does not get propagated inappropriately
+ def test_block_given_within_iterator
+ assert_equal(["b"], ["a", "b", "c"].grep(IterString.new("b")) {|s| s})
+ end
+
+ def test_enumerator
+ [1,2,3].each.with_index {|x,i|
+ assert_equal(x, i+1)
+ }
+
+ e = [1,2,3].each
+ assert_equal(1, e.next)
+ assert_equal(2, e.next)
+ assert_equal(3, e.next)
+ assert_raise(StopIteration){e.next}
+ e.rewind
+ assert_equal(1, e.next)
+ e.rewind
+ a = []
+ loop{a.push e.next}
+ assert_equal([1,2,3], a)
+
+ assert_equal([[8, 1, 10], [6, 2, 11], [4, 3, 12]],
+ [8,6,4].zip((1..10),(10..100)).to_a)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_keyword.rb b/jni/ruby/test/ruby/test_keyword.rb
new file mode 100644
index 0000000..9c76e15
--- /dev/null
+++ b/jni/ruby/test/ruby/test_keyword.rb
@@ -0,0 +1,579 @@
+require 'test/unit'
+
+class TestKeywordArguments < Test::Unit::TestCase
+ def f1(str: "foo", num: 424242)
+ [str, num]
+ end
+
+ def test_f1
+ assert_equal(["foo", 424242], f1)
+ assert_equal(["bar", 424242], f1(str: "bar"))
+ assert_equal(["foo", 111111], f1(num: 111111))
+ assert_equal(["bar", 111111], f1(str: "bar", num: 111111))
+ assert_raise(ArgumentError) { f1(str: "bar", check: true) }
+ assert_raise(ArgumentError) { f1("string") }
+ end
+
+
+ def f2(x, str: "foo", num: 424242)
+ [x, str, num]
+ end
+
+ def test_f2
+ assert_equal([:xyz, "foo", 424242], f2(:xyz))
+ assert_equal([{"bar"=>42}, "foo", 424242], f2("bar"=>42))
+ end
+
+
+ def f3(str: "foo", num: 424242, **h)
+ [str, num, h]
+ end
+
+ def test_f3
+ assert_equal(["foo", 424242, {}], f3)
+ assert_equal(["bar", 424242, {}], f3(str: "bar"))
+ assert_equal(["foo", 111111, {}], f3(num: 111111))
+ assert_equal(["bar", 111111, {}], f3(str: "bar", num: 111111))
+ assert_equal(["bar", 424242, {:check=>true}], f3(str: "bar", check: true))
+ assert_raise(ArgumentError) { f3("string") }
+ end
+
+
+ define_method(:f4) {|str: "foo", num: 424242| [str, num] }
+
+ def test_f4
+ assert_equal(["foo", 424242], f4)
+ assert_equal(["bar", 424242], f4(str: "bar"))
+ assert_equal(["foo", 111111], f4(num: 111111))
+ assert_equal(["bar", 111111], f4(str: "bar", num: 111111))
+ assert_raise(ArgumentError) { f4(str: "bar", check: true) }
+ assert_raise(ArgumentError) { f4("string") }
+ end
+
+
+ define_method(:f5) {|str: "foo", num: 424242, **h| [str, num, h] }
+
+ def test_f5
+ assert_equal(["foo", 424242, {}], f5)
+ assert_equal(["bar", 424242, {}], f5(str: "bar"))
+ assert_equal(["foo", 111111, {}], f5(num: 111111))
+ assert_equal(["bar", 111111, {}], f5(str: "bar", num: 111111))
+ assert_equal(["bar", 424242, {:check=>true}], f5(str: "bar", check: true))
+ assert_raise(ArgumentError) { f5("string") }
+ end
+
+
+ def f6(str: "foo", num: 424242, **h, &blk)
+ [str, num, h, blk]
+ end
+
+ def test_f6 # [ruby-core:40518]
+ assert_equal(["foo", 424242, {}, nil], f6)
+ assert_equal(["bar", 424242, {}, nil], f6(str: "bar"))
+ assert_equal(["foo", 111111, {}, nil], f6(num: 111111))
+ assert_equal(["bar", 111111, {}, nil], f6(str: "bar", num: 111111))
+ assert_equal(["bar", 424242, {:check=>true}, nil], f6(str: "bar", check: true))
+ a = f6 {|x| x + 42 }
+ assert_equal(["foo", 424242, {}], a[0, 3])
+ assert_equal(43, a.last.call(1))
+ end
+
+ def f7(*r, str: "foo", num: 424242, **h)
+ [r, str, num, h]
+ end
+
+ def test_f7 # [ruby-core:41772]
+ assert_equal([[], "foo", 424242, {}], f7)
+ assert_equal([[], "bar", 424242, {}], f7(str: "bar"))
+ assert_equal([[], "foo", 111111, {}], f7(num: 111111))
+ assert_equal([[], "bar", 111111, {}], f7(str: "bar", num: 111111))
+ assert_equal([[1], "foo", 424242, {}], f7(1))
+ assert_equal([[1, 2], "foo", 424242, {}], f7(1, 2))
+ assert_equal([[1, 2, 3], "foo", 424242, {}], f7(1, 2, 3))
+ assert_equal([[1], "bar", 424242, {}], f7(1, str: "bar"))
+ assert_equal([[1, 2], "bar", 424242, {}], f7(1, 2, str: "bar"))
+ assert_equal([[1, 2, 3], "bar", 424242, {}], f7(1, 2, 3, str: "bar"))
+ end
+
+ define_method(:f8) { |opt = :ion, *rest, key: :word|
+ [opt, rest, key]
+ }
+
+ def test_f8
+ assert_equal([:ion, [], :word], f8)
+ assert_equal([1, [], :word], f8(1))
+ assert_equal([1, [2], :word], f8(1, 2))
+ end
+
+ def f9(r, o=42, *args, p, k: :key, **kw, &b)
+ [r, o, args, p, k, kw, b]
+ end
+
+ def test_f9
+ assert_equal([1, 42, [], 2, :key, {}, nil], f9(1, 2))
+ assert_equal([1, 2, [], 3, :key, {}, nil], f9(1, 2, 3))
+ assert_equal([1, 2, [3], 4, :key, {}, nil], f9(1, 2, 3, 4))
+ assert_equal([1, 2, [3, 4], 5, :key, {str: "bar"}, nil], f9(1, 2, 3, 4, 5, str: "bar"))
+ end
+
+ def f10(a: 1, **)
+ a
+ end
+
+ def test_f10
+ assert_equal(42, f10(a: 42))
+ assert_equal(1, f10(b: 42))
+ end
+
+ def test_method_parameters
+ assert_equal([[:key, :str], [:key, :num]], method(:f1).parameters);
+ assert_equal([[:req, :x], [:key, :str], [:key, :num]], method(:f2).parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h]], method(:f3).parameters);
+ assert_equal([[:key, :str], [:key, :num]], method(:f4).parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h]], method(:f5).parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h], [:block, :blk]], method(:f6).parameters);
+ assert_equal([[:rest, :r], [:key, :str], [:key, :num], [:keyrest, :h]], method(:f7).parameters);
+ assert_equal([[:opt, :opt], [:rest, :rest], [:key, :key]], method(:f8).parameters) # [Bug #7540] [ruby-core:50735]
+ assert_equal([[:req, :r], [:opt, :o], [:rest, :args], [:req, :p], [:key, :k],
+ [:keyrest, :kw], [:block, :b]], method(:f9).parameters)
+ end
+
+ def test_lambda
+ f = ->(str: "foo", num: 424242) { [str, num] }
+ assert_equal(["foo", 424242], f[])
+ assert_equal(["bar", 424242], f[str: "bar"])
+ assert_equal(["foo", 111111], f[num: 111111])
+ assert_equal(["bar", 111111], f[str: "bar", num: 111111])
+ end
+
+
+ def p1
+ Proc.new do |str: "foo", num: 424242|
+ [str, num]
+ end
+ end
+
+ def test_p1
+ assert_equal(["foo", 424242], p1[])
+ assert_equal(["bar", 424242], p1[str: "bar"])
+ assert_equal(["foo", 111111], p1[num: 111111])
+ assert_equal(["bar", 111111], p1[str: "bar", num: 111111])
+ assert_raise(ArgumentError) { p1[str: "bar", check: true] }
+ assert_equal(["foo", 424242], p1["string"] )
+ end
+
+
+ def p2
+ Proc.new do |x, str: "foo", num: 424242|
+ [x, str, num]
+ end
+ end
+
+ def test_p2
+ assert_equal([nil, "foo", 424242], p2[])
+ assert_equal([:xyz, "foo", 424242], p2[:xyz])
+ end
+
+
+ def p3
+ Proc.new do |str: "foo", num: 424242, **h|
+ [str, num, h]
+ end
+ end
+
+ def test_p3
+ assert_equal(["foo", 424242, {}], p3[])
+ assert_equal(["bar", 424242, {}], p3[str: "bar"])
+ assert_equal(["foo", 111111, {}], p3[num: 111111])
+ assert_equal(["bar", 111111, {}], p3[str: "bar", num: 111111])
+ assert_equal(["bar", 424242, {:check=>true}], p3[str: "bar", check: true])
+ assert_equal(["foo", 424242, {}], p3["string"])
+ end
+
+
+ def p4
+ Proc.new do |str: "foo", num: 424242, **h, &blk|
+ [str, num, h, blk]
+ end
+ end
+
+ def test_p4
+ assert_equal(["foo", 424242, {}, nil], p4[])
+ assert_equal(["bar", 424242, {}, nil], p4[str: "bar"])
+ assert_equal(["foo", 111111, {}, nil], p4[num: 111111])
+ assert_equal(["bar", 111111, {}, nil], p4[str: "bar", num: 111111])
+ assert_equal(["bar", 424242, {:check=>true}, nil], p4[str: "bar", check: true])
+ a = p4.call {|x| x + 42 }
+ assert_equal(["foo", 424242, {}], a[0, 3])
+ assert_equal(43, a.last.call(1))
+ end
+
+
+ def p5
+ Proc.new do |*r, str: "foo", num: 424242, **h|
+ [r, str, num, h]
+ end
+ end
+
+ def test_p5
+ assert_equal([[], "foo", 424242, {}], p5[])
+ assert_equal([[], "bar", 424242, {}], p5[str: "bar"])
+ assert_equal([[], "foo", 111111, {}], p5[num: 111111])
+ assert_equal([[], "bar", 111111, {}], p5[str: "bar", num: 111111])
+ assert_equal([[1], "foo", 424242, {}], p5[1])
+ assert_equal([[1, 2], "foo", 424242, {}], p5[1, 2])
+ assert_equal([[1, 2, 3], "foo", 424242, {}], p5[1, 2, 3])
+ assert_equal([[1], "bar", 424242, {}], p5[1, str: "bar"])
+ assert_equal([[1, 2], "bar", 424242, {}], p5[1, 2, str: "bar"])
+ assert_equal([[1, 2, 3], "bar", 424242, {}], p5[1, 2, 3, str: "bar"])
+ end
+
+
+ def p6
+ Proc.new do |o1, o2=42, *args, p, k: :key, **kw, &b|
+ [o1, o2, args, p, k, kw, b]
+ end
+ end
+
+ def test_p6
+ assert_equal([nil, 42, [], nil, :key, {}, nil], p6[])
+ assert_equal([1, 42, [], 2, :key, {}, nil], p6[1, 2])
+ assert_equal([1, 2, [], 3, :key, {}, nil], p6[1, 2, 3])
+ assert_equal([1, 2, [3], 4, :key, {}, nil], p6[1, 2, 3, 4])
+ assert_equal([1, 2, [3, 4], 5, :key, {str: "bar"}, nil], p6[1, 2, 3, 4, 5, str: "bar"])
+ end
+
+ def test_proc_parameters
+ assert_equal([[:key, :str], [:key, :num]], p1.parameters);
+ assert_equal([[:opt, :x], [:key, :str], [:key, :num]], p2.parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h]], p3.parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h], [:block, :blk]], p4.parameters);
+ assert_equal([[:rest, :r], [:key, :str], [:key, :num], [:keyrest, :h]], p5.parameters);
+ assert_equal([[:opt, :o1], [:opt, :o2], [:rest, :args], [:opt, :p], [:key, :k],
+ [:keyrest, :kw], [:block, :b]], p6.parameters)
+ end
+
+ def m1(*args)
+ yield(*args)
+ end
+
+ def test_block
+ blk = Proc.new {|str: "foo", num: 424242| [str, num] }
+ assert_equal(["foo", 424242], m1(&blk))
+ assert_equal(["bar", 424242], m1(str: "bar", &blk))
+ assert_equal(["foo", 111111], m1(num: 111111, &blk))
+ assert_equal(["bar", 111111], m1(str: "bar", num: 111111, &blk))
+ end
+
+ def rest_keyrest(*args, **opt)
+ return *args, opt
+ end
+
+ def test_rest_keyrest
+ bug7665 = '[ruby-core:51278]'
+ bug8463 = '[ruby-core:55203] [Bug #8463]'
+ expect = [*%w[foo bar], {zzz: 42}]
+ assert_equal(expect, rest_keyrest(*expect), bug7665)
+ pr = proc {|*args, **opt| next *args, opt}
+ assert_equal(expect, pr.call(*expect), bug7665)
+ assert_equal(expect, pr.call(expect), bug8463)
+ pr = proc {|a, *b, **opt| next a, *b, opt}
+ assert_equal(expect, pr.call(expect), bug8463)
+ pr = proc {|a, **opt| next a, opt}
+ assert_equal(expect.values_at(0, -1), pr.call(expect), bug8463)
+ end
+
+ def test_bare_kwrest
+ # valid syntax, but its semantics is undefined
+ assert_valid_syntax("def bug7662(**) end")
+ assert_valid_syntax("def bug7662(*, **) end")
+ assert_valid_syntax("def bug7662(a, **) end")
+ end
+
+ def test_without_paren
+ bug7942 = '[ruby-core:52820] [Bug #7942]'
+ assert_valid_syntax("def bug7942 a: 1; end")
+ assert_valid_syntax("def bug7942 a: 1, **; end")
+
+ o = Object.new
+ eval("def o.bug7942 a: 1; a; end", nil, __FILE__, __LINE__)
+ assert_equal(1, o.bug7942(), bug7942)
+ assert_equal(42, o.bug7942(a: 42), bug7942)
+
+ o = Object.new
+ eval("def o.bug7942 a: 1, **; a; end", nil, __FILE__, __LINE__)
+ assert_equal(1, o.bug7942(), bug7942)
+ assert_equal(42, o.bug7942(a: 42), bug7942)
+ end
+
+ def test_required_keyword
+ feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
+ o = Object.new
+ assert_nothing_raised(SyntaxError, feature7701) do
+ eval("def o.foo(a:) a; end", nil, "xyzzy")
+ eval("def o.bar(a:,**b) [a, b]; end")
+ end
+ assert_raise_with_message(ArgumentError, /missing keyword/, feature7701) {o.foo}
+ assert_raise_with_message(ArgumentError, /unknown keyword/, feature7701) {o.foo(a:0, b:1)}
+ begin
+ o.foo(a: 0, b: 1)
+ rescue => e
+ assert_equal('xyzzy', e.backtrace_locations[0].path)
+ end
+ assert_equal(42, o.foo(a: 42), feature7701)
+ assert_equal([[:keyreq, :a]], o.method(:foo).parameters, feature7701)
+
+ bug8139 = '[ruby-core:53608] [Bug #8139] required keyword argument with rest hash'
+ assert_equal([42, {}], o.bar(a: 42), feature7701)
+ assert_equal([42, {c: feature7701}], o.bar(a: 42, c: feature7701), feature7701)
+ assert_equal([[:keyreq, :a], [:keyrest, :b]], o.method(:bar).parameters, feature7701)
+ assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {o.bar(c: bug8139)}
+ assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {o.bar}
+ end
+
+ def test_required_keyword_with_newline
+ bug9669 = '[ruby-core:61658] [Bug #9669]'
+ assert_nothing_raised(SyntaxError, bug9669) do
+ eval(<<-'end;', nil, __FILE__, __LINE__)
+ def bug9669.foo a:
+ return a
+ end
+ end;
+ end
+ assert_equal(42, bug9669.foo(a: 42))
+ o = nil
+ assert_nothing_raised(SyntaxError, bug9669) do
+ eval(<<-'end;', nil, __FILE__, __LINE__)
+ o = {
+ a:
+ 1
+ }
+ end;
+ end
+ assert_equal({a: 1}, o, bug9669)
+ end
+
+ def test_required_keyword_with_reserved
+ bug10279 = '[ruby-core:65211] [Bug #10279]'
+ h = nil
+ assert_nothing_raised(SyntaxError, bug10279) do
+ break eval(<<-'end;', nil, __FILE__, __LINE__)
+ h = {a: if true then 42 end}
+ end;
+ end
+ assert_equal({a: 42}, h, bug10279)
+ end
+
+ def test_block_required_keyword
+ feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
+ b = assert_nothing_raised(SyntaxError, feature7701) do
+ break eval("proc {|a:| a}", nil, 'xyzzy', __LINE__)
+ end
+ assert_raise_with_message(ArgumentError, /missing keyword/, feature7701) {b.call}
+ assert_raise_with_message(ArgumentError, /unknown keyword/, feature7701) {b.call(a:0, b:1)}
+ begin
+ b.call(a: 0, b: 1)
+ rescue => e
+ assert_equal('xyzzy', e.backtrace_locations[0].path)
+ end
+
+ assert_equal(42, b.call(a: 42), feature7701)
+ assert_equal([[:keyreq, :a]], b.parameters, feature7701)
+
+ bug8139 = '[ruby-core:53608] [Bug #8139] required keyword argument with rest hash'
+ b = assert_nothing_raised(SyntaxError, feature7701) do
+ break eval("proc {|a:, **bl| [a, bl]}", nil, __FILE__, __LINE__)
+ end
+ assert_equal([42, {}], b.call(a: 42), feature7701)
+ assert_equal([42, {c: feature7701}], b.call(a: 42, c: feature7701), feature7701)
+ assert_equal([[:keyreq, :a], [:keyrest, :bl]], b.parameters, feature7701)
+ assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {b.call(c: bug8139)}
+ assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {b.call}
+ end
+
+ def test_super_with_keyword
+ bug8236 = '[ruby-core:54094] [Bug #8236]'
+ base = Class.new do
+ def foo(*args)
+ args
+ end
+ end
+ a = Class.new(base) do
+ def foo(arg, bar: 'x')
+ super
+ end
+ end
+ b = Class.new(base) do
+ def foo(*args, bar: 'x')
+ super
+ end
+ end
+ assert_equal([42, {:bar=>"x"}], a.new.foo(42), bug8236)
+ assert_equal([42, {:bar=>"x"}], b.new.foo(42), bug8236)
+ end
+
+ def test_zsuper_only_named_kwrest
+ bug8416 = '[ruby-core:55033] [Bug #8416]'
+ base = Class.new do
+ def foo(**h)
+ h
+ end
+ end
+ a = Class.new(base) do
+ def foo(**h)
+ super
+ end
+ end
+ assert_equal({:bar=>"x"}, a.new.foo(bar: "x"), bug8416)
+ end
+
+ def test_zsuper_only_anonymous_kwrest
+ bug8416 = '[ruby-core:55033] [Bug #8416]'
+ base = Class.new do
+ def foo(**h)
+ h
+ end
+ end
+ a = Class.new(base) do
+ def foo(**)
+ super
+ end
+ end
+ assert_equal({:bar=>"x"}, a.new.foo(bar: "x"), bug8416)
+ end
+
+ def test_precedence_of_keyword_arguments
+ bug8040 = '[ruby-core:53199] [Bug #8040]'
+ a = Class.new do
+ def foo(x, **h)
+ [x, h]
+ end
+ end
+ assert_equal([{}, {}], a.new.foo({}))
+ assert_equal([{}, {:bar=>"x"}], a.new.foo({}, bar: "x"), bug8040)
+ end
+
+ def test_precedence_of_keyword_arguments_with_post_argument
+ bug8993 = '[ruby-core:57706] [Bug #8993]'
+ a = Class.new do
+ def foo(a, b, c=1, *d, e, f:2, **g)
+ [a, b, c, d, e, f, g]
+ end
+ end
+ assert_equal([1, 2, 1, [], {:f=>5}, 2, {}], a.new.foo(1, 2, f:5), bug8993)
+ end
+
+ def test_splat_keyword_nondestructive
+ bug9776 = '[ruby-core:62161] [Bug #9776]'
+
+ h = {a: 1}
+ assert_equal({a:1, b:2}, {**h, b:2})
+ assert_equal({a:1}, h, bug9776)
+
+ pr = proc {|**opt| next opt}
+ assert_equal({a: 1}, pr.call(**h))
+ assert_equal({a: 1, b: 2}, pr.call(**h, b: 2))
+ assert_equal({a: 1}, h, bug9776)
+ end
+
+ def test_splat_hash_conversion
+ bug9898 = '[ruby-core:62921] [Bug #9898]'
+
+ o = Object.new
+ def o.to_hash() { a: 1 } end
+ assert_equal({a: 1}, m1(**o) {|x| break x}, bug9898)
+ o2 = Object.new
+ def o2.to_hash() { b: 2 } end
+ assert_equal({a: 1, b: 2}, m1(**o, **o2) {|x| break x}, bug9898)
+ end
+
+ def test_implicit_hash_conversion
+ bug10016 = '[ruby-core:63593] [Bug #10016]'
+
+ o = Object.new
+ def o.to_hash() { k: 9 } end
+ assert_equal([1, 42, [], o, :key, {}, nil], f9(1, o))
+ assert_equal([1, 9], m1(1, o) {|a, k: 0| break [a, k]}, bug10016)
+ assert_equal([1, 9], m1(1, o, &->(a, k: 0) {break [a, k]}), bug10016)
+ end
+
+ def test_gced_object_in_stack
+ bug8964 = '[ruby-dev:47729] [Bug #8964]'
+ assert_normal_exit %q{
+ def m(a: [])
+ end
+ GC.stress = true
+ tap { m }
+ GC.start
+ tap { m }
+ }, bug8964
+ assert_normal_exit %q{
+ prc = Proc.new {|a: []|}
+ GC.stress = true
+ tap { prc.call }
+ GC.start
+ tap { prc.call }
+ }, bug8964
+ end
+
+ def test_dynamic_symbol_keyword
+ bug10266 = '[ruby-dev:48564] [Bug #10266]'
+ assert_separately(['-', bug10266], <<-'end;') # do
+ bug = ARGV.shift
+ "hoge".to_sym
+ assert_nothing_raised(bug) {eval("def a(hoge:); end")}
+ end;
+ end
+
+ def test_unknown_keyword_with_block
+ bug10413 = '[ruby-core:65837] [Bug #10413]'
+ class << (o = Object.new)
+ def bar(k2: 'v2')
+ end
+
+ def foo
+ bar(k1: 1)
+ end
+ end
+ assert_raise_with_message(ArgumentError, /unknown keyword: k1/, bug10413) {
+ o.foo {raise "unreachable"}
+ }
+ end
+
+ def test_super_with_anon_restkeywords
+ bug10659 = '[ruby-core:67157] [Bug #10659]'
+
+ foo = Class.new do
+ def foo(**h)
+ h
+ end
+ end
+
+ class << (obj = foo.new)
+ def foo(bar: "bar", **)
+ super
+ end
+ end
+
+ assert_nothing_raised(TypeError, bug10659) {
+ assert_equal({:bar => "bar"}, obj.foo, bug10659)
+ }
+ end
+
+ def m(a) yield a end
+
+ def test_nonsymbol_key
+ result = m(["a" => 10]) { |a = nil, **b| [a, b] }
+ assert_equal([{"a" => 10}, {}], result)
+ end
+
+ def method_for_test_to_hash_call_during_setup_complex_parameters k1:, k2:, **rest_kw
+ [k1, k2, rest_kw]
+ end
+
+ def test_to_hash_call_during_setup_complex_parameters
+ sym = "sym_#{Time.now}".to_sym
+ h = method_for_test_to_hash_call_during_setup_complex_parameters k1: "foo", k2: "bar", sym => "baz"
+ assert_equal ["foo", "bar", {sym => "baz"}], h, '[Bug #11027]'
+ end
+end
diff --git a/jni/ruby/test/ruby/test_lambda.rb b/jni/ruby/test/ruby/test_lambda.rb
new file mode 100644
index 0000000..0f3382c
--- /dev/null
+++ b/jni/ruby/test/ruby/test_lambda.rb
@@ -0,0 +1,173 @@
+require 'test/unit'
+
+class TestLambdaParameters < Test::Unit::TestCase
+
+ def test_exact_parameter
+ assert_raise(ArgumentError){(1..3).each(&lambda{})}
+ end
+
+ def test_call_simple
+ assert_equal(1, lambda{|a| a}.call(1))
+ assert_equal([1,2], lambda{|a, b| [a,b]}.call(1,2))
+ assert_raise(ArgumentError) { lambda{|a|}.call(1,2) }
+ assert_raise(ArgumentError) { lambda{|a|}.call() }
+ assert_raise(ArgumentError) { lambda{}.call(1) }
+ assert_raise(ArgumentError) { lambda{|a, b|}.call(1,2,3) }
+
+ assert_equal(1, ->(a){ a }.call(1))
+ assert_equal([1,2], ->(a,b){ [a,b] }.call(1,2))
+ assert_raise(ArgumentError) { ->(a){ }.call(1,2) }
+ assert_raise(ArgumentError) { ->(a){ }.call() }
+ assert_raise(ArgumentError) { ->(){ }.call(1) }
+ assert_raise(ArgumentError) { ->(a,b){ }.call(1,2,3) }
+ end
+
+ def test_lambda_as_iterator
+ a = 0
+ 2.times(&->(_){ a += 1 })
+ assert_equal(2, a)
+ assert_raise(ArgumentError) {1.times(&->(){ a += 1 })}
+ bug9605 = '[ruby-core:61468] [Bug #9605]'
+ assert_nothing_raised(ArgumentError, bug9605) {1.times(&->(n){ a += 1 })}
+ assert_equal(3, a, bug9605)
+ assert_nothing_raised(ArgumentError, bug9605) {a = [[1, 2]].map(&->(x, y) {x+y})}
+ assert_equal([3], a, bug9605)
+ end
+
+ def test_call_rest_args
+ assert_equal([1,2], ->(*a){ a }.call(1,2))
+ assert_equal([1,2,[]], ->(a,b,*c){ [a,b,c] }.call(1,2))
+ assert_raise(ArgumentError){ ->(a,*b){ }.call() }
+ end
+
+ def test_call_opt_args
+ assert_equal([1,2,3,4], ->(a,b,c=3,d=4){ [a,b,c,d] }.call(1,2))
+ assert_equal([1,2,3,4], ->(a,b,c=0,d=4){ [a,b,c,d] }.call(1,2,3))
+ assert_raise(ArgumentError){ ->(a,b=1){ }.call() }
+ assert_raise(ArgumentError){ ->(a,b=1){ }.call(1,2,3) }
+ end
+
+ def test_call_rest_and_opt
+ assert_equal([1,2,3,[]], ->(a,b=2,c=3,*d){ [a,b,c,d] }.call(1))
+ assert_equal([1,2,3,[]], ->(a,b=0,c=3,*d){ [a,b,c,d] }.call(1,2))
+ assert_equal([1,2,3,[4,5,6]], ->(a,b=0,c=0,*d){ [a,b,c,d] }.call(1,2,3,4,5,6))
+ assert_raise(ArgumentError){ ->(a,b=1,*c){ }.call() }
+ end
+
+ def test_call_with_block
+ f = ->(a,b,c=3,*d,&e){ [a,b,c,d,e.call(d + [a,b,c])] }
+ assert_equal([1,2,3,[],6], f.call(1,2){|z| z.inject{|s,x| s+x} } )
+ assert_equal(nil, ->(&b){ b }.call)
+ foo { puts "bogus block " }
+ assert_equal(1, ->(&b){ b.call }.call { 1 })
+ b = nil
+ assert_equal(1, ->(&b){ b.call }.call { 1 })
+ assert_nil(b)
+ end
+
+ def test_call_block_from_lambda
+ bug9605 = '[ruby-core:61470] [Bug #9605]'
+ plus = ->(x,y) {x+y}
+ assert_raise(ArgumentError, bug9605) {proc(&plus).call [1,2]}
+ end
+
+ def yield_1(arg)
+ yield arg
+ end
+
+ tap do |;bug9605, expected, result|
+ bug9605 = '[ruby-core:65887] [Bug #9605] arity check should be relaxed'
+ expected = [1,2,3]
+
+ [
+ ["array", expected],
+ ["to_ary", Struct.new(:to_ary).new(expected)],
+ ].product \
+ [
+ ["proc", proc {|a, b, c| [a, b, c]}],
+ ["lambda", lambda {|a, b, c| [a, b, c]}],
+ ] do
+ |(vtype, val), (btype, block)|
+ define_method("test_yeild_relaxed(#{vtype},&#{btype})") do
+ result = assert_nothing_raised(ArgumentError, bug9605) {
+ break yield_1(val, &block)
+ }
+ assert_equal(expected, result, bug9605)
+ end
+ end
+ end
+
+ def foo
+ assert_equal(nil, ->(&b){ b }.call)
+ end
+
+ def test_in_basic_object
+ bug5966 = '[ruby-core:42349]'
+ called = false
+ BasicObject.new.instance_eval {->() {called = true}.()}
+ assert_equal(true, called, bug5966)
+ end
+
+ def test_location_on_error
+ bug6151 = '[ruby-core:43314]'
+ called = 0
+ line, f = __LINE__, lambda do
+ called += 1
+ true
+ end
+ e = assert_raise(ArgumentError) do
+ f.call(42)
+ end
+ assert_send([e.backtrace.first, :start_with?, "#{__FILE__}:#{line}:"], bug6151)
+ assert_equal(0, called)
+ e = assert_raise(ArgumentError) do
+ 42.times(&f)
+ end
+ assert_send([e.backtrace.first, :start_with?, "#{__FILE__}:#{line}:"], bug6151)
+ assert_equal(0, called)
+ end
+
+ def return_in_current(val)
+ 1.tap(&->(*) {return 0})
+ val
+ end
+
+ def yield_block
+ yield
+ end
+
+ def return_in_callee(val)
+ yield_block(&->(*) {return 0})
+ val
+ end
+
+ def test_return
+ feature8693 = '[ruby-core:56193] [Feature #8693]'
+ assert_equal(42, return_in_current(42), feature8693)
+ assert_equal(42, return_in_callee(42), feature8693)
+ end
+
+ def test_do_lambda_source_location
+ exp_lineno = __LINE__ + 3
+ lmd = ->(x,
+ y,
+ z) do
+ #
+ end
+ file, lineno = lmd.source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(exp_lineno, lineno, "must be at the beginning of the block")
+ end
+
+ def test_brace_lambda_source_location
+ exp_lineno = __LINE__ + 3
+ lmd = ->(x,
+ y,
+ z) {
+ #
+ }
+ file, lineno = lmd.source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(exp_lineno, lineno, "must be at the beginning of the block")
+ end
+end
diff --git a/jni/ruby/test/ruby/test_lazy_enumerator.rb b/jni/ruby/test/ruby/test_lazy_enumerator.rb
new file mode 100644
index 0000000..75f2458
--- /dev/null
+++ b/jni/ruby/test/ruby/test_lazy_enumerator.rb
@@ -0,0 +1,494 @@
+require 'test/unit'
+
+class TestLazyEnumerator < Test::Unit::TestCase
+ class Step
+ include Enumerable
+ attr_reader :current, :args
+
+ def initialize(enum)
+ @enum = enum
+ @current = nil
+ @args = nil
+ end
+
+ def each(*args)
+ @args = args
+ @enum.each {|i| @current = i; yield i}
+ end
+ end
+
+ def test_initialize
+ assert_equal([1, 2, 3], [1, 2, 3].lazy.to_a)
+ assert_equal([1, 2, 3], Enumerator::Lazy.new([1, 2, 3]){|y, v| y << v}.to_a)
+ assert_raise(ArgumentError) { Enumerator::Lazy.new([1, 2, 3]) }
+
+ a = [1, 2, 3].lazy
+ a.freeze
+ assert_raise(RuntimeError) {
+ a.__send__ :initialize, [4, 5], &->(y, *v) { y << yield(*v) }
+ }
+ end
+
+ def test_each_args
+ a = Step.new(1..3)
+ assert_equal(1, a.lazy.each(4).first)
+ assert_equal([4], a.args)
+ end
+
+ def test_each_line
+ name = lineno = nil
+ File.open(__FILE__) do |f|
+ f.each("").map do |paragraph|
+ paragraph[/\A\s*(.*)/, 1]
+ end.find do |line|
+ if name = line[/^class\s+(\S+)/, 1]
+ lineno = f.lineno
+ true
+ end
+ end
+ end
+ assert_equal(self.class.name, name)
+ assert_operator(lineno, :>, 2)
+
+ name = lineno = nil
+ File.open(__FILE__) do |f|
+ f.lazy.each("").map do |paragraph|
+ paragraph[/\A\s*(.*)/, 1]
+ end.find do |line|
+ if name = line[/^class\s+(\S+)/, 1]
+ lineno = f.lineno
+ true
+ end
+ end
+ end
+ assert_equal(self.class.name, name)
+ assert_equal(2, lineno)
+ end
+
+ def test_select
+ a = Step.new(1..6)
+ assert_equal(4, a.select {|x| x > 3}.first)
+ assert_equal(6, a.current)
+ assert_equal(4, a.lazy.select {|x| x > 3}.first)
+ assert_equal(4, a.current)
+
+ a = Step.new(['word', nil, 1])
+ assert_raise(TypeError) {a.select {|x| "x"+x}.first}
+ assert_equal(nil, a.current)
+ assert_equal("word", a.lazy.select {|x| "x"+x}.first)
+ assert_equal("word", a.current)
+ end
+
+ def test_select_multiple_values
+ e = Enumerator.new { |yielder|
+ for i in 1..5
+ yielder.yield(i, i.to_s)
+ end
+ }
+ assert_equal([[2, "2"], [4, "4"]],
+ e.select {|x| x[0] % 2 == 0})
+ assert_equal([[2, "2"], [4, "4"]],
+ e.lazy.select {|x| x[0] % 2 == 0}.force)
+ end
+
+ def test_map
+ a = Step.new(1..3)
+ assert_equal(2, a.map {|x| x * 2}.first)
+ assert_equal(3, a.current)
+ assert_equal(2, a.lazy.map {|x| x * 2}.first)
+ assert_equal(1, a.current)
+ end
+
+ def test_flat_map
+ a = Step.new(1..3)
+ assert_equal(2, a.flat_map {|x| [x * 2]}.first)
+ assert_equal(3, a.current)
+ assert_equal(2, a.lazy.flat_map {|x| [x * 2]}.first)
+ assert_equal(1, a.current)
+ end
+
+ def test_flat_map_nested
+ a = Step.new(1..3)
+ assert_equal([1, "a"],
+ a.flat_map {|x| ("a".."c").map {|y| [x, y]}}.first)
+ assert_equal(3, a.current)
+ assert_equal([1, "a"],
+ a.lazy.flat_map {|x| ("a".."c").lazy.map {|y| [x, y]}}.first)
+ assert_equal(1, a.current)
+ end
+
+ def test_flat_map_to_ary
+ to_ary = Class.new {
+ def initialize(value)
+ @value = value
+ end
+
+ def to_ary
+ [:to_ary, @value]
+ end
+ }
+ assert_equal([:to_ary, 1, :to_ary, 2, :to_ary, 3],
+ [1, 2, 3].flat_map {|x| to_ary.new(x)})
+ assert_equal([:to_ary, 1, :to_ary, 2, :to_ary, 3],
+ [1, 2, 3].lazy.flat_map {|x| to_ary.new(x)}.force)
+ end
+
+ def test_flat_map_non_array
+ assert_equal(["1", "2", "3"], [1, 2, 3].flat_map {|x| x.to_s})
+ assert_equal(["1", "2", "3"], [1, 2, 3].lazy.flat_map {|x| x.to_s}.force)
+ end
+
+ def test_flat_map_hash
+ assert_equal([{?a=>97}, {?b=>98}, {?c=>99}], [?a, ?b, ?c].flat_map {|x| {x=>x.ord}})
+ assert_equal([{?a=>97}, {?b=>98}, {?c=>99}], [?a, ?b, ?c].lazy.flat_map {|x| {x=>x.ord}}.force)
+ end
+
+ def test_reject
+ a = Step.new(1..6)
+ assert_equal(4, a.reject {|x| x < 4}.first)
+ assert_equal(6, a.current)
+ assert_equal(4, a.lazy.reject {|x| x < 4}.first)
+ assert_equal(4, a.current)
+
+ a = Step.new(['word', nil, 1])
+ assert_equal(nil, a.reject {|x| x}.first)
+ assert_equal(1, a.current)
+ assert_equal(nil, a.lazy.reject {|x| x}.first)
+ assert_equal(nil, a.current)
+ end
+
+ def test_reject_multiple_values
+ e = Enumerator.new { |yielder|
+ for i in 1..5
+ yielder.yield(i, i.to_s)
+ end
+ }
+ assert_equal([[2, "2"], [4, "4"]],
+ e.reject {|x| x[0] % 2 != 0})
+ assert_equal([[2, "2"], [4, "4"]],
+ e.lazy.reject {|x| x[0] % 2 != 0}.force)
+ end
+
+ def test_grep
+ a = Step.new('a'..'f')
+ assert_equal('c', a.grep(/c/).first)
+ assert_equal('f', a.current)
+ assert_equal('c', a.lazy.grep(/c/).first)
+ assert_equal('c', a.current)
+ assert_equal(%w[a e], a.grep(proc {|x| /[aeiou]/ =~ x}))
+ assert_equal(%w[a e], a.lazy.grep(proc {|x| /[aeiou]/ =~ x}).to_a)
+ end
+
+ def test_grep_with_block
+ a = Step.new('a'..'f')
+ assert_equal('C', a.grep(/c/) {|i| i.upcase}.first)
+ assert_equal('C', a.lazy.grep(/c/) {|i| i.upcase}.first)
+ end
+
+ def test_grep_multiple_values
+ e = Enumerator.new { |yielder|
+ 3.times { |i|
+ yielder.yield(i, i.to_s)
+ }
+ }
+ assert_equal([[2, "2"]], e.grep(proc {|x| x == [2, "2"]}))
+ assert_equal([[2, "2"]], e.lazy.grep(proc {|x| x == [2, "2"]}).force)
+ assert_equal(["22"],
+ e.lazy.grep(proc {|x| x == [2, "2"]}, &:join).force)
+ end
+
+ def test_zip
+ a = Step.new(1..3)
+ assert_equal([1, "a"], a.zip("a".."c").first)
+ assert_equal(3, a.current)
+ assert_equal([1, "a"], a.lazy.zip("a".."c").first)
+ assert_equal(1, a.current)
+ end
+
+ def test_zip_short_arg
+ a = Step.new(1..5)
+ assert_equal([5, nil], a.zip("a".."c").last)
+ assert_equal([5, nil], a.lazy.zip("a".."c").force.last)
+ end
+
+ def test_zip_without_arg
+ a = Step.new(1..3)
+ assert_equal([1], a.zip.first)
+ assert_equal(3, a.current)
+ assert_equal([1], a.lazy.zip.first)
+ assert_equal(1, a.current)
+ end
+
+ def test_zip_bad_arg
+ a = Step.new(1..3)
+ assert_raise(TypeError){ a.lazy.zip(42) }
+ end
+
+ def test_zip_with_block
+ # zip should be eager when a block is given
+ a = Step.new(1..3)
+ ary = []
+ assert_equal(nil, a.lazy.zip("a".."c") {|x, y| ary << [x, y]})
+ assert_equal(a.zip("a".."c"), ary)
+ assert_equal(3, a.current)
+ end
+
+ def test_take
+ a = Step.new(1..10)
+ assert_equal(1, a.take(5).first)
+ assert_equal(5, a.current)
+ assert_equal(1, a.lazy.take(5).first)
+ assert_equal(1, a.current)
+ assert_equal((1..5).to_a, a.lazy.take(5).force)
+ assert_equal(5, a.current)
+ a = Step.new(1..10)
+ assert_equal([], a.lazy.take(0).force)
+ assert_equal(nil, a.current)
+ end
+
+ def test_take_recycle
+ bug6428 = '[ruby-dev:45634]'
+ a = Step.new(1..10)
+ take5 = a.lazy.take(5)
+ assert_equal((1..5).to_a, take5.force, bug6428)
+ assert_equal((1..5).to_a, take5.force, bug6428)
+ end
+
+ def test_take_nested
+ bug7696 = '[ruby-core:51470]'
+ a = Step.new(1..10)
+ take5 = a.lazy.take(5)
+ assert_equal([*(1..5)]*5, take5.flat_map{take5}.force, bug7696)
+ end
+
+ def test_drop_while_nested
+ bug7696 = '[ruby-core:51470]'
+ a = Step.new(1..10)
+ drop5 = a.lazy.drop_while{|x| x < 6}
+ assert_equal([*(6..10)]*5, drop5.flat_map{drop5}.force, bug7696)
+ end
+
+ def test_drop_nested
+ bug7696 = '[ruby-core:51470]'
+ a = Step.new(1..10)
+ drop5 = a.lazy.drop(5)
+ assert_equal([*(6..10)]*5, drop5.flat_map{drop5}.force, bug7696)
+ end
+
+ def test_zip_nested
+ bug7696 = '[ruby-core:51470]'
+ enum = ('a'..'z').each
+ enum.next
+ zip = (1..3).lazy.zip(enum, enum)
+ assert_equal([[1, 'a', 'a'], [2, 'b', 'b'], [3, 'c', 'c']]*3, zip.flat_map{zip}.force, bug7696)
+ end
+
+ def test_zip_lazy_on_args
+ zip = Step.new(1..2).lazy.zip(42..Float::INFINITY)
+ assert_equal [[1, 42], [2, 43]], zip.force
+ end
+
+ def test_zip_efficient_on_array_args
+ ary = [42, :foo]
+ %i[to_enum enum_for lazy each].each do |forbid|
+ ary.define_singleton_method(forbid){ fail "#{forbid} was called"}
+ end
+ zip = Step.new(1..2).lazy.zip(ary)
+ assert_equal [[1, 42], [2, :foo]], zip.force
+ end
+
+ def test_zip_nonsingle
+ bug8735 = '[ruby-core:56383] [Bug #8735]'
+
+ obj = Object.new
+ def obj.each
+ yield
+ yield 1, 2
+ end
+
+ assert_equal(obj.to_enum.zip(obj.to_enum), obj.to_enum.lazy.zip(obj.to_enum).force, bug8735)
+ end
+
+ def test_take_rewound
+ bug7696 = '[ruby-core:51470]'
+ e=(1..42).lazy.take(2)
+ assert_equal 1, e.next, bug7696
+ assert_equal 2, e.next, bug7696
+ e.rewind
+ assert_equal 1, e.next, bug7696
+ assert_equal 2, e.next, bug7696
+ end
+
+ def test_take_while
+ a = Step.new(1..10)
+ assert_equal(1, a.take_while {|i| i < 5}.first)
+ assert_equal(5, a.current)
+ assert_equal(1, a.lazy.take_while {|i| i < 5}.first)
+ assert_equal(1, a.current)
+ assert_equal((1..4).to_a, a.lazy.take_while {|i| i < 5}.to_a)
+ end
+
+ def test_drop
+ a = Step.new(1..10)
+ assert_equal(6, a.drop(5).first)
+ assert_equal(10, a.current)
+ assert_equal(6, a.lazy.drop(5).first)
+ assert_equal(6, a.current)
+ assert_equal((6..10).to_a, a.lazy.drop(5).to_a)
+ end
+
+ def test_drop_while
+ a = Step.new(1..10)
+ assert_equal(5, a.drop_while {|i| i % 5 > 0}.first)
+ assert_equal(10, a.current)
+ assert_equal(5, a.lazy.drop_while {|i| i % 5 > 0}.first)
+ assert_equal(5, a.current)
+ assert_equal((5..10).to_a, a.lazy.drop_while {|i| i % 5 > 0}.to_a)
+ end
+
+ def test_drop_and_take
+ assert_equal([4, 5], (1..Float::INFINITY).lazy.drop(3).take(2).to_a)
+ end
+
+ def test_cycle
+ a = Step.new(1..3)
+ assert_equal("1", a.cycle(2).map(&:to_s).first)
+ assert_equal(3, a.current)
+ assert_equal("1", a.lazy.cycle(2).map(&:to_s).first)
+ assert_equal(1, a.current)
+ end
+
+ def test_cycle_with_block
+ # cycle should be eager when a block is given
+ a = Step.new(1..3)
+ ary = []
+ assert_equal(nil, a.lazy.cycle(2) {|i| ary << i})
+ assert_equal(a.cycle(2).to_a, ary)
+ assert_equal(3, a.current)
+ end
+
+ def test_cycle_chain
+ a = 1..3
+ assert_equal([1,2,3,1,2,3,1,2,3,1], a.lazy.cycle.take(10).force)
+ assert_equal([2,2,2,2,2,2,2,2,2,2], a.lazy.cycle.select {|x| x == 2}.take(10).force)
+ assert_equal([2,2,2,2,2,2,2,2,2,2], a.lazy.select {|x| x == 2}.cycle.take(10).force)
+ end
+
+ def test_force
+ assert_equal([1, 2, 3], (1..Float::INFINITY).lazy.take(3).force)
+ end
+
+ def test_inspect
+ assert_equal("#<Enumerator::Lazy: 1..10>", (1..10).lazy.inspect)
+ assert_equal('#<Enumerator::Lazy: #<Enumerator: "foo":each_char>>',
+ "foo".each_char.lazy.inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:map>",
+ (1..10).lazy.map {}.inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:take(0)>",
+ (1..10).lazy.take(0).inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:take(3)>",
+ (1..10).lazy.take(3).inspect)
+ assert_equal('#<Enumerator::Lazy: #<Enumerator::Lazy: "a".."c">:grep(/b/)>',
+ ("a".."c").lazy.grep(/b/).inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:cycle(3)>",
+ (1..10).lazy.cycle(3).inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:cycle>",
+ (1..10).lazy.cycle.inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:cycle(3)>",
+ (1..10).lazy.cycle(3).inspect)
+ l = (1..10).lazy.map {}.collect {}.flat_map {}.collect_concat {}.select {}.find_all {}.reject {}.grep(1).zip(?a..?c).take(10).take_while {}.drop(3).drop_while {}.cycle(3)
+ assert_equal(<<EOS.chomp, l.inspect)
+#<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:map>:collect>:flat_map>:collect_concat>:select>:find_all>:reject>:grep(1)>:zip("a".."c")>:take(10)>:take_while>:drop(3)>:drop_while>:cycle(3)>
+EOS
+ end
+
+ def test_lazy_to_enum
+ lazy = [1, 2, 3].lazy
+ def lazy.foo(*args)
+ yield args
+ yield args
+ end
+ enum = lazy.to_enum(:foo, :hello, :world)
+ assert_equal Enumerator::Lazy, enum.class
+ assert_equal nil, enum.size
+ assert_equal [[:hello, :world], [:hello, :world]], enum.to_a
+
+ assert_equal [1, 2, 3], lazy.to_enum.to_a
+ end
+
+ def test_size
+ lazy = [1, 2, 3].lazy
+ assert_equal 3, lazy.size
+ assert_equal 42, Enumerator::Lazy.new([],->{42}){}.size
+ assert_equal 42, Enumerator::Lazy.new([],42){}.size
+ assert_equal 42, Enumerator::Lazy.new([],42){}.lazy.size
+ assert_equal 42, lazy.to_enum{ 42 }.size
+
+ %i[map collect].each do |m|
+ assert_equal 3, lazy.send(m){}.size
+ end
+ assert_equal 3, lazy.zip([4]).size
+ %i[flat_map collect_concat select find_all reject take_while drop_while].each do |m|
+ assert_equal nil, lazy.send(m){}.size
+ end
+ assert_equal nil, lazy.grep(//).size
+
+ assert_equal 2, lazy.take(2).size
+ assert_equal 3, lazy.take(4).size
+ assert_equal 4, loop.lazy.take(4).size
+ assert_equal nil, lazy.select{}.take(4).size
+
+ assert_equal 1, lazy.drop(2).size
+ assert_equal 0, lazy.drop(4).size
+ assert_equal Float::INFINITY, loop.lazy.drop(4).size
+ assert_equal nil, lazy.select{}.drop(4).size
+
+ assert_equal 0, lazy.cycle(0).size
+ assert_equal 6, lazy.cycle(2).size
+ assert_equal 3 << 80, 4.times.inject(lazy){|enum| enum.cycle(1 << 20)}.size
+ assert_equal Float::INFINITY, lazy.cycle.size
+ assert_equal Float::INFINITY, loop.lazy.cycle(4).size
+ assert_equal Float::INFINITY, loop.lazy.cycle.size
+ assert_equal nil, lazy.select{}.cycle(4).size
+ assert_equal nil, lazy.select{}.cycle.size
+ end
+
+ def test_map_zip
+ bug7507 = '[ruby-core:50545]'
+ assert_ruby_status(["-e", "GC.stress = true", "-e", "(1..10).lazy.map{}.zip(){}"], "", bug7507)
+ assert_ruby_status(["-e", "GC.stress = true", "-e", "(1..10).lazy.map{}.zip().to_a"], "", bug7507)
+ end
+
+ def test_require_block
+ %i[select reject drop_while take_while map flat_map].each do |method|
+ assert_raise(ArgumentError){ [].lazy.send(method) }
+ end
+ end
+
+ def test_laziness_conservation
+ bug7507 = '[ruby-core:51510]'
+ {
+ slice_before: //,
+ slice_after: //,
+ with_index: nil,
+ cycle: nil,
+ each_with_object: 42,
+ each_slice: 42,
+ each_entry: nil,
+ each_cons: 42,
+ }.each do |method, arg|
+ assert_equal Enumerator::Lazy, [].lazy.send(method, *arg).class, bug7507
+ end
+ assert_equal Enumerator::Lazy, [].lazy.chunk{}.class, bug7507
+ assert_equal Enumerator::Lazy, [].lazy.slice_when{}.class, bug7507
+ end
+
+ def test_no_warnings
+ le = (1..3).lazy
+ assert_warning("") {le.zip([4,5,6]).force}
+ assert_warning("") {le.zip(4..6).force}
+ assert_warning("") {le.take(1).force}
+ assert_warning("") {le.drop(1).force}
+ assert_warning("") {le.drop_while{false}.force}
+ end
+end
diff --git a/jni/ruby/test/ruby/test_literal.rb b/jni/ruby/test/ruby/test_literal.rb
new file mode 100644
index 0000000..ed5f836
--- /dev/null
+++ b/jni/ruby/test/ruby/test_literal.rb
@@ -0,0 +1,461 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+
+class TestRubyLiteral < Test::Unit::TestCase
+
+ def test_special_const
+ assert_equal 'true', true.inspect
+ assert_instance_of TrueClass, true
+ assert_equal 'false', false.inspect
+ assert_instance_of FalseClass, false
+ assert_equal 'nil', nil.inspect
+ assert_instance_of NilClass, nil
+ assert_equal ':sym', :sym.inspect
+ assert_instance_of Symbol, :sym
+ assert_equal '1234', 1234.inspect
+ assert_instance_of Fixnum, 1234
+ assert_equal '1234', 1_2_3_4.inspect
+ assert_instance_of Fixnum, 1_2_3_4
+ assert_equal '18', 0x12.inspect
+ assert_instance_of Fixnum, 0x12
+ assert_raise(SyntaxError) { eval("0x") }
+ assert_equal '15', 0o17.inspect
+ assert_instance_of Fixnum, 0o17
+ assert_raise(SyntaxError) { eval("0o") }
+ assert_equal '5', 0b101.inspect
+ assert_instance_of Fixnum, 0b101
+ assert_raise(SyntaxError) { eval("0b") }
+ assert_equal '123456789012345678901234567890', 123456789012345678901234567890.inspect
+ assert_instance_of Bignum, 123456789012345678901234567890
+ assert_instance_of Float, 1.3
+ assert_equal '2', eval("0x00+2").inspect
+ end
+
+ def test_self
+ assert_equal self, self
+ assert_instance_of TestRubyLiteral, self
+ assert_respond_to self, :test_self
+ end
+
+ def test_string
+ assert_instance_of String, ?a
+ assert_equal "a", ?a
+ assert_instance_of String, ?A
+ assert_equal "A", ?A
+ assert_instance_of String, ?\n
+ assert_equal "\n", ?\n
+ assert_equal " ", ?\ # space
+ assert_equal '', ''
+ assert_equal 'string', 'string'
+ assert_equal 'string string', 'string string'
+ assert_equal ' ', ' '
+ assert_equal ' ', " "
+ assert_equal "\0", "\0"
+ assert_equal "\1", "\1"
+ assert_equal "3", "\x33"
+ assert_equal "\n", "\n"
+ bug2500 = '[ruby-core:27228]'
+ bug5262 = '[ruby-core:39222]'
+ %w[c C- M-].each do |pre|
+ ["u", %w[u{ }]].each do |open, close|
+ ["?", ['"', '"']].each do |qopen, qclose|
+ str = "#{qopen}\\#{pre}\\#{open}5555#{close}#{qclose}"
+ assert_raise(SyntaxError, "#{bug2500} eval(#{str})") {eval(str)}
+
+ str = "#{qopen}\\#{pre}\\#{open}\u201c#{close}#{qclose}"
+ assert_raise(SyntaxError, "#{bug5262} eval(#{str})") {eval(str)}
+
+ str = "#{qopen}\\#{pre}\\#{open}\u201c#{close}#{qclose}".encode("euc-jp")
+ assert_raise(SyntaxError, "#{bug5262} eval(#{str})") {eval(str)}
+
+ str = "#{qopen}\\#{pre}\\#{open}\u201c#{close}#{qclose}".encode("iso-8859-13")
+ assert_raise(SyntaxError, "#{bug5262} eval(#{str})") {eval(str)}
+
+ str = "#{qopen}\\#{pre}\\#{open}\xe2\x7f#{close}#{qclose}".force_encoding("utf-8")
+ assert_raise(SyntaxError, "#{bug5262} eval(#{str})") {eval(str)}
+ end
+ end
+ end
+ bug6069 = '[ruby-dev:45278]'
+ assert_equal "\x13", "\c\x33"
+ assert_equal "\x13", "\C-\x33"
+ assert_equal "\xB3", "\M-\x33"
+ assert_equal "\u201c", eval(%["\\\u{201c}"]), bug5262
+ assert_equal "\u201c".encode("euc-jp"), eval(%["\\\u{201c}"].encode("euc-jp")), bug5262
+ assert_equal "\u201c".encode("iso-8859-13"), eval(%["\\\u{201c}"].encode("iso-8859-13")), bug5262
+ assert_equal "\\\u201c", eval(%['\\\u{201c}']), bug6069
+ assert_equal "\\\u201c".encode("euc-jp"), eval(%['\\\u{201c}'].encode("euc-jp")), bug6069
+ assert_equal "\\\u201c".encode("iso-8859-13"), eval(%['\\\u{201c}'].encode("iso-8859-13")), bug6069
+ assert_equal "\u201c", eval(%[?\\\u{201c}]), bug6069
+ assert_equal "\u201c".encode("euc-jp"), eval(%[?\\\u{201c}].encode("euc-jp")), bug6069
+ assert_equal "\u201c".encode("iso-8859-13"), eval(%[?\\\u{201c}].encode("iso-8859-13")), bug6069
+ end
+
+ def test_dstring
+ assert_equal '2', "#{1+1}"
+ assert_equal '16', "#{2 ** 4}"
+ s = "string"
+ assert_equal s, "#{s}"
+ a = 'Foo'
+ b = "#{a}" << 'Bar'
+ assert_equal('Foo', a, 'r3842')
+ assert_equal('FooBar', b, 'r3842')
+ end
+
+ def test_dsymbol
+ assert_equal :a3c, :"a#{1+2}c"
+ end
+
+ def test_xstring
+ assert_equal "foo\n", `echo foo`
+ s = 'foo'
+ assert_equal "foo\n", `echo #{s}`
+ end
+
+ def test_regexp
+ assert_instance_of Regexp, //
+ assert_match(//, 'a')
+ assert_match(//, '')
+ assert_instance_of Regexp, /a/
+ assert_match(/a/, 'a')
+ assert_no_match(/test/, 'tes')
+ re = /test/
+ assert_match re, 'test'
+ str = 'test'
+ assert_match re, str
+ assert_match(/test/, str)
+ assert_equal 0, (/test/ =~ 'test')
+ assert_equal 0, (re =~ 'test')
+ assert_equal 0, (/test/ =~ str)
+ assert_equal 0, (re =~ str)
+ assert_equal 0, ('test' =~ /test/)
+ assert_equal 0, ('test' =~ re)
+ assert_equal 0, (str =~ /test/)
+ assert_equal 0, (str =~ re)
+ end
+
+ def test_dregexp
+ assert_instance_of Regexp, /re#{'ge'}xp/
+ assert_equal(/regexp/, /re#{'ge'}xp/)
+ bug3903 = '[ruby-core:32682]'
+ assert_raise(SyntaxError, bug3903) {eval('/[#{"\x80"}]/')}
+ end
+
+ def test_array
+ assert_instance_of Array, []
+ assert_equal [], []
+ assert_equal 0, [].size
+ assert_instance_of Array, [0]
+ assert_equal [3], [3]
+ assert_equal 1, [3].size
+ a = [3]
+ assert_equal 3, a[0]
+ assert_instance_of Array, [1,2]
+ assert_equal [1,2], [1,2]
+ assert_instance_of Array, [1,2,3,4,5]
+ assert_equal [1,2,3,4,5], [1,2,3,4,5]
+ assert_equal 5, [1,2,3,4,5].size
+ a = [1,2]
+ assert_equal 1, a[0]
+ assert_equal 2, a[1]
+ a = [1 + 2, 3 + 4, 5 + 6]
+ assert_instance_of Array, a
+ assert_equal [3, 7, 11], a
+ assert_equal 7, a[1]
+ assert_equal 1, ([0][0] += 1)
+ assert_equal 1, ([2][0] -= 1)
+ a = [obj = Object.new]
+ assert_instance_of Array, a
+ assert_equal 1, a.size
+ assert_equal obj, a[0]
+ a = [1,2,3]
+ a[1] = 5
+ assert_equal 5, a[1]
+ end
+
+ def test_hash
+ assert_instance_of Hash, {}
+ assert_equal({}, {})
+ assert_instance_of Hash, {1 => 2}
+ assert_equal({1 => 2}, {1 => 2})
+ h = {1 => 2}
+ assert_equal 2, h[1]
+ h = {"string" => "literal", "goto" => "hell"}
+ assert_equal h, h
+ assert_equal 2, h.size
+ assert_equal h, h
+ assert_equal "literal", h["string"]
+ end
+
+ def test_big_array_and_hash_literal
+ assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).map{'x'}.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
+ assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).to_a.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
+ assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("{#{(1..1_000_000).map{|n| "#{n} => x"}.join(', ')}}").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
+ assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("{#{(1..1_000_000).map{|n| "#{n} => #{n}"}.join(', ')}}").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
+ end
+
+ def test_big_hash_literal
+ bug7466 = '[ruby-dev:46658]'
+ h = {
+ 0xFE042 => 0xE5CD,
+ 0xFE043 => 0xE5CD,
+ 0xFE045 => 0xEA94,
+ 0xFE046 => 0xE4E3,
+ 0xFE047 => 0xE4E2,
+ 0xFE048 => 0xEA96,
+ 0xFE049 => 0x3013,
+ 0xFE04A => 0xEB36,
+ 0xFE04B => 0xEB37,
+ 0xFE04C => 0xEB38,
+ 0xFE04D => 0xEB49,
+ 0xFE04E => 0xEB82,
+ 0xFE04F => 0xE4D2,
+ 0xFE050 => 0xEB35,
+ 0xFE051 => 0xEAB9,
+ 0xFE052 => 0xEABA,
+ 0xFE053 => 0xE4D4,
+ 0xFE054 => 0xE4CD,
+ 0xFE055 => 0xEABB,
+ 0xFE056 => 0xEABC,
+ 0xFE057 => 0xEB32,
+ 0xFE058 => 0xEB33,
+ 0xFE059 => 0xEB34,
+ 0xFE05A => 0xEB39,
+ 0xFE05B => 0xEB5A,
+ 0xFE190 => 0xE5A4,
+ 0xFE191 => 0xE5A5,
+ 0xFE192 => 0xEAD0,
+ 0xFE193 => 0xEAD1,
+ 0xFE194 => 0xEB47,
+ 0xFE195 => 0xE509,
+ 0xFE196 => 0xEAA0,
+ 0xFE197 => 0xE50B,
+ 0xFE198 => 0xEAA1,
+ 0xFE199 => 0xEAA2,
+ 0xFE19A => 0x3013,
+ 0xFE19B => 0xE4FC,
+ 0xFE19C => 0xE4FA,
+ 0xFE19D => 0xE4FC,
+ 0xFE19E => 0xE4FA,
+ 0xFE19F => 0xE501,
+ 0xFE1A0 => 0x3013,
+ 0xFE1A1 => 0xE5DD,
+ 0xFE1A2 => 0xEADB,
+ 0xFE1A3 => 0xEAE9,
+ 0xFE1A4 => 0xEB13,
+ 0xFE1A5 => 0xEB14,
+ 0xFE1A6 => 0xEB15,
+ 0xFE1A7 => 0xEB16,
+ 0xFE1A8 => 0xEB17,
+ 0xFE1A9 => 0xEB18,
+ 0xFE1AA => 0xEB19,
+ 0xFE1AB => 0xEB1A,
+ 0xFE1AC => 0xEB44,
+ 0xFE1AD => 0xEB45,
+ 0xFE1AE => 0xE4CB,
+ 0xFE1AF => 0xE5BF,
+ 0xFE1B0 => 0xE50E,
+ 0xFE1B1 => 0xE4EC,
+ 0xFE1B2 => 0xE4EF,
+ 0xFE1B3 => 0xE4F8,
+ 0xFE1B4 => 0x3013,
+ 0xFE1B5 => 0x3013,
+ 0xFE1B6 => 0xEB1C,
+ 0xFE1B9 => 0xEB7E,
+ 0xFE1D3 => 0xEB22,
+ 0xFE7DC => 0xE4D8,
+ 0xFE1D4 => 0xEB23,
+ 0xFE1D5 => 0xEB24,
+ 0xFE1D6 => 0xEB25,
+ 0xFE1CC => 0xEB1F,
+ 0xFE1CD => 0xEB20,
+ 0xFE1CE => 0xE4D9,
+ 0xFE1CF => 0xE48F,
+ 0xFE1C5 => 0xE5C7,
+ 0xFE1C6 => 0xEAEC,
+ 0xFE1CB => 0xEB1E,
+ 0xFE1DA => 0xE4DD,
+ 0xFE1E1 => 0xEB57,
+ 0xFE1E2 => 0xEB58,
+ 0xFE1E3 => 0xE492,
+ 0xFE1C9 => 0xEB1D,
+ 0xFE1D9 => 0xE4D3,
+ 0xFE1DC => 0xE5D4,
+ 0xFE1BA => 0xE4E0,
+ 0xFE1BB => 0xEB76,
+ 0xFE1C8 => 0xE4E0,
+ 0xFE1DD => 0xE5DB,
+ 0xFE1BC => 0xE4DC,
+ 0xFE1D8 => 0xE4DF,
+ 0xFE1BD => 0xE49A,
+ 0xFE1C7 => 0xEB1B,
+ 0xFE1C2 => 0xE5C2,
+ 0xFE1C0 => 0xE5C0,
+ 0xFE1B8 => 0xE4DB,
+ 0xFE1C3 => 0xE470,
+ 0xFE1BE => 0xE4D8,
+ 0xFE1C4 => 0xE4D9,
+ 0xFE1B7 => 0xE4E1,
+ 0xFE1BF => 0xE4DE,
+ 0xFE1C1 => 0xE5C1,
+ 0xFE1CA => 0x3013,
+ 0xFE1D0 => 0xE4E1,
+ 0xFE1D1 => 0xEB21,
+ 0xFE1D2 => 0xE4D7,
+ 0xFE1D7 => 0xE4DA,
+ 0xFE1DB => 0xE4EE,
+ 0xFE1DE => 0xEB3F,
+ 0xFE1DF => 0xEB46,
+ 0xFE1E0 => 0xEB48,
+ 0xFE336 => 0xE4FB,
+ 0xFE320 => 0xE472,
+ 0xFE321 => 0xEB67,
+ 0xFE322 => 0xEACA,
+ 0xFE323 => 0xEAC0,
+ 0xFE324 => 0xE5AE,
+ 0xFE325 => 0xEACB,
+ 0xFE326 => 0xEAC9,
+ 0xFE327 => 0xE5C4,
+ 0xFE328 => 0xEAC1,
+ 0xFE329 => 0xE4E7,
+ 0xFE32A => 0xE4E7,
+ 0xFE32B => 0xEACD,
+ 0xFE32C => 0xEACF,
+ 0xFE32D => 0xEACE,
+ 0xFE32E => 0xEAC7,
+ 0xFE32F => 0xEAC8,
+ 0xFE330 => 0xE471,
+ 0xFE331 => "[Bug #7466]",
+ }
+ k = h.keys
+ assert_equal([129, 0xFE331], [k.size, k.last], bug7466)
+
+ code = [
+ "h = {",
+ (1..128).map {|i| "#{i} => 0,"},
+ (129..140).map {|i| "#{i} => [],"},
+ "}",
+ ].join
+ assert_separately([], <<-"end;")
+ GC.stress = true
+ #{code}
+ GC.stress = false
+ assert_equal(140, h.size)
+ end;
+ end
+
+ def test_range
+ assert_instance_of Range, (1..2)
+ assert_equal(1..2, 1..2)
+ r = 1..2
+ assert_equal 1, r.begin
+ assert_equal 2, r.end
+ assert_equal false, r.exclude_end?
+ assert_instance_of Range, (1...3)
+ assert_equal(1...3, 1...3)
+ r = 1...3
+ assert_equal 1, r.begin
+ assert_equal 3, r.end
+ assert_equal true, r.exclude_end?
+ r = 1+2 .. 3+4
+ assert_instance_of Range, r
+ assert_equal 3, r.begin
+ assert_equal 7, r.end
+ assert_equal false, r.exclude_end?
+ r = 1+2 ... 3+4
+ assert_instance_of Range, r
+ assert_equal 3, r.begin
+ assert_equal 7, r.end
+ assert_equal true, r.exclude_end?
+ assert_instance_of Range, 'a'..'c'
+ r = 'a'..'c'
+ assert_equal 'a', r.begin
+ assert_equal 'c', r.end
+ end
+
+ def test__FILE__
+ assert_instance_of String, __FILE__
+ assert_equal __FILE__, __FILE__
+ assert_equal 'test_literal.rb', File.basename(__FILE__)
+ end
+
+ def test__LINE__
+ assert_instance_of Fixnum, __LINE__
+ assert_equal __LINE__, __LINE__
+ end
+
+ def test_integer
+ head = ['', '0x', '0o', '0b', '0d', '-', '+']
+ chars = ['0', '1', '_', '9', 'f']
+ head.each {|h|
+ 4.times {|len|
+ a = [h]
+ len.times { a = a.product(chars).map {|x| x.join('') } }
+ a.each {|s|
+ next if s.empty?
+ begin
+ r1 = Integer(s)
+ rescue ArgumentError
+ r1 = :err
+ end
+ begin
+ r2 = eval(s)
+ rescue NameError, SyntaxError
+ r2 = :err
+ end
+ assert_equal(r1, r2, "Integer(#{s.inspect}) != eval(#{s.inspect})")
+ }
+ }
+ }
+ bug2407 = '[ruby-dev:39798]'
+ head.each {|h|
+ if /^0/ =~ h
+ begin
+ eval("#{h}_")
+ rescue SyntaxError => e
+ assert_match(/numeric literal without digits\Z/, e.message, bug2407)
+ end
+ end
+ }
+ end
+
+ def test_float
+ head = ['', '-', '+']
+ chars = ['0', '1', '_', '9', 'f', '.']
+ head.each {|h|
+ 6.times {|len|
+ a = [h]
+ len.times { a = a.product(chars).map {|x| x.join('') } }
+ a.each {|s|
+ next if s.empty?
+ next if /\.\z/ =~ s
+ next if /\A[-+]?\./ =~ s
+ next if /\A[-+]?0/ =~ s
+ begin
+ r1 = Float(s)
+ rescue ArgumentError
+ r1 = :err
+ end
+ begin
+ r2 = eval(s)
+ rescue NameError, SyntaxError
+ r2 = :err
+ end
+ r2 = :err if Range === r2
+ assert_equal(r1, r2, "Float(#{s.inspect}) != eval(#{s.inspect})")
+ }
+ }
+ }
+ end
+
+ def test_symbol_list
+ assert_equal([:foo, :bar], %i[foo bar])
+ assert_equal([:"\"foo"], %i["foo])
+
+ x = 10
+ assert_equal([:foo, :b10], %I[foo b#{x}])
+ assert_equal([:"\"foo10"], %I["foo#{x}])
+
+ assert_ruby_status(["--disable-gems", "--dump=parsetree"], "%I[foo bar]")
+ end
+end
diff --git a/jni/ruby/test/ruby/test_m17n.rb b/jni/ruby/test/ruby/test_m17n.rb
new file mode 100644
index 0000000..980f9c8
--- /dev/null
+++ b/jni/ruby/test/ruby/test_m17n.rb
@@ -0,0 +1,1599 @@
+# coding: US-ASCII
+require 'test/unit'
+
+class TestM17N < Test::Unit::TestCase
+ def assert_encoding(encname, actual, message=nil)
+ assert_equal(Encoding.find(encname), actual, message)
+ end
+
+ module AESU
+ def ua(str) str.dup.force_encoding("US-ASCII") end
+ def a(str) str.dup.force_encoding("ASCII-8BIT") end
+ def e(str) str.dup.force_encoding("EUC-JP") end
+ def s(str) str.dup.force_encoding("Windows-31J") end
+ def u(str) str.dup.force_encoding("UTF-8") end
+ end
+ include AESU
+ extend AESU
+
+ def assert_strenc(bytes, enc, actual, message=nil)
+ assert_instance_of(String, actual, message)
+ enc = Encoding.find(enc) if String === enc
+ assert_equal(enc, actual.encoding, message)
+ assert_equal(a(bytes), a(actual), message)
+ end
+
+ def assert_regexp_generic_encoding(r)
+ assert_not_predicate(r, :fixed_encoding?)
+ %w[ASCII-8BIT EUC-JP Windows-31J UTF-8].each {|ename|
+ # "\xc2\xa1" is a valid sequence for ASCII-8BIT, EUC-JP, Windows-31J and UTF-8.
+ assert_nothing_raised { r =~ "\xc2\xa1".force_encoding(ename) }
+ }
+ end
+
+ def assert_regexp_fixed_encoding(r)
+ assert_predicate(r, :fixed_encoding?)
+ %w[ASCII-8BIT EUC-JP Windows-31J UTF-8].each {|ename|
+ enc = Encoding.find(ename)
+ if enc == r.encoding
+ assert_nothing_raised { r =~ "\xc2\xa1".force_encoding(enc) }
+ else
+ assert_raise(Encoding::CompatibilityError) { r =~ "\xc2\xa1".force_encoding(enc) }
+ end
+ }
+ end
+
+ def assert_regexp_generic_ascii(r)
+ assert_encoding("US-ASCII", r.encoding)
+ assert_regexp_generic_encoding(r)
+ end
+
+ def assert_regexp_fixed_ascii8bit(r)
+ assert_encoding("ASCII-8BIT", r.encoding)
+ assert_regexp_fixed_encoding(r)
+ end
+
+ def assert_regexp_fixed_eucjp(r)
+ assert_encoding("EUC-JP", r.encoding)
+ assert_regexp_fixed_encoding(r)
+ end
+
+ def assert_regexp_fixed_sjis(r)
+ assert_encoding("Windows-31J", r.encoding)
+ assert_regexp_fixed_encoding(r)
+ end
+
+ def assert_regexp_fixed_utf8(r)
+ assert_encoding("UTF-8", r.encoding)
+ assert_regexp_fixed_encoding(r)
+ end
+
+ def assert_regexp_usascii_literal(r, enc, ex = nil)
+ code = "# -*- encoding: US-ASCII -*-\n#{r}.encoding"
+ if ex
+ assert_raise(ex) { eval(code) }
+ else
+ assert_equal(enc, eval(code))
+ end
+ end
+
+ def encdump(str)
+ d = str.dump
+ if /\.force_encoding\("[A-Za-z0-9.:_+-]*"\)\z/ =~ d
+ d
+ else
+ "#{d}.force_encoding(#{str.encoding.name.dump})"
+ end
+ end
+
+ def encdumpargs(args)
+ r = '('
+ args.each_with_index {|a, i|
+ r << ',' if 0 < i
+ if String === a
+ r << encdump(a)
+ else
+ r << a.inspect
+ end
+ }
+ r << ')'
+ r
+ end
+
+ def assert_str_enc_propagation(t, s1, s2)
+ if !s1.ascii_only?
+ assert_equal(s1.encoding, t.encoding)
+ elsif !s2.ascii_only?
+ assert_equal(s2.encoding, t.encoding)
+ else
+ assert_include([s1.encoding, s2.encoding], t.encoding)
+ end
+ end
+
+ def assert_same_result(expected_proc, actual_proc)
+ e = nil
+ begin
+ t = expected_proc.call
+ rescue
+ e = $!
+ end
+ if e
+ assert_raise(e.class) { actual_proc.call }
+ else
+ assert_equal(t, actual_proc.call)
+ end
+ end
+
+ def str_enc_compatible?(*strs)
+ encs = []
+ strs.each {|s|
+ encs << s.encoding if !s.ascii_only?
+ }
+ encs.uniq!
+ encs.length <= 1
+ end
+
+ # tests start
+
+ def test_string_ascii_literal
+ assert_encoding("ASCII-8BIT", eval(a(%{""})).encoding)
+ assert_encoding("ASCII-8BIT", eval(a(%{"a"})).encoding)
+ end
+
+ def test_string_eucjp_literal
+ assert_encoding("EUC-JP", eval(e(%{""})).encoding)
+ assert_encoding("EUC-JP", eval(e(%{"a"})).encoding)
+ assert_encoding("EUC-JP", eval(e(%{"\xa1\xa1"})).encoding)
+ assert_encoding("EUC-JP", eval(e(%{"\\xa1\\xa1"})).encoding)
+ assert_encoding("EUC-JP", eval(e(%{"\\x20"})).encoding)
+ assert_encoding("EUC-JP", eval(e(%{"\\n"})).encoding)
+ assert_encoding("EUC-JP", eval(e(%{"\\x80"})).encoding)
+ end
+
+ def test_utf8_literal
+ assert_equal(Encoding::UTF_8, "\u3042".encoding, "[ruby-dev:33406] \"\\u3042\".encoding")
+ assert_raise(SyntaxError) { eval(a('\u3052\x80')) }
+ end
+
+ def test_string_mixed_unicode
+ assert_raise(SyntaxError) { eval(a(%{"\xc2\xa1\\u{6666}"})) }
+ assert_raise(SyntaxError) { eval(e(%{"\xc2\xa1\\u{6666}"})) }
+ assert_raise(SyntaxError) { eval(s(%{"\xc2\xa1\\u{6666}"})) }
+ assert_nothing_raised { eval(u(%{"\xc2\xa1\\u{6666}"})) }
+ assert_raise(SyntaxError) { eval(a(%{"\\u{6666}\xc2\xa1"})) }
+ assert_raise(SyntaxError) { eval(e(%{"\\u{6666}\xc2\xa1"})) }
+ assert_raise(SyntaxError) { eval(s(%{"\\u{6666}\xc2\xa1"})) }
+ assert_nothing_raised { eval(u(%{"\\u{6666}\xc2\xa1"})) }
+ end
+
+ def test_string_inspect_invalid
+ assert_equal('"\xFE"', e("\xfe").inspect)
+ assert_equal('"\x8E"', e("\x8e").inspect)
+ assert_equal('"\x8F"', e("\x8f").inspect)
+ assert_equal('"\x8F\xA1"', e("\x8f\xa1").inspect)
+ assert_equal('"\xEF"', s("\xef").inspect)
+ assert_equal('"\xC2"', u("\xc2").inspect)
+ assert_equal('"\xE0\x80"', u("\xe0\x80").inspect)
+ assert_equal('"\xF0\x80\x80"', u("\xf0\x80\x80").inspect)
+ assert_equal('"\xF8\x80\x80\x80"', u("\xf8\x80\x80\x80").inspect)
+ assert_equal('"\xFC\x80\x80\x80\x80"', u("\xfc\x80\x80\x80\x80").inspect)
+
+ assert_equal('"\xFE "', e("\xfe ").inspect)
+ assert_equal('"\x8E "', e("\x8e ").inspect)
+ assert_equal('"\x8F "', e("\x8f ").inspect)
+ assert_equal('"\x8F\xA1 "', e("\x8f\xa1 ").inspect)
+ assert_equal('"\xEF "', s("\xef ").inspect)
+ assert_equal('"\xC2 "', u("\xc2 ").inspect)
+ assert_equal('"\xE0\x80 "', u("\xe0\x80 ").inspect)
+ assert_equal('"\xF0\x80\x80 "', u("\xf0\x80\x80 ").inspect)
+ assert_equal('"\xF8\x80\x80\x80 "', u("\xf8\x80\x80\x80 ").inspect)
+ assert_equal('"\xFC\x80\x80\x80\x80 "', u("\xfc\x80\x80\x80\x80 ").inspect)
+
+ assert_equal('"\x81."', s("\x81.").inspect)
+ assert_equal('"\xFC"', u("\xfc").inspect)
+ end
+
+ def test_string_inspect_encoding
+ EnvUtil.suppress_warning do
+ begin
+ orig_int = Encoding.default_internal
+ orig_ext = Encoding.default_external
+ Encoding.default_internal = nil
+ [Encoding::UTF_8, Encoding::EUC_JP, Encoding::Windows_31J, Encoding::GB18030].
+ each do |e|
+ Encoding.default_external = e
+ str = "\x81\x30\x81\x30".force_encoding('GB18030')
+ assert_equal(Encoding::GB18030 == e ? %{"#{str}"} : '"\x{81308130}"', str.inspect)
+ str = e("\xa1\x8f\xa1\xa1")
+ expected = "\"\\xA1\x8F\xA1\xA1\"".force_encoding("EUC-JP")
+ assert_equal(Encoding::EUC_JP == e ? expected : "\"\\xA1\\x{8FA1A1}\"", str.inspect)
+ str = s("\x81@")
+ assert_equal(Encoding::Windows_31J == e ? %{"#{str}"} : '"\x{8140}"', str.inspect)
+ str = "\u3042\u{10FFFD}"
+ assert_equal(Encoding::UTF_8 == e ? %{"#{str}"} : '"\u3042\u{10FFFD}"', str.inspect)
+ end
+ Encoding.default_external = Encoding::UTF_8
+ [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE,
+ Encoding::UTF8_SOFTBANK].each do |e|
+ str = "abc".encode(e)
+ assert_equal('"abc"', str.inspect)
+ end
+ ensure
+ Encoding.default_internal = orig_int
+ Encoding.default_external = orig_ext
+ end
+ end
+ end
+
+ STR_WITHOUT_BOM = "\u3042".freeze
+ STR_WITH_BOM = "\uFEFF\u3042".freeze
+ bug8940 = '[ruby-core:59757] [Bug #8940]'
+ bug9415 = '[ruby-dev:47895] [Bug #9415]'
+ %w/UTF-16 UTF-32/.each do |enc|
+ %w/BE LE/.each do |endian|
+ bom = "\uFEFF".encode("#{enc}#{endian}").force_encoding(enc)
+
+ define_method("test_utf_16_32_inspect(#{enc}#{endian})") do
+ s = STR_WITHOUT_BOM.encode(enc + endian)
+ # When a UTF-16/32 string doesn't have a BOM,
+ # inspect as a dummy encoding string.
+ assert_equal(s.dup.force_encoding("ISO-2022-JP").inspect,
+ s.dup.force_encoding(enc).inspect)
+ assert_normal_exit("#{bom.b.dump}.force_encoding('#{enc}').inspect", bug8940)
+ end
+
+ define_method("test_utf_16_32_codepoints(#{enc}#{endian})") do
+ assert_equal([0xFEFF], bom.codepoints, bug9415)
+ end
+
+ define_method("test_utf_16_32_ord(#{enc}#{endian})") do
+ assert_equal(0xFEFF, bom.ord, bug9415)
+ end
+
+ define_method("test_utf_16_32_inspect(#{enc}#{endian}-BOM)") do
+ s = STR_WITH_BOM.encode(enc + endian)
+ # When a UTF-16/32 string has a BOM,
+ # inspect as a particular encoding string.
+ assert_equal(s.inspect,
+ s.dup.force_encoding(enc).inspect)
+ end
+ end
+ end
+
+ def test_utf_without_bom_asciionly
+ bug10598 = '[ruby-core:66835] [Bug #10598]'
+ encs = [Encoding::UTF_16, Encoding::UTF_32].find_all {|enc|
+ "abcd".force_encoding(enc).ascii_only?
+ }
+ assert_empty(encs, bug10598)
+ end
+
+ def test_object_utf16_32_inspect
+ EnvUtil.suppress_warning do
+ begin
+ orig_int = Encoding.default_internal
+ orig_ext = Encoding.default_external
+ Encoding.default_internal = nil
+ Encoding.default_external = Encoding::UTF_8
+ o = Object.new
+ [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE].each do |e|
+ o.instance_eval "undef inspect;def inspect;'abc'.encode('#{e}');end"
+ assert_raise(Encoding::CompatibilityError) { [o].inspect }
+ end
+ ensure
+ Encoding.default_internal = orig_int
+ Encoding.default_external = orig_ext
+ end
+ end
+ end
+
+ def test_object_inspect_external
+ orig_v, $VERBOSE = $VERBOSE, false
+ orig_int, Encoding.default_internal = Encoding.default_internal, nil
+ orig_ext = Encoding.default_external
+ o = Object.new
+
+ Encoding.default_external = Encoding::UTF_16BE
+ def o.inspect
+ "abc"
+ end
+ assert_nothing_raised(Encoding::CompatibilityError) { [o].inspect }
+
+ def o.inspect
+ "abc".encode(Encoding.default_external)
+ end
+ assert_raise(Encoding::CompatibilityError) { [o].inspect }
+
+ Encoding.default_external = Encoding::US_ASCII
+ def o.inspect
+ "\u3042"
+ end
+ assert_raise(Encoding::CompatibilityError) { [o].inspect }
+ ensure
+ Encoding.default_internal = orig_int
+ Encoding.default_external = orig_ext
+ $VERBOSE = orig_v
+ end
+
+ def test_str_dump
+ [
+ e("\xfe"),
+ e("\x8e"),
+ e("\x8f"),
+ e("\x8f\xa1"),
+ s("\xef"),
+ u("\xc2"),
+ u("\xe0\x80"),
+ u("\xf0\x80\x80"),
+ u("\xf8\x80\x80\x80"),
+ u("\xfc\x80\x80\x80\x80"),
+
+ e("\xfe "),
+ e("\x8e "),
+ e("\x8f "),
+ e("\x8f\xa1 "),
+ s("\xef "),
+ u("\xc2 "),
+ u("\xe0\x80 "),
+ u("\xf0\x80\x80 "),
+ u("\xf8\x80\x80\x80 "),
+ u("\xfc\x80\x80\x80\x80 "),
+
+
+ e("\xa1\x8f\xa1\xa1"),
+
+ s("\x81."),
+ s("\x81@"),
+
+ u("\xfc"),
+ "\u3042",
+ "ascii",
+
+ "\u3042".encode("UTF-16LE"),
+ "\u3042".encode("UTF-16BE"),
+ ].each do |str|
+ assert_equal(str, eval(str.dump), "[ruby-dev:33142]")
+ end
+ end
+
+ def test_validate_redundant_utf8
+ bits_0x10ffff = "11110100 10001111 10111111 10111111"
+ [
+ "0xxxxxxx",
+ "110XXXXx 10xxxxxx",
+ "1110XXXX 10Xxxxxx 10xxxxxx",
+ "11110XXX 10XXxxxx 10xxxxxx 10xxxxxx",
+ "111110XX 10XXXxxx 10xxxxxx 10xxxxxx 10xxxxxx",
+ "1111110X 10XXXXxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx",
+ "11111110 10XXXXXx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx",
+ "11111111 10XXXXXX 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx",
+ ].each {|pat0|
+ [
+ pat0.gsub(/x/, '1'),
+ pat0.gsub(/x/, '0')
+ ].each {|pat1|
+ [
+ pat1.sub(/X([^X]*)\z/, '1\1').gsub(/X/, "0"),
+ pat1.gsub(/X/, "1"),
+ ].each {|pat2|
+ s = [pat2.gsub(/ /, "")].pack("B*").force_encoding("utf-8")
+ if pat2 <= bits_0x10ffff
+ assert_predicate(s, :valid_encoding?, "#{pat2}")
+ else
+ assert_not_predicate(s, :valid_encoding?, "#{pat2}")
+ end
+ }
+ if / / =~ pat0
+ pat3 = pat1.gsub(/X/, "0")
+ s = [pat3.gsub(/ /, "")].pack("B*").force_encoding("utf-8")
+ assert_not_predicate(s, :valid_encoding?, "#{pat3}")
+ end
+ }
+ }
+ end
+
+ def test_validate_surrogate
+ # 1110XXXX 10Xxxxxx 10xxxxxx : 3 bytes UTF-8
+ pats = [
+ "11101101 10011111 10111111", # just before surrogate high
+ "11101101 1010xxxx 10xxxxxx", # surrogate high
+ "11101101 1011xxxx 10xxxxxx", # surrogate low
+ "11101110 10000000 10000000", # just after surrogate low
+ ]
+ pats.values_at(1,2).each {|pat0|
+ [
+ pat0.gsub(/x/, '0'),
+ pat0.gsub(/x/, '1'),
+ ].each {|pat1|
+ s = [pat1.gsub(/ /, "")].pack("B*").force_encoding("utf-8")
+ assert_not_predicate(s, :valid_encoding?, "#{pat1}")
+ }
+ }
+ pats.values_at(0,3).each {|pat|
+ s = [pat.gsub(/ /, "")].pack("B*").force_encoding("utf-8")
+ assert_predicate(s, :valid_encoding?, "#{pat}")
+ }
+ end
+
+ def test_regexp_too_short_multibyte_character
+ assert_raise(SyntaxError) { eval('/\xfe/e') }
+ assert_raise(SyntaxError) { eval('/\x8e/e') }
+ assert_raise(SyntaxError) { eval('/\x8f/e') }
+ assert_raise(SyntaxError) { eval('/\x8f\xa1/e') }
+ assert_raise(SyntaxError) { eval('/\xef/s') }
+ assert_raise(SyntaxError) { eval('/\xc2/u') }
+ assert_raise(SyntaxError) { eval('/\xe0\x80/u') }
+ assert_raise(SyntaxError) { eval('/\xf0\x80\x80/u') }
+ assert_raise(SyntaxError) { eval('/\xf8\x80\x80\x80/u') }
+ assert_raise(SyntaxError) { eval('/\xfc\x80\x80\x80\x80/u') }
+
+ # raw 8bit
+ assert_raise(SyntaxError) { eval("/\xfe/e") }
+ assert_raise(SyntaxError) { eval("/\xc2/u") }
+
+ # invalid suffix
+ assert_raise(SyntaxError) { eval('/\xc2\xff/u') }
+ assert_raise(SyntaxError) { eval('/\xc2 /u') }
+ assert_raise(SyntaxError) { eval('/\xc2\x20/u') }
+ end
+
+ def test_regexp_generic
+ assert_regexp_generic_ascii(/a/)
+ assert_regexp_generic_ascii(Regexp.new(a("a")))
+ assert_regexp_generic_ascii(Regexp.new(e("a")))
+ assert_regexp_generic_ascii(Regexp.new(s("a")))
+ assert_regexp_generic_ascii(Regexp.new(u("a")))
+
+ [/a/, Regexp.new(a("a"))].each {|r|
+ assert_equal(0, r =~ a("a"))
+ assert_equal(0, r =~ e("a"))
+ assert_equal(0, r =~ s("a"))
+ assert_equal(0, r =~ u("a"))
+ assert_equal(nil, r =~ a("\xc2\xa1"))
+ assert_equal(nil, r =~ e("\xc2\xa1"))
+ assert_equal(nil, r =~ s("\xc2\xa1"))
+ assert_equal(nil, r =~ u("\xc2\xa1"))
+ }
+ end
+
+ def test_regexp_ascii_none
+ r = /a/n
+
+ assert_warning(%r{regexp match /.../n against to}) {
+ assert_regexp_generic_ascii(r)
+ }
+
+ assert_equal(0, r =~ a("a"))
+ assert_equal(0, r =~ e("a"))
+ assert_equal(0, r =~ s("a"))
+ assert_equal(0, r =~ u("a"))
+ assert_equal(nil, r =~ a("\xc2\xa1"))
+ assert_warning(%r{regexp match /.../n against to EUC-JP string}) {
+ assert_equal(nil, r =~ e("\xc2\xa1"))
+ }
+ assert_warning(%r{regexp match /.../n against to Windows-31J string}) {
+ assert_equal(nil, r =~ s("\xc2\xa1"))
+ }
+ assert_warning(%r{regexp match /.../n against to UTF-8 string}) {
+ assert_equal(nil, r =~ u("\xc2\xa1"))
+ }
+
+ assert_nothing_raised { eval(e("/\\x80/n")) }
+ end
+
+ def test_regexp_ascii
+ assert_regexp_fixed_ascii8bit(/\xc2\xa1/n)
+ assert_regexp_fixed_ascii8bit(eval(a(%{/\xc2\xa1/})))
+ assert_regexp_fixed_ascii8bit(eval(a(%{/\xc2\xa1/n})))
+ assert_regexp_fixed_ascii8bit(eval(a(%q{/\xc2\xa1/})))
+
+ s = '\xc2\xa1'
+ assert_regexp_fixed_ascii8bit(/#{s}/)
+
+ assert_raise(SyntaxError) { eval("/\xa1\xa1/n".force_encoding("euc-jp")) }
+
+ [/\xc2\xa1/n, eval(a(%{/\xc2\xa1/})), eval(a(%{/\xc2\xa1/n}))].each {|r|
+ assert_equal(nil, r =~ a("a"))
+ assert_equal(nil, r =~ e("a"))
+ assert_equal(nil, r =~ s("a"))
+ assert_equal(nil, r =~ u("a"))
+ assert_equal(0, r =~ a("\xc2\xa1"))
+ assert_raise(Encoding::CompatibilityError) { r =~ e("\xc2\xa1") }
+ assert_raise(Encoding::CompatibilityError) { r =~ s("\xc2\xa1") }
+ assert_raise(Encoding::CompatibilityError) { r =~ u("\xc2\xa1") }
+ }
+ end
+
+ def test_regexp_euc
+ assert_regexp_fixed_eucjp(/a/e)
+ assert_regexp_fixed_eucjp(/\xc2\xa1/e)
+ assert_regexp_fixed_eucjp(eval(e(%{/\xc2\xa1/})))
+ assert_regexp_fixed_eucjp(eval(e(%q{/\xc2\xa1/})))
+
+ [/a/e].each {|r|
+ assert_equal(0, r =~ a("a"))
+ assert_equal(0, r =~ e("a"))
+ assert_equal(0, r =~ s("a"))
+ assert_equal(0, r =~ u("a"))
+ assert_raise(Encoding::CompatibilityError) { r =~ a("\xc2\xa1") }
+ assert_equal(nil, r =~ e("\xc2\xa1"))
+ assert_raise(Encoding::CompatibilityError) { r =~ s("\xc2\xa1") }
+ assert_raise(Encoding::CompatibilityError) { r =~ u("\xc2\xa1") }
+ }
+
+ [/\xc2\xa1/e, eval(e(%{/\xc2\xa1/})), eval(e(%q{/\xc2\xa1/}))].each {|r|
+ assert_equal(nil, r =~ a("a"))
+ assert_equal(nil, r =~ e("a"))
+ assert_equal(nil, r =~ s("a"))
+ assert_equal(nil, r =~ u("a"))
+ assert_raise(Encoding::CompatibilityError) { r =~ a("\xc2\xa1") }
+ assert_equal(0, r =~ e("\xc2\xa1"))
+ assert_raise(Encoding::CompatibilityError) { r =~ s("\xc2\xa1") }
+ assert_raise(Encoding::CompatibilityError) { r =~ u("\xc2\xa1") }
+ }
+ end
+
+ def test_regexp_sjis
+ assert_regexp_fixed_sjis(/a/s)
+ assert_regexp_fixed_sjis(/\xc2\xa1/s)
+ assert_regexp_fixed_sjis(eval(s(%{/\xc2\xa1/})))
+ assert_regexp_fixed_sjis(eval(s(%q{/\xc2\xa1/})))
+ end
+
+ def test_regexp_windows_31j
+ begin
+ Regexp.new("\xa1".force_encoding("windows-31j")) =~ "\xa1\xa1".force_encoding("euc-jp")
+ rescue Encoding::CompatibilityError
+ err = $!
+ end
+ assert_match(/windows-31j/i, err.message)
+ end
+
+ def test_regexp_embed
+ r = eval(e("/\xc2\xa1/"))
+ assert_raise(RegexpError) { eval(s("/\xc2\xa1\#{r}/s")) }
+ assert_raise(RegexpError) { eval(s("/\#{r}\xc2\xa1/s")) }
+
+ r = /\xc2\xa1/e
+ assert_raise(RegexpError) { eval(s("/\xc2\xa1\#{r}/s")) }
+ assert_raise(RegexpError) { eval(s("/\#{r}\xc2\xa1/s")) }
+
+ r = eval(e("/\xc2\xa1/"))
+ assert_raise(RegexpError) { /\xc2\xa1#{r}/s }
+
+ r = /\xc2\xa1/e
+ assert_raise(RegexpError) { /\xc2\xa1#{r}/s }
+
+ r1 = Regexp.new('foo'.force_encoding("ascii-8bit"))
+ r2 = eval('/bar#{r1}/'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::US_ASCII, r2.encoding)
+
+ r1 = Regexp.new('foo'.force_encoding("us-ascii"))
+ r2 = eval('/bar#{r1}/'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::US_ASCII, r2.encoding)
+
+ r1 = Regexp.new('foo'.force_encoding("ascii-8bit"))
+ r2 = eval('/bar#{r1}/'.force_encoding('us-ascii'))
+ assert_equal(Encoding::US_ASCII, r2.encoding)
+
+ r1 = Regexp.new('foo'.force_encoding("us-ascii"))
+ r2 = eval('/bar#{r1}/'.force_encoding('us-ascii'))
+ assert_equal(Encoding::US_ASCII, r2.encoding)
+
+ r1 = Regexp.new('\xa1'.force_encoding("ascii-8bit"))
+ r2 = eval('/bar#{r1}/'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::ASCII_8BIT, r2.encoding)
+
+ r1 = Regexp.new('\xa1'.force_encoding("ascii-8bit"))
+ r2 = eval('/bar#{r1}/'.force_encoding('us-ascii'))
+ assert_equal(Encoding::ASCII_8BIT, r2.encoding)
+
+ r1 = Regexp.new('foo'.force_encoding("ascii-8bit"))
+ r2 = eval('/\xa1#{r1}/'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::ASCII_8BIT, r2.encoding)
+
+ r1 = Regexp.new('foo'.force_encoding("us-ascii"))
+ r2 = eval('/\xa1#{r1}/'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::ASCII_8BIT, r2.encoding)
+
+ r1 = Regexp.new('\xa1'.force_encoding("ascii-8bit"))
+ r2 = eval('/\xa1#{r1}/'.force_encoding('ascii-8bit'))
+ assert_equal(Encoding::ASCII_8BIT, r2.encoding)
+ end
+
+ def test_regexp_named_class
+ assert_match(/[[:space:]]/u, "\u{00a0}")
+ assert_match(/[[:space:]]/, "\u{00a0}")
+ end
+
+ def test_regexp_property
+ s = '\p{Hiragana}'.force_encoding("euc-jp")
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ r = nil
+ assert_nothing_raised {
+ r = Regexp.new(s)
+ }
+ assert_predicate(r, :fixed_encoding?)
+ assert_match(r, "\xa4\xa2".force_encoding("euc-jp"))
+
+ r = eval('/\p{Hiragana}/'.force_encoding("euc-jp"))
+ assert_predicate(r, :fixed_encoding?)
+ assert_match(r, "\xa4\xa2".force_encoding("euc-jp"))
+
+ r = /\p{Hiragana}/e
+ assert_predicate(r, :fixed_encoding?)
+ assert_match(r, "\xa4\xa2".force_encoding("euc-jp"))
+
+ r = /\p{AsciI}/e
+ assert_predicate(r, :fixed_encoding?)
+ assert_match(r, "a".force_encoding("euc-jp"))
+
+ r = /\p{hiraganA}/e
+ assert_predicate(r, :fixed_encoding?)
+ assert_match(r, "\xa4\xa2".force_encoding("euc-jp"))
+
+ r = eval('/\u{3042}\p{Hiragana}/'.force_encoding("euc-jp"))
+ assert_predicate(r, :fixed_encoding?)
+ assert_equal(Encoding::UTF_8, r.encoding)
+
+ r = eval('/\p{Hiragana}\u{3042}/'.force_encoding("euc-jp"))
+ assert_predicate(r, :fixed_encoding?)
+ assert_equal(Encoding::UTF_8, r.encoding)
+ end
+
+ def test_regexp_embed_preprocess
+ r1 = /\xa4\xa2/e
+ r2 = /#{r1}/
+ assert_include(r2.source, r1.source)
+ end
+
+ def test_begin_end_offset
+ str = e("\244\242\244\244\244\246\244\250\244\252a")
+ assert(/(a)/ =~ str)
+ assert_equal("a", $&)
+ assert_equal(5, $~.begin(0))
+ assert_equal(6, $~.end(0))
+ assert_equal([5,6], $~.offset(0))
+ assert_equal(5, $~.begin(1))
+ assert_equal(6, $~.end(1))
+ assert_equal([5,6], $~.offset(1))
+ end
+
+ def test_begin_end_offset_sjis
+ str = s("\x81@@")
+ assert(/@/ =~ str)
+ assert_equal(s("\x81@"), $`)
+ assert_equal("@", $&)
+ assert_equal("", $')
+ assert_equal([1,2], $~.offset(0))
+ end
+
+ def test_quote
+ assert_regexp_generic_ascii(/#{Regexp.quote(a("a"))}#{Regexp.quote(e("e"))}/)
+
+ assert_encoding("US-ASCII", Regexp.quote(a("")).encoding)
+ assert_encoding("US-ASCII", Regexp.quote(e("")).encoding)
+ assert_encoding("US-ASCII", Regexp.quote(s("")).encoding)
+ assert_encoding("US-ASCII", Regexp.quote(u("")).encoding)
+ assert_encoding("US-ASCII", Regexp.quote(a("a")).encoding)
+ assert_encoding("US-ASCII", Regexp.quote(e("a")).encoding)
+ assert_encoding("US-ASCII", Regexp.quote(s("a")).encoding)
+ assert_encoding("US-ASCII", Regexp.quote(u("a")).encoding)
+
+ assert_encoding("ASCII-8BIT", Regexp.quote(a("\xc2\xa1")).encoding)
+ assert_encoding("EUC-JP", Regexp.quote(e("\xc2\xa1")).encoding)
+ assert_encoding("Windows-31J", Regexp.quote(s("\xc2\xa1")).encoding)
+ assert_encoding("UTF-8", Regexp.quote(u("\xc2\xa1")).encoding)
+ end
+
+ def test_union_0
+ r = Regexp.union
+ assert_regexp_generic_ascii(r)
+ assert_not_match(r, a(""))
+ assert_not_match(r, e(""))
+ assert_not_match(r, s(""))
+ assert_not_match(r, u(""))
+ end
+
+ def test_union_1_asciionly_string
+ assert_regexp_generic_ascii(Regexp.union(a("")))
+ assert_regexp_generic_ascii(Regexp.union(e("")))
+ assert_regexp_generic_ascii(Regexp.union(s("")))
+ assert_regexp_generic_ascii(Regexp.union(u("")))
+ assert_regexp_generic_ascii(Regexp.union(a("a")))
+ assert_regexp_generic_ascii(Regexp.union(e("a")))
+ assert_regexp_generic_ascii(Regexp.union(s("a")))
+ assert_regexp_generic_ascii(Regexp.union(u("a")))
+ assert_regexp_generic_ascii(Regexp.union(a("\t")))
+ assert_regexp_generic_ascii(Regexp.union(e("\t")))
+ assert_regexp_generic_ascii(Regexp.union(s("\t")))
+ assert_regexp_generic_ascii(Regexp.union(u("\t")))
+ end
+
+ def test_union_1_nonascii_string
+ assert_regexp_fixed_ascii8bit(Regexp.union(a("\xc2\xa1")))
+ assert_regexp_fixed_eucjp(Regexp.union(e("\xc2\xa1")))
+ assert_regexp_fixed_sjis(Regexp.union(s("\xc2\xa1")))
+ assert_regexp_fixed_utf8(Regexp.union(u("\xc2\xa1")))
+ end
+
+ def test_union_1_regexp
+ assert_regexp_generic_ascii(Regexp.union(//))
+ assert_warning(%r{regexp match /.../n against to}) {
+ assert_regexp_generic_ascii(Regexp.union(//n))
+ }
+ assert_regexp_fixed_eucjp(Regexp.union(//e))
+ assert_regexp_fixed_sjis(Regexp.union(//s))
+ assert_regexp_fixed_utf8(Regexp.union(//u))
+ end
+
+ def test_union_2
+ ary = [
+ a(""), e(""), s(""), u(""),
+ a("\xc2\xa1"), e("\xc2\xa1"), s("\xc2\xa1"), u("\xc2\xa1")
+ ]
+ ary.each {|s1|
+ ary.each {|s2|
+ if s1.empty?
+ if s2.empty?
+ assert_regexp_generic_ascii(Regexp.union(s1, s2))
+ else
+ r = Regexp.union(s1, s2)
+ assert_regexp_fixed_encoding(r)
+ assert_equal(s2.encoding, r.encoding)
+ end
+ else
+ if s2.empty?
+ r = Regexp.union(s1, s2)
+ assert_regexp_fixed_encoding(r)
+ assert_equal(s1.encoding, r.encoding)
+ else
+ if s1.encoding == s2.encoding
+ r = Regexp.union(s1, s2)
+ assert_regexp_fixed_encoding(r)
+ assert_equal(s1.encoding, r.encoding)
+ else
+ assert_raise(ArgumentError) { Regexp.union(s1, s2) }
+ end
+ end
+ end
+ }
+ }
+ end
+
+ def test_dynamic_ascii_regexp
+ assert_warning(%r{regexp match /.../n against to}) {
+ assert_regexp_generic_ascii(/#{ }/n)
+ }
+ assert_regexp_fixed_ascii8bit(/#{ }\xc2\xa1/n)
+ assert_regexp_fixed_ascii8bit(/\xc2\xa1#{ }/n)
+ assert_nothing_raised { s1, s2 = a('\xc2'), a('\xa1'); /#{s1}#{s2}/ }
+ end
+
+ def test_dynamic_eucjp_regexp
+ assert_regexp_fixed_eucjp(/#{ }/e)
+ assert_regexp_fixed_eucjp(/#{ }\xc2\xa1/e)
+ assert_regexp_fixed_eucjp(/\xc2\xa1#{ }/e)
+ assert_raise(SyntaxError) { eval('/\xc2#{ }/e') }
+ assert_raise(SyntaxError) { eval('/#{ }\xc2/e') }
+ assert_raise(SyntaxError) { eval('/\xc2#{ }\xa1/e') }
+ assert_raise(ArgumentError) { s1, s2 = e('\xc2'), e('\xa1'); /#{s1}#{s2}/ }
+ end
+
+ def test_dynamic_sjis_regexp
+ assert_regexp_fixed_sjis(/#{ }/s)
+ assert_regexp_fixed_sjis(/#{ }\xc2\xa1/s)
+ assert_regexp_fixed_sjis(/\xc2\xa1#{ }/s)
+ assert_raise(SyntaxError) { eval('/\x81#{ }/s') }
+ assert_raise(SyntaxError) { eval('/#{ }\x81/s') }
+ assert_raise(SyntaxError) { eval('/\x81#{ }\xa1/s') }
+ assert_raise(ArgumentError) { s1, s2 = s('\x81'), s('\xa1'); /#{s1}#{s2}/ }
+ end
+
+ def test_dynamic_utf8_regexp
+ assert_regexp_fixed_utf8(/#{ }/u)
+ assert_regexp_fixed_utf8(/#{ }\xc2\xa1/u)
+ assert_regexp_fixed_utf8(/\xc2\xa1#{ }/u)
+ assert_raise(SyntaxError) { eval('/\xc2#{ }/u') }
+ assert_raise(SyntaxError) { eval('/#{ }\xc2/u') }
+ assert_raise(SyntaxError) { eval('/\xc2#{ }\xa1/u') }
+ assert_raise(ArgumentError) { s1, s2 = u('\xc2'), u('\xa1'); /#{s1}#{s2}/ }
+ end
+
+ def test_regexp_unicode
+ assert_nothing_raised { eval '/\u{0}/u' }
+ assert_nothing_raised { eval '/\u{D7FF}/u' }
+ assert_raise(SyntaxError) { eval '/\u{D800}/u' }
+ assert_raise(SyntaxError) { eval '/\u{DFFF}/u' }
+ assert_nothing_raised { eval '/\u{E000}/u' }
+ assert_nothing_raised { eval '/\u{10FFFF}/u' }
+ assert_raise(SyntaxError) { eval '/\u{110000}/u' }
+ end
+
+ def test_regexp_mixed_unicode
+ assert_raise(SyntaxError) { eval(a(%{/\xc2\xa1\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(e(%{/\xc2\xa1\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(s(%{/\xc2\xa1\\u{6666}/})) }
+ assert_nothing_raised { eval(u(%{/\xc2\xa1\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(a(%{/\\u{6666}\xc2\xa1/})) }
+ assert_raise(SyntaxError) { eval(e(%{/\\u{6666}\xc2\xa1/})) }
+ assert_raise(SyntaxError) { eval(s(%{/\\u{6666}\xc2\xa1/})) }
+ assert_nothing_raised { eval(u(%{/\\u{6666}\xc2\xa1/})) }
+
+ assert_raise(SyntaxError) { eval(a(%{/\\xc2\\xa1\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(e(%{/\\xc2\\xa1\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(s(%{/\\xc2\\xa1\\u{6666}/})) }
+ assert_nothing_raised { eval(u(%{/\\xc2\\xa1\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(a(%{/\\u{6666}\\xc2\\xa1/})) }
+ assert_raise(SyntaxError) { eval(e(%{/\\u{6666}\\xc2\\xa1/})) }
+ assert_raise(SyntaxError) { eval(s(%{/\\u{6666}\\xc2\\xa1/})) }
+ assert_nothing_raised { eval(u(%{/\\u{6666}\\xc2\\xa1/})) }
+
+ assert_raise(SyntaxError) { eval(a(%{/\xc2\xa1#{ }\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(e(%{/\xc2\xa1#{ }\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(s(%{/\xc2\xa1#{ }\\u{6666}/})) }
+ assert_nothing_raised { eval(u(%{/\xc2\xa1#{ }\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(a(%{/\\u{6666}#{ }\xc2\xa1/})) }
+ assert_raise(SyntaxError) { eval(e(%{/\\u{6666}#{ }\xc2\xa1/})) }
+ assert_raise(SyntaxError) { eval(s(%{/\\u{6666}#{ }\xc2\xa1/})) }
+ assert_nothing_raised { eval(u(%{/\\u{6666}#{ }\xc2\xa1/})) }
+
+ assert_raise(SyntaxError) { eval(a(%{/\\xc2\\xa1#{ }\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(e(%{/\\xc2\\xa1#{ }\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(s(%{/\\xc2\\xa1#{ }\\u{6666}/})) }
+ assert_nothing_raised { eval(u(%{/\\xc2\\xa1#{ }\\u{6666}/})) }
+ assert_raise(SyntaxError) { eval(a(%{/\\u{6666}#{ }\\xc2\\xa1/})) }
+ assert_raise(SyntaxError) { eval(e(%{/\\u{6666}#{ }\\xc2\\xa1/})) }
+ assert_raise(SyntaxError) { eval(s(%{/\\u{6666}#{ }\\xc2\\xa1/})) }
+ assert_nothing_raised { eval(u(%{/\\u{6666}#{ }\\xc2\\xa1/})) }
+ end
+
+ def test_str_allocate
+ s = String.allocate
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ end
+
+ def test_str_String
+ s = String(10)
+ assert_equal(Encoding::US_ASCII, s.encoding)
+ end
+
+ def test_sprintf_c
+ assert_strenc("\x80", 'ASCII-8BIT', a("%c") % 128)
+ #assert_raise(ArgumentError) { a("%c") % 0xc2a1 }
+ assert_strenc("\xc2\xa1", 'EUC-JP', e("%c") % 0xc2a1)
+ assert_raise(ArgumentError) { e("%c") % 0xc2 }
+ assert_strenc("\xc2", 'Windows-31J', s("%c") % 0xc2)
+ #assert_raise(ArgumentError) { s("%c") % 0xc2a1 }
+ assert_strenc("\u{c2a1}", 'UTF-8', u("%c") % 0xc2a1)
+ assert_strenc("\u{c2}", 'UTF-8', u("%c") % 0xc2)
+ assert_raise(Encoding::CompatibilityError) {
+ "%s%s" % [s("\xc2\xa1"), e("\xc2\xa1")]
+ }
+ end
+
+ def test_sprintf_p
+ Encoding.list.each do |e|
+ format = "%p".force_encoding(e)
+ ['', 'a', "\xC2\xA1", "\x00"].each do |s|
+ s.force_encoding(e)
+ enc = (''.force_encoding(e) + s.inspect).encoding
+ assert_strenc(s.inspect, enc, format % s)
+ end
+ s = "\xC2\xA1".force_encoding(e)
+ enc = ('' + s.inspect).encoding
+ assert_strenc('%10s' % s.inspect, enc, "%10p" % s)
+ end
+ end
+
+ def test_sprintf_s
+ assert_strenc('', 'ASCII-8BIT', a("%s") % a(""))
+ assert_strenc('', 'EUC-JP', e("%s") % e(""))
+ assert_strenc('', 'Windows-31J', s("%s") % s(""))
+ assert_strenc('', 'UTF-8', u("%s") % u(""))
+
+ assert_strenc('a', 'ASCII-8BIT', a("%s") % a("a"))
+ assert_strenc('a', 'EUC-JP', e("%s") % e("a"))
+ assert_strenc('a', 'Windows-31J', s("%s") % s("a"))
+ assert_strenc('a', 'UTF-8', u("%s") % u("a"))
+
+ assert_strenc("\xC2\xA1", 'ASCII-8BIT', a("%s") % a("\xc2\xa1"))
+ assert_strenc("\xC2\xA1", 'EUC-JP', e("%s") % e("\xc2\xa1"))
+ #assert_strenc("\xC2\xA1", 'Windows-31J', s("%s") % s("\xc2\xa1"))
+ assert_strenc("\xC2\xA1", 'UTF-8', u("%s") % u("\xc2\xa1"))
+
+ assert_strenc(" \xC2\xA1", 'ASCII-8BIT', "%10s" % a("\xc2\xa1"))
+ assert_strenc(" \xA1\xA1", 'EUC-JP', "%10s" % e("\xa1\xa1"))
+ #assert_strenc(" \xC2\xA1", 'Windows-31J', "%10s" % s("\xc2\xa1"))
+ assert_strenc(" \xC2\xA1", 'UTF-8', "%10s" % u("\xc2\xa1"))
+
+ assert_strenc("\x00", 'ASCII-8BIT', a("%s") % a("\x00"))
+ assert_strenc("\x00", 'EUC-JP', e("%s") % e("\x00"))
+ assert_strenc("\x00", 'Windows-31J', s("%s") % s("\x00"))
+ assert_strenc("\x00", 'UTF-8', u("%s") % u("\x00"))
+ assert_equal("EUC-JP", (e("\xc2\xa1 %s") % "foo").encoding.name)
+ end
+
+ def test_str_lt
+ assert_operator(a("a"), :<, a("\xa1"))
+ assert_operator(a("a"), :<, s("\xa1"))
+ assert_operator(s("a"), :<, a("\xa1"))
+ end
+
+ def test_str_multiply
+ str = "\u3042"
+ assert_equal(true, (str * 0).ascii_only?, "[ruby-dev:33895]")
+ assert_equal(false, (str * 1).ascii_only?)
+ assert_equal(false, (str * 2).ascii_only?)
+ end
+
+ def test_str_aref
+ assert_equal(a("\xc2"), a("\xc2\xa1")[0])
+ assert_equal(a("\xa1"), a("\xc2\xa1")[1])
+ assert_equal(nil, a("\xc2\xa1")[2])
+ assert_equal(e("\xc2\xa1"), e("\xc2\xa1")[0])
+ assert_equal(nil, e("\xc2\xa1")[1])
+ assert_equal(s("\xc2"), s("\xc2\xa1")[0])
+ assert_equal(s("\xa1"), s("\xc2\xa1")[1])
+ assert_equal(nil, s("\xc2\xa1")[2])
+ assert_equal(u("\xc2\xa1"), u("\xc2\xa1")[0])
+ assert_equal(nil, u("\xc2\xa1")[1])
+
+ str = "\u3042"
+ assert_equal(true, str[0, 0].ascii_only?, "[ruby-dev:33895]")
+ assert_equal(false, str[0, 1].ascii_only?)
+ assert_equal(false, str[0..-1].ascii_only?)
+ end
+
+ def test_utf8str_aref
+ s = "abcdefghijklmnopqrstuvwxyz\u{3042 3044 3046 3048 304A}"
+ assert_equal("a", s[0])
+ assert_equal("h", s[7])
+ assert_equal("i", s[8])
+ assert_equal("j", s[9])
+ assert_equal("\u{3044}", s[27])
+ assert_equal("\u{3046}", s[28])
+ assert_equal("\u{3048}", s[29])
+ s = "abcdefghijklmnopqrstuvw\u{3042 3044 3046 3048 304A}"
+ assert_equal("\u{3044}", s[24])
+ end
+
+ def test_str_aref_len
+ assert_equal(a("\xa1"), a("\xc2\xa1\xc2\xa2\xc2\xa3")[1, 1])
+ assert_equal(a("\xa1\xc2"), a("\xc2\xa1\xc2\xa2\xc2\xa3")[1, 2])
+
+ assert_equal(e("\xc2\xa2"), e("\xc2\xa1\xc2\xa2\xc2\xa3")[1, 1])
+ assert_equal(e("\xc2\xa2\xc2\xa3"), e("\xc2\xa1\xc2\xa2\xc2\xa3")[1, 2])
+
+ assert_equal(s("\xa1"), s("\xc2\xa1\xc2\xa2\xc2\xa3")[1, 1])
+ assert_equal(s("\xa1\xc2"), s("\xc2\xa1\xc2\xa2\xc2\xa3")[1, 2])
+
+ assert_equal(u("\xc2\xa2"), u("\xc2\xa1\xc2\xa2\xc2\xa3")[1, 1])
+ assert_equal(u("\xc2\xa2\xc2\xa3"), u("\xc2\xa1\xc2\xa2\xc2\xa3")[1, 2])
+ end
+
+ def test_str_aref_substr
+ assert_equal(a("\xa1\xc2"), a("\xc2\xa1\xc2\xa2\xc2\xa3")[a("\xa1\xc2")])
+ assert_raise(Encoding::CompatibilityError) { a("\xc2\xa1\xc2\xa2\xc2\xa3")[e("\xa1\xc2")] }
+
+ assert_equal(nil, e("\xc2\xa1\xc2\xa2\xc2\xa3")[e("\xa1\xc2")])
+ assert_raise(Encoding::CompatibilityError) { e("\xc2\xa1\xc2\xa2\xc2\xa3")[s("\xa1\xc2")] }
+
+ assert_equal(s("\xa1\xc2"), s("\xc2\xa1\xc2\xa2\xc2\xa3")[s("\xa1\xc2")])
+ assert_raise(Encoding::CompatibilityError) { s("\xc2\xa1\xc2\xa2\xc2\xa3")[u("\xa1\xc2")] }
+
+ assert_equal(nil, u("\xc2\xa1\xc2\xa2\xc2\xa3")[u("\xa1\xc2")])
+ assert_raise(Encoding::CompatibilityError) { u("\xc2\xa1\xc2\xa2\xc2\xa3")[a("\xa1\xc2")] }
+ assert_nil(e("\xa1\xa2\xa3\xa4")[e("\xa2\xa3")])
+
+ bug2379 = '[ruby-core:26787]'
+ assert_equal("\u{439}", "\u{439}"[0, 30], bug2379)
+ assert_equal("\u{439}", "a\u{439}"[1, 30], bug2379)
+ assert_equal("\u{439}", "a\u{439}bcdefghijklmnop"[1, 1][0, 1], bug2379)
+ end
+
+ def test_str_aref_force_encoding
+ bug5836 = '[ruby-core:41896]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ s = "abc".force_encoding(enc)
+ assert_equal("", s[3, 1], bug5836)
+ end
+ end
+
+ def test_aset
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_raise(Encoding::CompatibilityError){s["\xb0\xa3"] = "foo"}
+
+ a = ua("a")
+ a[/a/] = u("")
+ assert_equal Encoding::US_ASCII, a.encoding
+ end
+
+ def test_str_center
+ assert_encoding("EUC-JP", "a".center(5, e("\xa1\xa2")).encoding)
+ assert_encoding("EUC-JP", e("\xa3\xb0").center(10).encoding)
+ end
+
+ def test_squeeze
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb1\xa3\xb3\xa3\xb4")
+ assert_equal(e("\xa3\xb0\xa3\xb1\xa3\xb3\xa3\xb4"), s.squeeze)
+ end
+
+ def test_tr
+ s = s("\x81\x41")
+ assert_equal(s.tr("A", "B"), s)
+ assert_equal(s.tr_s("A", "B"), s)
+
+ assert_nothing_raised {
+ "a".force_encoding("ASCII-8BIT").tr(a("a"), a("a"))
+ }
+
+ assert_equal(e("\xA1\xA1"), a("a").tr(a("a"), e("\xA1\xA1")))
+
+ assert_equal("X\u3042\u3044X", "A\u3042\u3044\u3046".tr("^\u3042\u3044", "X"))
+ assert_equal("\u3042\u3046" * 100, ("\u3042\u3044" * 100).tr("\u3044", "\u3046"))
+ assert_equal("Y", "\u3042".tr("^X", "Y"))
+ end
+
+ def test_tr_s
+ assert_equal("\xA1\xA1".force_encoding("EUC-JP"),
+ "a".force_encoding("ASCII-8BIT").tr("a".force_encoding("ASCII-8BIT"), "\xA1\xA1".force_encoding("EUC-JP")))
+ end
+
+ def test_count
+ assert_equal(0, e("\xa1\xa2").count("z"))
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_raise(Encoding::CompatibilityError){s.count(a("\xa3\xb0"))}
+ end
+
+ def test_count_sjis_trailing_byte
+ bug10078 = '[ruby-dev:48442] [Bug #10078]'
+ assert_equal(0, s("\x98\x61").count("a"), bug10078)
+ end
+
+ def test_delete
+ assert_equal(1, e("\xa1\xa2").delete("z").length)
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_raise(Encoding::CompatibilityError){s.delete(a("\xa3\xb2"))}
+
+ a = "\u3042\u3044\u3046\u3042\u3044\u3046"
+ a.delete!("\u3042\u3044", "^\u3044")
+ assert_equal("\u3044\u3046\u3044\u3046", a)
+ end
+
+ def test_include?
+ assert_equal(false, e("\xa1\xa2\xa3\xa4").include?(e("\xa3")))
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_equal(false, s.include?(e("\xb0\xa3")))
+ end
+
+ def test_index
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_nil(s.index(e("\xb3\xa3")))
+ assert_nil(e("\xa1\xa2\xa3\xa4").index(e("\xa3")))
+ assert_nil(e("\xa1\xa2\xa3\xa4").rindex(e("\xa3")))
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_raise(Encoding::CompatibilityError){s.rindex(a("\xb1\xa3"))}
+ end
+
+ def test_next
+ s1 = e("\xa1\xa1")
+ s2 = s1.dup
+ (94*94+94).times { s2.next! }
+ assert_not_equal(s1, s2)
+ end
+
+ def test_sub
+ s = "abc".sub(/b/, "\xa1\xa1".force_encoding("euc-jp"))
+ assert_encoding("EUC-JP", s.encoding)
+ assert_equal(Encoding::EUC_JP, "\xa4\xa2".force_encoding("euc-jp").sub(/./, '\&').encoding)
+ assert_equal(Encoding::EUC_JP, "\xa4\xa2".force_encoding("euc-jp").gsub(/./, '\&').encoding)
+ end
+
+ def test_sub2
+ s = "\x80".force_encoding("ASCII-8BIT")
+ r = Regexp.new("\x80".force_encoding("ASCII-8BIT"))
+ s2 = s.sub(r, "")
+ assert_empty(s2)
+ assert_predicate(s2, :ascii_only?)
+ end
+
+ def test_sub3
+ repl = "\x81".force_encoding("sjis")
+ assert_equal(false, repl.valid_encoding?)
+ s = "a@".sub(/a/, repl)
+ assert_predicate(s, :valid_encoding?)
+ end
+
+ def test_insert
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_equal(e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4a"), s.insert(-1, "a"))
+ end
+
+ def test_scan
+ assert_equal(["a"], e("\xa1\xa2a\xa3\xa4").scan(/a/))
+ end
+
+ def test_dup_scan
+ s1 = e("\xa4\xa2")*100
+ s2 = s1.dup.force_encoding("ascii-8bit")
+ s2.scan(/\A./n) {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.encoding)
+ }
+ end
+
+ def test_dup_aref
+ s1 = e("\xa4\xa2")*100
+ s2 = s1.dup.force_encoding("ascii-8bit")
+ assert_equal(Encoding::ASCII_8BIT, s2[10..-1].encoding)
+ end
+
+ def test_upto
+ s1 = e("\xa1\xa2")
+ s2 = s("\xa1\xa2")
+ assert_raise(Encoding::CompatibilityError){s1.upto(s2) {|x| break }}
+ end
+
+ def test_casecmp
+ s1 = s("\x81\x41")
+ s2 = s("\x81\x61")
+ assert_not_equal(0, s1.casecmp(s2))
+ end
+
+ def test_reverse
+ bug11387 = '[ruby-dev:49189] [Bug #11387]'
+ s1 = u("abcdefghij\xf0")
+ s2 = s1.reverse
+ assert_not_predicate(s1, :valid_encoding?, bug11387)
+ assert_equal(u("\xf0jihgfedcba"), s2)
+ assert_not_predicate(s2, :valid_encoding?, bug11387)
+ end
+
+ def test_reverse_bang
+ s = u("abcdefghij\xf0")
+ s.reverse!
+ assert_equal(u("\xf0jihgfedcba"), s)
+ end
+
+ def test_plus
+ assert_raise(Encoding::CompatibilityError){u("\xe3\x81\x82") + a("\xa1")}
+ end
+
+ def test_chomp
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_raise(Encoding::CompatibilityError){s.chomp(s("\xa3\xb4"))}
+ end
+
+ def test_gsub
+ s = 'abc'
+ s.ascii_only?
+ s.gsub!(/b/, "\x80")
+ assert_equal(false, s.ascii_only?, "[ruby-core:14566] reported by Sam Ruby")
+
+ s = "abc".force_encoding(Encoding::ASCII_8BIT)
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+
+ assert_raise(Encoding::CompatibilityError) {
+ "abc".gsub(/[ac]/) {
+ $& == "a" ? "\xc2\xa1".force_encoding("euc-jp") :
+ "\xc2\xa1".force_encoding("utf-8")
+ }
+ }
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_equal(e("\xa3\xb0z\xa3\xb2\xa3\xb3\xa3\xb4"), s.gsub(/\xa3\xb1/e, "z"))
+
+ assert_equal(Encoding::ASCII_8BIT, (a("").gsub(//) { e("") }.encoding))
+ assert_equal(Encoding::ASCII_8BIT, (a("a").gsub(/a/) { e("") }.encoding))
+ end
+
+ def test_end_with
+ s1 = s("\x81\x40")
+ s2 = "@"
+ assert_equal(false, s1.end_with?(s2), "#{encdump s1}.end_with?(#{encdump s2})")
+ each_encoding("\u3042\u3044", "\u3044") do |_s1, _s2|
+ assert_equal(true, _s1.end_with?(_s2), "#{encdump _s1}.end_with?(#{encdump _s2})")
+ end
+ each_encoding("\u3042a\u3044", "a\u3044") do |_s1, _s2|
+ assert_equal(true, _s1.end_with?(_s2), "#{encdump _s1}.end_with?(#{encdump _s2})")
+ end
+ end
+
+ def test_each_line
+ s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
+ assert_raise(Encoding::CompatibilityError){s.each_line(a("\xa3\xb1")) {|l| }}
+ s = e("\xa4\xa2\nfoo")
+
+ actual = []
+ s.each_line {|line| actual << line }
+ expected = [e("\xa4\xa2\n"), e("foo")]
+ assert_equal(expected, actual)
+ end
+
+ def test_each_char
+ a = [e("\xa4\xa2"), "b", e("\xa4\xa4"), "c"]
+ s = "\xa4\xa2b\xa4\xa4c".force_encoding("euc-jp")
+ assert_equal(a, s.each_char.to_a, "[ruby-dev:33211] #{encdump s}.each_char.to_a")
+ end
+
+ def test_str_concat
+ assert_equal(1, "".concat(0xA2).size)
+ assert_equal(Encoding::ASCII_8BIT, "".force_encoding("US-ASCII").concat(0xA2).encoding)
+ assert_equal("A\x84\x31\xA4\x39".force_encoding("GB18030"),
+ "A".force_encoding("GB18030") << 0x8431A439)
+ end
+
+ def test_regexp_match
+ assert_equal([0,0], //.match("\xa1\xa1".force_encoding("euc-jp"),-1).offset(0))
+ assert_equal(0, // =~ :a)
+ end
+
+ def test_split
+ assert_equal(e("\xa1\xa2\xa1\xa3").split(//),
+ [e("\xa1\xa2"), e("\xa1\xa3")],
+ '[ruby-dev:32452]')
+
+ each_encoding("abc,def", ",", "abc", "def") do |str, sep, *expected|
+ assert_equal(expected, str.split(sep, -1))
+ end
+ each_encoding("abc\0def", "\0", "abc", "def") do |str, sep, *expected|
+ assert_equal(expected, str.split(sep, -1))
+ end
+ end
+
+ def test_nonascii_method_name
+ eval(e("def \xc2\xa1() @nonascii_method_name = :e end"))
+ eval(u("def \xc2\xa1() @nonascii_method_name = :u end"))
+ eval(e("\xc2\xa1()"))
+ assert_equal(:e, @nonascii_method_name)
+ eval(u("\xc2\xa1()"))
+ assert_equal(:u, @nonascii_method_name)
+ me = method(e("\xc2\xa1"))
+ mu = method(u("\xc2\xa1"))
+ assert_not_equal(me.name, mu.name)
+ assert_not_equal(me.inspect, mu.inspect)
+ assert_equal(e("\xc2\xa1"), me.name.to_s)
+ assert_equal(u("\xc2\xa1"), mu.name.to_s)
+ end
+
+ def test_symbol
+ s1 = "\xc2\xa1".force_encoding("euc-jp").intern
+ s2 = "\xc2\xa1".force_encoding("utf-8").intern
+ assert_not_equal(s1, s2)
+ end
+
+ def test_symbol_op
+ ops = %w"
+ .. ... + - * / % ** +@ -@ | ^ & ! <=> > >= < <= ==
+ === != =~ !~ ~ ! [] []= << >> :: `
+ "
+ ops.each do |op|
+ assert_equal(Encoding::US_ASCII, op.intern.encoding, "[ruby-dev:33449]")
+ end
+ end
+
+ def test_chr
+ 0.upto(255) {|b|
+ assert_equal([b].pack("C"), b.chr)
+ }
+ assert_equal("\x84\x31\xA4\x39".force_encoding("GB18030"), 0x8431A439.chr("GB18030"))
+ e = assert_raise(RangeError) {
+ 2206368128.chr(Encoding::UTF_8)
+ }
+ assert_not_match(/-\d+ out of char range/, e.message)
+
+ assert_raise(RangeError){ 0x80.chr("US-ASCII") }
+ assert_raise(RangeError){ 0x80.chr("SHIFT_JIS") }
+ assert_raise(RangeError){ 0xE0.chr("SHIFT_JIS") }
+ assert_raise(RangeError){ 0x100.chr("SHIFT_JIS") }
+ assert_raise(RangeError){ 0xA0.chr("EUC-JP") }
+ assert_raise(RangeError){ 0x100.chr("EUC-JP") }
+ assert_raise(RangeError){ 0xA1A0.chr("EUC-JP") }
+ end
+
+ def test_marshal
+ s1 = "\xa1\xa1".force_encoding("euc-jp")
+ s2 = Marshal.load(Marshal.dump(s1))
+ assert_equal(s1, s2)
+ end
+
+ def test_env
+ locale_encoding = Encoding.find("locale")
+ ENV.each {|k, v|
+ assert_equal(locale_encoding, k.encoding, k)
+ assert_equal(locale_encoding, v.encoding, v)
+ }
+ end
+
+ def test_empty_string
+ assert_equal(Encoding::US_ASCII, "".encoding)
+ end
+
+ def test_nil_to_s
+ assert_equal(Encoding::US_ASCII, nil.to_s.encoding)
+ end
+
+ def test_nil_inspect
+ assert_equal(Encoding::US_ASCII, nil.inspect.encoding)
+ end
+
+ def test_true_to_s
+ assert_equal(Encoding::US_ASCII, true.to_s.encoding)
+ end
+
+ def test_false_to_s
+ assert_equal(Encoding::US_ASCII, false.to_s.encoding)
+ end
+
+ def test_fixnum_to_s
+ assert_equal(Encoding::US_ASCII, 1.to_s.encoding)
+ end
+
+ def test_float_to_s
+ assert_equal(Encoding::US_ASCII, 1.0.to_s.encoding)
+ end
+
+ def test_bignum_to_s
+ assert_equal(Encoding::US_ASCII, (1 << 129).to_s.encoding)
+ end
+
+ def test_array_to_s
+ assert_equal(Encoding::US_ASCII, [].to_s.encoding)
+ assert_equal(Encoding::US_ASCII, [nil].to_s.encoding)
+ assert_equal(Encoding::US_ASCII, [1].to_s.encoding)
+ assert_equal("".inspect.encoding, [""].to_s.encoding)
+ assert_equal("a".inspect.encoding, ["a"].to_s.encoding)
+ assert_equal(Encoding::US_ASCII, [nil,1,"","a","\x20",[]].to_s.encoding)
+ end
+
+ def test_hash_to_s
+ assert_equal(Encoding::US_ASCII, {}.to_s.encoding)
+ assert_equal(Encoding::US_ASCII, {1=>nil,"foo"=>""}.to_s.encoding)
+ end
+
+ def test_encoding_find
+ assert_raise(TypeError) {Encoding.find(nil)}
+ assert_raise(TypeError) {Encoding.find(0)}
+ assert_raise(TypeError) {Encoding.find([])}
+ assert_raise(TypeError) {Encoding.find({})}
+ end
+
+ def test_encoding_to_s
+ assert_equal(Encoding::US_ASCII, Encoding::US_ASCII.to_s.encoding)
+ assert_equal(Encoding::US_ASCII, Encoding::US_ASCII.inspect.encoding)
+ end
+
+ def test_regexp_source
+ s = "\xa4\xa2".force_encoding("euc-jp")
+ r = Regexp.new(s)
+ t = r.source
+ assert_equal(s, t, "[ruby-dev:33377] Regexp.new(#{encdump s}).source")
+ end
+
+ def test_magic_comment
+ assert_equal(Encoding::US_ASCII, eval("__ENCODING__".force_encoding("US-ASCII")))
+ assert_equal(Encoding::ASCII_8BIT, eval("__ENCODING__".force_encoding("ASCII-8BIT")))
+ assert_equal(Encoding::US_ASCII, eval("# -*- encoding: US-ASCII -*-\n__ENCODING__".force_encoding("ASCII-8BIT")))
+ assert_equal(Encoding::ASCII_8BIT, eval("# -*- encoding: ASCII-8BIT -*-\n__ENCODING__".force_encoding("US-ASCII")))
+ end
+
+ def test_magic_comment_vim
+ assert_equal(Encoding::US_ASCII, eval("# vim: filetype=ruby, fileencoding: US-ASCII, ts=3, sw=3\n__ENCODING__".force_encoding("ASCII-8BIT")))
+ assert_equal(Encoding::ASCII_8BIT, eval("# vim: filetype=ruby, fileencoding: ASCII-8BIT, ts=3, sw=3\n__ENCODING__".force_encoding("US-ASCII")))
+ end
+
+ def test_magic_comment_at_various_positions
+ # after shebang
+ assert_equal(Encoding::US_ASCII, eval("#!/usr/bin/ruby\n# -*- encoding: US-ASCII -*-\n__ENCODING__".force_encoding("ASCII-8BIT")))
+ assert_equal(Encoding::ASCII_8BIT, eval("#!/usr/bin/ruby\n# -*- encoding: ASCII-8BIT -*-\n__ENCODING__".force_encoding("US-ASCII")))
+ # wrong position
+ assert_equal(Encoding::ASCII_8BIT, eval("\n# -*- encoding: US-ASCII -*-\n__ENCODING__".force_encoding("ASCII-8BIT")))
+ assert_equal(Encoding::US_ASCII, eval("\n# -*- encoding: ASCII-8BIT -*-\n__ENCODING__".force_encoding("US-ASCII")))
+
+ # leading expressions
+ assert_equal(Encoding::ASCII_8BIT, eval("v=1 # -*- encoding: US-ASCII -*-\n__ENCODING__".force_encoding("ASCII-8BIT")))
+ assert_equal(Encoding::US_ASCII, eval("v=1 # -*- encoding: ASCII-8BIT -*-\n__ENCODING__".force_encoding("US-ASCII")))
+ end
+
+ def test_regexp_usascii
+ assert_regexp_usascii_literal('//', Encoding::US_ASCII)
+ assert_regexp_usascii_literal('/#{ }/', Encoding::US_ASCII)
+ assert_regexp_usascii_literal('/#{"a"}/', Encoding::US_ASCII)
+ assert_regexp_usascii_literal('/#{%q"\x80"}/', Encoding::ASCII_8BIT)
+ assert_regexp_usascii_literal('/#{"\x80"}/', nil, SyntaxError)
+
+ assert_regexp_usascii_literal('/a/', Encoding::US_ASCII)
+ assert_regexp_usascii_literal('/a#{ }/', Encoding::US_ASCII)
+ assert_regexp_usascii_literal('/a#{"a"}/', Encoding::US_ASCII)
+ assert_regexp_usascii_literal('/a#{%q"\x80"}/', Encoding::ASCII_8BIT)
+ assert_regexp_usascii_literal('/a#{"\x80"}/', nil, SyntaxError)
+
+ assert_regexp_usascii_literal('/\x80/', Encoding::ASCII_8BIT)
+ assert_regexp_usascii_literal('/\x80#{ }/', Encoding::ASCII_8BIT)
+ assert_regexp_usascii_literal('/\x80#{"a"}/', Encoding::ASCII_8BIT)
+ assert_regexp_usascii_literal('/\x80#{%q"\x80"}/', Encoding::ASCII_8BIT)
+ assert_regexp_usascii_literal('/\x80#{"\x80"}/', nil, SyntaxError)
+
+ assert_regexp_usascii_literal('/\u1234/', Encoding::UTF_8)
+ assert_regexp_usascii_literal('/\u1234#{ }/', Encoding::UTF_8)
+ assert_regexp_usascii_literal('/\u1234#{"a"}/', Encoding::UTF_8)
+ assert_regexp_usascii_literal('/\u1234#{%q"\x80"}/', nil, SyntaxError)
+ assert_regexp_usascii_literal('/\u1234#{"\x80"}/', nil, SyntaxError)
+ assert_regexp_usascii_literal('/\u1234\x80/', nil, SyntaxError)
+ assert_regexp_usascii_literal('/\u1234#{ }\x80/', nil, RegexpError)
+ end
+
+ def test_gbk
+ assert_equal("", "\x81\x40".force_encoding("GBK").chop)
+ end
+
+ def test_euc_tw
+ assert_equal("a", "a\x8e\xa2\xa1\xa1".force_encoding("euc-tw").chop)
+ end
+
+ def test_valid_encoding
+ s = "\xa1".force_encoding("euc-jp")
+ assert_equal(false, s.valid_encoding?)
+ assert_equal(true, (s+s).valid_encoding?, "[ruby-dev:33826]")
+ assert_equal(true, (s*2).valid_encoding?, "[ruby-dev:33826]")
+ assert_equal(true, ("%s%s" % [s, s]).valid_encoding?)
+ assert_equal(true, (s.dup << s).valid_encoding?)
+ assert_equal(true, "".center(2, s).valid_encoding?)
+
+ s = "\xa1\xa1\x8f".force_encoding("euc-jp")
+ assert_equal(false, s.valid_encoding?)
+ assert_equal(true, s.reverse.valid_encoding?)
+
+ bug4018 = '[ruby-core:33027]'
+ s = "\xa1\xa1".force_encoding("euc-jp")
+ assert_equal(true, s.valid_encoding?)
+ s << "\x8f".force_encoding("euc-jp")
+ assert_equal(false, s.valid_encoding?, bug4018)
+ s = "aa".force_encoding("utf-16be")
+ assert_equal(true, s.valid_encoding?)
+ s << "\xff".force_encoding("utf-16be")
+ assert_equal(false, s.valid_encoding?, bug4018)
+
+ bug6190 = '[ruby-core:43557]'
+ s = "\xe9"
+ s = s.encode("utf-8", "utf-8")
+ assert_equal(false, s.valid_encoding?, bug6190)
+ s = "\xe9"
+ s.encode!("utf-8", "utf-8")
+ assert_equal(false, s.valid_encoding?, bug6190)
+ end
+
+ def test_getbyte
+ assert_equal(0x82, u("\xE3\x81\x82\xE3\x81\x84").getbyte(2))
+ assert_equal(0x82, u("\xE3\x81\x82\xE3\x81\x84").getbyte(-4))
+ assert_nil(u("\xE3\x81\x82\xE3\x81\x84").getbyte(100))
+ end
+
+ def test_setbyte
+ s = u("\xE3\x81\x82\xE3\x81\x84")
+ s.setbyte(2, 0x84)
+ assert_equal(u("\xE3\x81\x84\xE3\x81\x84"), s)
+
+ s = u("\xE3\x81\x82\xE3\x81\x84")
+ assert_raise(IndexError) { s.setbyte(100, 0) }
+
+ s = u("\xE3\x81\x82\xE3\x81\x84")
+ s.setbyte(-4, 0x84)
+ assert_equal(u("\xE3\x81\x84\xE3\x81\x84"), s)
+ end
+
+ def test_compatible
+ assert_nil Encoding.compatible?("",0)
+ assert_equal(Encoding::UTF_8, Encoding.compatible?(u(""), ua("abc")))
+ assert_equal(Encoding::UTF_8, Encoding.compatible?(Encoding::UTF_8, Encoding::UTF_8))
+ assert_equal(Encoding::UTF_8, Encoding.compatible?(Encoding::UTF_8, Encoding::US_ASCII))
+ assert_equal(Encoding::ASCII_8BIT,
+ Encoding.compatible?(Encoding::ASCII_8BIT, Encoding::US_ASCII))
+ assert_nil Encoding.compatible?(Encoding::UTF_8, Encoding::ASCII_8BIT)
+ end
+
+ def test_force_encoding
+ assert_equal(u("\x80"), "".center(1, u("\x80")),
+ "moved from btest/knownbug, [ruby-dev:33807]")
+ a = "".force_encoding("ascii-8bit") << 0xC3 << 0xB6
+ assert_equal(1, a.force_encoding("utf-8").size, '[ruby-core:22437]')
+ b = "".force_encoding("ascii-8bit") << 0xC3.chr << 0xB6.chr
+ assert_equal(1, b.force_encoding("utf-8").size, '[ruby-core:22437]')
+
+ assert_raise(TypeError){ ''.force_encoding(nil) }
+ end
+
+ def test_combchar_codepoint
+ assert_equal([0x30BB, 0x309A], "\u30BB\u309A".codepoints.to_a)
+ assert_equal([0x30BB, 0x309A], "\u30BB\u309A".codepoints.to_a)
+ end
+
+ def each_encoding(*strings)
+ Encoding.list.each do |enc|
+ next if enc.dummy?
+ strs = strings.map {|s| s.encode(enc)} rescue next
+ yield(*strs)
+ end
+ end
+
+ def test_str_b
+ s = "\u3042"
+ assert_equal(a("\xE3\x81\x82"), s.b)
+ assert_equal(Encoding::ASCII_8BIT, s.b.encoding)
+ s.taint
+ assert_equal(true, s.b.tainted?)
+ s = "abc".b
+ assert_equal(true, s.b.ascii_only?)
+ end
+
+ def test_scrub
+ str = "\u3042\u3044"
+ assert_not_same(str, str.scrub)
+ str.force_encoding(Encoding::ISO_2022_JP) # dummy encoding
+ assert_not_same(str, str.scrub)
+ assert_nothing_raised(ArgumentError) {str.scrub(nil)}
+
+ assert_equal("\uFFFD\uFFFD\uFFFD", u("\x80\x80\x80").scrub)
+ assert_equal("\uFFFDA", u("\xF4\x80\x80A").scrub)
+
+ # examples in Unicode 6.1.0 D93b
+ assert_equal("\x41\uFFFD\uFFFD\x41\uFFFD\x41",
+ u("\x41\xC0\xAF\x41\xF4\x80\x80\x41").scrub)
+ assert_equal("\x41\uFFFD\uFFFD\uFFFD\x41",
+ u("\x41\xE0\x9F\x80\x41").scrub)
+ assert_equal("\u0061\uFFFD\uFFFD\uFFFD\u0062\uFFFD\u0063\uFFFD\uFFFD\u0064",
+ u("\x61\xF1\x80\x80\xE1\x80\xC2\x62\x80\x63\x80\xBF\x64").scrub)
+ assert_equal("abcdefghijklmnopqrstuvwxyz\u0061\uFFFD\uFFFD\uFFFD\u0062\uFFFD\u0063\uFFFD\uFFFD\u0064",
+ u("abcdefghijklmnopqrstuvwxyz\x61\xF1\x80\x80\xE1\x80\xC2\x62\x80\x63\x80\xBF\x64").scrub)
+
+ assert_equal("\u3042\u3013", u("\xE3\x81\x82\xE3\x81").scrub("\u3013"))
+ assert_raise(Encoding::CompatibilityError){ u("\xE3\x81\x82\xE3\x81").scrub(e("\xA4\xA2")) }
+ assert_raise(TypeError){ u("\xE3\x81\x82\xE3\x81").scrub(1) }
+ assert_raise(ArgumentError){ u("\xE3\x81\x82\xE3\x81\x82\xE3\x81").scrub(u("\x81")) }
+ assert_equal(e("\xA4\xA2\xA2\xAE"), e("\xA4\xA2\xA4").scrub(e("\xA2\xAE")))
+
+ assert_equal("\u3042<e381>", u("\xE3\x81\x82\xE3\x81").scrub{|x|'<'+x.unpack('H*')[0]+'>'})
+ assert_raise(Encoding::CompatibilityError){ u("\xE3\x81\x82\xE3\x81").scrub{e("\xA4\xA2")} }
+ assert_raise(TypeError){ u("\xE3\x81\x82\xE3\x81").scrub{1} }
+ assert_raise(ArgumentError){ u("\xE3\x81\x82\xE3\x81\x82\xE3\x81").scrub{u("\x81")} }
+ assert_equal(e("\xA4\xA2\xA2\xAE"), e("\xA4\xA2\xA4").scrub{e("\xA2\xAE")})
+
+ assert_equal(u("\x81"), u("a\x81").scrub {|c| break c})
+ assert_raise(ArgumentError) {u("a\x81").scrub {|c| c}}
+
+ assert_equal("\uFFFD\u3042".encode("UTF-16BE"),
+ "\xD8\x00\x30\x42".force_encoding(Encoding::UTF_16BE).
+ scrub)
+ assert_equal("\uFFFD\u3042".encode("UTF-16LE"),
+ "\x00\xD8\x42\x30".force_encoding(Encoding::UTF_16LE).
+ scrub)
+ assert_equal("\uFFFD".encode("UTF-32BE"),
+ "\xff".force_encoding(Encoding::UTF_32BE).
+ scrub)
+ assert_equal("\uFFFD".encode("UTF-32LE"),
+ "\xff".force_encoding(Encoding::UTF_32LE).
+ scrub)
+ end
+
+ def test_scrub_bang
+ str = "\u3042\u3044"
+ assert_same(str, str.scrub!)
+ str.force_encoding(Encoding::ISO_2022_JP) # dummy encoding
+ assert_same(str, str.scrub!)
+ assert_nothing_raised(ArgumentError) {str.scrub!(nil)}
+
+ str = u("\x80\x80\x80")
+ str.scrub!
+ assert_same(str, str.scrub!)
+ assert_equal("\uFFFD\uFFFD\uFFFD", str)
+ end
+
+ def test_escaped_metachar
+ bug10670 = '[ruby-core:67193] [Bug #10670]'
+
+ escape_plain = /\A[\x5B]*\z/.freeze
+
+ assert_match(escape_plain, 0x5b.chr(::Encoding::UTF_8), bug10670)
+ assert_match(escape_plain, 0x5b.chr, bug10670)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_m17n_comb.rb b/jni/ruby/test/ruby/test_m17n_comb.rb
new file mode 100644
index 0000000..3a37ed4
--- /dev/null
+++ b/jni/ruby/test/ruby/test_m17n_comb.rb
@@ -0,0 +1,1638 @@
+require 'test/unit'
+require 'etc'
+require_relative 'allpairs'
+
+class TestM17NComb < Test::Unit::TestCase
+ def assert_encoding(encname, actual, message=nil)
+ assert_equal(Encoding.find(encname), actual, message)
+ end
+
+ module AESU
+ def a(str) str.dup.force_encoding(Encoding::US_ASCII) end
+ def b(str) str.b end
+ def e(str) str.dup.force_encoding(Encoding::EUC_JP) end
+ def s(str) str.dup.force_encoding(Encoding::SJIS) end
+ def u(str) str.dup.force_encoding(Encoding::UTF_8) end
+ end
+ include AESU
+ extend AESU
+
+ def assert_strenc(bytes, enc, actual, message=nil)
+ assert_instance_of(String, actual, message)
+ enc = Encoding.find(enc) if String === enc
+ assert_equal(enc, actual.encoding, message)
+ assert_equal(b(bytes), b(actual), message)
+ end
+
+ STRINGS = [
+ b(""), e(""), s(""), u(""),
+ b("a"), e("a"), s("a"), u("a"),
+ b("."), e("."), s("."), u("."),
+
+ # single character
+ b("\x80"), b("\xff"),
+ e("\xa1\xa1"), e("\xfe\xfe"),
+ e("\x8e\xa1"), e("\x8e\xfe"),
+ e("\x8f\xa1\xa1"), e("\x8f\xfe\xfe"),
+ s("\x81\x40"), s("\xfc\xfc"),
+ s("\xa1"), s("\xdf"),
+ u("\xc2\x80"), u("\xf4\x8f\xbf\xbf"),
+
+ # same byte sequence
+ b("\xc2\xa1"), e("\xc2\xa1"), s("\xc2\xa1"), u("\xc2\xa1"),
+
+ s("\x81A"), # mutibyte character which contains "A"
+ s("\x81a"), # mutibyte character which contains "a"
+
+ # invalid
+ e("\xa1"), e("\x80"),
+ s("\x81"), s("\x80"),
+ u("\xc2"), u("\x80"),
+
+ # for transitivity test
+ u("\xe0\xa0\xa1"), e("\xe0\xa0\xa1"), s("\xe0\xa0\xa1"), # [ruby-dev:32693]
+ e("\xa1\xa1"), b("\xa1\xa1"), s("\xa1\xa1"), # [ruby-dev:36484]
+ ]
+
+ WSTRINGS = [
+ "aa".force_encoding("utf-16be"),
+ "aaaa".force_encoding("utf-32be"),
+ "aaa".force_encoding("utf-32be"),
+ ]
+
+ def combination(*args, &b)
+ AllPairs.each(*args, &b)
+ #AllPairs.exhaustive_each(*args, &b)
+ end
+
+ def encdump(str)
+ d = str.dump
+ if /\.force_encoding\("[A-Za-z0-9.:_+-]*"\)\z/ =~ d
+ d
+ else
+ "#{d}.force_encoding(#{str.encoding.name.dump})"
+ end
+ end
+
+ def encdumpargs(args)
+ r = '('
+ args.each_with_index {|a, i|
+ r << ',' if 0 < i
+ if String === a
+ r << encdump(a)
+ else
+ r << a.inspect
+ end
+ }
+ r << ')'
+ r
+ end
+
+ def encdumpcall(recv, meth, *args, &block)
+ desc = ''
+ if String === recv
+ desc << encdump(recv)
+ else
+ desc << recv.inspect
+ end
+ desc << '.' << meth.to_s
+ if !args.empty?
+ desc << '('
+ args.each_with_index {|a, i|
+ desc << ',' if 0 < i
+ if String === a
+ desc << encdump(a)
+ else
+ desc << a.inspect
+ end
+ }
+ desc << ')'
+ end
+ if block
+ desc << ' {}'
+ end
+ desc
+ end
+
+ def assert_enccall(recv, meth, *args, &block)
+ desc = encdumpcall(recv, meth, *args, &block)
+ result = nil
+ assert_nothing_raised(desc) {
+ result = recv.send(meth, *args, &block)
+ }
+ result
+ end
+ alias enccall assert_enccall
+
+ def assert_str_enc_propagation(t, s1, s2)
+ if !s1.ascii_only?
+ assert_equal(s1.encoding, t.encoding)
+ elsif !s2.ascii_only?
+ assert_equal(s2.encoding, t.encoding)
+ else
+ assert_include([s1.encoding, s2.encoding], t.encoding)
+ end
+ end
+
+ def assert_same_result(expected_proc, actual_proc)
+ e = nil
+ begin
+ t = expected_proc.call
+ rescue
+ e = $!
+ end
+ if e
+ assert_raise(e.class) { actual_proc.call }
+ else
+ assert_equal(t, actual_proc.call)
+ end
+ end
+
+ def each_slice_call
+ combination(STRINGS, -2..2) {|s, nth|
+ yield s, nth
+ }
+ combination(STRINGS, -2..2, 0..2) {|s, nth, len|
+ yield s, nth, len
+ }
+ combination(STRINGS, STRINGS) {|s, substr|
+ yield s, substr
+ }
+ combination(STRINGS, -2..2, 0..2) {|s, first, last|
+ yield s, first..last
+ yield s, first...last
+ }
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if !s2.valid_encoding?
+ next
+ end
+ yield s1, Regexp.new(Regexp.escape(s2))
+ }
+ combination(STRINGS, STRINGS, 0..2) {|s1, s2, nth|
+ if !s2.valid_encoding?
+ next
+ end
+ yield s1, Regexp.new(Regexp.escape(s2)), nth
+ }
+ end
+
+ ASCII_INCOMPATIBLE_ENCODINGS = %w[
+ UTF-16BE
+ UTF-16LE
+ UTF-32BE
+ UTF-32LE
+ ]
+ def str_enc_compatible?(*strs)
+ encs = []
+ ascii_incompatible_encodings = {}
+ has_ascii_compatible = false
+ strs.each {|s|
+ encs << s.encoding if !s.ascii_only?
+ if /\A#{Regexp.union ASCII_INCOMPATIBLE_ENCODINGS}\z/o =~ s.encoding.name
+ ascii_incompatible_encodings[s.encoding] = true
+ else
+ has_ascii_compatible = true
+ end
+ }
+ if ascii_incompatible_encodings.empty?
+ encs.uniq!
+ encs.length <= 1
+ else
+ !has_ascii_compatible && ascii_incompatible_encodings.size == 1
+ end
+ end
+
+ # tests start
+
+ def test_str_new
+ STRINGS.each {|s|
+ t = String.new(s)
+ assert_strenc(b(s), s.encoding, t)
+ }
+ end
+
+ def test_str_plus
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if s1.encoding != s2.encoding && !s1.ascii_only? && !s2.ascii_only?
+ assert_raise(Encoding::CompatibilityError) { s1 + s2 }
+ else
+ t = enccall(s1, :+, s2)
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_equal(b(s1) + b(s2), b(t))
+ assert_str_enc_propagation(t, s1, s2)
+ end
+ }
+ end
+
+ def test_str_times
+ STRINGS.each {|s|
+ [0,1,2].each {|n|
+ t = s * n
+ assert_predicate(t, :valid_encoding?) if s.valid_encoding?
+ assert_strenc(b(s) * n, s.encoding, t)
+ }
+ }
+ end
+
+ def test_sprintf_s
+ STRINGS.each {|s|
+ assert_strenc(b(s), s.encoding, "%s".force_encoding(s.encoding) % s)
+ if !s.empty? # xxx
+ t = enccall(b("%s"), :%, s)
+ assert_strenc(b(s), (b('')+s).encoding, t)
+ end
+ }
+ end
+
+ def test_str_eq_reflexive
+ STRINGS.each {|s|
+ assert_equal(s, s, "#{encdump s} == #{encdump s}")
+ }
+ end
+
+ def test_str_eq_symmetric
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if s1 == s2
+ assert_equal(s2, s1, "#{encdump s2} == #{encdump s1}")
+ else
+ assert_not_equal(s2, s1, "!(#{encdump s2} == #{encdump s1})")
+ end
+ }
+ end
+
+ def test_str_eq_transitive
+ combination(STRINGS, STRINGS, STRINGS) {|s1, s2, s3|
+ if s1 == s2 && s2 == s3
+ assert_equal(s1, s3, "transitive: #{encdump s1} == #{encdump s2} == #{encdump s3}")
+ end
+ }
+ end
+
+ def test_str_eq
+ combination(STRINGS, STRINGS) {|s1, s2|
+ desc_eq = "#{encdump s1} == #{encdump s2}"
+ if b(s1) == b(s2) and
+ (s1.ascii_only? && s2.ascii_only? or
+ s1.encoding == s2.encoding) then
+ assert_operator(s1, :==, s2, desc_eq)
+ assert_not_operator(s1, :!=, s2)
+ assert_equal(0, s1 <=> s2)
+ assert_operator(s1, :eql?, s2, desc_eq)
+ else
+ assert_not_operator(s1, :==, s2, "!(#{desc_eq})")
+ assert_operator(s1, :!=, s2)
+ assert_not_equal(0, s1 <=> s2)
+ assert_not_operator(s1, :eql?, s2)
+ end
+ }
+ end
+
+ def test_str_concat
+ combination(STRINGS, STRINGS) {|s1, s2|
+ s = s1.dup
+ if s1.ascii_only? || s2.ascii_only? || s1.encoding == s2.encoding
+ s << s2
+ assert_predicate(s, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_equal(b(s), b(s1) + b(s2))
+ assert_str_enc_propagation(s, s1, s2)
+ else
+ assert_raise(Encoding::CompatibilityError) { s << s2 }
+ end
+ }
+ end
+
+ def test_str_aref
+ STRINGS.each {|s|
+ t = ''.force_encoding(s.encoding)
+ 0.upto(s.length-1) {|i|
+ u = s[i]
+ assert_predicate(u, :valid_encoding?) if s.valid_encoding?
+ t << u
+ }
+ assert_equal(t, s)
+ }
+ end
+
+ def test_str_aref_len
+ STRINGS.each {|s|
+ t = ''.force_encoding(s.encoding)
+ 0.upto(s.length-1) {|i|
+ u = s[i,1]
+ assert_predicate(u, :valid_encoding?) if s.valid_encoding?
+ t << u
+ }
+ assert_equal(t, s)
+ }
+
+ STRINGS.each {|s|
+ t = ''.force_encoding(s.encoding)
+ 0.step(s.length-1, 2) {|i|
+ u = s[i,2]
+ assert_predicate(u, :valid_encoding?) if s.valid_encoding?
+ t << u
+ }
+ assert_equal(t, s)
+ }
+ end
+
+ def test_str_aref_substr
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if s1.ascii_only? || s2.ascii_only? || s1.encoding == s2.encoding
+ t = enccall(s1, :[], s2)
+ if t != nil
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_equal(s2, t)
+ assert_match(/#{Regexp.escape(b(s2))}/, b(s1))
+ if s1.valid_encoding?
+ assert_match(/#{Regexp.escape(s2)}/, s1)
+ end
+ end
+ else
+ assert_raise(Encoding::CompatibilityError) { s1[s2] }
+ end
+ }
+ end
+
+ def test_str_aref_range2
+ combination(STRINGS, -2..2, -2..2) {|s, first, last|
+ desc = "#{encdump s}[#{first}..#{last}]"
+ t = s[first..last]
+ if first < 0
+ first += s.length
+ if first < 0
+ assert_nil(t, desc)
+ next
+ end
+ end
+ if s.length < first
+ assert_nil(t, desc)
+ next
+ end
+ assert_predicate(t, :valid_encoding?) if s.valid_encoding?
+ if last < 0
+ last += s.length
+ end
+ t2 = ''
+ first.upto(last) {|i|
+ c = s[i]
+ t2 << c if c
+ }
+ assert_equal(t2, t, desc)
+ }
+ end
+
+ def test_str_aref_range3
+ combination(STRINGS, -2..2, -2..2) {|s, first, last|
+ desc = "#{encdump s}[#{first}..#{last}]"
+ t = s[first...last]
+ if first < 0
+ first += s.length
+ if first < 0
+ assert_nil(t, desc)
+ next
+ end
+ end
+ if s.length < first
+ assert_nil(t, desc)
+ next
+ end
+ if last < 0
+ last += s.length
+ end
+ assert_predicate(t, :valid_encoding?) if s.valid_encoding?
+ t2 = ''
+ first.upto(last-1) {|i|
+ c = s[i]
+ t2 << c if c
+ }
+ assert_equal(t2, t, desc)
+ }
+ end
+
+ def test_str_assign
+ combination(STRINGS, STRINGS) {|s1, s2|
+ (-2).upto(2) {|i|
+ t = s1.dup
+ if s1.ascii_only? || s2.ascii_only? || s1.encoding == s2.encoding
+ if i < -s1.length || s1.length < i
+ assert_raise(IndexError) { t[i] = s2 }
+ else
+ t[i] = s2
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s2)])
+ if s1.valid_encoding? && s2.valid_encoding?
+ if i == s1.length && s2.empty?
+ assert_nil(t[i])
+ elsif i < 0
+ assert_equal(s2, t[i-s2.length+1,s2.length],
+ "t = #{encdump(s1)}; t[#{i}] = #{encdump(s2)}; t[#{i-s2.length+1},#{s2.length}]")
+ else
+ assert_equal(s2, t[i,s2.length],
+ "t = #{encdump(s1)}; t[#{i}] = #{encdump(s2)}; t[#{i},#{s2.length}]")
+ end
+ end
+ end
+ else
+ assert_raise(Encoding::CompatibilityError) { t[i] = s2 }
+ end
+ }
+ }
+ end
+
+ def test_str_assign_len
+ combination(STRINGS, -2..2, 0..2, STRINGS) {|s1, i, len, s2|
+ t = s1.dup
+ if s1.ascii_only? || s2.ascii_only? || s1.encoding == s2.encoding
+ if i < -s1.length || s1.length < i
+ assert_raise(IndexError) { t[i,len] = s2 }
+ else
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ t[i,len] = s2
+ assert_send([b(t), :index, b(s2)])
+ if s1.valid_encoding? && s2.valid_encoding?
+ if i == s1.length && s2.empty?
+ assert_nil(t[i])
+ elsif i < 0
+ if -i < len
+ len = -i
+ end
+ assert_equal(s2, t[i-s2.length+len,s2.length],
+ "t = #{encdump(s1)}; t[#{i},#{len}] = #{encdump(s2)}; t[#{i-s2.length+len},#{s2.length}]")
+ else
+ assert_equal(s2, t[i,s2.length],
+ "t = #{encdump(s1)}; t[#{i},#{len}] = #{encdump(s2)}; t[#{i},#{s2.length}]")
+ end
+ end
+ end
+ else
+ assert_raise(Encoding::CompatibilityError) { t[i,len] = s2 }
+ end
+ }
+ end
+
+ def test_str_assign_substr
+ combination(STRINGS, STRINGS, STRINGS) {|s1, s2, s3|
+ t = s1.dup
+ encs = [
+ !s1.ascii_only? ? s1.encoding : nil,
+ !s2.ascii_only? ? s2.encoding : nil,
+ !s3.ascii_only? ? s3.encoding : nil].uniq.compact
+ if 1 < encs.length
+ assert_raise(Encoding::CompatibilityError, IndexError) { t[s2] = s3 }
+ else
+ if encs.empty?
+ encs = [
+ s1.encoding,
+ s2.encoding,
+ s3.encoding].uniq.reject {|e| e == Encoding.find("ASCII-8BIT") }
+ if encs.empty?
+ encs = [Encoding.find("ASCII-8BIT")]
+ end
+ end
+ if !t[s2]
+ else
+ enccall(t, :[]=, s2, s3)
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding? && s3.valid_encoding?
+ end
+ end
+ }
+ end
+
+ def test_str_assign_range2
+ combination(STRINGS, -2..2, -2..2, STRINGS) {|s1, first, last, s2|
+ t = s1.dup
+ if s1.ascii_only? || s2.ascii_only? || s1.encoding == s2.encoding
+ if first < -s1.length || s1.length < first
+ assert_raise(RangeError) { t[first..last] = s2 }
+ else
+ enccall(t, :[]=, first..last, s2)
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s2)])
+ if s1.valid_encoding? && s2.valid_encoding?
+ if first < 0
+ assert_equal(s2, t[s1.length+first, s2.length])
+ else
+ assert_equal(s2, t[first, s2.length])
+ end
+ end
+ end
+ else
+ assert_raise(Encoding::CompatibilityError, RangeError,
+ "t=#{encdump(s1)};t[#{first}..#{last}]=#{encdump(s2)}") {
+ t[first..last] = s2
+ }
+ end
+ }
+ end
+
+ def test_str_assign_range3
+ combination(STRINGS, -2..2, -2..2, STRINGS) {|s1, first, last, s2|
+ t = s1.dup
+ if s1.ascii_only? || s2.ascii_only? || s1.encoding == s2.encoding
+ if first < -s1.length || s1.length < first
+ assert_raise(RangeError) { t[first...last] = s2 }
+ else
+ enccall(t, :[]=, first...last, s2)
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s2)])
+ if s1.valid_encoding? && s2.valid_encoding?
+ if first < 0
+ assert_equal(s2, t[s1.length+first, s2.length])
+ else
+ assert_equal(s2, t[first, s2.length])
+ end
+ end
+ end
+ else
+ assert_raise(Encoding::CompatibilityError, RangeError,
+ "t=#{encdump(s1)};t[#{first}...#{last}]=#{encdump(s2)}") {
+ t[first...last] = s2
+ }
+ end
+ }
+ end
+
+ def test_str_cmp
+ combination(STRINGS, STRINGS) {|s1, s2|
+ desc = "#{encdump s1} <=> #{encdump s2}"
+ r = s1 <=> s2
+ if s1 == s2
+ assert_equal(0, r, desc)
+ else
+ assert_not_equal(0, r, desc)
+ end
+ }
+ end
+
+ def test_str_capitalize
+ STRINGS.each {|s|
+ begin
+ t1 = s.capitalize
+ rescue ArgumentError
+ assert_not_predicate(s, :valid_encoding?)
+ next
+ end
+ assert_predicate(t1, :valid_encoding?) if s.valid_encoding?
+ assert_operator(t1, :casecmp, s)
+ t2 = s.dup
+ t2.capitalize!
+ assert_equal(t1, t2)
+ r = s.downcase
+ r = enccall(r, :sub, /\A[a-z]/) {|ch| b(ch).upcase }
+ assert_equal(r, t1)
+ }
+ end
+
+ def test_str_casecmp
+ combination(STRINGS, STRINGS) {|s1, s2|
+ #puts "#{encdump(s1)}.casecmp(#{encdump(s2)})"
+ next unless s1.valid_encoding? && s2.valid_encoding? && Encoding.compatible?(s1, s2)
+ r = s1.casecmp(s2)
+ assert_equal(s1.upcase <=> s2.upcase, r)
+ }
+ end
+
+ def test_str_center
+ combination(STRINGS, [0,1,2,3,10]) {|s1, width|
+ t = s1.center(width)
+ assert_send([b(t), :index, b(s1)])
+ }
+ combination(STRINGS, [0,1,2,3,10], STRINGS) {|s1, width, s2|
+ if s2.empty?
+ assert_raise(ArgumentError) { s1.center(width, s2) }
+ next
+ end
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.center(width, s2) }
+ next
+ end
+ t = enccall(s1, :center, width, s2)
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s1)])
+ assert_str_enc_propagation(t, s1, s2) if (t != s1)
+ }
+ end
+
+ def test_str_ljust
+ combination(STRINGS, [0,1,2,3,10]) {|s1, width|
+ t = s1.ljust(width)
+ assert_send([b(t), :index, b(s1)])
+ }
+ combination(STRINGS, [0,1,2,3,10], STRINGS) {|s1, width, s2|
+ if s2.empty?
+ assert_raise(ArgumentError) { s1.ljust(width, s2) }
+ next
+ end
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.ljust(width, s2) }
+ next
+ end
+ t = enccall(s1, :ljust, width, s2)
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s1)])
+ assert_str_enc_propagation(t, s1, s2) if (t != s1)
+ }
+ end
+
+ def test_str_rjust
+ combination(STRINGS, [0,1,2,3,10]) {|s1, width|
+ t = s1.rjust(width)
+ assert_send([b(t), :index, b(s1)])
+ }
+ combination(STRINGS, [0,1,2,3,10], STRINGS) {|s1, width, s2|
+ if s2.empty?
+ assert_raise(ArgumentError) { s1.rjust(width, s2) }
+ next
+ end
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.rjust(width, s2) }
+ next
+ end
+ t = enccall(s1, :rjust, width, s2)
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s1)])
+ assert_str_enc_propagation(t, s1, s2) if (t != s1)
+ }
+ end
+
+ def test_str_chomp
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if !s1.ascii_only? && !s2.ascii_only? && !Encoding.compatible?(s1,s2)
+ if s1.bytesize > s2.bytesize
+ assert_raise(Encoding::CompatibilityError, "#{encdump(s1)}.chomp(#{encdump(s2)})") do
+ s1.chomp(s2)
+ end
+ end
+ next
+ end
+ t = enccall(s1, :chomp, s2)
+ assert_predicate(t, :valid_encoding?, "#{encdump(s1)}.chomp(#{encdump(s2)})") if s1.valid_encoding? && s2.valid_encoding?
+ assert_equal(s1.encoding, t.encoding)
+ t2 = s1.dup
+ t2.chomp!(s2)
+ assert_equal(t, t2)
+ }
+ end
+
+ def test_str_smart_chomp
+ bug10893 = '[ruby-core:68258] [Bug #10893]'
+ encodings = Encoding.list.select {|enc| !enc.dummy?}
+ combination(encodings, encodings) do |e1, e2|
+ expected = "abc".encode(e1)
+ combination(["abc\n", "abc\r\n"], ["", "\n"]) do |str, rs|
+ assert_equal(expected, str.encode(e1).chomp(rs.encode(e2)), bug10893)
+ end
+ end
+ end
+
+ def test_str_chop
+ STRINGS.each {|s|
+ s = s.dup
+ desc = "#{encdump s}.chop"
+ t = nil
+ assert_nothing_raised(desc) { t = s.chop }
+ assert_predicate(t, :valid_encoding?) if s.valid_encoding?
+ assert_send([b(s), :index, b(t)])
+ t2 = s.dup
+ t2.chop!
+ assert_equal(t, t2)
+ }
+ end
+
+ def test_str_clear
+ STRINGS.each {|s|
+ t = s.dup
+ t.clear
+ assert_predicate(t, :valid_encoding?)
+ assert_empty(t)
+ }
+ end
+
+ def test_str_clone
+ STRINGS.each {|s|
+ t = s.clone
+ assert_equal(s, t)
+ assert_equal(s.encoding, t.encoding)
+ assert_equal(b(s), b(t))
+ }
+ end
+
+ def test_str_dup
+ STRINGS.each {|s|
+ t = s.dup
+ assert_equal(s, t)
+ assert_equal(s.encoding, t.encoding)
+ assert_equal(b(s), b(t))
+ }
+ end
+
+ def test_str_count
+ combination(STRINGS, STRINGS) {|s1, s2|
+ desc = proc {encdumpcall(s1, :count, s2)}
+ if !s1.valid_encoding? || !s2.valid_encoding?
+ assert_raise(ArgumentError, Encoding::CompatibilityError, desc) { s1.count(s2) }
+ next
+ end
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError, desc) { s1.count(s2) }
+ next
+ end
+ n = enccall(s1, :count, s2)
+ n0 = b(s1).count(b(s2))
+ assert_operator(n, :<=, n0)
+ }
+ end
+
+ # glibc 2.16 or later denies salt contained other than [0-9A-Za-z./] #7312
+ # we use this check to test strict and non-strict behavior separately #11045
+ strict_crypt = if defined? Etc::CS_GNU_LIBC_VERSION
+ glibcver = Etc.confstr(Etc::CS_GNU_LIBC_VERSION).scan(/\d+/).map(&:to_i)
+ (glibcver <=> [2, 16]) >= 0
+ end
+
+ def test_str_crypt
+ combination(STRINGS, STRINGS) {|str, salt|
+ # skip input other than [0-9A-Za-z./] to confirm strict behavior
+ next unless salt.ascii_only? && /\A[0-9a-zA-Z.\/]+\z/ =~ salt
+
+ confirm_crypt_result(str, salt)
+ }
+ end
+
+ if !strict_crypt
+ def test_str_crypt_nonstrict
+ combination(STRINGS, STRINGS) {|str, salt|
+ # only test input other than [0-9A-Za-z./] to confirm non-strict behavior
+ next if salt.ascii_only? && /\A[0-9a-zA-Z.\/]+\z/ =~ salt
+
+ confirm_crypt_result(str, salt)
+ }
+ end
+ end
+
+ private def confirm_crypt_result(str, salt)
+ if b(salt).length < 2
+ assert_raise(ArgumentError) { str.crypt(salt) }
+ return
+ end
+ t = str.crypt(salt)
+ assert_equal(b(str).crypt(b(salt)), t, "#{encdump(str)}.crypt(#{encdump(salt)})")
+ assert_encoding('ASCII-8BIT', t.encoding)
+ end
+
+ def test_str_delete
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if s1.empty?
+ assert_equal(s1, s1.delete(s2))
+ next
+ end
+ if !s1.valid_encoding? || !s2.valid_encoding?
+ assert_raise(ArgumentError, Encoding::CompatibilityError) { s1.delete(s2) }
+ next
+ end
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.delete(s2) }
+ next
+ end
+ t = enccall(s1, :delete, s2)
+ assert_predicate(t, :valid_encoding?)
+ assert_equal(t.encoding, s1.encoding)
+ assert_operator(t.length, :<=, s1.length)
+ t2 = s1.dup
+ t2.delete!(s2)
+ assert_equal(t, t2)
+ }
+ end
+
+ def test_str_downcase
+ STRINGS.each {|s|
+ if !s.valid_encoding?
+ assert_raise(ArgumentError) { s.downcase }
+ next
+ end
+ t = s.downcase
+ assert_predicate(t, :valid_encoding?)
+ assert_equal(t.encoding, s.encoding)
+ assert_operator(t, :casecmp, s)
+ t2 = s.dup
+ t2.downcase!
+ assert_equal(t, t2)
+ }
+ end
+
+ def test_str_dump
+ STRINGS.each {|s|
+ t = s.dump
+ assert_predicate(t, :valid_encoding?)
+ assert_predicate(t, :ascii_only?)
+ u = eval(t)
+ assert_equal(b(s), b(u))
+ }
+ end
+
+ def test_str_each_line
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.each_line(s2) {} }
+ next
+ end
+ lines = []
+ enccall(s1, :each_line, s2) {|line|
+ assert_equal(s1.encoding, line.encoding)
+ lines << line
+ }
+ next if lines.size == 0
+ s2 = lines.join('')
+ assert_equal(s1.encoding, s2.encoding)
+ assert_equal(s1, s2)
+ }
+ end
+
+ def test_str_each_byte
+ STRINGS.each {|s|
+ bytes = []
+ s.each_byte {|b|
+ bytes << b
+ }
+ b(s).split(//).each_with_index {|ch, i|
+ assert_equal(ch.ord, bytes[i])
+ }
+ }
+ end
+
+ def test_str_empty?
+ STRINGS.each {|s|
+ if s.length == 0
+ assert_empty(s)
+ else
+ assert_not_empty(s)
+ end
+ }
+ end
+
+ def test_str_hex
+ STRINGS.each {|s|
+ t = s.hex
+ t2 = b(s)[/\A[0-9a-fA-Fx]*/].hex
+ assert_equal(t2, t)
+ }
+ end
+
+ def test_str_include?
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.include?(s2) }
+ assert_raise(Encoding::CompatibilityError) { s1.index(s2) }
+ assert_raise(Encoding::CompatibilityError) { s1.rindex(s2) }
+ next
+ end
+ t = enccall(s1, :include?, s2)
+ if t
+ assert_include(b(s1), b(s2))
+ assert_send([s1, :index, s2])
+ assert_send([s1, :rindex, s2])
+ else
+ assert_not_send([s1, :index, s2])
+ assert_not_send([s1, :rindex, s2], "!#{encdump(s1)}.rindex(#{encdump(s2)})")
+ end
+ if s2.empty?
+ assert_equal(true, t)
+ next
+ end
+ if !s1.valid_encoding? || !s2.valid_encoding?
+ assert_equal(false, t, "#{encdump s1}.include?(#{encdump s2})")
+ next
+ end
+ if t && s1.valid_encoding? && s2.valid_encoding?
+ assert_match(/#{Regexp.escape(s2)}/, s1)
+ else
+ assert_no_match(/#{Regexp.escape(s2)}/, s1)
+ end
+ }
+ end
+
+ def test_str_index
+ combination(STRINGS, STRINGS, -2..2) {|s1, s2, pos|
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.index(s2) }
+ next
+ end
+ t = enccall(s1, :index, s2, pos)
+ if s2.empty?
+ if pos < 0 && pos+s1.length < 0
+ assert_equal(nil, t, "#{encdump s1}.index(#{encdump s2}, #{pos})");
+ elsif pos < 0
+ assert_equal(s1.length+pos, t, "#{encdump s1}.index(#{encdump s2}, #{pos})");
+ elsif s1.length < pos
+ assert_equal(nil, t, "#{encdump s1}.index(#{encdump s2}, #{pos})");
+ else
+ assert_equal(pos, t, "#{encdump s1}.index(#{encdump s2}, #{pos})");
+ end
+ next
+ end
+ if !s1.valid_encoding? || !s2.valid_encoding?
+ assert_equal(nil, t, "#{encdump s1}.index(#{encdump s2}, #{pos})");
+ next
+ end
+ if t
+ re = /#{Regexp.escape(s2)}/
+ assert(re.match(s1, pos))
+ assert_equal($`.length, t, "#{encdump s1}.index(#{encdump s2}, #{pos})")
+ else
+ assert_no_match(/#{Regexp.escape(s2)}/, s1[pos..-1])
+ end
+ }
+ end
+
+ def test_str_rindex
+ combination(STRINGS, STRINGS, -2..2) {|s1, s2, pos|
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.rindex(s2) }
+ next
+ end
+ t = enccall(s1, :rindex, s2, pos)
+ if s2.empty?
+ if pos < 0 && pos+s1.length < 0
+ assert_equal(nil, t, "#{encdump s1}.rindex(#{encdump s2}, #{pos})")
+ elsif pos < 0
+ assert_equal(s1.length+pos, t, "#{encdump s1}.rindex(#{encdump s2}, #{pos})")
+ elsif s1.length < pos
+ assert_equal(s1.length, t, "#{encdump s1}.rindex(#{encdump s2}, #{pos})")
+ else
+ assert_equal(pos, t, "#{encdump s1}.rindex(#{encdump s2}, #{pos})")
+ end
+ next
+ end
+ if !s1.valid_encoding? || !s2.valid_encoding?
+ assert_equal(nil, t, "#{encdump s1}.rindex(#{encdump s2}, #{pos})")
+ next
+ end
+ if t
+ #puts "#{encdump s1}.rindex(#{encdump s2}, #{pos}) => #{t}"
+ assert_send([b(s1), :index, b(s2)])
+ pos2 = pos
+ pos2 += s1.length if pos < 0
+ re = /\A(.{0,#{pos2}})#{Regexp.escape(s2)}/m
+ m = enccall(re, :match, s1)
+ assert(m, "#{re.inspect}.match(#{encdump(s1)})")
+ assert_equal(m[1].length, t, "#{encdump s1}.rindex(#{encdump s2}, #{pos})")
+ else
+ re = /#{Regexp.escape(s2)}/
+ n = re =~ s1
+ if n
+ if pos < 0
+ assert_operator(n, :>, s1.length+pos)
+ else
+ assert_operator(n, :>, pos)
+ end
+ end
+ end
+ }
+ end
+
+ def test_str_insert
+ combination(STRINGS, 0..2, STRINGS) {|s1, nth, s2|
+ t1 = s1.dup
+ t2 = s1.dup
+ begin
+ t1[nth, 0] = s2
+ rescue Encoding::CompatibilityError, IndexError => e1
+ end
+ begin
+ t2.insert(nth, s2)
+ rescue Encoding::CompatibilityError, IndexError => e2
+ end
+ assert_equal(t1, t2, "t=#{encdump s1}; t.insert(#{nth},#{encdump s2}); t")
+ assert_equal(e1.class, e2.class, "begin #{encdump s1}.insert(#{nth},#{encdump s2}); rescue ArgumentError, IndexError => e; e end")
+ }
+ combination(STRINGS, -2..-1, STRINGS) {|s1, nth, s2|
+ next if s1.length + nth < 0
+ next unless s1.valid_encoding?
+ next unless s2.valid_encoding?
+ t1 = s1.dup
+ begin
+ t1.insert(nth, s2)
+ slen = s2.length
+ assert_equal(t1[nth-slen+1,slen], s2, "t=#{encdump s1}; t.insert(#{nth},#{encdump s2}); t")
+ rescue Encoding::CompatibilityError, IndexError
+ end
+ }
+ end
+
+ def test_str_intern
+ STRINGS.each {|s|
+ if /\0/ =~ b(s)
+ assert_raise(ArgumentError) { s.intern }
+ elsif s.valid_encoding?
+ sym = s.intern
+ assert_equal(s, sym.to_s, "#{encdump s}.intern.to_s")
+ assert_equal(sym, s.to_sym)
+ else
+ assert_raise(EncodingError) { s.intern }
+ end
+ }
+ end
+
+ def test_str_length
+ STRINGS.each {|s|
+ assert_operator(s.length, :<=, s.bytesize)
+ }
+ end
+
+ def test_str_oct
+ STRINGS.each {|s|
+ t = s.oct
+ t2 = b(s)[/\A[0-9a-fA-FxX]*/].oct
+ assert_equal(t2, t)
+ }
+ end
+
+ def test_str_replace
+ combination(STRINGS, STRINGS) {|s1, s2|
+ t = s1.dup
+ t.replace s2
+ assert_equal(s2, t)
+ assert_equal(s2.encoding, t.encoding)
+ }
+ end
+
+ def test_str_reverse
+ STRINGS.each {|s|
+ t = s.reverse
+ assert_equal(s.bytesize, t.bytesize)
+ if !s.valid_encoding?
+ assert_operator(t.length, :<=, s.length)
+ next
+ end
+ assert_equal(s, t.reverse)
+ }
+ end
+
+ def test_str_scan
+ combination(STRINGS, STRINGS) {|s1, s2|
+ desc = proc {"#{s1.dump}.scan(#{s2.dump})"}
+ if !s2.valid_encoding?
+ assert_raise(RegexpError, desc) { s1.scan(s2) }
+ next
+ end
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ if s1.valid_encoding?
+ assert_raise(Encoding::CompatibilityError, desc) { s1.scan(s2) }
+ else
+ assert_raise_with_message(ArgumentError, /invalid byte sequence/, desc) { s1.scan(s2) }
+ end
+ next
+ end
+ if !s1.valid_encoding?
+ assert_raise(ArgumentError, desc) { s1.scan(s2) }
+ next
+ end
+ r = enccall(s1, :scan, s2)
+ r.each {|t|
+ assert_equal(s2, t, desc)
+ }
+ }
+ end
+
+ def test_str_slice
+ each_slice_call {|obj, *args|
+ assert_same_result(lambda { obj[*args] }, lambda { obj.slice(*args) })
+ }
+ end
+
+ def test_str_slice!
+ each_slice_call {|s, *args|
+ desc_slice = "#{encdump s}.slice#{encdumpargs args}"
+ desc_slice_bang = "#{encdump s}.slice!#{encdumpargs args}"
+ t = s.dup
+ begin
+ r = t.slice!(*args)
+ rescue
+ e = $!
+ end
+ if e
+ assert_raise(e.class, desc_slice) { s.slice(*args) }
+ next
+ end
+ if !r
+ assert_nil(s.slice(*args), desc_slice)
+ next
+ end
+ assert_equal(s.slice(*args), r, desc_slice_bang)
+ assert_equal(s.bytesize, r.bytesize + t.bytesize)
+ if args.length == 1 && String === args[0]
+ assert_equal(args[0].encoding, r.encoding,
+ "#{encdump s}.slice!#{encdumpargs args}.encoding")
+ else
+ assert_equal(s.encoding, r.encoding,
+ "#{encdump s}.slice!#{encdumpargs args}.encoding")
+ end
+ if [s, *args].all? {|o| !(String === o) || o.valid_encoding? }
+ assert_predicate(r, :valid_encoding?)
+ assert_predicate(t, :valid_encoding?)
+ assert_equal(s.length, r.length + t.length)
+ end
+ }
+ end
+
+ def test_str_split
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if !s2.valid_encoding?
+ assert_raise(ArgumentError, RegexpError) { s1.split(s2) }
+ next
+ end
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(ArgumentError, Encoding::CompatibilityError) { s1.split(s2) }
+ next
+ end
+ if !s1.valid_encoding?
+ assert_raise(ArgumentError) { s1.split(s2) }
+ next
+ end
+ t = enccall(s1, :split, s2)
+ t.each {|r|
+ assert_include(b(s1), b(r))
+ assert_equal(s1.encoding, r.encoding)
+ }
+ assert_include(b(s1), t.map {|u| b(u) }.join(b(s2)))
+ if s1.valid_encoding? && s2.valid_encoding?
+ t.each {|r|
+ assert_predicate(r, :valid_encoding?)
+ }
+ end
+ }
+ end
+
+ def test_str_squeeze
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if !s1.valid_encoding? || !s2.valid_encoding?
+ assert_raise(ArgumentError, Encoding::CompatibilityError, "#{encdump s1}.squeeze(#{encdump s2})") { s1.squeeze(s2) }
+ next
+ end
+ if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
+ assert_raise(Encoding::CompatibilityError) { s1.squeeze(s2) }
+ next
+ end
+ t = enccall(s1, :squeeze, s2)
+ assert_operator(t.length, :<=, s1.length)
+ t2 = s1.dup
+ t2.squeeze!(s2)
+ assert_equal(t, t2)
+ }
+ end
+
+ def test_str_strip
+ STRINGS.each {|s|
+ if !s.valid_encoding?
+ assert_raise(ArgumentError, "#{encdump s}.strip") { s.strip }
+ next
+ end
+ t = s.strip
+ l = s.lstrip
+ r = s.rstrip
+ assert_operator(l.length, :<=, s.length)
+ assert_operator(r.length, :<=, s.length)
+ assert_operator(t.length, :<=, l.length)
+ assert_operator(t.length, :<=, r.length)
+ t2 = s.dup
+ t2.strip!
+ assert_equal(t, t2)
+ l2 = s.dup
+ l2.lstrip!
+ assert_equal(l, l2)
+ r2 = s.dup
+ r2.rstrip!
+ assert_equal(r, r2)
+ }
+ end
+
+ def test_str_sum
+ STRINGS.each {|s|
+ assert_equal(b(s).sum, s.sum)
+ }
+ end
+
+ def test_str_swapcase
+ STRINGS.each {|s|
+ if !s.valid_encoding?
+ assert_raise(ArgumentError, "#{encdump s}.swapcase") { s.swapcase }
+ next
+ end
+ t1 = s.swapcase
+ assert_predicate(t1, :valid_encoding?) if s.valid_encoding?
+ assert_operator(t1, :casecmp, s)
+ t2 = s.dup
+ t2.swapcase!
+ assert_equal(t1, t2)
+ t3 = t1.swapcase
+ assert_equal(s, t3);
+ }
+ end
+
+
+ def test_str_to_f
+ STRINGS.each {|s|
+ assert_nothing_raised { s.to_f }
+ }
+ end
+
+ def test_str_to_i
+ STRINGS.each {|s|
+ assert_nothing_raised { s.to_i }
+ 2.upto(36) {|radix|
+ assert_nothing_raised { s.to_i(radix) }
+ }
+ }
+ end
+
+ def test_str_to_s
+ STRINGS.each {|s|
+ assert_same(s, s.to_s)
+ assert_same(s, s.to_str)
+ }
+ end
+
+ def test_tr
+ combination(STRINGS, STRINGS, STRINGS) {|s1, s2, s3|
+ desc = "#{encdump s1}.tr(#{encdump s2}, #{encdump s3})"
+ if s1.empty?
+ assert_equal(s1, s1.tr(s2, s3), desc)
+ next
+ end
+ if !str_enc_compatible?(s1, s2, s3)
+ assert_raise(Encoding::CompatibilityError, desc) { s1.tr(s2, s3) }
+ next
+ end
+ if !s1.valid_encoding?
+ assert_raise(ArgumentError, desc) { s1.tr(s2, s3) }
+ next
+ end
+ if s2.empty?
+ t = enccall(s1, :tr, s2, s3)
+ assert_equal(s1, t, desc)
+ next
+ end
+ if !s2.valid_encoding? || !s3.valid_encoding?
+ assert_raise(ArgumentError, desc) { s1.tr(s2, s3) }
+ next
+ end
+ t = enccall(s1, :tr, s2, s3)
+ assert_operator(s1.length, :>=, t.length, desc)
+ }
+ end
+
+ def test_tr_sjis
+ expected = "\x83}\x83~\x83\x80\x83\x81\x83\x82".force_encoding(Encoding::SJIS)
+ source = "\xCF\xD0\xD1\xD2\xD3".force_encoding(Encoding::SJIS)
+ from = "\xCF-\xD3".force_encoding(Encoding::SJIS)
+ to = "\x83}-\x83\x82".force_encoding(Encoding::SJIS)
+ assert_equal(expected, source.tr(from, to))
+
+ expected = "\x84}\x84~\x84\x80\x84\x81\x84\x82".force_encoding(Encoding::SJIS)
+ source = "\x84M\x84N\x84O\x84P\x84Q".force_encoding(Encoding::SJIS)
+ from = "\x84@-\x84`".force_encoding(Encoding::SJIS)
+ to = "\x84p-\x84\x91".force_encoding(Encoding::SJIS)
+ assert_equal(expected, source.tr(from, to))
+ end
+
+ def test_tr_s
+ combination(STRINGS, STRINGS, STRINGS) {|s1, s2, s3|
+ desc = "#{encdump s1}.tr_s(#{encdump s2}, #{encdump s3})"
+ if s1.empty?
+ assert_equal(s1, s1.tr_s(s2, s3), desc)
+ next
+ end
+ if !s1.valid_encoding?
+ assert_raise(ArgumentError, Encoding::CompatibilityError, desc) { s1.tr_s(s2, s3) }
+ next
+ end
+ if !str_enc_compatible?(s1, s2, s3)
+ assert_raise(Encoding::CompatibilityError, desc) { s1.tr(s2, s3) }
+ next
+ end
+ if s2.empty?
+ t = enccall(s1, :tr_s, s2, s3)
+ assert_equal(s1, t, desc)
+ next
+ end
+ if !s2.valid_encoding? || !s3.valid_encoding?
+ assert_raise(ArgumentError, desc) { s1.tr_s(s2, s3) }
+ next
+ end
+
+ t = enccall(s1, :tr_s, s2, s3)
+ assert_operator(s1.length, :>=, t.length, desc)
+ }
+ end
+
+ def test_str_upcase
+ STRINGS.each {|s|
+ desc = "#{encdump s}.upcase"
+ if !s.valid_encoding?
+ assert_raise(ArgumentError, desc) { s.upcase }
+ next
+ end
+ t1 = s.upcase
+ assert_predicate(t1, :valid_encoding?)
+ assert_operator(t1, :casecmp, s)
+ t2 = s.dup
+ t2.upcase!
+ assert_equal(t1, t2)
+ }
+ end
+
+ def test_str_succ
+ STRINGS.each {|s0|
+ next if s0.empty?
+ s = s0.dup
+ n = 300
+ h = {}
+ n.times {|i|
+ if h[s]
+ assert(false, "#{encdump s} cycle with succ #{i-h[s]} times")
+ end
+ h[s] = i
+ assert_operator(s.length, :<=, s0.length + Math.log2(i+1) + 1, "#{encdump s0} succ #{i} times => #{encdump s}")
+ #puts encdump(s)
+ t = s.succ
+ if s.valid_encoding?
+ assert_predicate(t, :valid_encoding?, "#{encdump s}.succ.valid_encoding?")
+ end
+ s = t
+ }
+ }
+
+ Encoding.list.each do |enc|
+ next if enc.dummy?
+ {"A"=>"B", "A1"=>"A2", "A9"=>"B0", "9"=>"10", "Z"=>"AA"}.each do |orig, expected|
+ s = orig.encode(enc)
+ assert_strenc(expected.encode(enc), enc, s.succ, proc {"#{orig.dump}.encode(#{enc}).succ"})
+ end
+ end
+ end
+
+ def test_str_succ2
+ assert_equal(a("\x01\x00"), a("\x7f").succ)
+ assert_equal(b("\x01\x00"), b("\xff").succ)
+ end
+
+ def test_str_hash
+ combination(STRINGS, STRINGS) {|s1, s2|
+ if s1.eql?(s2)
+ assert_equal(s1.hash, s2.hash, "#{encdump s1}.hash == #{encdump s2}.dump")
+ end
+ }
+ end
+
+ def test_marshal
+ STRINGS.each {|s|
+ m = Marshal.dump(s)
+ t = Marshal.load(m)
+ assert_equal(s, t)
+ }
+ end
+
+ def test_str_sub
+ combination(STRINGS, STRINGS, STRINGS) {|s1, s2, s3|
+ if !s2.valid_encoding?
+ assert_raise(RegexpError) { Regexp.new(Regexp.escape(s2)) }
+ next
+ end
+ r2 = Regexp.new(Regexp.escape(s2))
+ [
+ [
+ "#{encdump s1}.sub(Regexp.new(#{encdump s2}), #{encdump s3})",
+ lambda { s1.sub(r2, s3) },
+ false
+ ],
+ [
+ "#{encdump s1}.sub(Regexp.new(#{encdump s2}), #{encdump s3})",
+ lambda { s1.sub(r2) { s3 } },
+ false
+ ],
+ [
+ "#{encdump s1}.gsub(Regexp.new(#{encdump s2}), #{encdump s3})",
+ lambda { s1.gsub(r2, s3) },
+ true
+ ],
+ [
+ "#{encdump s1}.gsub(Regexp.new(#{encdump s2}), #{encdump s3})",
+ lambda { s1.gsub(r2) { s3 } },
+ true
+ ]
+ ].each {|desc, doit, g|
+ if !s1.valid_encoding?
+ assert_raise(ArgumentError, desc) { doit.call }
+ next
+ end
+ if !str_enc_compatible?(s1, s2)
+ assert_raise(Encoding::CompatibilityError, desc) { doit.call }
+ next
+ end
+ if !enccall(s1, :include?, s2)
+ assert_equal(s1, doit.call)
+ next
+ end
+ if !str_enc_compatible?(g ? s1.gsub(r2, '') : s1.sub(r2, ''), s3)
+ assert_raise(Encoding::CompatibilityError, desc) { doit.call }
+ next
+ end
+ t = nil
+ assert_nothing_raised(desc) {
+ t = doit.call
+ }
+ if s2 == s3
+ assert_equal(s1, t, desc)
+ else
+ assert_not_equal(s1, t, desc)
+ end
+ }
+ }
+ end
+
+ def test_str_sub!
+ combination(STRINGS, STRINGS, STRINGS) {|s1, s2, s3|
+ if !s2.valid_encoding?
+ assert_raise(RegexpError) { Regexp.new(Regexp.escape(s2)) }
+ next
+ end
+ r2 = Regexp.new(Regexp.escape(s2))
+ [
+ [
+ "t=#{encdump s1}.dup;t.sub!(Regexp.new(#{encdump s2}), #{encdump s3})",
+ lambda { t=s1.dup; [t, t.sub!(r2, s3)] },
+ false
+ ],
+ [
+ "t=#{encdump s1}.dup;t.sub!(Regexp.new(#{encdump s2}), #{encdump s3})",
+ lambda { t=s1.dup; [t, t.sub!(r2) { s3 }] },
+ false
+ ],
+ [
+ "t=#{encdump s1}.dup;t.gsub!(Regexp.new(#{encdump s2}), #{encdump s3})",
+ lambda { t=s1.dup; [t, t.gsub!(r2, s3)] },
+ true
+ ],
+ [
+ "t=#{encdump s1}.dup;t.gsub!(Regexp.new(#{encdump s2}), #{encdump s3})",
+ lambda { t=s1.dup; [t, t.gsub!(r2) { s3 }] },
+ true
+ ]
+ ].each {|desc, doit, g|
+ if !s1.valid_encoding?
+ assert_raise(ArgumentError, desc) { doit.call }
+ next
+ end
+ if !str_enc_compatible?(s1, s2)
+ assert_raise(Encoding::CompatibilityError, desc) { doit.call }
+ next
+ end
+ if !enccall(s1, :include?, s2)
+ assert_equal([s1, nil], doit.call)
+ next
+ end
+ if !str_enc_compatible?(g ? s1.gsub(r2, '') : s1.sub(r2, ''), s3)
+ assert_raise(Encoding::CompatibilityError, desc) { doit.call }
+ next
+ end
+ t = ret = nil
+ assert_nothing_raised(desc) {
+ t, ret = doit.call
+ }
+ assert(ret)
+ if s2 == s3
+ assert_equal(s1, t, desc)
+ else
+ assert_not_equal(s1, t, desc)
+ end
+ }
+ }
+ end
+
+ def test_str_bytes
+ STRINGS.each {|s1|
+ ary = []
+ s1.bytes.each {|b|
+ ary << b
+ }
+ assert_equal(s1.unpack("C*"), ary)
+ }
+ end
+
+ def test_str_bytesize
+ STRINGS.each {|s1|
+ assert_equal(s1.unpack("C*").length, s1.bytesize)
+ }
+ end
+
+ def test_str_chars
+ STRINGS.each {|s1|
+ ary = []
+ s1.chars.each {|c|
+ ary << c
+ }
+ expected = []
+ s1.length.times {|i|
+ expected << s1[i]
+ }
+ assert_equal(expected, ary)
+ }
+ end
+
+ def test_str_chr
+ STRINGS.each {|s1|
+ if s1.empty?
+ assert_equal("", s1.chr)
+ next
+ end
+ assert_equal(s1[0], s1.chr)
+ }
+ end
+
+ def test_str_end_with?
+ combination(STRINGS, STRINGS) {|s1, s2|
+ desc = "#{encdump s1}.end_with?(#{encdump s2})"
+ if !str_enc_compatible?(s1, s2)
+ assert_raise(Encoding::CompatibilityError, desc) { s1.end_with?(s2) }
+ next
+ end
+ if s1.length < s2.length
+ assert_equal(false, enccall(s1, :end_with?, s2), desc)
+ next
+ end
+ if s1[s1.length-s2.length, s2.length] == s2
+ assert_equal(true, enccall(s1, :end_with?, s2), desc)
+ next
+ end
+ assert_equal(false, enccall(s1, :end_with?, s2), desc)
+ }
+ end
+
+ def test_str_start_with?
+ combination(STRINGS, STRINGS) {|s1, s2|
+ desc = "#{encdump s1}.start_with?(#{encdump s2})"
+ if !str_enc_compatible?(s1, s2)
+ assert_raise(Encoding::CompatibilityError, desc) { s1.start_with?(s2) }
+ next
+ end
+ s1 = s1.dup.force_encoding("ASCII-8BIT")
+ s2 = s2.dup.force_encoding("ASCII-8BIT")
+ if s1.length < s2.length
+ assert_equal(false, enccall(s1, :start_with?, s2), desc)
+ next
+ end
+ if s1[0, s2.length] == s2
+ assert_equal(true, enccall(s1, :start_with?, s2), desc)
+ next
+ end
+ assert_equal(false, enccall(s1, :start_with?, s2), desc)
+ }
+ end
+
+ def test_str_ord
+ STRINGS.each {|s1|
+ if s1.empty?
+ assert_raise(ArgumentError) { s1.ord }
+ next
+ end
+ if !s1.valid_encoding?
+ assert_raise(ArgumentError) { s1.ord }
+ next
+ end
+ assert_equal(s1[0].ord, s1.ord)
+ }
+ end
+
+ def test_str_partition
+ combination(STRINGS, STRINGS) {|s1, s2|
+ desc = "#{encdump s1}.partition(#{encdump s2})"
+ if !str_enc_compatible?(s1, s2)
+ assert_raise(Encoding::CompatibilityError, desc) { s1.partition(s2) }
+ next
+ end
+ i = enccall(s1, :index, s2)
+ if !i
+ assert_equal([s1, "", ""], s1.partition(s2), desc)
+ next
+ end
+ assert_equal([s1[0,i], s2, s1[(i+s2.length)..-1]], s1.partition(s2), desc)
+ }
+ end
+
+ def test_str_rpartition
+ combination(STRINGS, STRINGS) {|s1, s2|
+ desc = "#{encdump s1}.rpartition(#{encdump s2})"
+ if !str_enc_compatible?(s1, s2)
+ assert_raise(Encoding::CompatibilityError, desc) { s1.rpartition(s2) }
+ next
+ end
+ i = enccall(s1, :rindex, s2)
+ if !i
+ assert_equal(["", "", s1], s1.rpartition(s2), desc)
+ next
+ end
+ assert_equal([s1[0,i], s2, s1[(i+s2.length)..-1]], s1.rpartition(s2), desc)
+ }
+ end
+
+end
diff --git a/jni/ruby/test/ruby/test_marshal.rb b/jni/ruby/test/ruby/test_marshal.rb
new file mode 100644
index 0000000..3d5d6c9
--- /dev/null
+++ b/jni/ruby/test/ruby/test_marshal.rb
@@ -0,0 +1,650 @@
+require 'test/unit'
+require 'tempfile'
+require_relative 'marshaltestlib'
+
+class TestMarshal < Test::Unit::TestCase
+ include MarshalTestLib
+
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def encode(o)
+ Marshal.dump(o)
+ end
+
+ def decode(s)
+ Marshal.load(s)
+ end
+
+ def fact(n)
+ return 1 if n == 0
+ f = 1
+ while n>0
+ f *= n
+ n -= 1
+ end
+ return f
+ end
+
+ def test_marshal
+ a = [1, 2, 3, [4,5,"foo"], {1=>"bar"}, 2.5, fact(30)]
+ assert_equal a, Marshal.load(Marshal.dump(a))
+
+ [[1,2,3,4], [81, 2, 118, 3146]].each { |w,x,y,z|
+ obj = (x.to_f + y.to_f / z.to_f) * Math.exp(w.to_f / (x.to_f + y.to_f / z.to_f))
+ assert_equal obj, Marshal.load(Marshal.dump(obj))
+ }
+
+ bug3659 = '[ruby-dev:41936]'
+ [1.0, 10.0, 100.0, 110.0].each {|x|
+ assert_equal(x, Marshal.load(Marshal.dump(x)), bug3659)
+ }
+ end
+
+ StrClone = String.clone
+ def test_marshal_cloned_class
+ assert_instance_of(StrClone, Marshal.load(Marshal.dump(StrClone.new("abc"))))
+ end
+
+ def test_inconsistent_struct
+ TestMarshal.const_set :StructOrNot, Struct.new(:a)
+ s = Marshal.dump(StructOrNot.new(1))
+ TestMarshal.instance_eval { remove_const :StructOrNot }
+ TestMarshal.const_set :StructOrNot, Class.new
+ assert_raise(TypeError, "[ruby-dev:31709]") { Marshal.load(s) }
+ end
+
+ def test_struct_invalid_members
+ TestMarshal.const_set :StructInvalidMembers, Struct.new(:a)
+ assert_raise(TypeError, "[ruby-dev:31759]") {
+ Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo")
+ TestMarshal::StructInvalidMembers.members
+ }
+ end
+
+ class C
+ def initialize(str)
+ @str = str
+ end
+ attr_reader :str
+ def _dump(limit)
+ @str
+ end
+ def self._load(s)
+ new(s)
+ end
+ end
+
+ def test_too_long_string
+ data = Marshal.dump(C.new("a".force_encoding("ascii-8bit")))
+ data[-2, 1] = "\003\377\377\377"
+ assert_raise_with_message(ArgumentError, "marshal data too short", "[ruby-dev:32054]") {
+ Marshal.load(data)
+ }
+ end
+
+
+ def test_userdef_encoding
+ s1 = "\xa4\xa4".force_encoding("euc-jp")
+ o1 = C.new(s1)
+ m = Marshal.dump(o1)
+ o2 = Marshal.load(m)
+ s2 = o2.str
+ assert_equal(s1, s2)
+ end
+
+ def test_pipe
+ o1 = C.new("a" * 10000)
+
+ IO.pipe do |r, w|
+ th = Thread.new {Marshal.dump(o1, w)}
+ o2 = Marshal.load(r)
+ th.join
+ assert_equal(o1.str, o2.str)
+ end
+
+ IO.pipe do |r, w|
+ th = Thread.new {Marshal.dump(o1, w, 2)}
+ o2 = Marshal.load(r)
+ th.join
+ assert_equal(o1.str, o2.str)
+ end
+
+ assert_raise(TypeError) { Marshal.dump("foo", Object.new) }
+ assert_raise(TypeError) { Marshal.load(Object.new) }
+ end
+
+ def test_limit
+ assert_equal([[[]]], Marshal.load(Marshal.dump([[[]]], 3)))
+ assert_raise(ArgumentError) { Marshal.dump([[[]]], 2) }
+ assert_nothing_raised(ArgumentError, '[ruby-core:24100]') { Marshal.dump("\u3042", 1) }
+ end
+
+ def test_userdef_invalid
+ o = C.new(nil)
+ assert_raise(TypeError) { Marshal.dump(o) }
+ end
+
+ def test_class
+ o = class << Object.new; self; end
+ assert_raise(TypeError) { Marshal.dump(o) }
+ assert_equal(Object, Marshal.load(Marshal.dump(Object)))
+ assert_equal(Enumerable, Marshal.load(Marshal.dump(Enumerable)))
+ end
+
+ class C2
+ def initialize(ary)
+ @ary = ary
+ end
+ def _dump(s)
+ @ary.clear
+ "foo"
+ end
+ end
+
+ def test_modify_array_during_dump
+ a = []
+ o = C2.new(a)
+ a << o << nil
+ assert_raise(RuntimeError) { Marshal.dump(a) }
+ end
+
+ def test_change_class_name
+ eval("class C3; def _dump(s); 'foo'; end; end")
+ m = Marshal.dump(C3.new)
+ assert_raise(TypeError) { Marshal.load(m) }
+ eval("C3 = nil")
+ assert_raise(TypeError) { Marshal.load(m) }
+ end
+
+ def test_change_struct
+ eval("C3 = Struct.new(:foo, :bar)")
+ m = Marshal.dump(C3.new("FOO", "BAR"))
+ eval("C3 = Struct.new(:foo)")
+ assert_raise(TypeError) { Marshal.load(m) }
+ eval("C3 = Struct.new(:foo, :baz)")
+ assert_raise(TypeError) { Marshal.load(m) }
+ end
+
+ class C4
+ def initialize(gc)
+ @gc = gc
+ end
+ def _dump(s)
+ GC.start if @gc
+ "foo"
+ end
+ end
+
+ def test_gc
+ assert_nothing_raised do
+ Marshal.dump((0..1000).map {|x| C4.new(x % 50 == 25) })
+ end
+ end
+
+ def test_taint
+ x = Object.new
+ x.taint
+ s = Marshal.dump(x)
+ assert_equal(true, s.tainted?)
+ y = Marshal.load(s)
+ assert_equal(true, y.tainted?)
+ end
+
+ def test_taint_each_object
+ x = Object.new
+ obj = [[x]]
+
+ # clean object causes crean stream
+ assert_equal(false, obj.tainted?)
+ assert_equal(false, obj.first.tainted?)
+ assert_equal(false, obj.first.first.tainted?)
+ s = Marshal.dump(obj)
+ assert_equal(false, s.tainted?)
+
+ # tainted object causes tainted stream
+ x.taint
+ assert_equal(false, obj.tainted?)
+ assert_equal(false, obj.first.tainted?)
+ assert_equal(true, obj.first.first.tainted?)
+ t = Marshal.dump(obj)
+ assert_equal(true, t.tainted?)
+
+ # clean stream causes clean objects
+ assert_equal(false, s.tainted?)
+ y = Marshal.load(s)
+ assert_equal(false, y.tainted?)
+ assert_equal(false, y.first.tainted?)
+ assert_equal(false, y.first.first.tainted?)
+
+ # tainted stream causes tainted objects
+ assert_equal(true, t.tainted?)
+ y = Marshal.load(t)
+ assert_equal(true, y.tainted?)
+ assert_equal(true, y.first.tainted?)
+ assert_equal(true, y.first.first.tainted?)
+
+ # same tests by different senario
+ s.taint
+ assert_equal(true, s.tainted?)
+ y = Marshal.load(s)
+ assert_equal(true, y.tainted?)
+ assert_equal(true, y.first.tainted?)
+ assert_equal(true, y.first.first.tainted?)
+ end
+
+ def test_symbol2
+ [:ruby, :"\u{7d05}\u{7389}"].each do |sym|
+ assert_equal(sym, Marshal.load(Marshal.dump(sym)), '[ruby-core:24788]')
+ end
+ bug2548 = '[ruby-core:27375]'
+ ary = [:$1, nil]
+ assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug2548)
+ end
+
+ def test_symlink
+ assert_include(Marshal.dump([:a, :a]), ';')
+ end
+
+ def test_symlink_in_ivar
+ bug10991 = '[ruby-core:68587] [Bug #10991]'
+ sym = Marshal.load("\x04\x08" +
+ "I" ":\x0bKernel" +
+ ("\x06" +
+ ("I" ":\x07@a" +
+ ("\x06" ":\x07@b" "e;\x0""o:\x0bObject""\x0")) +
+ "0"))
+ assert_equal(:Kernel, sym, bug10991)
+ end
+
+ ClassUTF8 = eval("class R\u{e9}sum\u{e9}; self; end")
+
+ iso_8859_1 = Encoding::ISO_8859_1
+
+ structISO8859_1 = Struct.new("r\xe9sum\xe9".force_encoding(iso_8859_1).intern)
+ const_set("R\xe9sum\xe9".force_encoding(iso_8859_1), structISO8859_1)
+ structISO8859_1.name
+ StructISO8859_1 = structISO8859_1
+ classISO8859_1 = Class.new do
+ attr_accessor "r\xe9sum\xe9".force_encoding(iso_8859_1)
+ eval("def initialize(x) @r\xe9sum\xe9 = x; end".force_encoding(iso_8859_1))
+ end
+ const_set("R\xe9sum\xe92".force_encoding(iso_8859_1), classISO8859_1)
+ classISO8859_1.name
+ ClassISO8859_1 = classISO8859_1
+
+ def test_class_nonascii
+ a = ClassUTF8.new
+ assert_instance_of(ClassUTF8, Marshal.load(Marshal.dump(a)), '[ruby-core:24790]')
+
+ bug1932 = '[ruby-core:24882]'
+
+ a = StructISO8859_1.new(10)
+ assert_nothing_raised(bug1932) do
+ assert_equal(a, Marshal.load(Marshal.dump(a)), bug1932)
+ end
+ a.__send__("#{StructISO8859_1.members[0]}=", a)
+ assert_nothing_raised(bug1932) do
+ assert_equal(a, Marshal.load(Marshal.dump(a)), bug1932)
+ end
+
+ a = ClassISO8859_1.new(10)
+ assert_nothing_raised(bug1932) do
+ b = Marshal.load(Marshal.dump(a))
+ assert_equal(ClassISO8859_1, b.class, bug1932)
+ assert_equal(a.instance_variables, b.instance_variables, bug1932)
+ a.instance_variables.each do |i|
+ assert_equal(a.instance_variable_get(i), b.instance_variable_get(i), bug1932)
+ end
+ end
+ a.__send__(a.methods(true).grep(/=\z/)[0], a)
+ assert_nothing_raised(bug1932) do
+ b = Marshal.load(Marshal.dump(a))
+ assert_equal(ClassISO8859_1, b.class, bug1932)
+ assert_equal(a.instance_variables, b.instance_variables, bug1932)
+ assert_equal(b, b.instance_variable_get(a.instance_variables[0]), bug1932)
+ end
+ end
+
+ def test_regexp2
+ assert_equal(/\\u/, Marshal.load("\004\b/\b\\\\u\000"))
+ assert_equal(/u/, Marshal.load("\004\b/\a\\u\000"))
+ assert_equal(/u/, Marshal.load("\004\bI/\a\\u\000\006:\016@encoding\"\vEUC-JP"))
+
+ bug2109 = '[ruby-core:25625]'
+ a = "\x82\xa0".force_encoding(Encoding::Windows_31J)
+ b = "\x82\xa2".force_encoding(Encoding::Windows_31J)
+ c = [/#{a}/, /#{b}/]
+ assert_equal(c, Marshal.load(Marshal.dump(c)), bug2109)
+
+ assert_nothing_raised(ArgumentError, '[ruby-dev:40386]') do
+ re = Tempfile.create("marshal_regexp") do |f|
+ f.binmode.write("\x04\bI/\x00\x00\x06:\rencoding\"\rUS-ASCII")
+ f.rewind
+ re2 = Marshal.load(f)
+ re2
+ end
+ assert_equal(//, re)
+ end
+ end
+
+ class DumpTest
+ def marshal_dump
+ @@block.call(:marshal_dump)
+ end
+
+ def dump_each(&block)
+ @@block = block
+ Marshal.dump(self)
+ end
+ end
+
+ class LoadTest
+ def marshal_dump
+ nil
+ end
+ def marshal_load(obj)
+ @@block.call(:marshal_load)
+ end
+ def self.load_each(m, &block)
+ @@block = block
+ Marshal.load(m)
+ end
+ end
+
+ def test_context_switch
+ o = DumpTest.new
+ e = o.enum_for(:dump_each)
+ assert_equal(:marshal_dump, e.next)
+ GC.start
+ assert(true, '[ruby-dev:39425]')
+ assert_raise(StopIteration) {e.next}
+
+ o = LoadTest.new
+ m = Marshal.dump(o)
+ e = LoadTest.enum_for(:load_each, m)
+ assert_equal(:marshal_load, e.next)
+ GC.start
+ assert(true, '[ruby-dev:39425]')
+ assert_raise(StopIteration) {e.next}
+ end
+
+ def test_dump_buffer
+ bug2390 = '[ruby-dev:39744]'
+ w = ""
+ def w.write(str)
+ self << str.to_s
+ end
+ Marshal.dump(Object.new, w)
+ assert_not_empty(w, bug2390)
+ end
+
+ class C5
+ def marshal_dump
+ "foo"
+ end
+ def marshal_load(foo)
+ @foo = foo
+ end
+ def initialize(x)
+ @x = x
+ end
+ end
+ def test_marshal_dump
+ c = C5.new("bar")
+ s = Marshal.dump(c)
+ d = Marshal.load(s)
+ assert_equal("foo", d.instance_variable_get(:@foo))
+ assert_equal(false, d.instance_variable_defined?(:@x))
+ end
+
+ class C6
+ def initialize
+ @stdin = STDIN
+ end
+ attr_reader :stdin
+ def marshal_dump
+ 1
+ end
+ def marshal_load(x)
+ @stdin = STDIN
+ end
+ end
+ def test_marshal_dump_extra_iv
+ o = C6.new
+ m = nil
+ assert_nothing_raised("[ruby-dev:21475] [ruby-dev:39845]") {
+ m = Marshal.dump(o)
+ }
+ o2 = Marshal.load(m)
+ assert_equal(STDIN, o2.stdin)
+ end
+
+ def test_marshal_string_encoding
+ o1 = ["foo".force_encoding("EUC-JP")] + [ "bar" ] * 2
+ m = Marshal.dump(o1)
+ o2 = Marshal.load(m)
+ assert_equal(o1, o2, "[ruby-dev:40388]")
+ end
+
+ def test_marshal_regexp_encoding
+ o1 = [Regexp.new("r1".force_encoding("EUC-JP"))] + ["r2"] * 2
+ m = Marshal.dump(o1)
+ o2 = Marshal.load(m)
+ assert_equal(o1, o2, "[ruby-dev:40416]")
+ end
+
+ def test_marshal_encoding_encoding
+ o1 = [Encoding.find("EUC-JP")] + ["r2"] * 2
+ m = Marshal.dump(o1)
+ o2 = Marshal.load(m)
+ assert_equal(o1, o2)
+ end
+
+ def test_marshal_symbol_ascii8bit
+ bug6209 = '[ruby-core:43762]'
+ o1 = "\xff".force_encoding("ASCII-8BIT").intern
+ m = Marshal.dump(o1)
+ o2 = nil
+ assert_nothing_raised(EncodingError, bug6209) {o2 = Marshal.load(m)}
+ assert_equal(o1, o2, bug6209)
+ end
+
+ class PrivateClass
+ def initialize(foo)
+ @foo = foo
+ end
+ attr_reader :foo
+ end
+ private_constant :PrivateClass
+
+ def test_marshal_private_class
+ o1 = PrivateClass.new("test")
+ o2 = Marshal.load(Marshal.dump(o1))
+ assert_equal(o1.class, o2.class)
+ assert_equal(o1.foo, o2.foo)
+ end
+
+ def test_marshal_complex
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x05")}
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x06i\x00")}
+ assert_equal(Complex(1, 2), Marshal.load("\x04\bU:\fComplex[\ai\x06i\a"))
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\bi\x00i\x00i\x00")}
+ end
+
+ def test_marshal_rational
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x05")}
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x06i\x00")}
+ assert_equal(Rational(1, 2), Marshal.load("\x04\bU:\rRational[\ai\x06i\a"))
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\bi\x00i\x00i\x00")}
+ end
+
+ def test_marshal_flonum_reference
+ bug7348 = '[ruby-core:49323]'
+ e = []
+ ary = [ [2.0, e], [e] ]
+ assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug7348)
+ end
+
+ class TestClass
+ end
+
+ module TestModule
+ end
+
+ def test_marshal_load_should_not_taint_classes
+ bug7325 = '[ruby-core:49198]'
+ for c in [TestClass, TestModule]
+ assert_not_predicate(c, :tainted?)
+ c2 = Marshal.load(Marshal.dump(c).taint)
+ assert_same(c, c2)
+ assert_not_predicate(c, :tainted?, bug7325)
+ end
+ end
+
+ class Bug7627 < Struct.new(:bar)
+ attr_accessor :foo
+
+ def marshal_dump; 'dump'; end # fake dump data
+ def marshal_load(*); end # do nothing
+ end
+
+ def test_marshal_dump_struct_ivar
+ bug7627 = '[ruby-core:51163]'
+ obj = Bug7627.new
+ obj.foo = '[Bug #7627]'
+
+ dump = Marshal.dump(obj)
+ loaded = Marshal.load(dump)
+
+ assert_equal(obj, loaded, bug7627)
+ assert_nil(loaded.foo, bug7627)
+ end
+
+ class LoadData
+ attr_reader :data
+ def initialize(data)
+ @data = data
+ end
+ alias marshal_dump data
+ alias marshal_load initialize
+ end
+
+ class Bug8276 < LoadData
+ def initialize(*)
+ super
+ freeze
+ end
+ alias marshal_load initialize
+ end
+
+ class FrozenData < LoadData
+ def marshal_load(data)
+ super
+ data.instance_variables.each do |iv|
+ instance_variable_set(iv, data.instance_variable_get(iv))
+ end
+ freeze
+ end
+ end
+
+ def test_marshal_dump_excess_encoding
+ bug8276 = '[ruby-core:54334] [Bug #8276]'
+ t = Bug8276.new(bug8276)
+ s = Marshal.dump(t)
+ assert_nothing_raised(RuntimeError, bug8276) {s = Marshal.load(s)}
+ assert_equal(t.data, s.data, bug8276)
+ end
+
+ def test_marshal_dump_ivar
+ s = "data with ivar"
+ s.instance_variable_set(:@t, 42)
+ t = Bug8276.new(s)
+ s = Marshal.dump(t)
+ assert_raise(RuntimeError) {Marshal.load(s)}
+ end
+
+ def test_marshal_load_ivar
+ s = "data with ivar"
+ s.instance_variable_set(:@t, 42)
+ hook = ->(v) {
+ if LoadData === v
+ assert_send([v, :instance_variable_defined?, :@t], v.class.name)
+ assert_equal(42, v.instance_variable_get(:@t), v.class.name)
+ end
+ v
+ }
+ [LoadData, FrozenData].each do |klass|
+ t = klass.new(s)
+ d = Marshal.dump(t)
+ v = assert_nothing_raised(RuntimeError) {break Marshal.load(d, hook)}
+ assert_send([v, :instance_variable_defined?, :@t], klass.name)
+ assert_equal(42, v.instance_variable_get(:@t), klass.name)
+ end
+ end
+
+ def test_class_ivar
+ assert_raise(TypeError) {Marshal.load("\x04\x08Ic\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_not_operator(TestClass, :instance_variable_defined?, :@bug)
+ end
+
+ def test_module_ivar
+ assert_raise(TypeError) {Marshal.load("\x04\x08Im\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_not_operator(TestModule, :instance_variable_defined?, :@bug)
+ end
+
+ class TestForRespondToFalse
+ def respond_to?(a)
+ false
+ end
+ end
+
+ def test_marshal_respond_to_arity
+ assert_nothing_raised(ArgumentError, '[Bug #7722]') do
+ Marshal.dump(TestForRespondToFalse.new)
+ end
+ end
+
+ def test_packed_string
+ packed = ["foo"].pack("p")
+ bare = "".force_encoding(Encoding::ASCII_8BIT) << packed
+ assert_equal(Marshal.dump(bare), Marshal.dump(packed))
+ end
+
+ def test_untainted_numeric
+ bug8945 = '[ruby-core:57346] [Bug #8945] Numerics never be tainted'
+ b = 1 << 32
+ b *= b until Bignum === b
+ tainted = [0, 1.0, 1.72723e-77, b].select do |x|
+ Marshal.load(Marshal.dump(x).taint).tainted?
+ end
+ assert_empty(tainted.map {|x| [x, x.class]}, bug8945)
+ end
+
+ class Bug9523
+ attr_reader :cc
+ def marshal_dump
+ callcc {|c| @cc = c }
+ nil
+ end
+ def marshal_load(v)
+ end
+ end
+
+ def test_continuation
+ require "continuation"
+ c = Bug9523.new
+ assert_raise_with_message(RuntimeError, /Marshal\.dump reentered at marshal_dump/) do
+ Marshal.dump(c)
+ c.cc.call
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_math.rb b/jni/ruby/test/ruby/test_math.rb
new file mode 100644
index 0000000..c4d0ff9
--- /dev/null
+++ b/jni/ruby/test/ruby/test_math.rb
@@ -0,0 +1,292 @@
+require 'test/unit'
+
+class TestMath < Test::Unit::TestCase
+ def assert_infinity(a, *rest)
+ rest = ["not infinity: #{a.inspect}"] if rest.empty?
+ assert_predicate(a, :infinite?, *rest)
+ end
+
+ def assert_nan(a, *rest)
+ rest = ["not nan: #{a.inspect}"] if rest.empty?
+ assert_predicate(a, :nan?, *rest)
+ end
+
+ def assert_float(a, b)
+ err = [Float::EPSILON * 4, [a.abs, b.abs].max * Float::EPSILON * 256].max
+ assert_in_delta(a, b, err)
+ end
+ alias check assert_float
+
+ def test_atan2
+ check(+0.0, Math.atan2(+0.0, +0.0))
+ check(-0.0, Math.atan2(-0.0, +0.0))
+ check(+Math::PI, Math.atan2(+0.0, -0.0))
+ check(-Math::PI, Math.atan2(-0.0, -0.0))
+
+ inf = Float::INFINITY
+ expected = 3.0 * Math::PI / 4.0
+ assert_nothing_raised { check(+expected, Math.atan2(+inf, -inf)) }
+ assert_nothing_raised { check(-expected, Math.atan2(-inf, -inf)) }
+ expected = Math::PI / 4.0
+ assert_nothing_raised { check(+expected, Math.atan2(+inf, +inf)) }
+ assert_nothing_raised { check(-expected, Math.atan2(-inf, +inf)) }
+
+ check(0, Math.atan2(0, 1))
+ check(Math::PI / 4, Math.atan2(1, 1))
+ check(Math::PI / 2, Math.atan2(1, 0))
+ end
+
+ def test_cos
+ check(1.0, Math.cos(0 * Math::PI / 4))
+ check(1.0 / Math.sqrt(2), Math.cos(1 * Math::PI / 4))
+ check(0.0, Math.cos(2 * Math::PI / 4))
+ check(-1.0, Math.cos(4 * Math::PI / 4))
+ check(0.0, Math.cos(6 * Math::PI / 4))
+ end
+
+ def test_sin
+ check(0.0, Math.sin(0 * Math::PI / 4))
+ check(1.0 / Math.sqrt(2), Math.sin(1 * Math::PI / 4))
+ check(1.0, Math.sin(2 * Math::PI / 4))
+ check(0.0, Math.sin(4 * Math::PI / 4))
+ check(-1.0, Math.sin(6 * Math::PI / 4))
+ end
+
+ def test_tan
+ check(0.0, Math.tan(0 * Math::PI / 4))
+ check(1.0, Math.tan(1 * Math::PI / 4))
+ assert_operator(Math.tan(2 * Math::PI / 4).abs, :>, 1024)
+ check(0.0, Math.tan(4 * Math::PI / 4))
+ assert_operator(Math.tan(6 * Math::PI / 4).abs, :>, 1024)
+ end
+
+ def test_acos
+ check(0 * Math::PI / 4, Math.acos( 1.0))
+ check(1 * Math::PI / 4, Math.acos( 1.0 / Math.sqrt(2)))
+ check(2 * Math::PI / 4, Math.acos( 0.0))
+ check(4 * Math::PI / 4, Math.acos(-1.0))
+ assert_raise(Math::DomainError) { Math.acos(+1.0 + Float::EPSILON) }
+ assert_raise(Math::DomainError) { Math.acos(-1.0 - Float::EPSILON) }
+ assert_raise(Math::DomainError) { Math.acos(2.0) }
+ end
+
+ def test_asin
+ check( 0 * Math::PI / 4, Math.asin( 0.0))
+ check( 1 * Math::PI / 4, Math.asin( 1.0 / Math.sqrt(2)))
+ check( 2 * Math::PI / 4, Math.asin( 1.0))
+ check(-2 * Math::PI / 4, Math.asin(-1.0))
+ assert_raise(Math::DomainError) { Math.asin(+1.0 + Float::EPSILON) }
+ assert_raise(Math::DomainError) { Math.asin(-1.0 - Float::EPSILON) }
+ assert_raise(Math::DomainError) { Math.asin(2.0) }
+ end
+
+ def test_atan
+ check( 0 * Math::PI / 4, Math.atan( 0.0))
+ check( 1 * Math::PI / 4, Math.atan( 1.0))
+ check( 2 * Math::PI / 4, Math.atan(1.0 / 0.0))
+ check(-1 * Math::PI / 4, Math.atan(-1.0))
+ end
+
+ def test_cosh
+ check(1, Math.cosh(0))
+ check((Math::E ** 1 + Math::E ** -1) / 2, Math.cosh(1))
+ check((Math::E ** 2 + Math::E ** -2) / 2, Math.cosh(2))
+ end
+
+ def test_sinh
+ check(0, Math.sinh(0))
+ check((Math::E ** 1 - Math::E ** -1) / 2, Math.sinh(1))
+ check((Math::E ** 2 - Math::E ** -2) / 2, Math.sinh(2))
+ end
+
+ def test_tanh
+ check(Math.sinh(0) / Math.cosh(0), Math.tanh(0))
+ check(Math.sinh(1) / Math.cosh(1), Math.tanh(1))
+ check(Math.sinh(2) / Math.cosh(2), Math.tanh(2))
+ end
+
+ def test_acosh
+ check(0, Math.acosh(1))
+ check(1, Math.acosh((Math::E ** 1 + Math::E ** -1) / 2))
+ check(2, Math.acosh((Math::E ** 2 + Math::E ** -2) / 2))
+ assert_raise(Math::DomainError) { Math.acosh(1.0 - Float::EPSILON) }
+ assert_raise(Math::DomainError) { Math.acosh(0) }
+ end
+
+ def test_asinh
+ check(0, Math.asinh(0))
+ check(1, Math.asinh((Math::E ** 1 - Math::E ** -1) / 2))
+ check(2, Math.asinh((Math::E ** 2 - Math::E ** -2) / 2))
+ end
+
+ def test_atanh
+ check(0, Math.atanh(Math.sinh(0) / Math.cosh(0)))
+ check(1, Math.atanh(Math.sinh(1) / Math.cosh(1)))
+ check(2, Math.atanh(Math.sinh(2) / Math.cosh(2)))
+ assert_nothing_raised { assert_infinity(Math.atanh(1)) }
+ assert_nothing_raised { assert_infinity(-Math.atanh(-1)) }
+ assert_raise(Math::DomainError) { Math.atanh(+1.0 + Float::EPSILON) }
+ assert_raise(Math::DomainError) { Math.atanh(-1.0 - Float::EPSILON) }
+ end
+
+ def test_exp
+ check(1, Math.exp(0))
+ check(Math.sqrt(Math::E), Math.exp(0.5))
+ check(Math::E, Math.exp(1))
+ check(Math::E ** 2, Math.exp(2))
+ end
+
+ def test_log
+ check(0, Math.log(1))
+ check(1, Math.log(Math::E))
+ check(0, Math.log(1, 10))
+ check(1, Math.log(10, 10))
+ check(2, Math.log(100, 10))
+ check(Math.log(2.0 ** 64), Math.log(1 << 64))
+ assert_equal(1.0/0, Math.log(1.0/0))
+ assert_nothing_raised { assert_infinity(-Math.log(+0.0)) }
+ assert_nothing_raised { assert_infinity(-Math.log(-0.0)) }
+ assert_raise(Math::DomainError) { Math.log(-1.0) }
+ assert_raise(TypeError) { Math.log(1,nil) }
+ assert_raise(Math::DomainError, '[ruby-core:62309] [ruby-Bug #9797]') { Math.log(1.0, -1.0) }
+ assert_nothing_raised { assert_nan(Math.log(0.0, 0.0)) }
+ end
+
+ def test_log2
+ check(0, Math.log2(1))
+ check(1, Math.log2(2))
+ check(2, Math.log2(4))
+ check(Math.log2(2.0 ** 64), Math.log2(1 << 64))
+ assert_equal(1.0/0, Math.log2(1.0/0))
+ assert_nothing_raised { assert_infinity(-Math.log2(+0.0)) }
+ assert_nothing_raised { assert_infinity(-Math.log2(-0.0)) }
+ assert_raise(Math::DomainError) { Math.log2(-1.0) }
+ end
+
+ def test_log10
+ check(0, Math.log10(1))
+ check(1, Math.log10(10))
+ check(2, Math.log10(100))
+ check(Math.log10(2.0 ** 64), Math.log10(1 << 64))
+ assert_equal(1.0/0, Math.log10(1.0/0))
+ assert_nothing_raised { assert_infinity(-Math.log10(+0.0)) }
+ assert_nothing_raised { assert_infinity(-Math.log10(-0.0)) }
+ assert_raise(Math::DomainError) { Math.log10(-1.0) }
+ end
+
+ def test_sqrt
+ check(0, Math.sqrt(0))
+ check(1, Math.sqrt(1))
+ check(2, Math.sqrt(4))
+ assert_equal(1.0/0, Math.sqrt(1.0/0))
+ assert_equal("0.0", Math.sqrt(-0.0).to_s) # insure it is +0.0, not -0.0
+ assert_raise(Math::DomainError) { Math.sqrt(-1.0) }
+ end
+
+ def test_frexp
+ check(0.0, Math.frexp(0.0).first)
+ assert_equal(0, Math.frexp(0).last)
+ check(0.5, Math.frexp(0.5).first)
+ assert_equal(0, Math.frexp(0.5).last)
+ check(0.5, Math.frexp(1.0).first)
+ assert_equal(1, Math.frexp(1.0).last)
+ check(0.5, Math.frexp(2.0).first)
+ assert_equal(2, Math.frexp(2.0).last)
+ check(0.75, Math.frexp(3.0).first)
+ assert_equal(2, Math.frexp(3.0).last)
+ end
+
+ def test_ldexp
+ check(0.0, Math.ldexp(0.0, 0.0))
+ check(0.5, Math.ldexp(0.5, 0.0))
+ check(1.0, Math.ldexp(0.5, 1.0))
+ check(2.0, Math.ldexp(0.5, 2.0))
+ check(3.0, Math.ldexp(0.75, 2.0))
+ end
+
+ def test_hypot
+ check(5, Math.hypot(3, 4))
+ end
+
+ def test_erf
+ check(0, Math.erf(0))
+ check(1, Math.erf(1.0 / 0.0))
+ end
+
+ def test_erfc
+ check(1, Math.erfc(0))
+ check(0, Math.erfc(1.0 / 0.0))
+ end
+
+ def test_gamma
+ sqrt_pi = Math.sqrt(Math::PI)
+ check(4 * sqrt_pi / 3, Math.gamma(-1.5))
+ check(-2 * sqrt_pi, Math.gamma(-0.5))
+ check(sqrt_pi, Math.gamma(0.5))
+ check(1, Math.gamma(1))
+ check(sqrt_pi / 2, Math.gamma(1.5))
+ check(1, Math.gamma(2))
+ check(3 * sqrt_pi / 4, Math.gamma(2.5))
+ check(2, Math.gamma(3))
+ check(15 * sqrt_pi / 8, Math.gamma(3.5))
+ check(6, Math.gamma(4))
+
+ # no SEGV [ruby-core:25257]
+ 31.upto(65) do |i|
+ i = 1 << i
+ assert_infinity(Math.gamma(i), "Math.gamma(#{i}) should be INF")
+ assert_infinity(Math.gamma(i-1), "Math.gamma(#{i-1}) should be INF")
+ end
+
+ assert_raise(Math::DomainError) { Math.gamma(-Float::INFINITY) }
+ end
+
+ def test_lgamma
+ sqrt_pi = Math.sqrt(Math::PI)
+
+ g, s = Math.lgamma(-1.5)
+ check(Math.log(4 * sqrt_pi / 3), g)
+ assert_equal(s, 1)
+
+ g, s = Math.lgamma(-0.5)
+ check(Math.log(2 * sqrt_pi), g)
+ assert_equal(s, -1)
+
+ g, s = Math.lgamma(0.5)
+ check(Math.log(sqrt_pi), g)
+ assert_equal(s, 1)
+
+ assert_equal([0, 1], Math.lgamma(1))
+
+ g, s = Math.lgamma(1.5)
+ check(Math.log(sqrt_pi / 2), g)
+ assert_equal(s, 1)
+
+ assert_equal([0, 1], Math.lgamma(2))
+
+ g, s = Math.lgamma(2.5)
+ check(Math.log(3 * sqrt_pi / 4), g)
+ assert_equal(s, 1)
+
+ g, s = Math.lgamma(3)
+ check(Math.log(2), g)
+ assert_equal(s, 1)
+
+ g, s = Math.lgamma(3.5)
+ check(Math.log(15 * sqrt_pi / 8), g)
+ assert_equal(s, 1)
+
+ g, s = Math.lgamma(4)
+ check(Math.log(6), g)
+ assert_equal(s, 1)
+
+ assert_raise(Math::DomainError) { Math.lgamma(-Float::INFINITY) }
+ end
+
+ def test_cbrt
+ check(1, Math.cbrt(1))
+ check(-2, Math.cbrt(-8))
+ check(3, Math.cbrt(27))
+ check(-0.1, Math.cbrt(-0.001))
+ end
+end
diff --git a/jni/ruby/test/ruby/test_metaclass.rb b/jni/ruby/test/ruby/test_metaclass.rb
new file mode 100644
index 0000000..6386a02
--- /dev/null
+++ b/jni/ruby/test/ruby/test_metaclass.rb
@@ -0,0 +1,167 @@
+require 'test/unit'
+
+class TestMetaclass < Test::Unit::TestCase
+ class Foo; end
+ class Bar < Foo; end
+ class Baz; end
+
+ def setup
+ Object.class_eval do
+ def method_o; end
+ end
+ Module.class_eval do
+ def method_m; end
+ end
+ Class.class_eval do
+ def method_c; end
+ end
+ end
+ def teardown
+ Object.class_eval do
+ remove_method :method_o rescue nil
+ end
+ Module.class_eval do
+ remove_method :method_m rescue nil
+ end
+ Class.class_eval do
+ remove_method :method_c rescue nil
+ end
+ Object.class_eval do
+ class << self
+ remove_method :class_method_o rescue nil
+ end
+ end
+ Module.class_eval do
+ class << self
+ remove_method :class_method_m rescue nil
+ end
+ end
+ Class.class_eval do
+ class << self
+ remove_method :class_method_c rescue nil
+ end
+ end
+ Object.class_eval do
+ class << self
+ class << self
+ remove_method :metaclass_method_o rescue nil
+ end
+ end
+ end
+ Module.class_eval do
+ class << self
+ class << self
+ remove_method :metaclass_method_m rescue nil
+ end
+ end
+ end
+ Class.class_eval do
+ class << self
+ class << self
+ remove_method :metaclass_method_c rescue nil
+ end
+ end
+ end
+ end
+
+ def test_metaclass
+ class << Object
+ def class_method_o; end
+ end
+ class << Foo
+ def class_method_f; end
+ end
+ class << Baz
+ def class_method_b; end
+ end
+ assert_nothing_raised{ Bar.method_o }
+ assert_nothing_raised{ Bar.method_m }
+ assert_nothing_raised{ Bar.method_c }
+ assert_nothing_raised{ Bar.class_method_o }
+ assert_nothing_raised{ Bar.class_method_f }
+ assert_raise(NoMethodError){ Bar.class_method_b }
+
+ class << Module
+ def class_method_m; end
+ end
+ class << Class
+ def class_method_c; end
+ end
+ class << Object
+ class << self
+ def metaclass_method_o; end
+ end
+ end
+ class << Foo
+ class << self
+ def metaclass_method_f; end
+ end
+ end
+ class << Baz
+ class << self
+ def metaclass_method_b; end
+ end
+ end
+ metaclass_of_bar = class << Bar; self end
+ assert_nothing_raised{ metaclass_of_bar.method_o }
+ assert_nothing_raised{ metaclass_of_bar.method_m }
+ assert_nothing_raised{ metaclass_of_bar.method_c }
+ assert_nothing_raised{ metaclass_of_bar.class_method_o }
+ assert_raise(NoMethodError){ metaclass_of_bar.class_method_f }
+ assert_raise(NoMethodError){ metaclass_of_bar.class_method_b }
+ assert_nothing_raised{ metaclass_of_bar.class_method_m }
+ assert_nothing_raised{ metaclass_of_bar.class_method_c }
+ assert_nothing_raised{ metaclass_of_bar.metaclass_method_o }
+ assert_nothing_raised{ metaclass_of_bar.metaclass_method_f }
+ assert_raise(NoMethodError){ metaclass_of_bar.metaclass_method_b }
+
+ class << Module
+ class << self
+ def metaclass_method_m; end
+ end
+ end
+ class << Class
+ class << self
+ def metaclass_method_c; end
+ end
+ end
+ class << Object
+ class << self
+ class << self
+ def metametaclass_method_o; end
+ end
+ end
+ end
+ class << Foo
+ class << self
+ class << self
+ def metametaclass_method_f; end
+ end
+ end
+ end
+ class << Baz
+ class << self
+ class << self
+ def metametaclass_method_b; end
+ end
+ end
+ end
+ metametaclass_of_bar = class << metaclass_of_bar; self end
+ assert_nothing_raised{ metametaclass_of_bar.method_o }
+ assert_nothing_raised{ metametaclass_of_bar.method_m }
+ assert_nothing_raised{ metametaclass_of_bar.method_c }
+ assert_nothing_raised{ metametaclass_of_bar.class_method_o }
+ assert_raise(NoMethodError){ metametaclass_of_bar.class_method_f }
+ assert_raise(NoMethodError){ metametaclass_of_bar.class_method_b }
+ assert_nothing_raised{ metametaclass_of_bar.class_method_m }
+ assert_nothing_raised{ metametaclass_of_bar.class_method_c }
+ assert_nothing_raised{ metametaclass_of_bar.metaclass_method_o }
+ assert_raise(NoMethodError){ metametaclass_of_bar.metaclass_method_f }
+ assert_raise(NoMethodError){ metametaclass_of_bar.metaclass_method_b }
+ assert_nothing_raised{ metametaclass_of_bar.metaclass_method_m }
+ assert_nothing_raised{ metametaclass_of_bar.metaclass_method_c }
+ assert_nothing_raised{ metametaclass_of_bar.metametaclass_method_o }
+ assert_nothing_raised{ metametaclass_of_bar.metametaclass_method_f }
+ assert_raise(NoMethodError){ metametaclass_of_bar.metaclass_method_b }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_method.rb b/jni/ruby/test/ruby/test_method.rb
new file mode 100644
index 0000000..35ddc87
--- /dev/null
+++ b/jni/ruby/test/ruby/test_method.rb
@@ -0,0 +1,894 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+
+class TestMethod < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def m0() end
+ def m1(a) end
+ def m2(a, b) end
+ def mo1(a = nil, &b) end
+ def mo2(a, b = nil) end
+ def mo3(*a) end
+ def mo4(a, *b, &c) end
+ def mo5(a, *b, c) end
+ def mo6(a, *b, c, &d) end
+ def mo7(a, b = nil, *c, d, &e) end
+ def ma1((a), &b) nil && a end
+ def mk1(**) end
+ def mk2(**o) nil && o end
+ def mk3(a, **o) nil && o end
+ def mk4(a = nil, **o) nil && o end
+ def mk5(a, b = nil, **o) nil && o end
+ def mk6(a, b = nil, c, **o) nil && o end
+ def mk7(a, b = nil, *c, d, **o) nil && o end
+
+ class Base
+ def foo() :base end
+ end
+ class Derived < Base
+ def foo() :derived end
+ end
+ class T
+ def initialize; end
+ def initialize_copy(*) super end
+ def initialize_clone(*) super end
+ def initialize_dup(*) super end
+ def respond_to_missing?(*) super end
+ def normal_method; end
+ end
+ module M
+ def func; end
+ module_function :func
+ def meth; :meth end
+ end
+
+ def mv1() end
+ def mv2() end
+ private :mv2
+ def mv3() end
+ protected :mv3
+
+ class Visibility
+ def mv1() end
+ def mv2() end
+ private :mv2
+ def mv3() end
+ protected :mv3
+ end
+
+ def test_arity
+ assert_equal(0, method(:m0).arity)
+ assert_equal(1, method(:m1).arity)
+ assert_equal(2, method(:m2).arity)
+ assert_equal(-1, method(:mo1).arity)
+ assert_equal(-2, method(:mo2).arity)
+ assert_equal(-1, method(:mo3).arity)
+ assert_equal(-2, method(:mo4).arity)
+ assert_equal(-3, method(:mo5).arity)
+ assert_equal(-3, method(:mo6).arity)
+ assert_equal(-1, method(:mk1).arity)
+ assert_equal(-1, method(:mk2).arity)
+ assert_equal(-2, method(:mk3).arity)
+ assert_equal(-1, method(:mk4).arity)
+ assert_equal(-2, method(:mk5).arity)
+ assert_equal(-3, method(:mk6).arity)
+ assert_equal(-3, method(:mk7).arity)
+ end
+
+ def test_arity_special
+ assert_equal(-1, method(:__send__).arity)
+ end
+
+ def test_unbind
+ assert_equal(:derived, Derived.new.foo)
+ um = Derived.new.method(:foo).unbind
+ assert_instance_of(UnboundMethod, um)
+ Derived.class_eval do
+ def foo() :changed end
+ end
+ assert_equal(:changed, Derived.new.foo)
+ assert_equal(:derived, um.bind(Derived.new).call)
+ assert_raise(TypeError) do
+ um.bind(Base.new)
+ end
+ end
+
+ def test_callee
+ assert_equal(:test_callee, __method__)
+ assert_equal(:m, Class.new {def m; __method__; end}.new.m)
+ assert_equal(:m, Class.new {def m; tap{return __method__}; end}.new.m)
+ assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m)
+ assert_equal(:m, Class.new {define_method(:m) {tap{return __method__}}}.new.m)
+ assert_nil(eval("class TestCallee; __method__; end"))
+
+ assert_equal(:test_callee, __callee__)
+ [
+ ["method", Class.new {def m; __callee__; end},],
+ ["block", Class.new {def m; tap{return __callee__}; end},],
+ ["define_method", Class.new {define_method(:m) {__callee__}}],
+ ["define_method block", Class.new {define_method(:m) {tap{return __callee__}}}],
+ ].each do |mesg, c|
+ c.class_eval {alias m2 m}
+ o = c.new
+ assert_equal(:m, o.m, mesg)
+ assert_equal(:m2, o.m2, mesg)
+ end
+ assert_nil(eval("class TestCallee; __callee__; end"))
+ end
+
+ def test_method_in_define_method_block
+ bug4606 = '[ruby-core:35386]'
+ c = Class.new do
+ [:m1, :m2].each do |m|
+ define_method(m) do
+ __method__
+ end
+ end
+ end
+ assert_equal(:m1, c.new.m1, bug4606)
+ assert_equal(:m2, c.new.m2, bug4606)
+ end
+
+ def test_method_in_block_in_define_method_block
+ bug4606 = '[ruby-core:35386]'
+ c = Class.new do
+ [:m1, :m2].each do |m|
+ define_method(m) do
+ tap { return __method__ }
+ end
+ end
+ end
+ assert_equal(:m1, c.new.m1, bug4606)
+ assert_equal(:m2, c.new.m2, bug4606)
+ end
+
+ def test_body
+ o = Object.new
+ def o.foo; end
+ assert_nothing_raised { RubyVM::InstructionSequence.disasm(o.method(:foo)) }
+ assert_nothing_raised { RubyVM::InstructionSequence.disasm("x".method(:upcase)) }
+ assert_nothing_raised { RubyVM::InstructionSequence.disasm(method(:to_s).to_proc) }
+ end
+
+ def test_new
+ c1 = Class.new
+ c1.class_eval { def foo; :foo; end }
+ c2 = Class.new(c1)
+ c2.class_eval { private :foo }
+ o = c2.new
+ o.extend(Module.new)
+ assert_raise(NameError) { o.method(:bar) }
+ assert_raise(NameError) { o.public_method(:foo) }
+ assert_equal(:foo, o.method(:foo).call)
+ end
+
+ def test_eq
+ o = Object.new
+ class << o
+ def foo; end
+ alias bar foo
+ def baz; end
+ end
+ assert_not_equal(o.method(:foo), nil)
+ m = o.method(:foo)
+ def m.foo; end
+ assert_not_equal(o.method(:foo), m)
+ assert_equal(o.method(:foo), o.method(:foo))
+ assert_equal(o.method(:foo), o.method(:bar))
+ assert_not_equal(o.method(:foo), o.method(:baz))
+ end
+
+ def test_hash
+ o = Object.new
+ def o.foo; end
+ assert_kind_of(Integer, o.method(:foo).hash)
+ assert_equal(Array.instance_method(:map).hash, Array.instance_method(:collect).hash)
+ end
+
+ def test_owner
+ c = Class.new do
+ def foo; end
+ end
+ assert_equal(c, c.instance_method(:foo).owner)
+ c2 = Class.new(c)
+ assert_equal(c, c2.instance_method(:foo).owner)
+ end
+
+ def test_owner_missing
+ c = Class.new do
+ def respond_to_missing?(name, bool)
+ name == :foo
+ end
+ end
+ c2 = Class.new(c)
+ assert_equal(c, c.new.method(:foo).owner)
+ assert_equal(c2, c2.new.method(:foo).owner)
+ end
+
+ def test_receiver_name_owner
+ o = Object.new
+ def o.foo; end
+ m = o.method(:foo)
+ assert_equal(o, m.receiver)
+ assert_equal(:foo, m.name)
+ assert_equal(class << o; self; end, m.owner)
+ assert_equal(:foo, m.unbind.name)
+ assert_equal(class << o; self; end, m.unbind.owner)
+ class << o
+ alias bar foo
+ end
+ m = o.method(:bar)
+ assert_equal(:bar, m.name)
+ assert_equal(:foo, m.original_name)
+ end
+
+ def test_instance_method
+ c = Class.new
+ c.class_eval do
+ def foo; :foo; end
+ private :foo
+ end
+ o = c.new
+ o.method(:foo).unbind
+ assert_raise(NoMethodError) { o.foo }
+ c.instance_method(:foo).bind(o)
+ assert_equal(:foo, o.instance_eval { foo })
+ assert_raise(NameError) { c.public_instance_method(:foo) }
+ def o.bar; end
+ m = o.method(:bar).unbind
+ assert_raise(TypeError) { m.bind(Object.new) }
+
+ feature4254 = '[ruby-core:34267]'
+ m = M.instance_method(:meth)
+ assert_equal(:meth, m.bind(Object.new).call, feature4254)
+ end
+
+ def test_define_method
+ c = Class.new
+ c.class_eval { def foo; :foo; end }
+ o = c.new
+ def o.bar; :bar; end
+ assert_raise(TypeError) do
+ c.class_eval { define_method(:foo, :foo) }
+ end
+ assert_raise(ArgumentError) do
+ c.class_eval { define_method }
+ end
+ c2 = Class.new(c)
+ c2.class_eval { define_method(:baz, o.method(:foo)) }
+ assert_equal(:foo, c2.new.baz)
+ assert_raise(TypeError) do
+ Class.new.class_eval { define_method(:foo, o.method(:foo)) }
+ end
+ assert_raise(TypeError) do
+ Class.new.class_eval { define_method(:bar, o.method(:bar)) }
+ end
+ end
+
+ def test_define_singleton_method
+ o = Object.new
+ def o.foo(c)
+ c.class_eval { define_method(:foo) }
+ end
+ c = Class.new
+ o.foo(c) { :foo }
+ assert_equal(:foo, c.new.foo)
+
+ o = Object.new
+ o.instance_eval { define_singleton_method(:foo) { :foo } }
+ assert_equal(:foo, o.foo)
+
+ assert_raise(TypeError) do
+ Class.new.class_eval { define_method(:foo, Object.new) }
+ end
+
+ assert_raise(TypeError) do
+ Module.new.module_eval {define_method(:foo, Base.instance_method(:foo))}
+ end
+ end
+
+ def test_define_singleton_method_with_extended_method
+ bug8686 = "[ruby-core:56174]"
+
+ m = Module.new do
+ extend self
+
+ def a
+ "a"
+ end
+ end
+
+ assert_nothing_raised(bug8686) do
+ m.define_singleton_method(:a, m.method(:a))
+ end
+ end
+
+ def test_define_method_transplating
+ feature4254 = '[ruby-core:34267]'
+ m = Module.new {define_method(:meth, M.instance_method(:meth))}
+ assert_equal(:meth, Object.new.extend(m).meth, feature4254)
+ c = Class.new {define_method(:meth, M.instance_method(:meth))}
+ assert_equal(:meth, c.new.meth, feature4254)
+ end
+
+ def test_define_method_visibility
+ c = Class.new do
+ public
+ define_method(:foo) {:foo}
+ protected
+ define_method(:bar) {:bar}
+ private
+ define_method(:baz) {:baz}
+ end
+
+ assert_equal(true, c.public_method_defined?(:foo))
+ assert_equal(false, c.public_method_defined?(:bar))
+ assert_equal(false, c.public_method_defined?(:baz))
+
+ assert_equal(false, c.protected_method_defined?(:foo))
+ assert_equal(true, c.protected_method_defined?(:bar))
+ assert_equal(false, c.protected_method_defined?(:baz))
+
+ assert_equal(false, c.private_method_defined?(:foo))
+ assert_equal(false, c.private_method_defined?(:bar))
+ assert_equal(true, c.private_method_defined?(:baz))
+
+ m = Module.new do
+ module_function
+ define_method(:foo) {:foo}
+ end
+ assert_equal(true, m.respond_to?(:foo))
+ assert_equal(false, m.public_method_defined?(:foo))
+ assert_equal(false, m.protected_method_defined?(:foo))
+ assert_equal(true, m.private_method_defined?(:foo))
+ end
+
+ def test_define_method_in_private_scope
+ bug9005 = '[ruby-core:57747] [Bug #9005]'
+ c = Class.new
+ class << c
+ public :define_method
+ end
+ TOPLEVEL_BINDING.eval("proc{|c|c.define_method(:x) {|x|throw x}}").call(c)
+ o = c.new
+ assert_throw(bug9005) {o.x(bug9005)}
+ end
+
+ def test_singleton_define_method_in_private_scope
+ bug9141 = '[ruby-core:58497] [Bug #9141]'
+ o = Object.new
+ class << o
+ public :define_singleton_method
+ end
+ TOPLEVEL_BINDING.eval("proc{|o|o.define_singleton_method(:x) {|x|throw x}}").call(o)
+ assert_throw(bug9141) do
+ o.x(bug9141)
+ end
+ end
+
+ def test_super_in_proc_from_define_method
+ c1 = Class.new {
+ def m
+ :m1
+ end
+ }
+ c2 = Class.new(c1) { define_method(:m) { Proc.new { super() } } }
+ # c2.new.m.call should return :m1, but currently it raise NoMethodError.
+ # see [Bug #4881] and [Bug #3136]
+ assert_raise(NoMethodError) {
+ c2.new.m.call
+ }
+ end
+
+ def test_clone
+ o = Object.new
+ def o.foo; :foo; end
+ m = o.method(:foo)
+ def m.bar; :bar; end
+ assert_equal(:foo, m.clone.call)
+ assert_equal(:bar, m.clone.bar)
+ end
+
+ def test_inspect
+ o = Object.new
+ def o.foo; end
+ m = o.method(:foo)
+ assert_equal("#<Method: #{ o.inspect }.foo>", m.inspect)
+ m = o.method(:foo)
+ assert_equal("#<UnboundMethod: #{ class << o; self; end.inspect }#foo>", m.unbind.inspect)
+
+ c = Class.new
+ c.class_eval { def foo; end; }
+ m = c.new.method(:foo)
+ assert_equal("#<Method: #{ c.inspect }#foo>", m.inspect)
+ m = c.instance_method(:foo)
+ assert_equal("#<UnboundMethod: #{ c.inspect }#foo>", m.inspect)
+
+ c2 = Class.new(c)
+ c2.class_eval { private :foo }
+ m2 = c2.new.method(:foo)
+ assert_equal("#<Method: #{ c2.inspect }(#{ c.inspect })#foo>", m2.inspect)
+
+ bug7806 = '[ruby-core:52048] [Bug #7806]'
+ c3 = Class.new(c)
+ c3.class_eval { alias bar foo }
+ m3 = c3.new.method(:bar)
+ assert_equal("#<Method: #{c3.inspect}(#{c.inspect})#bar(foo)>", m3.inspect, bug7806)
+ end
+
+ def test_callee_top_level
+ assert_in_out_err([], "p __callee__", %w(nil), [])
+ end
+
+ def test_caller_top_level
+ assert_in_out_err([], "p caller", %w([]), [])
+ end
+
+ def test_caller_negative_level
+ assert_raise(ArgumentError) { caller(-1) }
+ end
+
+ def test_attrset_ivar
+ c = Class.new
+ c.class_eval { attr_accessor :foo }
+ o = c.new
+ o.method(:foo=).call(42)
+ assert_equal(42, o.foo)
+ assert_raise(ArgumentError) { o.method(:foo=).call(1, 2, 3) }
+ assert_raise(ArgumentError) { o.method(:foo).call(1) }
+ end
+
+ def test_default_accessibility
+ tmethods = T.public_instance_methods
+ assert_include tmethods, :normal_method, 'normal methods are public by default'
+ assert_not_include tmethods, :initialize, '#initialize is private'
+ assert_not_include tmethods, :initialize_copy, '#initialize_copy is private'
+ assert_not_include tmethods, :initialize_clone, '#initialize_clone is private'
+ assert_not_include tmethods, :initialize_dup, '#initialize_dup is private'
+ assert_not_include tmethods, :respond_to_missing?, '#respond_to_missing? is private'
+ mmethods = M.public_instance_methods
+ assert_not_include mmethods, :func, 'module methods are private by default'
+ assert_include mmethods, :meth, 'normal methods are public by default'
+ end
+
+ define_method(:pm0) {||}
+ define_method(:pm1) {|a|}
+ define_method(:pm2) {|a, b|}
+ define_method(:pmo1) {|a = nil, &b|}
+ define_method(:pmo2) {|a, b = nil|}
+ define_method(:pmo3) {|*a|}
+ define_method(:pmo4) {|a, *b, &c|}
+ define_method(:pmo5) {|a, *b, c|}
+ define_method(:pmo6) {|a, *b, c, &d|}
+ define_method(:pmo7) {|a, b = nil, *c, d, &e|}
+ define_method(:pma1) {|(a), &b| nil && a}
+ define_method(:pmk1) {|**|}
+ define_method(:pmk2) {|**o|}
+ define_method(:pmk3) {|a, **o|}
+ define_method(:pmk4) {|a = nil, **o|}
+ define_method(:pmk5) {|a, b = nil, **o|}
+ define_method(:pmk6) {|a, b = nil, c, **o|}
+ define_method(:pmk7) {|a, b = nil, *c, d, **o|}
+
+ def test_bound_parameters
+ assert_equal([], method(:m0).parameters)
+ assert_equal([[:req, :a]], method(:m1).parameters)
+ assert_equal([[:req, :a], [:req, :b]], method(:m2).parameters)
+ assert_equal([[:opt, :a], [:block, :b]], method(:mo1).parameters)
+ assert_equal([[:req, :a], [:opt, :b]], method(:mo2).parameters)
+ assert_equal([[:rest, :a]], method(:mo3).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:mo4).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:mo5).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:mo6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:mo7).parameters)
+ assert_equal([[:req], [:block, :b]], method(:ma1).parameters)
+ assert_equal([[:keyrest]], method(:mk1).parameters)
+ assert_equal([[:keyrest, :o]], method(:mk2).parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], method(:mk3).parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], method(:mk4).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:mk5).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:mk6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:mk7).parameters)
+ end
+
+ def test_unbound_parameters
+ assert_equal([], self.class.instance_method(:m0).parameters)
+ assert_equal([[:req, :a]], self.class.instance_method(:m1).parameters)
+ assert_equal([[:req, :a], [:req, :b]], self.class.instance_method(:m2).parameters)
+ assert_equal([[:opt, :a], [:block, :b]], self.class.instance_method(:mo1).parameters)
+ assert_equal([[:req, :a], [:opt, :b]], self.class.instance_method(:mo2).parameters)
+ assert_equal([[:rest, :a]], self.class.instance_method(:mo3).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:block, :c]], self.class.instance_method(:mo4).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:mo5).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:mo6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:mo7).parameters)
+ assert_equal([[:req], [:block, :b]], self.class.instance_method(:ma1).parameters)
+ assert_equal([[:keyrest]], self.class.instance_method(:mk1).parameters)
+ assert_equal([[:keyrest, :o]], self.class.instance_method(:mk2).parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], self.class.instance_method(:mk3).parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], self.class.instance_method(:mk4).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], self.class.instance_method(:mk5).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], self.class.instance_method(:mk6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:mk7).parameters)
+ end
+
+ def test_bmethod_bound_parameters
+ assert_equal([], method(:pm0).parameters)
+ assert_equal([[:req, :a]], method(:pm1).parameters)
+ assert_equal([[:req, :a], [:req, :b]], method(:pm2).parameters)
+ assert_equal([[:opt, :a], [:block, :b]], method(:pmo1).parameters)
+ assert_equal([[:req, :a], [:opt, :b]], method(:pmo2).parameters)
+ assert_equal([[:rest, :a]], method(:pmo3).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:pmo4).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:pmo5).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).parameters)
+ assert_equal([[:req], [:block, :b]], method(:pma1).parameters)
+ assert_equal([[:keyrest]], method(:pmk1).parameters)
+ assert_equal([[:keyrest, :o]], method(:pmk2).parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], method(:pmk3).parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], method(:pmk4).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:pmk5).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:pmk6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:pmk7).parameters)
+ end
+
+ def test_bmethod_unbound_parameters
+ assert_equal([], self.class.instance_method(:pm0).parameters)
+ assert_equal([[:req, :a]], self.class.instance_method(:pm1).parameters)
+ assert_equal([[:req, :a], [:req, :b]], self.class.instance_method(:pm2).parameters)
+ assert_equal([[:opt, :a], [:block, :b]], self.class.instance_method(:pmo1).parameters)
+ assert_equal([[:req, :a], [:opt, :b]], self.class.instance_method(:pmo2).parameters)
+ assert_equal([[:rest, :a]], self.class.instance_method(:pmo3).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:block, :c]], self.class.instance_method(:pmo4).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:pmo5).parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:pmo6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:pmo7).parameters)
+ assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters)
+ assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters)
+ assert_equal([[:keyrest]], self.class.instance_method(:pmk1).parameters)
+ assert_equal([[:keyrest, :o]], self.class.instance_method(:pmk2).parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], self.class.instance_method(:pmk3).parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], self.class.instance_method(:pmk4).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], self.class.instance_method(:pmk5).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], self.class.instance_method(:pmk6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:pmk7).parameters)
+ end
+
+ def test_public_method_with_zsuper_method
+ c = Class.new
+ c.class_eval do
+ def foo
+ :ok
+ end
+ private :foo
+ end
+ d = Class.new(c)
+ d.class_eval do
+ public :foo
+ end
+ assert_equal(:ok, d.new.public_method(:foo).call)
+ end
+
+ def test_public_methods_with_extended
+ m = Module.new do def m1; end end
+ a = Class.new do def a; end end
+ bug = '[ruby-dev:41553]'
+ obj = a.new
+ assert_equal([:a], obj.public_methods(false), bug)
+ obj.extend(m)
+ assert_equal([:m1, :a], obj.public_methods(false), bug)
+ end
+
+ def test_visibility
+ assert_equal('method', defined?(mv1))
+ assert_equal('method', defined?(mv2))
+ assert_equal('method', defined?(mv3))
+
+ assert_equal('method', defined?(self.mv1))
+ assert_equal(nil, defined?(self.mv2))
+ assert_equal('method', defined?(self.mv3))
+
+ assert_equal(true, respond_to?(:mv1))
+ assert_equal(false, respond_to?(:mv2))
+ assert_equal(false, respond_to?(:mv3))
+
+ assert_equal(true, respond_to?(:mv1, true))
+ assert_equal(true, respond_to?(:mv2, true))
+ assert_equal(true, respond_to?(:mv3, true))
+
+ assert_nothing_raised { mv1 }
+ assert_nothing_raised { mv2 }
+ assert_nothing_raised { mv3 }
+
+ assert_nothing_raised { self.mv1 }
+ assert_raise(NoMethodError) { self.mv2 }
+ assert_nothing_raised { self.mv3 }
+
+ v = Visibility.new
+
+ assert_equal('method', defined?(v.mv1))
+ assert_equal(nil, defined?(v.mv2))
+ assert_equal(nil, defined?(v.mv3))
+
+ assert_equal(true, v.respond_to?(:mv1))
+ assert_equal(false, v.respond_to?(:mv2))
+ assert_equal(false, v.respond_to?(:mv3))
+
+ assert_equal(true, v.respond_to?(:mv1, true))
+ assert_equal(true, v.respond_to?(:mv2, true))
+ assert_equal(true, v.respond_to?(:mv3, true))
+
+ assert_nothing_raised { v.mv1 }
+ assert_raise(NoMethodError) { v.mv2 }
+ assert_raise(NoMethodError) { v.mv3 }
+
+ assert_nothing_raised { v.__send__(:mv1) }
+ assert_nothing_raised { v.__send__(:mv2) }
+ assert_nothing_raised { v.__send__(:mv3) }
+
+ assert_nothing_raised { v.instance_eval { mv1 } }
+ assert_nothing_raised { v.instance_eval { mv2 } }
+ assert_nothing_raised { v.instance_eval { mv3 } }
+ end
+
+ def test_bound_method_entry
+ bug6171 = '[ruby-core:43383]'
+ assert_ruby_status([], <<-EOC, bug6171)
+ class Bug6171
+ def initialize(target)
+ define_singleton_method(:reverse, target.method(:reverse).to_proc)
+ end
+ end
+ 100.times {p = Bug6171.new('test'); 1000.times {p.reverse}}
+ EOC
+ end
+
+ def test___dir__
+ assert_instance_of String, __dir__
+ assert_equal(File.dirname(File.realpath(__FILE__)), __dir__)
+ bug8436 = '[ruby-core:55123] [Bug #8436]'
+ assert_equal(__dir__, eval("__dir__", binding), bug8436)
+ bug8662 = '[ruby-core:56099] [Bug #8662]'
+ assert_equal("arbitrary", eval("__dir__", binding, "arbitrary/file.rb"), bug8662)
+ assert_equal("arbitrary", Object.new.instance_eval("__dir__", "arbitrary/file.rb"), bug8662)
+ end
+
+ def test_alias_owner
+ bug7613 = '[ruby-core:51105]'
+ bug7993 = '[Bug #7993]'
+ c = Class.new {
+ def foo
+ end
+ prepend Module.new
+ attr_reader :zot
+ }
+ x = c.new
+ class << x
+ alias bar foo
+ end
+ assert_equal(c, c.instance_method(:foo).owner)
+ assert_equal(c, x.method(:foo).owner)
+ assert_equal(x.singleton_class, x.method(:bar).owner)
+ assert_not_equal(x.method(:foo), x.method(:bar), bug7613)
+ assert_equal(c, x.method(:zot).owner, bug7993)
+ assert_equal(c, c.instance_method(:zot).owner, bug7993)
+ end
+
+ def test_included
+ m = Module.new {
+ def foo
+ end
+ }
+ c = Class.new {
+ def foo
+ end
+ include m
+ }
+ assert_equal(c, c.instance_method(:foo).owner)
+ end
+
+ def test_prepended
+ bug7836 = '[ruby-core:52160] [Bug #7836]'
+ bug7988 = '[ruby-core:53038] [Bug #7988]'
+ m = Module.new {
+ def foo
+ end
+ }
+ c = Class.new {
+ def foo
+ end
+ prepend m
+ }
+ assert_raise(NameError, bug7988) {Module.new{prepend m}.instance_method(:bar)}
+ true || c || bug7836
+ end
+
+ def test_gced_bmethod
+ assert_normal_exit %q{
+ require 'irb'
+ IRB::Irb.module_eval do
+ define_method(:eval_input) do
+ IRB::Irb.module_eval { alias_method :eval_input, :to_s }
+ GC.start
+ Kernel
+ end
+ end
+ IRB.start
+ }, '[Bug #7825]'
+ end
+
+ def test_unlinked_method_entry_in_method_object_bug
+ bug8100 = '[ruby-core:53640] [Bug #8100]'
+ begin
+ assert_normal_exit %q{
+ loop do
+ def x
+ "hello" * 1000
+ end
+ method(:x).call
+ end
+ }, bug8100, timeout: 2
+ rescue Timeout::Error => e
+ else
+ end
+ assert_raise(Timeout::Error, bug8100) {raise e if e}
+ end
+
+ def test_singleton_method
+ feature8391 = '[ruby-core:54914] [Feature #8391]'
+ c1 = Class.new
+ c1.class_eval { def foo; :foo; end }
+ o = c1.new
+ def o.bar; :bar; end
+ assert_nothing_raised(NameError) {o.method(:foo)}
+ assert_raise(NameError, feature8391) {o.singleton_method(:foo)}
+ m = assert_nothing_raised(NameError, feature8391) {break o.singleton_method(:bar)}
+ assert_equal(:bar, m.call, feature8391)
+ end
+
+ Feature9783 = '[ruby-core:62212] [Feature #9783]'
+
+ def assert_curry_three_args(m)
+ curried = m.curry
+ assert_equal(6, curried.(1).(2).(3), Feature9783)
+
+ curried = m.curry(3)
+ assert_equal(6, curried.(1).(2).(3), Feature9783)
+
+ assert_raise_with_message(ArgumentError, /wrong number/) {m.curry(2)}
+ end
+
+ def test_curry_method
+ c = Class.new {
+ def three_args(a,b,c) a + b + c end
+ }
+ assert_curry_three_args(c.new.method(:three_args))
+ end
+
+ def test_curry_from_proc
+ c = Class.new {
+ define_method(:three_args) {|a,b,c| a + b + c}
+ }
+ assert_curry_three_args(c.new.method(:three_args))
+ end
+
+ def assert_curry_var_args(m)
+ curried = m.curry(3)
+ assert_equal([1, 2, 3], curried.(1).(2).(3), Feature9783)
+
+ curried = m.curry(2)
+ assert_equal([1, 2], curried.(1).(2), Feature9783)
+
+ curried = m.curry(0)
+ assert_equal([1], curried.(1), Feature9783)
+ end
+
+ def test_curry_var_args
+ c = Class.new {
+ def var_args(*args) args end
+ }
+ assert_curry_var_args(c.new.method(:var_args))
+ end
+
+ def test_curry_from_proc_var_args
+ c = Class.new {
+ define_method(:var_args) {|*args| args}
+ }
+ assert_curry_var_args(c.new.method(:var_args))
+ end
+
+ Feature9781 = '[ruby-core:62202] [Feature #9781]'
+
+ def test_super_method
+ o = Derived.new
+ m = o.method(:foo).super_method
+ assert_equal(Base, m.owner, Feature9781)
+ assert_same(o, m.receiver, Feature9781)
+ assert_equal(:foo, m.name, Feature9781)
+ m = assert_nothing_raised(NameError, Feature9781) {break m.super_method}
+ assert_nil(m, Feature9781)
+ end
+
+ def test_super_method_unbound
+ m = Derived.instance_method(:foo)
+ m = m.super_method
+ assert_equal(Base.instance_method(:foo), m, Feature9781)
+ m = assert_nothing_raised(NameError, Feature9781) {break m.super_method}
+ assert_nil(m, Feature9781)
+ end
+
+ def test_super_method_module
+ m1 = Module.new {def foo; end}
+ c1 = Class.new(Derived) {include m1; def foo; end}
+ m = c1.instance_method(:foo)
+ assert_equal(c1, m.owner, Feature9781)
+ m = m.super_method
+ assert_equal(m1, m.owner, Feature9781)
+ m = m.super_method
+ assert_equal(Derived, m.owner, Feature9781)
+ m = m.super_method
+ assert_equal(Base, m.owner, Feature9781)
+ m2 = Module.new {def foo; end}
+ o = c1.new.extend(m2)
+ m = o.method(:foo)
+ assert_equal(m2, m.owner, Feature9781)
+ m = m.super_method
+ assert_equal(c1, m.owner, Feature9781)
+ assert_same(o, m.receiver, Feature9781)
+ end
+
+ def test_super_method_removed
+ c1 = Class.new {private def foo; end}
+ c2 = Class.new(c1) {public :foo}
+ c3 = Class.new(c2) {def foo; end}
+ c1.class_eval {undef foo}
+ m = c3.instance_method(:foo)
+ m = assert_nothing_raised(NameError, Feature9781) {break m.super_method}
+ assert_nil(m, Feature9781)
+ end
+
+ def rest_parameter(*rest)
+ rest
+ end
+
+ def test_splat_long_array
+ n = 10_000_000
+ assert_equal n , rest_parameter(*(1..n)).size, '[Feature #10440]'
+ end
+
+ def test_insecure_method
+ m = "\u{5371 967a}"
+ c = Class.new do
+ proc {$SAFE=3;def foo;end}.call
+ alias_method m, "foo"
+ eval "def bar; #{m}; end"
+ end
+ obj = c.new
+ assert_raise_with_message(SecurityError, /#{m}/) do
+ obj.bar
+ end
+ end
+
+ def test_to_proc_binding
+ bug11012 = '[ruby-core:68673] [Bug #11012]'
+ class << (obj = Object.new)
+ src = 1000.times.map {|i|"v#{i} = nil"}.join("\n")
+ eval("def foo()\n""#{src}\n""end")
+ end
+
+ b = obj.method(:foo).to_proc.binding
+ b.local_variables.each_with_index {|n, i|
+ b.local_variable_set(n, i)
+ }
+ assert_equal([998, 999], %w[v998 v999].map {|n| b.local_variable_get(n)}, bug11012)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_mixed_unicode_escapes.rb b/jni/ruby/test/ruby/test_mixed_unicode_escapes.rb
new file mode 100644
index 0000000..982b57e
--- /dev/null
+++ b/jni/ruby/test/ruby/test_mixed_unicode_escapes.rb
@@ -0,0 +1,25 @@
+# -*- coding: cp932 -*-
+# This test is in a differnt file than TestUnicodeEscapes
+# So that we can have a different coding comment above
+
+require 'test/unit'
+
+class TestMixedUnicodeEscape < Test::Unit::TestCase
+ def test_basic
+ # Unicode escapes do work in an sjis encoded file, but only
+ # if they don't contain other multi-byte chars
+ assert_equal("A", "\u0041")
+ # 8-bit character escapes are okay.
+ assert_equal("B\xFF", "\u0042\xFF")
+
+ # sjis mb chars mixed with Unicode shound not work
+ assert_raise(SyntaxError) { eval %q("\u1234")}
+ assert_raise(SyntaxError) { eval %q("\u{1234}")}
+
+ # String interpolation turns into an expression and we get
+ # a different kind of error, but we still can't mix these
+ assert_raise(Encoding::CompatibilityError) { eval %q("\u{1234}#{nil}")}
+ assert_raise(Encoding::CompatibilityError) { eval %q("#{nil}\u1234")}
+
+ end
+end
diff --git a/jni/ruby/test/ruby/test_module.rb b/jni/ruby/test/ruby/test_module.rb
new file mode 100644
index 0000000..2f3e5c2
--- /dev/null
+++ b/jni/ruby/test/ruby/test_module.rb
@@ -0,0 +1,2032 @@
+require 'test/unit'
+require 'pp'
+
+$m0 = Module.nesting
+
+class TestModule < Test::Unit::TestCase
+ def _wrap_assertion
+ yield
+ end
+
+ def assert_method_defined?(klass, mid, message="")
+ message = build_message(message, "#{klass}\##{mid} expected to be defined.")
+ _wrap_assertion do
+ klass.method_defined?(mid) or
+ raise Test::Unit::AssertionFailedError, message, caller(3)
+ end
+ end
+
+ def assert_method_not_defined?(klass, mid, message="")
+ message = build_message(message, "#{klass}\##{mid} expected to not be defined.")
+ _wrap_assertion do
+ klass.method_defined?(mid) and
+ raise Test::Unit::AssertionFailedError, message, caller(3)
+ end
+ end
+
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def test_LT_0
+ assert_equal true, String < Object
+ assert_equal false, Object < String
+ assert_nil String < Array
+ assert_equal true, Array < Enumerable
+ assert_equal false, Enumerable < Array
+ assert_nil Proc < Comparable
+ assert_nil Comparable < Proc
+ end
+
+ def test_GT_0
+ assert_equal false, String > Object
+ assert_equal true, Object > String
+ assert_nil String > Array
+ assert_equal false, Array > Enumerable
+ assert_equal true, Enumerable > Array
+ assert_nil Comparable > Proc
+ assert_nil Proc > Comparable
+ end
+
+ def test_CMP_0
+ assert_equal(-1, (String <=> Object))
+ assert_equal 1, (Object <=> String)
+ assert_nil(Array <=> String)
+ end
+
+ ExpectedException = NoMethodError
+
+ # Support stuff
+
+ module Mixin
+ MIXIN = 1
+ def mixin
+ end
+ end
+
+ module User
+ USER = 2
+ include Mixin
+ def user
+ end
+ end
+
+ module Other
+ def other
+ end
+ end
+
+ class AClass
+ def AClass.cm1
+ "cm1"
+ end
+ def AClass.cm2
+ cm1 + "cm2" + cm3
+ end
+ def AClass.cm3
+ "cm3"
+ end
+
+ private_class_method :cm1, "cm3"
+
+ def aClass
+ :aClass
+ end
+
+ def aClass1
+ :aClass1
+ end
+
+ def aClass2
+ :aClass2
+ end
+
+ private :aClass1
+ protected :aClass2
+ end
+
+ class BClass < AClass
+ def bClass1
+ :bClass1
+ end
+
+ private
+
+ def bClass2
+ :bClass2
+ end
+
+ protected
+ def bClass3
+ :bClass3
+ end
+ end
+
+ class CClass < BClass
+ def self.cClass
+ end
+ end
+
+ MyClass = AClass.clone
+ class MyClass
+ public_class_method :cm1
+ end
+
+ # -----------------------------------------------------------
+
+ def test_CMP # '<=>'
+ assert_equal( 0, Mixin <=> Mixin)
+ assert_equal(-1, User <=> Mixin)
+ assert_equal( 1, Mixin <=> User)
+
+ assert_equal( 0, Object <=> Object)
+ assert_equal(-1, String <=> Object)
+ assert_equal( 1, Object <=> String)
+ end
+
+ def test_GE # '>='
+ assert_operator(Mixin, :>=, User)
+ assert_operator(Mixin, :>=, Mixin)
+ assert_not_operator(User, :>=, Mixin)
+
+ assert_operator(Object, :>=, String)
+ assert_operator(String, :>=, String)
+ assert_not_operator(String, :>=, Object)
+ end
+
+ def test_GT # '>'
+ assert_operator(Mixin, :>, User)
+ assert_not_operator(Mixin, :>, Mixin)
+ assert_not_operator(User, :>, Mixin)
+
+ assert_operator(Object, :>, String)
+ assert_not_operator(String, :>, String)
+ assert_not_operator(String, :>, Object)
+ end
+
+ def test_LE # '<='
+ assert_operator(User, :<=, Mixin)
+ assert_operator(Mixin, :<=, Mixin)
+ assert_not_operator(Mixin, :<=, User)
+
+ assert_operator(String, :<=, Object)
+ assert_operator(String, :<=, String)
+ assert_not_operator(Object, :<=, String)
+ end
+
+ def test_LT # '<'
+ assert_operator(User, :<, Mixin)
+ assert_not_operator(Mixin, :<, Mixin)
+ assert_not_operator(Mixin, :<, User)
+
+ assert_operator(String, :<, Object)
+ assert_not_operator(String, :<, String)
+ assert_not_operator(Object, :<, String)
+ end
+
+ def test_VERY_EQUAL # '==='
+ assert_operator(Object, :===, self)
+ assert_operator(Test::Unit::TestCase, :===, self)
+ assert_operator(TestModule, :===, self)
+ assert_not_operator(String, :===, self)
+ end
+
+ def test_ancestors
+ assert_equal([User, Mixin], User.ancestors)
+ assert_equal([Mixin], Mixin.ancestors)
+
+ ancestors = Object.ancestors
+ mixins = ancestors - [Object, Kernel, BasicObject]
+ mixins << JSON::Ext::Generator::GeneratorMethods::String if defined?(JSON::Ext::Generator::GeneratorMethods::String)
+ assert_equal([Object, Kernel, BasicObject], ancestors - mixins)
+ assert_equal([String, Comparable, Object, Kernel, BasicObject], String.ancestors - mixins)
+ end
+
+ CLASS_EVAL = 2
+ @@class_eval = 'b'
+
+ def test_class_eval
+ Other.class_eval("CLASS_EVAL = 1")
+ assert_equal(1, Other::CLASS_EVAL)
+ assert_include(Other.constants, :CLASS_EVAL)
+ assert_equal(2, Other.class_eval { CLASS_EVAL })
+
+ Other.class_eval("@@class_eval = 'a'")
+ assert_equal('a', Other.class_variable_get(:@@class_eval))
+ assert_equal('b', Other.class_eval { @@class_eval })
+
+ Other.class_eval do
+ module_function
+
+ def class_eval_test
+ "foo"
+ end
+ end
+ assert_equal("foo", Other.class_eval_test)
+
+ assert_equal([Other], Other.class_eval { |*args| args })
+ end
+
+ def test_const_defined?
+ assert_operator(Math, :const_defined?, :PI)
+ assert_operator(Math, :const_defined?, "PI")
+ assert_not_operator(Math, :const_defined?, :IP)
+ assert_not_operator(Math, :const_defined?, "IP")
+ end
+
+ def each_bad_constants(m, &b)
+ [
+ "#<Class:0x7b8b718b>",
+ ":Object",
+ "",
+ ":",
+ ["String::", "[Bug #7573]"],
+ "\u3042",
+ "Name?",
+ ].each do |name, msg|
+ expected = "wrong constant name %s" % quote(name)
+ msg = "#{msg}#{': ' if msg}wrong constant name #{name.dump}"
+ assert_raise_with_message(NameError, expected, "#{msg} to #{m}") do
+ yield name
+ end
+ end
+ end
+
+ def test_bad_constants_get
+ each_bad_constants("get") {|name|
+ Object.const_get name
+ }
+ end
+
+ def test_bad_constants_defined
+ each_bad_constants("defined?") {|name|
+ Object.const_defined? name
+ }
+ end
+
+ def test_leading_colons
+ assert_equal Object, AClass.const_get('::Object')
+ end
+
+ def test_const_get
+ assert_equal(Math::PI, Math.const_get("PI"))
+ assert_equal(Math::PI, Math.const_get(:PI))
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "PI"; end
+ def n.count; @count; end
+ assert_equal(Math::PI, Math.const_get(n))
+ assert_equal(1, n.count)
+ end
+
+ def test_nested_get
+ assert_equal Other, Object.const_get([self.class, Other].join('::'))
+ assert_equal User::USER, self.class.const_get([User, 'USER'].join('::'))
+ end
+
+ def test_nested_get_symbol
+ const = [self.class, Other].join('::').to_sym
+ assert_raise(NameError) {Object.const_get(const)}
+
+ const = [User, 'USER'].join('::').to_sym
+ assert_raise(NameError) {self.class.const_get(const)}
+ end
+
+ def test_nested_get_const_missing
+ classes = []
+ klass = Class.new {
+ define_singleton_method(:const_missing) { |name|
+ classes << name
+ klass
+ }
+ }
+ klass.const_get("Foo::Bar::Baz")
+ assert_equal [:Foo, :Bar, :Baz], classes
+ end
+
+ def test_nested_get_bad_class
+ assert_raise(TypeError) do
+ self.class.const_get([User, 'USER', 'Foo'].join('::'))
+ end
+ end
+
+ def test_nested_defined
+ assert_send([Object, :const_defined?, [self.class.name, 'Other'].join('::')])
+ assert_send([self.class, :const_defined?, 'User::USER'])
+ assert_not_send([self.class, :const_defined?, 'User::Foo'])
+ end
+
+ def test_nested_defined_symbol
+ const = [self.class, Other].join('::').to_sym
+ assert_raise(NameError) {Object.const_defined?(const)}
+
+ const = [User, 'USER'].join('::').to_sym
+ assert_raise(NameError) {self.class.const_defined?(const)}
+ end
+
+ def test_nested_defined_bad_class
+ assert_raise(TypeError) do
+ self.class.const_defined?('User::USER::Foo')
+ end
+ end
+
+ def test_const_set
+ assert_not_operator(Other, :const_defined?, :KOALA)
+ Other.const_set(:KOALA, 99)
+ assert_operator(Other, :const_defined?, :KOALA)
+ assert_equal(99, Other::KOALA)
+ Other.const_set("WOMBAT", "Hi")
+ assert_equal("Hi", Other::WOMBAT)
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "HOGE"; end
+ def n.count; @count; end
+ def n.count=(v); @count=v; end
+ assert_not_operator(Other, :const_defined?, :HOGE)
+ Other.const_set(n, 999)
+ assert_equal(1, n.count)
+ n.count = 0
+ assert_equal(999, Other.const_get(n))
+ assert_equal(1, n.count)
+ n.count = 0
+ assert_equal(true, Other.const_defined?(n))
+ assert_equal(1, n.count)
+ end
+
+ def test_constants
+ assert_equal([:MIXIN], Mixin.constants)
+ assert_equal([:MIXIN, :USER], User.constants.sort)
+ end
+
+ def test_self_initialize_copy
+ bug9535 = '[ruby-dev:47989] [Bug #9535]'
+ m = Module.new do
+ def foo
+ :ok
+ end
+ initialize_copy(self)
+ end
+ assert_equal(:ok, Object.new.extend(m).foo, bug9535)
+ end
+
+ def test_initialize_copy_empty
+ bug9813 = '[ruby-dev:48182] [Bug #9813]'
+ m = Module.new do
+ def x
+ end
+ const_set(:X, 1)
+ @x = 2
+ end
+ assert_equal([:x], m.instance_methods)
+ assert_equal([:@x], m.instance_variables)
+ assert_equal([:X], m.constants)
+ m.module_eval do
+ initialize_copy(Module.new)
+ end
+ assert_empty(m.instance_methods, bug9813)
+ assert_empty(m.instance_variables, bug9813)
+ assert_empty(m.constants, bug9813)
+ end
+
+ def test_dup
+ bug6454 = '[ruby-core:45132]'
+
+ a = Module.new
+ Other.const_set :BUG6454, a
+ b = a.dup
+ Other.const_set :BUG6454_dup, b
+
+ assert_equal "TestModule::Other::BUG6454_dup", b.inspect, bug6454
+ end
+
+ def test_dup_anonymous
+ bug6454 = '[ruby-core:45132]'
+
+ a = Module.new
+ original = a.inspect
+
+ b = a.dup
+
+ assert_not_equal original, b.inspect, bug6454
+ end
+
+ def test_public_include
+ assert_nothing_raised('#8846') do
+ Module.new.include(Module.new { def foo; end }).instance_methods == [:foo]
+ end
+ end
+
+ def test_include_toplevel
+ assert_separately([], <<-EOS)
+ Mod = Module.new {def foo; :include_foo end}
+ TOPLEVEL_BINDING.eval('include Mod')
+
+ assert_equal(:include_foo, TOPLEVEL_BINDING.eval('foo'))
+ assert_equal([Object, Mod], Object.ancestors.slice(0, 2))
+ EOS
+ end
+
+ def test_included_modules
+ assert_equal([], Mixin.included_modules)
+ assert_equal([Mixin], User.included_modules)
+
+ mixins = Object.included_modules - [Kernel]
+ mixins << JSON::Ext::Generator::GeneratorMethods::String if defined?(JSON::Ext::Generator::GeneratorMethods::String)
+ assert_equal([Kernel], Object.included_modules - mixins)
+ assert_equal([Comparable, Kernel], String.included_modules - mixins)
+ end
+
+ def test_instance_methods
+ assert_equal([:user], User.instance_methods(false))
+ assert_equal([:user, :mixin].sort, User.instance_methods(true).sort)
+ assert_equal([:mixin], Mixin.instance_methods)
+ assert_equal([:mixin], Mixin.instance_methods(true))
+ assert_equal([:cClass], (class << CClass; self; end).instance_methods(false))
+ assert_equal([], (class << BClass; self; end).instance_methods(false))
+ assert_equal([:cm2], (class << AClass; self; end).instance_methods(false))
+ # Ruby 1.8 feature change:
+ # #instance_methods includes protected methods.
+ #assert_equal([:aClass], AClass.instance_methods(false))
+ assert_equal([:aClass, :aClass2], AClass.instance_methods(false).sort)
+ assert_equal([:aClass, :aClass2],
+ (AClass.instance_methods(true) - Object.instance_methods(true)).sort)
+ end
+
+ def test_method_defined?
+ assert_method_not_defined?(User, :wombat)
+ assert_method_defined?(User, :user)
+ assert_method_defined?(User, :mixin)
+ assert_method_not_defined?(User, :wombat)
+ assert_method_defined?(User, :user)
+ assert_method_defined?(User, :mixin)
+ end
+
+ def module_exec_aux
+ Proc.new do
+ def dynamically_added_method_3; end
+ end
+ end
+ def module_exec_aux_2(&block)
+ User.module_exec(&block)
+ end
+
+ def test_module_exec
+ User.module_exec do
+ def dynamically_added_method_1; end
+ end
+ assert_method_defined?(User, :dynamically_added_method_1)
+
+ block = Proc.new do
+ def dynamically_added_method_2; end
+ end
+ User.module_exec(&block)
+ assert_method_defined?(User, :dynamically_added_method_2)
+
+ User.module_exec(&module_exec_aux)
+ assert_method_defined?(User, :dynamically_added_method_3)
+
+ module_exec_aux_2 do
+ def dynamically_added_method_4; end
+ end
+ assert_method_defined?(User, :dynamically_added_method_4)
+ end
+
+ def test_module_eval
+ User.module_eval("MODULE_EVAL = 1")
+ assert_equal(1, User::MODULE_EVAL)
+ assert_include(User.constants, :MODULE_EVAL)
+ User.instance_eval("remove_const(:MODULE_EVAL)")
+ assert_not_include(User.constants, :MODULE_EVAL)
+ end
+
+ def test_name
+ assert_equal("Fixnum", Fixnum.name)
+ assert_equal("TestModule::Mixin", Mixin.name)
+ assert_equal("TestModule::User", User.name)
+ end
+
+ def test_classpath
+ m = Module.new
+ n = Module.new
+ m.const_set(:N, n)
+ assert_nil(m.name)
+ assert_nil(n.name)
+ assert_equal([:N], m.constants)
+ m.module_eval("module O end")
+ assert_equal([:N, :O], m.constants)
+ m.module_eval("class C; end")
+ assert_equal([:N, :O, :C], m.constants)
+ assert_nil(m::N.name)
+ assert_match(/\A#<Module:.*>::O\z/, m::O.name)
+ assert_match(/\A#<Module:.*>::C\z/, m::C.name)
+ self.class.const_set(:M, m)
+ prefix = self.class.name + "::M::"
+ assert_equal(prefix+"N", m.const_get(:N).name)
+ assert_equal(prefix+"O", m.const_get(:O).name)
+ assert_equal(prefix+"C", m.const_get(:C).name)
+ end
+
+ def test_private_class_method
+ assert_raise(ExpectedException) { AClass.cm1 }
+ assert_raise(ExpectedException) { AClass.cm3 }
+ assert_equal("cm1cm2cm3", AClass.cm2)
+ end
+
+ def test_private_instance_methods
+ assert_equal([:aClass1], AClass.private_instance_methods(false))
+ assert_equal([:bClass2], BClass.private_instance_methods(false))
+ assert_equal([:aClass1, :bClass2],
+ (BClass.private_instance_methods(true) -
+ Object.private_instance_methods(true)).sort)
+ end
+
+ def test_protected_instance_methods
+ assert_equal([:aClass2], AClass.protected_instance_methods)
+ assert_equal([:bClass3], BClass.protected_instance_methods(false))
+ assert_equal([:bClass3, :aClass2].sort,
+ (BClass.protected_instance_methods(true) -
+ Object.protected_instance_methods(true)).sort)
+ end
+
+ def test_public_class_method
+ assert_equal("cm1", MyClass.cm1)
+ assert_equal("cm1cm2cm3", MyClass.cm2)
+ assert_raise(ExpectedException) { eval "MyClass.cm3" }
+ end
+
+ def test_public_instance_methods
+ assert_equal([:aClass], AClass.public_instance_methods(false))
+ assert_equal([:bClass1], BClass.public_instance_methods(false))
+ end
+
+ def test_s_constants
+ c1 = Module.constants
+ Object.module_eval "WALTER = 99"
+ c2 = Module.constants
+ assert_equal([:WALTER], c2 - c1)
+
+ assert_equal([], Module.constants(true))
+ assert_equal([], Module.constants(false))
+
+ src = <<-INPUT
+ ary = Module.constants
+ module M
+ WALTER = 99
+ end
+ class Module
+ include M
+ end
+ p Module.constants - ary, Module.constants(true), Module.constants(false)
+ INPUT
+ assert_in_out_err([], src, %w([:M] [:WALTER] []), [])
+
+ klass = Class.new do
+ const_set(:X, 123)
+ end
+ assert_equal(false, klass.class_eval { Module.constants }.include?(:X))
+ end
+
+ module M1
+ $m1 = Module.nesting
+ module M2
+ $m2 = Module.nesting
+ end
+ end
+
+ def test_s_nesting
+ assert_equal([], $m0)
+ assert_equal([TestModule::M1, TestModule], $m1)
+ assert_equal([TestModule::M1::M2,
+ TestModule::M1, TestModule], $m2)
+ end
+
+ def test_s_new
+ m = Module.new
+ assert_instance_of(Module, m)
+ end
+
+ def test_freeze
+ m = Module.new
+ m.freeze
+ assert_raise(RuntimeError) do
+ m.module_eval do
+ def foo; end
+ end
+ end
+ end
+
+ def test_attr_obsoleted_flag
+ c = Class.new
+ c.class_eval do
+ def initialize
+ @foo = :foo
+ @bar = :bar
+ end
+ attr :foo, true
+ attr :bar, false
+ end
+ o = c.new
+ assert_equal(true, o.respond_to?(:foo))
+ assert_equal(true, o.respond_to?(:foo=))
+ assert_equal(true, o.respond_to?(:bar))
+ assert_equal(false, o.respond_to?(:bar=))
+ end
+
+ def test_const_get_evaled
+ c1 = Class.new
+ c2 = Class.new(c1)
+
+ eval("c1::Foo = :foo")
+ assert_equal(:foo, c1::Foo)
+ assert_equal(:foo, c2::Foo)
+ assert_equal(:foo, c2.const_get(:Foo))
+ assert_raise(NameError) { c2.const_get(:Foo, false) }
+
+ eval("c1::Foo = :foo")
+ assert_raise(NameError) { c1::Bar }
+ assert_raise(NameError) { c2::Bar }
+ assert_raise(NameError) { c2.const_get(:Bar) }
+ assert_raise(NameError) { c2.const_get(:Bar, false) }
+ assert_raise(NameError) { c2.const_get("Bar", false) }
+ assert_raise(NameError) { c2.const_get("BaR11", false) }
+ assert_raise(NameError) { Object.const_get("BaR11", false) }
+
+ c1.instance_eval do
+ def const_missing(x)
+ x
+ end
+ end
+
+ assert_equal(:Bar, c1::Bar)
+ assert_equal(:Bar, c2::Bar)
+ assert_equal(:Bar, c2.const_get(:Bar))
+ assert_equal(:Bar, c2.const_get(:Bar, false))
+ assert_equal(:Bar, c2.const_get("Bar"))
+ assert_equal(:Bar, c2.const_get("Bar", false))
+
+ v = c2.const_get("Bar11", false)
+ assert_equal("Bar11".to_sym, v)
+
+ assert_raise(NameError) { c1.const_get(:foo) }
+ end
+
+ def test_const_set_invalid_name
+ c1 = Class.new
+ assert_raise_with_message(NameError, /foo/) { c1.const_set(:foo, :foo) }
+ assert_raise_with_message(NameError, /bar/) { c1.const_set("bar", :foo) }
+ assert_raise_with_message(TypeError, /1/) { c1.const_set(1, :foo) }
+ assert_nothing_raised(NameError) { c1.const_set("X\u{3042}", :foo) }
+ assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-16be"), :foo) }
+ assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-16le"), :foo) }
+ assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32be"), :foo) }
+ assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32le"), :foo) }
+ cx = EnvUtil.labeled_class("X\u{3042}")
+ EnvUtil.with_default_internal(Encoding::UTF_8) {
+ assert_raise_with_message(TypeError, /X\u{3042}/) { c1.const_set(cx, :foo) }
+ }
+ end
+
+ def test_const_get_invalid_name
+ c1 = Class.new
+ assert_raise(NameError) { c1.const_get(:foo) }
+ bug5084 = '[ruby-dev:44200]'
+ assert_raise(TypeError, bug5084) { c1.const_get(1) }
+ bug7574 = '[ruby-dev:46749]'
+ assert_raise_with_message(NameError, "wrong constant name \"String\\u0000\"", bug7574) {
+ Object.const_get("String\0")
+ }
+ end
+
+ def test_const_defined_invalid_name
+ c1 = Class.new
+ assert_raise(NameError) { c1.const_defined?(:foo) }
+ bug5084 = '[ruby-dev:44200]'
+ assert_raise(TypeError, bug5084) { c1.const_defined?(1) }
+ bug7574 = '[ruby-dev:46749]'
+ assert_raise_with_message(NameError, "wrong constant name \"String\\u0000\"", bug7574) {
+ Object.const_defined?("String\0")
+ }
+ end
+
+ def test_const_get_no_inherited
+ bug3422 = '[ruby-core:30719]'
+ assert_in_out_err([], <<-INPUT, %w[1 NameError A], [], bug3422)
+ BasicObject::A = 1
+ puts [true, false].map {|inh|
+ begin
+ Object.const_get(:A, inh)
+ rescue NameError => e
+ [e.class, e.name]
+ end
+ }
+ INPUT
+ end
+
+ def test_const_get_inherited
+ bug3423 = '[ruby-core:30720]'
+ assert_in_out_err([], <<-INPUT, %w[NameError A NameError A], [], bug3423)
+ module Foo; A = 1; end
+ class Object; include Foo; end
+ class Bar; include Foo; end
+
+ puts [Object, Bar].map {|klass|
+ begin
+ klass.const_get(:A, false)
+ rescue NameError => e
+ [e.class, e.name]
+ end
+ }
+ INPUT
+ end
+
+ def test_const_in_module
+ bug3423 = '[ruby-core:37698]'
+ assert_in_out_err([], <<-INPUT, %w[ok], [], bug3423)
+ module LangModuleSpecInObject
+ module LangModuleTop
+ end
+ end
+ include LangModuleSpecInObject
+ module LangModuleTop
+ end
+ puts "ok" if LangModuleSpecInObject::LangModuleTop == LangModuleTop
+ INPUT
+
+ bug5264 = '[ruby-core:39227]'
+ assert_in_out_err([], <<-'INPUT', [], [], bug5264)
+ class A
+ class X; end
+ end
+ class B < A
+ module X; end
+ end
+ INPUT
+ end
+
+ def test_class_variable_get
+ c = Class.new
+ c.class_eval('@@foo = :foo')
+ assert_equal(:foo, c.class_variable_get(:@@foo))
+ assert_raise(NameError) { c.class_variable_get(:@@bar) } # c.f. instance_variable_get
+ assert_raise(NameError) { c.class_variable_get(:'@@') }
+ assert_raise(NameError) { c.class_variable_get('@@') }
+ assert_raise(NameError) { c.class_variable_get(:foo) }
+ assert_raise(NameError) { c.class_variable_get("bar") }
+ assert_raise(TypeError) { c.class_variable_get(1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@@foo"; end
+ def n.count; @count; end
+ assert_equal(:foo, c.class_variable_get(n))
+ assert_equal(1, n.count)
+ end
+
+ def test_class_variable_set
+ c = Class.new
+ c.class_variable_set(:@@foo, :foo)
+ assert_equal(:foo, c.class_eval('@@foo'))
+ assert_raise(NameError) { c.class_variable_set(:'@@', 1) }
+ assert_raise(NameError) { c.class_variable_set('@@', 1) }
+ assert_raise(NameError) { c.class_variable_set(:foo, 1) }
+ assert_raise(NameError) { c.class_variable_set("bar", 1) }
+ assert_raise(TypeError) { c.class_variable_set(1, 1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@@foo"; end
+ def n.count; @count; end
+ c.class_variable_set(n, :bar)
+ assert_equal(:bar, c.class_eval('@@foo'))
+ assert_equal(1, n.count)
+ end
+
+ def test_class_variable_defined
+ c = Class.new
+ c.class_eval('@@foo = :foo')
+ assert_equal(true, c.class_variable_defined?(:@@foo))
+ assert_equal(false, c.class_variable_defined?(:@@bar))
+ assert_raise(NameError) { c.class_variable_defined?(:'@@') }
+ assert_raise(NameError) { c.class_variable_defined?('@@') }
+ assert_raise(NameError) { c.class_variable_defined?(:foo) }
+ assert_raise(NameError) { c.class_variable_defined?("bar") }
+ assert_raise(TypeError) { c.class_variable_defined?(1) }
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@@foo"; end
+ def n.count; @count; end
+ assert_equal(true, c.class_variable_defined?(n))
+ assert_equal(1, n.count)
+ end
+
+ def test_remove_class_variable
+ c = Class.new
+ c.class_eval('@@foo = :foo')
+ c.class_eval { remove_class_variable(:@@foo) }
+ assert_equal(false, c.class_variable_defined?(:@@foo))
+ end
+
+ def test_export_method
+ m = Module.new
+ assert_raise(NameError) do
+ m.instance_eval { public(:foo) }
+ end
+ end
+
+ def test_attr
+ assert_in_out_err([], <<-INPUT, %w(:ok nil), /warning: private attribute\?$/)
+ $VERBOSE = true
+ c = Class.new
+ c.instance_eval do
+ private
+ attr_reader :foo
+ end
+ o = c.new
+ o.foo rescue p(:ok)
+ p(o.instance_eval { foo })
+ INPUT
+
+ c = Class.new
+ assert_raise(NameError) do
+ c.instance_eval { attr_reader :"." }
+ end
+ end
+
+ def test_undef
+ c = Class.new
+ assert_raise(NameError) do
+ c.instance_eval { undef_method(:foo) }
+ end
+
+ m = Module.new
+ assert_raise(NameError) do
+ m.instance_eval { undef_method(:foo) }
+ end
+
+ o = Object.new
+ assert_raise(NameError) do
+ class << o; self; end.instance_eval { undef_method(:foo) }
+ end
+
+ %w(object_id __send__ initialize).each do |n|
+ assert_in_out_err([], <<-INPUT, [], %r"warning: undefining `#{n}' may cause serious problems$")
+ $VERBOSE = false
+ Class.new.instance_eval { undef_method(:#{n}) }
+ INPUT
+ end
+ end
+
+ def test_alias
+ m = Module.new
+ assert_raise(NameError) do
+ m.class_eval { alias foo bar }
+ end
+
+ assert_in_out_err([], <<-INPUT, %w(2), /discarding old foo$/)
+ $VERBOSE = true
+ c = Class.new
+ c.class_eval do
+ def foo; 1; end
+ def bar; 2; end
+ end
+ c.class_eval { alias foo bar }
+ p c.new.foo
+ INPUT
+ end
+
+ def test_mod_constants
+ m = Module.new
+ m.const_set(:Foo, :foo)
+ assert_equal([:Foo], m.constants(true))
+ assert_equal([:Foo], m.constants(false))
+ m.instance_eval { remove_const(:Foo) }
+ end
+
+ class Bug9413
+ class << self
+ Foo = :foo
+ end
+ end
+
+ def test_singleton_constants
+ bug9413 = '[ruby-core:59763] [Bug #9413]'
+ c = Bug9413.singleton_class
+ assert_include(c.constants(true), :Foo, bug9413)
+ assert_include(c.constants(false), :Foo, bug9413)
+ end
+
+ def test_frozen_module
+ m = Module.new
+ m.freeze
+ assert_raise(RuntimeError) do
+ m.instance_eval { undef_method(:foo) }
+ end
+ end
+
+ def test_frozen_class
+ c = Class.new
+ c.freeze
+ assert_raise(RuntimeError) do
+ c.instance_eval { undef_method(:foo) }
+ end
+ end
+
+ def test_frozen_singleton_class
+ klass = Class.new
+ o = klass.new
+ c = class << o; self; end
+ c.freeze
+ assert_raise_with_message(RuntimeError, /frozen/) do
+ c.instance_eval { undef_method(:foo) }
+ end
+ klass.class_eval do
+ def self.foo
+ end
+ end
+ end
+
+ def test_method_defined
+ c = Class.new
+ c.class_eval do
+ def foo; end
+ def bar; end
+ def baz; end
+ public :foo
+ protected :bar
+ private :baz
+ end
+
+ assert_equal(true, c.public_method_defined?(:foo))
+ assert_equal(false, c.public_method_defined?(:bar))
+ assert_equal(false, c.public_method_defined?(:baz))
+
+ assert_equal(false, c.protected_method_defined?(:foo))
+ assert_equal(true, c.protected_method_defined?(:bar))
+ assert_equal(false, c.protected_method_defined?(:baz))
+
+ assert_equal(false, c.private_method_defined?(:foo))
+ assert_equal(false, c.private_method_defined?(:bar))
+ assert_equal(true, c.private_method_defined?(:baz))
+ end
+
+ def test_top_public_private
+ assert_in_out_err([], <<-INPUT, %w([:foo] [:bar]), [])
+ private
+ def foo; :foo; end
+ public
+ def bar; :bar; end
+ p self.private_methods.grep(/^foo$|^bar$/)
+ p self.methods.grep(/^foo$|^bar$/)
+ INPUT
+ end
+
+ def test_append_features
+ t = nil
+ m = Module.new
+ m.module_eval do
+ def foo; :foo; end
+ end
+ class << m; self; end.class_eval do
+ define_method(:append_features) do |mod|
+ t = mod
+ super(mod)
+ end
+ end
+
+ m2 = Module.new
+ m2.module_eval { include(m) }
+ assert_equal(m2, t)
+
+ o = Object.new
+ o.extend(m2)
+ assert_equal(true, o.respond_to?(:foo))
+ end
+
+ def test_append_features_raise
+ m = Module.new
+ m.module_eval do
+ def foo; :foo; end
+ end
+ class << m; self; end.class_eval do
+ define_method(:append_features) {|mod| raise }
+ end
+
+ m2 = Module.new
+ assert_raise(RuntimeError) do
+ m2.module_eval { include(m) }
+ end
+
+ o = Object.new
+ o.extend(m2)
+ assert_equal(false, o.respond_to?(:foo))
+ end
+
+ def test_append_features_type_error
+ assert_raise(TypeError) do
+ Module.new.instance_eval { append_features(1) }
+ end
+ end
+
+ def test_included
+ m = Module.new
+ m.module_eval do
+ def foo; :foo; end
+ end
+ class << m; self; end.class_eval do
+ define_method(:included) {|mod| raise }
+ end
+
+ m2 = Module.new
+ assert_raise(RuntimeError) do
+ m2.module_eval { include(m) }
+ end
+
+ o = Object.new
+ o.extend(m2)
+ assert_equal(true, o.respond_to?(:foo))
+ end
+
+ def test_cyclic_include
+ m1 = Module.new
+ m2 = Module.new
+ m1.instance_eval { include(m2) }
+ assert_raise(ArgumentError) do
+ m2.instance_eval { include(m1) }
+ end
+ end
+
+ def test_include_p
+ m = Module.new
+ c1 = Class.new
+ c1.instance_eval { include(m) }
+ c2 = Class.new(c1)
+ assert_equal(true, c1.include?(m))
+ assert_equal(true, c2.include?(m))
+ assert_equal(false, m.include?(m))
+ end
+
+ def test_send
+ a = AClass.new
+ assert_equal(:aClass, a.__send__(:aClass))
+ assert_equal(:aClass1, a.__send__(:aClass1))
+ assert_equal(:aClass2, a.__send__(:aClass2))
+ b = BClass.new
+ assert_equal(:aClass, b.__send__(:aClass))
+ assert_equal(:aClass1, b.__send__(:aClass1))
+ assert_equal(:aClass2, b.__send__(:aClass2))
+ assert_equal(:bClass1, b.__send__(:bClass1))
+ assert_equal(:bClass2, b.__send__(:bClass2))
+ assert_equal(:bClass3, b.__send__(:bClass3))
+ end
+
+
+ def test_nonascii_name
+ c = eval("class ::C\u{df}; self; end")
+ assert_equal("C\u{df}", c.name, '[ruby-core:24600]')
+ c = eval("class C\u{df}; self; end")
+ assert_equal("TestModule::C\u{df}", c.name, '[ruby-core:24600]')
+ end
+
+ def test_method_added
+ memo = []
+ mod = Module.new do
+ mod = self
+ (class << self ; self ; end).class_eval do
+ define_method :method_added do |sym|
+ memo << sym
+ memo << mod.instance_methods(false)
+ memo << (mod.instance_method(sym) rescue nil)
+ end
+ end
+ def f
+ end
+ alias g f
+ attr_reader :a
+ attr_writer :a
+ end
+ assert_equal :f, memo.shift
+ assert_equal [:f], memo.shift, '[ruby-core:25536]'
+ assert_equal mod.instance_method(:f), memo.shift
+ assert_equal :g, memo.shift
+ assert_equal [:f, :g], memo.shift
+ assert_equal mod.instance_method(:f), memo.shift
+ assert_equal :a, memo.shift
+ assert_equal [:f, :g, :a], memo.shift
+ assert_equal mod.instance_method(:a), memo.shift
+ assert_equal :a=, memo.shift
+ assert_equal [:f, :g, :a, :a=], memo.shift
+ assert_equal mod.instance_method(:a=), memo.shift
+ end
+
+ def test_method_undefined
+ added = []
+ undefed = []
+ removed = []
+ mod = Module.new do
+ mod = self
+ def f
+ end
+ (class << self ; self ; end).class_eval do
+ define_method :method_added do |sym|
+ added << sym
+ end
+ define_method :method_undefined do |sym|
+ undefed << sym
+ end
+ define_method :method_removed do |sym|
+ removed << sym
+ end
+ end
+ end
+ assert_method_defined?(mod, :f)
+ mod.module_eval do
+ undef :f
+ end
+ assert_equal [], added
+ assert_equal [:f], undefed
+ assert_equal [], removed
+ end
+
+ def test_method_removed
+ added = []
+ undefed = []
+ removed = []
+ mod = Module.new do
+ mod = self
+ def f
+ end
+ (class << self ; self ; end).class_eval do
+ define_method :method_added do |sym|
+ added << sym
+ end
+ define_method :method_undefined do |sym|
+ undefed << sym
+ end
+ define_method :method_removed do |sym|
+ removed << sym
+ end
+ end
+ end
+ assert_method_defined?(mod, :f)
+ mod.module_eval do
+ remove_method :f
+ end
+ assert_equal [], added
+ assert_equal [], undefed
+ assert_equal [:f], removed
+ end
+
+ def test_method_redefinition
+ feature2155 = '[ruby-dev:39400]'
+
+ line = __LINE__+4
+ stderr = EnvUtil.verbose_warning do
+ Module.new do
+ def foo; end
+ def foo; end
+ end
+ end
+ assert_match(/:#{line}: warning: method redefined; discarding old foo/, stderr)
+ assert_match(/:#{line-1}: warning: previous definition of foo/, stderr, feature2155)
+
+ assert_warning '' do
+ Module.new do
+ def foo; end
+ alias bar foo
+ def foo; end
+ end
+ end
+
+ assert_warning '' do
+ Module.new do
+ def foo; end
+ alias bar foo
+ alias bar foo
+ end
+ end
+
+ line = __LINE__+4
+ stderr = EnvUtil.verbose_warning do
+ Module.new do
+ define_method(:foo) do end
+ def foo; end
+ end
+ end
+ assert_match(/:#{line}: warning: method redefined; discarding old foo/, stderr)
+ assert_match(/:#{line-1}: warning: previous definition of foo/, stderr, feature2155)
+
+ assert_warning '' do
+ Module.new do
+ define_method(:foo) do end
+ alias bar foo
+ alias bar foo
+ end
+ end
+
+ assert_warning('', '[ruby-dev:39397]') do
+ Module.new do
+ module_function
+ def foo; end
+ module_function :foo
+ end
+ end
+
+ assert_warning '' do
+ Module.new do
+ def foo; end
+ undef foo
+ end
+ end
+ end
+
+ def test_protected_singleton_method
+ klass = Class.new
+ x = klass.new
+ class << x
+ protected
+
+ def foo
+ end
+ end
+ assert_raise(NoMethodError) do
+ x.foo
+ end
+ klass.send(:define_method, :bar) do
+ x.foo
+ end
+ assert_nothing_raised do
+ x.bar
+ end
+ y = klass.new
+ assert_raise(NoMethodError) do
+ y.bar
+ end
+ end
+
+ def test_uninitialized_toplevel_constant
+ bug3123 = '[ruby-dev:40951]'
+ e = assert_raise(NameError) {eval("Bug3123", TOPLEVEL_BINDING)}
+ assert_not_match(/Object::/, e.message, bug3123)
+ end
+
+ def test_attr_inherited_visibility
+ bug3406 = '[ruby-core:30638]'
+ c = Class.new do
+ class << self
+ private
+ def attr_accessor(*); super; end
+ end
+ attr_accessor :x
+ end.new
+ assert_nothing_raised(bug3406) {c.x = 1}
+ assert_equal(1, c.x, bug3406)
+ end
+
+ def test_attr_writer_with_no_arguments
+ bug8540 = "[ruby-core:55543]"
+ c = Class.new do
+ attr_writer :foo
+ end
+ assert_raise(ArgumentError, bug8540) { c.new.send :foo= }
+ end
+
+ def test_private_constant
+ c = Class.new
+ c.const_set(:FOO, "foo")
+ assert_equal("foo", c::FOO)
+ c.private_constant(:FOO)
+ assert_raise(NameError) { c::FOO }
+ assert_equal("foo", c.class_eval("FOO"))
+ assert_equal("foo", c.const_get("FOO"))
+ $VERBOSE, verbose = nil, $VERBOSE
+ c.const_set(:FOO, "foo")
+ $VERBOSE = verbose
+ assert_raise(NameError) { c::FOO }
+ end
+
+ def test_private_constant2
+ c = Class.new
+ c.const_set(:FOO, "foo")
+ c.const_set(:BAR, "bar")
+ assert_equal("foo", c::FOO)
+ assert_equal("bar", c::BAR)
+ c.private_constant(:FOO, :BAR)
+ assert_raise(NameError) { c::FOO }
+ assert_raise(NameError) { c::BAR }
+ assert_equal("foo", c.class_eval("FOO"))
+ assert_equal("bar", c.class_eval("BAR"))
+ end
+
+ def test_private_constant_with_no_args
+ assert_in_out_err([], <<-RUBY, [], ["-:3: warning: private_constant with no argument is just ignored"])
+ $-w = true
+ class X
+ private_constant
+ end
+ RUBY
+ end
+
+ class PrivateClass
+ end
+ private_constant :PrivateClass
+
+ def test_define_module_under_private_constant
+ assert_raise(NameError) do
+ eval %q{class TestModule::PrivateClass; end}
+ end
+ assert_raise(NameError) do
+ eval %q{module TestModule::PrivateClass::TestModule; end}
+ end
+ eval %q{class PrivateClass; end}
+ eval %q{module PrivateClass::TestModule; end}
+ assert_instance_of(Module, PrivateClass::TestModule)
+ PrivateClass.class_eval { remove_const(:TestModule) }
+ end
+
+ def test_public_constant
+ c = Class.new
+ c.const_set(:FOO, "foo")
+ assert_equal("foo", c::FOO)
+ c.private_constant(:FOO)
+ assert_raise(NameError) { c::FOO }
+ assert_equal("foo", c.class_eval("FOO"))
+ c.public_constant(:FOO)
+ assert_equal("foo", c::FOO)
+ end
+
+ def test_constants_with_private_constant
+ assert_not_include(::TestModule.constants, :PrivateClass)
+ end
+
+ def test_toplevel_private_constant
+ src = <<-INPUT
+ class Object
+ private_constant :Object
+ end
+ p Object
+ begin
+ p ::Object
+ rescue
+ p :ok
+ end
+ INPUT
+ assert_in_out_err([], src, %w(Object :ok), [])
+ end
+
+ def test_private_constants_clear_inlinecache
+ bug5702 = '[ruby-dev:44929]'
+ src = <<-INPUT
+ class A
+ C = :Const
+ def self.get_C
+ A::C
+ end
+ # fill cache
+ A.get_C
+ private_constant :C, :D rescue nil
+ begin
+ A.get_C
+ rescue NameError
+ puts "A.get_C"
+ end
+ end
+ INPUT
+ assert_in_out_err([], src, %w(A.get_C), [], bug5702)
+ end
+
+ def test_constant_lookup_in_method_defined_by_class_eval
+ src = <<-INPUT
+ class A
+ B = 42
+ end
+
+ A.class_eval do
+ def self.f
+ B
+ end
+
+ def f
+ B
+ end
+ end
+
+ begin
+ A.f
+ rescue NameError
+ puts "A.f"
+ end
+ begin
+ A.new.f
+ rescue NameError
+ puts "A.new.f"
+ end
+ INPUT
+ assert_in_out_err([], src, %w(A.f A.new.f), [])
+ end
+
+ def test_constant_lookup_in_toplevel_class_eval
+ src = <<-INPUT
+ module X
+ A = 123
+ end
+ begin
+ X.class_eval { A }
+ rescue NameError => e
+ puts e
+ end
+ INPUT
+ assert_in_out_err([], src, ["uninitialized constant A"], [])
+ end
+
+ def test_constant_lookup_in_module_in_class_eval
+ src = <<-INPUT
+ class A
+ B = 42
+ end
+
+ A.class_eval do
+ module C
+ begin
+ B
+ rescue NameError
+ puts "NameError"
+ end
+ end
+ end
+ INPUT
+ assert_in_out_err([], src, ["NameError"], [])
+ end
+
+ module M0
+ def m1; [:M0] end
+ end
+ module M1
+ def m1; [:M1, *super] end
+ end
+ module M2
+ def m1; [:M2, *super] end
+ end
+ M3 = Module.new do
+ def m1; [:M3, *super] end
+ end
+ module M4
+ def m1; [:M4, *super] end
+ end
+ class C
+ def m1; end
+ end
+ class C0 < C
+ include M0
+ prepend M1
+ def m1; [:C0, *super] end
+ end
+ class C1 < C0
+ prepend M2, M3
+ include M4
+ def m1; [:C1, *super] end
+ end
+
+ def test_prepend
+ obj = C0.new
+ expected = [:M1,:C0,:M0]
+ assert_equal(expected, obj.m1)
+ obj = C1.new
+ expected = [:M2,:M3,:C1,:M4,:M1,:C0,:M0]
+ assert_equal(expected, obj.m1)
+ end
+
+ def test_public_prepend
+ assert_nothing_raised('#8846') do
+ Class.new.prepend(Module.new)
+ end
+ end
+
+ def test_prepend_inheritance
+ bug6654 = '[ruby-core:45914]'
+ a = labeled_module("a")
+ b = labeled_module("b") {include a}
+ c = labeled_class("c") {prepend b}
+ assert_operator(c, :<, b, bug6654)
+ assert_operator(c, :<, a, bug6654)
+ bug8357 = '[ruby-core:54736] [Bug #8357]'
+ b = labeled_module("b") {prepend a}
+ c = labeled_class("c") {include b}
+ assert_operator(c, :<, b, bug8357)
+ assert_operator(c, :<, a, bug8357)
+ bug8357 = '[ruby-core:54742] [Bug #8357]'
+ assert_kind_of(b, c.new, bug8357)
+ end
+
+ def test_prepend_instance_methods
+ bug6655 = '[ruby-core:45915]'
+ assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655)
+ end
+
+ def test_prepend_singleton_methods
+ o = Object.new
+ o.singleton_class.class_eval {prepend Module.new}
+ assert_equal([], o.singleton_methods)
+ end
+
+ def test_prepend_remove_method
+ c = Class.new do
+ prepend Module.new {def foo; end}
+ end
+ assert_raise(NameError) do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ c.class_eval do
+ def foo; end
+ end
+ removed = nil
+ c.singleton_class.class_eval do
+ define_method(:method_removed) {|id| removed = id}
+ end
+ assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ assert_equal(:foo, removed)
+ end
+
+ def test_prepend_class_ancestors
+ bug6658 = '[ruby-core:45919]'
+ m = labeled_module("m")
+ c = labeled_class("c") {prepend m}
+ assert_equal([m, c], c.ancestors[0, 2], bug6658)
+
+ bug6662 = '[ruby-dev:45868]'
+ c2 = labeled_class("c2", c)
+ anc = c2.ancestors
+ assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662)
+ end
+
+ def test_prepend_module_ancestors
+ bug6659 = '[ruby-dev:45861]'
+ m0 = labeled_module("m0") {def x; [:m0, *super] end}
+ m1 = labeled_module("m1") {def x; [:m1, *super] end; prepend m0}
+ m2 = labeled_module("m2") {def x; [:m2, *super] end; prepend m1}
+ c0 = labeled_class("c0") {def x; [:c0] end}
+ c1 = labeled_class("c1") {def x; [:c1] end; prepend m2}
+ c2 = labeled_class("c2", c0) {def x; [:c2, *super] end; include m2}
+
+ assert_equal([m0, m1], m1.ancestors, bug6659)
+
+ bug6662 = '[ruby-dev:45868]'
+ assert_equal([m0, m1, m2], m2.ancestors, bug6662)
+ assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662)
+ assert_equal([:m0, :m1, :m2, :c1], c1.new.x)
+ assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662)
+ assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x)
+
+ m3 = labeled_module("m3") {include m1; prepend m1}
+ assert_equal([m3, m0, m1], m3.ancestors)
+ m3 = labeled_module("m3") {prepend m1; include m1}
+ assert_equal([m0, m1, m3], m3.ancestors)
+ m3 = labeled_module("m3") {prepend m1; prepend m1}
+ assert_equal([m0, m1, m3], m3.ancestors)
+ m3 = labeled_module("m3") {include m1; include m1}
+ assert_equal([m3, m0, m1], m3.ancestors)
+ end
+
+ def labeled_module(name, &block)
+ EnvUtil.labeled_module(name, &block)
+ end
+
+ def labeled_class(name, superclass = Object, &block)
+ EnvUtil.labeled_class(name, superclass, &block)
+ end
+
+ def test_prepend_instance_methods_false
+ bug6660 = '[ruby-dev:45863]'
+ assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
+ assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
+ end
+
+ def test_cyclic_prepend
+ bug7841 = '[ruby-core:52205] [Bug #7841]'
+ m1 = Module.new
+ m2 = Module.new
+ m1.instance_eval { prepend(m2) }
+ assert_raise(ArgumentError, bug7841) do
+ m2.instance_eval { prepend(m1) }
+ end
+ end
+
+ def test_prepend_optmethod
+ bug7983 = '[ruby-dev:47124] [Bug #7983]'
+ assert_separately [], %{
+ module M
+ def /(other)
+ to_f / other
+ end
+ end
+ Fixnum.send(:prepend, M)
+ assert_equal(0.5, 1 / 2, "#{bug7983}")
+ }
+ assert_equal(0, 1 / 2)
+ end
+
+ def test_prepend_visibility
+ bug8005 = '[ruby-core:53106] [Bug #8005]'
+ c = Class.new do
+ prepend Module.new {}
+ def foo() end
+ protected :foo
+ end
+ a = c.new
+ assert_respond_to a, [:foo, true], bug8005
+ assert_nothing_raised(NoMethodError, bug8005) {a.send :foo}
+ end
+
+ def test_prepend_visibility_inherited
+ bug8238 = '[ruby-core:54105] [Bug #8238]'
+ assert_separately [], <<-"end;", timeout: 20
+ class A
+ def foo() A; end
+ private :foo
+ end
+ class B < A
+ public :foo
+ prepend Module.new
+ end
+ assert_equal(A, B.new.foo, "#{bug8238}")
+ end;
+ end
+
+ def test_prepend_included_modules
+ bug8025 = '[ruby-core:53158] [Bug #8025]'
+ mixin = labeled_module("mixin")
+ c = labeled_module("c") {prepend mixin}
+ im = c.included_modules
+ assert_not_include(im, c, bug8025)
+ assert_include(im, mixin, bug8025)
+ c1 = labeled_class("c1") {prepend mixin}
+ c2 = labeled_class("c2", c1)
+ im = c2.included_modules
+ assert_not_include(im, c1, bug8025)
+ assert_not_include(im, c2, bug8025)
+ assert_include(im, mixin, bug8025)
+ end
+
+ def test_prepend_super_in_alias
+ bug7842 = '[Bug #7842]'
+
+ p = labeled_module("P") do
+ def m; "P"+super; end
+ end
+ a = labeled_class("A") do
+ def m; "A"; end
+ end
+ b = labeled_class("B", a) do
+ def m; "B"+super; end
+ alias m2 m
+ prepend p
+ alias m3 m
+ end
+ assert_equal("BA", b.new.m2, bug7842)
+ assert_equal("PBA", b.new.m3, bug7842)
+ end
+
+ def test_include_super_in_alias
+ bug9236 = '[Bug #9236]'
+
+ fun = labeled_module("Fun") do
+ def hello
+ orig_hello
+ end
+ end
+
+ m1 = labeled_module("M1") do
+ def hello
+ 'hello!'
+ end
+ end
+
+ m2 = labeled_module("M2") do
+ def hello
+ super
+ end
+ end
+
+ foo = labeled_class("Foo") do
+ include m1
+ include m2
+
+ alias orig_hello hello
+ include fun
+ end
+
+ assert_equal('hello!', foo.new.hello, bug9236)
+ end
+
+ def test_prepend_call_super
+ assert_separately([], <<-'end;') #do
+ bug10847 = '[ruby-core:68093] [Bug #10847]'
+ module M; end
+ Float.prepend M
+ assert_nothing_raised(SystemStackError, bug10847) do
+ 0.3.numerator
+ end
+ end;
+ end
+
+ def test_class_variables
+ m = Module.new
+ m.class_variable_set(:@@foo, 1)
+ m2 = Module.new
+ m2.send(:include, m)
+ m2.class_variable_set(:@@bar, 2)
+ assert_equal([:@@foo], m.class_variables)
+ assert_equal([:@@bar, :@@foo], m2.class_variables)
+ assert_equal([:@@bar, :@@foo], m2.class_variables(true))
+ assert_equal([:@@bar], m2.class_variables(false))
+ end
+
+ Bug6891 = '[ruby-core:47241]'
+
+ def test_extend_module_with_protected_method
+ list = []
+
+ x = Class.new {
+ @list = list
+
+ extend Module.new {
+ protected
+
+ def inherited(klass)
+ @list << "protected"
+ super(klass)
+ end
+ }
+
+ extend Module.new {
+ def inherited(klass)
+ @list << "public"
+ super(klass)
+ end
+ }
+ }
+
+ assert_nothing_raised(NoMethodError, Bug6891) {Class.new(x)}
+ assert_equal(['public', 'protected'], list)
+ end
+
+ def test_extend_module_with_protected_bmethod
+ list = []
+
+ x = Class.new {
+ extend Module.new {
+ protected
+
+ define_method(:inherited) do |klass|
+ list << "protected"
+ super(klass)
+ end
+ }
+
+ extend Module.new {
+ define_method(:inherited) do |klass|
+ list << "public"
+ super(klass)
+ end
+ }
+ }
+
+ assert_nothing_raised(NoMethodError, Bug6891) {Class.new(x)}
+ assert_equal(['public', 'protected'], list)
+ end
+
+ def test_invalid_attr
+ %W[
+ foo?
+ @foo
+ @@foo
+ $foo
+ \u3042$
+ ].each do |name|
+ assert_raise_with_message(NameError, /#{Regexp.quote(quote(name))}/) do
+ Module.new { attr_accessor name.to_sym }
+ end
+ end
+ end
+
+ private def quote(name)
+ encoding = Encoding.default_internal || Encoding.default_external
+ (name.encoding == encoding || name.ascii_only?) ? name : name.inspect
+ end
+
+ class AttrTest
+ class << self
+ attr_accessor :cattr
+ end
+ attr_accessor :iattr
+ def ivar
+ @ivar
+ end
+ end
+
+ def test_uninitialized_instance_variable
+ a = AttrTest.new
+ assert_warning(/instance variable @ivar not initialized/) do
+ assert_nil(a.ivar)
+ end
+ a.instance_variable_set(:@ivar, 42)
+ assert_warning '' do
+ assert_equal(42, a.ivar)
+ end
+
+ name = "@\u{5909 6570}"
+ assert_warning(/instance variable #{name} not initialized/) do
+ val = EnvUtil.with_default_external(Encoding::UTF_8) {
+ a.instance_eval(name)
+ }
+ assert_nil(val)
+ end
+ end
+
+ def test_uninitialized_attr
+ a = AttrTest.new
+ assert_warning '' do
+ assert_nil(a.iattr)
+ end
+ a.iattr = 42
+ assert_warning '' do
+ assert_equal(42, a.iattr)
+ end
+ end
+
+ def test_uninitialized_attr_class
+ assert_warning '' do
+ assert_nil(AttrTest.cattr)
+ end
+ AttrTest.cattr = 42
+ assert_warning '' do
+ assert_equal(42, AttrTest.cattr)
+ end
+ end
+
+ def test_uninitialized_attr_non_object
+ a = Class.new(Array) do
+ attr_accessor :iattr
+ end.new
+ assert_warning '' do
+ assert_nil(a.iattr)
+ end
+ a.iattr = 42
+ assert_warning '' do
+ assert_equal(42, a.iattr)
+ end
+ end
+
+ def test_remove_const
+ m = Module.new
+ assert_raise(NameError){ m.instance_eval { remove_const(:__FOO__) } }
+ end
+
+ def test_private_top_methods
+ assert_top_method_is_private(:include)
+ assert_top_method_is_private(:public)
+ assert_top_method_is_private(:private)
+ assert_top_method_is_private(:define_method)
+ end
+
+ module PrivateConstantReopen
+ PRIVATE_CONSTANT = true
+ private_constant :PRIVATE_CONSTANT
+ end
+
+ def test_private_constant_reopen
+ assert_raise(NameError) do
+ eval <<-EOS, TOPLEVEL_BINDING
+ module TestModule::PrivateConstantReopen::PRIVATE_CONSTANT
+ end
+ EOS
+ end
+ assert_raise(NameError) do
+ eval <<-EOS, TOPLEVEL_BINDING
+ class TestModule::PrivateConstantReopen::PRIVATE_CONSTANT
+ end
+ EOS
+ end
+ end
+
+ def test_singleton_class_ancestors
+ feature8035 = '[ruby-core:53171]'
+ obj = Object.new
+ assert_equal [obj.singleton_class, Object], obj.singleton_class.ancestors.first(2), feature8035
+
+ mod = Module.new
+ obj.extend mod
+ assert_equal [obj.singleton_class, mod, Object], obj.singleton_class.ancestors.first(3)
+
+ obj = Object.new
+ obj.singleton_class.send :prepend, mod
+ assert_equal [mod, obj.singleton_class, Object], obj.singleton_class.ancestors.first(3)
+ end
+
+ def test_visibility_by_public_class_method
+ bug8284 = '[ruby-core:54404] [Bug #8284]'
+ assert_raise(NoMethodError) {Object.define_method}
+ Module.new.public_class_method(:define_method)
+ assert_raise(NoMethodError, bug8284) {Object.define_method}
+ end
+
+ def test_include_module_with_constants_invalidates_method_cache
+ assert_in_out_err([], <<-RUBY, %w(123 456), [])
+ A = 123
+
+ class Foo
+ def self.a
+ A
+ end
+ end
+
+ module M
+ A = 456
+ end
+
+ puts Foo.a
+ Foo.send(:include, M)
+ puts Foo.a
+ RUBY
+ end
+
+ def test_return_value_of_define_method
+ retvals = []
+ Class.new.class_eval do
+ retvals << define_method(:foo){}
+ retvals << define_method(:bar, instance_method(:foo))
+ end
+ assert_equal :foo, retvals[0]
+ assert_equal :bar, retvals[1]
+ end
+
+ def test_return_value_of_define_singleton_method
+ retvals = []
+ Class.new do
+ retvals << define_singleton_method(:foo){}
+ retvals << define_singleton_method(:bar, method(:foo))
+ end
+ assert_equal :foo, retvals[0]
+ assert_equal :bar, retvals[1]
+ end
+
+ def test_prepend_gc
+ assert_separately [], %{
+ module Foo
+ end
+ class Object
+ prepend Foo
+ end
+ GC.start # make created T_ICLASS old (or remembered shady)
+ class Object # add methods into T_ICLASS (need WB if it is old)
+ def foo; end
+ attr_reader :bar
+ end
+ 1_000_000.times{''} # cause GC
+ }
+ end
+
+ def test_inspect_segfault
+ bug_10282 = '[ruby-core:65214] [Bug #10282]'
+ assert_separately [], <<-RUBY
+ module ShallowInspect
+ def shallow_inspect
+ "foo"
+ end
+ end
+
+ module InspectIsShallow
+ include ShallowInspect
+ alias_method :inspect, :shallow_inspect
+ end
+
+ class A
+ end
+
+ A.prepend InspectIsShallow
+
+ expect = "#<Method: A(Object)#inspect(shallow_inspect)>"
+ assert_equal expect, A.new.method(:inspect).inspect, "#{bug_10282}"
+ RUBY
+ end
+
+ private
+
+ def assert_top_method_is_private(method)
+ assert_separately [], %{
+ methods = singleton_class.private_instance_methods(false)
+ assert_include(methods, :#{method}, ":#{method} should be private")
+
+ assert_raise_with_message(NoMethodError, "private method `#{method}' called for main:Object") {
+ self.#{method}
+ }
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_not.rb b/jni/ruby/test/ruby/test_not.rb
new file mode 100644
index 0000000..486075b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_not.rb
@@ -0,0 +1,12 @@
+require 'test/unit'
+
+class TestIfunless < Test::Unit::TestCase
+ def test_not_with_grouped_expression
+ assert_equal(false, (not (true)))
+ assert_equal(true, (not (false)))
+ end
+
+ def test_not_with_empty_grouped_expression
+ assert_equal(true, (not ()))
+ end
+end
diff --git a/jni/ruby/test/ruby/test_notimp.rb b/jni/ruby/test/ruby/test_notimp.rb
new file mode 100644
index 0000000..9721723
--- /dev/null
+++ b/jni/ruby/test/ruby/test_notimp.rb
@@ -0,0 +1,84 @@
+require 'test/unit'
+require 'timeout'
+require 'tmpdir'
+
+class TestNotImplement < Test::Unit::TestCase
+ def test_respond_to_fork
+ assert_include(Process.methods, :fork)
+ if /linux/ =~ RUBY_PLATFORM
+ assert_equal(true, Process.respond_to?(:fork))
+ end
+ end
+
+ def test_respond_to_lchmod
+ assert_include(File.methods, :lchmod)
+ if /linux/ =~ RUBY_PLATFORM
+ assert_equal(false, File.respond_to?(:lchmod))
+ end
+ if /freebsd/ =~ RUBY_PLATFORM
+ assert_equal(true, File.respond_to?(:lchmod))
+ end
+ end
+
+ def test_call_fork
+ GC.start
+ pid = nil
+ ps =
+ case RUBY_PLATFORM
+ when /linux/ # assume Linux Distribution uses procps
+ proc {`ps -eLf #{pid}`}
+ when /freebsd/
+ proc {`ps -lH #{pid}`}
+ when /darwin/
+ proc {`ps -lM #{pid}`}
+ else
+ proc {`ps -l #{pid}`}
+ end
+ assert_nothing_raised(Timeout::Error, ps) do
+ Timeout.timeout(5) {
+ pid = fork {}
+ Process.wait pid
+ pid = nil
+ }
+ end
+ ensure
+ if pid
+ Process.kill(:KILL, pid)
+ Process.wait pid
+ end
+ end if Process.respond_to?(:fork)
+
+ def test_call_lchmod
+ if File.respond_to?(:lchmod)
+ Dir.mktmpdir {|d|
+ f = "#{d}/f"
+ g = "#{d}/g"
+ File.open(f, "w") {}
+ File.symlink f, g
+ newmode = 0444
+ File.lchmod newmode, "#{d}/g"
+ snew = File.lstat(g)
+ assert_equal(newmode, snew.mode & 0777)
+ }
+ end
+ end
+
+ def test_method_inspect_fork
+ m = Process.method(:fork)
+ if Process.respond_to?(:fork)
+ assert_not_match(/not-implemented/, m.inspect)
+ else
+ assert_match(/not-implemented/, m.inspect)
+ end
+ end
+
+ def test_method_inspect_lchmod
+ m = File.method(:lchmod)
+ if File.respond_to?(:lchmod)
+ assert_not_match(/not-implemented/, m.inspect)
+ else
+ assert_match(/not-implemented/, m.inspect)
+ end
+ end
+
+end
diff --git a/jni/ruby/test/ruby/test_numeric.rb b/jni/ruby/test/ruby/test_numeric.rb
new file mode 100644
index 0000000..4c94c69
--- /dev/null
+++ b/jni/ruby/test/ruby/test_numeric.rb
@@ -0,0 +1,366 @@
+require 'test/unit'
+
+class TestNumeric < Test::Unit::TestCase
+ class DummyNumeric < Numeric
+ end
+
+ def test_coerce
+ a, b = 1.coerce(2)
+ assert_equal(Fixnum, a.class)
+ assert_equal(Fixnum, b.class)
+
+ a, b = 1.coerce(2.0)
+ assert_equal(Float, a.class)
+ assert_equal(Float, b.class)
+
+ assert_raise(TypeError) { -Numeric.new }
+
+ assert_raise_with_message(TypeError, /can't be coerced into /) {1+:foo}
+ assert_raise_with_message(TypeError, /can't be coerced into /) {1&:foo}
+ assert_raise_with_message(TypeError, /can't be coerced into /) {1|:foo}
+ assert_raise_with_message(TypeError, /can't be coerced into /) {1^:foo}
+
+ EnvUtil.with_default_external(Encoding::UTF_8) do
+ assert_raise_with_message(TypeError, /:\u{3042}/) {1+:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:\u{3042}/) {1&:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:\u{3042}/) {1|:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:\u{3042}/) {1^:"\u{3042}"}
+ end
+ EnvUtil.with_default_external(Encoding::US_ASCII) do
+ assert_raise_with_message(TypeError, /:"\\u3042"/) {1+:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:"\\u3042"/) {1&:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:"\\u3042"/) {1|:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:"\\u3042"/) {1^:"\u{3042}"}
+ end
+
+ bug10711 = '[ruby-core:67405] [Bug #10711]'
+ exp = "1.2 can't be coerced into Fixnum"
+ assert_raise_with_message(TypeError, exp, bug10711) { 1 & 1.2 }
+ end
+
+ def test_dummynumeric
+ a = DummyNumeric.new
+
+ DummyNumeric.class_eval do
+ def coerce(x); nil; end
+ end
+ assert_raise(TypeError) { -a }
+ assert_nil(1 <=> a)
+ assert_raise(ArgumentError) { 1 <= a }
+
+ DummyNumeric.class_eval do
+ remove_method :coerce
+ def coerce(x); 1.coerce(x); end
+ end
+ assert_equal(2, 1 + a)
+ assert_equal(0, 1 <=> a)
+ assert_operator(1, :<=, a)
+
+ DummyNumeric.class_eval do
+ remove_method :coerce
+ def coerce(x); [x, 1]; end
+ end
+ assert_equal(-1, -a)
+
+ bug7688 = '[ruby-core:51389] [Bug #7688]'
+ DummyNumeric.class_eval do
+ remove_method :coerce
+ def coerce(x); raise StandardError; end
+ end
+ assert_raise_with_message(TypeError, /can't be coerced into /) { 1 + a }
+ warn = /will no more rescue exceptions of #coerce.+ in the next release/m
+ assert_warn(warn, bug7688) { assert_raise(ArgumentError) { 1 < a } }
+
+ DummyNumeric.class_eval do
+ remove_method :coerce
+ def coerce(x); :bad_return_value; end
+ end
+ assert_raise_with_message(TypeError, "coerce must return [x, y]") { 1 + a }
+ warn = /Bad return value for #coerce.+next release will raise an error/m
+ assert_warn(warn, bug7688) { assert_raise(ArgumentError) { 1 < a } }
+
+ ensure
+ DummyNumeric.class_eval do
+ remove_method :coerce
+ end
+ end
+
+ def test_singleton_method
+ a = Numeric.new
+ assert_raise_with_message(TypeError, /foo/) { def a.foo; end }
+ assert_raise_with_message(TypeError, /\u3042/) { eval("def a.\u3042; end") }
+ end
+
+ def test_dup
+ a = Numeric.new
+ assert_raise(TypeError) { a.dup }
+
+ c = Module.new do
+ break eval("class C\u{3042} < Numeric; self; end")
+ end
+ assert_raise_with_message(TypeError, /C\u3042/) {c.new.dup}
+ end
+
+ def test_quo
+ assert_raise(TypeError) {DummyNumeric.new.quo(1)}
+ end
+
+ def test_quo_ruby_core_41575
+ x = DummyNumeric.new
+ rat = 84.quo(1)
+ DummyNumeric.class_eval do
+ define_method(:to_r) { rat }
+ end
+ assert_equal(2.quo(1), x.quo(42), '[ruby-core:41575]')
+ ensure
+ DummyNumeric.class_eval do
+ remove_method :to_r
+ end
+ end
+
+ def test_divmod
+=begin
+ DummyNumeric.class_eval do
+ def /(x); 42.0; end
+ def %(x); :mod; end
+ end
+
+ assert_equal(42, DummyNumeric.new.div(1))
+ assert_equal(:mod, DummyNumeric.new.modulo(1))
+ assert_equal([42, :mod], DummyNumeric.new.divmod(1))
+=end
+
+ assert_kind_of(Integer, 11.divmod(3.5).first, '[ruby-dev:34006]')
+
+=begin
+ ensure
+ DummyNumeric.class_eval do
+ remove_method :/, :%
+ end
+=end
+ end
+
+ def test_real_p
+ assert_predicate(Numeric.new, :real?)
+ end
+
+ def test_integer_p
+ assert_not_predicate(Numeric.new, :integer?)
+ end
+
+ def test_abs
+ a = DummyNumeric.new
+ DummyNumeric.class_eval do
+ def -@; :ok; end
+ def <(x); true; end
+ end
+
+ assert_equal(:ok, a.abs)
+
+ DummyNumeric.class_eval do
+ remove_method :<
+ def <(x); false; end
+ end
+
+ assert_equal(a, a.abs)
+
+ ensure
+ DummyNumeric.class_eval do
+ remove_method :-@, :<
+ end
+ end
+
+ def test_zero_p
+ DummyNumeric.class_eval do
+ def ==(x); true; end
+ end
+
+ assert_predicate(DummyNumeric.new, :zero?)
+
+ ensure
+ DummyNumeric.class_eval do
+ remove_method :==
+ end
+ end
+
+ def test_to_int
+ DummyNumeric.class_eval do
+ def to_i; :ok; end
+ end
+
+ assert_equal(:ok, DummyNumeric.new.to_int)
+
+ ensure
+ DummyNumeric.class_eval do
+ remove_method :to_i
+ end
+ end
+
+ def test_cmp
+ a = Numeric.new
+ assert_equal(0, a <=> a)
+ assert_nil(a <=> :foo)
+ end
+
+ def test_floor_ceil_round_truncate
+ DummyNumeric.class_eval do
+ def to_f; 1.5; end
+ end
+
+ a = DummyNumeric.new
+ assert_equal(1, a.floor)
+ assert_equal(2, a.ceil)
+ assert_equal(2, a.round)
+ assert_equal(1, a.truncate)
+
+ DummyNumeric.class_eval do
+ remove_method :to_f
+ def to_f; 1.4; end
+ end
+
+ a = DummyNumeric.new
+ assert_equal(1, a.floor)
+ assert_equal(2, a.ceil)
+ assert_equal(1, a.round)
+ assert_equal(1, a.truncate)
+
+ DummyNumeric.class_eval do
+ remove_method :to_f
+ def to_f; -1.5; end
+ end
+
+ a = DummyNumeric.new
+ assert_equal(-2, a.floor)
+ assert_equal(-1, a.ceil)
+ assert_equal(-2, a.round)
+ assert_equal(-1, a.truncate)
+
+ ensure
+ DummyNumeric.class_eval do
+ remove_method :to_f
+ end
+ end
+
+ def assert_step(expected, (from, *args), inf: false)
+ enum = from.step(*args)
+ size = enum.size
+ xsize = expected.size
+
+ if inf
+ assert_send [size, :infinite?], "step size: +infinity"
+ assert_send [size, :>, 0], "step size: +infinity"
+
+ a = []
+ from.step(*args) { |x| a << x; break if a.size == xsize }
+ assert_equal expected, a, "step"
+
+ a = []
+ enum.each { |x| a << x; break if a.size == xsize }
+ assert_equal expected, a, "step enumerator"
+ else
+ assert_equal expected.size, size, "step size"
+
+ a = []
+ from.step(*args) { |x| a << x }
+ assert_equal expected, a, "step"
+
+ a = []
+ enum.each { |x| a << x }
+ assert_equal expected, a, "step enumerator"
+ end
+ end
+
+ def test_step
+ i, bignum = 32, 1 << 30
+ bignum <<= (i <<= 1) - 32 until bignum.is_a?(Bignum)
+ assert_raise(ArgumentError) { 1.step(10, 1, 0) { } }
+ assert_raise(ArgumentError) { 1.step(10, 1, 0).size }
+ assert_raise(ArgumentError) { 1.step(10, 0) { } }
+ assert_raise(ArgumentError) { 1.step(10, 0).size }
+ assert_raise(ArgumentError) { 1.step(10, "1") { } }
+ assert_raise(ArgumentError) { 1.step(10, "1").size }
+ assert_raise(TypeError) { 1.step(10, nil) { } }
+ assert_raise(TypeError) { 1.step(10, nil).size }
+ assert_nothing_raised { 1.step(by: 0, to: nil) }
+ assert_nothing_raised { 1.step(by: 0, to: nil).size }
+ assert_nothing_raised { 1.step(by: 0) }
+ assert_nothing_raised { 1.step(by: 0).size }
+ assert_nothing_raised { 1.step(by: nil) }
+ assert_nothing_raised { 1.step(by: nil).size }
+
+ bug9811 = '[ruby-dev:48177] [Bug #9811]'
+ assert_raise(ArgumentError, bug9811) { 1.step(10, foo: nil) {} }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, foo: nil).size }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, to: 11) {} }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, to: 11).size }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, 1, by: 11) {} }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, 1, by: 11).size }
+
+ assert_equal(bignum*2+1, (-bignum).step(bignum, 1).size)
+ assert_equal(bignum*2, (-bignum).step(bignum-1, 1).size)
+
+ assert_equal(10+1, (0.0).step(10.0, 1.0).size)
+
+ i, bigflo = 1, bignum.to_f
+ i <<= 1 until (bigflo - i).to_i < bignum
+ bigflo -= i >> 1
+ assert_equal(bigflo.to_i, (0.0).step(bigflo-1.0, 1.0).size)
+ assert_operator((0.0).step(bignum.to_f, 1.0).size, :>=, bignum) # may loose precision
+
+ assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 10]
+ assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, to: 10]
+ assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, to: 10, by: nil]
+ assert_step [1, 3, 5, 7, 9], [1, 10, 2]
+ assert_step [1, 3, 5, 7, 9], [1, to: 10, by: 2]
+
+ assert_step [10, 8, 6, 4, 2], [10, 1, -2]
+ assert_step [10, 8, 6, 4, 2], [10, to: 1, by: -2]
+ assert_step [1.0, 3.0, 5.0, 7.0, 9.0], [1.0, 10.0, 2.0]
+ assert_step [1.0, 3.0, 5.0, 7.0, 9.0], [1.0, to: 10.0, by: 2.0]
+ assert_step [1], [1, 10, bignum]
+ assert_step [1], [1, to: 10, by: bignum]
+
+ assert_step [], [2, 1, 3]
+ assert_step [], [-2, -1, -3]
+ assert_step [3, 3, 3, 3], [3, by: 0], inf: true
+ assert_step [3, 3, 3, 3], [3, by: 0, to: 42], inf: true
+ assert_step [10], [10, 1, -bignum]
+
+ assert_step [], [1, 0, Float::INFINITY]
+ assert_step [], [0, 1, -Float::INFINITY]
+ assert_step [10], [10, to: 1, by: -bignum]
+
+ assert_step [10, 11, 12, 13], [10], inf: true
+ assert_step [10, 9, 8, 7], [10, by: -1], inf: true
+ assert_step [10, 9, 8, 7], [10, by: -1, to: nil], inf: true
+
+ assert_step [42, 42, 42, 42], [42, by: 0, to: -Float::INFINITY], inf: true
+ assert_step [42, 42, 42, 42], [42, by: 0, to: 42.5], inf: true
+ assert_step [4.2, 4.2, 4.2, 4.2], [4.2, by: 0.0], inf: true
+ assert_step [4.2, 4.2, 4.2, 4.2], [4.2, by: -0.0], inf: true
+ assert_step [42.0, 42.0, 42.0, 42.0], [42, by: 0.0, to: 44], inf: true
+ assert_step [42.0, 42.0, 42.0, 42.0], [42, by: 0.0, to: 0], inf: true
+ assert_step [42.0, 42.0, 42.0, 42.0], [42, by: -0.0, to: 44], inf: true
+
+ assert_step [bignum]*4, [bignum, by: 0], inf: true
+ assert_step [bignum]*4, [bignum, by: 0.0], inf: true
+ assert_step [bignum]*4, [bignum, by: 0, to: bignum+1], inf: true
+ assert_step [bignum]*4, [bignum, by: 0, to: 0], inf: true
+ end
+
+ def test_num2long
+ assert_raise(TypeError) { 1 & nil }
+ assert_raise(TypeError) { 1 & 1.0 }
+ assert_raise(TypeError) { 1 & 2147483648.0 }
+ assert_raise(TypeError) { 1 & 9223372036854777856.0 }
+ o = Object.new
+ def o.to_int; 1; end
+ assert_raise(TypeError) { assert_equal(1, 1 & o) }
+ end
+
+ def test_eql
+ assert_equal(1, 1.0)
+ assert_not_operator(1, :eql?, 1.0)
+ assert_not_operator(1, :eql?, 2)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_object.rb b/jni/ruby/test/ruby/test_object.rb
new file mode 100644
index 0000000..d5b4a1b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_object.rb
@@ -0,0 +1,861 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+
+class TestObject < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def test_itself
+ feature6373 = '[ruby-core:44704] [Feature #6373]'
+ object = Object.new
+ assert_same(object, object.itself, feature6373)
+ end
+
+ def test_dup
+ assert_raise(TypeError) { 1.dup }
+ assert_raise(TypeError) { true.dup }
+ assert_raise(TypeError) { nil.dup }
+
+ assert_raise(TypeError) do
+ Object.new.instance_eval { initialize_copy(1) }
+ end
+ end
+
+ def test_init_dupclone
+ cls = Class.new do
+ def initialize_clone(orig); throw :initialize_clone; end
+ def initialize_dup(orig); throw :initialize_dup; end
+ end
+
+ obj = cls.new
+ assert_throw(:initialize_clone) {obj.clone}
+ assert_throw(:initialize_dup) {obj.dup}
+ end
+
+ def test_instance_of
+ assert_raise(TypeError) { 1.instance_of?(1) }
+ end
+
+ def test_kind_of
+ assert_raise(TypeError) { 1.kind_of?(1) }
+ end
+
+ def test_taint_frozen_obj
+ o = Object.new
+ o.freeze
+ assert_raise(RuntimeError) { o.taint }
+
+ o = Object.new
+ o.taint
+ o.freeze
+ assert_raise(RuntimeError) { o.untaint }
+ end
+
+ def test_freeze_immediate
+ assert_equal(true, 1.frozen?)
+ 1.freeze
+ assert_equal(true, 1.frozen?)
+ assert_equal(true, 2.frozen?)
+ assert_equal(true, true.frozen?)
+ assert_equal(true, false.frozen?)
+ assert_equal(true, nil.frozen?)
+ end
+
+ def test_frozen_error_message
+ name = "C\u{30c6 30b9 30c8}"
+ klass = EnvUtil.labeled_class(name) {
+ attr_accessor :foo
+ }
+ obj = klass.new.freeze
+ assert_raise_with_message(RuntimeError, /#{name}/) {
+ obj.foo = 1
+ }
+ end
+
+ def test_nil_to_f
+ assert_equal(0.0, nil.to_f)
+ end
+
+ def test_not
+ assert_equal(false, Object.new.send(:!))
+ assert_equal(true, nil.send(:!))
+ end
+
+ def test_true_and
+ assert_equal(true, true & true)
+ assert_equal(true, true & 1)
+ assert_equal(false, true & false)
+ assert_equal(false, true & nil)
+ end
+
+ def test_true_or
+ assert_equal(true, true | true)
+ assert_equal(true, true | 1)
+ assert_equal(true, true | false)
+ assert_equal(true, true | nil)
+ end
+
+ def test_true_xor
+ assert_equal(false, true ^ true)
+ assert_equal(false, true ^ 1)
+ assert_equal(true, true ^ false)
+ assert_equal(true, true ^ nil)
+ end
+
+ def test_false_and
+ assert_equal(false, false & true)
+ assert_equal(false, false & 1)
+ assert_equal(false, false & false)
+ assert_equal(false, false & nil)
+ end
+
+ def test_false_or
+ assert_equal(true, false | true)
+ assert_equal(true, false | 1)
+ assert_equal(false, false | false)
+ assert_equal(false, false | nil)
+ end
+
+ def test_false_xor
+ assert_equal(true, false ^ true)
+ assert_equal(true, false ^ 1)
+ assert_equal(false, false ^ false)
+ assert_equal(false, false ^ nil)
+ end
+
+ def test_methods
+ o = Object.new
+ a1 = o.methods
+ a2 = o.methods(false)
+
+ def o.foo; end
+
+ assert_equal([:foo], o.methods(true) - a1)
+ assert_equal([:foo], o.methods(false) - a2)
+ end
+
+ def test_methods2
+ c0 = Class.new
+ c1 = Class.new(c0)
+ c1.module_eval do
+ public ; def foo; end
+ protected; def bar; end
+ private ; def baz; end
+ end
+ c2 = Class.new(c1)
+ c2.module_eval do
+ public ; def foo2; end
+ protected; def bar2; end
+ private ; def baz2; end
+ end
+
+ o0 = c0.new
+ o2 = c2.new
+
+ assert_equal([:baz, :baz2], (o2.private_methods - o0.private_methods).sort)
+ assert_equal([:baz2], (o2.private_methods(false) - o0.private_methods(false)).sort)
+
+ assert_equal([:bar, :bar2], (o2.protected_methods - o0.protected_methods).sort)
+ assert_equal([:bar2], (o2.protected_methods(false) - o0.protected_methods(false)).sort)
+
+ assert_equal([:foo, :foo2], (o2.public_methods - o0.public_methods).sort)
+ assert_equal([:foo2], (o2.public_methods(false) - o0.public_methods(false)).sort)
+ end
+
+ def test_methods_prepend
+ bug8044 = '[ruby-core:53207] [Bug #8044]'
+ o = Object.new
+ def o.foo; end
+ assert_equal([:foo], o.methods(false))
+ class << o; prepend Module.new; end
+ assert_equal([:foo], o.methods(false), bug8044)
+ end
+
+ def test_instance_variable_get
+ o = Object.new
+ o.instance_eval { @foo = :foo }
+ assert_equal(:foo, o.instance_variable_get(:@foo))
+ assert_equal(nil, o.instance_variable_get(:@bar))
+ assert_raise(NameError) { o.instance_variable_get('@') }
+ assert_raise(NameError) { o.instance_variable_get(:'@') }
+ assert_raise(NameError) { o.instance_variable_get(:foo) }
+ assert_raise(NameError) { o.instance_variable_get("bar") }
+ assert_raise(TypeError) { o.instance_variable_get(1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
+ def n.count; @count; end
+ assert_equal(:foo, o.instance_variable_get(n))
+ assert_equal(1, n.count)
+ end
+
+ def test_instance_variable_set
+ o = Object.new
+ o.instance_variable_set(:@foo, :foo)
+ assert_equal(:foo, o.instance_eval { @foo })
+ assert_raise(NameError) { o.instance_variable_set(:'@', 1) }
+ assert_raise(NameError) { o.instance_variable_set('@', 1) }
+ assert_raise(NameError) { o.instance_variable_set(:foo, 1) }
+ assert_raise(NameError) { o.instance_variable_set("bar", 1) }
+ assert_raise(TypeError) { o.instance_variable_set(1, 1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
+ def n.count; @count; end
+ o.instance_variable_set(n, :bar)
+ assert_equal(:bar, o.instance_eval { @foo })
+ assert_equal(1, n.count)
+ end
+
+ def test_instance_variable_defined
+ o = Object.new
+ o.instance_eval { @foo = :foo }
+ assert_equal(true, o.instance_variable_defined?(:@foo))
+ assert_equal(false, o.instance_variable_defined?(:@bar))
+ assert_raise(NameError) { o.instance_variable_defined?(:'@') }
+ assert_raise(NameError) { o.instance_variable_defined?('@') }
+ assert_raise(NameError) { o.instance_variable_defined?(:foo) }
+ assert_raise(NameError) { o.instance_variable_defined?("bar") }
+ assert_raise(TypeError) { o.instance_variable_defined?(1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
+ def n.count; @count; end
+ assert_equal(true, o.instance_variable_defined?(n))
+ assert_equal(1, n.count)
+ end
+
+ def test_remove_instance_variable
+ o = Object.new
+ o.instance_eval { @foo = :foo }
+ o.remove_instance_variable(:@foo)
+ assert_equal(false, o.instance_variable_defined?(:@foo))
+ end
+
+ def test_convert_type
+ o = Object.new
+ def o.to_s; 1; end
+ assert_raise(TypeError) { String(o) }
+ def o.to_s; "o"; end
+ assert_equal("o", String(o))
+ def o.respond_to?(*) false; end
+ assert_raise(TypeError) { String(o) }
+ end
+
+ def test_check_convert_type
+ o = Object.new
+ def o.to_a; 1; end
+ assert_raise(TypeError) { Array(o) }
+ def o.to_a; [1]; end
+ assert_equal([1], Array(o))
+ def o.respond_to?(*) false; end
+ assert_equal([o], Array(o))
+ end
+
+ def test_convert_hash
+ assert_equal({}, Hash(nil))
+ assert_equal({}, Hash([]))
+ assert_equal({key: :value}, Hash(key: :value))
+ assert_raise(TypeError) { Hash([1,2]) }
+ assert_raise(TypeError) { Hash(Object.new) }
+ o = Object.new
+ def o.to_hash; {a: 1, b: 2}; end
+ assert_equal({a: 1, b: 2}, Hash(o))
+ def o.to_hash; 9; end
+ assert_raise(TypeError) { Hash(o) }
+ end
+
+ def test_to_integer
+ o = Object.new
+ def o.to_i; nil; end
+ assert_raise(TypeError) { Integer(o) }
+ def o.to_i; 42; end
+ assert_equal(42, Integer(o))
+ def o.respond_to?(*) false; end
+ assert_raise(TypeError) { Integer(o) }
+ end
+
+ class MyInteger
+ def initialize(n); @num = n; end
+ def to_int; @num; end
+ def <=>(n); @num <=> n.to_int; end
+ def <=(n); @num <= n.to_int; end
+ def +(n); MyInteger.new(@num + n.to_int); end
+ end
+
+ def test_check_to_integer
+ o1 = MyInteger.new(1)
+ o9 = MyInteger.new(9)
+ n = 0
+ Range.new(o1, o9).step(2) {|x| n += x.to_int }
+ assert_equal(1+3+5+7+9, n)
+ end
+
+ def test_redefine_method_under_verbose
+ assert_in_out_err([], <<-INPUT, %w(2), /warning: method redefined; discarding old foo$/)
+ $VERBOSE = true
+ o = Object.new
+ def o.foo; 1; end
+ def o.foo; 2; end
+ p o.foo
+ INPUT
+ end
+
+ def test_redefine_method_which_may_case_serious_problem
+ assert_in_out_err([], <<-INPUT, [], %r"warning: redefining `object_id' may cause serious problems$")
+ $VERBOSE = false
+ def (Object.new).object_id; end
+ INPUT
+
+ assert_in_out_err([], <<-INPUT, [], %r"warning: redefining `__send__' may cause serious problems$")
+ $VERBOSE = false
+ def (Object.new).__send__; end
+ INPUT
+
+ bug10421 = '[ruby-dev:48691] [Bug #10421]'
+ assert_in_out_err([], <<-INPUT, ["1"], [], bug10421)
+ $VERBOSE = false
+ class C < BasicObject
+ def object_id; 1; end
+ end
+ puts C.new.object_id
+ INPUT
+ end
+
+ def test_remove_method
+ c = Class.new
+ c.freeze
+ assert_raise(RuntimeError) do
+ c.instance_eval { remove_method(:foo) }
+ end
+
+ c = Class.new do
+ def meth1; "meth" end
+ end
+ d = Class.new(c) do
+ alias meth2 meth1
+ end
+ o1 = c.new
+ assert_respond_to(o1, :meth1)
+ assert_equal("meth", o1.meth1)
+ o2 = d.new
+ assert_respond_to(o2, :meth1)
+ assert_equal("meth", o2.meth1)
+ assert_respond_to(o2, :meth2)
+ assert_equal("meth", o2.meth2)
+ d.class_eval do
+ remove_method :meth2
+ end
+ bug2202 = '[ruby-core:26074]'
+ assert_raise(NoMethodError, bug2202) {o2.meth2}
+
+ %w(object_id __send__ initialize).each do |m|
+ assert_in_out_err([], <<-INPUT, %w(:ok), %r"warning: removing `#{m}' may cause serious problems$")
+ $VERBOSE = false
+ begin
+ Class.new.instance_eval { remove_method(:#{m}) }
+ rescue NameError
+ p :ok
+ end
+ INPUT
+ end
+
+ m = "\u{30e1 30bd 30c3 30c9}"
+ c = Class.new
+ assert_raise_with_message(NameError, /#{m}/) do
+ c.class_eval {remove_method m}
+ end
+ c = Class.new {
+ define_method(m) {}
+ remove_method(m)
+ }
+ assert_raise_with_message(NameError, /#{m}/) do
+ c.class_eval {remove_method m}
+ end
+ end
+
+ def test_method_missing
+ assert_raise(ArgumentError) do
+ 1.instance_eval { method_missing }
+ end
+
+ c = Class.new
+ c.class_eval do
+ protected
+ def foo; end
+ end
+ assert_raise(NoMethodError) do
+ c.new.foo
+ end
+
+ assert_raise(NoMethodError) do
+ 1.instance_eval { method_missing(:method_missing) }
+ end
+
+ c.class_eval do
+ undef_method(:method_missing)
+ end
+
+ assert_raise(ArgumentError) do
+ c.new.method_missing
+ end
+
+ bug2494 = '[ruby-core:27219]'
+ c = Class.new do
+ def method_missing(meth, *args)
+ super
+ end
+ end
+ b = c.new
+ foo rescue nil
+ assert_nothing_raised(bug2494) {[b].flatten}
+ end
+
+ def test_respond_to_missing_string
+ c = Class.new do
+ def respond_to_missing?(id, priv)
+ !(id !~ /\Agadzoks\d+\z/) ^ priv
+ end
+ end
+ foo = c.new
+ assert_equal(false, foo.respond_to?("gadzooks16"))
+ assert_equal(true, foo.respond_to?("gadzooks17", true))
+ assert_equal(true, foo.respond_to?("gadzoks16"))
+ assert_equal(false, foo.respond_to?("gadzoks17", true))
+ end
+
+ def test_respond_to_missing
+ c = Class.new do
+ def respond_to_missing?(id, priv)
+ if id == :foobar
+ true
+ else
+ false
+ end
+ end
+ def method_missing(id, *args)
+ if id == :foobar
+ return [:foo, *args]
+ else
+ super
+ end
+ end
+ end
+
+ foo = c.new
+ assert_equal([:foo], foo.foobar);
+ assert_equal([:foo, 1], foo.foobar(1));
+ assert_equal([:foo, 1, 2, 3, 4, 5], foo.foobar(1, 2, 3, 4, 5));
+ assert_respond_to(foo, :foobar)
+ assert_not_respond_to(foo, :foobarbaz)
+ assert_raise(NoMethodError) do
+ foo.foobarbaz
+ end
+
+ foobar = foo.method(:foobar)
+ assert_equal(-1, foobar.arity);
+ assert_equal([:foo], foobar.call);
+ assert_equal([:foo, 1], foobar.call(1));
+ assert_equal([:foo, 1, 2, 3, 4, 5], foobar.call(1, 2, 3, 4, 5));
+ assert_equal(foobar, foo.method(:foobar))
+ assert_not_equal(foobar, c.new.method(:foobar))
+
+ c = Class.new(c)
+ assert_equal(false, c.method_defined?(:foobar))
+ assert_raise(NameError, '[ruby-core:25748]') do
+ c.instance_method(:foobar)
+ end
+
+ m = Module.new
+ assert_equal(false, m.method_defined?(:foobar))
+ assert_raise(NameError, '[ruby-core:25748]') do
+ m.instance_method(:foobar)
+ end
+ end
+
+ def test_implicit_respond_to
+ bug5158 = '[ruby-core:38799]'
+
+ p = Object.new
+
+ called = []
+ p.singleton_class.class_eval do
+ define_method(:to_ary) do
+ called << [:to_ary, bug5158]
+ end
+ end
+ [[p]].flatten
+ assert_equal([[:to_ary, bug5158]], called, bug5158)
+
+ called = []
+ p.singleton_class.class_eval do
+ define_method(:respond_to?) do |*a|
+ called << [:respond_to?, *a]
+ false
+ end
+ end
+ [[p]].flatten
+ assert_equal([[:respond_to?, :to_ary, true]], called, bug5158)
+ end
+
+ def test_implicit_respond_to_arity_1
+ p = Object.new
+
+ called = []
+ p.singleton_class.class_eval do
+ define_method(:respond_to?) do |a|
+ called << [:respond_to?, a]
+ false
+ end
+ end
+ [[p]].flatten
+ assert_equal([[:respond_to?, :to_ary]], called, '[bug:6000]')
+ end
+
+ def test_implicit_respond_to_arity_3
+ p = Object.new
+
+ called = []
+ p.singleton_class.class_eval do
+ define_method(:respond_to?) do |a, b, c|
+ called << [:respond_to?, a, b, c]
+ false
+ end
+ end
+
+ msg = 'respond_to? must accept 1 or 2 arguments (requires 3)'
+ assert_raise_with_message(ArgumentError, msg, '[bug:6000]') do
+ [[p]].flatten
+ end
+ end
+
+ def test_method_missing_passed_block
+ bug5731 = '[ruby-dev:44961]'
+
+ c = Class.new do
+ def method_missing(meth, *args) yield(meth, *args) end
+ end
+ a = c.new
+ result = nil
+ assert_nothing_raised(LocalJumpError, bug5731) do
+ a.foo {|x| result = x}
+ end
+ assert_equal(:foo, result, bug5731)
+ result = nil
+ e = a.enum_for(:foo)
+ assert_nothing_raised(LocalJumpError, bug5731) do
+ e.each {|x| result = x}
+ end
+ assert_equal(:foo, result, bug5731)
+
+ c = Class.new do
+ def respond_to_missing?(id, priv)
+ true
+ end
+ def method_missing(id, *args, &block)
+ return block.call(:foo, *args)
+ end
+ end
+ foo = c.new
+
+ result = nil
+ assert_nothing_raised(LocalJumpError, bug5731) do
+ foo.foobar {|x| result = x}
+ end
+ assert_equal(:foo, result, bug5731)
+ result = nil
+ assert_nothing_raised(LocalJumpError, bug5731) do
+ foo.enum_for(:foobar).each {|x| result = x}
+ end
+ assert_equal(:foo, result, bug5731)
+
+ result = nil
+ foobar = foo.method(:foobar)
+ foobar.call {|x| result = x}
+ assert_equal(:foo, result, bug5731)
+
+ result = nil
+ foobar = foo.method(:foobar)
+ foobar.enum_for(:call).each {|x| result = x}
+ assert_equal(:foo, result, bug5731)
+ end
+
+ def test_send_with_no_arguments
+ assert_raise(ArgumentError) { 1.send }
+ end
+
+ def test_send_with_block
+ x = :ng
+ 1.send(:times) { x = :ok }
+ assert_equal(:ok, x)
+
+ x = :ok
+ o = Object.new
+ def o.inspect
+ yield if block_given?
+ super
+ end
+ begin
+ nil.public_send(o) { x = :ng }
+ rescue TypeError
+ end
+ assert_equal(:ok, x)
+ end
+
+ def test_public_send
+ c = Class.new do
+ def pub
+ :ok
+ end
+
+ def invoke(m)
+ public_send(m)
+ end
+
+ protected
+ def prot
+ :ng
+ end
+
+ private
+ def priv
+ :ng
+ end
+ end.new
+ assert_equal(:ok, c.public_send(:pub))
+ assert_raise(NoMethodError) {c.public_send(:priv)}
+ assert_raise(NoMethodError) {c.public_send(:prot)}
+ assert_raise(NoMethodError) {c.invoke(:priv)}
+ bug7499 = '[ruby-core:50489]'
+ assert_raise(NoMethodError, bug7499) {c.invoke(:prot)}
+ end
+
+ def test_no_superclass_method
+ bug2312 = '[ruby-dev:39581]'
+
+ o = Object.new
+ e = assert_raise(NoMethodError) {
+ o.method(:__send__).call(:never_defined_test_no_superclass_method)
+ }
+ m1 = e.message
+ assert_no_match(/no superclass method/, m1, bug2312)
+ e = assert_raise(NoMethodError) {
+ o.method(:__send__).call(:never_defined_test_no_superclass_method)
+ }
+ assert_equal(m1, e.message, bug2312)
+ e = assert_raise(NoMethodError) {
+ o.never_defined_test_no_superclass_method
+ }
+ assert_equal(m1, e.message, bug2312)
+ end
+
+ def test_superclass_method
+ bug2312 = '[ruby-dev:39581]'
+ assert_in_out_err(["-e", "module Enumerable;undef min;end; (1..2).min{}"],
+ "", [], /no superclass method/, bug2312)
+ end
+
+ def test_specific_eval_with_wrong_arguments
+ assert_raise(ArgumentError) do
+ 1.instance_eval("foo") { foo }
+ end
+
+ assert_raise(ArgumentError) do
+ 1.instance_eval
+ end
+
+ assert_raise(ArgumentError) do
+ 1.instance_eval("", 1, 1, 1)
+ end
+ end
+
+ class InstanceExec
+ INSTANCE_EXEC = 123
+ end
+
+ def test_instance_exec
+ x = 1.instance_exec(42) {|a| self + a }
+ assert_equal(43, x)
+
+ x = "foo".instance_exec("bar") {|a| self + a }
+ assert_equal("foobar", x)
+
+ assert_raise(NameError) do
+ InstanceExec.new.instance_exec { INSTANCE_EXEC }
+ end
+ end
+
+ def test_extend
+ assert_raise(ArgumentError) do
+ 1.extend
+ end
+ end
+
+ def test_untrusted
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ obj = Object.new
+ assert_equal(false, obj.untrusted?)
+ assert_equal(false, obj.tainted?)
+ obj.untrust
+ assert_equal(true, obj.untrusted?)
+ assert_equal(true, obj.tainted?)
+ obj.trust
+ assert_equal(false, obj.untrusted?)
+ assert_equal(false, obj.tainted?)
+ obj.taint
+ assert_equal(true, obj.untrusted?)
+ assert_equal(true, obj.tainted?)
+ obj.untaint
+ assert_equal(false, obj.untrusted?)
+ assert_equal(false, obj.tainted?)
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ def test_to_s
+ x = Object.new
+ x.taint
+ s = x.to_s
+ assert_equal(true, s.tainted?)
+
+ x = eval(<<-EOS)
+ class ToS\u{3042}
+ new.to_s
+ end
+ EOS
+ assert_match(/\bToS\u{3042}:/, x)
+ end
+
+ def test_inspect
+ x = Object.new
+ assert_match(/\A#<Object:0x\h+>\z/, x.inspect)
+
+ x.instance_variable_set(:@ivar, :value)
+ assert_match(/\A#<Object:0x\h+ @ivar=:value>\z/, x.inspect)
+
+ x = Object.new
+ x.instance_variable_set(:@recur, x)
+ assert_match(/\A#<Object:0x\h+ @recur=#<Object:0x\h+ \.\.\.>>\z/, x.inspect)
+
+ x = Object.new
+ x.instance_variable_set(:@foo, "value")
+ x.instance_variable_set(:@bar, 42)
+ assert_match(/\A#<Object:0x\h+ (?:@foo="value", @bar=42|@bar=42, @foo="value")>\z/, x.inspect)
+
+ # #inspect does not call #to_s anymore
+ feature6130 = '[ruby-core:43238]'
+ x = Object.new
+ def x.to_s
+ "to_s"
+ end
+ assert_match(/\A#<Object:0x\h+>\z/, x.inspect, feature6130)
+
+ x = eval(<<-EOS)
+ class Inspect\u{3042}
+ new.inspect
+ end
+ EOS
+ assert_match(/\bInspect\u{3042}:/, x)
+
+ x = eval(<<-EOS)
+ class Inspect\u{3042}
+ def initialize
+ @\u{3044} = 42
+ end
+ new
+ end
+ EOS
+ assert_match(/\bInspect\u{3042}:.* @\u{3044}=42\b/, x.inspect)
+ x.instance_variable_set("@\u{3046}".encode(Encoding::EUC_JP), 6)
+ assert_match(/@\u{3046}=6\b/, x.inspect)
+ end
+
+ def test_singleton_class
+ x = Object.new
+ xs = class << x; self; end
+ assert_equal(xs, x.singleton_class)
+
+ y = Object.new
+ ys = y.singleton_class
+ assert_equal(class << y; self; end, ys)
+
+ assert_equal(NilClass, nil.singleton_class)
+ assert_equal(TrueClass, true.singleton_class)
+ assert_equal(FalseClass, false.singleton_class)
+
+ assert_raise(TypeError) do
+ 123.singleton_class
+ end
+ assert_raise(TypeError) do
+ :foo.singleton_class
+ end
+ end
+
+ def test_redef_method_missing
+ bug5473 = '[ruby-core:40287]'
+ ['ArgumentError.new("bug5473")', 'ArgumentError, "bug5473"', '"bug5473"'].each do |code|
+ out, err, status = EnvUtil.invoke_ruby([], <<-SRC, true, true)
+ class ::Object
+ def method_missing(m, *a, &b)
+ raise #{code}
+ end
+ end
+
+ p((1.foo rescue $!))
+ SRC
+ assert_send([status, :success?], bug5473)
+ assert_equal("", err, bug5473)
+ assert_equal((eval("raise #{code}") rescue $!.inspect), out.chomp, bug5473)
+ end
+ end
+
+ def assert_not_initialize_copy
+ a = yield
+ b = yield
+ assert_nothing_raised("copy") {a.instance_eval {initialize_copy(b)}}
+ c = a.dup.freeze
+ assert_raise(RuntimeError, "frozen") {c.instance_eval {initialize_copy(b)}}
+ d = a.dup.trust
+ [a, b, c, d]
+ end
+
+ def test_bad_initialize_copy
+ assert_not_initialize_copy {Object.new}
+ assert_not_initialize_copy {[].to_enum}
+ assert_not_initialize_copy {Enumerator::Generator.new {}}
+ assert_not_initialize_copy {Enumerator::Yielder.new {}}
+ assert_not_initialize_copy {File.stat(__FILE__)}
+ assert_not_initialize_copy {open(__FILE__)}.each(&:close)
+ assert_not_initialize_copy {ARGF.class.new}
+ assert_not_initialize_copy {Random.new}
+ assert_not_initialize_copy {//}
+ assert_not_initialize_copy {/.*/.match("foo")}
+ st = Struct.new(:foo)
+ assert_not_initialize_copy {st.new}
+ end
+
+ def test_type_error_message
+ _issue = "Bug #7539"
+ assert_raise_with_message(TypeError, "can't convert Array into Integer") {Integer([42])}
+ assert_raise_with_message(TypeError, 'no implicit conversion of Array into Integer') {[].first([42])}
+ end
+
+ def test_copied_ivar_memory_leak
+ bug10191 = '[ruby-core:64700] [Bug #10191]'
+ assert_no_memory_leak([], <<-"end;", <<-"end;", bug10191, timeout: 60, limit: 1.8)
+ def (a = Object.new).set; @v = nil; end
+ num = 500_000
+ end;
+ num.times {a.clone.set}
+ end;
+ end
+end
diff --git a/jni/ruby/test/ruby/test_objectspace.rb b/jni/ruby/test/ruby/test_objectspace.rb
new file mode 100644
index 0000000..d519041
--- /dev/null
+++ b/jni/ruby/test/ruby/test_objectspace.rb
@@ -0,0 +1,112 @@
+require 'test/unit'
+
+class TestObjectSpace < Test::Unit::TestCase
+ def self.deftest_id2ref(obj)
+ /:(\d+)/ =~ caller[0]
+ file = $`
+ line = $1.to_i
+ code = <<"End"
+ define_method("test_id2ref_#{line}") {\
+ o = ObjectSpace._id2ref(obj.object_id);\
+ assert_same(obj, o, "didn't round trip: \#{obj.inspect}");\
+ }
+End
+ eval code, binding, file, line
+ end
+
+ deftest_id2ref(-0x4000000000000001)
+ deftest_id2ref(-0x4000000000000000)
+ deftest_id2ref(-0x40000001)
+ deftest_id2ref(-0x40000000)
+ deftest_id2ref(-1)
+ deftest_id2ref(0)
+ deftest_id2ref(1)
+ deftest_id2ref(0x3fffffff)
+ deftest_id2ref(0x40000000)
+ deftest_id2ref(0x3fffffffffffffff)
+ deftest_id2ref(0x4000000000000000)
+ deftest_id2ref(:a)
+ deftest_id2ref(:abcdefghijilkjl)
+ deftest_id2ref(:==)
+ deftest_id2ref(Object.new)
+ deftest_id2ref(self)
+ deftest_id2ref(true)
+ deftest_id2ref(false)
+ deftest_id2ref(nil)
+
+ def test_count_objects
+ h = {}
+ ObjectSpace.count_objects(h)
+ assert_kind_of(Hash, h)
+ assert_empty(h.keys.delete_if {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
+ assert_empty(h.values.delete_if {|x| x.is_a?(Integer) })
+
+ h = ObjectSpace.count_objects
+ assert_kind_of(Hash, h)
+ assert_empty(h.keys.delete_if {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
+ assert_empty(h.values.delete_if {|x| x.is_a?(Integer) })
+
+ assert_raise(TypeError) { ObjectSpace.count_objects(1) }
+
+ h0 = {:T_FOO=>1000}
+ h = ObjectSpace.count_objects(h0)
+ assert_same(h0, h)
+ assert_equal(0, h0[:T_FOO])
+ end
+
+ def test_finalizer
+ assert_in_out_err(["-e", <<-END], "", %w(:ok :ok :ok :ok), [])
+ a = []
+ ObjectSpace.define_finalizer(a) { p :ok }
+ b = a.dup
+ ObjectSpace.define_finalizer(a) { p :ok }
+ !b
+ END
+ assert_raise(ArgumentError) { ObjectSpace.define_finalizer([], Object.new) }
+
+ code = proc do |priv|
+ <<-"CODE"
+ fin = Object.new
+ class << fin
+ #{priv}def call(id)
+ puts "finalized"
+ end
+ end
+ ObjectSpace.define_finalizer([], fin)
+ CODE
+ end
+ assert_in_out_err([], code[""], ["finalized"])
+ assert_in_out_err([], code["private "], ["finalized"])
+ c = EnvUtil.labeled_class("C\u{3042}").new
+ o = Object.new
+ assert_raise_with_message(ArgumentError, /C\u{3042}/) {
+ ObjectSpace.define_finalizer(o, c)
+ }
+ end
+
+ def test_each_object
+ assert_separately([], <<-End)
+ GC.disable
+ eval('begin; 1.times{}; rescue; ensure; end')
+ arys = []
+ ObjectSpace.each_object(Array){|ary|
+ arys << ary
+ }
+ GC.enable
+ arys.each{|ary|
+ begin
+ assert_equal(String, ary.inspect.class) # should not cause SEGV
+ rescue RuntimeError
+ # rescue "can't modify frozen File" error.
+ end
+ }
+ End
+ end
+
+ def test_each_object_recursive_key
+ assert_normal_exit(<<-'end;', '[ruby-core:66742] [Bug #10579]')
+ h = {["foo"]=>nil}
+ p Thread.current[:__recursive_key__]
+ end;
+ end
+end
diff --git a/jni/ruby/test/ruby/test_optimization.rb b/jni/ruby/test/ruby/test_optimization.rb
new file mode 100644
index 0000000..4a5484e
--- /dev/null
+++ b/jni/ruby/test/ruby/test_optimization.rb
@@ -0,0 +1,292 @@
+require 'test/unit'
+
+class TestRubyOptimization < Test::Unit::TestCase
+
+ BIGNUM_POS_MIN_32 = 1073741824 # 2 ** 30
+ if BIGNUM_POS_MIN_32.kind_of?(Fixnum)
+ FIXNUM_MAX = 4611686018427387903 # 2 ** 62 - 1
+ else
+ FIXNUM_MAX = 1073741823 # 2 ** 30 - 1
+ end
+
+ BIGNUM_NEG_MAX_32 = -1073741825 # -2 ** 30 - 1
+ if BIGNUM_NEG_MAX_32.kind_of?(Fixnum)
+ FIXNUM_MIN = -4611686018427387904 # -2 ** 62
+ else
+ FIXNUM_MIN = -1073741824 # -2 ** 30
+ end
+
+ def assert_redefine_method(klass, method, code, msg = nil)
+ assert_separately([], <<-"end;")# do
+ class #{klass}
+ undef #{method}
+ def #{method}(*args)
+ args[0]
+ end
+ end
+ #{code}
+ end;
+ end
+
+ def test_fixnum_plus
+ a, b = 1, 2
+ assert_equal 3, a + b
+ assert_instance_of Fixnum, FIXNUM_MAX
+ assert_instance_of Bignum, FIXNUM_MAX + 1
+
+ assert_equal 21, 10 + 11
+ assert_redefine_method('Fixnum', '+', 'assert_equal 11, 10 + 11')
+ end
+
+ def test_fixnum_minus
+ assert_equal 5, 8 - 3
+ assert_instance_of Fixnum, FIXNUM_MIN
+ assert_instance_of Bignum, FIXNUM_MIN - 1
+
+ assert_equal 5, 8 - 3
+ assert_redefine_method('Fixnum', '-', 'assert_equal 3, 8 - 3')
+ end
+
+ def test_fixnum_mul
+ assert_equal 15, 3 * 5
+ assert_redefine_method('Fixnum', '*', 'assert_equal 5, 3 * 5')
+ end
+
+ def test_fixnum_div
+ assert_equal 3, 15 / 5
+ assert_redefine_method('Fixnum', '/', 'assert_equal 5, 15 / 5')
+ end
+
+ def test_fixnum_mod
+ assert_equal 1, 8 % 7
+ assert_redefine_method('Fixnum', '%', 'assert_equal 7, 8 % 7')
+ end
+
+ def test_float_plus
+ assert_equal 4.0, 2.0 + 2.0
+ assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
+ end
+
+ def test_float_minus
+ assert_equal 4.0, 2.0 + 2.0
+ assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
+ end
+
+ def test_float_mul
+ assert_equal 29.25, 4.5 * 6.5
+ assert_redefine_method('Float', '*', 'assert_equal 6.5, 4.5 * 6.5')
+ end
+
+ def test_float_div
+ assert_in_delta 0.63063063063063063, 4.2 / 6.66
+ assert_redefine_method('Float', '/', 'assert_equal 6.66, 4.2 / 6.66', "[Bug #9238]")
+ end
+
+ def test_string_length
+ assert_equal 6, "string".length
+ assert_redefine_method('String', 'length', 'assert_nil "string".length')
+ end
+
+ def test_string_size
+ assert_equal 6, "string".size
+ assert_redefine_method('String', 'size', 'assert_nil "string".size')
+ end
+
+ def test_string_empty?
+ assert_equal true, "".empty?
+ assert_equal false, "string".empty?
+ assert_redefine_method('String', 'empty?', 'assert_nil "string".empty?')
+ end
+
+ def test_string_plus
+ assert_equal "", "" + ""
+ assert_equal "x", "x" + ""
+ assert_equal "x", "" + "x"
+ assert_equal "ab", "a" + "b"
+ assert_redefine_method('String', '+', 'assert_equal "b", "a" + "b"')
+ end
+
+ def test_string_succ
+ assert_equal 'b', 'a'.succ
+ assert_equal 'B', 'A'.succ
+ end
+
+ def test_string_format
+ assert_equal '2', '%d' % 2
+ assert_redefine_method('String', '%', 'assert_equal 2, "%d" % 2')
+ end
+
+ def test_string_freeze
+ assert_equal "foo", "foo".freeze
+ assert_equal "foo".freeze.object_id, "foo".freeze.object_id
+ assert_redefine_method('String', 'freeze', 'assert_nil "foo".freeze')
+ end
+
+ def test_string_eq_neq
+ %w(== !=).each do |m|
+ assert_redefine_method('String', m, <<-end)
+ assert_equal :b, ("a" #{m} "b").to_sym
+ b = 'b'
+ assert_equal :b, ("a" #{m} b).to_sym
+ assert_equal :b, (b #{m} "b").to_sym
+ end
+ end
+ end
+
+ def test_string_ltlt
+ assert_equal "", "" << ""
+ assert_equal "x", "x" << ""
+ assert_equal "x", "" << "x"
+ assert_equal "ab", "a" << "b"
+ assert_redefine_method('String', '<<', 'assert_equal "b", "a" << "b"')
+ end
+
+ def test_array_plus
+ assert_equal [1,2], [1]+[2]
+ assert_redefine_method('Array', '+', 'assert_equal [2], [1]+[2]')
+ end
+
+ def test_array_minus
+ assert_equal [2], [1,2] - [1]
+ assert_redefine_method('Array', '-', 'assert_equal [1], [1,2]-[1]')
+ end
+
+ def test_array_length
+ assert_equal 0, [].length
+ assert_equal 3, [1,2,3].length
+ assert_redefine_method('Array', 'length', 'assert_nil([].length); assert_nil([1,2,3].length)')
+ end
+
+ def test_array_empty?
+ assert_equal true, [].empty?
+ assert_equal false, [1,2,3].empty?
+ assert_redefine_method('Array', 'empty?', 'assert_nil([].empty?); assert_nil([1,2,3].empty?)')
+ end
+
+ def test_hash_length
+ assert_equal 0, {}.length
+ assert_equal 1, {1=>1}.length
+ assert_redefine_method('Hash', 'length', 'assert_nil({}.length); assert_nil({1=>1}.length)')
+ end
+
+ def test_hash_empty?
+ assert_equal true, {}.empty?
+ assert_equal false, {1=>1}.empty?
+ assert_redefine_method('Hash', 'empty?', 'assert_nil({}.empty?); assert_nil({1=>1}.empty?)')
+ end
+
+ def test_hash_aref_with
+ h = { "foo" => 1 }
+ assert_equal 1, h["foo"]
+ assert_redefine_method('Hash', '[]', <<-end)
+ h = { "foo" => 1 }
+ assert_equal "foo", h["foo"]
+ end
+ end
+
+ def test_hash_aset_with
+ h = {}
+ assert_equal 1, h["foo"] = 1
+ assert_redefine_method('Hash', '[]=', <<-end)
+ h = {}
+ assert_equal 1, h["foo"] = 1, "assignment always returns value set"
+ assert_nil h["foo"]
+ end
+ end
+
+ class MyObj
+ def ==(other)
+ true
+ end
+ end
+
+ def test_eq
+ assert_equal true, nil == nil
+ assert_equal true, 1 == 1
+ assert_equal true, 'string' == 'string'
+ assert_equal true, 1 == MyObj.new
+ assert_equal false, nil == MyObj.new
+ assert_equal true, MyObj.new == 1
+ assert_equal true, MyObj.new == nil
+ end
+
+ def test_tailcall
+ bug4082 = '[ruby-core:33289]'
+
+ option = {
+ tailcall_optimization: true,
+ trace_instruction: false,
+ }
+ RubyVM::InstructionSequence.new(<<-EOF, "Bug#4082", bug4082, nil, option).eval
+ class #{self.class}::Tailcall
+ def fact_helper(n, res)
+ if n == 1
+ res
+ else
+ fact_helper(n - 1, n * res)
+ end
+ end
+ def fact(n)
+ fact_helper(n, 1)
+ end
+ end
+ EOF
+ assert_equal(9131, Tailcall.new.fact(3000).to_s.size, bug4082)
+ end
+
+ def test_tailcall_with_block
+ bug6901 = '[ruby-dev:46065]'
+
+ option = {
+ tailcall_optimization: true,
+ trace_instruction: false,
+ }
+ RubyVM::InstructionSequence.new(<<-EOF, "Bug#6901", bug6901, nil, option).eval
+ def identity(val)
+ val
+ end
+
+ def delay
+ -> {
+ identity(yield)
+ }
+ end
+ EOF
+ assert_equal(123, delay { 123 }.call, bug6901)
+ end
+
+ class Bug10557
+ def [](_)
+ block_given?
+ end
+
+ def []=(_, _)
+ block_given?
+ end
+ end
+
+ def test_block_given_aset_aref
+ bug10557 = '[ruby-core:66595]'
+ assert_equal(true, Bug10557.new.[](nil){}, bug10557)
+ assert_equal(true, Bug10557.new.[](0){}, bug10557)
+ assert_equal(true, Bug10557.new.[](false){}, bug10557)
+ assert_equal(true, Bug10557.new.[](''){}, bug10557)
+ assert_equal(true, Bug10557.new.[]=(nil, 1){}, bug10557)
+ assert_equal(true, Bug10557.new.[]=(0, 1){}, bug10557)
+ assert_equal(true, Bug10557.new.[]=(false, 1){}, bug10557)
+ assert_equal(true, Bug10557.new.[]=('', 1){}, bug10557)
+ end
+
+ def test_string_freeze_block
+ assert_separately([], <<-"end;")# do
+ class String
+ undef freeze
+ def freeze
+ block_given?
+ end
+ end
+ assert_equal(true, "block".freeze {})
+ assert_equal(false, "block".freeze)
+ end;
+ end
+end
diff --git a/jni/ruby/test/ruby/test_pack.rb b/jni/ruby/test/ruby/test_pack.rb
new file mode 100644
index 0000000..5d2b656
--- /dev/null
+++ b/jni/ruby/test/ruby/test_pack.rb
@@ -0,0 +1,730 @@
+# coding: US-ASCII
+require 'test/unit'
+
+class TestPack < Test::Unit::TestCase
+ def test_pack
+ $format = "c2x5CCxsdils_l_a6";
+ # Need the expression in here to force ary[5] to be numeric. This avoids
+ # test2 failing because ary2 goes str->numeric->str and ary does not.
+ ary = [1,-100,127,128,32767,987.654321098 / 100.0,12345,123456,-32767,-123456,"abcdef"]
+ $x = ary.pack($format)
+ ary2 = $x.unpack($format)
+
+ assert_equal(ary.length, ary2.length)
+ assert_equal(ary.join(':'), ary2.join(':'))
+ assert_match(/def/, $x)
+
+ $x = [-1073741825]
+ assert_equal($x, $x.pack("q").unpack("q"))
+
+ $x = [-1]
+ assert_equal($x, $x.pack("l").unpack("l"))
+ end
+
+ def test_pack_n
+ assert_equal "\000\000", [0].pack('n')
+ assert_equal "\000\001", [1].pack('n')
+ assert_equal "\000\002", [2].pack('n')
+ assert_equal "\000\003", [3].pack('n')
+ assert_equal "\377\376", [65534].pack('n')
+ assert_equal "\377\377", [65535].pack('n')
+
+ assert_equal "\200\000", [2**15].pack('n')
+ assert_equal "\177\377", [-2**15-1].pack('n')
+ assert_equal "\377\377", [-1].pack('n')
+
+ assert_equal "\000\001\000\001", [1,1].pack('n*')
+ assert_equal "\000\001\000\001\000\001", [1,1,1].pack('n*')
+ end
+
+ def test_unpack_n
+ assert_equal 1, "\000\001".unpack('n')[0]
+ assert_equal 2, "\000\002".unpack('n')[0]
+ assert_equal 3, "\000\003".unpack('n')[0]
+ assert_equal 65535, "\377\377".unpack('n')[0]
+ assert_equal [1,1], "\000\001\000\001".unpack('n*')
+ assert_equal [1,1,1], "\000\001\000\001\000\001".unpack('n*')
+ end
+
+ def test_pack_N
+ assert_equal "\000\000\000\000", [0].pack('N')
+ assert_equal "\000\000\000\001", [1].pack('N')
+ assert_equal "\000\000\000\002", [2].pack('N')
+ assert_equal "\000\000\000\003", [3].pack('N')
+ assert_equal "\377\377\377\376", [4294967294].pack('N')
+ assert_equal "\377\377\377\377", [4294967295].pack('N')
+
+ assert_equal "\200\000\000\000", [2**31].pack('N')
+ assert_equal "\177\377\377\377", [-2**31-1].pack('N')
+ assert_equal "\377\377\377\377", [-1].pack('N')
+
+ assert_equal "\000\000\000\001\000\000\000\001", [1,1].pack('N*')
+ assert_equal "\000\000\000\001\000\000\000\001\000\000\000\001", [1,1,1].pack('N*')
+ end
+
+ def test_unpack_N
+ assert_equal 1, "\000\000\000\001".unpack('N')[0]
+ assert_equal 2, "\000\000\000\002".unpack('N')[0]
+ assert_equal 3, "\000\000\000\003".unpack('N')[0]
+ assert_equal 4294967295, "\377\377\377\377".unpack('N')[0]
+ assert_equal [1,1], "\000\000\000\001\000\000\000\001".unpack('N*')
+ assert_equal [1,1,1], "\000\000\000\001\000\000\000\001\000\000\000\001".unpack('N*')
+ end
+
+ def _integer_big_endian(mod='')
+ assert_equal("\x01\x02", [0x0102].pack("s"+mod))
+ assert_equal("\x01\x02", [0x0102].pack("S"+mod))
+ assert_equal("\x01\x02\x03\x04", [0x01020304].pack("l"+mod))
+ assert_equal("\x01\x02\x03\x04", [0x01020304].pack("L"+mod))
+ assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("q"+mod))
+ assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("Q"+mod))
+ assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("s!"+mod))
+ assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("S!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("l!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("L!"+mod))
+ %w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
+ fmt += mod
+ nuls = [0].pack(fmt)
+ v = 0
+ s = "".force_encoding("ascii-8bit")
+ nuls.bytesize.times {|i|
+ j = i + 40
+ v = v * 256 + j
+ s << [j].pack("C")
+ }
+ assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
+ assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
+ s2 = s+s
+ fmt2 = fmt+"*"
+ assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
+ }
+ end
+
+ def _integer_little_endian(mod='')
+ assert_equal("\x02\x01", [0x0102].pack("s"+mod))
+ assert_equal("\x02\x01", [0x0102].pack("S"+mod))
+ assert_equal("\x04\x03\x02\x01", [0x01020304].pack("l"+mod))
+ assert_equal("\x04\x03\x02\x01", [0x01020304].pack("L"+mod))
+ assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("q"+mod))
+ assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("Q"+mod))
+ assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("s!"+mod))
+ assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("S!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("l!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("L!"+mod))
+ %w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
+ fmt += mod
+ nuls = [0].pack(fmt)
+ v = 0
+ s = "".force_encoding("ascii-8bit")
+ nuls.bytesize.times {|i|
+ j = i+40
+ v = v * 256 + j
+ s << [j].pack("C")
+ }
+ s.reverse!
+ assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
+ assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
+ s2 = s+s
+ fmt2 = fmt+"*"
+ assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
+ }
+ end
+
+ def test_integer_endian
+ s = [1].pack("s")
+ assert_include(["\0\1", "\1\0"], s)
+ if s == "\0\1"
+ _integer_big_endian()
+ else
+ _integer_little_endian()
+ end
+ assert_equal("\x01\x02\x02\x01", [0x0102,0x0102].pack("s>s<"))
+ assert_equal([0x0102,0x0102], "\x01\x02\x02\x01".unpack("s>s<"))
+ end
+
+ def test_integer_endian_explicit
+ _integer_big_endian('>')
+ _integer_little_endian('<')
+ end
+
+ def test_pack_U
+ assert_raise(RangeError) { [-0x40000001].pack("U") }
+ assert_raise(RangeError) { [-0x40000000].pack("U") }
+ assert_raise(RangeError) { [-1].pack("U") }
+ assert_equal "\000", [0].pack("U")
+ assert_equal "\374\277\277\277\277\277".force_encoding(Encoding::UTF_8), [0x3fffffff].pack("U")
+ assert_equal "\375\200\200\200\200\200".force_encoding(Encoding::UTF_8), [0x40000000].pack("U")
+ assert_equal "\375\277\277\277\277\277".force_encoding(Encoding::UTF_8), [0x7fffffff].pack("U")
+ assert_raise(RangeError) { [0x80000000].pack("U") }
+ assert_raise(RangeError) { [0x100000000].pack("U") }
+ end
+
+ def test_pack_P
+ a = ["abc"]
+ assert_equal a, a.pack("P").unpack("P*")
+ assert_equal "a", a.pack("P").unpack("P")[0]
+ assert_equal a, a.pack("P").freeze.unpack("P*")
+ assert_raise(ArgumentError) { (a.pack("P") + "").unpack("P*") }
+ end
+
+ def test_pack_p
+ a = ["abc"]
+ assert_equal a, a.pack("p").unpack("p*")
+ assert_equal a[0], a.pack("p").unpack("p")[0]
+ assert_equal a, a.pack("p").freeze.unpack("p*")
+ assert_raise(ArgumentError) { (a.pack("p") + "").unpack("p*") }
+ assert_equal a, (a.pack("p") << "d").unpack("p*")
+ end
+
+ def test_format_string_modified
+ fmt = "CC"
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:to_int) { fmt.clear; 0 }
+ end
+ assert_raise(RuntimeError) do
+ [o, o].pack(fmt)
+ end
+ end
+
+ def test_comment
+ assert_equal("\0\1", [0,1].pack(" C #foo \n C "))
+ assert_equal([0,1], "\0\1".unpack(" C #foo \n C "))
+ end
+
+ def test_illegal_bang
+ assert_raise(ArgumentError) { [].pack("a!") }
+ assert_raise(ArgumentError) { "".unpack("a!") }
+ end
+
+ def test_pack_unpack_aA
+ assert_equal("f", ["foo"].pack("A"))
+ assert_equal("f", ["foo"].pack("a"))
+ assert_equal("foo", ["foo"].pack("A*"))
+ assert_equal("foo", ["foo"].pack("a*"))
+ assert_equal("fo", ["foo"].pack("A2"))
+ assert_equal("fo", ["foo"].pack("a2"))
+ assert_equal("foo ", ["foo"].pack("A4"))
+ assert_equal("foo\0", ["foo"].pack("a4"))
+ assert_equal(" ", [nil].pack("A"))
+ assert_equal("\0", [nil].pack("a"))
+ assert_equal("", [nil].pack("A*"))
+ assert_equal("", [nil].pack("a*"))
+ assert_equal(" ", [nil].pack("A2"))
+ assert_equal("\0\0", [nil].pack("a2"))
+
+ assert_equal("foo" + "\0" * 27, ["foo"].pack("a30"))
+
+ assert_equal(["f"], "foo\0".unpack("A"))
+ assert_equal(["f"], "foo\0".unpack("a"))
+ assert_equal(["foo"], "foo\0".unpack("A4"))
+ assert_equal(["foo\0"], "foo\0".unpack("a4"))
+ assert_equal(["foo"], "foo ".unpack("A4"))
+ assert_equal(["foo "], "foo ".unpack("a4"))
+ assert_equal(["foo"], "foo".unpack("A4"))
+ assert_equal(["foo"], "foo".unpack("a4"))
+ end
+
+ def test_pack_unpack_Z
+ assert_equal("f", ["foo"].pack("Z"))
+ assert_equal("foo\0", ["foo"].pack("Z*"))
+ assert_equal("fo", ["foo"].pack("Z2"))
+ assert_equal("foo\0\0", ["foo"].pack("Z5"))
+ assert_equal("\0", [nil].pack("Z"))
+ assert_equal("\0", [nil].pack("Z*"))
+ assert_equal("\0\0", [nil].pack("Z2"))
+
+ assert_equal(["f"], "foo\0".unpack("Z"))
+ assert_equal(["foo"], "foo".unpack("Z*"))
+ assert_equal(["foo"], "foo\0".unpack("Z*"))
+ assert_equal(["foo"], "foo".unpack("Z5"))
+ end
+
+ def test_pack_unpack_bB
+ assert_equal("\xff\x00", ["1111111100000000"].pack("b*"))
+ assert_equal("\x01\x02", ["1000000001000000"].pack("b*"))
+ assert_equal("", ["1"].pack("b0"))
+ assert_equal("\x01", ["1"].pack("b1"))
+ assert_equal("\x01\x00", ["1"].pack("b2"))
+ assert_equal("\x01\x00", ["1"].pack("b3"))
+ assert_equal("\x01\x00\x00", ["1"].pack("b4"))
+ assert_equal("\x01\x00\x00", ["1"].pack("b5"))
+ assert_equal("\x01\x00\x00\x00", ["1"].pack("b6"))
+
+ assert_equal("\xff\x00", ["1111111100000000"].pack("B*"))
+ assert_equal("\x01\x02", ["0000000100000010"].pack("B*"))
+ assert_equal("", ["1"].pack("B0"))
+ assert_equal("\x80", ["1"].pack("B1"))
+ assert_equal("\x80\x00", ["1"].pack("B2"))
+ assert_equal("\x80\x00", ["1"].pack("B3"))
+ assert_equal("\x80\x00\x00", ["1"].pack("B4"))
+ assert_equal("\x80\x00\x00", ["1"].pack("B5"))
+ assert_equal("\x80\x00\x00\x00", ["1"].pack("B6"))
+
+ assert_equal(["1111111100000000"], "\xff\x00".unpack("b*"))
+ assert_equal(["1000000001000000"], "\x01\x02".unpack("b*"))
+ assert_equal([""], "".unpack("b0"))
+ assert_equal(["1"], "\x01".unpack("b1"))
+ assert_equal(["10"], "\x01".unpack("b2"))
+ assert_equal(["100"], "\x01".unpack("b3"))
+
+ assert_equal(["1111111100000000"], "\xff\x00".unpack("B*"))
+ assert_equal(["0000000100000010"], "\x01\x02".unpack("B*"))
+ assert_equal([""], "".unpack("B0"))
+ assert_equal(["1"], "\x80".unpack("B1"))
+ assert_equal(["10"], "\x80".unpack("B2"))
+ assert_equal(["100"], "\x80".unpack("B3"))
+
+ assert_equal(Encoding::US_ASCII, "\xff\x00".unpack("b*")[0].encoding)
+ assert_equal(Encoding::US_ASCII, "\xff\x00".unpack("B*")[0].encoding)
+ end
+
+ def test_pack_unpack_hH
+ assert_equal("\x01\xfe", ["10ef"].pack("h*"))
+ assert_equal("", ["10ef"].pack("h0"))
+ assert_equal("\x01\x0e", ["10ef"].pack("h3"))
+ assert_equal("\x01\xfe\x0", ["10ef"].pack("h5"))
+ assert_equal("\xff\x0f", ["fff"].pack("h3"))
+ assert_equal("\xff\x0f", ["fff"].pack("h4"))
+ assert_equal("\xff\x0f\0", ["fff"].pack("h5"))
+ assert_equal("\xff\x0f\0", ["fff"].pack("h6"))
+ assert_equal("\xff\x0f\0\0", ["fff"].pack("h7"))
+ assert_equal("\xff\x0f\0\0", ["fff"].pack("h8"))
+
+ assert_equal("\x10\xef", ["10ef"].pack("H*"))
+ assert_equal("", ["10ef"].pack("H0"))
+ assert_equal("\x10\xe0", ["10ef"].pack("H3"))
+ assert_equal("\x10\xef\x0", ["10ef"].pack("H5"))
+ assert_equal("\xff\xf0", ["fff"].pack("H3"))
+ assert_equal("\xff\xf0", ["fff"].pack("H4"))
+ assert_equal("\xff\xf0\0", ["fff"].pack("H5"))
+ assert_equal("\xff\xf0\0", ["fff"].pack("H6"))
+ assert_equal("\xff\xf0\0\0", ["fff"].pack("H7"))
+ assert_equal("\xff\xf0\0\0", ["fff"].pack("H8"))
+
+ assert_equal(["10ef"], "\x01\xfe".unpack("h*"))
+ assert_equal([""], "\x01\xfe".unpack("h0"))
+ assert_equal(["1"], "\x01\xfe".unpack("h1"))
+ assert_equal(["10"], "\x01\xfe".unpack("h2"))
+ assert_equal(["10e"], "\x01\xfe".unpack("h3"))
+ assert_equal(["10ef"], "\x01\xfe".unpack("h4"))
+ assert_equal(["10ef"], "\x01\xfe".unpack("h5"))
+
+ assert_equal(["10ef"], "\x10\xef".unpack("H*"))
+ assert_equal([""], "\x10\xef".unpack("H0"))
+ assert_equal(["1"], "\x10\xef".unpack("H1"))
+ assert_equal(["10"], "\x10\xef".unpack("H2"))
+ assert_equal(["10e"], "\x10\xef".unpack("H3"))
+ assert_equal(["10ef"], "\x10\xef".unpack("H4"))
+ assert_equal(["10ef"], "\x10\xef".unpack("H5"))
+
+ assert_equal(Encoding::US_ASCII, "\x10\xef".unpack("h*")[0].encoding)
+ assert_equal(Encoding::US_ASCII, "\x10\xef".unpack("H*")[0].encoding)
+ end
+
+ def test_pack_unpack_cC
+ assert_equal("\0\1\xff", [0, 1, 255].pack("c*"))
+ assert_equal("\0\1\xff", [0, 1, -1].pack("c*"))
+
+ assert_equal("\0\1\xff", [0, 1, 255].pack("C*"))
+ assert_equal("\0\1\xff", [0, 1, -1].pack("C*"))
+
+ assert_equal([0, 1, -1], "\0\1\xff".unpack("c*"))
+
+ assert_equal([0, 1, 255], "\0\1\xff".unpack("C*"))
+ end
+
+ def test_pack_unpack_sS
+ s1 = [513, -514].pack("s*")
+ s2 = [513, 65022].pack("S*")
+ assert_equal(s1, s2)
+ assert_equal([513, -514], s2.unpack("s*"))
+ assert_equal([513, 65022], s1.unpack("S*"))
+
+ s1 = [513, -514].pack("s!*")
+ s2 = [513, 65022].pack("S!*")
+ assert_equal(s1, s2)
+ assert_equal([513, -514], s2.unpack("s!*"))
+ assert_equal([513, 65022], s1.unpack("S!*"))
+
+ assert_equal(2, [1].pack("s").bytesize)
+ assert_equal(2, [1].pack("S").bytesize)
+ assert_operator(2, :<=, [1].pack("s!").bytesize)
+ assert_operator(2, :<=, [1].pack("S!").bytesize)
+ end
+
+ def test_pack_unpack_iI
+ s1 = [67305985, -50462977].pack("i*")
+ s2 = [67305985, 4244504319].pack("I*")
+ assert_equal(s1, s2)
+ assert_equal([67305985, -50462977], s2.unpack("i*"))
+ assert_equal([67305985, 4244504319], s1.unpack("I*"))
+
+ s1 = [67305985, -50462977].pack("i!*")
+ s2 = [67305985, 4244504319].pack("I!*")
+ assert_equal([67305985, -50462977], s1.unpack("i!*"))
+ assert_equal([67305985, 4244504319], s2.unpack("I!*"))
+
+ assert_operator(4, :<=, [1].pack("i").bytesize)
+ assert_operator(4, :<=, [1].pack("I").bytesize)
+ assert_operator(4, :<=, [1].pack("i!").bytesize)
+ assert_operator(4, :<=, [1].pack("I!").bytesize)
+ end
+
+ def test_pack_unpack_lL
+ s1 = [67305985, -50462977].pack("l*")
+ s2 = [67305985, 4244504319].pack("L*")
+ assert_equal(s1, s2)
+ assert_equal([67305985, -50462977], s2.unpack("l*"))
+ assert_equal([67305985, 4244504319], s1.unpack("L*"))
+
+ s1 = [67305985, -50462977].pack("l!*")
+ s2 = [67305985, 4244504319].pack("L!*")
+ assert_equal([67305985, -50462977], s1.unpack("l!*"))
+ assert_equal([67305985, 4244504319], s2.unpack("L!*"))
+
+ assert_equal(4, [1].pack("l").bytesize)
+ assert_equal(4, [1].pack("L").bytesize)
+ assert_operator(4, :<=, [1].pack("l!").bytesize)
+ assert_operator(4, :<=, [1].pack("L!").bytesize)
+ end
+
+ def test_pack_unpack_qQ
+ s1 = [578437695752307201, -506097522914230529].pack("q*")
+ s2 = [578437695752307201, 17940646550795321087].pack("Q*")
+ assert_equal(s1, s2)
+ assert_equal([578437695752307201, -506097522914230529], s2.unpack("q*"))
+ assert_equal([578437695752307201, 17940646550795321087], s1.unpack("Q*"))
+
+ # Note: q! and Q! should not work on platform which has no long long type.
+ # Is there a such platform now?
+ s1 = [578437695752307201, -506097522914230529].pack("q!*")
+ s2 = [578437695752307201, 17940646550795321087].pack("Q!*")
+ assert_equal([578437695752307201, -506097522914230529], s2.unpack("q!*"))
+ assert_equal([578437695752307201, 17940646550795321087], s1.unpack("Q!*"))
+
+ assert_equal(8, [1].pack("q").bytesize)
+ assert_equal(8, [1].pack("Q").bytesize)
+ assert_operator(8, :<=, [1].pack("q!").bytesize)
+ assert_operator(8, :<=, [1].pack("Q!").bytesize)
+ end
+
+ def test_pack_unpack_nN
+ assert_equal("\000\000\000\001\377\377\177\377\200\000\377\377", [0,1,-1,32767,-32768,65535].pack("n*"))
+ assert_equal("\000\000\000\000\000\000\000\001\377\377\377\377", [0,1,-1].pack("N*"))
+
+ assert_equal([0,1,65535,32767,32768,65535], "\000\000\000\001\377\377\177\377\200\000\377\377".unpack("n*"))
+ assert_equal([0,1,4294967295], "\000\000\000\000\000\000\000\001\377\377\377\377".unpack("N*"))
+
+ assert_equal(2, [1].pack("n").bytesize)
+ assert_equal(4, [1].pack("N").bytesize)
+ end
+
+ def test_pack_unpack_vV
+ assert_equal("\000\000\001\000\377\377\377\177\000\200\377\377", [0,1,-1,32767,-32768,65535].pack("v*"))
+ assert_equal("\000\000\000\000\001\000\000\000\377\377\377\377", [0,1,-1].pack("V*"))
+
+ assert_equal([0,1,65535,32767,32768,65535], "\000\000\001\000\377\377\377\177\000\200\377\377".unpack("v*"))
+ assert_equal([0,1,4294967295], "\000\000\000\000\001\000\000\000\377\377\377\377".unpack("V*"))
+
+ assert_equal(2, [1].pack("v").bytesize)
+ assert_equal(4, [1].pack("V").bytesize)
+ end
+
+ def test_pack_unpack_fdeEgG
+ inf = 1.0/0.0
+ nan = inf/inf
+ [0.0, 1.0, 3.0, inf, -inf, nan].each do |x|
+ %w(f d e E g G).each do |f|
+ v = [x].pack(f).unpack(f)
+ if x.nan?
+ assert_predicate(v.first, :nan?)
+ else
+ assert_equal([x], v)
+ end
+ end
+ end
+ end
+
+ def test_pack_unpack_x
+ assert_equal("", [].pack("x0"))
+ assert_equal("\0", [].pack("x"))
+ assert_equal("\0" * 30, [].pack("x30"))
+
+ assert_equal([0, 2], "\x00\x00\x02".unpack("CxC"))
+ assert_raise(ArgumentError) { "".unpack("x") }
+ end
+
+ def test_pack_unpack_X
+ assert_equal("\x00\x02", [0, 1, 2].pack("CCXC"))
+ assert_equal("\x02", [0, 1, 2].pack("CCX2C"))
+ assert_raise(ArgumentError) { [].pack("X") }
+
+ assert_equal([0, 2, 2], "\x00\x02".unpack("CCXC"))
+ assert_raise(ArgumentError) { "".unpack("X") }
+ end
+
+ def test_pack_unpack_atmark
+ assert_equal("\x01\x00\x00\x02", [1, 2].pack("C@3C"))
+ assert_equal("\x02", [1, 2].pack("C@0C"))
+ assert_equal("\x01\x02", [1, 2].pack("C@1C"))
+
+ assert_equal([1, 2], "\x01\x00\x00\x02".unpack("C@3C"))
+ assert_equal([nil], "\x00".unpack("@1C")) # is it OK?
+ assert_raise(ArgumentError) { "\x00".unpack("@2C") }
+ end
+
+ def test_pack_unpack_percent
+ assert_raise(ArgumentError) { [].pack("%") }
+ assert_raise(ArgumentError) { "".unpack("%") }
+ end
+
+ def test_pack_unpack_U
+ assert_equal([0], [0].pack("U").unpack("U"))
+ assert_equal([0x80], [0x80].pack("U").unpack("U"))
+ assert_equal([0x800], [0x800].pack("U").unpack("U"))
+ assert_equal([0x10000], [0x10000].pack("U").unpack("U"))
+ assert_equal([0x400000], [0x400000].pack("U").unpack("U"))
+
+ assert_raise(ArgumentError) { "\x80".unpack("U") }
+ assert_raise(ArgumentError) { "\xff".unpack("U") }
+ assert_raise(ArgumentError) { "\xfc\x00".unpack("U") }
+ assert_raise(ArgumentError) { "\xc0\xc0".unpack("U") }
+ assert_raise(ArgumentError) { "\xe0\x80\x80".unpack("U") }
+ end
+
+ def test_pack_unpack_u
+ assert_equal("", [""].pack("u"))
+ assert_equal("!80``\n", ["a"].pack("u"))
+ assert_equal("#86)C\n", ["abc"].pack("u"))
+ assert_equal("$86)C9```\n", ["abcd"].pack("u"))
+ assert_equal("M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n", ["a"*45].pack("u"))
+ assert_equal("M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n", ["a"*46].pack("u"))
+ assert_equal("&86)C9&5F\n#9VAI\n", ["abcdefghi"].pack("u6"))
+
+ assert_equal("M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n", ["a"*46].pack("u0"))
+ assert_equal("M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n", ["a"*46].pack("u1"))
+ assert_equal("M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n", ["a"*46].pack("u2"))
+ assert_equal(<<EXPECTED, ["a"*80].pack("u68"))
+_86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+186%A86%A86%A86%A86%A86$`
+EXPECTED
+
+ assert_equal([""], "".unpack("u"))
+ assert_equal(["a"], "!80``\n".unpack("u"))
+ assert_equal(["abc"], "#86)C\n".unpack("u"))
+ assert_equal(["abcd"], "$86)C9```\n".unpack("u"))
+ assert_equal(["a"*45], "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n".unpack("u"))
+ assert_equal(["a"*46], "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n".unpack("u"))
+ assert_equal(["abcdefghi"], "&86)C9&5F\n#9VAI\n".unpack("u"))
+
+ assert_equal(["abcdef"], "#86)C\n#9&5F\n".unpack("u"))
+ assert_equal(["abcdef"], "#86)CX\n#9&5FX\n".unpack("u")) # X is a (dummy) checksum.
+ assert_equal(["abcdef"], "#86)C\r\n#9&5F\r\n".unpack("u"))
+ assert_equal(["abcdef"], "#86)CX\r\n#9&5FX\r\n".unpack("u")) # X is a (dummy) checksum.
+
+ assert_equal(["\x00"], "\"\n".unpack("u"))
+ assert_equal(["\x00"], "! \r \n".unpack("u"))
+ end
+
+ def test_pack_unpack_m
+ assert_equal("", [""].pack("m"))
+ assert_equal("AA==\n", ["\0"].pack("m"))
+ assert_equal("AAA=\n", ["\0\0"].pack("m"))
+ assert_equal("AAAA\n", ["\0\0\0"].pack("m"))
+ assert_equal("/w==\n", ["\377"].pack("m"))
+ assert_equal("//8=\n", ["\377\377"].pack("m"))
+ assert_equal("////\n", ["\377\377\377"].pack("m"))
+
+ assert_equal([""], "".unpack("m"))
+ assert_equal(["\0"], "AA==\n".unpack("m"))
+ assert_equal(["\0\0"], "AAA=\n".unpack("m"))
+ assert_equal(["\0\0\0"], "AAAA\n".unpack("m"))
+ assert_equal(["\377"], "/w==\n".unpack("m"))
+ assert_equal(["\377\377"], "//8=\n".unpack("m"))
+ assert_equal(["\377\377\377"], "////\n".unpack("m"))
+ assert_equal([""], "A\n".unpack("m"))
+ assert_equal(["\0"], "AA\n".unpack("m"))
+ assert_equal(["\0"], "AA=\n".unpack("m"))
+ assert_equal(["\0\0"], "AAA\n".unpack("m"))
+
+ bug10019 = '[ruby-core:63604] [Bug #10019]'
+ size = ((4096-4)/4*3+1)
+ assert_separately(%W[- #{size} #{bug10019}], <<-'end;')
+ size = ARGV.shift.to_i
+ bug = ARGV.shift
+ assert_equal(size, ["a"*size].pack("m#{size+2}").unpack("m")[0].size, bug)
+ end;
+ end
+
+ def test_pack_unpack_m0
+ assert_equal("", [""].pack("m0"))
+ assert_equal("AA==", ["\0"].pack("m0"))
+ assert_equal("AAA=", ["\0\0"].pack("m0"))
+ assert_equal("AAAA", ["\0\0\0"].pack("m0"))
+ assert_equal("/w==", ["\377"].pack("m0"))
+ assert_equal("//8=", ["\377\377"].pack("m0"))
+ assert_equal("////", ["\377\377\377"].pack("m0"))
+
+ assert_equal([""], "".unpack("m0"))
+ assert_equal(["\0"], "AA==".unpack("m0"))
+ assert_equal(["\0\0"], "AAA=".unpack("m0"))
+ assert_equal(["\0\0\0"], "AAAA".unpack("m0"))
+ assert_equal(["\377"], "/w==".unpack("m0"))
+ assert_equal(["\377\377"], "//8=".unpack("m0"))
+ assert_equal(["\377\377\377"], "////".unpack("m0"))
+
+ assert_raise(ArgumentError) { "^".unpack("m0") }
+ assert_raise(ArgumentError) { "A".unpack("m0") }
+ assert_raise(ArgumentError) { "A^".unpack("m0") }
+ assert_raise(ArgumentError) { "AA".unpack("m0") }
+ assert_raise(ArgumentError) { "AA=".unpack("m0") }
+ assert_raise(ArgumentError) { "AA===".unpack("m0") }
+ assert_raise(ArgumentError) { "AA=x".unpack("m0") }
+ assert_raise(ArgumentError) { "AAA".unpack("m0") }
+ assert_raise(ArgumentError) { "AAA^".unpack("m0") }
+ assert_raise(ArgumentError) { "AB==".unpack("m0") }
+ assert_raise(ArgumentError) { "AAB=".unpack("m0") }
+ end
+
+ def test_pack_unpack_M
+ assert_equal("a b c\td =\n\ne=\n", ["a b c\td \ne"].pack("M"))
+ assert_equal(["a b c\td \ne"], "a b c\td =\n\ne=\n".unpack("M"))
+ assert_equal("=00=\n", ["\0"].pack("M"))
+ assert_equal("a"*73+"=\na=\n", ["a"*74].pack("M"))
+ assert_equal(("a"*73+"=\n")*14+"a=\n", ["a"*1023].pack("M"))
+ assert_equal(["\0"], "=00=\n".unpack("M"))
+ assert_equal(["a"*74], ("a"*73+"=\na=\n").unpack("M"))
+ assert_equal(["a"*1023], (("a"*73+"=\n")*14+"a=\n").unpack("M"))
+ assert_equal(["\x0a"], "=0a=\n".unpack("M"))
+ assert_equal(["\x0a"], "=0A=\n".unpack("M"))
+ assert_equal(["=0Z=\n"], "=0Z=\n".unpack("M"))
+ assert_equal([""], "=\r\n".unpack("M"))
+ assert_equal(["\xC6\xF7"], "=C6=F7".unpack('M*'))
+
+ assert_equal(["pre123after"], "pre=31=32=33after".unpack("M"))
+ assert_equal(["preafter"], "pre=\nafter".unpack("M"))
+ assert_equal(["preafter"], "pre=\r\nafter".unpack("M"))
+ assert_equal(["pre="], "pre=".unpack("M"))
+ assert_equal(["pre=\r"], "pre=\r".unpack("M"))
+ assert_equal(["pre=hoge"], "pre=hoge".unpack("M"))
+ assert_equal(["pre==31after"], "pre==31after".unpack("M"))
+ assert_equal(["pre===31after"], "pre===31after".unpack("M"))
+ end
+
+ def test_pack_unpack_P2
+ assert_raise(ArgumentError) { ["abc"].pack("P4") }
+ assert_raise(ArgumentError) { [""].pack("P") }
+
+ assert_equal([], ".".unpack("P"))
+ assert_equal([], ".".unpack("p"))
+ assert_equal([nil], ("\0" * 1024).unpack("P"))
+ end
+
+ def test_pack_p2
+ assert_match(/\A\0*\Z/, [nil].pack("p"))
+ end
+
+ def test_pack_unpack_w
+ assert_equal("\000", [0].pack("w"))
+ assert_equal("\001", [1].pack("w"))
+ assert_equal("\177", [127].pack("w"))
+ assert_equal("\201\000", [128].pack("w"))
+ assert_equal("\377\177", [0x3fff].pack("w"))
+ assert_equal("\201\200\000", [0x4000].pack("w"))
+ assert_equal("\203\377\377\377\177", [0x3fffffff].pack("w"))
+ assert_equal("\204\200\200\200\000", [0x40000000].pack("w"))
+ assert_equal("\217\377\377\377\177", [0xffffffff].pack("w"))
+ assert_equal("\220\200\200\200\000", [0x100000000].pack("w"))
+ assert_raise(ArgumentError) { [-1].pack("w") }
+
+ assert_equal([0], "\000".unpack("w"))
+ assert_equal([1], "\001".unpack("w"), [1])
+ assert_equal([127], "\177".unpack("w"), [127])
+ assert_equal([128], "\201\000".unpack("w"), [128])
+ assert_equal([0x3fff], "\377\177".unpack("w"), [0x3fff])
+ assert_equal([0x4000], "\201\200\000".unpack("w"), [0x4000])
+ assert_equal([0x3fffffff], "\203\377\377\377\177".unpack("w"), [0x3fffffff])
+ assert_equal([0x40000000], "\204\200\200\200\000".unpack("w"), [0x40000000])
+ assert_equal([0xffffffff], "\217\377\377\377\177".unpack("w"), [0xffffffff])
+ assert_equal([0x100000000], "\220\200\200\200\000".unpack("w"), [0x100000000])
+ end
+
+ def test_length_too_big
+ assert_raise(RangeError) { [].pack("C100000000000000000000") }
+ end
+
+ def test_short_string
+ %w[n N v V s S i I l L q Q s! S! i! I! l! l!].each {|fmt|
+ str = [1].pack(fmt)
+ assert_equal([1,nil], str.unpack("#{fmt}2"))
+ }
+ end
+
+ def test_short_with_block
+ bug4059 = '[ruby-core:33193]'
+ result = :ok
+ assert_nil("".unpack("i") {|x| result = x}, bug4059)
+ assert_equal(:ok, result)
+ end
+
+ def test_pack_garbage
+ verbose = $VERBOSE
+ $VERBOSE = false
+
+ assert_silent do
+ assert_equal "\000", [0].pack("*U")
+ end
+
+ $VERBOSE = true
+
+ _, err = capture_io do
+ assert_equal "\000", [0].pack("*U")
+ end
+
+ assert_match %r%unknown pack directive '\*' in '\*U'$%, err
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def test_unpack_garbage
+ verbose = $VERBOSE
+ $VERBOSE = false
+
+ assert_silent do
+ assert_equal [0], "\000".unpack("*U")
+ end
+
+ $VERBOSE = true
+
+ _, err = capture_io do
+ assert_equal [0], "\000".unpack("*U")
+ end
+
+ assert_match %r%unknown unpack directive '\*' in '\*U'$%, err
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def test_invalid_warning
+ assert_warning(/unknown pack directive ',' in ','/) {
+ [].pack(",")
+ }
+ assert_warning(/\A[ -~]+\Z/) {
+ [].pack("\x7f")
+ }
+ assert_warning(/\A(.* in '\u{3042}'\n)+\z/) {
+ EnvUtil.with_default_external(Encoding::UTF_8) {
+ [].pack("\u{3042}")
+ }
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_parse.rb b/jni/ruby/test/ruby/test_parse.rb
new file mode 100644
index 0000000..43de64b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_parse.rb
@@ -0,0 +1,882 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'stringio'
+
+class TestParse < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def test_else_without_rescue
+ x = eval <<-END, nil, __FILE__, __LINE__+1
+ begin
+ else
+ 42
+ end
+ END
+ assert_equal(42, x)
+ end
+
+ def test_alias_backref
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ alias $foo $1
+ END
+ end
+ end
+
+ def test_command_call
+ t = Object.new
+ def t.foo(x); x; end
+
+ a = false
+ b = c = d = true
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ a &&= t.foo 42
+ b &&= t.foo 42
+ c &&= t.foo nil
+ d &&= t.foo false
+ END
+ end
+ assert_equal([false, 42, nil, false], [a, b, c, d])
+
+ a = 3
+ assert_nothing_raised { eval("a &= t.foo 5") }
+ assert_equal(1, a)
+
+ a = [nil, nil, true, true]
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ a[0] ||= t.foo 42
+ a[1] &&= t.foo 42
+ a[2] ||= t.foo 42
+ a[3] &&= t.foo 42
+ END
+ end
+ assert_equal([42, nil, true, 42], a)
+
+ o = Object.new
+ class << o
+ attr_accessor :foo, :bar, :Foo, :Bar, :baz, :qux
+ end
+ o.foo = o.Foo = o::baz = nil
+ o.bar = o.Bar = o::qux = 1
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ o.foo ||= t.foo 42
+ o.bar &&= t.foo 42
+ o.Foo ||= t.foo 42
+ o.Bar &&= t.foo 42
+ o::baz ||= t.foo 42
+ o::qux &&= t.foo 42
+ END
+ end
+ assert_equal([42, 42], [o.foo, o.bar])
+ assert_equal([42, 42], [o.Foo, o.Bar])
+ assert_equal([42, 42], [o::baz, o::qux])
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ $1 ||= t.foo 42
+ END
+ end
+
+ def t.bar(x); x + yield; end
+
+ a = b = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ a = t.bar "foo" do
+ "bar"
+ end.gsub "ob", "OB"
+ b = t.bar "foo" do
+ "bar"
+ end::gsub "ob", "OB"
+ END
+ end
+ assert_equal("foOBar", a)
+ assert_equal("foOBar", b)
+
+ a = nil
+ assert_nothing_raised do
+ t.instance_eval <<-END, __FILE__, __LINE__+1
+ a = bar "foo" do "bar" end
+ END
+ end
+ assert_equal("foobar", a)
+
+ a = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ a = t::bar "foo" do "bar" end
+ END
+ end
+ assert_equal("foobar", a)
+
+ def t.baz(*r)
+ @baz = r + (block_given? ? [yield] : [])
+ end
+
+ assert_nothing_raised do
+ t.instance_eval "baz (1), 2"
+ end
+ assert_equal([1, 2], t.instance_eval { @baz })
+ end
+
+ def test_mlhs_node
+ c = Class.new
+ class << c
+ attr_accessor :foo, :bar, :Foo, :Bar
+ FOO = BAR = nil
+ end
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ c::foo, c::bar = 1, 2
+ c.Foo, c.Bar = 1, 2
+ c::FOO, c::BAR = 1, 2
+ END
+ end
+ assert_equal([1, 2], [c::foo, c::bar])
+ assert_equal([1, 2], [c.Foo, c.Bar])
+ assert_equal([1, 2], [c::FOO, c::BAR])
+ end
+
+ def test_dynamic_constant_assignment
+ assert_raise(SyntaxError) do
+ Object.new.instance_eval <<-END, __FILE__, __LINE__+1
+ def foo
+ self::FOO, self::BAR = 1, 2
+ ::FOO, ::BAR = 1, 2
+ end
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ $1, $2 = 1, 2
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ Object.new.instance_eval <<-END, __FILE__, __LINE__+1
+ def foo
+ ::FOO = 1
+ end
+ END
+ end
+
+ c = Class.new
+ assert_nothing_raised(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ if false
+ c::FOO &= 1
+ ::FOO &= 1
+ end
+ END
+ end
+
+ c = Class.new
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ $1 &= 1
+ END
+ end
+ end
+
+ def test_class_module
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ class foo; end
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def foo
+ class Foo; end
+ module Bar; end
+ end
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ class Foo Bar; end
+ END
+ end
+ end
+
+ def test_op_name
+ o = Object.new
+ def o.>(x); x; end
+ def o./(x); x; end
+
+ assert_nothing_raised do
+ o.instance_eval <<-END, __FILE__, __LINE__+1
+ undef >, /
+ END
+ end
+ end
+
+ def test_arg
+ o = Object.new
+ class << o
+ attr_accessor :foo, :bar, :Foo, :Bar, :baz, :qux
+ end
+ o.foo = o.Foo = o::baz = nil
+ o.bar = o.Bar = o::qux = 1
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ o.foo ||= 42
+ o.bar &&= 42
+ o.Foo ||= 42
+ o.Bar &&= 42
+ o::baz ||= 42
+ o::qux &&= 42
+ END
+ end
+ assert_equal([42, 42], [o.foo, o.bar])
+ assert_equal([42, 42], [o.Foo, o.Bar])
+ assert_equal([42, 42], [o::baz, o::qux])
+
+ a = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ a = -2.0 ** 2
+ END
+ end
+ assert_equal(-4.0, a)
+ end
+
+ def test_block_variable
+ o = Object.new
+ def o.foo(*r); yield(*r); end
+
+ a = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ o.foo 1 do|; a| a = 42 end
+ END
+ end
+ assert_nil(a)
+ end
+
+ def test_bad_arg
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def foo(FOO); end
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def foo(@foo); end
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def foo($foo); end
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def foo(@@foo); end
+ END
+ end
+
+ o = Object.new
+ def o.foo(*r); yield(*r); end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ o.foo 1 {|; @a| @a = 42 }
+ END
+ end
+ end
+
+ def test_do_lambda
+ a = b = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ a = -> do
+ b = 42
+ end
+ END
+ end
+ a.call
+ assert_equal(42, b)
+ end
+
+ def test_block_call_colon2
+ o = Object.new
+ def o.foo(x); x + yield; end
+
+ a = b = nil
+ assert_nothing_raised do
+ o.instance_eval <<-END, __FILE__, __LINE__+1
+ a = foo 1 do 42 end.to_s
+ b = foo 1 do 42 end::to_s
+ END
+ end
+ assert_equal("43", a)
+ assert_equal("43", b)
+ end
+
+ def test_call_method
+ a = b = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ a = proc {|x| x + "bar" }.("foo")
+ b = proc {|x| x + "bar" }::("foo")
+ END
+ end
+ assert_equal("foobar", a)
+ assert_equal("foobar", b)
+ end
+
+ def test_xstring
+ assert_raise(Errno::ENOENT) do
+ eval("``")
+ end
+ end
+
+ def test_words
+ assert_equal([], %W( ))
+ end
+
+ def test_dstr
+ @@foo = 1
+ assert_equal("foo 1 bar", "foo #@@foo bar")
+ "1" =~ /(.)/
+ assert_equal("foo 1 bar", "foo #$1 bar")
+ end
+
+ def test_dstr_disallowed_variable
+ bug8375 = '[ruby-core:54885] [Bug #8375]'
+ %w[@ @1 @@. @@ @@1 @@. $ $%].each do |src|
+ src = '#'+src+' '
+ str = assert_nothing_raised(SyntaxError, "#{bug8375} #{src.dump}") do
+ break eval('"'+src+'"')
+ end
+ assert_equal(src, str, bug8375)
+ end
+ end
+
+ def test_dsym
+ assert_nothing_raised { eval(':""') }
+ end
+
+ def test_arg2
+ o = Object.new
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def o.foo(a=42,*r,z,&b); b.call(r.inject(a*1000+z*100, :+)); end
+ END
+ end
+ assert_equal(-1405, o.foo(1,2,3,4) {|x| -x })
+ assert_equal(-1302, o.foo(1,2,3) {|x| -x })
+ assert_equal(-1200, o.foo(1,2) {|x| -x })
+ assert_equal(-42100, o.foo(1) {|x| -x })
+ assert_raise(ArgumentError) { o.foo() }
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def o.foo(a=42,z,&b); b.call(a*1000+z*100); end
+ END
+ end
+ assert_equal(-1200, o.foo(1,2) {|x| -x } )
+ assert_equal(-42100, o.foo(1) {|x| -x } )
+ assert_raise(ArgumentError) { o.foo() }
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def o.foo(*r,z,&b); b.call(r.inject(z*100, :+)); end
+ END
+ end
+ assert_equal(-303, o.foo(1,2,3) {|x| -x } )
+ assert_equal(-201, o.foo(1,2) {|x| -x } )
+ assert_equal(-100, o.foo(1) {|x| -x } )
+ assert_raise(ArgumentError) { o.foo() }
+ end
+
+ def test_duplicate_argument
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ 1.times {|&b?| }
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ 1.times {|a, a|}
+ END
+ end
+
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def foo(a, a); end
+ END
+ end
+ end
+
+ def test_define_singleton_error
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def ("foo").foo; end
+ END
+ end
+ end
+
+ def test_backquote
+ t = Object.new
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def t.`(x); "foo" + x + "bar"; end
+ END
+ end
+ a = b = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ a = t.` "zzz"
+ 1.times {|;z| t.` ("zzz") }
+ END
+ t.instance_eval <<-END, __FILE__, __LINE__+1
+ b = `zzz`
+ END
+ end
+ assert_equal("foozzzbar", a)
+ assert_equal("foozzzbar", b)
+ end
+
+ def test_carrige_return
+ assert_equal(2, eval("1 +\r\n1"))
+ end
+
+ def test_string
+ assert_raise(SyntaxError) do
+ eval '"\xg1"'
+ end
+
+ assert_raise(SyntaxError) do
+ eval '"\u{1234"'
+ end
+
+ assert_raise(SyntaxError) do
+ eval '"\M1"'
+ end
+
+ assert_raise(SyntaxError) do
+ eval '"\C1"'
+ end
+
+ assert_equal("\x81", eval('"\C-\M-a"'))
+ assert_equal("\177", eval('"\c?"'))
+ end
+
+ def test_question
+ assert_raise(SyntaxError) { eval('?') }
+ assert_raise(SyntaxError) { eval('? ') }
+ assert_raise(SyntaxError) { eval("?\n") }
+ assert_raise(SyntaxError) { eval("?\t") }
+ assert_raise(SyntaxError) { eval("?\v") }
+ assert_raise(SyntaxError) { eval("?\r") }
+ assert_raise(SyntaxError) { eval("?\f") }
+ assert_equal("\u{1234}", eval("?\u{1234}"))
+ assert_equal("\u{1234}", eval('?\u{1234}'))
+ end
+
+ def test_percent
+ assert_equal(:foo, eval('%s(foo)'))
+ assert_raise(SyntaxError) { eval('%s') }
+ assert_raise(SyntaxError) { eval('%ss') }
+ assert_raise(SyntaxError) { eval('%z()') }
+ end
+
+ def test_symbol
+ bug = '[ruby-dev:41447]'
+ sym = "foo\0bar".to_sym
+ assert_nothing_raised(SyntaxError, bug) do
+ assert_equal(sym, eval(":'foo\0bar'"))
+ end
+ assert_nothing_raised(SyntaxError, bug) do
+ assert_equal(sym, eval(':"foo\u0000bar"'))
+ end
+ assert_nothing_raised(SyntaxError, bug) do
+ assert_equal(sym, eval(':"foo\u{0}bar"'))
+ end
+ assert_raise(SyntaxError) do
+ eval ':"foo\u{}bar"'
+ end
+ end
+
+ def test_parse_string
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+/
+ END
+ end
+ end
+
+ def test_here_document
+ x = nil
+
+ assert_raise(SyntaxError) do
+ eval %Q(
+<\<FOO
+ )
+ end
+
+ assert_nothing_raised(SyntaxError) do
+ x = eval %q(
+<<FOO
+#$
+FOO
+ )
+ end
+ assert_equal "\#$\n", x
+
+ assert_raise(SyntaxError) do
+ eval %Q(
+<\<\"
+ )
+ end
+
+ assert_raise(SyntaxError) do
+ eval %q(
+<<``
+ )
+ end
+
+ assert_raise(SyntaxError) do
+ eval %q(
+<<--
+ )
+ end
+
+ assert_nothing_raised(SyntaxError) do
+ x = eval %q(
+<<FOO
+#$
+foo
+FOO
+ )
+ end
+ assert_equal "\#$\nfoo\n", x
+
+ assert_nothing_raised do
+ eval "x = <<""FOO\r\n1\r\nFOO"
+ end
+ assert_equal("1\n", x)
+ end
+
+ def test_magic_comment
+ x = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+# coding = utf-8
+x = __ENCODING__
+ END
+ end
+ assert_equal(Encoding.find("UTF-8"), x)
+
+ assert_raise(ArgumentError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+# coding = foobarbazquxquux_dummy_enconding
+x = __ENCODING__
+ END
+ end
+ end
+
+ def test_utf8_bom
+ x = nil
+ assert_nothing_raised do
+ eval "\xef\xbb\xbf x = __ENCODING__"
+ end
+ assert_equal(Encoding.find("UTF-8"), x)
+ assert_raise(NameError) { eval "\xef" }
+ end
+
+ def test_dot_in_next_line
+ x = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ x = 1
+ .to_s
+ END
+ end
+ assert_equal("1", x)
+ end
+
+ def test_pow_asgn
+ x = 3
+ assert_nothing_raised { eval("x **= 2") }
+ assert_equal(9, x)
+ end
+
+ def test_embedded_rd
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+=begin
+ END
+ end
+ end
+
+ def test_float
+ assert_equal(1.0/0, eval("1e10000"))
+ assert_raise(SyntaxError) { eval('1_E') }
+ assert_raise(SyntaxError) { eval('1E1E1') }
+ end
+
+ def test_global_variable
+ assert_equal(nil, eval('$-x'))
+ assert_equal(nil, eval('alias $preserve_last_match $&'))
+ assert_equal(nil, eval('alias $& $test_parse_foobarbazqux'))
+ $test_parse_foobarbazqux = nil
+ assert_equal(nil, $&)
+ assert_equal(nil, eval('alias $& $preserve_last_match'))
+ assert_raise(SyntaxError) { eval('$#') }
+ end
+
+ def test_invalid_instance_variable
+ assert_raise(SyntaxError) { eval('@#') }
+ assert_raise(SyntaxError) { eval('@') }
+ end
+
+ def test_invalid_class_variable
+ assert_raise(SyntaxError) { eval('@@1') }
+ assert_raise(SyntaxError) { eval('@@') }
+ end
+
+ def test_invalid_char
+ bug10117 = '[ruby-core:64243] [Bug #10117]'
+ invalid_char = /Invalid char `\\x01'/
+ x = 1
+ assert_in_out_err(%W"-e \x01x", "", [], invalid_char, bug10117)
+ assert_syntax_error("\x01x", invalid_char, bug10117)
+ assert_equal(nil, eval("\x04x"))
+ end
+
+ def test_literal_concat
+ x = "baz"
+ assert_equal("foobarbaz", eval('"foo" "bar#{x}"'))
+ end
+
+ def test_unassignable
+ assert_raise(SyntaxError) do
+ eval %q(self = 1)
+ end
+ assert_raise(SyntaxError) do
+ eval %q(nil = 1)
+ end
+ assert_raise(SyntaxError) do
+ eval %q(true = 1)
+ end
+ assert_raise(SyntaxError) do
+ eval %q(false = 1)
+ end
+ assert_raise(SyntaxError) do
+ eval %q(__FILE__ = 1)
+ end
+ assert_raise(SyntaxError) do
+ eval %q(__LINE__ = 1)
+ end
+ assert_raise(SyntaxError) do
+ eval %q(__ENCODING__ = 1)
+ end
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ def foo
+ FOO = 1
+ end
+ END
+ end
+ end
+
+ def test_block_dup
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ foo(&proc{}) {}
+ END
+ end
+ end
+
+ def test_set_backref
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ $& = 1
+ END
+ end
+ end
+
+ def test_arg_concat
+ o = Object.new
+ class << o; self; end.instance_eval do
+ define_method(:[]=) {|*r, &b| b.call(r) }
+ end
+ r = nil
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ o[&proc{|x| r = x }] = 1
+ END
+ end
+ assert_equal([1], r)
+ end
+
+ def test_void_expr_stmts_value
+ # This test checks if void contexts are warned correctly.
+ # Thus, warnings MUST NOT be suppressed.
+ $VERBOSE = true
+ stderr = $stderr
+ $stderr = StringIO.new("")
+ x = 1
+ assert_nil eval("x; nil")
+ assert_nil eval("1+1; nil")
+ assert_nil eval("TestParse; nil")
+ assert_nil eval("::TestParse; nil")
+ assert_nil eval("x..x; nil")
+ assert_nil eval("x...x; nil")
+ assert_nil eval("self; nil")
+ assert_nil eval("nil; nil")
+ assert_nil eval("true; nil")
+ assert_nil eval("false; nil")
+ assert_nil eval("defined?(1); nil")
+
+ assert_raise(SyntaxError) do
+ eval %q(1; next; 2)
+ end
+
+ assert_equal(13, $stderr.string.lines.to_a.size)
+ $stderr = stderr
+ end
+
+ def test_assign_in_conditional
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ (x, y = 1, 2) ? 1 : 2
+ END
+ end
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ if @x = true
+ 1
+ else
+ 2
+ end
+ END
+ end
+ end
+
+ def test_literal_in_conditional
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ "foo" ? 1 : 2
+ END
+ end
+
+ assert_nothing_raised do
+ x = "bar"
+ eval <<-END, nil, __FILE__, __LINE__+1
+ /foo#{x}baz/ ? 1 : 2
+ END
+ end
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ (true..false) ? 1 : 2
+ END
+ end
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ ("foo".."bar") ? 1 : 2
+ END
+ end
+
+ assert_nothing_raised do
+ x = "bar"
+ eval <<-END, nil, __FILE__, __LINE__+1
+ :"foo#{"x"}baz" ? 1 : 2
+ END
+ end
+ end
+
+ def test_no_blockarg
+ assert_raise(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ yield(&:+)
+ END
+ end
+ end
+
+ def test_intern
+ assert_equal(':""', ''.intern.inspect)
+ assert_equal(':$foo', '$foo'.intern.inspect)
+ assert_equal(':"!foo"', '!foo'.intern.inspect)
+ assert_equal(':"foo=="', "foo==".intern.inspect)
+ end
+
+ def test_all_symbols
+ x = Symbol.all_symbols
+ assert_kind_of(Array, x)
+ assert_empty(x.reject {|s| s.is_a?(Symbol) })
+ end
+
+ def test_is_class_id
+ c = Class.new
+ assert_raise(NameError) do
+ c.instance_eval { remove_class_variable(:@var) }
+ end
+ end
+
+ def test_method_block_location
+ bug5614 = '[ruby-core:40936]'
+ expected = nil
+ e = assert_raise(NoMethodError) do
+ 1.times do
+ expected = __LINE__+1
+ end.print do
+ #
+ end
+ end
+ actual = e.backtrace.first[/\A#{Regexp.quote(__FILE__)}:(\d+):/o, 1].to_i
+ assert_equal(expected, actual, bug5614)
+ end
+
+ def test_shadowing_variable
+ assert_warning(/shadowing outer local variable/) {eval("a=1; tap {|a|}")}
+ a = "\u{3042}"
+ assert_warning(/#{a}/o) {eval("#{a}=1; tap {|#{a}|}")}
+ end
+
+ def test_unused_variable
+ o = Object.new
+ assert_warning(/assigned but unused variable/) {o.instance_eval("def foo; a=1; nil; end")}
+ a = "\u{3042}"
+ assert_warning(/#{a}/) {o.instance_eval("def foo; #{a}=1; nil; end")}
+ end
+
+ def test_named_capture_conflict
+ a = 1
+ assert_warning(/named capture conflict/) {eval("a = 1; /(?<a>)/ =~ ''")}
+ a = "\u{3042}"
+ assert_warning(/#{a}/) {eval("#{a} = 1; /(?<#{a}>)/ =~ ''")}
+ end
+
+=begin
+ def test_past_scope_variable
+ assert_warning(/past scope/) {catch {|tag| eval("BEGIN{throw tag}; tap {a = 1}; a")}}
+ end
+=end
+end
diff --git a/jni/ruby/test/ruby/test_path.rb b/jni/ruby/test/ruby/test_path.rb
new file mode 100644
index 0000000..2db7be0
--- /dev/null
+++ b/jni/ruby/test/ruby/test_path.rb
@@ -0,0 +1,259 @@
+require 'test/unit'
+
+class TestPath < Test::Unit::TestCase
+ def test_path
+ assert_equal("a", File.basename("a"))
+ assert_equal("b", File.basename("a/b"))
+ assert_equal("b", File.basename("a/b/"))
+ assert_equal("/", File.basename("/"))
+ assert_equal("/", File.basename("//"))
+ assert_equal("/", File.basename("///"))
+ assert_equal("b", File.basename("a/b////"))
+ assert_equal("a", File.basename("a.rb", ".rb"))
+ assert_equal("a", File.basename("a.rb///", ".rb"))
+ assert_equal("a", File.basename("a.rb///", ".*"))
+ assert_equal("a.rb", File.basename("a.rb///", ".c"))
+ assert_equal(".", File.dirname("a"))
+ assert_equal("/", File.dirname("/"))
+ assert_equal("/", File.dirname("/a"))
+ assert_equal("a", File.dirname("a/b"))
+ assert_equal("a/b", File.dirname("a/b/c"))
+ assert_equal("/a/b", File.dirname("/a/b/c"))
+ assert_equal("/a", File.dirname("/a/b/"))
+ assert_equal("/a", File.dirname("/a/b///"))
+ case Dir.pwd
+ when %r'\A\w:'
+ assert_match(/\A\w:\/\z/, File.expand_path(".", "/"))
+ assert_match(/\A\w:\/a\z/, File.expand_path("a", "/"))
+ dosish = true
+ when %r'\A//'
+ assert_match(%r'\A//[^/]+/[^/]+\z', File.expand_path(".", "/"))
+ assert_match(%r'\A//[^/]+/[^/]+/a\z', File.expand_path(".", "/"))
+ dosish = true
+ else
+ assert_equal("/", File.expand_path(".", "/"))
+ assert_equal("/sub", File.expand_path("sub", "/"))
+ end
+ if dosish
+ assert_equal("//127.0.0.1/share", File.expand_path("/", "//127.0.0.1/share/sub"))
+ assert_equal("//127.0.0.1/share/dir", File.expand_path("/dir", "//127.0.0.1/share/sub"))
+ assert_equal("z:/", File.expand_path("/", "z:/sub"))
+ assert_equal("z:/dir", File.expand_path("/dir", "z:/sub"))
+ end
+ assert_equal("//", File.expand_path(".", "//"))
+ assert_equal("//sub", File.expand_path("sub", "//"))
+
+ assert_equal("//127.0.0.1/\u3042", File.expand_path("\u3042", "//127.0.0.1"))
+ end
+
+ def test_dirname
+ if /(bcc|ms)win\d|mingw|cygwin|emx/ =~ RUBY_PLATFORM
+ # DOSISH_DRIVE_LETTER
+ assert_equal('C:.', File.dirname('C:'))
+ assert_equal('C:.', File.dirname('C:a'))
+ assert_equal('C:.', File.dirname('C:a/'))
+ assert_equal('C:a', File.dirname('C:a/b'), "[ruby-dev:27738]")
+
+ assert_equal('C:/', File.dirname('C:/'))
+ assert_equal('C:/', File.dirname('C:/a'))
+ assert_equal('C:/', File.dirname('C:/a/'))
+ assert_equal('C:/a', File.dirname('C:/a/b'))
+
+ assert_equal('C:/', File.dirname('C://'))
+ assert_equal('C:/', File.dirname('C://a'))
+ assert_equal('C:/', File.dirname('C://a/'))
+ assert_equal('C:/a', File.dirname('C://a/b'))
+
+ assert_equal('C:/', File.dirname('C:///'), "[ruby-dev:27738]")
+ assert_equal('C:/', File.dirname('C:///a'))
+ assert_equal('C:/', File.dirname('C:///a/'))
+ assert_equal('C:/a', File.dirname('C:///a/b'))
+ else
+ # others
+ assert_equal('.', File.dirname('C:'))
+ assert_equal('.', File.dirname('C:a'))
+ assert_equal('.', File.dirname('C:a/'))
+ assert_equal('C:a', File.dirname('C:a/b'))
+
+ assert_equal('.', File.dirname('C:/'))
+ assert_equal('C:', File.dirname('C:/a'))
+ assert_equal('C:', File.dirname('C:/a/'))
+ assert_equal('C:/a', File.dirname('C:/a/b'))
+
+ assert_equal('.', File.dirname('C://'))
+ assert_equal('C:', File.dirname('C://a'))
+ assert_equal('C:', File.dirname('C://a/'))
+ # not spec.
+ #assert_equal('C://a', File.dirname('C://a/b'))
+
+ assert_equal('.', File.dirname('C:///'))
+ assert_equal('C:', File.dirname('C:///a'))
+ assert_equal('C:', File.dirname('C:///a/'))
+ # not spec.
+ #assert_equal('C:///a', File.dirname('C:///a/b'))
+ end
+
+ assert_equal('.', File.dirname(''))
+ assert_equal('.', File.dirname('a'))
+ assert_equal('.', File.dirname('a/'))
+ assert_equal('a', File.dirname('a/b'))
+
+ assert_equal('/', File.dirname('/'))
+ assert_equal('/', File.dirname('/a'))
+ assert_equal('/', File.dirname('/a/'))
+ assert_equal('/a', File.dirname('/a/b'))
+
+ if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM
+ # DOSISH_UNC
+ assert_equal('//', File.dirname('//'))
+ assert_equal('//a', File.dirname('//a'))
+ assert_equal('//a', File.dirname('//a/'))
+ assert_equal('//a/b', File.dirname('//a/b'))
+ assert_equal('//a/b', File.dirname('//a/b/'))
+ assert_equal('//a/b', File.dirname('//a/b/c'))
+
+ assert_equal('//', File.dirname('///'))
+ assert_equal('//a', File.dirname('///a'))
+ assert_equal('//a', File.dirname('///a/'))
+ assert_equal('//a/b', File.dirname('///a/b'))
+ assert_equal('//a/b', File.dirname('///a/b/'))
+ assert_equal('//a/b', File.dirname('///a/b/c'))
+ else
+ # others
+ assert_equal('/', File.dirname('//'))
+ assert_equal('/', File.dirname('//a'))
+ assert_equal('/', File.dirname('//a/'))
+ assert_equal('/a', File.dirname('//a/b'))
+ assert_equal('/a', File.dirname('//a/b/'))
+ assert_equal('/a/b', File.dirname('//a/b/c'))
+
+ assert_equal('/', File.dirname('///'))
+ assert_equal('/', File.dirname('///a'))
+ assert_equal('/', File.dirname('///a/'))
+ assert_equal('/a', File.dirname('///a/b'))
+ assert_equal('/a', File.dirname('///a/b/'))
+ assert_equal('/a/b', File.dirname('///a/b/c'))
+ end
+ end
+
+ def test_basename
+ if /(bcc|ms)win\d|mingw|cygwin|emx/ =~ RUBY_PLATFORM
+ # DOSISH_DRIVE_LETTER
+ assert_equal('', File.basename('C:'))
+ assert_equal('a', File.basename('C:a'))
+ assert_equal('a', File.basename('C:a/'))
+ assert_equal('b', File.basename('C:a/b'))
+
+ assert_equal('/', File.basename('C:/'))
+ assert_equal('a', File.basename('C:/a'))
+ assert_equal('a', File.basename('C:/a/'))
+ assert_equal('b', File.basename('C:/a/b'))
+
+ assert_equal('/', File.basename('C://'))
+ assert_equal('a', File.basename('C://a'))
+ assert_equal('a', File.basename('C://a/'))
+ assert_equal('b', File.basename('C://a/b'))
+
+ assert_equal('/', File.basename('C:///'))
+ assert_equal('a', File.basename('C:///a'))
+ assert_equal('a', File.basename('C:///a/'))
+ assert_equal('b', File.basename('C:///a/b'))
+ else
+ # others
+ assert_equal('C:', File.basename('C:'))
+ assert_equal('C:a', File.basename('C:a'))
+ assert_equal('C:a', File.basename('C:a/'))
+ assert_equal('b', File.basename('C:a/b'))
+
+ assert_equal('C:', File.basename('C:/'))
+ assert_equal('a', File.basename('C:/a'))
+ assert_equal('a', File.basename('C:/a/'))
+ assert_equal('b', File.basename('C:/a/b'))
+
+ assert_equal('C:', File.basename('C://'))
+ assert_equal('a', File.basename('C://a'))
+ assert_equal('a', File.basename('C://a/'))
+ assert_equal('b', File.basename('C://a/b'))
+
+ assert_equal('C:', File.basename('C:///'))
+ assert_equal('a', File.basename('C:///a'))
+ assert_equal('a', File.basename('C:///a/'))
+ assert_equal('b', File.basename('C:///a/b'))
+ end
+
+ assert_equal('', File.basename(''))
+ assert_equal('a', File.basename('a'))
+ assert_equal('a', File.basename('a/'))
+ assert_equal('b', File.basename('a/b'))
+
+ assert_equal('/', File.basename('/'))
+ assert_equal('a', File.basename('/a'))
+ assert_equal('a', File.basename('/a/'))
+ assert_equal('b', File.basename('/a/b'))
+
+ assert_equal("..", File.basename("..", ".*"))
+
+ if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM
+ # DOSISH_UNC
+ assert_equal('/', File.basename('//'))
+ assert_equal('/', File.basename('//a'))
+ assert_equal('/', File.basename('//a/'))
+ assert_equal('/', File.basename('//a/b'), "[ruby-dev:27776]")
+ assert_equal('/', File.basename('//a/b/'))
+ assert_equal('c', File.basename('//a/b/c'))
+
+ assert_equal('/', File.basename('///'))
+ assert_equal('/', File.basename('///a'))
+ assert_equal('/', File.basename('///a/'))
+ assert_equal('/', File.basename('///a/b'))
+ assert_equal('/', File.basename('///a/b/'))
+ assert_equal('c', File.basename('///a/b/c'))
+ else
+ # others
+ assert_equal('/', File.basename('//'))
+ assert_equal('a', File.basename('//a'))
+ assert_equal('a', File.basename('//a/'))
+ assert_equal('b', File.basename('//a/b'))
+ assert_equal('b', File.basename('//a/b/'))
+ assert_equal('c', File.basename('//a/b/c'))
+
+ assert_equal('/', File.basename('///'))
+ assert_equal('a', File.basename('///a'))
+ assert_equal('a', File.basename('///a/'))
+ assert_equal('b', File.basename('///a/b'))
+ assert_equal('b', File.basename('///a/b/'))
+ assert_equal('c', File.basename('///a/b/c'))
+ end
+ end
+
+ def test_extname
+ assert_equal('', File.extname('a'))
+ ext = '.rb'
+ assert_equal(ext, File.extname('a.rb'))
+ unless /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ # trailing spaces and dots are ignored on NTFS.
+ ext = ''
+ end
+ assert_equal(ext, File.extname('a.rb.'))
+ assert_equal('', File.extname('a.'))
+ assert_equal('', File.extname('.x'))
+ assert_equal('', File.extname('..x'))
+ end
+
+ def test_ascii_incompatible_path
+ s = "\u{221e}\u{2603}"
+ assert_raise(Encoding::CompatibilityError) {open(s.encode("utf-16be"))}
+ assert_raise(Encoding::CompatibilityError) {open(s.encode("utf-16le"))}
+ assert_raise(Encoding::CompatibilityError) {open(s.encode("utf-32be"))}
+ assert_raise(Encoding::CompatibilityError) {open(s.encode("utf-32le"))}
+ end
+
+ def test_join
+ bug5483 = '[ruby-core:40338]'
+ path = %w[a b]
+ Encoding.list.each do |e|
+ next unless e.ascii_compatible?
+ assert_equal(e, File.join(*path.map {|s| s.force_encoding(e)}).encoding, bug5483)
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_pipe.rb b/jni/ruby/test/ruby/test_pipe.rb
new file mode 100644
index 0000000..bcea91b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_pipe.rb
@@ -0,0 +1,29 @@
+require 'test/unit'
+require_relative 'ut_eof'
+
+class TestPipe < Test::Unit::TestCase
+ include TestEOF
+ def open_file(content)
+ r, w = IO.pipe
+ w << content
+ w.close
+ begin
+ yield r
+ ensure
+ r.close
+ end
+ end
+ class WithConversion < self
+ def open_file(content)
+ r, w = IO.pipe
+ w << content
+ w.close
+ r.set_encoding("us-ascii:utf-8")
+ begin
+ yield r
+ ensure
+ r.close
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_primitive.rb b/jni/ruby/test/ruby/test_primitive.rb
new file mode 100644
index 0000000..9d451e2
--- /dev/null
+++ b/jni/ruby/test/ruby/test_primitive.rb
@@ -0,0 +1,423 @@
+require 'test/unit'
+
+class TestRubyPrimitive < Test::Unit::TestCase
+
+ def test_not
+ assert_equal false, !true
+ assert_equal true, !false
+ assert_equal true, !nil
+ assert_equal false, !(1+1)
+ assert_equal false, !!nil
+ assert_equal true, !!1
+ end
+
+ def test_lvar
+ a = 1
+ assert_equal 1, a
+ b = 2
+ assert_equal 1, a
+ a = b = 3
+ assert_equal 3, a
+ assert_equal 3, b
+ a = b = c = 4
+ assert_equal 4, a
+ assert_equal 4, b
+ assert_equal 4, c
+ end
+
+ C = 1
+ class A
+ Const = 1
+ class B
+ Const = 2
+ class C
+ Const = 3
+ def const
+ Const
+ end
+ end
+ end
+ end
+ (1..2).map {
+ A::B::C::Const
+ }
+
+ def test_constant
+ assert_equal 1, C
+ assert_equal 1, C
+ assert_equal 1, A::Const
+ assert_equal 2, A::B::Const
+ assert_equal 3, A::B::C::Const
+ assert_equal 3, A::B::C.new.const
+ assert_equal 1, ::TestRubyPrimitive::A::Const
+ A::B::C.send(:remove_const, :Const)
+ assert_equal 2, A::B::C.new.const
+ assert_raise(TypeError) {
+ C::CONST
+ }
+ end
+
+ class A2
+ class B2
+ class C2
+ C = 7
+ end
+ end
+ end
+
+ def test_constant_cache
+ i = 0
+ while i < 3
+ r = A2::B2::C2::C
+ i += 1
+ end
+ assert_equal 7, r
+ end
+
+ class A3
+ class B3
+ C = 99
+ end
+ end
+ i = 0
+ while i < 3
+ r = A3::B3::C # cache
+ class A3::B3
+ remove_const :C
+ end
+ A3::B3::C = i ** i
+ i += 1
+ end
+
+ def test_constant_cache2
+ assert_equal 4, A3::B3::C
+ end
+
+ class A4
+ Const = 7
+ (1..3).map {
+ $test_ruby_primitive_constant_cache3 = self::Const
+ }
+ end
+
+ def test_constant_cache3
+ assert_equal 7, $test_ruby_primitive_constant_cache3
+ end
+
+ class A5
+ Const = 8
+ (1..3).map {
+ $test_ruby_primitive_constant_cache4 = eval('self')::Const
+ }
+ end
+
+ def test_constatant_cache4
+ assert_equal 8, $test_ruby_primitive_constant_cache4
+ end
+
+ class A6
+ Const = 0
+ def self.foo
+ self::Const
+ end
+ end
+ class B6 < A6
+ Const = 1
+ end
+ class C6 < B6
+ Const = 2
+ end
+ $test_ruby_primitive_constant_cache5 = [A6.foo, B6.foo, C6.foo]
+
+ def test_constant_cache5
+ assert_equal [0, 1, 2], $test_ruby_primitive_constant_cache5
+ end
+
+ def test_gvar
+ $test_ruby_primitive_gvar = 7
+ assert_equal 7, $test_ruby_primitive_gvar
+ assert_equal 7, $test_ruby_primitive_gvar
+ $test_ruby_primitive_gvar = 88
+ assert_equal 88, $test_ruby_primitive_gvar
+ assert_equal 88, $test_ruby_primitive_gvar
+ assert_equal 7, ($test_ruby_primitive_gvar = 7)
+ assert_equal 7, ($test_ruby_primitive_gvar = 7)
+ end
+
+ class A7
+ @@c = 1
+ def m
+ @@c += 1
+ end
+ end
+
+ def test_cvar_from_instance_method
+ assert_equal 2, A7.new.m
+ assert_equal 3, A7.new.m
+ assert_equal 4, A7.new.m
+ end
+
+ class A8
+ @@c = 1
+ class << self
+ def m
+ @@c += 1
+ end
+ end
+ end
+
+ def test_cvar_from_singleton_method
+ assert_equal 2, A8.m
+ assert_equal 3, A8.m
+ assert_equal 4, A8.m
+ end
+
+ class A9
+ @@c = 1
+ def self.m
+ @@c += 1
+ end
+ end
+
+ def test_cvar_from_singleton_method2
+ assert_equal 2, A9.m
+ assert_equal 3, A9.m
+ assert_equal 4, A9.m
+ end
+
+ class A10
+ attr_accessor :a
+ end
+
+ def test_opassign
+ i = 0
+ i += 1
+ assert_equal 1, i
+
+ @iv = 2
+ @iv += 2
+ assert_equal 4, @iv
+
+ @@cv ||= 1
+ assert_equal 1, @@cv
+ @@cv &&= 2
+ assert_equal 2, @@cv
+ @@cv ||= 99
+ assert_equal 2, @@cv
+
+ $gv = 3
+ $gv += 4
+ assert_equal 7, $gv
+
+ obj = A10.new
+ obj.a = 9
+ obj.a &&= 7
+ assert_equal 7, obj.a
+
+ obj.a = nil
+ obj.a ||= 2
+ assert_equal 2, obj.a
+
+ obj.a &&= 3
+ assert_equal 3, obj.a
+
+ a = []
+ a[0] ||= 3
+ assert_equal 3, a[0]
+ a[0] &&= 7
+ assert_equal 7, a[0]
+ a[0] ||= 3
+ assert_equal 7, a[0]
+
+ a = [0, 1, nil, 3, 4]
+ a[*[2]] ||= :foo
+ assert_equal [0, 1, :foo, 3, 4], a
+ a[*[1,3]] &&= [:bar]
+ assert_equal [0, :bar, 4], a
+ end
+
+ def test_opassign_and_or
+ a = 1
+ a ||= 2
+ assert_equal 1, a
+ a = nil
+ a ||= 2
+ assert_equal 2, a
+ a = 1
+ a &&= 3
+ assert_equal 3, a
+ a = nil
+ a &&= 4
+ assert_nil a
+
+ h = {}
+ h[0] ||= 1
+ assert_equal 1, h[0]
+ h = {}
+ h[0] &&= 1
+ assert_nil h[0]
+ h = {0 => 7}
+ h[0] ||= 1
+ assert_equal 7, h[0]
+ h = {0 => 7}
+ h[0] &&= 1
+ assert_equal 1, h[0]
+ end
+
+ def test_backref
+ /a(b)(c)d/ =~ 'xyzabcdefgabcdefg'
+ assert_equal 'b', $1
+ assert_equal 'c', $2
+ assert_nil $3
+ assert_instance_of MatchData, $~
+ assert_equal 'abcd', $&
+ assert_equal 'xyz', $`
+ assert_equal 'efgabcdefg', $'
+ assert_equal 'c', $+
+
+ /(?!)/ =~ 'xyzabcdefgabcdefg'
+ assert_nil $1
+ assert_nil $2
+ assert_nil $3
+ assert_nil $~
+ assert_nil $&
+ assert_nil $`
+ assert_nil $'
+ assert_nil $+
+ end
+
+ def test_fact
+ assert_equal 306057512216440636035370461297268629388588804173576999416776741259476533176716867465515291422477573349939147888701726368864263907759003154226842927906974559841225476930271954604008012215776252176854255965356903506788725264321896264299365204576448830388909753943489625436053225980776521270822437639449120128678675368305712293681943649956460498166450227716500185176546469340112226034729724066333258583506870150169794168850353752137554910289126407157154830282284937952636580145235233156936482233436799254594095276820608062232812387383880817049600000000000000000000000000000000000000000000000000000000000000000000000000, fact(300)
+ end
+
+ def fact(n)
+ if n > 1
+ n * fact(n - 1)
+ else
+ 1
+ end
+ end
+
+ def test_mul
+ assert_equal 0, 2 * 0
+ assert_equal 0, 0 * 2
+ assert_equal 4, 2 * 2
+ end
+
+ class MyNum
+ def /(a)
+ a * 100
+ end
+ end
+
+ def test_div
+ assert_equal 1, 3 / 2
+ assert_equal 1.5, 3.0 / 2.0
+ assert_equal 300, MyNum.new / 3
+ end
+
+ class MyArr
+ def length
+ 'string'
+ end
+ end
+
+ def test_length
+ assert_equal 0, [].length
+ assert_equal 1, [1].length
+ assert_equal 2, [1,2].length
+ assert_equal 0, {}.length
+ assert_equal 1, {1=>1}.length
+ assert_equal 2, {1=>1, 2=>2}.length
+ assert_equal 'string', MyArr.new.length
+ end
+
+ class MyNum2
+ def %(a)
+ a * 100
+ end
+ end
+
+ def test_mod
+ assert_equal 2, 5 % 3
+ assert_equal 1.0, 3.0 % 2.0
+ assert_equal 300, MyNum2.new % 3
+ end
+
+ class MyObj
+ def [](*args)
+ args
+ end
+
+ def []=(*args)
+ args
+ end
+ end
+
+ def test_aref
+ a = [0,1]
+ assert_equal 0, a[0]
+ assert_equal 1, a[1]
+ assert_nil a[2]
+ h = {0=>0, 1=>1}
+ obj = MyObj.new
+ assert_equal 0, h[0]
+ assert_equal 1, h[1]
+ assert_nil h[2]
+ assert_equal [0], obj[0]
+ assert_equal [0,1], obj[0,1]
+ assert_equal [0,1,2], obj[0,1,2]
+ end
+
+ def test_aset
+ obj = MyObj.new
+ assert_equal 7, (obj[0] = 7)
+ assert_equal 7, (obj[0,1] = 7)
+ assert_equal 7, (obj[0,1,2] = 7)
+ end
+
+ class MyObj2
+ def attr=(*args)
+ args
+ end
+ end
+
+ def test_attr_setter
+ obj = MyObj2.new
+ assert_equal 1, (obj.attr = 1)
+ end
+
+ def test_list_expand
+ a = []
+ assert_equal [0], [0, *a]
+ a = [1]
+ assert_equal [0,1], [0, *a]
+ a = [1,2]
+ assert_equal [0,1,2], [0, *a]
+ a = [1,2,3]
+ assert_equal [0,1,2,3], [0, *a]
+ #a = [1,2,3]
+ #assert_equal [0,1,2,3,4], [0, *a, 4]
+ end
+
+ def test_concatarray_ruby_dev_41933
+ bug3658 = '[ruby-dev:41933]'
+ [0, *x=1]
+ assert_equal(1, x, bug3658)
+ [0, *x=1, 2]
+ assert_equal(1, x, bug3658)
+ class << (x = Object.new)
+ attr_accessor :to_a_called
+ def to_a
+ @to_a_called = true
+ [self]
+ end
+ end
+ x.to_a_called = false
+ [0, *x]
+ assert_predicate(x, :to_a_called, bug3658)
+ x.to_a_called = false
+ [0, *x, 2]
+ assert_predicate(x, :to_a_called, bug3658)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_proc.rb b/jni/ruby/test/ruby/test_proc.rb
new file mode 100644
index 0000000..728e2b7
--- /dev/null
+++ b/jni/ruby/test/ruby/test_proc.rb
@@ -0,0 +1,1323 @@
+require 'test/unit'
+
+class TestProc < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def test_proc
+ p1 = proc{|i| i}
+ assert_equal(2, p1.call(2))
+ assert_equal(3, p1.call(3))
+
+ p1 = proc{|i| i*2}
+ assert_equal(4, p1.call(2))
+ assert_equal(6, p1.call(3))
+
+ p2 = nil
+ x=0
+
+ proc{
+ iii=5 # nested local variable
+ p1 = proc{|i|
+ iii = i
+ }
+ p2 = proc {
+ x = iii # nested variables shared by procs
+ }
+ # scope of nested variables
+ assert(defined?(iii))
+ }.call
+ assert(!defined?(iii)) # out of scope
+
+ loop{iii=iii=5; assert(eval("defined? iii")); break}
+ loop {
+ iii=iii = 10
+ def self.dyna_var_check
+ loop {
+ assert(!defined?(iii))
+ break
+ }
+ end
+ dyna_var_check
+ break
+ }
+ p1.call(5)
+ p2.call
+ assert_equal(5, x)
+ end
+
+ def assert_arity(n)
+ meta = class << self; self; end
+ meta.class_eval {define_method(:foo, Proc.new)}
+ assert_equal(n, method(:foo).arity)
+ end
+
+ def test_arity
+ assert_equal(0, proc{}.arity)
+ assert_equal(0, proc{||}.arity)
+ assert_equal(1, proc{|x|}.arity)
+ assert_equal(0, proc{|x=1|}.arity)
+ assert_equal(2, proc{|x, y|}.arity)
+ assert_equal(1, proc{|x=0, y|}.arity)
+ assert_equal(0, proc{|x=0, y=0|}.arity)
+ assert_equal(1, proc{|x, y=0|}.arity)
+ assert_equal(-2, proc{|x, *y|}.arity)
+ assert_equal(-1, proc{|x=0, *y|}.arity)
+ assert_equal(-1, proc{|*x|}.arity)
+ assert_equal(-1, proc{|*|}.arity)
+ assert_equal(-3, proc{|x, *y, z|}.arity)
+ assert_equal(-2, proc{|x=0, *y, z|}.arity)
+ assert_equal(2, proc{|(x, y), z|[x,y]}.arity)
+ assert_equal(1, proc{|(x, y), z=0|[x,y]}.arity)
+ assert_equal(-4, proc{|x, *y, z, a|}.arity)
+ assert_equal(0, proc{|**|}.arity)
+ assert_equal(0, proc{|**o|}.arity)
+ assert_equal(1, proc{|x, **o|}.arity)
+ assert_equal(0, proc{|x=0, **o|}.arity)
+ assert_equal(1, proc{|x, y=0, **o|}.arity)
+ assert_equal(2, proc{|x, y=0, z, **o|}.arity)
+ assert_equal(-3, proc{|x, y=0, *z, w, **o|}.arity)
+
+ assert_equal(2, proc{|x, y=0, z, a:1|}.arity)
+ assert_equal(3, proc{|x, y=0, z, a:|}.arity)
+ assert_equal(-4, proc{|x, y, *rest, a:, b:, c:|}.arity)
+ assert_equal(3, proc{|x, y=0, z, a:, **o|}.arity)
+
+ assert_equal(0, lambda{}.arity)
+ assert_equal(0, lambda{||}.arity)
+ assert_equal(1, lambda{|x|}.arity)
+ assert_equal(-1, lambda{|x=1|}.arity) # different from proc
+ assert_equal(2, lambda{|x, y|}.arity)
+ assert_equal(-2, lambda{|x=0, y|}.arity) # different from proc
+ assert_equal(-1, lambda{|x=0, y=0|}.arity) # different from proc
+ assert_equal(-2, lambda{|x, y=0|}.arity) # different from proc
+ assert_equal(-2, lambda{|x, *y|}.arity)
+ assert_equal(-1, lambda{|x=0, *y|}.arity)
+ assert_equal(-1, lambda{|*x|}.arity)
+ assert_equal(-1, lambda{|*|}.arity)
+ assert_equal(-3, lambda{|x, *y, z|}.arity)
+ assert_equal(-2, lambda{|x=0, *y, z|}.arity)
+ assert_equal(2, lambda{|(x, y), z|[x,y]}.arity)
+ assert_equal(-2, lambda{|(x, y), z=0|[x,y]}.arity)
+ assert_equal(-4, lambda{|x, *y, z, a|}.arity)
+ assert_equal(-1, lambda{|**|}.arity)
+ assert_equal(-1, lambda{|**o|}.arity)
+ assert_equal(-2, lambda{|x, **o|}.arity)
+ assert_equal(-1, lambda{|x=0, **o|}.arity)
+ assert_equal(-2, lambda{|x, y=0, **o|}.arity)
+ assert_equal(-3, lambda{|x, y=0, z, **o|}.arity)
+ assert_equal(-3, lambda{|x, y=0, *z, w, **o|}.arity)
+
+ assert_arity(0) {}
+ assert_arity(0) {||}
+ assert_arity(1) {|x|}
+ assert_arity(2) {|x, y|}
+ assert_arity(-2) {|x, *y|}
+ assert_arity(-3) {|x, *y, z|}
+ assert_arity(-1) {|*x|}
+ assert_arity(-1) {|*|}
+ assert_arity(-1) {|**o|}
+ assert_arity(-1) {|**|}
+ assert_arity(-2) {|x, *y, **|}
+ assert_arity(-3) {|x, *y, z, **|}
+ end
+
+ def m(x)
+ lambda { x }
+ end
+
+ def test_eq
+ a = m(1)
+ b = m(2)
+ assert_not_equal(a, b, "[ruby-dev:22592]")
+ assert_not_equal(a.call, b.call, "[ruby-dev:22592]")
+
+ assert_not_equal(proc {||}, proc {|x,y|}, "[ruby-dev:22599]")
+
+ a = lambda {|x| lambda {} }.call(1)
+ b = lambda {}
+ assert_not_equal(a, b, "[ruby-dev:22601]")
+ end
+
+ def test_block_par
+ assert_equal(10, Proc.new{|&b| b.call(10)}.call {|x| x})
+ assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x})
+ end
+
+ def test_safe
+ safe = $SAFE
+ c = Class.new
+ x = c.new
+
+ p = proc {
+ $SAFE += 1
+ proc {$SAFE}
+ }.call
+ assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, p.call)
+ assert_equal(safe, $SAFE)
+
+ c.class_eval {define_method(:safe, p)}
+ assert_equal(safe, x.safe)
+ assert_equal(safe, x.method(:safe).call)
+ assert_equal(safe, x.method(:safe).to_proc.call)
+
+ p = proc {$SAFE += 1}
+ assert_equal(safe + 1, p.call)
+ assert_equal(safe, $SAFE)
+
+ c.class_eval {define_method(:inc, p)}
+ assert_equal(safe + 1, proc {x.inc; $SAFE}.call)
+ assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, proc {x.method(:inc).call; $SAFE}.call)
+ assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, proc {x.method(:inc).to_proc.call; $SAFE}.call)
+ assert_equal(safe, $SAFE)
+ end
+
+ def m2
+ "OK"
+ end
+
+ def block
+ method(:m2).to_proc
+ end
+
+ def m1(var)
+ var
+ end
+
+ def m_block_given?
+ m1(block_given?)
+ end
+
+ # [yarv-dev:777] block made by Method#to_proc
+ def test_method_to_proc
+ b = block()
+ assert_equal "OK", b.call
+ b = b.binding
+ assert_instance_of(Binding, b, '[ruby-core:25589]')
+ bug10432 = '[ruby-core:65919] [Bug #10432]'
+ assert_same(self, b.receiver, bug10432)
+ assert_not_send [b, :local_variable_defined?, :value]
+ assert_raise(NameError) {
+ b.local_variable_get(:value)
+ }
+ assert_equal 42, b.local_variable_set(:value, 42)
+ assert_send [b, :local_variable_defined?, :value]
+ assert_equal 42, b.local_variable_get(:value)
+ end
+
+ def test_block_given_method
+ m = method(:m_block_given?)
+ assert(!m.call, "without block")
+ assert(m.call {}, "with block")
+ assert(!m.call, "without block second")
+ end
+
+ def test_block_given_method_to_proc
+ bug8341 = '[Bug #8341]'
+ m = method(:m_block_given?).to_proc
+ assert(!m.call, "#{bug8341} without block")
+ assert(m.call {}, "#{bug8341} with block")
+ assert(!m.call, "#{bug8341} without block second")
+ end
+
+ def test_block_persist_between_calls
+ bug8341 = '[Bug #8341]'
+ o = Object.new
+ def o.m1(top=true)
+ if top
+ [block_given?, @m.call(false)]
+ else
+ block_given?
+ end
+ end
+ m = o.method(:m1).to_proc
+ o.instance_variable_set(:@m, m)
+ assert_equal([true, false], m.call {}, "#{bug8341} nested with block")
+ assert_equal([false, false], m.call, "#{bug8341} nested without block")
+ end
+
+ def test_curry
+ b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
+ assert_equal(6, b.curry[1][2][3])
+ assert_equal(6, b.curry[1, 2][3, 4])
+ assert_equal(6, b.curry(5)[1][2][3][4][5])
+ assert_equal(6, b.curry(5)[1, 2][3, 4][5])
+ assert_equal(1, b.curry(1)[1])
+
+ b = proc {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
+ assert_equal(6, b.curry[1][2][3])
+ assert_equal(10, b.curry[1, 2][3, 4])
+ assert_equal(15, b.curry(5)[1][2][3][4][5])
+ assert_equal(15, b.curry(5)[1, 2][3, 4][5])
+ assert_equal(1, b.curry(1)[1])
+
+ b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
+ assert_equal(6, b.curry[1][2][3])
+ assert_raise(ArgumentError) { b.curry[1, 2][3, 4] }
+ assert_raise(ArgumentError) { b.curry(5) }
+ assert_raise(ArgumentError) { b.curry(1) }
+
+ b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
+ assert_equal(6, b.curry[1][2][3])
+ assert_equal(10, b.curry[1, 2][3, 4])
+ assert_equal(15, b.curry(5)[1][2][3][4][5])
+ assert_equal(15, b.curry(5)[1, 2][3, 4][5])
+ assert_raise(ArgumentError) { b.curry(1) }
+
+ b = proc { :foo }
+ assert_equal(:foo, b.curry[])
+
+ b = lambda {|x, y, &blk| blk.call(x + y) }.curry
+ b = b.call(2) { raise }
+ b = b.call(3) {|x| x + 4 }
+ assert_equal(9, b)
+
+ l = proc {}
+ assert_equal(false, l.lambda?)
+ assert_equal(false, l.curry.lambda?, '[ruby-core:24127]')
+ l = lambda {}
+ assert_equal(true, l.lambda?)
+ assert_equal(true, l.curry.lambda?, '[ruby-core:24127]')
+ end
+
+ def test_curry_ski_fib
+ s = proc {|f, g, x| f[x][g[x]] }.curry
+ k = proc {|x, y| x }.curry
+ i = proc {|x| x }.curry
+
+ fib = []
+ inc = proc {|x| fib[-1] += 1; x }.curry
+ ret = proc {|x| throw :end if fib.size > 10; fib << 0; x }.curry
+
+ catch(:end) do
+ s[
+ s[s[i][i]][k[i]]
+ ][
+ k[inc]
+ ][
+ s[
+ s[
+ k[s]
+ ][
+ s[k[s[k[s]]]
+ ][
+ s[s[k[s]][s[k[s[k[ret]]]][s[k[s[i]]][k]]]][k]]
+ ]
+ ][
+ k[s[k[s]][k]]
+ ]
+ ]
+ end
+
+ assert_equal(fib, [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89])
+ end
+
+ def test_curry_from_knownbug
+ a = lambda {|x, y, &b| b }
+ b = a.curry[1]
+
+ assert_equal(:ok,
+ if b.call(2){} == nil
+ :ng
+ else
+ :ok
+ end, 'moved from btest/knownbug, [ruby-core:15551]')
+ end
+
+ def test_curry_instance_exec
+ a = lambda { |x, y| [x + y, self] }
+ b = a.curry.call(1)
+ result = instance_exec 2, &b
+
+ assert_equal(3, result[0])
+ assert_equal(self, result[1])
+ end
+
+ def test_curry_optional_params
+ obj = Object.new
+ def obj.foo(a, b=42); end
+ assert_raise(ArgumentError) { obj.method(:foo).to_proc.curry(3) }
+ assert_raise(ArgumentError) { ->(a, b=42){}.curry(3) }
+ end
+
+ def test_dup_clone
+ b = proc {|x| x + "bar" }
+ class << b; attr_accessor :foo; end
+
+ bd = b.dup
+ assert_equal("foobar", bd.call("foo"))
+ assert_raise(NoMethodError) { bd.foo = :foo }
+ assert_raise(NoMethodError) { bd.foo }
+
+ bc = b.clone
+ assert_equal("foobar", bc.call("foo"))
+ bc.foo = :foo
+ assert_equal(:foo, bc.foo)
+ end
+
+ def test_binding
+ b = proc {|x, y, z| proc {}.binding }.call(1, 2, 3)
+ class << b; attr_accessor :foo; end
+
+ bd = b.dup
+ assert_equal([1, 2, 3], bd.eval("[x, y, z]"))
+ assert_raise(NoMethodError) { bd.foo = :foo }
+ assert_raise(NoMethodError) { bd.foo }
+
+ bc = b.clone
+ assert_equal([1, 2, 3], bc.eval("[x, y, z]"))
+ bc.foo = :foo
+ assert_equal(:foo, bc.foo)
+
+ b = nil
+ 1.times { x, y, z = 1, 2, 3; [x,y,z]; b = binding }
+ assert_equal([1, 2, 3], b.eval("[x, y, z]"))
+ end
+
+ def test_proc_lambda
+ assert_raise(ArgumentError) { proc }
+ assert_raise(ArgumentError) { lambda }
+
+ o = Object.new
+ def o.foo
+ b = nil
+ 1.times { b = lambda }
+ b
+ end
+ assert_equal(:foo, o.foo { :foo }.call)
+
+ def o.foo(&b)
+ b = nil
+ 1.times { b = lambda }
+ b
+ end
+ assert_equal(:foo, o.foo { :foo }.call)
+ end
+
+ def test_arity2
+ assert_equal(0, method(:proc).to_proc.arity)
+ assert_equal(-1, proc {}.curry.arity)
+
+ c = Class.new
+ c.class_eval { attr_accessor :foo }
+ assert_equal(1, c.new.method(:foo=).to_proc.arity)
+ end
+
+ def test_proc_location
+ t = Thread.new { sleep }
+ assert_raise(ThreadError) { t.instance_eval { initialize { } } }
+ t.kill
+ t.join
+ end
+
+ def test_to_proc
+ b = proc { :foo }
+ assert_equal(:foo, b.to_proc.call)
+ end
+
+ def test_localjump_error
+ o = o = Object.new
+ def foo; yield; end
+ exc = foo rescue $!
+ assert_nil(exc.exit_value)
+ assert_equal(:noreason, exc.reason)
+ end
+
+ def test_binding2
+ assert_raise(ArgumentError) { proc {}.curry.binding }
+ end
+
+ def test_proc_args_plain
+ pr = proc {|a,b,c,d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal [nil,nil,nil,nil,nil], pr.call()
+ assert_equal [1,nil,nil,nil,nil], pr.call(1)
+ assert_equal [1,2,nil,nil,nil], pr.call(1,2)
+ assert_equal [1,2,3,nil,nil], pr.call(1,2,3)
+ assert_equal [1,2,3,4,nil], pr.call(1,2,3,4)
+ assert_equal [1,2,3,4,5], pr.call(1,2,3,4,5)
+ assert_equal [1,2,3,4,5], pr.call(1,2,3,4,5,6)
+
+ assert_equal [nil,nil,nil,nil,nil], pr.call([])
+ assert_equal [1,nil,nil,nil,nil], pr.call([1])
+ assert_equal [1,2,nil,nil,nil], pr.call([1,2])
+ assert_equal [1,2,3,nil,nil], pr.call([1,2,3])
+ assert_equal [1,2,3,4,nil], pr.call([1,2,3,4])
+ assert_equal [1,2,3,4,5], pr.call([1,2,3,4,5])
+ assert_equal [1,2,3,4,5], pr.call([1,2,3,4,5,6])
+
+ r = proc{|a| a}.call([1,2,3])
+ assert_equal [1,2,3], r
+
+ r = proc{|a,| a}.call([1,2,3])
+ assert_equal 1, r
+
+ r = proc{|a,| a}.call([])
+ assert_equal nil, r
+ end
+
+
+ def test_proc_args_rest
+ pr = proc {|a,b,c,*d|
+ [a,b,c,d]
+ }
+ assert_equal [nil,nil,nil,[]], pr.call()
+ assert_equal [1,nil,nil,[]], pr.call(1)
+ assert_equal [1,2,nil,[]], pr.call(1,2)
+ assert_equal [1,2,3,[]], pr.call(1,2,3)
+ assert_equal [1,2,3,[4]], pr.call(1,2,3,4)
+ assert_equal [1,2,3,[4,5]], pr.call(1,2,3,4,5)
+ assert_equal [1,2,3,[4,5,6]], pr.call(1,2,3,4,5,6)
+
+ assert_equal [nil,nil,nil,[]], pr.call([])
+ assert_equal [1,nil,nil,[]], pr.call([1])
+ assert_equal [1,2,nil,[]], pr.call([1,2])
+ assert_equal [1,2,3,[]], pr.call([1,2,3])
+ assert_equal [1,2,3,[4]], pr.call([1,2,3,4])
+ assert_equal [1,2,3,[4,5]], pr.call([1,2,3,4,5])
+ assert_equal [1,2,3,[4,5,6]], pr.call([1,2,3,4,5,6])
+
+ r = proc{|*a| a}.call([1,2,3])
+ assert_equal [[1,2,3]], r
+ end
+
+ def test_proc_args_pos_rest_post
+ pr = proc {|a,b,*c,d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal [nil, nil, [], nil, nil], pr.call()
+ assert_equal [1, nil, [], nil, nil], pr.call(1)
+ assert_equal [1, 2, [], nil, nil], pr.call(1,2)
+ assert_equal [1, 2, [], 3, nil], pr.call(1,2,3)
+ assert_equal [1, 2, [], 3, 4], pr.call(1,2,3,4)
+ assert_equal [1, 2, [3], 4, 5], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, [3, 4], 5, 6], pr.call(1,2,3,4,5,6)
+ assert_equal [1, 2, [3, 4, 5], 6,7], pr.call(1,2,3,4,5,6,7)
+
+ assert_equal [nil, nil, [], nil, nil], pr.call([])
+ assert_equal [1, nil, [], nil, nil], pr.call([1])
+ assert_equal [1, 2, [], nil, nil], pr.call([1,2])
+ assert_equal [1, 2, [], 3, nil], pr.call([1,2,3])
+ assert_equal [1, 2, [], 3, 4], pr.call([1,2,3,4])
+ assert_equal [1, 2, [3], 4, 5], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, [3, 4], 5, 6], pr.call([1,2,3,4,5,6])
+ assert_equal [1, 2, [3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7])
+ end
+
+ def test_proc_args_rest_post
+ pr = proc {|*a,b,c|
+ [a,b,c]
+ }
+ assert_equal [[], nil, nil], pr.call()
+ assert_equal [[], 1, nil], pr.call(1)
+ assert_equal [[], 1, 2], pr.call(1,2)
+ assert_equal [[1], 2, 3], pr.call(1,2,3)
+ assert_equal [[1, 2], 3, 4], pr.call(1,2,3,4)
+ assert_equal [[1, 2, 3], 4, 5], pr.call(1,2,3,4,5)
+ assert_equal [[1, 2, 3, 4], 5, 6], pr.call(1,2,3,4,5,6)
+ assert_equal [[1, 2, 3, 4, 5], 6,7], pr.call(1,2,3,4,5,6,7)
+
+ assert_equal [[], nil, nil], pr.call([])
+ assert_equal [[], 1, nil], pr.call([1])
+ assert_equal [[], 1, 2], pr.call([1,2])
+ assert_equal [[1], 2, 3], pr.call([1,2,3])
+ assert_equal [[1, 2], 3, 4], pr.call([1,2,3,4])
+ assert_equal [[1, 2, 3], 4, 5], pr.call([1,2,3,4,5])
+ assert_equal [[1, 2, 3, 4], 5, 6], pr.call([1,2,3,4,5,6])
+ assert_equal [[1, 2, 3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7])
+ end
+
+ def test_proc_args_pos_opt
+ pr = proc {|a,b,c=:c|
+ [a,b,c]
+ }
+ assert_equal [nil, nil, :c], pr.call()
+ assert_equal [1, nil, :c], pr.call(1)
+ assert_equal [1, 2, :c], pr.call(1,2)
+ assert_equal [1, 2, 3], pr.call(1,2,3)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4,5,6)
+
+ assert_equal [nil, nil, :c], pr.call([])
+ assert_equal [1, nil, :c], pr.call([1])
+ assert_equal [1, 2, :c], pr.call([1,2])
+ assert_equal [1, 2, 3], pr.call([1,2,3])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_opt
+ pr = proc {|a=:a,b=:b,c=:c|
+ [a,b,c]
+ }
+ assert_equal [:a, :b, :c], pr.call()
+ assert_equal [1, :b, :c], pr.call(1)
+ assert_equal [1, 2, :c], pr.call(1,2)
+ assert_equal [1, 2, 3], pr.call(1,2,3)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4,5,6)
+
+ assert_equal [:a, :b, :c], pr.call([])
+ assert_equal [1, :b, :c], pr.call([1])
+ assert_equal [1, 2, :c], pr.call([1,2])
+ assert_equal [1, 2, 3], pr.call([1,2,3])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_opt_signle
+ bug7621 = '[ruby-dev:46801]'
+ pr = proc {|a=:a|
+ a
+ }
+ assert_equal :a, pr.call()
+ assert_equal 1, pr.call(1)
+ assert_equal 1, pr.call(1,2)
+
+ assert_equal [], pr.call([]), bug7621
+ assert_equal [1], pr.call([1]), bug7621
+ assert_equal [1, 2], pr.call([1,2]), bug7621
+ assert_equal [1, 2, 3], pr.call([1,2,3]), bug7621
+ assert_equal [1, 2, 3, 4], pr.call([1,2,3,4]), bug7621
+ end
+
+ def test_proc_args_pos_opt_post
+ pr = proc {|a,b,c=:c,d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal [nil, nil, :c, nil, nil], pr.call()
+ assert_equal [1, nil, :c, nil, nil], pr.call(1)
+ assert_equal [1, 2, :c, nil, nil], pr.call(1,2)
+ assert_equal [1, 2, :c, 3, nil], pr.call(1,2,3)
+ assert_equal [1, 2, :c, 3, 4], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, 5], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, 5], pr.call(1,2,3,4,5,6)
+
+ assert_equal [nil, nil, :c, nil, nil], pr.call([])
+ assert_equal [1, nil, :c, nil, nil], pr.call([1])
+ assert_equal [1, 2, :c, nil, nil], pr.call([1,2])
+ assert_equal [1, 2, :c, 3, nil], pr.call([1,2,3])
+ assert_equal [1, 2, :c, 3, 4], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_opt_post
+ pr = proc {|a=:a,b=:b,c=:c,d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal [:a, :b, :c, nil, nil], pr.call()
+ assert_equal [:a, :b, :c, 1, nil], pr.call(1)
+ assert_equal [:a, :b, :c, 1, 2], pr.call(1,2)
+ assert_equal [1, :b, :c, 2, 3], pr.call(1,2,3)
+ assert_equal [1, 2, :c, 3, 4], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, 5], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, 5], pr.call(1,2,3,4,5,6)
+
+ assert_equal [:a, :b, :c, nil, nil], pr.call([])
+ assert_equal [:a, :b, :c, 1, nil], pr.call([1])
+ assert_equal [:a, :b, :c, 1, 2], pr.call([1,2])
+ assert_equal [1, :b, :c, 2, 3], pr.call([1,2,3])
+ assert_equal [1, 2, :c, 3, 4], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_pos_opt_rest
+ pr = proc {|a,b,c=:c,*d|
+ [a,b,c,d]
+ }
+ assert_equal [nil, nil, :c, []], pr.call()
+ assert_equal [1, nil, :c, []], pr.call(1)
+ assert_equal [1, 2, :c, []], pr.call(1,2)
+ assert_equal [1, 2, 3, []], pr.call(1,2,3)
+ assert_equal [1, 2, 3, [4]], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, [4, 5]], pr.call(1,2,3,4,5)
+
+ assert_equal [nil, nil, :c, []], pr.call([])
+ assert_equal [1, nil, :c, []], pr.call([1])
+ assert_equal [1, 2, :c, []], pr.call([1,2])
+ assert_equal [1, 2, 3, []], pr.call([1,2,3])
+ assert_equal [1, 2, 3, [4]], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, [4, 5]], pr.call([1,2,3,4,5])
+ end
+
+ def test_proc_args_opt_rest
+ pr = proc {|a=:a,b=:b,c=:c,*d|
+ [a,b,c,d]
+ }
+ assert_equal [:a, :b, :c, []], pr.call()
+ assert_equal [1, :b, :c, []], pr.call(1)
+ assert_equal [1, 2, :c, []], pr.call(1,2)
+ assert_equal [1, 2, 3, []], pr.call(1,2,3)
+ assert_equal [1, 2, 3, [4]], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, [4, 5]], pr.call(1,2,3,4,5)
+
+ assert_equal [:a, :b, :c, []], pr.call([])
+ assert_equal [1, :b, :c, []], pr.call([1])
+ assert_equal [1, 2, :c, []], pr.call([1,2])
+ assert_equal [1, 2, 3, []], pr.call([1,2,3])
+ assert_equal [1, 2, 3, [4]], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, [4, 5]], pr.call([1,2,3,4,5])
+ end
+
+ def test_proc_args_pos_opt_rest_post
+ pr = proc {|a,b,c=:c,*d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal [nil, nil, :c, [], nil], pr.call()
+ assert_equal [1, nil, :c, [], nil], pr.call(1)
+ assert_equal [1, 2, :c, [], nil], pr.call(1,2)
+ assert_equal [1, 2, :c, [], 3], pr.call(1,2,3)
+ assert_equal [1, 2, 3, [], 4], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, [4], 5], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, [4,5], 6], pr.call(1,2,3,4,5,6)
+
+ assert_equal [nil, nil, :c, [], nil], pr.call([])
+ assert_equal [1, nil, :c, [], nil], pr.call([1])
+ assert_equal [1, 2, :c, [], nil], pr.call([1,2])
+ assert_equal [1, 2, :c, [], 3], pr.call([1,2,3])
+ assert_equal [1, 2, 3, [], 4], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, [4], 5], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3, [4,5], 6], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_opt_rest_post
+ pr = proc {|a=:a,b=:b,c=:c,*d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal [:a, :b, :c, [], nil], pr.call()
+ assert_equal [:a, :b, :c, [], 1], pr.call(1)
+ assert_equal [1, :b, :c, [], 2], pr.call(1,2)
+ assert_equal [1, 2, :c, [], 3], pr.call(1,2,3)
+ assert_equal [1, 2, 3, [], 4], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, [4], 5], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, [4,5], 6], pr.call(1,2,3,4,5,6)
+
+ assert_equal [:a, :b, :c, [], nil], pr.call([])
+ assert_equal [:a, :b, :c, [], 1], pr.call([1])
+ assert_equal [1, :b, :c, [], 2], pr.call([1,2])
+ assert_equal [1, 2, :c, [], 3], pr.call([1,2,3])
+ assert_equal [1, 2, 3, [], 4], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, [4], 5], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3, [4,5], 6], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_pos_block
+ pr = proc {|a,b,&c|
+ [a, b, c.class, c&&c.call(:x)]
+ }
+ assert_equal [nil, nil, NilClass, nil], pr.call()
+ assert_equal [1, nil, NilClass, nil], pr.call(1)
+ assert_equal [1, 2, NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, NilClass, nil], pr.call(1,2,3,4)
+
+ assert_equal [nil, nil, NilClass, nil], pr.call([])
+ assert_equal [1, nil, NilClass, nil], pr.call([1])
+ assert_equal [1, 2, NilClass, nil], pr.call([1,2])
+ assert_equal [1, 2, NilClass, nil], pr.call([1,2,3])
+ assert_equal [1, 2, NilClass, nil], pr.call([1,2,3,4])
+
+ assert_equal [nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+
+ assert_equal [nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ end
+
+ def test_proc_args_pos_rest_block
+ pr = proc {|a,b,*c,&d|
+ [a, b, c, d.class, d&&d.call(:x)]
+ }
+ assert_equal [nil, nil, [], NilClass, nil], pr.call()
+ assert_equal [1, nil, [], NilClass, nil], pr.call(1)
+ assert_equal [1, 2, [], NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, [3], NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, [3,4], NilClass, nil], pr.call(1,2,3,4)
+
+ assert_equal [nil, nil, [], Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, nil, [], Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, [], Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, [3], Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, [3,4], Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+
+ assert_equal [nil, nil, [], Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, nil, [], Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, [], Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, [3], Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, [3,4], Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ end
+
+ def test_proc_args_rest_block
+ pr = proc {|*c,&d|
+ [c, d.class, d&&d.call(:x)]
+ }
+ assert_equal [[], NilClass, nil], pr.call()
+ assert_equal [[1], NilClass, nil], pr.call(1)
+ assert_equal [[1, 2], NilClass, nil], pr.call(1,2)
+
+ assert_equal [[], Proc, :proc], (pr.call(){ :proc })
+ assert_equal [[1], Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [[1, 2], Proc, :proc], (pr.call(1, 2){ :proc })
+
+ assert_equal [[], Proc, :x], (pr.call(){|x| x})
+ assert_equal [[1], Proc, :x], (pr.call(1){|x| x})
+ assert_equal [[1, 2], Proc, :x], (pr.call(1, 2){|x| x})
+ end
+
+ def test_proc_args_pos_rest_post_block
+ pr = proc {|a,b,*c,d,e,&f|
+ [a, b, c, d, e, f.class, f&&f.call(:x)]
+ }
+ assert_equal [nil, nil, [], nil, nil, NilClass, nil], pr.call()
+ assert_equal [1, nil, [], nil, nil, NilClass, nil], pr.call(1)
+ assert_equal [1, 2, [], nil, nil, NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, [], 3, nil, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, [], 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, [3], 4, 5, NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, [3,4], 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6)
+
+ assert_equal [nil, nil, [], nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, nil, [], nil, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, [], nil, nil, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, [], 3, nil, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, [], 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, [3], 4, 5, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, [3,4], 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+
+ assert_equal [nil, nil, [], nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, nil, [], nil, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, [], nil, nil, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, [], 3, nil, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, [], 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, [3], 4, 5, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, [3,4], 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ end
+
+ def test_proc_args_rest_post_block
+ pr = proc {|*c,d,e,&f|
+ [c, d, e, f.class, f&&f.call(:x)]
+ }
+ assert_equal [[], nil, nil, NilClass, nil], pr.call()
+ assert_equal [[], 1, nil, NilClass, nil], pr.call(1)
+ assert_equal [[], 1, 2, NilClass, nil], pr.call(1,2)
+ assert_equal [[1], 2, 3, NilClass, nil], pr.call(1,2,3)
+ assert_equal [[1, 2], 3, 4, NilClass, nil], pr.call(1,2,3,4)
+
+ assert_equal [[], nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [[], 1, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [[], 1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [[1], 2, 3, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [[1, 2], 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+
+ assert_equal [[], nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [[], 1, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [[], 1, 2, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [[1], 2, 3, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [[1, 2], 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ end
+
+ def test_proc_args_pos_opt_block
+ pr = proc {|a,b,c=:c,d=:d,&e|
+ [a, b, c, d, e.class, e&&e.call(:x)]
+ }
+ assert_equal [nil, nil, :c, :d, NilClass, nil], pr.call()
+ assert_equal [1, nil, :c, :d, NilClass, nil], pr.call(1)
+ assert_equal [1, 2, :c, :d, NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, 3, :d, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, NilClass, nil], pr.call(1,2,3,4,5)
+
+ assert_equal [nil, nil, :c, :d, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, nil, :c, :d, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, :c, :d, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, 3, :d, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+
+ assert_equal [nil, nil, :c, :d, Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, nil, :c, :d, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, :c, :d, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, 3, :d, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ end
+
+ def test_proc_args_opt_block
+ pr = proc {|a=:a,b=:b,c=:c,d=:d,&e|
+ [a, b, c, d, e.class, e&&e.call(:x)]
+ }
+ assert_equal [:a, :b, :c, :d, NilClass, nil], pr.call()
+ assert_equal [1, :b, :c, :d, NilClass, nil], pr.call(1)
+ assert_equal [1, 2, :c, :d, NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, 3, :d, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, NilClass, nil], pr.call(1,2,3,4,5)
+
+ assert_equal [:a, :b, :c, :d, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, :b, :c, :d, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, :c, :d, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, 3, :d, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+
+ assert_equal [:a, :b, :c, :d, Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, :b, :c, :d, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, :c, :d, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, 3, :d, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ end
+
+ def test_proc_args_pos_opt_post_block
+ pr = proc {|a,b,c=:c,d=:d,e,f,&g|
+ [a, b, c, d, e, f, g.class, g&&g.call(:x)]
+ }
+ assert_equal [nil, nil, :c, :d, nil, nil, NilClass, nil], pr.call()
+ assert_equal [1, nil, :c, :d, nil, nil, NilClass, nil], pr.call(1)
+ assert_equal [1, 2, :c, :d, nil, nil, NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, :c, :d, 3, nil, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, :c, :d, 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, :d, 4, 5, NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6)
+ assert_equal [1, 2, 3, 4, 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6,7)
+
+ assert_equal [nil, nil, :c, :d, nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, nil, :c, :d, nil, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, :c, :d, nil, nil, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, :c, :d, 3, nil, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, :c, :d, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, :d, 4, 5, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7){ :proc })
+
+ assert_equal [nil, nil, :c, :d, nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, nil, :c, :d, nil, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, :c, :d, nil, nil, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, :c, :d, 3, nil, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, :c, :d, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, :d, 4, 5, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7){|x| x})
+ end
+
+ def test_proc_args_opt_post_block
+ pr = proc {|a=:a,b=:b,c=:c,d=:d,e,f,&g|
+ [a, b, c, d, e, f, g.class, g&&g.call(:x)]
+ }
+ assert_equal [:a, :b, :c, :d, nil, nil, NilClass, nil], pr.call()
+ assert_equal [:a, :b, :c, :d, 1, nil, NilClass, nil], pr.call(1)
+ assert_equal [:a, :b, :c, :d, 1, 2, NilClass, nil], pr.call(1,2)
+ assert_equal [1, :b, :c, :d, 2, 3, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, :c, :d, 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, :d, 4, 5, NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6)
+ assert_equal [1, 2, 3, 4, 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6,7)
+
+ assert_equal [:a, :b, :c, :d, nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [:a, :b, :c, :d, 1, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [:a, :b, :c, :d, 1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, :b, :c, :d, 2, 3, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, :c, :d, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, :d, 4, 5, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7){ :proc })
+
+ assert_equal [:a, :b, :c, :d, nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [:a, :b, :c, :d, 1, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [:a, :b, :c, :d, 1, 2, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, :b, :c, :d, 2, 3, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, :c, :d, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, :d, 4, 5, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7){|x| x})
+ end
+
+ def test_proc_args_pos_opt_rest_block
+ pr = proc {|a,b,c=:c,d=:d,*e,&f|
+ [a, b, c, d, e, f.class, f&&f.call(:x)]
+ }
+ assert_equal [nil, nil, :c, :d, [], NilClass, nil], pr.call()
+ assert_equal [1, nil, :c, :d, [], NilClass, nil], pr.call(1)
+ assert_equal [1, 2, :c, :d, [], NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, 3, :d, [], NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, 3, 4, [], NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, [5], NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, [5,6], NilClass, nil], pr.call(1,2,3,4,5,6)
+
+ assert_equal [nil, nil, :c, :d, [], Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, nil, :c, :d, [], Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, :c, :d, [], Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, 3, :d, [], Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, 3, 4, [], Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, 4, [5], Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, [5,6], Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+
+ assert_equal [nil, nil, :c, :d, [], Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, nil, :c, :d, [], Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, :c, :d, [], Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, 3, :d, [], Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, 3, 4, [], Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, 4, [5], Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, [5,6], Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ end
+
+ def test_proc_args_opt_rest_block
+ pr = proc {|a=:a,b=:b,c=:c,d=:d,*e,&f|
+ [a, b, c, d, e, f.class, f&&f.call(:x)]
+ }
+ assert_equal [:a, :b, :c, :d, [], NilClass, nil], pr.call()
+ assert_equal [1, :b, :c, :d, [], NilClass, nil], pr.call(1)
+ assert_equal [1, 2, :c, :d, [], NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, 3, :d, [], NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, 3, 4, [], NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, [5], NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, [5,6], NilClass, nil], pr.call(1,2,3,4,5,6)
+
+ assert_equal [:a, :b, :c, :d, [], Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, :b, :c, :d, [], Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, :c, :d, [], Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, 3, :d, [], Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, 3, 4, [], Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, 4, [5], Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, [5,6], Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+
+ assert_equal [:a, :b, :c, :d, [], Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, :b, :c, :d, [], Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, :c, :d, [], Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, 3, :d, [], Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, 3, 4, [], Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, 4, [5], Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, [5,6], Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ end
+
+ def test_proc_args_pos_opt_rest_post_block
+ pr = proc {|a,b,c=:c,d=:d,*e,f,g,&h|
+ [a, b, c, d, e, f, g, h.class, h&&h.call(:x)]
+ }
+ assert_equal [nil, nil, :c, :d, [], nil, nil, NilClass, nil], pr.call()
+ assert_equal [1, nil, :c, :d, [], nil, nil, NilClass, nil], pr.call(1)
+ assert_equal [1, 2, :c, :d, [], nil, nil, NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, :c, :d, [], 3, nil, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, :c, :d, [], 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, :d, [], 4, 5, NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, [], 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6)
+ assert_equal [1, 2, 3, 4, [5], 6, 7, NilClass, nil], pr.call(1,2,3,4,5,6,7)
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, NilClass, nil], pr.call(1,2,3,4,5,6,7,8)
+
+ assert_equal [nil, nil, :c, :d, [], nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, nil, :c, :d, [], nil, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, :c, :d, [], nil, nil, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, :c, :d, [], 3, nil, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, :c, :d, [], 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, :d, [], 4, 5, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, [], 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+ assert_equal [1, 2, 3, 4, [5], 6, 7, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7){ :proc })
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7, 8){ :proc })
+
+ assert_equal [nil, nil, :c, :d, [], nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, nil, :c, :d, [], nil, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, :c, :d, [], nil, nil, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, :c, :d, [], 3, nil, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, :c, :d, [], 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, :d, [], 4, 5, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, [], 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ assert_equal [1, 2, 3, 4, [5], 6, 7, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7){|x| x})
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7, 8){|x| x})
+ end
+
+ def test_proc_args_opt_rest_post_block
+ pr = proc {|a=:a,b=:b,c=:c,d=:d,*e,f,g,&h|
+ [a, b, c, d, e, f, g, h.class, h&&h.call(:x)]
+ }
+ assert_equal [:a, :b, :c, :d, [], nil, nil, NilClass, nil], pr.call()
+ assert_equal [:a, :b, :c, :d, [], 1, nil, NilClass, nil], pr.call(1)
+ assert_equal [:a, :b, :c, :d, [], 1, 2, NilClass, nil], pr.call(1,2)
+ assert_equal [1, :b, :c, :d, [], 2, 3, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, :c, :d, [], 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, :d, [], 4, 5, NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, [], 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6)
+ assert_equal [1, 2, 3, 4, [5], 6, 7, NilClass, nil], pr.call(1,2,3,4,5,6,7)
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, NilClass, nil], pr.call(1,2,3,4,5,6,7,8)
+
+ assert_equal [:a, :b, :c, :d, [], nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [:a, :b, :c, :d, [], 1, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [:a, :b, :c, :d, [], 1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, :b, :c, :d, [], 2, 3, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, :c, :d, [], 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, :d, [], 4, 5, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, [], 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+ assert_equal [1, 2, 3, 4, [5], 6, 7, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7){ :proc })
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7, 8){ :proc })
+
+ assert_equal [:a, :b, :c, :d, [], nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [:a, :b, :c, :d, [], 1, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [:a, :b, :c, :d, [], 1, 2, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, :b, :c, :d, [], 2, 3, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, :c, :d, [], 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, :d, [], 4, 5, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, [], 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ assert_equal [1, 2, 3, 4, [5], 6, 7, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7){|x| x})
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7, 8){|x| x})
+ end
+
+ def test_proc_args_pos_unleashed
+ r = proc {|a,b=1,*c,d,e|
+ [a,b,c,d,e]
+ }.call(1,2,3,4,5)
+ assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
+ end
+
+ def test_parameters
+ assert_equal([], proc {}.parameters)
+ assert_equal([], proc {||}.parameters)
+ assert_equal([[:opt, :a]], proc {|a|}.parameters)
+ assert_equal([[:opt, :a], [:opt, :b]], proc {|a, b|}.parameters)
+ assert_equal([[:opt, :a], [:block, :b]], proc {|a=:a, &b|}.parameters)
+ assert_equal([[:opt, :a], [:opt, :b]], proc {|a, b=:b|}.parameters)
+ assert_equal([[:rest, :a]], proc {|*a|}.parameters)
+ assert_equal([[:opt, :a], [:rest, :b], [:block, :c]], proc {|a, *b, &c|}.parameters)
+ assert_equal([[:opt, :a], [:rest, :b], [:opt, :c]], proc {|a, *b, c|}.parameters)
+ assert_equal([[:opt, :a], [:rest, :b], [:opt, :c], [:block, :d]], proc {|a, *b, c, &d|}.parameters)
+ assert_equal([[:opt, :a], [:opt, :b], [:rest, :c], [:opt, :d], [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters)
+ assert_equal([[:opt, nil], [:block, :b]], proc {|(a), &b|a}.parameters)
+ assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], proc {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
+
+ assert_equal([[:req]], method(:putc).parameters)
+ assert_equal([[:rest]], method(:p).parameters)
+ end
+
+ def pm0() end
+ def pm1(a) end
+ def pm2(a, b) end
+ def pmo1(a = :a, &b) end
+ def pmo2(a, b = :b) end
+ def pmo3(*a) end
+ def pmo4(a, *b, &c) end
+ def pmo5(a, *b, c) end
+ def pmo6(a, *b, c, &d) end
+ def pmo7(a, b = :b, *c, d, &e) end
+ def pma1((a), &b) a; end
+ def pmk1(**) end
+ def pmk2(**o) nil && o end
+ def pmk3(a, **o) nil && o end
+ def pmk4(a = nil, **o) nil && o end
+ def pmk5(a, b = nil, **o) nil && o end
+ def pmk6(a, b = nil, c, **o) nil && o end
+ def pmk7(a, b = nil, *c, d, **o) nil && o end
+
+
+ def test_bound_parameters
+ assert_equal([], method(:pm0).to_proc.parameters)
+ assert_equal([[:req, :a]], method(:pm1).to_proc.parameters)
+ assert_equal([[:req, :a], [:req, :b]], method(:pm2).to_proc.parameters)
+ assert_equal([[:opt, :a], [:block, :b]], method(:pmo1).to_proc.parameters)
+ assert_equal([[:req, :a], [:opt, :b]], method(:pmo2).to_proc.parameters)
+ assert_equal([[:rest, :a]], method(:pmo3).to_proc.parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:pmo4).to_proc.parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:pmo5).to_proc.parameters)
+ assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).to_proc.parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).to_proc.parameters)
+ assert_equal([[:req], [:block, :b]], method(:pma1).to_proc.parameters)
+ assert_equal([[:keyrest]], method(:pmk1).to_proc.parameters)
+ assert_equal([[:keyrest, :o]], method(:pmk2).to_proc.parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], method(:pmk3).to_proc.parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], method(:pmk4).to_proc.parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:pmk5).to_proc.parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:pmk6).to_proc.parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:pmk7).to_proc.parameters)
+
+ assert_equal([], "".method(:upcase).to_proc.parameters)
+ assert_equal([[:rest]], "".method(:gsub).to_proc.parameters)
+ assert_equal([[:rest]], proc {}.curry.parameters)
+ end
+
+ def test_to_s
+ assert_match(/^#<Proc:0x\h+@#{ Regexp.quote(__FILE__) }:\d+>$/, proc {}.to_s)
+ assert_match(/^#<Proc:0x\h+@#{ Regexp.quote(__FILE__) }:\d+ \(lambda\)>$/, lambda {}.to_s)
+ assert_match(/^#<Proc:0x\h+ \(lambda\)>$/, method(:p).to_proc.to_s)
+ x = proc {}
+ x.taint
+ assert_predicate(x.to_s, :tainted?)
+ end
+
+ @@line_of_source_location_test = __LINE__ + 1
+ def source_location_test a=1,
+ b=2
+ end
+
+ def test_source_location
+ file, lineno = method(:source_location_test).source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(@@line_of_source_location_test, lineno, 'Bug #2427')
+ end
+
+ @@line_of_attr_reader_source_location_test = __LINE__ + 3
+ @@line_of_attr_writer_source_location_test = __LINE__ + 3
+ @@line_of_attr_accessor_source_location_test = __LINE__ + 3
+ attr_reader :attr_reader_source_location_test
+ attr_writer :attr_writer_source_location_test
+ attr_accessor :attr_accessor_source_location_test
+
+ def test_attr_source_location
+ file, lineno = method(:attr_reader_source_location_test).source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(@@line_of_attr_reader_source_location_test, lineno)
+
+ file, lineno = method(:attr_writer_source_location_test=).source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(@@line_of_attr_writer_source_location_test, lineno)
+
+ file, lineno = method(:attr_accessor_source_location_test).source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(@@line_of_attr_accessor_source_location_test, lineno)
+
+ file, lineno = method(:attr_accessor_source_location_test=).source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(@@line_of_attr_accessor_source_location_test, lineno)
+ end
+
+ def block_source_location_test(*args, &block)
+ block.source_location
+ end
+
+ def test_block_source_location
+ exp_lineno = __LINE__ + 3
+ file, lineno = block_source_location_test(1,
+ 2,
+ 3) do
+ end
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(exp_lineno, lineno)
+ end
+
+ def test_splat_without_respond_to
+ def (obj = Object.new).respond_to?(m,*); false end
+ [obj].each do |a, b|
+ assert_equal([obj, nil], [a, b], '[ruby-core:24139]')
+ end
+ end
+
+ def test_curry_with_trace
+ # bug3751 = '[ruby-core:31871]'
+ set_trace_func(proc {})
+ test_curry
+ ensure
+ set_trace_func(nil)
+ end
+
+ def test_block_propagation
+ bug3792 = '[ruby-core:32075]'
+ c = Class.new do
+ def foo
+ yield
+ end
+ end
+
+ o = c.new
+ f = :foo.to_proc
+ assert_nothing_raised(LocalJumpError, bug3792) {
+ assert_equal('bar', f.(o) {'bar'}, bug3792)
+ }
+ assert_nothing_raised(LocalJumpError, bug3792) {
+ assert_equal('zot', o.method(:foo).to_proc.() {'zot'}, bug3792)
+ }
+ end
+
+ def test_overridden_lambda
+ bug8345 = '[ruby-core:54687] [Bug #8345]'
+ assert_normal_exit('def lambda; end; method(:puts).to_proc', bug8345)
+ end
+
+ def test_overridden_proc
+ bug8345 = '[ruby-core:54688] [Bug #8345]'
+ assert_normal_exit('def proc; end; ->{}.curry', bug8345)
+ end
+
+ def get_binding if: 1, case: 2, when: 3, begin: 4, end: 5
+ a ||= 0
+ binding
+ end
+
+ def test_local_variables
+ b = get_binding
+ assert_equal(%i'if case when begin end a', b.local_variables)
+ a = tap {|;a, b| break binding.local_variables}
+ assert_equal(%i[a b], a.sort)
+ end
+
+ def test_local_variables_nested
+ b = tap {break binding}
+ assert_equal(%i[b], b.local_variables, '[ruby-dev:48351] [Bug #10001]')
+ end
+
+ def local_variables_of(bind)
+ this_should_not_be_in_bind = 2
+ bind.local_variables
+ end
+
+ def test_local_variables_in_other_context
+ feature8773 = '[Feature #8773]'
+ assert_equal([:feature8773], local_variables_of(binding), feature8773)
+ end
+
+ def test_local_variable_get
+ b = get_binding
+ assert_equal(0, b.local_variable_get(:a))
+ assert_raise(NameError){ b.local_variable_get(:b) }
+
+ # access keyword named local variables
+ assert_equal(1, b.local_variable_get(:if))
+ assert_equal(2, b.local_variable_get(:case))
+ assert_equal(3, b.local_variable_get(:when))
+ assert_equal(4, b.local_variable_get(:begin))
+ assert_equal(5, b.local_variable_get(:end))
+ end
+
+ def test_local_variable_set
+ b = get_binding
+ b.local_variable_set(:a, 10)
+ b.local_variable_set(:b, 20)
+ assert_equal(10, b.local_variable_get(:a))
+ assert_equal(20, b.local_variable_get(:b))
+ assert_equal(10, b.eval("a"))
+ assert_equal(20, b.eval("b"))
+ end
+
+ def test_local_variable_defined?
+ b = get_binding
+ assert_equal(true, b.local_variable_defined?(:a))
+ assert_equal(false, b.local_variable_defined?(:b))
+ end
+
+ def test_binding_receiver
+ feature8779 = '[ruby-dev:47613] [Feature #8779]'
+
+ assert_same(self, binding.receiver, feature8779)
+
+ obj = Object.new
+ def obj.b; binding; end
+ assert_same(obj, obj.b.receiver, feature8779)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_process.rb b/jni/ruby/test/ruby/test_process.rb
new file mode 100644
index 0000000..8847f6b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_process.rb
@@ -0,0 +1,2023 @@
+require 'test/unit'
+require 'tempfile'
+require 'timeout'
+require 'io/wait'
+require 'rbconfig'
+
+class TestProcess < Test::Unit::TestCase
+ RUBY = EnvUtil.rubybin
+
+ def setup
+ Process.waitall
+ end
+
+ def teardown
+ Process.waitall
+ end
+
+ def windows?
+ self.class.windows?
+ end
+ def self.windows?
+ return /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
+ end
+
+ def write_file(filename, content)
+ File.open(filename, "w") {|f|
+ f << content
+ }
+ end
+
+ def with_tmpchdir
+ Dir.mktmpdir {|d|
+ d = File.realpath(d)
+ Dir.chdir(d) {
+ yield d
+ }
+ }
+ end
+
+ def run_in_child(str) # should be called in a temporary directory
+ write_file("test-script", str)
+ Process.wait spawn(RUBY, "test-script")
+ $?
+ end
+
+ def test_rlimit_availability
+ begin
+ Process.getrlimit(nil)
+ rescue NotImplementedError
+ assert_raise(NotImplementedError) { Process.setrlimit }
+ rescue TypeError
+ assert_raise(ArgumentError) { Process.setrlimit }
+ end
+ end
+
+ def rlimit_exist?
+ Process.getrlimit(nil)
+ rescue NotImplementedError
+ return false
+ rescue TypeError
+ return true
+ end
+
+ def test_rlimit_nofile
+ return unless rlimit_exist?
+ with_tmpchdir {
+ write_file 's', <<-"End"
+ # Too small RLIMIT_NOFILE, such as zero, causes problems.
+ # [OpenBSD] Setting to zero freezes this test.
+ # [GNU/Linux] EINVAL on poll(). EINVAL on ruby's internal poll() ruby with "[ASYNC BUG] thread_timer: select".
+ pipes = IO.pipe
+ limit = pipes.map {|io| io.fileno }.min
+ result = 1
+ begin
+ Process.setrlimit(Process::RLIMIT_NOFILE, limit)
+ rescue Errno::EINVAL
+ result = 0
+ end
+ if result == 1
+ begin
+ IO.pipe
+ rescue Errno::EMFILE
+ result = 0
+ end
+ end
+ exit result
+ End
+ pid = spawn RUBY, "s"
+ Process.wait pid
+ assert_equal(0, $?.to_i, "#{$?}")
+ }
+ end
+
+ def test_rlimit_name
+ return unless rlimit_exist?
+ [
+ :AS, "AS",
+ :CORE, "CORE",
+ :CPU, "CPU",
+ :DATA, "DATA",
+ :FSIZE, "FSIZE",
+ :MEMLOCK, "MEMLOCK",
+ :MSGQUEUE, "MSGQUEUE",
+ :NICE, "NICE",
+ :NOFILE, "NOFILE",
+ :NPROC, "NPROC",
+ :RSS, "RSS",
+ :RTPRIO, "RTPRIO",
+ :RTTIME, "RTTIME",
+ :SBSIZE, "SBSIZE",
+ :SIGPENDING, "SIGPENDING",
+ :STACK, "STACK",
+ ].each {|name|
+ if Process.const_defined? "RLIMIT_#{name}"
+ assert_nothing_raised { Process.getrlimit(name) }
+ else
+ assert_raise(ArgumentError) { Process.getrlimit(name) }
+ end
+ }
+ assert_raise(ArgumentError) { Process.getrlimit(:FOO) }
+ assert_raise(ArgumentError) { Process.getrlimit("FOO") }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.getrlimit("\u{30eb 30d3 30fc}") }
+ end
+
+ def test_rlimit_value
+ return unless rlimit_exist?
+ assert_raise(ArgumentError) { Process.setrlimit(:FOO, 0) }
+ assert_raise(ArgumentError) { Process.setrlimit(:CORE, :FOO) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.setrlimit("\u{30eb 30d3 30fc}", 0) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.setrlimit(:CORE, "\u{30eb 30d3 30fc}") }
+ with_tmpchdir do
+ s = run_in_child(<<-'End')
+ cur, max = Process.getrlimit(:NOFILE)
+ Process.setrlimit(:NOFILE, [max-10, cur].min)
+ begin
+ Process.setrlimit(:NOFILE, :INFINITY)
+ rescue Errno::EPERM
+ exit false
+ end
+ End
+ assert_not_predicate(s, :success?)
+ s = run_in_child(<<-'End')
+ cur, max = Process.getrlimit(:NOFILE)
+ Process.setrlimit(:NOFILE, [max-10, cur].min)
+ begin
+ Process.setrlimit(:NOFILE, "INFINITY")
+ rescue Errno::EPERM
+ exit false
+ end
+ End
+ assert_not_predicate(s, :success?)
+ end
+ end
+
+ TRUECOMMAND = [RUBY, '-e', '']
+
+ def test_execopts_opts
+ assert_nothing_raised {
+ Process.wait Process.spawn(*TRUECOMMAND, {})
+ }
+ assert_raise(ArgumentError) {
+ Process.wait Process.spawn(*TRUECOMMAND, :foo => 100)
+ }
+ assert_raise(ArgumentError) {
+ Process.wait Process.spawn(*TRUECOMMAND, Process => 100)
+ }
+ end
+
+ def test_execopts_pgroup
+ skip "system(:pgroup) is not supported" if windows?
+ assert_nothing_raised { system(*TRUECOMMAND, :pgroup=>false) }
+
+ io = IO.popen([RUBY, "-e", "print Process.getpgrp"])
+ assert_equal(Process.getpgrp.to_s, io.read)
+ io.close
+
+ io = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>true])
+ assert_equal(io.pid.to_s, io.read)
+ io.close
+
+ assert_raise(ArgumentError) { system(*TRUECOMMAND, :pgroup=>-1) }
+ assert_raise(Errno::EPERM) { Process.wait spawn(*TRUECOMMAND, :pgroup=>2) }
+
+ io1 = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>true])
+ io2 = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>io1.pid])
+ assert_equal(io1.pid.to_s, io1.read)
+ assert_equal(io1.pid.to_s, io2.read)
+ Process.wait io1.pid
+ Process.wait io2.pid
+ io1.close
+ io2.close
+ end
+
+ def test_execopts_rlimit
+ return unless rlimit_exist?
+ assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_foo=>0) }
+ assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_NOFILE=>0) }
+ assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_nofile=>[]) }
+ assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_nofile=>[1,2,3]) }
+
+ max = Process.getrlimit(:CORE).last
+
+ n = max
+ IO.popen([RUBY, "-e",
+ "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
+ assert_equal("[#{n}, #{n}]\n", io.read)
+ }
+
+ n = 0
+ IO.popen([RUBY, "-e",
+ "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
+ assert_equal("[#{n}, #{n}]\n", io.read)
+ }
+
+ n = max
+ IO.popen([RUBY, "-e",
+ "p Process.getrlimit(:CORE)", :rlimit_core=>[n]]) {|io|
+ assert_equal("[#{n}, #{n}]", io.read.chomp)
+ }
+
+ m, n = 0, max
+ IO.popen([RUBY, "-e",
+ "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
+ assert_equal("[#{m}, #{n}]", io.read.chomp)
+ }
+
+ m, n = 0, 0
+ IO.popen([RUBY, "-e",
+ "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
+ assert_equal("[#{m}, #{n}]", io.read.chomp)
+ }
+
+ n = max
+ IO.popen([RUBY, "-e",
+ "p Process.getrlimit(:CORE), Process.getrlimit(:CPU)",
+ :rlimit_core=>n, :rlimit_cpu=>3600]) {|io|
+ assert_equal("[#{n}, #{n}]\n[3600, 3600]", io.read.chomp)
+ }
+ end
+
+ MANDATORY_ENVS = %w[RUBYLIB]
+ case RbConfig::CONFIG['target_os']
+ when /linux/
+ MANDATORY_ENVS << 'LD_PRELOAD'
+ when /mswin|mingw/
+ MANDATORY_ENVS.concat(%w[HOME USER TMPDIR])
+ when /darwin/
+ MANDATORY_ENVS.concat(ENV.keys.grep(/\A__CF_/))
+ end
+ if e = RbConfig::CONFIG['LIBPATHENV']
+ MANDATORY_ENVS << e
+ end
+ PREENVARG = ['-e', "%w[#{MANDATORY_ENVS.join(' ')}].each{|e|ENV.delete(e)}"]
+ ENVARG = ['-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }']
+ ENVCOMMAND = [RUBY].concat(PREENVARG).concat(ENVARG)
+
+ def test_execopts_env
+ assert_raise(ArgumentError) {
+ system({"F=O"=>"BAR"}, *TRUECOMMAND)
+ }
+
+ with_tmpchdir {|d|
+ prog = "#{d}/notexist"
+ e = assert_raise(Errno::ENOENT) {
+ Process.wait Process.spawn({"FOO"=>"BAR"}, prog)
+ }
+ assert_equal(prog, e.message.sub(/.* - /, ''))
+ e = assert_raise(Errno::ENOENT) {
+ Process.wait Process.spawn({"FOO"=>"BAR"}, [prog, "blar"])
+ }
+ assert_equal(prog, e.message.sub(/.* - /, ''))
+ }
+ h = {}
+ cmd = [h, RUBY]
+ (ENV.keys + MANDATORY_ENVS).each do |k|
+ case k
+ when /\APATH\z/i
+ when *MANDATORY_ENVS
+ cmd << '-e' << "ENV.delete('#{k}')"
+ else
+ h[k] = nil
+ end
+ end
+ cmd << '-e' << 'puts ENV.keys.map{|e|e.upcase}'
+ IO.popen(cmd) {|io|
+ assert_equal("PATH\n", io.read)
+ }
+
+ IO.popen([{"FOO"=>"BAR"}, *ENVCOMMAND]) {|io|
+ assert_match(/^FOO=BAR$/, io.read)
+ }
+
+ with_tmpchdir {|d|
+ system({"fofo"=>"haha"}, *ENVCOMMAND, STDOUT=>"out")
+ assert_match(/^fofo=haha$/, File.read("out").chomp)
+ }
+
+ old = ENV["hmm"]
+ begin
+ ENV["hmm"] = "fufu"
+ IO.popen(ENVCOMMAND) {|io| assert_match(/^hmm=fufu$/, io.read) }
+ IO.popen([{"hmm"=>""}, *ENVCOMMAND]) {|io| assert_match(/^hmm=$/, io.read) }
+ IO.popen([{"hmm"=>nil}, *ENVCOMMAND]) {|io| assert_not_match(/^hmm=/, io.read) }
+ ENV["hmm"] = ""
+ IO.popen(ENVCOMMAND) {|io| assert_match(/^hmm=$/, io.read) }
+ IO.popen([{"hmm"=>""}, *ENVCOMMAND]) {|io| assert_match(/^hmm=$/, io.read) }
+ IO.popen([{"hmm"=>nil}, *ENVCOMMAND]) {|io| assert_not_match(/^hmm=/, io.read) }
+ ENV["hmm"] = nil
+ IO.popen(ENVCOMMAND) {|io| assert_not_match(/^hmm=/, io.read) }
+ IO.popen([{"hmm"=>""}, *ENVCOMMAND]) {|io| assert_match(/^hmm=$/, io.read) }
+ IO.popen([{"hmm"=>nil}, *ENVCOMMAND]) {|io| assert_not_match(/^hmm=/, io.read) }
+ ensure
+ ENV["hmm"] = old
+ end
+ end
+
+ def _test_execopts_env_popen(cmd)
+ message = cmd.inspect
+ IO.popen({"FOO"=>"BAR"}, cmd) {|io|
+ assert_equal('FOO=BAR', io.read[/^FOO=.*/], message)
+ }
+
+ old = ENV["hmm"]
+ begin
+ ENV["hmm"] = "fufu"
+ IO.popen(cmd) {|io| assert_match(/^hmm=fufu$/, io.read, message)}
+ IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)}
+ IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)}
+ ENV["hmm"] = ""
+ IO.popen(cmd) {|io| assert_match(/^hmm=$/, io.read, message)}
+ IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)}
+ IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)}
+ ENV["hmm"] = nil
+ IO.popen(cmd) {|io| assert_not_match(/^hmm=/, io.read, message)}
+ IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)}
+ IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)}
+ ensure
+ ENV["hmm"] = old
+ end
+ end
+
+ def test_execopts_env_popen_vector
+ _test_execopts_env_popen(ENVCOMMAND)
+ end
+
+ def test_execopts_env_popen_string
+ with_tmpchdir do |d|
+ open('test-script', 'w') do |f|
+ ENVCOMMAND.each_with_index do |cmd, i|
+ next if i.zero? or cmd == "-e"
+ f.puts cmd
+ end
+ end
+ _test_execopts_env_popen("#{RUBY} test-script")
+ end
+ end
+
+ def test_execopts_preserve_env_on_exec_failure
+ with_tmpchdir {|d|
+ write_file 's', <<-"End"
+ ENV["mgg"] = nil
+ prog = "./nonexistent"
+ begin
+ Process.exec({"mgg" => "mggoo"}, [prog, prog])
+ rescue Errno::ENOENT
+ end
+ open('out', 'w') {|f|
+ f.print ENV["mgg"].inspect
+ }
+ End
+ system(RUBY, 's')
+ assert_equal(nil.inspect, File.read('out'),
+ "[ruby-core:44093] [ruby-trunk - Bug #6249]")
+ }
+ end
+
+ def test_execopts_env_single_word
+ with_tmpchdir {|d|
+ open("test_execopts_env_single_word.rb", "w") {|f|
+ f.puts "print ENV['hgga']"
+ }
+ system({"hgga"=>"ugu"}, RUBY,
+ :in => 'test_execopts_env_single_word.rb',
+ :out => 'test_execopts_env_single_word.out')
+ assert_equal('ugu', File.read('test_execopts_env_single_word.out'))
+ }
+ end
+
+ def test_execopts_unsetenv_others
+ h = {}
+ MANDATORY_ENVS.each {|k| e = ENV[k] and h[k] = e}
+ IO.popen([h, *ENVCOMMAND, :unsetenv_others=>true]) {|io|
+ assert_equal("", io.read)
+ }
+ IO.popen([h.merge("A"=>"B"), *ENVCOMMAND, :unsetenv_others=>true]) {|io|
+ assert_equal("A=B\n", io.read)
+ }
+ end
+
+ PWD = [RUBY, '-e', 'puts Dir.pwd']
+
+ def test_execopts_chdir
+ with_tmpchdir {|d|
+ IO.popen([*PWD, :chdir => d]) {|io|
+ assert_equal(d, io.read.chomp)
+ }
+ assert_raise(Errno::ENOENT) {
+ Process.wait Process.spawn(*PWD, :chdir => "d/notexist")
+ }
+ }
+ end
+
+ def test_execopts_open_chdir
+ with_tmpchdir {|d|
+ Dir.mkdir "foo"
+ system(*PWD, :chdir => "foo", :out => "open_chdir_test")
+ assert_file.exist?("open_chdir_test")
+ assert_file.not_exist?("foo/open_chdir_test")
+ assert_equal("#{d}/foo", File.read("open_chdir_test").chomp)
+ }
+ end
+
+ UMASK = [RUBY, '-e', 'printf "%04o\n", File.umask']
+
+ def test_execopts_umask
+ skip "umask is not supported" if windows?
+ IO.popen([*UMASK, :umask => 0]) {|io|
+ assert_equal("0000", io.read.chomp)
+ }
+ IO.popen([*UMASK, :umask => 0777]) {|io|
+ assert_equal("0777", io.read.chomp)
+ }
+ end
+
+ def with_pipe
+ begin
+ r, w = IO.pipe
+ yield r, w
+ ensure
+ r.close unless r.closed?
+ w.close unless w.closed?
+ end
+ end
+
+ def with_pipes(n)
+ ary = []
+ begin
+ n.times {
+ ary << IO.pipe
+ }
+ yield ary
+ ensure
+ ary.each {|r, w|
+ r.close unless r.closed?
+ w.close unless w.closed?
+ }
+ end
+ end
+
+ ECHO = lambda {|arg| [RUBY, '-e', "puts #{arg.dump}; STDOUT.flush"] }
+ SORT = [RUBY, '-e', "puts ARGF.readlines.sort"]
+ CAT = [RUBY, '-e', "IO.copy_stream STDIN, STDOUT"]
+
+ def test_execopts_redirect_fd
+ with_tmpchdir {|d|
+ Process.wait Process.spawn(*ECHO["a"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
+ assert_equal("a", File.read("out").chomp)
+ if windows?
+ # currently telling to child the file modes is not supported.
+ open("out", "a") {|f| f.write "0\n"}
+ else
+ Process.wait Process.spawn(*ECHO["0"], STDOUT=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644])
+ assert_equal("a\n0\n", File.read("out"))
+ end
+ Process.wait Process.spawn(*SORT, STDIN=>["out", File::RDONLY, 0644],
+ STDOUT=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644])
+ assert_equal("0\na\n", File.read("out2"))
+ Process.wait Process.spawn(*ECHO["b"], [STDOUT, STDERR]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
+ assert_equal("b", File.read("out").chomp)
+ # problem occur with valgrind
+ #Process.wait Process.spawn(*ECHO["a"], STDOUT=>:close, STDERR=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
+ #p File.read("out")
+ #assert_not_empty(File.read("out")) # error message such as "-e:1:in `flush': Bad file descriptor (Errno::EBADF)"
+ Process.wait Process.spawn(*ECHO["c"], STDERR=>STDOUT, STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
+ assert_equal("c", File.read("out").chomp)
+ File.open("out", "w") {|f|
+ Process.wait Process.spawn(*ECHO["d"], STDOUT=>f)
+ assert_equal("d", File.read("out").chomp)
+ }
+ opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]}
+ opts.merge(3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT) unless windows?
+ Process.wait Process.spawn(*ECHO["e"], opts)
+ assert_equal("e", File.read("out").chomp)
+ opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]}
+ opts.merge(3=>0, 4=>:in, 5=>STDIN, 6=>1, 7=>:out, 8=>STDOUT, 9=>2, 10=>:err, 11=>STDERR) unless windows?
+ Process.wait Process.spawn(*ECHO["ee"], opts)
+ assert_equal("ee", File.read("out").chomp)
+ unless windows?
+ # passing non-stdio fds is not supported on Windows
+ File.open("out", "w") {|f|
+ h = {STDOUT=>f, f=>STDOUT}
+ 3.upto(30) {|i| h[i] = STDOUT if f.fileno != i }
+ Process.wait Process.spawn(*ECHO["f"], h)
+ assert_equal("f", File.read("out").chomp)
+ }
+ end
+ assert_raise(ArgumentError) {
+ Process.wait Process.spawn(*ECHO["f"], 1=>Process)
+ }
+ assert_raise(ArgumentError) {
+ Process.wait Process.spawn(*ECHO["f"], [Process]=>1)
+ }
+ assert_raise(ArgumentError) {
+ Process.wait Process.spawn(*ECHO["f"], [1, STDOUT]=>2)
+ }
+ assert_raise(ArgumentError) {
+ Process.wait Process.spawn(*ECHO["f"], -1=>2)
+ }
+ Process.wait Process.spawn(*ECHO["hhh\nggg\n"], STDOUT=>"out")
+ assert_equal("hhh\nggg\n", File.read("out"))
+ Process.wait Process.spawn(*SORT, STDIN=>"out", STDOUT=>"out2")
+ assert_equal("ggg\nhhh\n", File.read("out2"))
+
+ unless windows?
+ # passing non-stdio fds is not supported on Windows
+ assert_raise(Errno::ENOENT) {
+ Process.wait Process.spawn("non-existing-command", (3..60).to_a=>["err", File::WRONLY|File::CREAT])
+ }
+ assert_equal("", File.read("err"))
+ end
+
+ system(*ECHO["bb\naa\n"], STDOUT=>["out", "w"])
+ assert_equal("bb\naa\n", File.read("out"))
+ system(*SORT, STDIN=>["out"], STDOUT=>"out2")
+ assert_equal("aa\nbb\n", File.read("out2"))
+ }
+ end
+
+ def test_execopts_redirect_pipe
+ with_pipe {|r1, w1|
+ with_pipe {|r2, w2|
+ opts = {STDIN=>r1, STDOUT=>w2}
+ opts.merge(w1=>:close, r2=>:close) unless windows?
+ pid = spawn(*SORT, opts)
+ r1.close
+ w2.close
+ w1.puts "c"
+ w1.puts "a"
+ w1.puts "b"
+ w1.close
+ assert_equal("a\nb\nc\n", r2.read)
+ r2.close
+ Process.wait(pid)
+ }
+ }
+
+ unless windows?
+ # passing non-stdio fds is not supported on Windows
+ with_pipes(5) {|pipes|
+ ios = pipes.flatten
+ h = {}
+ ios.length.times {|i| h[ios[i]] = ios[(i-1)%ios.length] }
+ h2 = h.invert
+ _rios = pipes.map {|r, w| r }
+ wios = pipes.map {|r, w| w }
+ child_wfds = wios.map {|w| h2[w].fileno }
+ pid = spawn(RUBY, "-e",
+ "[#{child_wfds.join(',')}].each {|fd| IO.new(fd, 'w').puts fd }", h)
+ pipes.each {|r, w|
+ assert_equal("#{h2[w].fileno}\n", r.gets)
+ }
+ Process.wait pid;
+ }
+
+ with_pipes(5) {|pipes|
+ ios = pipes.flatten
+ h = {}
+ ios.length.times {|i| h[ios[i]] = ios[(i+1)%ios.length] }
+ h2 = h.invert
+ _rios = pipes.map {|r, w| r }
+ wios = pipes.map {|r, w| w }
+ child_wfds = wios.map {|w| h2[w].fileno }
+ pid = spawn(RUBY, "-e",
+ "[#{child_wfds.join(',')}].each {|fd| IO.new(fd, 'w').puts fd }", h)
+ pipes.each {|r, w|
+ assert_equal("#{h2[w].fileno}\n", r.gets)
+ }
+ Process.wait pid
+ }
+
+ closed_fd = nil
+ with_pipes(5) {|pipes|
+ io = pipes.last.last
+ closed_fd = io.fileno
+ }
+ assert_raise(Errno::EBADF) { Process.wait spawn(*TRUECOMMAND, closed_fd=>closed_fd) }
+
+ with_pipe {|r, w|
+ if w.respond_to?(:"close_on_exec=")
+ w.close_on_exec = true
+ pid = spawn(RUBY, "-e", "IO.new(#{w.fileno}, 'w').print 'a'", w=>w)
+ w.close
+ assert_equal("a", r.read)
+ Process.wait pid
+ end
+ }
+ end
+ end
+
+ def test_execopts_redirect_symbol
+ with_tmpchdir {|d|
+ system(*ECHO["funya"], :out=>"out")
+ assert_equal("funya\n", File.read("out"))
+ system(RUBY, '-e', 'STDOUT.reopen(STDERR); puts "henya"', :err=>"out")
+ assert_equal("henya\n", File.read("out"))
+ IO.popen([*CAT, :in=>"out"]) {|io|
+ assert_equal("henya\n", io.read)
+ }
+ }
+ end
+
+ def test_execopts_redirect_nonascii_path
+ bug9946 = '[ruby-core:63185] [Bug #9946]'
+ with_tmpchdir {|d|
+ path = "t-\u{30c6 30b9 30c8 f6}.txt"
+ system(*ECHO["a"], out: path)
+ assert_file.for(bug9946).exist?(path)
+ assert_equal("a\n", File.read(path), bug9946)
+ }
+ end
+
+ def test_execopts_redirect_to_out_and_err
+ with_tmpchdir {|d|
+ ret = system(RUBY, "-e", 'STDERR.print "e"; STDOUT.print "o"', [:out, :err] => "foo")
+ assert_equal(true, ret)
+ assert_equal("eo", File.read("foo"))
+ ret = system(RUBY, "-e", 'STDERR.print "E"; STDOUT.print "O"', [:err, :out] => "bar")
+ assert_equal(true, ret)
+ assert_equal("EO", File.read("bar"))
+ }
+ end
+
+ def test_execopts_redirect_dup2_child
+ with_tmpchdir {|d|
+ Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'",
+ STDOUT=>"out", STDERR=>[:child, STDOUT])
+ assert_equal("errout", File.read("out"))
+
+ Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'",
+ STDERR=>"out", STDOUT=>[:child, STDERR])
+ assert_equal("errout", File.read("out"))
+
+ skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
+ Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'",
+ STDOUT=>"out",
+ STDERR=>[:child, 3],
+ 3=>[:child, 4],
+ 4=>[:child, STDOUT]
+ )
+ assert_equal("errout", File.read("out"))
+
+ IO.popen([RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", STDERR=>[:child, STDOUT]]) {|io|
+ assert_equal("errout", io.read)
+ }
+
+ assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, STDOUT]) }
+ assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 3]) }
+ assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 5], 5=>[:child, 3]) }
+ assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, 3]) }
+ }
+ end
+
+ def test_execopts_exec
+ with_tmpchdir {|d|
+ write_file("s", 'exec "echo aaa", STDOUT=>"foo"')
+ pid = spawn RUBY, 's'
+ Process.wait pid
+ assert_equal("aaa\n", File.read("foo"))
+ }
+ end
+
+ def test_execopts_popen
+ with_tmpchdir {|d|
+ IO.popen("#{RUBY} -e 'puts :foo'") {|io| assert_equal("foo\n", io.read) }
+ assert_raise(Errno::ENOENT) { IO.popen(["echo bar"]) {} } # assuming "echo bar" command not exist.
+ IO.popen(ECHO["baz"]) {|io| assert_equal("baz\n", io.read) }
+ assert_raise(ArgumentError) {
+ IO.popen([*ECHO["qux"], STDOUT=>STDOUT]) {|io| }
+ }
+ IO.popen([*ECHO["hoge"], STDERR=>STDOUT]) {|io|
+ assert_equal("hoge\n", io.read)
+ }
+ assert_raise(ArgumentError) {
+ IO.popen([*ECHO["fuga"], STDOUT=>"out"]) {|io| }
+ }
+ skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
+ with_pipe {|r, w|
+ IO.popen([RUBY, '-e', 'IO.new(3, "w").puts("a"); puts "b"', 3=>w]) {|io|
+ assert_equal("b\n", io.read)
+ }
+ w.close
+ assert_equal("a\n", r.read)
+ }
+ IO.popen([RUBY, '-e', "IO.new(9, 'w').puts(:b)",
+ 9=>["out2", File::WRONLY|File::CREAT|File::TRUNC]]) {|io|
+ assert_equal("", io.read)
+ }
+ assert_equal("b\n", File.read("out2"))
+ }
+ end
+
+ def test_popen_fork
+ IO.popen("-") {|io|
+ if !io
+ puts "fooo"
+ else
+ assert_equal("fooo\n", io.read)
+ end
+ }
+ rescue NotImplementedError
+ end
+
+ def test_fd_inheritance
+ skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
+ with_pipe {|r, w|
+ system(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts(:ba)', w.fileno.to_s, w=>w)
+ w.close
+ assert_equal("ba\n", r.read)
+ }
+ with_pipe {|r, w|
+ Process.wait spawn(RUBY, '-e',
+ 'IO.new(ARGV[0].to_i, "w").puts("bi") rescue nil',
+ w.fileno.to_s)
+ w.close
+ assert_equal("", r.read)
+ }
+ with_pipe {|r, w|
+ with_tmpchdir {|d|
+ write_file("s", <<-"End")
+ exec(#{RUBY.dump}, '-e',
+ 'IO.new(ARGV[0].to_i, "w").puts("bu") rescue nil',
+ #{w.fileno.to_s.dump}, :close_others=>false)
+ End
+ w.close_on_exec = false
+ Process.wait spawn(RUBY, "s", :close_others=>false)
+ w.close
+ assert_equal("bu\n", r.read)
+ }
+ }
+ with_pipe {|r, w|
+ io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('me')"])
+ begin
+ w.close
+ errmsg = io.read
+ assert_equal("", r.read)
+ assert_not_equal("", errmsg)
+ ensure
+ io.close
+ end
+ }
+ with_pipe {|r, w|
+ errmsg = `#{RUBY} -e "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts(123)"`
+ w.close
+ assert_equal("", r.read)
+ assert_not_equal("", errmsg)
+ }
+ end
+
+ def test_execopts_close_others
+ skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
+ with_tmpchdir {|d|
+ with_pipe {|r, w|
+ system(RUBY, '-e', 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i, "w").puts("ma")', w.fileno.to_s, :close_others=>true)
+ w.close
+ assert_equal("", r.read)
+ assert_not_equal("", File.read("err"))
+ File.unlink("err")
+ }
+ with_pipe {|r, w|
+ Process.wait spawn(RUBY, '-e', 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i, "w").puts("mi")', w.fileno.to_s, :close_others=>true)
+ w.close
+ assert_equal("", r.read)
+ assert_not_equal("", File.read("err"))
+ File.unlink("err")
+ }
+ with_pipe {|r, w|
+ w.close_on_exec = false
+ Process.wait spawn(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts("bi")', w.fileno.to_s, :close_others=>false)
+ w.close
+ assert_equal("bi\n", r.read)
+ }
+ with_pipe {|r, w|
+ write_file("s", <<-"End")
+ exec(#{RUBY.dump}, '-e',
+ 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i, "w").puts("mu")',
+ #{w.fileno.to_s.dump},
+ :close_others=>true)
+ End
+ Process.wait spawn(RUBY, "s", :close_others=>false)
+ w.close
+ assert_equal("", r.read)
+ assert_not_equal("", File.read("err"))
+ File.unlink("err")
+ }
+ with_pipe {|r, w|
+ io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('me')", :close_others=>true])
+ begin
+ w.close
+ errmsg = io.read
+ assert_equal("", r.read)
+ assert_not_equal("", errmsg)
+ ensure
+ io.close
+ end
+ }
+ with_pipe {|r, w|
+ w.close_on_exec = false
+ io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('mo')", :close_others=>false])
+ begin
+ w.close
+ errmsg = io.read
+ assert_equal("mo\n", r.read)
+ assert_equal("", errmsg)
+ ensure
+ io.close
+ end
+ }
+ with_pipe {|r, w|
+ w.close_on_exec = false
+ io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('mo')", :close_others=>nil])
+ begin
+ w.close
+ errmsg = io.read
+ assert_equal("mo\n", r.read)
+ assert_equal("", errmsg)
+ ensure
+ io.close
+ end
+ }
+
+ }
+ end
+
+ def test_execopts_redirect_self
+ begin
+ with_pipe {|r, w|
+ w << "haha\n"
+ w.close
+ r.close_on_exec = true
+ IO.popen([RUBY, "-e", "print IO.new(#{r.fileno}, 'r').read", r.fileno=>r.fileno, :close_others=>false]) {|io|
+ assert_equal("haha\n", io.read)
+ }
+ }
+ rescue NotImplementedError
+ skip "IO#close_on_exec= is not supported"
+ end
+ end
+
+ def test_execopts_redirect_tempfile
+ bug6269 = '[ruby-core:44181]'
+ Tempfile.create("execopts") do |tmp|
+ pid = assert_nothing_raised(ArgumentError, bug6269) do
+ break spawn(RUBY, "-e", "print $$", out: tmp)
+ end
+ Process.wait(pid)
+ tmp.rewind
+ assert_equal(pid.to_s, tmp.read)
+ end
+ end
+
+ def test_execopts_duplex_io
+ IO.popen("#{RUBY} -e ''", "r+") {|duplex|
+ assert_raise(ArgumentError) { system("#{RUBY} -e ''", duplex=>STDOUT) }
+ assert_raise(ArgumentError) { system("#{RUBY} -e ''", STDOUT=>duplex) }
+ }
+ end
+
+ def test_execopts_modification
+ h = {}
+ Process.wait spawn(*TRUECOMMAND, h)
+ assert_equal({}, h)
+
+ h = {}
+ system(*TRUECOMMAND, h)
+ assert_equal({}, h)
+
+ h = {}
+ io = IO.popen([*TRUECOMMAND, h])
+ io.close
+ assert_equal({}, h)
+ end
+
+ def test_system_noshell
+ str = "echo non existing command name which contains spaces"
+ assert_nil(system([str, str]))
+ end
+
+ def test_spawn_noshell
+ str = "echo non existing command name which contains spaces"
+ assert_raise(Errno::ENOENT) { spawn([str, str]) }
+ end
+
+ def test_popen_noshell
+ str = "echo non existing command name which contains spaces"
+ assert_raise(Errno::ENOENT) { IO.popen([str, str]) }
+ end
+
+ def test_exec_noshell
+ with_tmpchdir {|d|
+ write_file("s", <<-"End")
+ str = "echo non existing command name which contains spaces"
+ STDERR.reopen(STDOUT)
+ begin
+ exec [str, str]
+ rescue Errno::ENOENT
+ print "Errno::ENOENT success"
+ end
+ End
+ r = IO.popen([RUBY, "s", :close_others=>false], "r") {|f| f.read}
+ assert_equal("Errno::ENOENT success", r)
+ }
+ end
+
+ def test_system_wordsplit
+ with_tmpchdir {|d|
+ write_file("script", <<-'End')
+ File.open("result", "w") {|t| t << "haha pid=#{$$} ppid=#{Process.ppid}" }
+ exit 5
+ End
+ str = "#{RUBY} script"
+ ret = system(str)
+ status = $?
+ assert_equal(false, ret)
+ assert_predicate(status, :exited?)
+ assert_equal(5, status.exitstatus)
+ assert_equal("haha pid=#{status.pid} ppid=#{$$}", File.read("result"))
+ }
+ end
+
+ def test_spawn_wordsplit
+ with_tmpchdir {|d|
+ write_file("script", <<-'End')
+ File.open("result", "w") {|t| t << "hihi pid=#{$$} ppid=#{Process.ppid}" }
+ exit 6
+ End
+ str = "#{RUBY} script"
+ pid = spawn(str)
+ Process.wait pid
+ status = $?
+ assert_equal(pid, status.pid)
+ assert_predicate(status, :exited?)
+ assert_equal(6, status.exitstatus)
+ assert_equal("hihi pid=#{status.pid} ppid=#{$$}", File.read("result"))
+ }
+ end
+
+ def test_popen_wordsplit
+ with_tmpchdir {|d|
+ write_file("script", <<-'End')
+ print "fufu pid=#{$$} ppid=#{Process.ppid}"
+ exit 7
+ End
+ str = "#{RUBY} script"
+ io = IO.popen(str)
+ pid = io.pid
+ result = io.read
+ io.close
+ status = $?
+ assert_equal(pid, status.pid)
+ assert_predicate(status, :exited?)
+ assert_equal(7, status.exitstatus)
+ assert_equal("fufu pid=#{status.pid} ppid=#{$$}", result)
+ }
+ end
+
+ def test_popen_wordsplit_beginning_and_trailing_spaces
+ with_tmpchdir {|d|
+ write_file("script", <<-'End')
+ print "fufumm pid=#{$$} ppid=#{Process.ppid}"
+ exit 7
+ End
+ str = " #{RUBY} script "
+ io = IO.popen(str)
+ pid = io.pid
+ result = io.read
+ io.close
+ status = $?
+ assert_equal(pid, status.pid)
+ assert_predicate(status, :exited?)
+ assert_equal(7, status.exitstatus)
+ assert_equal("fufumm pid=#{status.pid} ppid=#{$$}", result)
+ }
+ end
+
+ def test_exec_wordsplit
+ with_tmpchdir {|d|
+ write_file("script", <<-'End')
+ File.open("result", "w") {|t|
+ if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ t << "hehe ppid=#{Process.ppid}"
+ else
+ t << "hehe pid=#{$$} ppid=#{Process.ppid}"
+ end
+ }
+ exit 6
+ End
+ write_file("s", <<-"End")
+ ruby = #{RUBY.dump}
+ exec "\#{ruby} script"
+ End
+ pid = spawn(RUBY, "s")
+ Process.wait pid
+ status = $?
+ assert_equal(pid, status.pid)
+ assert_predicate(status, :exited?)
+ assert_equal(6, status.exitstatus)
+ if windows?
+ expected = "hehe ppid=#{status.pid}"
+ else
+ expected = "hehe pid=#{status.pid} ppid=#{$$}"
+ end
+ assert_equal(expected, File.read("result"))
+ }
+ end
+
+ def test_system_shell
+ with_tmpchdir {|d|
+ write_file("script1", <<-'End')
+ File.open("result1", "w") {|t| t << "taka pid=#{$$} ppid=#{Process.ppid}" }
+ exit 7
+ End
+ write_file("script2", <<-'End')
+ File.open("result2", "w") {|t| t << "taki pid=#{$$} ppid=#{Process.ppid}" }
+ exit 8
+ End
+ ret = system("#{RUBY} script1 || #{RUBY} script2")
+ status = $?
+ assert_equal(false, ret)
+ assert_predicate(status, :exited?)
+ result1 = File.read("result1")
+ result2 = File.read("result2")
+ assert_match(/\Ataka pid=\d+ ppid=\d+\z/, result1)
+ assert_match(/\Ataki pid=\d+ ppid=\d+\z/, result2)
+ assert_not_equal(result1[/\d+/].to_i, status.pid)
+
+ if windows?
+ Dir.mkdir(path = "path with space")
+ write_file(bat = path + "/bat test.bat", "@echo %1>out")
+ system(bat, "foo 'bar'")
+ assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]')
+ system(%[#{bat.dump} "foo 'bar'"])
+ assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]')
+ end
+ }
+ end
+
+ def test_spawn_shell
+ with_tmpchdir {|d|
+ write_file("script1", <<-'End')
+ File.open("result1", "w") {|t| t << "taku pid=#{$$} ppid=#{Process.ppid}" }
+ exit 7
+ End
+ write_file("script2", <<-'End')
+ File.open("result2", "w") {|t| t << "take pid=#{$$} ppid=#{Process.ppid}" }
+ exit 8
+ End
+ pid = spawn("#{RUBY} script1 || #{RUBY} script2")
+ Process.wait pid
+ status = $?
+ assert_predicate(status, :exited?)
+ assert_not_predicate(status, :success?)
+ result1 = File.read("result1")
+ result2 = File.read("result2")
+ assert_match(/\Ataku pid=\d+ ppid=\d+\z/, result1)
+ assert_match(/\Atake pid=\d+ ppid=\d+\z/, result2)
+ assert_not_equal(result1[/\d+/].to_i, status.pid)
+
+ if windows?
+ Dir.mkdir(path = "path with space")
+ write_file(bat = path + "/bat test.bat", "@echo %1>out")
+ pid = spawn(bat, "foo 'bar'")
+ Process.wait pid
+ status = $?
+ assert_predicate(status, :exited?)
+ assert_predicate(status, :success?)
+ assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]')
+ pid = spawn(%[#{bat.dump} "foo 'bar'"])
+ Process.wait pid
+ status = $?
+ assert_predicate(status, :exited?)
+ assert_predicate(status, :success?)
+ assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]')
+ end
+ }
+ end
+
+ def test_popen_shell
+ with_tmpchdir {|d|
+ write_file("script1", <<-'End')
+ puts "tako pid=#{$$} ppid=#{Process.ppid}"
+ exit 7
+ End
+ write_file("script2", <<-'End')
+ puts "tika pid=#{$$} ppid=#{Process.ppid}"
+ exit 8
+ End
+ io = IO.popen("#{RUBY} script1 || #{RUBY} script2")
+ result = io.read
+ io.close
+ status = $?
+ assert_predicate(status, :exited?)
+ assert_not_predicate(status, :success?)
+ assert_match(/\Atako pid=\d+ ppid=\d+\ntika pid=\d+ ppid=\d+\n\z/, result)
+ assert_not_equal(result[/\d+/].to_i, status.pid)
+
+ if windows?
+ Dir.mkdir(path = "path with space")
+ write_file(bat = path + "/bat test.bat", "@echo %1")
+ r = IO.popen([bat, "foo 'bar'"]) {|f| f.read}
+ assert_equal(%["foo 'bar'"\n], r, '[ruby-core:22960]')
+ r = IO.popen(%[#{bat.dump} "foo 'bar'"]) {|f| f.read}
+ assert_equal(%["foo 'bar'"\n], r, '[ruby-core:22960]')
+ end
+ }
+ end
+
+ def test_exec_shell
+ with_tmpchdir {|d|
+ write_file("script1", <<-'End')
+ File.open("result1", "w") {|t| t << "tiki pid=#{$$} ppid=#{Process.ppid}" }
+ exit 7
+ End
+ write_file("script2", <<-'End')
+ File.open("result2", "w") {|t| t << "tiku pid=#{$$} ppid=#{Process.ppid}" }
+ exit 8
+ End
+ write_file("s", <<-"End")
+ ruby = #{RUBY.dump}
+ exec("\#{ruby} script1 || \#{ruby} script2")
+ End
+ pid = spawn RUBY, "s"
+ Process.wait pid
+ status = $?
+ assert_predicate(status, :exited?)
+ assert_not_predicate(status, :success?)
+ result1 = File.read("result1")
+ result2 = File.read("result2")
+ assert_match(/\Atiki pid=\d+ ppid=\d+\z/, result1)
+ assert_match(/\Atiku pid=\d+ ppid=\d+\z/, result2)
+ assert_not_equal(result1[/\d+/].to_i, status.pid)
+ }
+ end
+
+ def test_argv0
+ with_tmpchdir {|d|
+ assert_equal(false, system([RUBY, "asdfg"], "-e", "exit false"))
+ assert_equal(true, system([RUBY, "zxcvb"], "-e", "exit true"))
+
+ Process.wait spawn([RUBY, "poiu"], "-e", "exit 4")
+ assert_equal(4, $?.exitstatus)
+
+ assert_equal("1", IO.popen([[RUBY, "qwerty"], "-e", "print 1"]) {|f| f.read })
+
+ write_file("s", <<-"End")
+ exec([#{RUBY.dump}, "lkjh"], "-e", "exit 5")
+ End
+ pid = spawn RUBY, "s"
+ Process.wait pid
+ assert_equal(5, $?.exitstatus)
+ }
+ end
+
+ def with_stdin(filename)
+ open(filename) {|f|
+ begin
+ old = STDIN.dup
+ begin
+ STDIN.reopen(filename)
+ yield
+ ensure
+ STDIN.reopen(old)
+ end
+ ensure
+ old.close
+ end
+ }
+ end
+
+ def test_argv0_noarg
+ with_tmpchdir {|d|
+ open("t", "w") {|f| f.print "exit true" }
+ open("f", "w") {|f| f.print "exit false" }
+
+ with_stdin("t") { assert_equal(true, system([RUBY, "qaz"])) }
+ with_stdin("f") { assert_equal(false, system([RUBY, "wsx"])) }
+
+ with_stdin("t") { Process.wait spawn([RUBY, "edc"]) }
+ assert_predicate($?, :success?)
+ with_stdin("f") { Process.wait spawn([RUBY, "rfv"]) }
+ assert_not_predicate($?, :success?)
+
+ with_stdin("t") { IO.popen([[RUBY, "tgb"]]) {|io| assert_equal("", io.read) } }
+ assert_predicate($?, :success?)
+ with_stdin("f") { IO.popen([[RUBY, "yhn"]]) {|io| assert_equal("", io.read) } }
+ assert_not_predicate($?, :success?)
+
+ status = run_in_child "STDIN.reopen('t'); exec([#{RUBY.dump}, 'ujm'])"
+ assert_predicate(status, :success?)
+ status = run_in_child "STDIN.reopen('f'); exec([#{RUBY.dump}, 'ik,'])"
+ assert_not_predicate(status, :success?)
+ }
+ end
+
+ def test_status
+ with_tmpchdir do
+ s = run_in_child("exit 1")
+ assert_equal("#<Process::Status: pid #{ s.pid } exit #{ s.exitstatus }>", s.inspect)
+
+ assert_equal(s, s)
+ assert_equal(s, s.to_i)
+
+ assert_equal(s.to_i & 0x55555555, s & 0x55555555)
+ assert_equal(s.to_i >> 1, s >> 1)
+ assert_equal(false, s.stopped?)
+ assert_equal(nil, s.stopsig)
+ end
+ end
+
+ def test_status_kill
+ return unless Process.respond_to?(:kill)
+ return unless Signal.list.include?("KILL")
+
+ # assume the system supports signal if SIGQUIT is available
+ expected = Signal.list.include?("QUIT") ? [false, true, false, nil] : [true, false, false, true]
+
+ with_tmpchdir do
+ write_file("foo", "Process.kill(:KILL, $$); exit(42)")
+ system(RUBY, "foo")
+ s = $?
+ assert_equal(expected,
+ [s.exited?, s.signaled?, s.stopped?, s.success?],
+ "[s.exited?, s.signaled?, s.stopped?, s.success?]")
+ end
+ end
+
+ def test_status_quit
+ return unless Process.respond_to?(:kill)
+ return unless Signal.list.include?("QUIT")
+
+ with_tmpchdir do
+ write_file("foo", "puts;STDOUT.flush;sleep 30")
+ pid = nil
+ IO.pipe do |r, w|
+ pid = spawn(RUBY, "foo", out: w)
+ w.close
+ th = Thread.new { r.read(1); Process.kill(:SIGQUIT, pid) }
+ Process.wait(pid)
+ th.join
+ end
+ t = Time.now
+ s = $?
+ assert_equal([false, true, false, nil],
+ [s.exited?, s.signaled?, s.stopped?, s.success?],
+ "[s.exited?, s.signaled?, s.stopped?, s.success?]")
+ assert_send(
+ [["#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig })>",
+ "#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig }) (core dumped)>"],
+ :include?,
+ s.inspect])
+ EnvUtil.diagnostic_reports("QUIT", RUBY, pid, t)
+ end
+ end
+
+ def test_wait_without_arg
+ with_tmpchdir do
+ write_file("foo", "sleep 0.1")
+ pid = spawn(RUBY, "foo")
+ assert_equal(pid, Process.wait)
+ end
+ end
+
+ def test_wait2
+ with_tmpchdir do
+ write_file("foo", "sleep 0.1")
+ pid = spawn(RUBY, "foo")
+ assert_equal([pid, 0], Process.wait2)
+ end
+ end
+
+ def test_waitall
+ with_tmpchdir do
+ write_file("foo", "sleep 0.1")
+ ps = (0...3).map { spawn(RUBY, "foo") }.sort
+ ss = Process.waitall.sort
+ ps.zip(ss) do |p1, (p2, s)|
+ assert_equal(p1, p2)
+ assert_equal(p1, s.pid)
+ end
+ end
+ end
+
+ def test_wait_exception
+ bug11340 = '[ruby-dev:49176] [Bug #11340]'
+ t0 = t1 = nil
+ IO.popen([RUBY, '-e', 'puts;STDOUT.flush;Thread.start{gets;exit};sleep(3)'], 'r+') do |f|
+ pid = f.pid
+ f.gets
+ t0 = Time.now
+ th = Thread.start(Thread.current) do |main|
+ Thread.pass until main.stop?
+ main.raise Interrupt
+ end
+ begin
+ assert_raise(Interrupt) {Process.wait(pid)}
+ ensure
+ th.kill.join
+ end
+ t1 = Time.now
+ f.puts
+ end
+ assert_operator(t1 - t0, :<, 3,
+ ->{"#{bug11340}: #{t1-t0} seconds to interrupt Process.wait"})
+ end
+
+ def test_abort
+ with_tmpchdir do
+ s = run_in_child("abort")
+ assert_not_equal(0, s.exitstatus)
+ end
+ end
+
+ def test_sleep
+ assert_raise(ArgumentError) { sleep(1, 1) }
+ end
+
+ def test_getpgid
+ assert_kind_of(Integer, Process.getpgid(Process.ppid))
+ rescue NotImplementedError
+ end
+
+ def test_getpriority
+ assert_kind_of(Integer, Process.getpriority(Process::PRIO_PROCESS, $$))
+ rescue NameError, NotImplementedError
+ end
+
+ def test_setpriority
+ if defined? Process::PRIO_USER
+ assert_nothing_raised do
+ pr = Process.getpriority(Process::PRIO_PROCESS, $$)
+ Process.setpriority(Process::PRIO_PROCESS, $$, pr)
+ end
+ end
+ end
+
+ def test_getuid
+ assert_kind_of(Integer, Process.uid)
+ end
+
+ def test_groups
+ gs = Process.groups
+ assert_instance_of(Array, gs)
+ gs.each {|g| assert_kind_of(Integer, g) }
+ rescue NotImplementedError
+ end
+
+ def test_maxgroups
+ assert_kind_of(Integer, Process.maxgroups)
+ rescue NotImplementedError
+ end
+
+ def test_geteuid
+ assert_kind_of(Integer, Process.euid)
+ end
+
+ def test_seteuid
+ assert_nothing_raised(TypeError) {Process.euid += 0}
+ rescue NotImplementedError
+ end
+
+ def test_seteuid_name
+ user = ENV["USER"] or return
+ assert_nothing_raised(TypeError) {Process.euid = user}
+ rescue NotImplementedError
+ end
+
+ def test_getegid
+ assert_kind_of(Integer, Process.egid)
+ end
+
+ def test_setegid
+ assert_nothing_raised(TypeError) {Process.egid += 0}
+ rescue NotImplementedError
+ end
+
+ def test_uid_re_exchangeable_p
+ r = Process::UID.re_exchangeable?
+ assert_include([true, false], r)
+ end
+
+ def test_gid_re_exchangeable_p
+ r = Process::GID.re_exchangeable?
+ assert_include([true, false], r)
+ end
+
+ def test_uid_sid_available?
+ r = Process::UID.sid_available?
+ assert_include([true, false], r)
+ end
+
+ def test_gid_sid_available?
+ r = Process::GID.sid_available?
+ assert_include([true, false], r)
+ end
+
+ def test_pst_inspect
+ assert_nothing_raised { Process::Status.allocate.inspect }
+ end
+
+ def test_wait_and_sigchild
+ if /freebsd|openbsd/ =~ RUBY_PLATFORM
+ # this relates #4173
+ # When ruby can use 2 cores, signal and wait4 may miss the signal.
+ skip "this fails on FreeBSD and OpenBSD on multithreaded environment"
+ end
+ signal_received = []
+ Signal.trap(:CHLD) { signal_received << true }
+ pid = nil
+ IO.pipe do |r, w|
+ pid = fork { r.read(1); exit }
+ Thread.start { raise }
+ w.puts
+ end
+ Process.wait pid
+ 10.times do
+ break unless signal_received.empty?
+ sleep 0.01
+ end
+ assert_equal [true], signal_received, " [ruby-core:19744]"
+ rescue NotImplementedError, ArgumentError
+ ensure
+ begin
+ Signal.trap(:CHLD, 'DEFAULT')
+ rescue ArgumentError
+ end
+ end
+
+ def test_no_curdir
+ with_tmpchdir {|d|
+ Dir.mkdir("vd")
+ status = nil
+ Dir.chdir("vd") {
+ dir = "#{d}/vd"
+ # OpenSolaris cannot remove the current directory.
+ system(RUBY, "--disable-gems", "-e", "Dir.chdir '..'; Dir.rmdir #{dir.dump}", err: File::NULL)
+ system({"RUBYLIB"=>nil}, RUBY, "--disable-gems", "-e", "exit true")
+ status = $?
+ }
+ assert_predicate(status, :success?, "[ruby-dev:38105]")
+ }
+ end
+
+ def test_fallback_to_sh
+ feature = '[ruby-core:32745]'
+ with_tmpchdir do |d|
+ open("tmp_script.#{$$}", "w") {|f| f.puts ": ;"; f.chmod(0755)}
+ assert_not_nil(pid = Process.spawn("./tmp_script.#{$$}"), feature)
+ wpid, st = Process.waitpid2(pid)
+ assert_equal([pid, true], [wpid, st.success?], feature)
+
+ open("tmp_script.#{$$}", "w") {|f| f.puts "echo $#: $@"; f.chmod(0755)}
+ result = IO.popen(["./tmp_script.#{$$}", "a b", "c"]) {|f| f.read}
+ assert_equal("2: a b c\n", result, feature)
+
+ open("tmp_script.#{$$}", "w") {|f| f.puts "echo $hghg"; f.chmod(0755)}
+ result = IO.popen([{"hghg" => "mogomogo"}, "./tmp_script.#{$$}", "a b", "c"]) {|f| f.read}
+ assert_equal("mogomogo\n", result, feature)
+
+ end
+ end if File.executable?("/bin/sh")
+
+ def test_spawn_too_long_path
+ bug4314 = '[ruby-core:34842]'
+ assert_fail_too_long_path(%w"echo", bug4314)
+ end
+
+ def test_aspawn_too_long_path
+ bug4315 = '[ruby-core:34833]'
+ assert_fail_too_long_path(%w"echo |", bug4315)
+ end
+
+ def assert_fail_too_long_path((cmd, sep), mesg)
+ sep ||= ""
+ min = 1_000 / (cmd.size + sep.size)
+ cmds = Array.new(min, cmd)
+ exs = [Errno::ENOENT]
+ exs << Errno::E2BIG if defined?(Errno::E2BIG)
+ EnvUtil.suppress_warning do
+ assert_raise(*exs, mesg) do
+ begin
+ loop do
+ Process.spawn(cmds.join(sep), [STDOUT, STDERR]=>File::NULL)
+ min = [cmds.size, min].max
+ cmds *= 100
+ end
+ rescue NoMemoryError
+ size = cmds.size
+ raise if min >= size - 1
+ min = [min, size /= 2].max
+ cmds[size..-1] = []
+ raise if size < 250
+ retry
+ end
+ end
+ end
+ end
+
+ def test_system_sigpipe
+ return if windows?
+
+ pid = 0
+
+ with_tmpchdir do
+ assert_nothing_raised('[ruby-dev:12261]') do
+ timeout(3) do
+ pid = spawn('yes | ls')
+ Process.waitpid pid
+ end
+ end
+ end
+ ensure
+ Process.kill(:KILL, pid) if (pid != 0) rescue false
+ end
+
+ if Process.respond_to?(:daemon)
+ def test_daemon_default
+ data = IO.popen("-", "r+") do |f|
+ break f.read if f
+ Process.daemon
+ puts "ng"
+ end
+ assert_equal("", data)
+ end
+
+ def test_daemon_noclose
+ data = IO.popen("-", "r+") do |f|
+ break f.read if f
+ Process.daemon(false, true)
+ puts "ok", Dir.pwd
+ end
+ assert_equal("ok\n/\n", data)
+ end
+
+ def test_daemon_nochdir_noclose
+ data = IO.popen("-", "r+") do |f|
+ break f.read if f
+ Process.daemon(true, true)
+ puts "ok", Dir.pwd
+ end
+ assert_equal("ok\n#{Dir.pwd}\n", data)
+ end
+
+ def test_daemon_readwrite
+ data = IO.popen("-", "r+") do |f|
+ if f
+ f.puts "ok?"
+ break f.read
+ end
+ Process.daemon(true, true)
+ puts STDIN.gets
+ end
+ assert_equal("ok?\n", data)
+ end
+
+ def test_daemon_pid
+ cpid, dpid = IO.popen("-", "r+") do |f|
+ break f.pid, Integer(f.read) if f
+ Process.daemon(false, true)
+ puts $$
+ end
+ assert_not_equal(cpid, dpid)
+ end
+
+ if File.directory?("/proc/self/task") && /netbsd[a-z]*[1-6]/ !~ RUBY_PLATFORM
+ def test_daemon_no_threads
+ pid, data = IO.popen("-", "r+") do |f|
+ break f.pid, f.readlines if f
+ Process.daemon(true, true)
+ puts Dir.entries("/proc/self/task") - %W[. ..]
+ end
+ bug4920 = '[ruby-dev:43873]'
+ assert_equal(2, data.size, bug4920)
+ assert_not_include(data.map(&:to_i), pid)
+ end
+ else # darwin
+ def test_daemon_no_threads
+ data = Timeout.timeout(3) do
+ IO.popen("-") do |f|
+ break f.readlines.map(&:chomp) if f
+ th = Thread.start {sleep 3}
+ Process.daemon(true, true)
+ puts Thread.list.size, th.status.inspect
+ end
+ end
+ assert_equal(["1", "false"], data)
+ end
+ end
+ end
+
+ def test_popen_cloexec
+ return unless defined? Fcntl::FD_CLOEXEC
+ IO.popen([RUBY, "-e", ""]) {|io|
+ assert_predicate(io, :close_on_exec?)
+ }
+ end
+
+ def test_execopts_new_pgroup
+ return unless windows?
+
+ assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>true) }
+ assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>false) }
+ assert_nothing_raised { spawn(*TRUECOMMAND, :new_pgroup=>true) }
+ assert_nothing_raised { IO.popen([*TRUECOMMAND, :new_pgroup=>true]) {} }
+ end
+
+ def test_execopts_uid
+ feature6975 = '[ruby-core:47414]'
+
+ [30000, [Process.uid, ENV["USER"]]].each do |uid, user|
+ if user
+ assert_nothing_raised(feature6975) do
+ begin
+ system(*TRUECOMMAND, uid: user)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+ end
+
+ assert_nothing_raised(feature6975) do
+ begin
+ system(*TRUECOMMAND, uid: uid)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+
+ assert_nothing_raised(feature6975) do
+ begin
+ u = IO.popen([RUBY, "-e", "print Process.uid", uid: user||uid], &:read)
+ assert_equal(uid.to_s, u, feature6975)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+ end
+ end
+
+ def test_execopts_gid
+ skip "Process.groups not implemented on Windows platform" if windows?
+ feature6975 = '[ruby-core:47414]'
+
+ [30000, *Process.groups.map {|g| g = Etc.getgrgid(g); [g.name, g.gid]}].each do |group, gid|
+ assert_nothing_raised(feature6975) do
+ begin
+ system(*TRUECOMMAND, gid: group)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+
+ gid = "#{gid || group}"
+ assert_nothing_raised(feature6975) do
+ begin
+ g = IO.popen([RUBY, "-e", "print Process.gid", gid: group], &:read)
+ assert_equal(gid, g, feature6975)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+ end
+ end
+
+ def test_sigpipe
+ system(RUBY, "-e", "")
+ with_pipe {|r, w|
+ r.close
+ assert_raise(Errno::EPIPE) { w.print "a" }
+ }
+ end
+
+ def test_sh_comment
+ IO.popen("echo a # fofoof") {|f|
+ assert_equal("a\n", f.read)
+ }
+ end if File.executable?("/bin/sh")
+
+ def test_sh_env
+ IO.popen("foofoo=barbar env") {|f|
+ lines = f.readlines
+ assert_operator(lines, :include?, "foofoo=barbar\n")
+ }
+ end if File.executable?("/bin/sh")
+
+ def test_sh_exec
+ IO.popen("exec echo exexexec") {|f|
+ assert_equal("exexexec\n", f.read)
+ }
+ end if File.executable?("/bin/sh")
+
+ def test_setsid
+ return unless Process.respond_to?(:setsid)
+ return unless Process.respond_to?(:getsid)
+ # OpenBSD and AIX don't allow Process::getsid(pid) when pid is in
+ # different session.
+ return if /openbsd|aix/ =~ RUBY_PLATFORM
+
+ IO.popen([RUBY, "-e", <<EOS]) do|io|
+ Marshal.dump(Process.getsid, STDOUT)
+ newsid = Process.setsid
+ Marshal.dump(newsid, STDOUT)
+ STDOUT.flush
+ # getsid() on MacOS X return ESRCH when target process is zombie
+ # even if it is valid process id.
+ sleep
+EOS
+ begin
+ # test Process.getsid() w/o arg
+ assert_equal(Marshal.load(io), Process.getsid)
+
+ # test Process.setsid return value and Process::getsid(pid)
+ assert_equal(Marshal.load(io), Process.getsid(io.pid))
+ ensure
+ Process.kill(:KILL, io.pid) rescue nil
+ Process.wait(io.pid)
+ end
+ end
+ end
+
+ def test_spawn_nonascii
+ bug1771 = '[ruby-core:24309] [Bug #1771]'
+
+ with_tmpchdir do
+ [
+ "\u{7d05 7389}",
+ "zuf\u{00E4}llige_\u{017E}lu\u{0165}ou\u{010D}k\u{00FD}_\u{10D2 10D0 10DB 10D4 10DD 10E0 10D4 10D1}_\u{0440 0430 0437 043B 043E 0433 0430}_\u{548C 65B0 52A0 5761 4EE5 53CA 4E1C}",
+ "c\u{1EE7}a",
+ ].each do |name|
+ msg = "#{bug1771} #{name}"
+ exename = "./#{name}.exe"
+ FileUtils.cp(ENV["COMSPEC"], exename)
+ assert_equal(true, system("#{exename} /c exit"), msg)
+ system("#{exename} /c exit 12")
+ assert_equal(12, $?.exitstatus, msg)
+ _, status = Process.wait2(Process.spawn("#{exename} /c exit 42"))
+ assert_equal(42, status.exitstatus, msg)
+ assert_equal("ok\n", `#{exename} /c echo ok`, msg)
+ assert_equal("ok\n", IO.popen("#{exename} /c echo ok", &:read), msg)
+ assert_equal("ok\n", IO.popen(%W"#{exename} /c echo ok", &:read), msg)
+ File.binwrite("#{name}.txt", "ok")
+ assert_equal("ok", `type #{name}.txt`)
+ end
+ end
+ end if windows?
+
+ def test_clock_gettime
+ t1 = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ t2 = Time.now; t2 = t2.tv_sec * 1000000000 + t2.tv_nsec
+ t3 = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ assert_operator(t1, :<=, t2)
+ assert_operator(t2, :<=, t3)
+ assert_raise(Errno::EINVAL) { Process.clock_gettime(:foo) }
+ end
+
+ def test_clock_gettime_unit
+ t0 = Time.now.to_f
+ [
+ [:nanosecond, 1_000_000_000],
+ [:microsecond, 1_000_000],
+ [:millisecond, 1_000],
+ [:second, 1],
+ [:float_microsecond, 1_000_000.0],
+ [:float_millisecond, 1_000.0],
+ [:float_second, 1.0],
+ [nil, 1.0],
+ [:foo],
+ ].each do |unit, num|
+ unless num
+ assert_raise(ArgumentError){ Process.clock_gettime(Process::CLOCK_REALTIME, unit) }
+ next
+ end
+ t1 = Process.clock_gettime(Process::CLOCK_REALTIME, unit)
+ assert_kind_of num.integer? ? Integer : num.class, t1, [unit, num].inspect
+ assert_in_delta t0, t1/num, 1, [unit, num].inspect
+ end
+ end
+
+ def test_clock_gettime_constants
+ Process.constants.grep(/\ACLOCK_/).each {|n|
+ c = Process.const_get(n)
+ begin
+ t = Process.clock_gettime(c)
+ rescue Errno::EINVAL
+ next
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(Process::#{n})")
+ }
+ end
+
+ def test_clock_gettime_GETTIMEOFDAY_BASED_CLOCK_REALTIME
+ n = :GETTIMEOFDAY_BASED_CLOCK_REALTIME
+ t = Process.clock_gettime(n)
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_TIME_BASED_CLOCK_REALTIME
+ n = :TIME_BASED_CLOCK_REALTIME
+ t = Process.clock_gettime(n)
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_TIMES_BASED_CLOCK_MONOTONIC
+ n = :TIMES_BASED_CLOCK_MONOTONIC
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
+ t = Process.clock_gettime(n)
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
+ n = :MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_getres
+ r = Process.clock_getres(Process::CLOCK_REALTIME, :nanosecond)
+ rescue Errno::EINVAL
+ else
+ assert_kind_of(Integer, r)
+ assert_raise(Errno::EINVAL) { Process.clock_getres(:foo) }
+ end
+
+ def test_clock_getres_constants
+ Process.constants.grep(/\ACLOCK_/).each {|n|
+ c = Process.const_get(n)
+ begin
+ t = Process.clock_getres(c)
+ rescue Errno::EINVAL
+ next
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(Process::#{n})")
+ }
+ end
+
+ def test_clock_getres_GETTIMEOFDAY_BASED_CLOCK_REALTIME
+ n = :GETTIMEOFDAY_BASED_CLOCK_REALTIME
+ t = Process.clock_getres(n)
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ assert_equal(1000, Process.clock_getres(n, :nanosecond))
+ end
+
+ def test_clock_getres_TIME_BASED_CLOCK_REALTIME
+ n = :TIME_BASED_CLOCK_REALTIME
+ t = Process.clock_getres(n)
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ assert_equal(1000000000, Process.clock_getres(n, :nanosecond))
+ end
+
+ def test_clock_getres_TIMES_BASED_CLOCK_MONOTONIC
+ n = :TIMES_BASED_CLOCK_MONOTONIC
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ f = Process.clock_getres(n, :hertz)
+ assert_equal(0, f - f.floor)
+ end
+
+ def test_clock_getres_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ assert_equal(1000, Process.clock_getres(n, :nanosecond))
+ end
+
+ def test_clock_getres_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ f = Process.clock_getres(n, :hertz)
+ assert_equal(0, f - f.floor)
+ end
+
+ def test_clock_getres_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
+ t = Process.clock_getres(n)
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ f = Process.clock_getres(n, :hertz)
+ assert_equal(0, f - f.floor)
+ end
+
+ def test_clock_getres_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
+ n = :MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ end
+
+ def test_deadlock_by_signal_at_forking
+ assert_separately([], <<-INPUT, timeout: 60)
+ require 'io/wait'
+ begin
+ GC.start # reduce garbage
+ buf = ''
+ ruby = EnvUtil.rubybin
+ er, ew = IO.pipe
+ unless runner = IO.popen("-".freeze)
+ er.close
+ status = true
+ GC.disable # avoid triggering CoW after forks
+ begin
+ $stderr.reopen($stdout)
+ trap(:QUIT) {}
+ parent = $$
+ 100.times do |i|
+ pid = fork {Process.kill(:QUIT, parent)}
+ IO.popen(ruby, 'r+'.freeze){}
+ Process.wait(pid)
+ $stdout.puts
+ $stdout.flush
+ end
+ ensure
+ if $!
+ ew.puts([Marshal.dump($!)].pack("m0"))
+ status = false
+ end
+ ew.close
+ exit!(status)
+ end
+ end
+ ew.close
+ begin
+ loop do
+ runner.wait_readable(5)
+ runner.read_nonblock(100, buf)
+ end
+ rescue EOFError => e
+ _, status = Process.wait2(runner.pid)
+ rescue IO::WaitReadable => e
+ Process.kill(:INT, runner.pid)
+ exc = Marshal.load(er.read.unpack("m")[0])
+ if exc.kind_of? Interrupt
+ # Don't raise Interrupt. It aborts test-all.
+ flunk "timeout"
+ else
+ raise exc
+ end
+ end
+ assert_predicate(status, :success?)
+ ensure
+ er.close unless er.closed?
+ ew.close unless ew.closed?
+ if runner
+ begin
+ Process.kill(:TERM, runner.pid)
+ sleep 1
+ Process.kill(:KILL, runner.pid)
+ rescue Errno::ESRCH
+ end
+ runner.close
+ end
+ end
+ INPUT
+ end if defined?(fork)
+
+ def test_process_detach
+ pid = fork {}
+ th = Process.detach(pid)
+ assert_equal pid, th.pid
+ status = th.value
+ assert status.success?, status.inspect
+ end if defined?(fork)
+end
diff --git a/jni/ruby/test/ruby/test_rand.rb b/jni/ruby/test/ruby/test_rand.rb
new file mode 100644
index 0000000..d2a1244
--- /dev/null
+++ b/jni/ruby/test/ruby/test_rand.rb
@@ -0,0 +1,527 @@
+require 'test/unit'
+
+class TestRand < Test::Unit::TestCase
+ def assert_random_int(ws, m, init = 0)
+ srand(init)
+ rnds = [Random.new(init)]
+ rnds2 = [rnds[0].dup]
+ rnds3 = [rnds[0].dup]
+ ws.each_with_index do |w, i|
+ w = w.to_i
+ assert_equal(w, rand(m))
+ rnds.each do |rnd|
+ assert_equal(w, rnd.rand(m))
+ end
+ rnds2.each do |rnd|
+ r=rnd.rand(i...(m+i))
+ assert_equal(w+i, r)
+ end
+ rnds3.each do |rnd|
+ r=rnd.rand(i..(m+i-1))
+ assert_equal(w+i, r)
+ end
+ rnds << Marshal.load(Marshal.dump(rnds[-1]))
+ rnds2 << Marshal.load(Marshal.dump(rnds2[-1]))
+ end
+ end
+
+ def test_mt
+ assert_random_int(%w(1067595299 955945823 477289528 4107218783 4228976476),
+ 0x100000000, 0x00000456_00000345_00000234_00000123)
+ end
+
+ def test_0x3fffffff
+ assert_random_int(%w(209652396 398764591 924231285 404868288 441365315),
+ 0x3fffffff)
+ end
+
+ def test_0x40000000
+ assert_random_int(%w(209652396 398764591 924231285 404868288 441365315),
+ 0x40000000)
+ end
+
+ def test_0x40000001
+ assert_random_int(%w(209652396 398764591 924231285 441365315 192771779),
+ 0x40000001)
+ end
+
+ def test_0xffffffff
+ assert_random_int(%w(2357136044 2546248239 3071714933 3626093760 2588848963),
+ 0xffffffff)
+ end
+
+ def test_0x100000000
+ assert_random_int(%w(2357136044 2546248239 3071714933 3626093760 2588848963),
+ 0x100000000)
+ end
+
+ def test_0x100000001
+ assert_random_int(%w(2546248239 1277901399 243580376 1171049868 2051556033),
+ 0x100000001)
+ end
+
+ def test_rand_0x100000000
+ assert_random_int(%w(4119812344 3870378946 80324654 4294967296 410016213),
+ 0x100000001, 311702798)
+ end
+
+ def test_0x1000000000000
+ assert_random_int(%w(11736396900911
+ 183025067478208
+ 197104029029115
+ 130583529618791
+ 180361239846611),
+ 0x1000000000000)
+ end
+
+ def test_0x1000000000001
+ assert_random_int(%w(187121911899765
+ 197104029029115
+ 180361239846611
+ 236336749852452
+ 208739549485656),
+ 0x1000000000001)
+ end
+
+ def test_0x3fffffffffffffff
+ assert_random_int(%w(900450186894289455
+ 3969543146641149120
+ 1895649597198586619
+ 827948490035658087
+ 3203365596207111891),
+ 0x3fffffffffffffff)
+ end
+
+ def test_0x4000000000000000
+ assert_random_int(%w(900450186894289455
+ 3969543146641149120
+ 1895649597198586619
+ 827948490035658087
+ 3203365596207111891),
+ 0x4000000000000000)
+ end
+
+ def test_0x4000000000000001
+ assert_random_int(%w(900450186894289455
+ 3969543146641149120
+ 1895649597198586619
+ 827948490035658087
+ 2279347887019741461),
+ 0x4000000000000001)
+ end
+
+ def test_0x10000000000
+ ws = %w(455570294424 1073054410371 790795084744 2445173525 1088503892627)
+ assert_random_int(ws, 0x10000000000, 3)
+ end
+
+ def test_0x10000
+ ws = %w(2732 43567 42613 52416 45891)
+ assert_random_int(ws, 0x10000)
+ end
+
+ def test_types
+ srand(0)
+ rnd = Random.new(0)
+ assert_equal(44, rand(100.0))
+ assert_equal(44, rnd.rand(100))
+ assert_equal(1245085576965981900420779258691, rand((2**100).to_f))
+ assert_equal(1245085576965981900420779258691, rnd.rand(2**100))
+ assert_equal(914679880601515615685077935113, rand(-(2**100).to_f))
+
+ srand(0)
+ rnd = Random.new(0)
+ assert_equal(997707939797331598305742933184, rand(2**100))
+ assert_equal(997707939797331598305742933184, rnd.rand(2**100))
+ assert_in_delta(0.602763376071644, rand((2**100).coerce(0).first),
+ 0.000000000000001)
+ assert_raise(ArgumentError) {rnd.rand((2**100).coerce(0).first)}
+
+ srand(0)
+ rnd = Random.new(0)
+ assert_in_delta(0.548813503927325, rand(nil),
+ 0.000000000000001)
+ assert_in_delta(0.548813503927325, rnd.rand(),
+ 0.000000000000001)
+ srand(0)
+ rnd = Random.new(0)
+ o = Object.new
+ def o.to_int; 100; end
+ assert_equal(44, rand(o))
+ assert_equal(44, rnd.rand(o))
+ assert_equal(47, rand(o))
+ assert_equal(47, rnd.rand(o))
+ assert_equal(64, rand(o))
+ assert_equal(64, rnd.rand(o))
+ end
+
+ def test_srand
+ srand
+ assert_kind_of(Integer, rand(2))
+ assert_kind_of(Integer, Random.new.rand(2))
+
+ srand(2**100)
+ rnd = Random.new(2**100)
+ %w(3258412053).each {|w|
+ assert_equal(w.to_i, rand(0x100000000))
+ assert_equal(w.to_i, rnd.rand(0x100000000))
+ }
+ end
+
+ def test_shuffle
+ srand(0)
+ result = [*1..5].shuffle
+ assert_equal([*1..5], result.sort)
+ assert_equal(result, [*1..5].shuffle(random: Random.new(0)))
+ end
+
+ def test_big_seed
+ assert_random_int(%w(2757555016), 0x100000000, 2**1000000-1)
+ end
+
+ def test_random_gc
+ r = Random.new(0)
+ %w(2357136044 2546248239 3071714933).each do |w|
+ assert_equal(w.to_i, r.rand(0x100000000))
+ end
+ GC.start
+ %w(3626093760 2588848963 3684848379).each do |w|
+ assert_equal(w.to_i, r.rand(0x100000000))
+ end
+ end
+
+ def test_random_type_error
+ assert_raise(TypeError) { Random.new(Object.new) }
+ assert_raise(TypeError) { Random.new(0).rand(Object.new) }
+ end
+
+ def test_random_argument_error
+ r = Random.new(0)
+ assert_raise(ArgumentError) { r.rand(0, 0) }
+ assert_raise(ArgumentError, '[ruby-core:24677]') { r.rand(-1) }
+ assert_raise(ArgumentError, '[ruby-core:24677]') { r.rand(-1.0) }
+ assert_raise(ArgumentError, '[ruby-core:24677]') { r.rand(0) }
+ assert_equal(0, r.rand(1), '[ruby-dev:39166]')
+ assert_equal(0, r.rand(0...1), '[ruby-dev:39166]')
+ assert_equal(0, r.rand(0..0), '[ruby-dev:39166]')
+ assert_equal(0.0, r.rand(0.0..0.0), '[ruby-dev:39166]')
+ assert_raise(ArgumentError, '[ruby-dev:39166]') { r.rand(0...0) }
+ assert_raise(ArgumentError, '[ruby-dev:39166]') { r.rand(0..-1) }
+ assert_raise(ArgumentError, '[ruby-dev:39166]') { r.rand(0.0...0.0) }
+ assert_raise(ArgumentError, '[ruby-dev:39166]') { r.rand(0.0...-0.1) }
+ bug3027 = '[ruby-core:29075]'
+ assert_raise(ArgumentError, bug3027) { r.rand(nil) }
+ end
+
+ def test_random_seed
+ assert_equal(0, Random.new(0).seed)
+ assert_equal(0x100000000, Random.new(0x100000000).seed)
+ assert_equal(2**100, Random.new(2**100).seed)
+ end
+
+ def test_random_dup
+ r1 = Random.new(0)
+ r2 = r1.dup
+ %w(2357136044 2546248239 3071714933).each do |w|
+ assert_equal(w.to_i, r1.rand(0x100000000))
+ end
+ %w(2357136044 2546248239 3071714933).each do |w|
+ assert_equal(w.to_i, r2.rand(0x100000000))
+ end
+ r2 = r1.dup
+ %w(3626093760 2588848963 3684848379).each do |w|
+ assert_equal(w.to_i, r1.rand(0x100000000))
+ end
+ %w(3626093760 2588848963 3684848379).each do |w|
+ assert_equal(w.to_i, r2.rand(0x100000000))
+ end
+ end
+
+ def test_random_state
+ state = <<END
+3877134065023083674777481835852171977222677629000095857864323111193832400974413
+4782302161934463784850675209112299537259006497924090422596764895633625964527441
+6943943249411681406395713106007661119327771293929504639878577616749110507385924
+0173026285378896836022134086386136835407107422834685854738117043791709411958489
+3504364936306163473541948635570644161010981140452515307286926529085424765299100
+1255453260115310687580777474046203049197643434654645011966794531914127596390825
+0832232869378617194193100828000236737535657699356156021286278281306055217995213
+8911536025132779573429499813926910299964681785069915877910855089314686097947757
+2621451199734871158015842198110309034467412292693435515184023707918034746119728
+8223459645048255809852819129671833854560563104716892857257229121211527031509280
+2390605053896565646658122125171846129817536096211475312518457776328637574563312
+8113489216547503743508184872149896518488714209752552442327273883060730945969461
+6568672445225657265545983966820639165285082194907591432296265618266901318398982
+0560425129536975583916120558652408261759226803976460322062347123360444839683204
+9868507788028894111577023917218846128348302845774997500569465902983227180328307
+3735301552935104196244116381766459468172162284042207680945316590536094294865648
+5953156978630954893391701383648157037914019502853776972615500142898763385846315
+8457790690531675205213829055442306187692107777193071680668153335688203945049935
+3404449910419303330872435985327845889440458370416464132629866593538629877042969
+7589948685901343135964343582727302330074331803900821801076139161904900333497836
+6627028345784450211920229170280462474456504317367706849373957309797251052447898
+8436235456995644876515032202483449807136139063937892187299239634252391529822163
+9187055268750679730919937006195967184206072757082920250075756273182004964087790
+3812024063897424219316687828337007888030994779068081666133751070479581394604974
+6022215489604777611774245181115126041390161592199230774968475944753915533936834
+4740049163514318351045644344358598159463605453475585370226041981040238023241538
+4958436364776113598408428801867643946791659645708540669432995503575075657406359
+8086928867900590554805639837071298576728564946552163206007997000988745940681607
+4542883814997403673656291618517107133421335645430345871041730410669209035640945
+5024601618318371192091626092482640364669969307766919645222516407626038616667754
+5781148898846306894862390724358039251444333889446128074209417936830253204064223
+3424784857908022314095011879203259864909560830176189727132432100010493659154644
+8407326292884826469503093409465946018496358514999175268200846200025235441426140
+7783386235191526371372655894290440356560751680752191224383460972099834655086068
+9989413443881686756951804138910911737670495391762470293978321414964443502180391
+4665982575919524372985773336921990352313629822677022891307943536442258282401255
+5387646898976193134193506239982621725093291970351083631367582570375381334759004
+1784150668048523676387894646666460369896619585113435743180899362844070393586212
+5023920017185866399742380706352739465848708746963693663004068892056705603018655
+8686663087894205699555906146534549176352859823832196938386172810274748624517052
+8356758650653040545267425513047130342286119889879774951060662713807133125543465
+5104086026298674827575216701372513525846650644773241437066782037334367982012148
+7987782004646896468089089826267467005660035604553432197455616078731159778086155
+9443250946037119223468305483694093795324036812927501783593256716590840500905291
+2096608538205270323065573109227029887731553399324547696295234105140157179430410
+4003109602564833086703863221058381556776789018351726488797845637981974580864082
+1630093543020854542240690897858757985640869209737744458407777584279553258261599
+0246922348101147034463235613998979344685018577901996218099622190722307356620796
+5137485271371502385527388080824050288371607602101805675021790116223360483508538
+8832149997794718410946818866375912486788005950091851067237358294899771385995876
+7088239104394332452501033090159333224995108984871426750597513314521294001864578
+2353528356752869732412552685554334966798888534847483030947310518891788722418172
+6008607577773612004956373863580996793809969715725508939568919714424871639667201
+7922255031431159347210833846575355772055570279673262115911154370983086189948124
+4653677615895887099814174914248255026619941911735341818489822197472499295786997
+7728418516719104857455960900092226749725407204388193002835497055305427730656889
+1508308778869166073740855838213112709306743479676740893150000714099064468263284
+1873435518542972182497755500300784177067568586395485329021157235696300013490087
+2866571034916258390528533374944905429089028336079264760836949419754851422499614
+5732326011260304142074554782259843903215064144396140106592193961703288125005023
+5334375212799817540775536847622032852415253966587517800661605905489339306359573
+2234947905196298436841723673626428243649931398749552780311877734063703985375067
+1239508613417041942487245370152912391885566432830659640677893488723724763120121
+4111855277511356759926232894062814360449757490961653026194107761340614059045172
+1123363102660719217740126157997033682099769790976313166682432732518101889210276
+9574144065390305904944821051736021310524344626348851573631697771556587859836330
+6997324121866564283654784470215100159122764509197570402997911258816526554863326
+9877535269005418736225944874608987238997316999444215865249840762640949599725696
+0773083894168959823152054508672272612355108904098579447774398451678239199426513
+3439507737424049578587487505080347686371029156845461151278198605267053408259090
+3158676794894709281917034995611352710898103415304769654883981727681820369090169
+9295163908214854813365413456264812190842699054830709079275249714169405719140093
+1347572458245530016346604698682269779841803667099480215265926316505737171177810
+9969036572310084022695109125200937135540995157279354438704321290061646592229860
+0156566013602344870223183295508278359111174872740360473845615437106413256386849
+2286259982118315248148847764929974917157683083659364623458927512616369119194574
+2254080
+END
+ state = state.split.join.to_i
+ r = Random.new(0)
+ srand(0)
+ assert_equal(state, r.instance_eval { state })
+ assert_equal(state, Random.instance_eval { state })
+ r.rand(0x100)
+ assert_equal(state, r.instance_eval { state })
+ end
+
+ def test_random_left
+ r = Random.new(0)
+ assert_equal(1, r.instance_eval { left })
+ r.rand(0x100)
+ assert_equal(624, r.instance_eval { left })
+ r.rand(0x100)
+ assert_equal(623, r.instance_eval { left })
+ srand(0)
+ assert_equal(1, Random.instance_eval { left })
+ rand(0x100)
+ assert_equal(624, Random.instance_eval { left })
+ rand(0x100)
+ assert_equal(623, Random.instance_eval { left })
+ end
+
+ def test_random_bytes
+ assert_random_bytes(Random.new(0))
+ end
+
+ def assert_random_bytes(r)
+ assert_equal("", r.bytes(0))
+ assert_equal("\xAC".force_encoding("ASCII-8BIT"), r.bytes(1))
+ assert_equal("/\xAA\xC4\x97u\xA6\x16\xB7\xC0\xCC".force_encoding("ASCII-8BIT"),
+ r.bytes(10))
+ end
+
+ def test_random_range
+ srand(0)
+ r = Random.new(0)
+ %w(9 5 8).each {|w|
+ assert_equal(w.to_i, rand(5..9))
+ assert_equal(w.to_i, r.rand(5..9))
+ }
+ %w(-237 731 383).each {|w|
+ assert_equal(w.to_i, rand(-1000..1000))
+ assert_equal(w.to_i, r.rand(-1000..1000))
+ }
+ %w(1267650600228229401496703205382
+ 1267650600228229401496703205384
+ 1267650600228229401496703205383).each do |w|
+ assert_equal(w.to_i, rand(2**100+5..2**100+9))
+ assert_equal(w.to_i, r.rand(2**100+5..2**100+9))
+ end
+
+ v = rand(3.1..4)
+ assert_instance_of(Float, v, '[ruby-core:24679]')
+ assert_include(3.1..4, v)
+
+ v = r.rand(3.1..4)
+ assert_instance_of(Float, v, '[ruby-core:24679]')
+ assert_include(3.1..4, v)
+
+ now = Time.now
+ assert_equal(now, rand(now..now))
+ assert_equal(now, r.rand(now..now))
+ end
+
+ def test_random_float
+ r = Random.new(0)
+ assert_in_delta(0.5488135039273248, r.rand, 0.0001)
+ assert_in_delta(0.7151893663724195, r.rand, 0.0001)
+ assert_in_delta(0.6027633760716439, r.rand, 0.0001)
+ assert_in_delta(1.0897663659937937, r.rand(2.0), 0.0001)
+ assert_in_delta(5.3704626067153264e+29, r.rand((2**100).to_f), 10**25)
+
+ assert_raise(Errno::EDOM, Errno::ERANGE) { r.rand(1.0 / 0.0) }
+ assert_raise(Errno::EDOM, Errno::ERANGE) { r.rand(0.0 / 0.0) }
+
+ r = Random.new(0)
+ assert_in_delta(1.5488135039273248, r.rand(1.0...2.0), 0.0001, '[ruby-core:24655]')
+ assert_in_delta(1.7151893663724195, r.rand(1.0...2.0), 0.0001, '[ruby-core:24655]')
+ assert_in_delta(7.027633760716439, r.rand(1.0...11.0), 0.0001, '[ruby-core:24655]')
+ assert_in_delta(3.0897663659937937, r.rand(2.0...4.0), 0.0001, '[ruby-core:24655]')
+
+ assert_nothing_raised {r.rand(-Float::MAX..Float::MAX)}
+ end
+
+ def test_random_equal
+ r = Random.new(0)
+ assert_equal(r, r)
+ assert_equal(r, r.dup)
+ r1 = r.dup
+ r2 = r.dup
+ r1.rand(0x100)
+ assert_not_equal(r1, r2)
+ r2.rand(0x100)
+ assert_equal(r1, r2)
+ end
+
+ def test_fork_shuffle
+ pid = fork do
+ (1..10).to_a.shuffle
+ raise 'default seed is not set' if srand == 0
+ end
+ _, st = Process.waitpid2(pid)
+ assert_predicate(st, :success?, "#{st.inspect}")
+ rescue NotImplementedError, ArgumentError
+ end
+
+ def assert_fork_status(n, mesg, &block)
+ IO.pipe do |r, w|
+ (1..n).map do
+ p1 = fork {w.puts(block.call.to_s)}
+ _, st = Process.waitpid2(p1)
+ assert_send([st, :success?], mesg)
+ r.gets.strip
+ end
+ end
+ end
+
+ def test_rand_reseed_on_fork
+ GC.start
+ bug5661 = '[ruby-core:41209]'
+
+ assert_fork_status(1, bug5661) {Random.rand(4)}
+ r1, r2 = *assert_fork_status(2, bug5661) {Random.rand}
+ assert_not_equal(r1, r2, bug5661)
+
+ assert_fork_status(1, bug5661) {rand(4)}
+ r1, r2 = *assert_fork_status(2, bug5661) {rand}
+ assert_not_equal(r1, r2, bug5661)
+
+ stable = Random.new
+ assert_fork_status(1, bug5661) {stable.rand(4)}
+ r1, r2 = *assert_fork_status(2, bug5661) {stable.rand}
+ assert_equal(r1, r2, bug5661)
+ rescue NotImplementedError
+ end
+
+ def test_seed
+ bug3104 = '[ruby-core:29292]'
+ rand_1 = Random.new(-1).rand
+ assert_not_equal(rand_1, Random.new((1 << 31) -1).rand, "#{bug3104} (2)")
+ assert_not_equal(rand_1, Random.new((1 << 63) -1).rand, "#{bug3104} (2)")
+
+ [-1, -2**10, -2**40].each {|n|
+ b = (2**64).coerce(n)[0]
+ r1 = Random.new(n).rand
+ r2 = Random.new(b).rand
+ assert_equal(r1, r2)
+ }
+ end
+
+ def test_default
+ r1 = Random::DEFAULT.dup
+ r2 = Random::DEFAULT.dup
+ 3.times do
+ x0 = rand
+ x1 = r1.rand
+ x2 = r2.rand
+ assert_equal(x0, x1)
+ assert_equal(x0, x2)
+ end
+ end
+
+ def test_marshal
+ bug3656 = '[ruby-core:31622]'
+ assert_raise(TypeError, bug3656) {
+ Random.new.__send__(:marshal_load, 0)
+ }
+ end
+
+ def test_initialize_frozen
+ r = Random.new(0)
+ r.freeze
+ assert_raise(RuntimeError, '[Bug #6540]') do
+ r.__send__(:initialize, r)
+ end
+ end
+
+ def test_marshal_load_frozen
+ r = Random.new(0)
+ d = r.__send__(:marshal_dump)
+ r.freeze
+ assert_raise(RuntimeError, '[Bug #6540]') do
+ r.__send__(:marshal_load, d)
+ end
+ end
+
+ def test_random_ulong_limited
+ def (gen = Object.new).rand(*) 1 end
+ assert_equal([2], (1..100).map {[1,2,3].sample(random: gen)}.uniq)
+
+ def (gen = Object.new).rand(*) 100 end
+ assert_raise_with_message(RangeError, /big 100\z/) {[1,2,3].sample(random: gen)}
+
+ bug7903 = '[ruby-dev:47061] [Bug #7903]'
+ def (gen = Object.new).rand(*) -1 end
+ assert_raise_with_message(RangeError, /small -1\z/, bug7903) {[1,2,3].sample(random: gen)}
+
+ bug7935 = '[ruby-core:52779] [Bug #7935]'
+ class << (gen = Object.new)
+ def rand(limit) @limit = limit; 0 end
+ attr_reader :limit
+ end
+ [1, 2].sample(1, random: gen)
+ assert_equal(2, gen.limit, bug7935)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_range.rb b/jni/ruby/test/ruby/test_range.rb
new file mode 100644
index 0000000..852515f
--- /dev/null
+++ b/jni/ruby/test/ruby/test_range.rb
@@ -0,0 +1,583 @@
+require 'test/unit'
+require 'delegate'
+require 'timeout'
+require 'bigdecimal'
+
+class TestRange < Test::Unit::TestCase
+ def test_range_string
+ # XXX: Is this really the test of Range?
+ assert_equal([], ("a" ... "a").to_a)
+ assert_equal(["a"], ("a" .. "a").to_a)
+ assert_equal(["a"], ("a" ... "b").to_a)
+ assert_equal(["a", "b"], ("a" .. "b").to_a)
+ end
+
+ def test_range_numeric_string
+ assert_equal(["6", "7", "8"], ("6".."8").to_a, "[ruby-talk:343187]")
+ assert_equal(["6", "7"], ("6"..."8").to_a)
+ assert_equal(["9", "10"], ("9".."10").to_a)
+ assert_equal(["09", "10"], ("09".."10").to_a, "[ruby-dev:39361]")
+ assert_equal(["9", "10"], (SimpleDelegator.new("9").."10").to_a)
+ assert_equal(["9", "10"], ("9"..SimpleDelegator.new("10")).to_a)
+ end
+
+ def test_range_symbol
+ assert_equal([:a, :b], (:a .. :b).to_a)
+ end
+
+ def test_evaluation_order
+ arr = [1,2]
+ r = (arr.shift)..(arr.shift)
+ assert_equal(1..2, r, "[ruby-dev:26383]")
+ end
+
+ class DuckRange
+ def initialize(b,e,excl=false)
+ @begin = b
+ @end = e
+ @excl = excl
+ end
+ attr_reader :begin, :end
+
+ def exclude_end?
+ @excl
+ end
+ end
+
+ def test_duckrange
+ assert_equal("bc", "abcd"[DuckRange.new(1,2)])
+ end
+
+ def test_min
+ assert_equal(1, (1..2).min)
+ assert_equal(nil, (2..1).min)
+ assert_equal(1, (1...2).min)
+
+ assert_equal(1.0, (1.0..2.0).min)
+ assert_equal(nil, (2.0..1.0).min)
+ assert_equal(1, (1.0...2.0).min)
+
+ assert_equal(0, (0..0).min)
+ assert_equal(nil, (0...0).min)
+
+ assert_equal([0,1,2], (0..10).min(3))
+ assert_equal([0,1], (0..1).min(3))
+ end
+
+ def test_max
+ assert_equal(2, (1..2).max)
+ assert_equal(nil, (2..1).max)
+ assert_equal(1, (1...2).max)
+
+ assert_equal(2.0, (1.0..2.0).max)
+ assert_equal(nil, (2.0..1.0).max)
+ assert_raise(TypeError) { (1.0...2.0).max }
+ assert_raise(TypeError) { (1...1.5).max }
+ assert_raise(TypeError) { (1.5...2).max }
+
+ assert_equal(-0x80000002, ((-0x80000002)...(-0x80000001)).max)
+
+ assert_equal(0, (0..0).max)
+ assert_equal(nil, (0...0).max)
+
+ assert_equal([10,9,8], (0..10).max(3))
+ assert_equal([9,8,7], (0...10).max(3))
+ end
+
+ def test_initialize_twice
+ r = eval("1..2")
+ assert_raise(NameError) { r.instance_eval { initialize 3, 4 } }
+ assert_raise(NameError) { r.instance_eval { initialize_copy 3..4 } }
+ end
+
+ def test_uninitialized_range
+ r = Range.allocate
+ s = Marshal.dump(r)
+ r = Marshal.load(s)
+ assert_nothing_raised { r.instance_eval { initialize 5, 6} }
+ end
+
+ def test_bad_value
+ assert_raise(ArgumentError) { (1 .. :a) }
+ end
+
+ def test_exclude_end
+ assert_not_predicate(0..1, :exclude_end?)
+ assert_predicate(0...1, :exclude_end?)
+ end
+
+ def test_eq
+ r = (0..1)
+ assert_equal(r, r)
+ assert_equal(r, (0..1))
+ assert_not_equal(r, 0)
+ assert_not_equal(r, (1..2))
+ assert_not_equal(r, (0..2))
+ assert_not_equal(r, (0...1))
+ subclass = Class.new(Range)
+ assert_equal(r, subclass.new(0,1))
+ end
+
+ def test_eql
+ r = (0..1)
+ assert_operator(r, :eql?, r)
+ assert_operator(r, :eql?, 0..1)
+ assert_not_operator(r, :eql?, 0)
+ assert_not_operator(r, :eql?, 1..2)
+ assert_not_operator(r, :eql?, 0..2)
+ assert_not_operator(r, :eql?, 0...1)
+ subclass = Class.new(Range)
+ assert_operator(r, :eql?, subclass.new(0,1))
+ end
+
+ def test_hash
+ assert_kind_of(Fixnum, (0..1).hash)
+ end
+
+ def test_step
+ a = []
+ (0..10).step {|x| a << x }
+ assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
+
+ a = []
+ (0..10).step(2) {|x| a << x }
+ assert_equal([0, 2, 4, 6, 8, 10], a)
+
+ assert_raise(ArgumentError) { (0..10).step(-1) { } }
+ assert_raise(ArgumentError) { (0..10).step(0) { } }
+
+ a = []
+ ("a" .. "z").step(2) {|x| a << x }
+ assert_equal(%w(a c e g i k m o q s u w y), a)
+
+ a = []
+ ("a" .. "z").step(2**32) {|x| a << x }
+ assert_equal(["a"], a)
+
+ a = []
+ (2**32-1 .. 2**32+1).step(2) {|x| a << x }
+ assert_equal([4294967295, 4294967297], a)
+ zero = (2**32).coerce(0).first
+ assert_raise(ArgumentError) { (2**32-1 .. 2**32+1).step(zero) { } }
+
+ o1 = Object.new
+ o2 = Object.new
+ def o1.<=>(x); -1; end
+ def o2.<=>(x); 0; end
+ assert_raise(TypeError) { (o1..o2).step(1) { } }
+
+ class << o1; self; end.class_eval do
+ define_method(:succ) { o2 }
+ end
+ a = []
+ (o1..o2).step(1) {|x| a << x }
+ assert_equal([o1, o2], a)
+
+ a = []
+ (o1...o2).step(1) {|x| a << x }
+ assert_equal([o1], a)
+
+ assert_nothing_raised("[ruby-dev:34557]") { (0..2).step(0.5) {|x| } }
+
+ a = []
+ (0..2).step(0.5) {|x| a << x }
+ assert_equal([0, 0.5, 1.0, 1.5, 2.0], a)
+
+ a = []
+ (0x40000000..0x40000002).step(0.5) {|x| a << x }
+ assert_equal([1073741824, 1073741824.5, 1073741825.0, 1073741825.5, 1073741826], a)
+
+ o = Object.new
+ def o.to_int() 1 end
+ assert_nothing_raised("[ruby-dev:34558]") { (0..2).step(o) {|x| } }
+ end
+
+ def test_step_ruby_core_35753
+ assert_equal(6, (1...6.3).step.to_a.size)
+ assert_equal(5, (1.1...6).step.to_a.size)
+ assert_equal(5, (1...6).step(1.1).to_a.size)
+ assert_equal(3, (1.0...5.4).step(1.5).to_a.size)
+ assert_equal(3, (1.0...5.5).step(1.5).to_a.size)
+ assert_equal(4, (1.0...5.6).step(1.5).to_a.size)
+ end
+
+ def test_each
+ a = []
+ (0..10).each {|x| a << x }
+ assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
+
+ o1 = Object.new
+ o2 = Object.new
+ def o1.setcmp(v) @cmpresult = v end
+ o1.setcmp(-1)
+ def o1.<=>(x); @cmpresult; end
+ def o2.setcmp(v) @cmpresult = v end
+ o2.setcmp(0)
+ def o2.<=>(x); @cmpresult; end
+ class << o1; self; end.class_eval do
+ define_method(:succ) { o2 }
+ end
+
+ r1 = (o1..o2)
+ r2 = (o1...o2)
+
+ a = []
+ r1.each {|x| a << x }
+ assert_equal([o1, o2], a)
+
+ a = []
+ r2.each {|x| a << x }
+ assert_equal([o1], a)
+
+ o2.setcmp(1)
+
+ a = []
+ r1.each {|x| a << x }
+ assert_equal([o1], a)
+
+ o2.setcmp(nil)
+
+ a = []
+ r1.each {|x| a << x }
+ assert_equal([o1], a)
+
+ o1.setcmp(nil)
+
+ a = []
+ r2.each {|x| a << x }
+ assert_equal([], a)
+ end
+
+ def test_begin_end
+ assert_equal(0, (0..1).begin)
+ assert_equal(1, (0..1).end)
+ end
+
+ def test_first_last
+ assert_equal([0, 1, 2], (0..10).first(3))
+ assert_equal([8, 9, 10], (0..10).last(3))
+ assert_equal(0, (0..10).first)
+ assert_equal(10, (0..10).last)
+ assert_equal("a", ("a".."c").first)
+ assert_equal("c", ("a".."c").last)
+ assert_equal(0, (2..0).last)
+
+ assert_equal([0, 1, 2], (0...10).first(3))
+ assert_equal([7, 8, 9], (0...10).last(3))
+ assert_equal(0, (0...10).first)
+ assert_equal("a", ("a"..."c").first)
+ end
+
+ def test_to_s
+ assert_equal("0..1", (0..1).to_s)
+ assert_equal("0...1", (0...1).to_s)
+ end
+
+ def test_inspect
+ assert_equal("0..1", (0..1).inspect)
+ assert_equal("0...1", (0...1).inspect)
+ end
+
+ def test_eqq
+ assert_operator(0..10, :===, 5)
+ assert_not_operator(0..10, :===, 11)
+ end
+
+ def test_eqq_time
+ bug11113 = '[ruby-core:69052] [Bug #11113]'
+ t = Time.now
+ assert_nothing_raised(TypeError, bug11113) {
+ assert_operator(t..(t+10), :===, t+5)
+ }
+ end
+
+ def test_include
+ assert_include("a".."z", "c")
+ assert_not_include("a".."z", "5")
+ assert_include("a"..."z", "y")
+ assert_not_include("a"..."z", "z")
+ assert_not_include("a".."z", "cc")
+ assert_include(0...10, 5)
+ end
+
+ def test_cover
+ assert_operator("a".."z", :cover?, "c")
+ assert_not_operator("a".."z", :cover?, "5")
+ assert_operator("a"..."z", :cover?, "y")
+ assert_not_operator("a"..."z", :cover?, "z")
+ assert_operator("a".."z", :cover?, "cc")
+ end
+
+ def test_beg_len
+ o = Object.new
+ assert_raise(TypeError) { [][o] }
+ class << o; attr_accessor :begin end
+ o.begin = -10
+ assert_raise(TypeError) { [][o] }
+ class << o; attr_accessor :end end
+ o.end = 0
+ assert_raise(NoMethodError) { [][o] }
+ def o.exclude_end=(v) @exclude_end = v end
+ def o.exclude_end?() @exclude_end end
+ o.exclude_end = false
+ assert_nil([0][o])
+ assert_raise(RangeError) { [0][o] = 1 }
+ o.begin = 10
+ o.end = 10
+ assert_nil([0][o])
+ o.begin = 0
+ assert_equal([0], [0][o])
+ o.begin = 2
+ o.end = 0
+ assert_equal([], [0, 1, 2][o])
+ end
+
+ class CyclicRange < Range
+ def <=>(other); true; end
+ end
+ def test_cyclic_range_inspect
+ o = CyclicRange.allocate
+ o.instance_eval { initialize(o, 1) }
+ assert_equal("(... .. ...)..1", o.inspect)
+ end
+
+ def test_comparison_when_recursive
+ x = CyclicRange.allocate; x.send(:initialize, x, 1)
+ y = CyclicRange.allocate; y.send(:initialize, y, 1)
+ Timeout.timeout(1) {
+ assert_equal x, y
+ assert_operator x, :eql?, y
+ }
+
+ z = CyclicRange.allocate; z.send(:initialize, z, :another)
+ Timeout.timeout(1) {
+ assert_not_equal x, z
+ assert_not_operator x, :eql?, z
+ }
+
+ x = CyclicRange.allocate
+ y = CyclicRange.allocate
+ x.send(:initialize, y, 1)
+ y.send(:initialize, x, 1)
+ Timeout.timeout(1) {
+ assert_equal x, y
+ assert_operator x, :eql?, y
+ }
+
+ x = CyclicRange.allocate
+ z = CyclicRange.allocate
+ x.send(:initialize, z, 1)
+ z.send(:initialize, x, :other)
+ Timeout.timeout(1) {
+ assert_not_equal x, z
+ assert_not_operator x, :eql?, z
+ }
+ end
+
+ def test_size
+ assert_equal 42, (1..42).size
+ assert_equal 41, (1...42).size
+ assert_equal 6, (1...6.3).size
+ assert_equal 5, (1.1...6).size
+ assert_equal 42, (1..42).each.size
+ end
+
+ def test_bsearch_typechecks_return_values
+ assert_raise(TypeError) do
+ (1..42).bsearch{ "not ok" }
+ end
+ assert_equal (1..42).bsearch{}, (1..42).bsearch{false}
+ end
+
+ def test_bsearch_with_no_block
+ enum = (42...666).bsearch
+ assert_nil enum.size
+ assert_equal 200, enum.each{|x| x >= 200 }
+ end
+
+ def test_bsearch_for_other_numerics
+ assert_raise(TypeError) {
+ (Rational(-1,2)..Rational(9,4)).bsearch
+ }
+ assert_raise(TypeError) {
+ (BigDecimal('0.5')..BigDecimal('2.25')).bsearch
+ }
+ end
+
+ def test_bsearch_for_fixnum
+ ary = [3, 4, 7, 9, 12]
+ assert_equal(0, (0...ary.size).bsearch {|i| ary[i] >= 2 })
+ assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 4 })
+ assert_equal(2, (0...ary.size).bsearch {|i| ary[i] >= 6 })
+ assert_equal(3, (0...ary.size).bsearch {|i| ary[i] >= 8 })
+ assert_equal(4, (0...ary.size).bsearch {|i| ary[i] >= 10 })
+ assert_equal(nil, (0...ary.size).bsearch {|i| ary[i] >= 100 })
+ assert_equal(0, (0...ary.size).bsearch {|i| true })
+ assert_equal(nil, (0...ary.size).bsearch {|i| false })
+
+ ary = [0, 100, 100, 100, 200]
+ assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 100 })
+ end
+
+ def test_bsearch_for_float
+ inf = Float::INFINITY
+ assert_in_delta(10.0, (0.0...100.0).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+ assert_in_delta(10.0, (0.0...inf).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+ assert_in_delta(-10.0, (-inf..100.0).bsearch {|x| x >= 0 || Math.log(-x / 10) < 0 }, 0.0001)
+ assert_in_delta(10.0, (-inf..inf).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+ assert_equal(nil, (-inf..5).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+
+ assert_in_delta(10.0, (-inf.. 10).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+ assert_equal(nil, (-inf...10).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+
+ assert_equal(nil, (-inf..inf).bsearch { false })
+ assert_equal(-inf, (-inf..inf).bsearch { true })
+
+ assert_equal(inf, (0..inf).bsearch {|x| x == inf })
+ assert_equal(nil, (0...inf).bsearch {|x| x == inf })
+
+ v = (-inf..0).bsearch {|x| x != -inf }
+ assert_operator(-Float::MAX, :>=, v)
+ assert_operator(-inf, :<, v)
+
+ v = (0.0..1.0).bsearch {|x| x > 0 } # the nearest positive value to 0.0
+ assert_in_delta(0, v, 0.0001)
+ assert_operator(0, :<, v)
+ assert_equal(0.0, (-1.0..0.0).bsearch {|x| x >= 0 })
+ assert_equal(nil, (-1.0...0.0).bsearch {|x| x >= 0 })
+
+ v = (0..Float::MAX).bsearch {|x| x >= Float::MAX }
+ assert_in_delta(Float::MAX, v)
+ assert_equal(nil, v.infinite?)
+
+ v = (0..inf).bsearch {|x| x >= Float::MAX }
+ assert_in_delta(Float::MAX, v)
+ assert_equal(nil, v.infinite?)
+
+ v = (-Float::MAX..0).bsearch {|x| x > -Float::MAX }
+ assert_operator(-Float::MAX, :<, v)
+ assert_equal(nil, v.infinite?)
+
+ v = (-inf..0).bsearch {|x| x >= -Float::MAX }
+ assert_in_delta(-Float::MAX, v)
+ assert_equal(nil, v.infinite?)
+
+ v = (-inf..0).bsearch {|x| x > -Float::MAX }
+ assert_operator(-Float::MAX, :<, v)
+ assert_equal(nil, v.infinite?)
+
+ assert_in_delta(1.0, (0.0..inf).bsearch {|x| Math.log(x) >= 0 })
+ assert_in_delta(7.0, (0.0..10).bsearch {|x| 7.0 - x })
+ end
+
+ def check_bsearch_values(range, search)
+ from, to = range.begin, range.end
+ cmp = range.exclude_end? ? :< : :<=
+
+ # (0) trivial test
+ r = Range.new(to, from, range.exclude_end?).bsearch do |x|
+ fail "#{to}, #{from}, #{range.exclude_end?}, #{x}"
+ end
+ assert_equal nil, r
+
+ r = (to...to).bsearch do
+ fail
+ end
+ assert_equal nil, r
+
+ # prepare for others
+ yielded = []
+ r = range.bsearch do |val|
+ yielded << val
+ val >= search
+ end
+
+ # (1) log test
+ max = case from
+ when Float then 65
+ when Integer then Math.log(to-from+(range.exclude_end? ? 0 : 1), 2).to_i + 1
+ end
+ assert_operator yielded.size, :<=, max
+
+ # (2) coverage test
+ expect = if search < from
+ from
+ elsif search.send(cmp, to)
+ search
+ else
+ nil
+ end
+ assert_equal expect, r
+
+ # (3) uniqueness test
+ assert_equal nil, yielded.uniq!
+
+ # (4) end of range test
+ case
+ when range.exclude_end?
+ assert_not_include yielded, to
+ assert_not_equal r, to
+ when search >= to
+ assert_include yielded, to
+ assert_equal search == to ? to : nil, r
+ end
+
+ # start of range test
+ if search <= from
+ assert_include yielded, from
+ assert_equal from, r
+ end
+
+ # (5) out of range test
+ yielded.each do |val|
+ assert_operator from, :<=, val
+ assert_send [val, cmp, to]
+ end
+ end
+
+ def test_range_bsearch_for_floats
+ ints = [-1 << 100, -123456789, -42, -1, 0, 1, 42, 123456789, 1 << 100]
+ floats = [-Float::INFINITY, -Float::MAX, -42.0, -4.2, -Float::EPSILON, -Float::MIN, 0.0, Float::MIN, Float::EPSILON, Math::PI, 4.2, 42.0, Float::MAX, Float::INFINITY]
+
+ [ints, floats].each do |values|
+ values.combination(2).to_a.product(values).each do |(from, to), search|
+ check_bsearch_values(from..to, search)
+ check_bsearch_values(from...to, search)
+ end
+ end
+ end
+
+ def test_bsearch_for_bignum
+ bignum = 2**100
+ ary = [3, 4, 7, 9, 12]
+ assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 2 })
+ assert_equal(bignum + 1, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 4 })
+ assert_equal(bignum + 2, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 6 })
+ assert_equal(bignum + 3, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 8 })
+ assert_equal(bignum + 4, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 10 })
+ assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 100 })
+ assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true })
+ assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false })
+
+ assert_raise(TypeError) { ("a".."z").bsearch {} }
+ end
+
+ def test_bsearch_with_mathn
+ assert_separately ['-r', 'mathn'], %q{
+ msg = '[ruby-core:25740]'
+ answer = (1..(1 << 100)).bsearch{|x|
+ assert_predicate(x, :integer?, msg)
+ x >= 42
+ }
+ assert_equal(42, answer, msg)
+ }, ignore_stderr: true
+ end
+
+ def test_each_no_blockarg
+ a = "a"
+ def a.upto(x, e, &b)
+ super {|y| b.call(y) {|z| assert(false)}}
+ end
+ (a.."c").each {|x, &b| assert_nil(b)}
+ end
+end
diff --git a/jni/ruby/test/ruby/test_rational.rb b/jni/ruby/test/ruby/test_rational.rb
new file mode 100644
index 0000000..ae4a2c6
--- /dev/null
+++ b/jni/ruby/test/ruby/test_rational.rb
@@ -0,0 +1,927 @@
+require 'test/unit'
+
+class RationalSub < Rational; end
+
+class Rational_Test < Test::Unit::TestCase
+
+ def test_ratsub
+ c = RationalSub.__send__(:convert, 1)
+
+ assert_kind_of(Numeric, c)
+
+ assert_instance_of(RationalSub, c)
+
+ c2 = c + 1
+ assert_instance_of(RationalSub, c2)
+ c2 = c - 1
+ assert_instance_of(RationalSub, c2)
+
+ c3 = c - c2
+ assert_instance_of(RationalSub, c3)
+
+ s = Marshal.dump(c)
+ c5 = Marshal.load(s)
+ assert_equal(c, c5)
+ assert_instance_of(RationalSub, c5)
+
+ c1 = Rational(1)
+ assert_equal(c1.hash, c.hash, '[ruby-dev:38850]')
+ assert_equal([true, true], [c.eql?(c1), c1.eql?(c)])
+ end
+
+ def test_eql_p
+ c = Rational(0)
+ c2 = Rational(0)
+ c3 = Rational(1)
+
+ assert_equal(true, c.eql?(c2))
+ assert_equal(false, c.eql?(c3))
+
+ assert_equal(false, c.eql?(0))
+ end
+
+ def test_hash
+ assert_instance_of(Fixnum, Rational(1,2).hash)
+
+ h = {}
+ h[Rational(0)] = 0
+ h[Rational(1,1)] = 1
+ h[Rational(2,1)] = 2
+ h[Rational(3,1)] = 3
+
+ assert_equal(4, h.size)
+ assert_equal(2, h[Rational(2,1)])
+
+ h[Rational(0,1)] = 9
+ assert_equal(4, h.size)
+ end
+
+ def test_freeze
+ c = Rational(1)
+ c.freeze
+ assert_equal(true, c.frozen?)
+ assert_instance_of(String, c.to_s)
+ end
+
+ def test_conv
+ c = Rational(0,1)
+ assert_equal(Rational(0,1), c)
+
+ c = Rational(2**32, 2**32)
+ assert_equal(Rational(2**32,2**32), c)
+ assert_equal([1,1], [c.numerator,c.denominator])
+
+ c = Rational(-2**32, 2**32)
+ assert_equal(Rational(-2**32,2**32), c)
+ assert_equal([-1,1], [c.numerator,c.denominator])
+
+ c = Rational(2**32, -2**32)
+ assert_equal(Rational(2**32,-2**32), c)
+ assert_equal([-1,1], [c.numerator,c.denominator])
+
+ c = Rational(-2**32, -2**32)
+ assert_equal(Rational(-2**32,-2**32), c)
+ assert_equal([1,1], [c.numerator,c.denominator])
+
+ c = Rational(Rational(1,2),2)
+ assert_equal(Rational(1,4), c)
+
+ c = Rational(2,Rational(1,2))
+ assert_equal(Rational(4), c)
+
+ c = Rational(Rational(1,2),Rational(1,2))
+ assert_equal(Rational(1), c)
+
+ c = Rational(Complex(1,2),2)
+ assert_equal(Complex(Rational(1,2),1), c)
+
+ c = Rational(2,Complex(1,2))
+ assert_equal(Complex(Rational(2,5),Rational(-4,5)), c)
+
+ c = Rational(Complex(1,2),Complex(1,2))
+ assert_equal(Rational(1), c)
+
+ assert_equal(Rational(3),Rational(3))
+ assert_equal(Rational(1),Rational(3,3))
+ assert_equal(3.3.to_r,Rational(3.3))
+ assert_equal(1,Rational(3.3,3.3))
+ assert_equal(Rational(3),Rational('3'))
+ assert_equal(Rational(1),Rational('3.0','3.0'))
+ assert_equal(Rational(1),Rational('3/3','3/3'))
+ assert_raise(TypeError){Rational(nil)}
+ assert_raise(ArgumentError){Rational('')}
+ assert_raise(TypeError){Rational(Object.new)}
+ assert_raise(ArgumentError){Rational()}
+ assert_raise(ArgumentError){Rational(1,2,3)}
+
+ if (0.0/0).nan?
+ assert_raise(FloatDomainError){Rational(0.0/0)}
+ end
+ if (1.0/0).infinite?
+ assert_raise(FloatDomainError){Rational(1.0/0)}
+ end
+ end
+
+ def test_attr
+ c = Rational(4)
+
+ assert_equal(4, c.numerator)
+ assert_equal(1, c.denominator)
+
+ c = Rational(4,5)
+
+ assert_equal(4, c.numerator)
+ assert_equal(5, c.denominator)
+
+ c = Rational(4)
+
+ assert_equal(4, c.numerator)
+ assert_equal(1, c.denominator)
+
+ c = Rational(4,5)
+
+ assert_equal(4, c.numerator)
+ assert_equal(5, c.denominator)
+
+ c = Rational(4)
+
+ assert_equal(4, c.numerator)
+ assert_equal(1, c.denominator)
+
+ c = Rational(4,5)
+
+ assert_equal(4, c.numerator)
+ assert_equal(5, c.denominator)
+ end
+
+ def test_attr2
+ c = Rational(1)
+
+ assert_equal(false, c.integer?)
+ assert_equal(true, c.real?)
+
+ assert_equal(true, Rational(0).zero?)
+ assert_equal(true, Rational(0,1).zero?)
+ assert_equal(false, Rational(1,1).zero?)
+
+ assert_equal(nil, Rational(0).nonzero?)
+ assert_equal(nil, Rational(0,1).nonzero?)
+ assert_equal(Rational(1,1), Rational(1,1).nonzero?)
+ end
+
+ def test_uplus
+ assert_equal(Rational(1), +Rational(1))
+ assert_equal(Rational(-1), +Rational(-1))
+ assert_equal(Rational(1,1), +Rational(1,1))
+ assert_equal(Rational(-1,1), +Rational(-1,1))
+ assert_equal(Rational(-1,1), +Rational(1,-1))
+ assert_equal(Rational(1,1), +Rational(-1,-1))
+ end
+
+ def test_negate
+ assert_equal(Rational(-1), -Rational(1))
+ assert_equal(Rational(1), -Rational(-1))
+ assert_equal(Rational(-1,1), -Rational(1,1))
+ assert_equal(Rational(1,1), -Rational(-1,1))
+ assert_equal(Rational(1,1), -Rational(1,-1))
+ assert_equal(Rational(-1,1), -Rational(-1,-1))
+ end
+
+ def test_add
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_equal(Rational(7,6), c + c2)
+
+ assert_equal(Rational(5,2), c + 2)
+ assert_equal(2.5, c + 2.0)
+ end
+
+ def test_sub
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_equal(Rational(-1,6), c - c2)
+
+ assert_equal(Rational(-3,2), c - 2)
+ assert_equal(-1.5, c - 2.0)
+ end
+
+ def test_mul
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_equal(Rational(1,3), c * c2)
+
+ assert_equal(Rational(1,1), c * 2)
+ assert_equal(1.0, c * 2.0)
+ end
+
+ def test_div
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_equal(Rational(3,4), c / c2)
+
+ assert_equal(Rational(1,4), c / 2)
+ assert_equal(0.25, c / 2.0)
+
+ assert_raise(ZeroDivisionError){Rational(1, 3) / 0}
+ assert_raise(ZeroDivisionError){Rational(1, 3) / Rational(0)}
+
+ assert_equal(0, Rational(1, 3) / Float::INFINITY)
+ assert_predicate(Rational(1, 3) / 0.0, :infinite?, '[ruby-core:31626]')
+ end
+
+ def assert_eql(exp, act, *args)
+ unless Array === exp
+ exp = [exp]
+ end
+ unless Array === act
+ act = [act]
+ end
+ exp.zip(act).each do |e, a|
+ na = [e, a] + args
+ assert_equal(*na)
+ na = [e.class, a] + args
+ assert_instance_of(*na)
+ end
+ end
+
+ def test_idiv
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_eql(0, c.div(c2))
+ assert_eql(0, c.div(2))
+ assert_eql(0, c.div(2.0))
+
+ c = Rational(301,100)
+ c2 = Rational(7,5)
+
+ assert_equal(2, c.div(c2))
+ assert_equal(-3, c.div(-c2))
+ assert_equal(-3, (-c).div(c2))
+ assert_equal(2, (-c).div(-c2))
+
+ c = Rational(301,100)
+ c2 = Rational(2)
+
+ assert_equal(1, c.div(c2))
+ assert_equal(-2, c.div(-c2))
+ assert_equal(-2, (-c).div(c2))
+ assert_equal(1, (-c).div(-c2))
+
+ c = Rational(11)
+ c2 = Rational(3)
+
+ assert_equal(3, c.div(c2))
+ assert_equal(-4, c.div(-c2))
+ assert_equal(-4, (-c).div(c2))
+ assert_equal(3, (-c).div(-c2))
+ end
+
+ def test_modulo
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_eql(Rational(1,2), c.modulo(c2))
+ assert_eql(Rational(1,2), c.modulo(2))
+ assert_eql(0.5, c.modulo(2.0))
+
+ c = Rational(301,100)
+ c2 = Rational(7,5)
+
+ assert_equal(Rational(21,100), c.modulo(c2))
+ assert_equal(Rational(-119,100), c.modulo(-c2))
+ assert_equal(Rational(119,100), (-c).modulo(c2))
+ assert_equal(Rational(-21,100), (-c).modulo(-c2))
+
+ c = Rational(301,100)
+ c2 = Rational(2)
+
+ assert_equal(Rational(101,100), c.modulo(c2))
+ assert_equal(Rational(-99,100), c.modulo(-c2))
+ assert_equal(Rational(99,100), (-c).modulo(c2))
+ assert_equal(Rational(-101,100), (-c).modulo(-c2))
+
+ c = Rational(11)
+ c2 = Rational(3)
+
+ assert_equal(2, c.modulo(c2))
+ assert_equal(-1, c.modulo(-c2))
+ assert_equal(1, (-c).modulo(c2))
+ assert_equal(-2, (-c).modulo(-c2))
+ end
+
+ def test_divmod
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_eql([0, Rational(1,2)], c.divmod(c2))
+ assert_eql([0, Rational(1,2)], c.divmod(2))
+ assert_eql([0, 0.5], c.divmod(2.0))
+
+ c = Rational(301,100)
+ c2 = Rational(7,5)
+
+ assert_equal([2, Rational(21,100)], c.divmod(c2))
+ assert_equal([-3, Rational(-119,100)], c.divmod(-c2))
+ assert_equal([-3, Rational(119,100)], (-c).divmod(c2))
+ assert_equal([2, Rational(-21,100)], (-c).divmod(-c2))
+
+ c = Rational(301,100)
+ c2 = Rational(2)
+
+ assert_equal([1, Rational(101,100)], c.divmod(c2))
+ assert_equal([-2, Rational(-99,100)], c.divmod(-c2))
+ assert_equal([-2, Rational(99,100)], (-c).divmod(c2))
+ assert_equal([1, Rational(-101,100)], (-c).divmod(-c2))
+
+ c = Rational(11)
+ c2 = Rational(3)
+
+ assert_equal([3,2], c.divmod(c2))
+ assert_equal([-4,-1], c.divmod(-c2))
+ assert_equal([-4,1], (-c).divmod(c2))
+ assert_equal([3,-2], (-c).divmod(-c2))
+ end
+
+ def test_remainder
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_eql(Rational(1,2), c.remainder(c2))
+ assert_eql(Rational(1,2), c.remainder(2))
+ assert_eql(0.5, c.remainder(2.0))
+
+ c = Rational(301,100)
+ c2 = Rational(7,5)
+
+ assert_equal(Rational(21,100), c.remainder(c2))
+ assert_equal(Rational(21,100), c.remainder(-c2))
+ assert_equal(Rational(-21,100), (-c).remainder(c2))
+ assert_equal(Rational(-21,100), (-c).remainder(-c2))
+
+ c = Rational(301,100)
+ c2 = Rational(2)
+
+ assert_equal(Rational(101,100), c.remainder(c2))
+ assert_equal(Rational(101,100), c.remainder(-c2))
+ assert_equal(Rational(-101,100), (-c).remainder(c2))
+ assert_equal(Rational(-101,100), (-c).remainder(-c2))
+
+ c = Rational(11)
+ c2 = Rational(3)
+
+ assert_equal(2, c.remainder(c2))
+ assert_equal(2, c.remainder(-c2))
+ assert_equal(-2, (-c).remainder(c2))
+ assert_equal(-2, (-c).remainder(-c2))
+ end
+
+ def test_quo
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_equal(Rational(3,4), c.quo(c2))
+
+ assert_equal(Rational(1,4), c.quo(2))
+ assert_equal(0.25, c.quo(2.0))
+ end
+
+ def test_fdiv
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ assert_equal(0.75, c.fdiv(c2))
+
+ assert_equal(0.25, c.fdiv(2))
+ assert_equal(0.25, c.fdiv(2.0))
+ assert_equal(0, c.fdiv(Float::INFINITY))
+ assert_predicate(c.fdiv(0), :infinite?, '[ruby-core:31626]')
+ end
+
+ def test_expt
+ c = Rational(1,2)
+ c2 = Rational(2,3)
+
+ r = c ** c2
+ assert_in_delta(0.6299, r, 0.001)
+
+ assert_equal(Rational(1,4), c ** 2)
+ assert_equal(Rational(4), c ** -2)
+ assert_equal(Rational(1,4), (-c) ** 2)
+ assert_equal(Rational(4), (-c) ** -2)
+
+ assert_equal(0.25, c ** 2.0)
+ assert_equal(4.0, c ** -2.0)
+
+ assert_equal(Rational(1,4), c ** Rational(2))
+ assert_equal(Rational(4), c ** Rational(-2))
+
+ assert_equal(Rational(1), 0 ** Rational(0))
+ assert_equal(Rational(1), Rational(0) ** 0)
+ assert_equal(Rational(1), Rational(0) ** Rational(0))
+
+ # p ** p
+ x = 2 ** Rational(2)
+ assert_equal(Rational(4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(4, x.numerator)
+ assert_equal(1, x.denominator)
+
+ x = Rational(2) ** 2
+ assert_equal(Rational(4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(4, x.numerator)
+ assert_equal(1, x.denominator)
+
+ x = Rational(2) ** Rational(2)
+ assert_equal(Rational(4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(4, x.numerator)
+ assert_equal(1, x.denominator)
+
+ # -p ** p
+ x = (-2) ** Rational(2)
+ assert_equal(Rational(4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(4, x.numerator)
+ assert_equal(1, x.denominator)
+
+ x = Rational(-2) ** 2
+ assert_equal(Rational(4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(4, x.numerator)
+ assert_equal(1, x.denominator)
+
+ x = Rational(-2) ** Rational(2)
+ assert_equal(Rational(4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(4, x.numerator)
+ assert_equal(1, x.denominator)
+
+ # p ** -p
+ x = 2 ** Rational(-2)
+ assert_equal(Rational(1,4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(1, x.numerator)
+ assert_equal(4, x.denominator)
+
+ x = Rational(2) ** -2
+ assert_equal(Rational(1,4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(1, x.numerator)
+ assert_equal(4, x.denominator)
+
+ x = Rational(2) ** Rational(-2)
+ assert_equal(Rational(1,4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(1, x.numerator)
+ assert_equal(4, x.denominator)
+
+ # -p ** -p
+ x = (-2) ** Rational(-2)
+ assert_equal(Rational(1,4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(1, x.numerator)
+ assert_equal(4, x.denominator)
+
+ x = Rational(-2) ** -2
+ assert_equal(Rational(1,4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(1, x.numerator)
+ assert_equal(4, x.denominator)
+
+ x = Rational(-2) ** Rational(-2)
+ assert_equal(Rational(1,4), x)
+ assert_instance_of(Rational, x)
+ assert_equal(1, x.numerator)
+ assert_equal(4, x.denominator)
+
+ assert_raise(ZeroDivisionError){0 ** -1}
+ end
+
+ def test_cmp
+ assert_equal(-1, Rational(-1) <=> Rational(0))
+ assert_equal(0, Rational(0) <=> Rational(0))
+ assert_equal(+1, Rational(+1) <=> Rational(0))
+
+ assert_equal(-1, Rational(-1) <=> 0)
+ assert_equal(0, Rational(0) <=> 0)
+ assert_equal(+1, Rational(+1) <=> 0)
+
+ assert_equal(-1, Rational(-1) <=> 0.0)
+ assert_equal(0, Rational(0) <=> 0.0)
+ assert_equal(+1, Rational(+1) <=> 0.0)
+
+ assert_equal(-1, Rational(1,2) <=> Rational(2,3))
+ assert_equal(0, Rational(2,3) <=> Rational(2,3))
+ assert_equal(+1, Rational(2,3) <=> Rational(1,2))
+
+ f = 2**30-1
+ b = 2**30
+
+ assert_equal(0, Rational(f) <=> Rational(f))
+ assert_equal(-1, Rational(f) <=> Rational(b))
+ assert_equal(+1, Rational(b) <=> Rational(f))
+ assert_equal(0, Rational(b) <=> Rational(b))
+
+ assert_equal(-1, Rational(f-1) <=> Rational(f))
+ assert_equal(+1, Rational(f) <=> Rational(f-1))
+ assert_equal(-1, Rational(b-1) <=> Rational(b))
+ assert_equal(+1, Rational(b) <=> Rational(b-1))
+
+ assert_equal(false, Rational(0) < Rational(0))
+ assert_equal(true, Rational(0) <= Rational(0))
+ assert_equal(true, Rational(0) >= Rational(0))
+ assert_equal(false, Rational(0) > Rational(0))
+
+ assert_equal(nil, Rational(0) <=> nil)
+ assert_equal(nil, Rational(0) <=> 'foo')
+ end
+
+ def test_eqeq
+ assert_equal(Rational(1,1), Rational(1))
+ assert_equal(Rational(-1,1), Rational(-1))
+
+ assert_equal(false, Rational(2,1) == Rational(1))
+ assert_equal(true, Rational(2,1) != Rational(1))
+ assert_equal(false, Rational(1) == nil)
+ assert_equal(false, Rational(1) == '')
+ end
+
+ def test_coerce
+ assert_equal([Rational(2),Rational(1)], Rational(1).coerce(2))
+ assert_equal([Rational(2.2),Rational(1)], Rational(1).coerce(2.2))
+ assert_equal([Rational(2),Rational(1)], Rational(1).coerce(Rational(2)))
+
+ assert_nothing_raised(TypeError, '[Bug #5020] [ruby-devl:44088]') do
+ Rational(1,2).coerce(Complex(1,1))
+ end
+ end
+
+ class ObjectX
+ def + (x) Rational(1) end
+ alias - +
+ alias * +
+ alias / +
+ alias quo +
+ alias div +
+ alias % +
+ alias remainder +
+ alias ** +
+ def coerce(x) [x, Rational(1)] end
+ end
+
+ def test_coerce2
+ x = ObjectX.new
+ %w(+ - * / quo div % remainder **).each do |op|
+ assert_kind_of(Numeric, Rational(1).__send__(op, x))
+ end
+ end
+
+ def test_math
+ assert_equal(Rational(1,2), Rational(1,2).abs)
+ assert_equal(Rational(1,2), Rational(-1,2).abs)
+ assert_equal(Rational(1,2), Rational(1,2).magnitude)
+ assert_equal(Rational(1,2), Rational(-1,2).magnitude)
+
+ assert_equal(1, Rational(1,2).numerator)
+ assert_equal(2, Rational(1,2).denominator)
+ end
+
+ def test_trunc
+ [[Rational(13, 5), [ 2, 3, 2, 3]], # 2.6
+ [Rational(5, 2), [ 2, 3, 2, 3]], # 2.5
+ [Rational(12, 5), [ 2, 3, 2, 2]], # 2.4
+ [Rational(-12,5), [-3, -2, -2, -2]], # -2.4
+ [Rational(-5, 2), [-3, -2, -2, -3]], # -2.5
+ [Rational(-13, 5), [-3, -2, -2, -3]], # -2.6
+ ].each do |i, a|
+ assert_equal(a[0], i.floor)
+ assert_equal(a[1], i.ceil)
+ assert_equal(a[2], i.truncate)
+ assert_equal(a[3], i.round)
+ end
+ end
+
+ def test_to_s
+ c = Rational(1,2)
+
+ assert_instance_of(String, c.to_s)
+ assert_equal('1/2', c.to_s)
+
+ assert_equal('0/1', Rational(0,2).to_s)
+ assert_equal('0/1', Rational(0,-2).to_s)
+ assert_equal('1/2', Rational(1,2).to_s)
+ assert_equal('-1/2', Rational(-1,2).to_s)
+ assert_equal('1/2', Rational(-1,-2).to_s)
+ assert_equal('-1/2', Rational(1,-2).to_s)
+ assert_equal('1/2', Rational(-1,-2).to_s)
+ end
+
+ def test_inspect
+ c = Rational(1,2)
+
+ assert_instance_of(String, c.inspect)
+ assert_equal('(1/2)', c.inspect)
+ end
+
+ def test_marshal
+ c = Rational(1,2)
+ c.instance_eval{@ivar = 9}
+
+ s = Marshal.dump(c)
+ c2 = Marshal.load(s)
+ assert_equal(c, c2)
+ assert_equal(9, c2.instance_variable_get(:@ivar))
+ assert_instance_of(Rational, c2)
+
+ assert_raise(ZeroDivisionError){
+ Marshal.load("\x04\bU:\rRational[\ai\x06i\x05")
+ }
+
+ bug3656 = '[ruby-core:31622]'
+ c = Rational(1,2)
+ c.freeze
+ assert_predicate(c, :frozen?)
+ result = c.marshal_load([2,3]) rescue :fail
+ assert_equal(:fail, result, bug3656)
+ end
+
+ def test_marshal_compatibility
+ bug6625 = '[ruby-core:45775]'
+ dump = "\x04\x08o:\x0dRational\x07:\x11@denominatori\x07:\x0f@numeratori\x06"
+ assert_nothing_raised(bug6625) do
+ assert_equal(Rational(1, 2), Marshal.load(dump), bug6625)
+ end
+ end
+
+ def test_parse
+ assert_equal(Rational(5), '5'.to_r)
+ assert_equal(Rational(-5), '-5'.to_r)
+ assert_equal(Rational(5,3), '5/3'.to_r)
+ assert_equal(Rational(-5,3), '-5/3'.to_r)
+
+ assert_equal(Rational(5), '5.0'.to_r)
+ assert_equal(Rational(-5), '-5.0'.to_r)
+ assert_equal(Rational(5,3), '5.0/3'.to_r)
+ assert_equal(Rational(-5,3), '-5.0/3'.to_r)
+
+ assert_equal(Rational(5), '5e0'.to_r)
+ assert_equal(Rational(-5), '-5e0'.to_r)
+ assert_equal(Rational(5,3), '5e0/3'.to_r)
+ assert_equal(Rational(-5,3), '-5e0/3'.to_r)
+
+ assert_equal(Rational(5e1), '5e1'.to_r)
+ assert_equal(Rational(-5e2), '-5e2'.to_r)
+ assert_equal(Rational(5e3,3), '5e003/3'.to_r)
+ assert_equal(Rational(-5e4,3), '-5e004/3'.to_r)
+
+ assert_equal(Rational(33,100), '.33'.to_r)
+ assert_equal(Rational(33,100), '0.33'.to_r)
+ assert_equal(Rational(-33,100), '-.33'.to_r)
+ assert_equal(Rational(-33,100), '-0.33'.to_r)
+ assert_equal(Rational(-33,100), '-0.3_3'.to_r)
+
+ assert_equal(Rational(1,2), '5e-1'.to_r)
+ assert_equal(Rational(50), '5e+1'.to_r)
+ assert_equal(Rational(1,2), '5.0e-1'.to_r)
+ assert_equal(Rational(50), '5.0e+1'.to_r)
+ assert_equal(Rational(50), '5e1'.to_r)
+ assert_equal(Rational(50), '5E1'.to_r)
+ assert_equal(Rational(500), '5e2'.to_r)
+ assert_equal(Rational(5000), '5e3'.to_r)
+ assert_equal(Rational(500000000000), '5e1_1'.to_r)
+
+ assert_equal(Rational(5), Rational('5'))
+ assert_equal(Rational(-5), Rational('-5'))
+ assert_equal(Rational(5,3), Rational('5/3'))
+ assert_equal(Rational(-5,3), Rational('-5/3'))
+
+ assert_equal(Rational(5), Rational('5.0'))
+ assert_equal(Rational(-5), Rational('-5.0'))
+ assert_equal(Rational(5,3), Rational('5.0/3'))
+ assert_equal(Rational(-5,3), Rational('-5.0/3'))
+
+ assert_equal(Rational(5), Rational('5e0'))
+ assert_equal(Rational(-5), Rational('-5e0'))
+ assert_equal(Rational(5,3), Rational('5e0/3'))
+ assert_equal(Rational(-5,3), Rational('-5e0/3'))
+
+ assert_equal(Rational(5e1), Rational('5e1'))
+ assert_equal(Rational(-5e2), Rational('-5e2'))
+ assert_equal(Rational(5e3,3), Rational('5e003/3'))
+ assert_equal(Rational(-5e4,3), Rational('-5e004/3'))
+
+ assert_equal(Rational(33,100), Rational('.33'))
+ assert_equal(Rational(33,100), Rational('0.33'))
+ assert_equal(Rational(-33,100), Rational('-.33'))
+ assert_equal(Rational(-33,100), Rational('-0.33'))
+ assert_equal(Rational(-33,100), Rational('-0.3_3'))
+
+ assert_equal(Rational(1,2), Rational('5e-1'))
+ assert_equal(Rational(50), Rational('5e+1'))
+ assert_equal(Rational(1,2), Rational('5.0e-1'))
+ assert_equal(Rational(50), Rational('5.0e+1'))
+ assert_equal(Rational(50), Rational('5e1'))
+ assert_equal(Rational(50), Rational('5E1'))
+ assert_equal(Rational(500), Rational('5e2'))
+ assert_equal(Rational(5000), Rational('5e3'))
+ assert_equal(Rational(500000000000), Rational('5e1_1'))
+
+ assert_equal(Rational(0), ''.to_r)
+ assert_equal(Rational(0), ' '.to_r)
+ assert_equal(Rational(5), "\f\n\r\t\v5\0".to_r)
+ assert_equal(Rational(0), '_'.to_r)
+ assert_equal(Rational(0), '_5'.to_r)
+ assert_equal(Rational(5), '5_'.to_r)
+ assert_equal(Rational(5), '5x'.to_r)
+ assert_equal(Rational(5), '5/_3'.to_r)
+ assert_equal(Rational(5,3), '5/3_'.to_r)
+ assert_equal(Rational(5,3), '5/3.3'.to_r)
+ assert_equal(Rational(5,3), '5/3x'.to_r)
+ assert_raise(ArgumentError){ Rational('')}
+ assert_raise(ArgumentError){ Rational('_')}
+ assert_raise(ArgumentError){ Rational("\f\n\r\t\v5\0")}
+ assert_raise(ArgumentError){ Rational('_5')}
+ assert_raise(ArgumentError){ Rational('5_')}
+ assert_raise(ArgumentError){ Rational('5x')}
+ assert_raise(ArgumentError){ Rational('5/_3')}
+ assert_raise(ArgumentError){ Rational('5/3_')}
+ assert_raise(ArgumentError){ Rational('5/3.3')}
+ assert_raise(ArgumentError){ Rational('5/3x')}
+ end
+
+ def test_to_i
+ assert_equal(1, Rational(3,2).to_i)
+ assert_equal(1, Integer(Rational(3,2)))
+ end
+
+ def test_to_f
+ assert_equal(1.5, Rational(3,2).to_f)
+ assert_equal(1.5, Float(Rational(3,2)))
+ end
+
+ def test_to_c
+ assert_equal(Complex(Rational(3,2)), Rational(3,2).to_c)
+ assert_equal(Complex(Rational(3,2)), Complex(Rational(3,2)))
+ end
+
+ def test_to_r
+ c = nil.to_r
+ assert_equal([0,1], [c.numerator, c.denominator])
+
+ c = 0.to_r
+ assert_equal([0,1], [c.numerator, c.denominator])
+
+ c = 1.to_r
+ assert_equal([1,1], [c.numerator, c.denominator])
+
+ c = 1.1.to_r
+ assert_equal([2476979795053773, 2251799813685248],
+ [c.numerator, c.denominator])
+
+ c = Rational(1,2).to_r
+ assert_equal([1,2], [c.numerator, c.denominator])
+
+ assert_raise(RangeError){Complex(1,2).to_r}
+
+ if (0.0/0).nan?
+ assert_raise(FloatDomainError){(0.0/0).to_r}
+ end
+ if (1.0/0).infinite?
+ assert_raise(FloatDomainError){(1.0/0).to_r}
+ end
+ end
+
+ def test_rationalize
+ c = nil.rationalize
+ assert_equal([0,1], [c.numerator, c.denominator])
+
+ c = 0.rationalize
+ assert_equal([0,1], [c.numerator, c.denominator])
+
+ c = 1.rationalize
+ assert_equal([1,1], [c.numerator, c.denominator])
+
+ c = 1.1.rationalize
+ assert_equal([11, 10], [c.numerator, c.denominator])
+
+ c = Rational(1,2).rationalize
+ assert_equal([1,2], [c.numerator, c.denominator])
+
+ assert_equal(nil.rationalize(Rational(1,10)), Rational(0))
+ assert_equal(0.rationalize(Rational(1,10)), Rational(0))
+ assert_equal(10.rationalize(Rational(1,10)), Rational(10))
+
+ r = 0.3333
+ assert_equal(r.rationalize, Rational(3333, 10000))
+ assert_equal(r.rationalize(Rational(1,10)), Rational(1,3))
+ assert_equal(r.rationalize(Rational(-1,10)), Rational(1,3))
+
+ r = Rational(5404319552844595,18014398509481984)
+ assert_equal(r.rationalize, r)
+ assert_equal(r.rationalize(Rational(1,10)), Rational(1,3))
+ assert_equal(r.rationalize(Rational(-1,10)), Rational(1,3))
+
+ r = -0.3333
+ assert_equal(r.rationalize, Rational(-3333, 10000))
+ assert_equal(r.rationalize(Rational(1,10)), Rational(-1,3))
+ assert_equal(r.rationalize(Rational(-1,10)), Rational(-1,3))
+
+ r = Rational(-5404319552844595,18014398509481984)
+ assert_equal(r.rationalize, r)
+ assert_equal(r.rationalize(Rational(1,10)), Rational(-1,3))
+ assert_equal(r.rationalize(Rational(-1,10)), Rational(-1,3))
+
+ assert_raise(RangeError){Complex(1,2).rationalize}
+
+ if (0.0/0).nan?
+ assert_raise(FloatDomainError){(0.0/0).rationalize}
+ end
+ if (1.0/0).infinite?
+ assert_raise(FloatDomainError){(1.0/0).rationalize}
+ end
+ end
+
+ def test_gcdlcm
+ assert_equal(7, 91.gcd(-49))
+ assert_equal(5, 5.gcd(0))
+ assert_equal(5, 0.gcd(5))
+ assert_equal(70, 14.lcm(35))
+ assert_equal(0, 5.lcm(0))
+ assert_equal(0, 0.lcm(5))
+ assert_equal([5,0], 0.gcdlcm(5))
+ assert_equal([5,0], 5.gcdlcm(0))
+
+ assert_equal(1, 1073741827.gcd(1073741789))
+ assert_equal(1152921470247108503, 1073741827.lcm(1073741789))
+
+ assert_equal(1, 1073741789.gcd(1073741827))
+ assert_equal(1152921470247108503, 1073741789.lcm(1073741827))
+ end
+
+ def test_supp
+ assert_equal(true, 1.real?)
+ assert_equal(true, 1.1.real?)
+
+ assert_equal(1, 1.numerator)
+ assert_equal(9, 9.numerator)
+ assert_equal(1, 1.denominator)
+ assert_equal(1, 9.denominator)
+
+ assert_equal(1.0, 1.0.numerator)
+ assert_equal(9.0, 9.0.numerator)
+ assert_equal(1.0, 1.0.denominator)
+ assert_equal(1.0, 9.0.denominator)
+
+ assert_equal(Rational(1,2), 1.quo(2))
+ assert_equal(Rational(5000000000), 10000000000.quo(2))
+ assert_equal(0.5, 1.0.quo(2))
+ assert_equal(Rational(1,4), Rational(1,2).quo(2))
+ assert_equal(0, Rational(1,2).quo(Float::INFINITY))
+ assert_predicate(Rational(1,2).quo(0.0), :infinite?, '[ruby-core:31626]')
+
+ assert_equal(0.5, 1.fdiv(2))
+ assert_equal(5000000000.0, 10000000000.fdiv(2))
+ assert_equal(0.5, 1.0.fdiv(2))
+ assert_equal(0.25, Rational(1,2).fdiv(2))
+ end
+
+ def test_ruby19
+ assert_raise(NoMethodError){ Rational.new(1) }
+ assert_raise(NoMethodError){ Rational.new!(1) }
+ end
+
+ def test_fixed_bug
+ n = Float::MAX.to_i * 2
+ assert_equal(1.0, Rational(n + 2, n + 1).to_f, '[ruby-dev:33852]')
+ end
+
+ def test_power_of_1_and_minus_1
+ bug5715 = '[ruby-core:41498]'
+ big = 1 << 66
+ one = Rational( 1, 1)
+ assert_eql one, one ** -big , bug5715
+ assert_eql one, (-one) ** -big , bug5715
+ assert_eql -one, (-one) ** -(big+1) , bug5715
+ assert_equal Complex, ((-one) ** Rational(1,3)).class
+ end
+
+ def test_power_of_0
+ bug5713 = '[ruby-core:41494]'
+ big = 1 << 66
+ zero = Rational(0, 1)
+ assert_eql zero, zero ** big
+ assert_eql zero, zero ** Rational(2, 3)
+ assert_raise(ZeroDivisionError, bug5713) { Rational(0, 1) ** -big }
+ assert_raise(ZeroDivisionError, bug5713) { Rational(0, 1) ** Rational(-2,3) }
+ end
+
+ def test_known_bug
+ end
+
+end
diff --git a/jni/ruby/test/ruby/test_rational2.rb b/jni/ruby/test/ruby/test_rational2.rb
new file mode 100644
index 0000000..3b6a985
--- /dev/null
+++ b/jni/ruby/test/ruby/test_rational2.rb
@@ -0,0 +1,1386 @@
+require 'test/unit'
+
+class Rational_Test2 < Test::Unit::TestCase
+
+ def test_kumi
+ assert_equal(Rational(1, 1), +Rational(1, 1))
+ assert_equal(Rational(-1, 1), -Rational(1, 1))
+ assert_equal(Rational(2, 1),
+ Rational(1, 1) + Rational(1, 1))
+ assert_equal(Rational(0, 1),
+ Rational(1, 1) - Rational(1, 1))
+ assert_equal(Rational(1, 1),
+ Rational(1, 1) * Rational(1, 1))
+ assert_equal(Rational(1, 1),
+ Rational(1, 1) / Rational(1, 1))
+ assert_equal(Rational(3, 1),
+ Rational(1, 1) + Rational(2, 1))
+ assert_equal(Rational(-1, 1),
+ Rational(1, 1) - Rational(2, 1))
+ assert_equal(Rational(2, 1),
+ Rational(1, 1) * Rational(2, 1))
+ assert_equal(Rational(1, 2),
+ Rational(1, 1) / Rational(2, 1))
+ assert_equal(Rational(4, 1),
+ Rational(1, 1) + Rational(3, 1))
+ assert_equal(Rational(-2, 1),
+ Rational(1, 1) - Rational(3, 1))
+ assert_equal(Rational(3, 1),
+ Rational(1, 1) * Rational(3, 1))
+ assert_equal(Rational(1, 3),
+ Rational(1, 1) / Rational(3, 1))
+ assert_equal(Rational(1073741790, 1),
+ Rational(1, 1) + Rational(1073741789, 1))
+ assert_equal(Rational(-1073741788, 1),
+ Rational(1, 1) - Rational(1073741789, 1))
+ assert_equal(Rational(1073741789, 1),
+ Rational(1, 1) * Rational(1073741789, 1))
+ assert_equal(Rational(1, 1073741789),
+ Rational(1, 1) / Rational(1073741789, 1))
+ assert_equal(Rational(1073741828, 1),
+ Rational(1, 1) + Rational(1073741827, 1))
+ assert_equal(Rational(-1073741826, 1),
+ Rational(1, 1) - Rational(1073741827, 1))
+ assert_equal(Rational(1073741827, 1),
+ Rational(1, 1) * Rational(1073741827, 1))
+ assert_equal(Rational(1, 1073741827),
+ Rational(1, 1) / Rational(1073741827, 1))
+ assert_equal(Rational(5, 3),
+ Rational(1, 1) + Rational(2, 3))
+ assert_equal(Rational(1, 3),
+ Rational(1, 1) - Rational(2, 3))
+ assert_equal(Rational(2, 3),
+ Rational(1, 1) * Rational(2, 3))
+ assert_equal(Rational(3, 2),
+ Rational(1, 1) / Rational(2, 3))
+ assert_equal(Rational(5, 2),
+ Rational(1, 1) + Rational(3, 2))
+ assert_equal(Rational(-1, 2),
+ Rational(1, 1) - Rational(3, 2))
+ assert_equal(Rational(3, 2),
+ Rational(1, 1) * Rational(3, 2))
+ assert_equal(Rational(2, 3),
+ Rational(1, 1) / Rational(3, 2))
+ assert_equal(Rational(1073741792, 1073741789),
+ Rational(1, 1) + Rational(3, 1073741789))
+ assert_equal(Rational(1073741786, 1073741789),
+ Rational(1, 1) - Rational(3, 1073741789))
+ assert_equal(Rational(3, 1073741789),
+ Rational(1, 1) * Rational(3, 1073741789))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1, 1) / Rational(3, 1073741789))
+ assert_equal(Rational(1073741792, 3),
+ Rational(1, 1) + Rational(1073741789, 3))
+ assert_equal(Rational(-1073741786, 3),
+ Rational(1, 1) - Rational(1073741789, 3))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1, 1) * Rational(1073741789, 3))
+ assert_equal(Rational(3, 1073741789),
+ Rational(1, 1) / Rational(1073741789, 3))
+ assert_equal(Rational(1073741830, 1073741827),
+ Rational(1, 1) + Rational(3, 1073741827))
+ assert_equal(Rational(1073741824, 1073741827),
+ Rational(1, 1) - Rational(3, 1073741827))
+ assert_equal(Rational(3, 1073741827),
+ Rational(1, 1) * Rational(3, 1073741827))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1, 1) / Rational(3, 1073741827))
+ assert_equal(Rational(1073741830, 3),
+ Rational(1, 1) + Rational(1073741827, 3))
+ assert_equal(Rational(-1073741824, 3),
+ Rational(1, 1) - Rational(1073741827, 3))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1, 1) * Rational(1073741827, 3))
+ assert_equal(Rational(3, 1073741827),
+ Rational(1, 1) / Rational(1073741827, 3))
+ assert_equal(Rational(2147483616, 1073741827),
+ Rational(1, 1) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(38, 1073741827),
+ Rational(1, 1) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(1, 1) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(1, 1) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(2147483616, 1073741789),
+ Rational(1, 1) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(-38, 1073741789),
+ Rational(1, 1) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(1, 1) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(1, 1) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(2, 1), +Rational(2, 1))
+ assert_equal(Rational(-2, 1), -Rational(2, 1))
+ assert_equal(Rational(3, 1),
+ Rational(2, 1) + Rational(1, 1))
+ assert_equal(Rational(1, 1),
+ Rational(2, 1) - Rational(1, 1))
+ assert_equal(Rational(2, 1),
+ Rational(2, 1) * Rational(1, 1))
+ assert_equal(Rational(2, 1),
+ Rational(2, 1) / Rational(1, 1))
+ assert_equal(Rational(4, 1),
+ Rational(2, 1) + Rational(2, 1))
+ assert_equal(Rational(0, 1),
+ Rational(2, 1) - Rational(2, 1))
+ assert_equal(Rational(4, 1),
+ Rational(2, 1) * Rational(2, 1))
+ assert_equal(Rational(1, 1),
+ Rational(2, 1) / Rational(2, 1))
+ assert_equal(Rational(5, 1),
+ Rational(2, 1) + Rational(3, 1))
+ assert_equal(Rational(-1, 1),
+ Rational(2, 1) - Rational(3, 1))
+ assert_equal(Rational(6, 1),
+ Rational(2, 1) * Rational(3, 1))
+ assert_equal(Rational(2, 3),
+ Rational(2, 1) / Rational(3, 1))
+ assert_equal(Rational(1073741791, 1),
+ Rational(2, 1) + Rational(1073741789, 1))
+ assert_equal(Rational(-1073741787, 1),
+ Rational(2, 1) - Rational(1073741789, 1))
+ assert_equal(Rational(2147483578, 1),
+ Rational(2, 1) * Rational(1073741789, 1))
+ assert_equal(Rational(2, 1073741789),
+ Rational(2, 1) / Rational(1073741789, 1))
+ assert_equal(Rational(1073741829, 1),
+ Rational(2, 1) + Rational(1073741827, 1))
+ assert_equal(Rational(-1073741825, 1),
+ Rational(2, 1) - Rational(1073741827, 1))
+ assert_equal(Rational(2147483654, 1),
+ Rational(2, 1) * Rational(1073741827, 1))
+ assert_equal(Rational(2, 1073741827),
+ Rational(2, 1) / Rational(1073741827, 1))
+ assert_equal(Rational(8, 3),
+ Rational(2, 1) + Rational(2, 3))
+ assert_equal(Rational(4, 3),
+ Rational(2, 1) - Rational(2, 3))
+ assert_equal(Rational(4, 3),
+ Rational(2, 1) * Rational(2, 3))
+ assert_equal(Rational(3, 1),
+ Rational(2, 1) / Rational(2, 3))
+ assert_equal(Rational(7, 2),
+ Rational(2, 1) + Rational(3, 2))
+ assert_equal(Rational(1, 2),
+ Rational(2, 1) - Rational(3, 2))
+ assert_equal(Rational(3, 1),
+ Rational(2, 1) * Rational(3, 2))
+ assert_equal(Rational(4, 3),
+ Rational(2, 1) / Rational(3, 2))
+ assert_equal(Rational(2147483581, 1073741789),
+ Rational(2, 1) + Rational(3, 1073741789))
+ assert_equal(Rational(2147483575, 1073741789),
+ Rational(2, 1) - Rational(3, 1073741789))
+ assert_equal(Rational(6, 1073741789),
+ Rational(2, 1) * Rational(3, 1073741789))
+ assert_equal(Rational(2147483578, 3),
+ Rational(2, 1) / Rational(3, 1073741789))
+ assert_equal(Rational(1073741795, 3),
+ Rational(2, 1) + Rational(1073741789, 3))
+ assert_equal(Rational(-1073741783, 3),
+ Rational(2, 1) - Rational(1073741789, 3))
+ assert_equal(Rational(2147483578, 3),
+ Rational(2, 1) * Rational(1073741789, 3))
+ assert_equal(Rational(6, 1073741789),
+ Rational(2, 1) / Rational(1073741789, 3))
+ assert_equal(Rational(2147483657, 1073741827),
+ Rational(2, 1) + Rational(3, 1073741827))
+ assert_equal(Rational(2147483651, 1073741827),
+ Rational(2, 1) - Rational(3, 1073741827))
+ assert_equal(Rational(6, 1073741827),
+ Rational(2, 1) * Rational(3, 1073741827))
+ assert_equal(Rational(2147483654, 3),
+ Rational(2, 1) / Rational(3, 1073741827))
+ assert_equal(Rational(1073741833, 3),
+ Rational(2, 1) + Rational(1073741827, 3))
+ assert_equal(Rational(-1073741821, 3),
+ Rational(2, 1) - Rational(1073741827, 3))
+ assert_equal(Rational(2147483654, 3),
+ Rational(2, 1) * Rational(1073741827, 3))
+ assert_equal(Rational(6, 1073741827),
+ Rational(2, 1) / Rational(1073741827, 3))
+ assert_equal(Rational(3221225443, 1073741827),
+ Rational(2, 1) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741865, 1073741827),
+ Rational(2, 1) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(2147483578, 1073741827),
+ Rational(2, 1) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(2147483654, 1073741789),
+ Rational(2, 1) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(3221225405, 1073741789),
+ Rational(2, 1) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741751, 1073741789),
+ Rational(2, 1) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(2147483654, 1073741789),
+ Rational(2, 1) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(2147483578, 1073741827),
+ Rational(2, 1) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(3, 1), +Rational(3, 1))
+ assert_equal(Rational(-3, 1), -Rational(3, 1))
+ assert_equal(Rational(4, 1),
+ Rational(3, 1) + Rational(1, 1))
+ assert_equal(Rational(2, 1),
+ Rational(3, 1) - Rational(1, 1))
+ assert_equal(Rational(3, 1),
+ Rational(3, 1) * Rational(1, 1))
+ assert_equal(Rational(3, 1),
+ Rational(3, 1) / Rational(1, 1))
+ assert_equal(Rational(5, 1),
+ Rational(3, 1) + Rational(2, 1))
+ assert_equal(Rational(1, 1),
+ Rational(3, 1) - Rational(2, 1))
+ assert_equal(Rational(6, 1),
+ Rational(3, 1) * Rational(2, 1))
+ assert_equal(Rational(3, 2),
+ Rational(3, 1) / Rational(2, 1))
+ assert_equal(Rational(6, 1),
+ Rational(3, 1) + Rational(3, 1))
+ assert_equal(Rational(0, 1),
+ Rational(3, 1) - Rational(3, 1))
+ assert_equal(Rational(9, 1),
+ Rational(3, 1) * Rational(3, 1))
+ assert_equal(Rational(1, 1),
+ Rational(3, 1) / Rational(3, 1))
+ assert_equal(Rational(1073741792, 1),
+ Rational(3, 1) + Rational(1073741789, 1))
+ assert_equal(Rational(-1073741786, 1),
+ Rational(3, 1) - Rational(1073741789, 1))
+ assert_equal(Rational(3221225367, 1),
+ Rational(3, 1) * Rational(1073741789, 1))
+ assert_equal(Rational(3, 1073741789),
+ Rational(3, 1) / Rational(1073741789, 1))
+ assert_equal(Rational(1073741830, 1),
+ Rational(3, 1) + Rational(1073741827, 1))
+ assert_equal(Rational(-1073741824, 1),
+ Rational(3, 1) - Rational(1073741827, 1))
+ assert_equal(Rational(3221225481, 1),
+ Rational(3, 1) * Rational(1073741827, 1))
+ assert_equal(Rational(3, 1073741827),
+ Rational(3, 1) / Rational(1073741827, 1))
+ assert_equal(Rational(11, 3),
+ Rational(3, 1) + Rational(2, 3))
+ assert_equal(Rational(7, 3),
+ Rational(3, 1) - Rational(2, 3))
+ assert_equal(Rational(2, 1),
+ Rational(3, 1) * Rational(2, 3))
+ assert_equal(Rational(9, 2),
+ Rational(3, 1) / Rational(2, 3))
+ assert_equal(Rational(9, 2),
+ Rational(3, 1) + Rational(3, 2))
+ assert_equal(Rational(3, 2),
+ Rational(3, 1) - Rational(3, 2))
+ assert_equal(Rational(9, 2),
+ Rational(3, 1) * Rational(3, 2))
+ assert_equal(Rational(2, 1),
+ Rational(3, 1) / Rational(3, 2))
+ assert_equal(Rational(3221225370, 1073741789),
+ Rational(3, 1) + Rational(3, 1073741789))
+ assert_equal(Rational(3221225364, 1073741789),
+ Rational(3, 1) - Rational(3, 1073741789))
+ assert_equal(Rational(9, 1073741789),
+ Rational(3, 1) * Rational(3, 1073741789))
+ assert_equal(Rational(1073741789, 1),
+ Rational(3, 1) / Rational(3, 1073741789))
+ assert_equal(Rational(1073741798, 3),
+ Rational(3, 1) + Rational(1073741789, 3))
+ assert_equal(Rational(-1073741780, 3),
+ Rational(3, 1) - Rational(1073741789, 3))
+ assert_equal(Rational(1073741789, 1),
+ Rational(3, 1) * Rational(1073741789, 3))
+ assert_equal(Rational(9, 1073741789),
+ Rational(3, 1) / Rational(1073741789, 3))
+ assert_equal(Rational(3221225484, 1073741827),
+ Rational(3, 1) + Rational(3, 1073741827))
+ assert_equal(Rational(3221225478, 1073741827),
+ Rational(3, 1) - Rational(3, 1073741827))
+ assert_equal(Rational(9, 1073741827),
+ Rational(3, 1) * Rational(3, 1073741827))
+ assert_equal(Rational(1073741827, 1),
+ Rational(3, 1) / Rational(3, 1073741827))
+ assert_equal(Rational(1073741836, 3),
+ Rational(3, 1) + Rational(1073741827, 3))
+ assert_equal(Rational(-1073741818, 3),
+ Rational(3, 1) - Rational(1073741827, 3))
+ assert_equal(Rational(1073741827, 1),
+ Rational(3, 1) * Rational(1073741827, 3))
+ assert_equal(Rational(9, 1073741827),
+ Rational(3, 1) / Rational(1073741827, 3))
+ assert_equal(Rational(4294967270, 1073741827),
+ Rational(3, 1) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(2147483692, 1073741827),
+ Rational(3, 1) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(3221225367, 1073741827),
+ Rational(3, 1) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(3221225481, 1073741789),
+ Rational(3, 1) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(4294967194, 1073741789),
+ Rational(3, 1) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(2147483540, 1073741789),
+ Rational(3, 1) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(3221225481, 1073741789),
+ Rational(3, 1) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(3221225367, 1073741827),
+ Rational(3, 1) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741789, 1), +Rational(1073741789, 1))
+ assert_equal(Rational(-1073741789, 1), -Rational(1073741789, 1))
+ assert_equal(Rational(1073741790, 1),
+ Rational(1073741789, 1) + Rational(1, 1))
+ assert_equal(Rational(1073741788, 1),
+ Rational(1073741789, 1) - Rational(1, 1))
+ assert_equal(Rational(1073741789, 1),
+ Rational(1073741789, 1) * Rational(1, 1))
+ assert_equal(Rational(1073741789, 1),
+ Rational(1073741789, 1) / Rational(1, 1))
+ assert_equal(Rational(1073741791, 1),
+ Rational(1073741789, 1) + Rational(2, 1))
+ assert_equal(Rational(1073741787, 1),
+ Rational(1073741789, 1) - Rational(2, 1))
+ assert_equal(Rational(2147483578, 1),
+ Rational(1073741789, 1) * Rational(2, 1))
+ assert_equal(Rational(1073741789, 2),
+ Rational(1073741789, 1) / Rational(2, 1))
+ assert_equal(Rational(1073741792, 1),
+ Rational(1073741789, 1) + Rational(3, 1))
+ assert_equal(Rational(1073741786, 1),
+ Rational(1073741789, 1) - Rational(3, 1))
+ assert_equal(Rational(3221225367, 1),
+ Rational(1073741789, 1) * Rational(3, 1))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1073741789, 1) / Rational(3, 1))
+ assert_equal(Rational(2147483578, 1),
+ Rational(1073741789, 1) + Rational(1073741789, 1))
+ assert_equal(Rational(0, 1),
+ Rational(1073741789, 1) - Rational(1073741789, 1))
+ assert_equal(Rational(1152921429444920521, 1),
+ Rational(1073741789, 1) * Rational(1073741789, 1))
+ assert_equal(Rational(1, 1),
+ Rational(1073741789, 1) / Rational(1073741789, 1))
+ assert_equal(Rational(2147483616, 1),
+ Rational(1073741789, 1) + Rational(1073741827, 1))
+ assert_equal(Rational(-38, 1),
+ Rational(1073741789, 1) - Rational(1073741827, 1))
+ assert_equal(Rational(1152921470247108503, 1),
+ Rational(1073741789, 1) * Rational(1073741827, 1))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(1073741789, 1) / Rational(1073741827, 1))
+ assert_equal(Rational(3221225369, 3),
+ Rational(1073741789, 1) + Rational(2, 3))
+ assert_equal(Rational(3221225365, 3),
+ Rational(1073741789, 1) - Rational(2, 3))
+ assert_equal(Rational(2147483578, 3),
+ Rational(1073741789, 1) * Rational(2, 3))
+ assert_equal(Rational(3221225367, 2),
+ Rational(1073741789, 1) / Rational(2, 3))
+ assert_equal(Rational(2147483581, 2),
+ Rational(1073741789, 1) + Rational(3, 2))
+ assert_equal(Rational(2147483575, 2),
+ Rational(1073741789, 1) - Rational(3, 2))
+ assert_equal(Rational(3221225367, 2),
+ Rational(1073741789, 1) * Rational(3, 2))
+ assert_equal(Rational(2147483578, 3),
+ Rational(1073741789, 1) / Rational(3, 2))
+ assert_equal(Rational(1152921429444920524, 1073741789),
+ Rational(1073741789, 1) + Rational(3, 1073741789))
+ assert_equal(Rational(1152921429444920518, 1073741789),
+ Rational(1073741789, 1) - Rational(3, 1073741789))
+ assert_equal(Rational(3, 1),
+ Rational(1073741789, 1) * Rational(3, 1073741789))
+ assert_equal(Rational(1152921429444920521, 3),
+ Rational(1073741789, 1) / Rational(3, 1073741789))
+ assert_equal(Rational(4294967156, 3),
+ Rational(1073741789, 1) + Rational(1073741789, 3))
+ assert_equal(Rational(2147483578, 3),
+ Rational(1073741789, 1) - Rational(1073741789, 3))
+ assert_equal(Rational(1152921429444920521, 3),
+ Rational(1073741789, 1) * Rational(1073741789, 3))
+ assert_equal(Rational(3, 1),
+ Rational(1073741789, 1) / Rational(1073741789, 3))
+ assert_equal(Rational(1152921470247108506, 1073741827),
+ Rational(1073741789, 1) + Rational(3, 1073741827))
+ assert_equal(Rational(1152921470247108500, 1073741827),
+ Rational(1073741789, 1) - Rational(3, 1073741827))
+ assert_equal(Rational(3221225367, 1073741827),
+ Rational(1073741789, 1) * Rational(3, 1073741827))
+ assert_equal(Rational(1152921470247108503, 3),
+ Rational(1073741789, 1) / Rational(3, 1073741827))
+ assert_equal(Rational(4294967194, 3),
+ Rational(1073741789, 1) + Rational(1073741827, 3))
+ assert_equal(Rational(2147483540, 3),
+ Rational(1073741789, 1) - Rational(1073741827, 3))
+ assert_equal(Rational(1152921470247108503, 3),
+ Rational(1073741789, 1) * Rational(1073741827, 3))
+ assert_equal(Rational(3221225367, 1073741827),
+ Rational(1073741789, 1) / Rational(1073741827, 3))
+ assert_equal(Rational(1152921471320850292, 1073741827),
+ Rational(1073741789, 1) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921469173366714, 1073741827),
+ Rational(1073741789, 1) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921429444920521, 1073741827),
+ Rational(1073741789, 1) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741827, 1),
+ Rational(1073741789, 1) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921430518662348, 1073741789),
+ Rational(1073741789, 1) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921428371178694, 1073741789),
+ Rational(1073741789, 1) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741827, 1),
+ Rational(1073741789, 1) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921429444920521, 1073741827),
+ Rational(1073741789, 1) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741827, 1), +Rational(1073741827, 1))
+ assert_equal(Rational(-1073741827, 1), -Rational(1073741827, 1))
+ assert_equal(Rational(1073741828, 1),
+ Rational(1073741827, 1) + Rational(1, 1))
+ assert_equal(Rational(1073741826, 1),
+ Rational(1073741827, 1) - Rational(1, 1))
+ assert_equal(Rational(1073741827, 1),
+ Rational(1073741827, 1) * Rational(1, 1))
+ assert_equal(Rational(1073741827, 1),
+ Rational(1073741827, 1) / Rational(1, 1))
+ assert_equal(Rational(1073741829, 1),
+ Rational(1073741827, 1) + Rational(2, 1))
+ assert_equal(Rational(1073741825, 1),
+ Rational(1073741827, 1) - Rational(2, 1))
+ assert_equal(Rational(2147483654, 1),
+ Rational(1073741827, 1) * Rational(2, 1))
+ assert_equal(Rational(1073741827, 2),
+ Rational(1073741827, 1) / Rational(2, 1))
+ assert_equal(Rational(1073741830, 1),
+ Rational(1073741827, 1) + Rational(3, 1))
+ assert_equal(Rational(1073741824, 1),
+ Rational(1073741827, 1) - Rational(3, 1))
+ assert_equal(Rational(3221225481, 1),
+ Rational(1073741827, 1) * Rational(3, 1))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1073741827, 1) / Rational(3, 1))
+ assert_equal(Rational(2147483616, 1),
+ Rational(1073741827, 1) + Rational(1073741789, 1))
+ assert_equal(Rational(38, 1),
+ Rational(1073741827, 1) - Rational(1073741789, 1))
+ assert_equal(Rational(1152921470247108503, 1),
+ Rational(1073741827, 1) * Rational(1073741789, 1))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(1073741827, 1) / Rational(1073741789, 1))
+ assert_equal(Rational(2147483654, 1),
+ Rational(1073741827, 1) + Rational(1073741827, 1))
+ assert_equal(Rational(0, 1),
+ Rational(1073741827, 1) - Rational(1073741827, 1))
+ assert_equal(Rational(1152921511049297929, 1),
+ Rational(1073741827, 1) * Rational(1073741827, 1))
+ assert_equal(Rational(1, 1),
+ Rational(1073741827, 1) / Rational(1073741827, 1))
+ assert_equal(Rational(3221225483, 3),
+ Rational(1073741827, 1) + Rational(2, 3))
+ assert_equal(Rational(3221225479, 3),
+ Rational(1073741827, 1) - Rational(2, 3))
+ assert_equal(Rational(2147483654, 3),
+ Rational(1073741827, 1) * Rational(2, 3))
+ assert_equal(Rational(3221225481, 2),
+ Rational(1073741827, 1) / Rational(2, 3))
+ assert_equal(Rational(2147483657, 2),
+ Rational(1073741827, 1) + Rational(3, 2))
+ assert_equal(Rational(2147483651, 2),
+ Rational(1073741827, 1) - Rational(3, 2))
+ assert_equal(Rational(3221225481, 2),
+ Rational(1073741827, 1) * Rational(3, 2))
+ assert_equal(Rational(2147483654, 3),
+ Rational(1073741827, 1) / Rational(3, 2))
+ assert_equal(Rational(1152921470247108506, 1073741789),
+ Rational(1073741827, 1) + Rational(3, 1073741789))
+ assert_equal(Rational(1152921470247108500, 1073741789),
+ Rational(1073741827, 1) - Rational(3, 1073741789))
+ assert_equal(Rational(3221225481, 1073741789),
+ Rational(1073741827, 1) * Rational(3, 1073741789))
+ assert_equal(Rational(1152921470247108503, 3),
+ Rational(1073741827, 1) / Rational(3, 1073741789))
+ assert_equal(Rational(4294967270, 3),
+ Rational(1073741827, 1) + Rational(1073741789, 3))
+ assert_equal(Rational(2147483692, 3),
+ Rational(1073741827, 1) - Rational(1073741789, 3))
+ assert_equal(Rational(1152921470247108503, 3),
+ Rational(1073741827, 1) * Rational(1073741789, 3))
+ assert_equal(Rational(3221225481, 1073741789),
+ Rational(1073741827, 1) / Rational(1073741789, 3))
+ assert_equal(Rational(1152921511049297932, 1073741827),
+ Rational(1073741827, 1) + Rational(3, 1073741827))
+ assert_equal(Rational(1152921511049297926, 1073741827),
+ Rational(1073741827, 1) - Rational(3, 1073741827))
+ assert_equal(Rational(3, 1),
+ Rational(1073741827, 1) * Rational(3, 1073741827))
+ assert_equal(Rational(1152921511049297929, 3),
+ Rational(1073741827, 1) / Rational(3, 1073741827))
+ assert_equal(Rational(4294967308, 3),
+ Rational(1073741827, 1) + Rational(1073741827, 3))
+ assert_equal(Rational(2147483654, 3),
+ Rational(1073741827, 1) - Rational(1073741827, 3))
+ assert_equal(Rational(1152921511049297929, 3),
+ Rational(1073741827, 1) * Rational(1073741827, 3))
+ assert_equal(Rational(3, 1),
+ Rational(1073741827, 1) / Rational(1073741827, 3))
+ assert_equal(Rational(1152921512123039718, 1073741827),
+ Rational(1073741827, 1) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921509975556140, 1073741827),
+ Rational(1073741827, 1) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741789, 1),
+ Rational(1073741827, 1) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921511049297929, 1073741789),
+ Rational(1073741827, 1) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921471320850330, 1073741789),
+ Rational(1073741827, 1) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921469173366676, 1073741789),
+ Rational(1073741827, 1) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921511049297929, 1073741789),
+ Rational(1073741827, 1) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741789, 1),
+ Rational(1073741827, 1) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(2, 3), +Rational(2, 3))
+ assert_equal(Rational(-2, 3), -Rational(2, 3))
+ assert_equal(Rational(5, 3),
+ Rational(2, 3) + Rational(1, 1))
+ assert_equal(Rational(-1, 3),
+ Rational(2, 3) - Rational(1, 1))
+ assert_equal(Rational(2, 3),
+ Rational(2, 3) * Rational(1, 1))
+ assert_equal(Rational(2, 3),
+ Rational(2, 3) / Rational(1, 1))
+ assert_equal(Rational(8, 3),
+ Rational(2, 3) + Rational(2, 1))
+ assert_equal(Rational(-4, 3),
+ Rational(2, 3) - Rational(2, 1))
+ assert_equal(Rational(4, 3),
+ Rational(2, 3) * Rational(2, 1))
+ assert_equal(Rational(1, 3),
+ Rational(2, 3) / Rational(2, 1))
+ assert_equal(Rational(11, 3),
+ Rational(2, 3) + Rational(3, 1))
+ assert_equal(Rational(-7, 3),
+ Rational(2, 3) - Rational(3, 1))
+ assert_equal(Rational(2, 1),
+ Rational(2, 3) * Rational(3, 1))
+ assert_equal(Rational(2, 9),
+ Rational(2, 3) / Rational(3, 1))
+ assert_equal(Rational(3221225369, 3),
+ Rational(2, 3) + Rational(1073741789, 1))
+ assert_equal(Rational(-3221225365, 3),
+ Rational(2, 3) - Rational(1073741789, 1))
+ assert_equal(Rational(2147483578, 3),
+ Rational(2, 3) * Rational(1073741789, 1))
+ assert_equal(Rational(2, 3221225367),
+ Rational(2, 3) / Rational(1073741789, 1))
+ assert_equal(Rational(3221225483, 3),
+ Rational(2, 3) + Rational(1073741827, 1))
+ assert_equal(Rational(-3221225479, 3),
+ Rational(2, 3) - Rational(1073741827, 1))
+ assert_equal(Rational(2147483654, 3),
+ Rational(2, 3) * Rational(1073741827, 1))
+ assert_equal(Rational(2, 3221225481),
+ Rational(2, 3) / Rational(1073741827, 1))
+ assert_equal(Rational(4, 3),
+ Rational(2, 3) + Rational(2, 3))
+ assert_equal(Rational(0, 1),
+ Rational(2, 3) - Rational(2, 3))
+ assert_equal(Rational(4, 9),
+ Rational(2, 3) * Rational(2, 3))
+ assert_equal(Rational(1, 1),
+ Rational(2, 3) / Rational(2, 3))
+ assert_equal(Rational(13, 6),
+ Rational(2, 3) + Rational(3, 2))
+ assert_equal(Rational(-5, 6),
+ Rational(2, 3) - Rational(3, 2))
+ assert_equal(Rational(1, 1),
+ Rational(2, 3) * Rational(3, 2))
+ assert_equal(Rational(4, 9),
+ Rational(2, 3) / Rational(3, 2))
+ assert_equal(Rational(2147483587, 3221225367),
+ Rational(2, 3) + Rational(3, 1073741789))
+ assert_equal(Rational(2147483569, 3221225367),
+ Rational(2, 3) - Rational(3, 1073741789))
+ assert_equal(Rational(2, 1073741789),
+ Rational(2, 3) * Rational(3, 1073741789))
+ assert_equal(Rational(2147483578, 9),
+ Rational(2, 3) / Rational(3, 1073741789))
+ assert_equal(Rational(1073741791, 3),
+ Rational(2, 3) + Rational(1073741789, 3))
+ assert_equal(Rational(-357913929, 1),
+ Rational(2, 3) - Rational(1073741789, 3))
+ assert_equal(Rational(2147483578, 9),
+ Rational(2, 3) * Rational(1073741789, 3))
+ assert_equal(Rational(2, 1073741789),
+ Rational(2, 3) / Rational(1073741789, 3))
+ assert_equal(Rational(2147483663, 3221225481),
+ Rational(2, 3) + Rational(3, 1073741827))
+ assert_equal(Rational(2147483645, 3221225481),
+ Rational(2, 3) - Rational(3, 1073741827))
+ assert_equal(Rational(2, 1073741827),
+ Rational(2, 3) * Rational(3, 1073741827))
+ assert_equal(Rational(2147483654, 9),
+ Rational(2, 3) / Rational(3, 1073741827))
+ assert_equal(Rational(357913943, 1),
+ Rational(2, 3) + Rational(1073741827, 3))
+ assert_equal(Rational(-1073741825, 3),
+ Rational(2, 3) - Rational(1073741827, 3))
+ assert_equal(Rational(2147483654, 9),
+ Rational(2, 3) * Rational(1073741827, 3))
+ assert_equal(Rational(2, 1073741827),
+ Rational(2, 3) / Rational(1073741827, 3))
+ assert_equal(Rational(5368709021, 3221225481),
+ Rational(2, 3) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(-1073741713, 3221225481),
+ Rational(2, 3) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(2147483578, 3221225481),
+ Rational(2, 3) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(2147483654, 3221225367),
+ Rational(2, 3) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(5368709059, 3221225367),
+ Rational(2, 3) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(-1073741903, 3221225367),
+ Rational(2, 3) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(2147483654, 3221225367),
+ Rational(2, 3) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(2147483578, 3221225481),
+ Rational(2, 3) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(3, 2), +Rational(3, 2))
+ assert_equal(Rational(-3, 2), -Rational(3, 2))
+ assert_equal(Rational(5, 2),
+ Rational(3, 2) + Rational(1, 1))
+ assert_equal(Rational(1, 2),
+ Rational(3, 2) - Rational(1, 1))
+ assert_equal(Rational(3, 2),
+ Rational(3, 2) * Rational(1, 1))
+ assert_equal(Rational(3, 2),
+ Rational(3, 2) / Rational(1, 1))
+ assert_equal(Rational(7, 2),
+ Rational(3, 2) + Rational(2, 1))
+ assert_equal(Rational(-1, 2),
+ Rational(3, 2) - Rational(2, 1))
+ assert_equal(Rational(3, 1),
+ Rational(3, 2) * Rational(2, 1))
+ assert_equal(Rational(3, 4),
+ Rational(3, 2) / Rational(2, 1))
+ assert_equal(Rational(9, 2),
+ Rational(3, 2) + Rational(3, 1))
+ assert_equal(Rational(-3, 2),
+ Rational(3, 2) - Rational(3, 1))
+ assert_equal(Rational(9, 2),
+ Rational(3, 2) * Rational(3, 1))
+ assert_equal(Rational(1, 2),
+ Rational(3, 2) / Rational(3, 1))
+ assert_equal(Rational(2147483581, 2),
+ Rational(3, 2) + Rational(1073741789, 1))
+ assert_equal(Rational(-2147483575, 2),
+ Rational(3, 2) - Rational(1073741789, 1))
+ assert_equal(Rational(3221225367, 2),
+ Rational(3, 2) * Rational(1073741789, 1))
+ assert_equal(Rational(3, 2147483578),
+ Rational(3, 2) / Rational(1073741789, 1))
+ assert_equal(Rational(2147483657, 2),
+ Rational(3, 2) + Rational(1073741827, 1))
+ assert_equal(Rational(-2147483651, 2),
+ Rational(3, 2) - Rational(1073741827, 1))
+ assert_equal(Rational(3221225481, 2),
+ Rational(3, 2) * Rational(1073741827, 1))
+ assert_equal(Rational(3, 2147483654),
+ Rational(3, 2) / Rational(1073741827, 1))
+ assert_equal(Rational(13, 6),
+ Rational(3, 2) + Rational(2, 3))
+ assert_equal(Rational(5, 6),
+ Rational(3, 2) - Rational(2, 3))
+ assert_equal(Rational(1, 1),
+ Rational(3, 2) * Rational(2, 3))
+ assert_equal(Rational(9, 4),
+ Rational(3, 2) / Rational(2, 3))
+ assert_equal(Rational(3, 1),
+ Rational(3, 2) + Rational(3, 2))
+ assert_equal(Rational(0, 1),
+ Rational(3, 2) - Rational(3, 2))
+ assert_equal(Rational(9, 4),
+ Rational(3, 2) * Rational(3, 2))
+ assert_equal(Rational(1, 1),
+ Rational(3, 2) / Rational(3, 2))
+ assert_equal(Rational(3221225373, 2147483578),
+ Rational(3, 2) + Rational(3, 1073741789))
+ assert_equal(Rational(3221225361, 2147483578),
+ Rational(3, 2) - Rational(3, 1073741789))
+ assert_equal(Rational(9, 2147483578),
+ Rational(3, 2) * Rational(3, 1073741789))
+ assert_equal(Rational(1073741789, 2),
+ Rational(3, 2) / Rational(3, 1073741789))
+ assert_equal(Rational(2147483587, 6),
+ Rational(3, 2) + Rational(1073741789, 3))
+ assert_equal(Rational(-2147483569, 6),
+ Rational(3, 2) - Rational(1073741789, 3))
+ assert_equal(Rational(1073741789, 2),
+ Rational(3, 2) * Rational(1073741789, 3))
+ assert_equal(Rational(9, 2147483578),
+ Rational(3, 2) / Rational(1073741789, 3))
+ assert_equal(Rational(3221225487, 2147483654),
+ Rational(3, 2) + Rational(3, 1073741827))
+ assert_equal(Rational(3221225475, 2147483654),
+ Rational(3, 2) - Rational(3, 1073741827))
+ assert_equal(Rational(9, 2147483654),
+ Rational(3, 2) * Rational(3, 1073741827))
+ assert_equal(Rational(1073741827, 2),
+ Rational(3, 2) / Rational(3, 1073741827))
+ assert_equal(Rational(2147483663, 6),
+ Rational(3, 2) + Rational(1073741827, 3))
+ assert_equal(Rational(-2147483645, 6),
+ Rational(3, 2) - Rational(1073741827, 3))
+ assert_equal(Rational(1073741827, 2),
+ Rational(3, 2) * Rational(1073741827, 3))
+ assert_equal(Rational(9, 2147483654),
+ Rational(3, 2) / Rational(1073741827, 3))
+ assert_equal(Rational(5368709059, 2147483654),
+ Rational(3, 2) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741903, 2147483654),
+ Rational(3, 2) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(3221225367, 2147483654),
+ Rational(3, 2) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(3221225481, 2147483578),
+ Rational(3, 2) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(5368709021, 2147483578),
+ Rational(3, 2) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741713, 2147483578),
+ Rational(3, 2) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(3221225481, 2147483578),
+ Rational(3, 2) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(3221225367, 2147483654),
+ Rational(3, 2) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(3, 1073741789), +Rational(3, 1073741789))
+ assert_equal(Rational(-3, 1073741789), -Rational(3, 1073741789))
+ assert_equal(Rational(1073741792, 1073741789),
+ Rational(3, 1073741789) + Rational(1, 1))
+ assert_equal(Rational(-1073741786, 1073741789),
+ Rational(3, 1073741789) - Rational(1, 1))
+ assert_equal(Rational(3, 1073741789),
+ Rational(3, 1073741789) * Rational(1, 1))
+ assert_equal(Rational(3, 1073741789),
+ Rational(3, 1073741789) / Rational(1, 1))
+ assert_equal(Rational(2147483581, 1073741789),
+ Rational(3, 1073741789) + Rational(2, 1))
+ assert_equal(Rational(-2147483575, 1073741789),
+ Rational(3, 1073741789) - Rational(2, 1))
+ assert_equal(Rational(6, 1073741789),
+ Rational(3, 1073741789) * Rational(2, 1))
+ assert_equal(Rational(3, 2147483578),
+ Rational(3, 1073741789) / Rational(2, 1))
+ assert_equal(Rational(3221225370, 1073741789),
+ Rational(3, 1073741789) + Rational(3, 1))
+ assert_equal(Rational(-3221225364, 1073741789),
+ Rational(3, 1073741789) - Rational(3, 1))
+ assert_equal(Rational(9, 1073741789),
+ Rational(3, 1073741789) * Rational(3, 1))
+ assert_equal(Rational(1, 1073741789),
+ Rational(3, 1073741789) / Rational(3, 1))
+ assert_equal(Rational(1152921429444920524, 1073741789),
+ Rational(3, 1073741789) + Rational(1073741789, 1))
+ assert_equal(Rational(-1152921429444920518, 1073741789),
+ Rational(3, 1073741789) - Rational(1073741789, 1))
+ assert_equal(Rational(3, 1),
+ Rational(3, 1073741789) * Rational(1073741789, 1))
+ assert_equal(Rational(3, 1152921429444920521),
+ Rational(3, 1073741789) / Rational(1073741789, 1))
+ assert_equal(Rational(1152921470247108506, 1073741789),
+ Rational(3, 1073741789) + Rational(1073741827, 1))
+ assert_equal(Rational(-1152921470247108500, 1073741789),
+ Rational(3, 1073741789) - Rational(1073741827, 1))
+ assert_equal(Rational(3221225481, 1073741789),
+ Rational(3, 1073741789) * Rational(1073741827, 1))
+ assert_equal(Rational(3, 1152921470247108503),
+ Rational(3, 1073741789) / Rational(1073741827, 1))
+ assert_equal(Rational(2147483587, 3221225367),
+ Rational(3, 1073741789) + Rational(2, 3))
+ assert_equal(Rational(-2147483569, 3221225367),
+ Rational(3, 1073741789) - Rational(2, 3))
+ assert_equal(Rational(2, 1073741789),
+ Rational(3, 1073741789) * Rational(2, 3))
+ assert_equal(Rational(9, 2147483578),
+ Rational(3, 1073741789) / Rational(2, 3))
+ assert_equal(Rational(3221225373, 2147483578),
+ Rational(3, 1073741789) + Rational(3, 2))
+ assert_equal(Rational(-3221225361, 2147483578),
+ Rational(3, 1073741789) - Rational(3, 2))
+ assert_equal(Rational(9, 2147483578),
+ Rational(3, 1073741789) * Rational(3, 2))
+ assert_equal(Rational(2, 1073741789),
+ Rational(3, 1073741789) / Rational(3, 2))
+ assert_equal(Rational(6, 1073741789),
+ Rational(3, 1073741789) + Rational(3, 1073741789))
+ assert_equal(Rational(0, 1),
+ Rational(3, 1073741789) - Rational(3, 1073741789))
+ assert_equal(Rational(9, 1152921429444920521),
+ Rational(3, 1073741789) * Rational(3, 1073741789))
+ assert_equal(Rational(1, 1),
+ Rational(3, 1073741789) / Rational(3, 1073741789))
+ assert_equal(Rational(1152921429444920530, 3221225367),
+ Rational(3, 1073741789) + Rational(1073741789, 3))
+ assert_equal(Rational(-1152921429444920512, 3221225367),
+ Rational(3, 1073741789) - Rational(1073741789, 3))
+ assert_equal(Rational(1, 1),
+ Rational(3, 1073741789) * Rational(1073741789, 3))
+ assert_equal(Rational(9, 1152921429444920521),
+ Rational(3, 1073741789) / Rational(1073741789, 3))
+ assert_equal(Rational(6442450848, 1152921470247108503),
+ Rational(3, 1073741789) + Rational(3, 1073741827))
+ assert_equal(Rational(114, 1152921470247108503),
+ Rational(3, 1073741789) - Rational(3, 1073741827))
+ assert_equal(Rational(9, 1152921470247108503),
+ Rational(3, 1073741789) * Rational(3, 1073741827))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(3, 1073741789) / Rational(3, 1073741827))
+ assert_equal(Rational(1152921470247108512, 3221225367),
+ Rational(3, 1073741789) + Rational(1073741827, 3))
+ assert_equal(Rational(-1152921470247108494, 3221225367),
+ Rational(3, 1073741789) - Rational(1073741827, 3))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(3, 1073741789) * Rational(1073741827, 3))
+ assert_equal(Rational(9, 1152921470247108503),
+ Rational(3, 1073741789) / Rational(1073741827, 3))
+ assert_equal(Rational(1152921432666146002, 1152921470247108503),
+ Rational(3, 1073741789) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(-1152921426223695040, 1152921470247108503),
+ Rational(3, 1073741789) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(3, 1073741827),
+ Rational(3, 1073741789) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(3221225481, 1152921429444920521),
+ Rational(3, 1073741789) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741830, 1073741789),
+ Rational(3, 1073741789) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(-1073741824, 1073741789),
+ Rational(3, 1073741789) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(3221225481, 1152921429444920521),
+ Rational(3, 1073741789) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(3, 1073741827),
+ Rational(3, 1073741789) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741789, 3), +Rational(1073741789, 3))
+ assert_equal(Rational(-1073741789, 3), -Rational(1073741789, 3))
+ assert_equal(Rational(1073741792, 3),
+ Rational(1073741789, 3) + Rational(1, 1))
+ assert_equal(Rational(1073741786, 3),
+ Rational(1073741789, 3) - Rational(1, 1))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1073741789, 3) * Rational(1, 1))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1073741789, 3) / Rational(1, 1))
+ assert_equal(Rational(1073741795, 3),
+ Rational(1073741789, 3) + Rational(2, 1))
+ assert_equal(Rational(1073741783, 3),
+ Rational(1073741789, 3) - Rational(2, 1))
+ assert_equal(Rational(2147483578, 3),
+ Rational(1073741789, 3) * Rational(2, 1))
+ assert_equal(Rational(1073741789, 6),
+ Rational(1073741789, 3) / Rational(2, 1))
+ assert_equal(Rational(1073741798, 3),
+ Rational(1073741789, 3) + Rational(3, 1))
+ assert_equal(Rational(1073741780, 3),
+ Rational(1073741789, 3) - Rational(3, 1))
+ assert_equal(Rational(1073741789, 1),
+ Rational(1073741789, 3) * Rational(3, 1))
+ assert_equal(Rational(1073741789, 9),
+ Rational(1073741789, 3) / Rational(3, 1))
+ assert_equal(Rational(4294967156, 3),
+ Rational(1073741789, 3) + Rational(1073741789, 1))
+ assert_equal(Rational(-2147483578, 3),
+ Rational(1073741789, 3) - Rational(1073741789, 1))
+ assert_equal(Rational(1152921429444920521, 3),
+ Rational(1073741789, 3) * Rational(1073741789, 1))
+ assert_equal(Rational(1, 3),
+ Rational(1073741789, 3) / Rational(1073741789, 1))
+ assert_equal(Rational(4294967270, 3),
+ Rational(1073741789, 3) + Rational(1073741827, 1))
+ assert_equal(Rational(-2147483692, 3),
+ Rational(1073741789, 3) - Rational(1073741827, 1))
+ assert_equal(Rational(1152921470247108503, 3),
+ Rational(1073741789, 3) * Rational(1073741827, 1))
+ assert_equal(Rational(1073741789, 3221225481),
+ Rational(1073741789, 3) / Rational(1073741827, 1))
+ assert_equal(Rational(1073741791, 3),
+ Rational(1073741789, 3) + Rational(2, 3))
+ assert_equal(Rational(357913929, 1),
+ Rational(1073741789, 3) - Rational(2, 3))
+ assert_equal(Rational(2147483578, 9),
+ Rational(1073741789, 3) * Rational(2, 3))
+ assert_equal(Rational(1073741789, 2),
+ Rational(1073741789, 3) / Rational(2, 3))
+ assert_equal(Rational(2147483587, 6),
+ Rational(1073741789, 3) + Rational(3, 2))
+ assert_equal(Rational(2147483569, 6),
+ Rational(1073741789, 3) - Rational(3, 2))
+ assert_equal(Rational(1073741789, 2),
+ Rational(1073741789, 3) * Rational(3, 2))
+ assert_equal(Rational(2147483578, 9),
+ Rational(1073741789, 3) / Rational(3, 2))
+ assert_equal(Rational(1152921429444920530, 3221225367),
+ Rational(1073741789, 3) + Rational(3, 1073741789))
+ assert_equal(Rational(1152921429444920512, 3221225367),
+ Rational(1073741789, 3) - Rational(3, 1073741789))
+ assert_equal(Rational(1, 1),
+ Rational(1073741789, 3) * Rational(3, 1073741789))
+ assert_equal(Rational(1152921429444920521, 9),
+ Rational(1073741789, 3) / Rational(3, 1073741789))
+ assert_equal(Rational(2147483578, 3),
+ Rational(1073741789, 3) + Rational(1073741789, 3))
+ assert_equal(Rational(0, 1),
+ Rational(1073741789, 3) - Rational(1073741789, 3))
+ assert_equal(Rational(1152921429444920521, 9),
+ Rational(1073741789, 3) * Rational(1073741789, 3))
+ assert_equal(Rational(1, 1),
+ Rational(1073741789, 3) / Rational(1073741789, 3))
+ assert_equal(Rational(1152921470247108512, 3221225481),
+ Rational(1073741789, 3) + Rational(3, 1073741827))
+ assert_equal(Rational(1152921470247108494, 3221225481),
+ Rational(1073741789, 3) - Rational(3, 1073741827))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(1073741789, 3) * Rational(3, 1073741827))
+ assert_equal(Rational(1152921470247108503, 9),
+ Rational(1073741789, 3) / Rational(3, 1073741827))
+ assert_equal(Rational(715827872, 1),
+ Rational(1073741789, 3) + Rational(1073741827, 3))
+ assert_equal(Rational(-38, 3),
+ Rational(1073741789, 3) - Rational(1073741827, 3))
+ assert_equal(Rational(1152921470247108503, 9),
+ Rational(1073741789, 3) * Rational(1073741827, 3))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(1073741789, 3) / Rational(1073741827, 3))
+ assert_equal(Rational(1152921473468333870, 3221225481),
+ Rational(1073741789, 3) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921467025883136, 3221225481),
+ Rational(1073741789, 3) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921429444920521, 3221225481),
+ Rational(1073741789, 3) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1073741789, 3) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921432666146002, 3221225367),
+ Rational(1073741789, 3) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921426223695040, 3221225367),
+ Rational(1073741789, 3) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1073741789, 3) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921429444920521, 3221225481),
+ Rational(1073741789, 3) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(3, 1073741827), +Rational(3, 1073741827))
+ assert_equal(Rational(-3, 1073741827), -Rational(3, 1073741827))
+ assert_equal(Rational(1073741830, 1073741827),
+ Rational(3, 1073741827) + Rational(1, 1))
+ assert_equal(Rational(-1073741824, 1073741827),
+ Rational(3, 1073741827) - Rational(1, 1))
+ assert_equal(Rational(3, 1073741827),
+ Rational(3, 1073741827) * Rational(1, 1))
+ assert_equal(Rational(3, 1073741827),
+ Rational(3, 1073741827) / Rational(1, 1))
+ assert_equal(Rational(2147483657, 1073741827),
+ Rational(3, 1073741827) + Rational(2, 1))
+ assert_equal(Rational(-2147483651, 1073741827),
+ Rational(3, 1073741827) - Rational(2, 1))
+ assert_equal(Rational(6, 1073741827),
+ Rational(3, 1073741827) * Rational(2, 1))
+ assert_equal(Rational(3, 2147483654),
+ Rational(3, 1073741827) / Rational(2, 1))
+ assert_equal(Rational(3221225484, 1073741827),
+ Rational(3, 1073741827) + Rational(3, 1))
+ assert_equal(Rational(-3221225478, 1073741827),
+ Rational(3, 1073741827) - Rational(3, 1))
+ assert_equal(Rational(9, 1073741827),
+ Rational(3, 1073741827) * Rational(3, 1))
+ assert_equal(Rational(1, 1073741827),
+ Rational(3, 1073741827) / Rational(3, 1))
+ assert_equal(Rational(1152921470247108506, 1073741827),
+ Rational(3, 1073741827) + Rational(1073741789, 1))
+ assert_equal(Rational(-1152921470247108500, 1073741827),
+ Rational(3, 1073741827) - Rational(1073741789, 1))
+ assert_equal(Rational(3221225367, 1073741827),
+ Rational(3, 1073741827) * Rational(1073741789, 1))
+ assert_equal(Rational(3, 1152921470247108503),
+ Rational(3, 1073741827) / Rational(1073741789, 1))
+ assert_equal(Rational(1152921511049297932, 1073741827),
+ Rational(3, 1073741827) + Rational(1073741827, 1))
+ assert_equal(Rational(-1152921511049297926, 1073741827),
+ Rational(3, 1073741827) - Rational(1073741827, 1))
+ assert_equal(Rational(3, 1),
+ Rational(3, 1073741827) * Rational(1073741827, 1))
+ assert_equal(Rational(3, 1152921511049297929),
+ Rational(3, 1073741827) / Rational(1073741827, 1))
+ assert_equal(Rational(2147483663, 3221225481),
+ Rational(3, 1073741827) + Rational(2, 3))
+ assert_equal(Rational(-2147483645, 3221225481),
+ Rational(3, 1073741827) - Rational(2, 3))
+ assert_equal(Rational(2, 1073741827),
+ Rational(3, 1073741827) * Rational(2, 3))
+ assert_equal(Rational(9, 2147483654),
+ Rational(3, 1073741827) / Rational(2, 3))
+ assert_equal(Rational(3221225487, 2147483654),
+ Rational(3, 1073741827) + Rational(3, 2))
+ assert_equal(Rational(-3221225475, 2147483654),
+ Rational(3, 1073741827) - Rational(3, 2))
+ assert_equal(Rational(9, 2147483654),
+ Rational(3, 1073741827) * Rational(3, 2))
+ assert_equal(Rational(2, 1073741827),
+ Rational(3, 1073741827) / Rational(3, 2))
+ assert_equal(Rational(6442450848, 1152921470247108503),
+ Rational(3, 1073741827) + Rational(3, 1073741789))
+ assert_equal(Rational(-114, 1152921470247108503),
+ Rational(3, 1073741827) - Rational(3, 1073741789))
+ assert_equal(Rational(9, 1152921470247108503),
+ Rational(3, 1073741827) * Rational(3, 1073741789))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(3, 1073741827) / Rational(3, 1073741789))
+ assert_equal(Rational(1152921470247108512, 3221225481),
+ Rational(3, 1073741827) + Rational(1073741789, 3))
+ assert_equal(Rational(-1152921470247108494, 3221225481),
+ Rational(3, 1073741827) - Rational(1073741789, 3))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(3, 1073741827) * Rational(1073741789, 3))
+ assert_equal(Rational(9, 1152921470247108503),
+ Rational(3, 1073741827) / Rational(1073741789, 3))
+ assert_equal(Rational(6, 1073741827),
+ Rational(3, 1073741827) + Rational(3, 1073741827))
+ assert_equal(Rational(0, 1),
+ Rational(3, 1073741827) - Rational(3, 1073741827))
+ assert_equal(Rational(9, 1152921511049297929),
+ Rational(3, 1073741827) * Rational(3, 1073741827))
+ assert_equal(Rational(1, 1),
+ Rational(3, 1073741827) / Rational(3, 1073741827))
+ assert_equal(Rational(1152921511049297938, 3221225481),
+ Rational(3, 1073741827) + Rational(1073741827, 3))
+ assert_equal(Rational(-1152921511049297920, 3221225481),
+ Rational(3, 1073741827) - Rational(1073741827, 3))
+ assert_equal(Rational(1, 1),
+ Rational(3, 1073741827) * Rational(1073741827, 3))
+ assert_equal(Rational(9, 1152921511049297929),
+ Rational(3, 1073741827) / Rational(1073741827, 3))
+ assert_equal(Rational(1073741792, 1073741827),
+ Rational(3, 1073741827) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(-1073741786, 1073741827),
+ Rational(3, 1073741827) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(3221225367, 1152921511049297929),
+ Rational(3, 1073741827) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(3, 1073741789),
+ Rational(3, 1073741827) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921514270523296, 1152921470247108503),
+ Rational(3, 1073741827) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(-1152921507828072562, 1152921470247108503),
+ Rational(3, 1073741827) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(3, 1073741789),
+ Rational(3, 1073741827) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(3221225367, 1152921511049297929),
+ Rational(3, 1073741827) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741827, 3), +Rational(1073741827, 3))
+ assert_equal(Rational(-1073741827, 3), -Rational(1073741827, 3))
+ assert_equal(Rational(1073741830, 3),
+ Rational(1073741827, 3) + Rational(1, 1))
+ assert_equal(Rational(1073741824, 3),
+ Rational(1073741827, 3) - Rational(1, 1))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1073741827, 3) * Rational(1, 1))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1073741827, 3) / Rational(1, 1))
+ assert_equal(Rational(1073741833, 3),
+ Rational(1073741827, 3) + Rational(2, 1))
+ assert_equal(Rational(1073741821, 3),
+ Rational(1073741827, 3) - Rational(2, 1))
+ assert_equal(Rational(2147483654, 3),
+ Rational(1073741827, 3) * Rational(2, 1))
+ assert_equal(Rational(1073741827, 6),
+ Rational(1073741827, 3) / Rational(2, 1))
+ assert_equal(Rational(1073741836, 3),
+ Rational(1073741827, 3) + Rational(3, 1))
+ assert_equal(Rational(1073741818, 3),
+ Rational(1073741827, 3) - Rational(3, 1))
+ assert_equal(Rational(1073741827, 1),
+ Rational(1073741827, 3) * Rational(3, 1))
+ assert_equal(Rational(1073741827, 9),
+ Rational(1073741827, 3) / Rational(3, 1))
+ assert_equal(Rational(4294967194, 3),
+ Rational(1073741827, 3) + Rational(1073741789, 1))
+ assert_equal(Rational(-2147483540, 3),
+ Rational(1073741827, 3) - Rational(1073741789, 1))
+ assert_equal(Rational(1152921470247108503, 3),
+ Rational(1073741827, 3) * Rational(1073741789, 1))
+ assert_equal(Rational(1073741827, 3221225367),
+ Rational(1073741827, 3) / Rational(1073741789, 1))
+ assert_equal(Rational(4294967308, 3),
+ Rational(1073741827, 3) + Rational(1073741827, 1))
+ assert_equal(Rational(-2147483654, 3),
+ Rational(1073741827, 3) - Rational(1073741827, 1))
+ assert_equal(Rational(1152921511049297929, 3),
+ Rational(1073741827, 3) * Rational(1073741827, 1))
+ assert_equal(Rational(1, 3),
+ Rational(1073741827, 3) / Rational(1073741827, 1))
+ assert_equal(Rational(357913943, 1),
+ Rational(1073741827, 3) + Rational(2, 3))
+ assert_equal(Rational(1073741825, 3),
+ Rational(1073741827, 3) - Rational(2, 3))
+ assert_equal(Rational(2147483654, 9),
+ Rational(1073741827, 3) * Rational(2, 3))
+ assert_equal(Rational(1073741827, 2),
+ Rational(1073741827, 3) / Rational(2, 3))
+ assert_equal(Rational(2147483663, 6),
+ Rational(1073741827, 3) + Rational(3, 2))
+ assert_equal(Rational(2147483645, 6),
+ Rational(1073741827, 3) - Rational(3, 2))
+ assert_equal(Rational(1073741827, 2),
+ Rational(1073741827, 3) * Rational(3, 2))
+ assert_equal(Rational(2147483654, 9),
+ Rational(1073741827, 3) / Rational(3, 2))
+ assert_equal(Rational(1152921470247108512, 3221225367),
+ Rational(1073741827, 3) + Rational(3, 1073741789))
+ assert_equal(Rational(1152921470247108494, 3221225367),
+ Rational(1073741827, 3) - Rational(3, 1073741789))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(1073741827, 3) * Rational(3, 1073741789))
+ assert_equal(Rational(1152921470247108503, 9),
+ Rational(1073741827, 3) / Rational(3, 1073741789))
+ assert_equal(Rational(715827872, 1),
+ Rational(1073741827, 3) + Rational(1073741789, 3))
+ assert_equal(Rational(38, 3),
+ Rational(1073741827, 3) - Rational(1073741789, 3))
+ assert_equal(Rational(1152921470247108503, 9),
+ Rational(1073741827, 3) * Rational(1073741789, 3))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(1073741827, 3) / Rational(1073741789, 3))
+ assert_equal(Rational(1152921511049297938, 3221225481),
+ Rational(1073741827, 3) + Rational(3, 1073741827))
+ assert_equal(Rational(1152921511049297920, 3221225481),
+ Rational(1073741827, 3) - Rational(3, 1073741827))
+ assert_equal(Rational(1, 1),
+ Rational(1073741827, 3) * Rational(3, 1073741827))
+ assert_equal(Rational(1152921511049297929, 9),
+ Rational(1073741827, 3) / Rational(3, 1073741827))
+ assert_equal(Rational(2147483654, 3),
+ Rational(1073741827, 3) + Rational(1073741827, 3))
+ assert_equal(Rational(0, 1),
+ Rational(1073741827, 3) - Rational(1073741827, 3))
+ assert_equal(Rational(1152921511049297929, 9),
+ Rational(1073741827, 3) * Rational(1073741827, 3))
+ assert_equal(Rational(1, 1),
+ Rational(1073741827, 3) / Rational(1073741827, 3))
+ assert_equal(Rational(1152921514270523296, 3221225481),
+ Rational(1073741827, 3) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921507828072562, 3221225481),
+ Rational(1073741827, 3) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1073741827, 3) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921511049297929, 3221225367),
+ Rational(1073741827, 3) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921473468333984, 3221225367),
+ Rational(1073741827, 3) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921467025883022, 3221225367),
+ Rational(1073741827, 3) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921511049297929, 3221225367),
+ Rational(1073741827, 3) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1073741827, 3) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741789, 1073741827), +Rational(1073741789, 1073741827))
+ assert_equal(Rational(-1073741789, 1073741827), -Rational(1073741789, 1073741827))
+ assert_equal(Rational(2147483616, 1073741827),
+ Rational(1073741789, 1073741827) + Rational(1, 1))
+ assert_equal(Rational(-38, 1073741827),
+ Rational(1073741789, 1073741827) - Rational(1, 1))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(1073741789, 1073741827) * Rational(1, 1))
+ assert_equal(Rational(1073741789, 1073741827),
+ Rational(1073741789, 1073741827) / Rational(1, 1))
+ assert_equal(Rational(3221225443, 1073741827),
+ Rational(1073741789, 1073741827) + Rational(2, 1))
+ assert_equal(Rational(-1073741865, 1073741827),
+ Rational(1073741789, 1073741827) - Rational(2, 1))
+ assert_equal(Rational(2147483578, 1073741827),
+ Rational(1073741789, 1073741827) * Rational(2, 1))
+ assert_equal(Rational(1073741789, 2147483654),
+ Rational(1073741789, 1073741827) / Rational(2, 1))
+ assert_equal(Rational(4294967270, 1073741827),
+ Rational(1073741789, 1073741827) + Rational(3, 1))
+ assert_equal(Rational(-2147483692, 1073741827),
+ Rational(1073741789, 1073741827) - Rational(3, 1))
+ assert_equal(Rational(3221225367, 1073741827),
+ Rational(1073741789, 1073741827) * Rational(3, 1))
+ assert_equal(Rational(1073741789, 3221225481),
+ Rational(1073741789, 1073741827) / Rational(3, 1))
+ assert_equal(Rational(1152921471320850292, 1073741827),
+ Rational(1073741789, 1073741827) + Rational(1073741789, 1))
+ assert_equal(Rational(-1152921469173366714, 1073741827),
+ Rational(1073741789, 1073741827) - Rational(1073741789, 1))
+ assert_equal(Rational(1152921429444920521, 1073741827),
+ Rational(1073741789, 1073741827) * Rational(1073741789, 1))
+ assert_equal(Rational(1, 1073741827),
+ Rational(1073741789, 1073741827) / Rational(1073741789, 1))
+ assert_equal(Rational(1152921512123039718, 1073741827),
+ Rational(1073741789, 1073741827) + Rational(1073741827, 1))
+ assert_equal(Rational(-1152921509975556140, 1073741827),
+ Rational(1073741789, 1073741827) - Rational(1073741827, 1))
+ assert_equal(Rational(1073741789, 1),
+ Rational(1073741789, 1073741827) * Rational(1073741827, 1))
+ assert_equal(Rational(1073741789, 1152921511049297929),
+ Rational(1073741789, 1073741827) / Rational(1073741827, 1))
+ assert_equal(Rational(5368709021, 3221225481),
+ Rational(1073741789, 1073741827) + Rational(2, 3))
+ assert_equal(Rational(1073741713, 3221225481),
+ Rational(1073741789, 1073741827) - Rational(2, 3))
+ assert_equal(Rational(2147483578, 3221225481),
+ Rational(1073741789, 1073741827) * Rational(2, 3))
+ assert_equal(Rational(3221225367, 2147483654),
+ Rational(1073741789, 1073741827) / Rational(2, 3))
+ assert_equal(Rational(5368709059, 2147483654),
+ Rational(1073741789, 1073741827) + Rational(3, 2))
+ assert_equal(Rational(-1073741903, 2147483654),
+ Rational(1073741789, 1073741827) - Rational(3, 2))
+ assert_equal(Rational(3221225367, 2147483654),
+ Rational(1073741789, 1073741827) * Rational(3, 2))
+ assert_equal(Rational(2147483578, 3221225481),
+ Rational(1073741789, 1073741827) / Rational(3, 2))
+ assert_equal(Rational(1152921432666146002, 1152921470247108503),
+ Rational(1073741789, 1073741827) + Rational(3, 1073741789))
+ assert_equal(Rational(1152921426223695040, 1152921470247108503),
+ Rational(1073741789, 1073741827) - Rational(3, 1073741789))
+ assert_equal(Rational(3, 1073741827),
+ Rational(1073741789, 1073741827) * Rational(3, 1073741789))
+ assert_equal(Rational(1152921429444920521, 3221225481),
+ Rational(1073741789, 1073741827) / Rational(3, 1073741789))
+ assert_equal(Rational(1152921473468333870, 3221225481),
+ Rational(1073741789, 1073741827) + Rational(1073741789, 3))
+ assert_equal(Rational(-1152921467025883136, 3221225481),
+ Rational(1073741789, 1073741827) - Rational(1073741789, 3))
+ assert_equal(Rational(1152921429444920521, 3221225481),
+ Rational(1073741789, 1073741827) * Rational(1073741789, 3))
+ assert_equal(Rational(3, 1073741827),
+ Rational(1073741789, 1073741827) / Rational(1073741789, 3))
+ assert_equal(Rational(1073741792, 1073741827),
+ Rational(1073741789, 1073741827) + Rational(3, 1073741827))
+ assert_equal(Rational(1073741786, 1073741827),
+ Rational(1073741789, 1073741827) - Rational(3, 1073741827))
+ assert_equal(Rational(3221225367, 1152921511049297929),
+ Rational(1073741789, 1073741827) * Rational(3, 1073741827))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1073741789, 1073741827) / Rational(3, 1073741827))
+ assert_equal(Rational(1152921514270523296, 3221225481),
+ Rational(1073741789, 1073741827) + Rational(1073741827, 3))
+ assert_equal(Rational(-1152921507828072562, 3221225481),
+ Rational(1073741789, 1073741827) - Rational(1073741827, 3))
+ assert_equal(Rational(1073741789, 3),
+ Rational(1073741789, 1073741827) * Rational(1073741827, 3))
+ assert_equal(Rational(3221225367, 1152921511049297929),
+ Rational(1073741789, 1073741827) / Rational(1073741827, 3))
+ assert_equal(Rational(2147483578, 1073741827),
+ Rational(1073741789, 1073741827) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(0, 1),
+ Rational(1073741789, 1073741827) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921429444920521, 1152921511049297929),
+ Rational(1073741789, 1073741827) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(1, 1),
+ Rational(1073741789, 1073741827) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(2305842940494218450, 1152921470247108503),
+ Rational(1073741789, 1073741827) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(-81604377408, 1152921470247108503),
+ Rational(1073741789, 1073741827) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(1, 1),
+ Rational(1073741789, 1073741827) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921429444920521, 1152921511049297929),
+ Rational(1073741789, 1073741827) / Rational(1073741827, 1073741789))
+ assert_equal(Rational(1073741827, 1073741789), +Rational(1073741827, 1073741789))
+ assert_equal(Rational(-1073741827, 1073741789), -Rational(1073741827, 1073741789))
+ assert_equal(Rational(2147483616, 1073741789),
+ Rational(1073741827, 1073741789) + Rational(1, 1))
+ assert_equal(Rational(38, 1073741789),
+ Rational(1073741827, 1073741789) - Rational(1, 1))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(1073741827, 1073741789) * Rational(1, 1))
+ assert_equal(Rational(1073741827, 1073741789),
+ Rational(1073741827, 1073741789) / Rational(1, 1))
+ assert_equal(Rational(3221225405, 1073741789),
+ Rational(1073741827, 1073741789) + Rational(2, 1))
+ assert_equal(Rational(-1073741751, 1073741789),
+ Rational(1073741827, 1073741789) - Rational(2, 1))
+ assert_equal(Rational(2147483654, 1073741789),
+ Rational(1073741827, 1073741789) * Rational(2, 1))
+ assert_equal(Rational(1073741827, 2147483578),
+ Rational(1073741827, 1073741789) / Rational(2, 1))
+ assert_equal(Rational(4294967194, 1073741789),
+ Rational(1073741827, 1073741789) + Rational(3, 1))
+ assert_equal(Rational(-2147483540, 1073741789),
+ Rational(1073741827, 1073741789) - Rational(3, 1))
+ assert_equal(Rational(3221225481, 1073741789),
+ Rational(1073741827, 1073741789) * Rational(3, 1))
+ assert_equal(Rational(1073741827, 3221225367),
+ Rational(1073741827, 1073741789) / Rational(3, 1))
+ assert_equal(Rational(1152921430518662348, 1073741789),
+ Rational(1073741827, 1073741789) + Rational(1073741789, 1))
+ assert_equal(Rational(-1152921428371178694, 1073741789),
+ Rational(1073741827, 1073741789) - Rational(1073741789, 1))
+ assert_equal(Rational(1073741827, 1),
+ Rational(1073741827, 1073741789) * Rational(1073741789, 1))
+ assert_equal(Rational(1073741827, 1152921429444920521),
+ Rational(1073741827, 1073741789) / Rational(1073741789, 1))
+ assert_equal(Rational(1152921471320850330, 1073741789),
+ Rational(1073741827, 1073741789) + Rational(1073741827, 1))
+ assert_equal(Rational(-1152921469173366676, 1073741789),
+ Rational(1073741827, 1073741789) - Rational(1073741827, 1))
+ assert_equal(Rational(1152921511049297929, 1073741789),
+ Rational(1073741827, 1073741789) * Rational(1073741827, 1))
+ assert_equal(Rational(1, 1073741789),
+ Rational(1073741827, 1073741789) / Rational(1073741827, 1))
+ assert_equal(Rational(5368709059, 3221225367),
+ Rational(1073741827, 1073741789) + Rational(2, 3))
+ assert_equal(Rational(1073741903, 3221225367),
+ Rational(1073741827, 1073741789) - Rational(2, 3))
+ assert_equal(Rational(2147483654, 3221225367),
+ Rational(1073741827, 1073741789) * Rational(2, 3))
+ assert_equal(Rational(3221225481, 2147483578),
+ Rational(1073741827, 1073741789) / Rational(2, 3))
+ assert_equal(Rational(5368709021, 2147483578),
+ Rational(1073741827, 1073741789) + Rational(3, 2))
+ assert_equal(Rational(-1073741713, 2147483578),
+ Rational(1073741827, 1073741789) - Rational(3, 2))
+ assert_equal(Rational(3221225481, 2147483578),
+ Rational(1073741827, 1073741789) * Rational(3, 2))
+ assert_equal(Rational(2147483654, 3221225367),
+ Rational(1073741827, 1073741789) / Rational(3, 2))
+ assert_equal(Rational(1073741830, 1073741789),
+ Rational(1073741827, 1073741789) + Rational(3, 1073741789))
+ assert_equal(Rational(1073741824, 1073741789),
+ Rational(1073741827, 1073741789) - Rational(3, 1073741789))
+ assert_equal(Rational(3221225481, 1152921429444920521),
+ Rational(1073741827, 1073741789) * Rational(3, 1073741789))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1073741827, 1073741789) / Rational(3, 1073741789))
+ assert_equal(Rational(1152921432666146002, 3221225367),
+ Rational(1073741827, 1073741789) + Rational(1073741789, 3))
+ assert_equal(Rational(-1152921426223695040, 3221225367),
+ Rational(1073741827, 1073741789) - Rational(1073741789, 3))
+ assert_equal(Rational(1073741827, 3),
+ Rational(1073741827, 1073741789) * Rational(1073741789, 3))
+ assert_equal(Rational(3221225481, 1152921429444920521),
+ Rational(1073741827, 1073741789) / Rational(1073741789, 3))
+ assert_equal(Rational(1152921514270523296, 1152921470247108503),
+ Rational(1073741827, 1073741789) + Rational(3, 1073741827))
+ assert_equal(Rational(1152921507828072562, 1152921470247108503),
+ Rational(1073741827, 1073741789) - Rational(3, 1073741827))
+ assert_equal(Rational(3, 1073741789),
+ Rational(1073741827, 1073741789) * Rational(3, 1073741827))
+ assert_equal(Rational(1152921511049297929, 3221225367),
+ Rational(1073741827, 1073741789) / Rational(3, 1073741827))
+ assert_equal(Rational(1152921473468333984, 3221225367),
+ Rational(1073741827, 1073741789) + Rational(1073741827, 3))
+ assert_equal(Rational(-1152921467025883022, 3221225367),
+ Rational(1073741827, 1073741789) - Rational(1073741827, 3))
+ assert_equal(Rational(1152921511049297929, 3221225367),
+ Rational(1073741827, 1073741789) * Rational(1073741827, 3))
+ assert_equal(Rational(3, 1073741789),
+ Rational(1073741827, 1073741789) / Rational(1073741827, 3))
+ assert_equal(Rational(2305842940494218450, 1152921470247108503),
+ Rational(1073741827, 1073741789) + Rational(1073741789, 1073741827))
+ assert_equal(Rational(81604377408, 1152921470247108503),
+ Rational(1073741827, 1073741789) - Rational(1073741789, 1073741827))
+ assert_equal(Rational(1, 1),
+ Rational(1073741827, 1073741789) * Rational(1073741789, 1073741827))
+ assert_equal(Rational(1152921511049297929, 1152921429444920521),
+ Rational(1073741827, 1073741789) / Rational(1073741789, 1073741827))
+ assert_equal(Rational(2147483654, 1073741789),
+ Rational(1073741827, 1073741789) + Rational(1073741827, 1073741789))
+ assert_equal(Rational(0, 1),
+ Rational(1073741827, 1073741789) - Rational(1073741827, 1073741789))
+ assert_equal(Rational(1152921511049297929, 1152921429444920521),
+ Rational(1073741827, 1073741789) * Rational(1073741827, 1073741789))
+ assert_equal(Rational(1, 1),
+ Rational(1073741827, 1073741789) / Rational(1073741827, 1073741789))
+ end
+
+end
diff --git a/jni/ruby/test/ruby/test_readpartial.rb b/jni/ruby/test/ruby/test_readpartial.rb
new file mode 100644
index 0000000..7f2ec65
--- /dev/null
+++ b/jni/ruby/test/ruby/test_readpartial.rb
@@ -0,0 +1,72 @@
+require 'test/unit'
+require 'timeout'
+require 'fcntl'
+
+class TestReadPartial < Test::Unit::TestCase
+ def make_pipe
+ r, w = IO.pipe
+ r.binmode
+ w.binmode
+ begin
+ yield r, w
+ ensure
+ r.close unless r.closed?
+ w.close unless w.closed?
+ end
+ end
+
+ def pipe
+ make_pipe {|r, w|
+ yield r, w
+ }
+ return unless defined?(Fcntl::F_SETFL)
+ return unless defined?(Fcntl::F_GETFL)
+ return unless defined?(Fcntl::O_NONBLOCK)
+ make_pipe {|r, w|
+ r.fcntl(Fcntl::F_SETFL, r.fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
+ yield r, w
+ }
+ end
+
+ def test_length_zero
+ pipe {|r, w|
+ assert_equal('', r.readpartial(0))
+ }
+ end
+
+ def test_closed_pipe
+ pipe {|r, w|
+ w << 'abc'
+ w.close
+ assert_equal('ab', r.readpartial(2))
+ assert_equal('c', r.readpartial(2))
+ assert_raise(EOFError) { r.readpartial(2) }
+ assert_raise(EOFError) { r.readpartial(2) }
+ }
+ end
+
+ def test_open_pipe
+ pipe {|r, w|
+ w << 'abc'
+ assert_equal('ab', r.readpartial(2))
+ assert_equal('c', r.readpartial(2))
+ assert_raise(Timeout::Error) {
+ timeout(0.1) { r.readpartial(2) }
+ }
+ }
+ end
+
+ def test_with_stdio
+ pipe {|r, w|
+ w << "abc\ndef\n"
+ assert_equal("abc\n", r.gets)
+ w << "ghi\n"
+ assert_equal("de", r.readpartial(2))
+ assert_equal("f\n", r.readpartial(4096))
+ assert_equal("ghi\n", r.readpartial(4096))
+ assert_raise(Timeout::Error) {
+ timeout(0.1) { r.readpartial(2) }
+ }
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_refinement.rb b/jni/ruby/test/ruby/test_refinement.rb
new file mode 100644
index 0000000..557dc38
--- /dev/null
+++ b/jni/ruby/test/ruby/test_refinement.rb
@@ -0,0 +1,1450 @@
+require 'test/unit'
+
+# to supress warnings for future calls of Module#refine
+EnvUtil.suppress_warning do
+ Module.new {
+ refine(Object) {}
+ }
+end
+
+class TestRefinement < Test::Unit::TestCase
+ class Foo
+ def x
+ return "Foo#x"
+ end
+
+ def y
+ return "Foo#y"
+ end
+
+ def a
+ return "Foo#a"
+ end
+
+ def call_x
+ return x
+ end
+ end
+
+ module FooExt
+ refine Foo do
+ def x
+ return "FooExt#x"
+ end
+
+ def y
+ return "FooExt#y " + super
+ end
+
+ def z
+ return "FooExt#z"
+ end
+
+ def a
+ return "FooExt#a"
+ end
+ end
+ end
+
+ module FooExt2
+ refine Foo do
+ def x
+ return "FooExt2#x"
+ end
+
+ def y
+ return "FooExt2#y " + super
+ end
+
+ def z
+ return "FooExt2#z"
+ end
+ end
+ end
+
+ class FooSub < Foo
+ def x
+ return "FooSub#x"
+ end
+
+ def y
+ return "FooSub#y " + super
+ end
+ end
+
+ eval <<-EOF, TOPLEVEL_BINDING
+ using TestRefinement::FooExt
+
+ class TestRefinement::FooExtClient
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+
+ def self.invoke_z_on(foo)
+ return foo.z
+ end
+
+ def self.send_z_on(foo)
+ return foo.send(:z)
+ end
+
+ def self.method_z(foo)
+ return foo.method(:z)
+ end
+
+ def self.invoke_call_x_on(foo)
+ return foo.call_x
+ end
+ end
+ EOF
+
+ eval <<-EOF, TOPLEVEL_BINDING
+ using TestRefinement::FooExt
+ using TestRefinement::FooExt2
+
+ class TestRefinement::FooExtClient2
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+
+ def self.invoke_a_on(foo)
+ return foo.a
+ end
+ end
+ EOF
+
+ def test_override
+ foo = Foo.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("FooExt#x", FooExtClient.invoke_x_on(foo))
+ assert_equal("Foo#x", foo.x)
+ end
+
+ def test_super
+ foo = Foo.new
+ assert_equal("Foo#y", foo.y)
+ assert_equal("FooExt#y Foo#y", FooExtClient.invoke_y_on(foo))
+ assert_equal("Foo#y", foo.y)
+ end
+
+ def test_super_not_chained
+ foo = Foo.new
+ assert_equal("Foo#y", foo.y)
+ assert_equal("FooExt2#y Foo#y", FooExtClient2.invoke_y_on(foo))
+ assert_equal("Foo#y", foo.y)
+ end
+
+ def test_using_same_class_refinements
+ foo = Foo.new
+ assert_equal("Foo#a", foo.a)
+ assert_equal("FooExt#a", FooExtClient2.invoke_a_on(foo))
+ assert_equal("Foo#a", foo.a)
+ end
+
+ def test_new_method
+ foo = Foo.new
+ assert_raise(NoMethodError) { foo.z }
+ assert_equal("FooExt#z", FooExtClient.invoke_z_on(foo))
+ assert_raise(NoMethodError) { foo.z }
+ end
+
+ module RespondTo
+ class Super
+ def foo
+ end
+ end
+
+ class Sub < Super
+ end
+
+ module M
+ refine Sub do
+ def foo
+ end
+ end
+ end
+ end
+
+ def test_send_should_not_use_refinements
+ foo = Foo.new
+ assert_raise(NoMethodError) { foo.send(:z) }
+ assert_raise(NoMethodError) { FooExtClient.send_z_on(foo) }
+ assert_raise(NoMethodError) { foo.send(:z) }
+
+ assert_equal(true, RespondTo::Sub.new.respond_to?(:foo))
+ end
+
+ def test_method_should_not_use_refinements
+ foo = Foo.new
+ assert_raise(NameError) { foo.method(:z) }
+ assert_raise(NameError) { FooExtClient.method_z(foo) }
+ assert_raise(NameError) { foo.method(:z) }
+ end
+
+ def test_no_local_rebinding
+ foo = Foo.new
+ assert_equal("Foo#x", foo.call_x)
+ assert_equal("Foo#x", FooExtClient.invoke_call_x_on(foo))
+ assert_equal("Foo#x", foo.call_x)
+ end
+
+ def test_subclass_is_prior
+ sub = FooSub.new
+ assert_equal("FooSub#x", sub.x)
+ assert_equal("FooSub#x", FooExtClient.invoke_x_on(sub))
+ assert_equal("FooSub#x", sub.x)
+ end
+
+ def test_super_in_subclass
+ sub = FooSub.new
+ assert_equal("FooSub#y Foo#y", sub.y)
+ # not "FooSub#y FooExt#y Foo#y"
+ assert_equal("FooSub#y Foo#y", FooExtClient.invoke_y_on(sub))
+ assert_equal("FooSub#y Foo#y", sub.y)
+ end
+
+ def test_new_method_on_subclass
+ sub = FooSub.new
+ assert_raise(NoMethodError) { sub.z }
+ assert_equal("FooExt#z", FooExtClient.invoke_z_on(sub))
+ assert_raise(NoMethodError) { sub.z }
+ end
+
+ def test_module_eval
+ foo = Foo.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#x", FooExt.module_eval { foo.x })
+ assert_equal("Foo#x", FooExt.module_eval("foo.x"))
+ assert_equal("Foo#x", foo.x)
+ end
+
+ def test_instance_eval_without_refinement
+ foo = Foo.new
+ ext_client = FooExtClient.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#x", ext_client.instance_eval { foo.x })
+ assert_equal("Foo#x", foo.x)
+ end
+
+ module FixnumSlashExt
+ refine Fixnum do
+ def /(other) quo(other) end
+ end
+ end
+
+ def test_override_builtin_method
+ assert_equal(0, 1 / 2)
+ assert_equal(Rational(1, 2), eval_using(FixnumSlashExt, "1 / 2"))
+ assert_equal(0, 1 / 2)
+ end
+
+ module FixnumPlusExt
+ refine Fixnum do
+ def self.method_added(*args); end
+ def +(other) "overridden" end
+ end
+ end
+
+ def test_override_builtin_method_with_method_added
+ assert_equal(3, 1 + 2)
+ assert_equal("overridden", eval_using(FixnumPlusExt, "1 + 2"))
+ assert_equal(3, 1 + 2)
+ end
+
+ def test_return_value_of_refine
+ mod = nil
+ result = nil
+ Module.new {
+ result = refine(Object) {
+ mod = self
+ }
+ }
+ assert_equal mod, result
+ end
+
+ module RefineSameClass
+ REFINEMENT1 = refine(Fixnum) {
+ def foo; return "foo" end
+ }
+ REFINEMENT2 = refine(Fixnum) {
+ def bar; return "bar" end
+ }
+ REFINEMENT3 = refine(String) {
+ def baz; return "baz" end
+ }
+ end
+
+ def test_refine_same_class_twice
+ assert_equal("foo", eval_using(RefineSameClass, "1.foo"))
+ assert_equal("bar", eval_using(RefineSameClass, "1.bar"))
+ assert_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT2)
+ assert_not_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT3)
+ end
+
+ module FixnumFooExt
+ refine Fixnum do
+ def foo; "foo"; end
+ end
+ end
+
+ def test_respond_to_should_not_use_refinements
+ assert_equal(false, 1.respond_to?(:foo))
+ assert_equal(false, eval_using(FixnumFooExt, "1.respond_to?(:foo)"))
+ end
+
+ module StringCmpExt
+ refine String do
+ def <=>(other) return 0 end
+ end
+ end
+
+ module ArrayEachExt
+ refine Array do
+ def each
+ super do |i|
+ yield 2 * i
+ end
+ end
+ end
+ end
+
+ def test_builtin_method_no_local_rebinding
+ assert_equal(false, eval_using(StringCmpExt, '"1" >= "2"'))
+ assert_equal(1, eval_using(ArrayEachExt, "[1, 2, 3].min"))
+ end
+
+ module RefinePrependedClass
+ module M1
+ def foo
+ super << :m1
+ end
+ end
+
+ class C
+ prepend M1
+
+ def foo
+ [:c]
+ end
+ end
+
+ module M2
+ refine C do
+ def foo
+ super << :m2
+ end
+ end
+ end
+ end
+
+ def test_refine_prepended_class
+ x = eval_using(RefinePrependedClass::M2,
+ "TestRefinement::RefinePrependedClass::C.new.foo")
+ assert_equal([:c, :m1, :m2], x)
+ end
+
+ def test_refine_module
+ m1 = Module.new
+ assert_raise(TypeError) do
+ Module.new {
+ refine m1 do
+ def foo
+ :m2
+ end
+ end
+ }
+ end
+ end
+
+ def test_refine_neither_class_nor_module
+ assert_raise(TypeError) do
+ Module.new {
+ refine Object.new do
+ end
+ }
+ end
+ assert_raise(TypeError) do
+ Module.new {
+ refine 123 do
+ end
+ }
+ end
+ assert_raise(TypeError) do
+ Module.new {
+ refine "foo" do
+ end
+ }
+ end
+ end
+
+ def test_refine_in_class
+ assert_raise(NoMethodError) do
+ Class.new {
+ refine Fixnum do
+ def foo
+ "c"
+ end
+ end
+ }
+ end
+ end
+
+ def test_main_using
+ assert_in_out_err([], <<-INPUT, %w(:C :M), [])
+ class C
+ def foo
+ :C
+ end
+ end
+
+ module M
+ refine C do
+ def foo
+ :M
+ end
+ end
+ end
+
+ c = C.new
+ p c.foo
+ using M
+ p c.foo
+ INPUT
+ end
+
+ def test_main_using_is_private
+ assert_raise(NoMethodError) do
+ eval("self.using Module.new", TOPLEVEL_BINDING)
+ end
+ end
+
+ def test_no_kernel_using
+ assert_raise(NoMethodError) do
+ using Module.new
+ end
+ end
+
+ class UsingClass
+ end
+
+ def test_module_using_class
+ assert_raise(TypeError) do
+ eval("using TestRefinement::UsingClass", TOPLEVEL_BINDING)
+ end
+ end
+
+ def test_refine_without_block
+ c1 = Class.new
+ assert_raise_with_message(ArgumentError, "no block given") {
+ Module.new do
+ refine c1
+ end
+ }
+ end
+
+ module Inspect
+ module M
+ Fixnum = refine(Fixnum) {}
+ end
+ end
+
+ def test_inspect
+ assert_equal("#<refinement:Fixnum@TestRefinement::Inspect::M>",
+ Inspect::M::Fixnum.inspect)
+ end
+
+ def test_using_method_cache
+ assert_in_out_err([], <<-INPUT, %w(:M1 :M2), [])
+ class C
+ def foo
+ "original"
+ end
+ end
+
+ module M1
+ refine C do
+ def foo
+ :M1
+ end
+ end
+ end
+
+ module M2
+ refine C do
+ def foo
+ :M2
+ end
+ end
+ end
+
+ c = C.new
+ using M1
+ p c.foo
+ using M2
+ p c.foo
+ INPUT
+ end
+
+ module RedefineRefinedMethod
+ class C
+ def foo
+ "original"
+ end
+ end
+
+ module M
+ refine C do
+ def foo
+ "refined"
+ end
+ end
+ end
+
+ class C
+ def foo
+ "redefined"
+ end
+ end
+ end
+
+ def test_redefine_refined_method
+ x = eval_using(RedefineRefinedMethod::M,
+ "TestRefinement::RedefineRefinedMethod::C.new.foo")
+ assert_equal("refined", x)
+ end
+
+ module StringExt
+ refine String do
+ def foo
+ "foo"
+ end
+ end
+ end
+
+ module RefineScoping
+ refine String do
+ def foo
+ "foo"
+ end
+
+ def RefineScoping.call_in_refine_block
+ "".foo
+ end
+ end
+
+ def self.call_outside_refine_block
+ "".foo
+ end
+ end
+
+ def test_refine_scoping
+ assert_equal("foo", RefineScoping.call_in_refine_block)
+ assert_raise(NoMethodError) do
+ RefineScoping.call_outside_refine_block
+ end
+ end
+
+ module StringRecursiveLength
+ refine String do
+ def recursive_length
+ if empty?
+ 0
+ else
+ self[1..-1].recursive_length + 1
+ end
+ end
+ end
+ end
+
+ def test_refine_recursion
+ x = eval_using(StringRecursiveLength, "'foo'.recursive_length")
+ assert_equal(3, x)
+ end
+
+ module ToJSON
+ refine Integer do
+ def to_json; to_s; end
+ end
+
+ refine Array do
+ def to_json; "[" + map { |i| i.to_json }.join(",") + "]" end
+ end
+
+ refine Hash do
+ def to_json; "{" + map { |k, v| k.to_s.dump + ":" + v.to_json }.join(",") + "}" end
+ end
+ end
+
+ def test_refine_mutual_recursion
+ x = eval_using(ToJSON, "[{1=>2}, {3=>4}].to_json")
+ assert_equal('[{"1":2},{"3":4}]', x)
+ end
+
+ def test_refine_with_proc
+ assert_raise(ArgumentError) do
+ Module.new {
+ refine(String, &Proc.new {})
+ }
+ end
+ end
+
+ def test_using_in_module
+ assert_raise(RuntimeError) do
+ eval(<<-EOF, TOPLEVEL_BINDING)
+ $main = self
+ module M
+ end
+ module M2
+ $main.send(:using, M)
+ end
+ EOF
+ end
+ end
+
+ def test_using_in_method
+ assert_raise(RuntimeError) do
+ eval(<<-EOF, TOPLEVEL_BINDING)
+ $main = self
+ module M
+ end
+ def call_using_in_method
+ $main.send(:using, M)
+ end
+ call_using_in_method
+ EOF
+ end
+ end
+
+ module IncludeIntoRefinement
+ class C
+ def bar
+ return "C#bar"
+ end
+
+ def baz
+ return "C#baz"
+ end
+ end
+
+ module Mixin
+ def foo
+ return "Mixin#foo"
+ end
+
+ def bar
+ return super << " Mixin#bar"
+ end
+
+ def baz
+ return super << " Mixin#baz"
+ end
+ end
+
+ module M
+ refine C do
+ include Mixin
+
+ def baz
+ return super << " M#baz"
+ end
+ end
+ end
+ end
+
+ eval <<-EOF, TOPLEVEL_BINDING
+ using TestRefinement::IncludeIntoRefinement::M
+
+ module TestRefinement::IncludeIntoRefinement::User
+ def self.invoke_foo_on(x)
+ x.foo
+ end
+
+ def self.invoke_bar_on(x)
+ x.bar
+ end
+
+ def self.invoke_baz_on(x)
+ x.baz
+ end
+ end
+ EOF
+
+ def test_include_into_refinement
+ x = IncludeIntoRefinement::C.new
+ assert_equal("Mixin#foo", IncludeIntoRefinement::User.invoke_foo_on(x))
+ assert_equal("C#bar Mixin#bar",
+ IncludeIntoRefinement::User.invoke_bar_on(x))
+ assert_equal("C#baz Mixin#baz M#baz",
+ IncludeIntoRefinement::User.invoke_baz_on(x))
+ end
+
+ module PrependIntoRefinement
+ class C
+ def bar
+ return "C#bar"
+ end
+
+ def baz
+ return "C#baz"
+ end
+ end
+
+ module Mixin
+ def foo
+ return "Mixin#foo"
+ end
+
+ def bar
+ return super << " Mixin#bar"
+ end
+
+ def baz
+ return super << " Mixin#baz"
+ end
+ end
+
+ module M
+ refine C do
+ prepend Mixin
+
+ def baz
+ return super << " M#baz"
+ end
+ end
+ end
+ end
+
+ eval <<-EOF, TOPLEVEL_BINDING
+ using TestRefinement::PrependIntoRefinement::M
+
+ module TestRefinement::PrependIntoRefinement::User
+ def self.invoke_foo_on(x)
+ x.foo
+ end
+
+ def self.invoke_bar_on(x)
+ x.bar
+ end
+
+ def self.invoke_baz_on(x)
+ x.baz
+ end
+ end
+ EOF
+
+ def test_prepend_into_refinement
+ x = PrependIntoRefinement::C.new
+ assert_equal("Mixin#foo", PrependIntoRefinement::User.invoke_foo_on(x))
+ assert_equal("C#bar Mixin#bar",
+ PrependIntoRefinement::User.invoke_bar_on(x))
+ assert_equal("C#baz M#baz Mixin#baz",
+ PrependIntoRefinement::User.invoke_baz_on(x))
+ end
+
+ module PrependAfterRefine
+ class C
+ def foo
+ "original"
+ end
+ end
+
+ module M
+ refine C do
+ def foo
+ "refined"
+ end
+
+ def bar
+ "refined"
+ end
+ end
+ end
+
+ module Mixin
+ def foo
+ "mixin"
+ end
+
+ def bar
+ "mixin"
+ end
+ end
+
+ class C
+ prepend Mixin
+ end
+ end
+
+ def test_prepend_after_refine
+ x = eval_using(PrependAfterRefine::M,
+ "TestRefinement::PrependAfterRefine::C.new.foo")
+ assert_equal("refined", x)
+ assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.foo)
+ y = eval_using(PrependAfterRefine::M,
+ "TestRefinement::PrependAfterRefine::C.new.bar")
+ assert_equal("refined", y)
+ assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.bar)
+ end
+
+ module SuperInBlock
+ class C
+ def foo(*args)
+ [:foo, *args]
+ end
+ end
+
+ module R
+ refine C do
+ def foo(*args)
+ tap do
+ return super(:ref, *args)
+ end
+ end
+ end
+ end
+ end
+
+ def test_super_in_block
+ bug7925 = '[ruby-core:52750] [Bug #7925]'
+ x = eval_using(SuperInBlock::R,
+ "TestRefinement:: SuperInBlock::C.new.foo(#{bug7925.dump})")
+ assert_equal([:foo, :ref, bug7925], x, bug7925)
+ end
+
+ module ModuleUsing
+ using FooExt
+
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+
+ def self.invoke_z_on(foo)
+ return foo.z
+ end
+
+ def self.send_z_on(foo)
+ return foo.send(:z)
+ end
+
+ def self.method_z(foo)
+ return foo.method(:z)
+ end
+
+ def self.invoke_call_x_on(foo)
+ return foo.call_x
+ end
+ end
+
+ def test_module_using
+ foo = Foo.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#y", foo.y)
+ assert_raise(NoMethodError) { foo.z }
+ assert_equal("FooExt#x", ModuleUsing.invoke_x_on(foo))
+ assert_equal("FooExt#y Foo#y", ModuleUsing.invoke_y_on(foo))
+ assert_equal("FooExt#z", ModuleUsing.invoke_z_on(foo))
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#y", foo.y)
+ assert_raise(NoMethodError) { foo.z }
+ end
+
+ def test_module_using_in_method
+ assert_raise(RuntimeError) do
+ Module.new.send(:using, FooExt)
+ end
+ end
+
+ def test_module_using_invalid_self
+ assert_raise(RuntimeError) do
+ eval <<-EOF, TOPLEVEL_BINDING
+ module TestRefinement::TestModuleUsingInvalidSelf
+ Module.new.send(:using, TestRefinement::FooExt)
+ end
+ EOF
+ end
+ end
+
+ class Bar
+ end
+
+ module BarExt
+ refine Bar do
+ def x
+ return "BarExt#x"
+ end
+ end
+ end
+
+ module FooBarExt
+ include FooExt
+ include BarExt
+ end
+
+ module FooBarExtClient
+ using FooBarExt
+
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+ end
+
+ def test_module_inclusion
+ foo = Foo.new
+ assert_equal("FooExt#x", FooBarExtClient.invoke_x_on(foo))
+ bar = Bar.new
+ assert_equal("BarExt#x", FooBarExtClient.invoke_x_on(bar))
+ end
+
+ module FooFoo2Ext
+ include FooExt
+ include FooExt2
+ end
+
+ module FooFoo2ExtClient
+ using FooFoo2Ext
+
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+ end
+
+ def test_module_inclusion2
+ foo = Foo.new
+ assert_equal("FooExt2#x", FooFoo2ExtClient.invoke_x_on(foo))
+ assert_equal("FooExt2#y Foo#y", FooFoo2ExtClient.invoke_y_on(foo))
+ end
+
+ def test_eval_scoping
+ assert_in_out_err([], <<-INPUT, ["HELLO WORLD", "dlrow olleh", "HELLO WORLD"], [])
+ module M
+ refine String do
+ def upcase
+ reverse
+ end
+ end
+ end
+
+ puts "hello world".upcase
+ puts eval(%{using M; "hello world".upcase})
+ puts "hello world".upcase
+ INPUT
+ end
+
+ def test_eval_with_binding_scoping
+ assert_in_out_err([], <<-INPUT, ["HELLO WORLD", "dlrow olleh", "HELLO WORLD"], [])
+ module M
+ refine String do
+ def upcase
+ reverse
+ end
+ end
+ end
+
+ puts "hello world".upcase
+ puts eval(%{using M; "hello world".upcase}, TOPLEVEL_BINDING)
+ puts eval(%{"hello world".upcase}, TOPLEVEL_BINDING)
+ INPUT
+ end
+
+ def test_case_dispatch_is_aware_of_refinements
+ assert_in_out_err([], <<-RUBY, ["refinement used"], [])
+ module RefineSymbol
+ refine Symbol do
+ def ===(other)
+ true
+ end
+ end
+ end
+
+ using RefineSymbol
+
+ case :a
+ when :b
+ puts "refinement used"
+ else
+ puts "refinement not used"
+ end
+ RUBY
+ end
+
+ def test_refine_after_using
+ assert_separately([], <<-"end;")
+ bug8880 = '[ruby-core:57079] [Bug #8880]'
+ module Test
+ refine(String) do
+ end
+ end
+ using Test
+ def t
+ 'Refinements are broken!'.chop!
+ end
+ t
+ module Test
+ refine(String) do
+ def chop!
+ self.sub!(/broken/, 'fine')
+ end
+ end
+ end
+ assert_equal('Refinements are fine!', t, bug8880)
+ end;
+ end
+
+ def test_instance_methods
+ bug8881 = '[ruby-core:57080] [Bug #8881]'
+ assert_not_include(Foo.instance_methods(false), :z, bug8881)
+ assert_not_include(FooSub.instance_methods(true), :z, bug8881)
+ end
+
+ def test_method_defined
+ assert_not_send([Foo, :method_defined?, :z])
+ assert_not_send([FooSub, :method_defined?, :z])
+ end
+
+ def test_undef_refined_method
+ bug8966 = '[ruby-core:57466] [Bug #8966]'
+
+ assert_in_out_err([], <<-INPUT, ["NameError"], [], bug8966)
+ module Foo
+ refine Object do
+ def foo
+ puts "foo"
+ end
+ end
+ end
+
+ using Foo
+
+ class Object
+ begin
+ undef foo
+ rescue Exception => e
+ p e.class
+ end
+ end
+ INPUT
+
+ assert_in_out_err([], <<-INPUT, ["NameError"], [], bug8966)
+ module Foo
+ refine Object do
+ def foo
+ puts "foo"
+ end
+ end
+ end
+
+ # without `using Foo'
+
+ class Object
+ begin
+ undef foo
+ rescue Exception => e
+ p e.class
+ end
+ end
+ INPUT
+ end
+
+ def test_refine_undefed_method_and_call
+ assert_in_out_err([], <<-INPUT, ["NoMethodError"], [])
+ class Foo
+ def foo
+ end
+
+ undef foo
+ end
+
+ module FooExt
+ refine Foo do
+ def foo
+ end
+ end
+ end
+
+ begin
+ Foo.new.foo
+ rescue => e
+ p e.class
+ end
+ INPUT
+ end
+
+ def test_refine_undefed_method_and_send
+ assert_in_out_err([], <<-INPUT, ["NoMethodError"], [])
+ class Foo
+ def foo
+ end
+
+ undef foo
+ end
+
+ module FooExt
+ refine Foo do
+ def foo
+ end
+ end
+ end
+
+ begin
+ Foo.new.send(:foo)
+ rescue => e
+ p e.class
+ end
+ INPUT
+ end
+
+ def test_adding_private_method
+ bug9452 = '[ruby-core:60111] [Bug #9452]'
+
+ assert_in_out_err([], <<-INPUT, ["Success!", "NoMethodError"], [], bug9452)
+ module R
+ refine Object do
+ def m
+ puts "Success!"
+ end
+
+ private(:m)
+ end
+ end
+
+ using R
+
+ m
+ 42.m rescue p($!.class)
+ INPUT
+ end
+
+ def test_making_private_method_public
+ bug9452 = '[ruby-core:60111] [Bug #9452]'
+
+ assert_in_out_err([], <<-INPUT, ["Success!", "Success!"], [], bug9452)
+ class Object
+ private
+ def m
+ end
+ end
+
+ module R
+ refine Object do
+ def m
+ puts "Success!"
+ end
+ end
+ end
+
+ using R
+ m
+ 42.m
+ INPUT
+ end
+
+ def test_refine_basic_object
+ assert_separately([], <<-"end;")
+ bug10106 = '[ruby-core:64166] [Bug #10106]'
+ module RefinementBug
+ refine BasicObject do
+ def foo
+ 1
+ end
+ end
+ end
+
+ assert_raise(NoMethodError, bug10106) {Object.new.foo}
+ end;
+
+ assert_separately([], <<-"end;")
+ bug10707 = '[ruby-core:67389] [Bug #10707]'
+ module RefinementBug
+ refine BasicObject do
+ def foo
+ end
+ end
+ end
+
+ assert(methods, bug10707)
+ assert_raise(NameError, bug10707) {method(:foo)}
+ end;
+ end
+
+ def test_change_refined_new_method_visibility
+ assert_separately([], <<-"end;")
+ bug10706 = '[ruby-core:67387] [Bug #10706]'
+ module RefinementBug
+ refine Object do
+ def foo
+ end
+ end
+ end
+
+ assert_raise(NameError, bug10706) {private(:foo)}
+ end;
+ end
+
+ def test_alias_refined_method
+ assert_separately([], <<-"end;")
+ bug10731 = '[ruby-core:67523] [Bug #10731]'
+
+ class C
+ end
+
+ module RefinementBug
+ refine C do
+ def foo
+ end
+
+ def bar
+ end
+ end
+ end
+
+ assert_raise(NameError, bug10731) do
+ class C
+ alias foo bar
+ end
+ end
+ end;
+ end
+
+ def test_singleton_method_should_not_use_refinements
+ assert_separately([], <<-"end;")
+ bug10744 = '[ruby-core:67603] [Bug #10744]'
+
+ class C
+ end
+
+ module RefinementBug
+ refine C.singleton_class do
+ def foo
+ end
+ end
+ end
+
+ assert_raise(NameError, bug10744) { C.singleton_method(:foo) }
+ end;
+ end
+
+ def test_refined_method_defined
+ assert_separately([], <<-"end;")
+ bug10753 = '[ruby-core:67656] [Bug #10753]'
+
+ c = Class.new do
+ def refined_public; end
+ def refined_protected; end
+ def refined_private; end
+
+ public :refined_public
+ protected :refined_protected
+ private :refined_private
+ end
+
+ m = Module.new do
+ refine(c) do
+ def refined_public; end
+ def refined_protected; end
+ def refined_private; end
+
+ public :refined_public
+ protected :refined_protected
+ private :refined_private
+ end
+ end
+
+ using m
+
+ assert_equal(true, c.public_method_defined?(:refined_public), bug10753)
+ assert_equal(false, c.public_method_defined?(:refined_protected), bug10753)
+ assert_equal(false, c.public_method_defined?(:refined_private), bug10753)
+
+ assert_equal(false, c.protected_method_defined?(:refined_public), bug10753)
+ assert_equal(true, c.protected_method_defined?(:refined_protected), bug10753)
+ assert_equal(false, c.protected_method_defined?(:refined_private), bug10753)
+
+ assert_equal(false, c.private_method_defined?(:refined_public), bug10753)
+ assert_equal(false, c.private_method_defined?(:refined_protected), bug10753)
+ assert_equal(true, c.private_method_defined?(:refined_private), bug10753)
+ end;
+ end
+
+ def test_undefined_refined_method_defined
+ assert_separately([], <<-"end;")
+ bug10753 = '[ruby-core:67656] [Bug #10753]'
+
+ c = Class.new
+
+ m = Module.new do
+ refine(c) do
+ def undefined_refined_public; end
+ def undefined_refined_protected; end
+ def undefined_refined_private; end
+ public :undefined_refined_public
+ protected :undefined_refined_protected
+ private :undefined_refined_private
+ end
+ end
+
+ using m
+
+ assert_equal(false, c.public_method_defined?(:undefined_refined_public), bug10753)
+ assert_equal(false, c.public_method_defined?(:undefined_refined_protected), bug10753)
+ assert_equal(false, c.public_method_defined?(:undefined_refined_private), bug10753)
+
+ assert_equal(false, c.protected_method_defined?(:undefined_refined_public), bug10753)
+ assert_equal(false, c.protected_method_defined?(:undefined_refined_protected), bug10753)
+ assert_equal(false, c.protected_method_defined?(:undefined_refined_private), bug10753)
+
+ assert_equal(false, c.private_method_defined?(:undefined_refined_public), bug10753)
+ assert_equal(false, c.private_method_defined?(:undefined_refined_protected), bug10753)
+ assert_equal(false, c.private_method_defined?(:undefined_refined_private), bug10753)
+ end;
+ end
+
+ def test_remove_refined_method
+ assert_separately([], <<-"end;")
+ bug10765 = '[ruby-core:67722] [Bug #10765]'
+
+ class C
+ def foo
+ "C#foo"
+ end
+ end
+
+ module RefinementBug
+ refine C do
+ def foo
+ "RefinementBug#foo"
+ end
+ end
+ end
+
+ using RefinementBug
+
+ class C
+ remove_method :foo
+ end
+
+ assert_equal("RefinementBug#foo", C.new.foo, bug10765)
+ end;
+ end
+
+ def test_remove_undefined_refined_method
+ assert_separately([], <<-"end;")
+ bug10765 = '[ruby-core:67722] [Bug #10765]'
+
+ class C
+ end
+
+ module RefinementBug
+ refine C do
+ def foo
+ end
+ end
+ end
+
+ using RefinementBug
+
+ assert_raise(NameError, bug10765) {
+ class C
+ remove_method :foo
+ end
+ }
+ end;
+ end
+
+ module NotIncludeSuperclassMethod
+ class X
+ def foo
+ end
+ end
+
+ class Y < X
+ end
+
+ module Bar
+ refine Y do
+ def foo
+ end
+ end
+ end
+ end
+
+ def test_instance_methods_not_include_superclass_method
+ bug10826 = '[ruby-dev:48854] [Bug #10826]'
+ assert_not_include(NotIncludeSuperclassMethod::Y.instance_methods(false),
+ :foo, bug10826)
+ assert_include(NotIncludeSuperclassMethod::Y.instance_methods(true),
+ :foo, bug10826)
+ end
+
+ def test_call_refined_method_in_duplicate_module
+ bug10885 = '[ruby-dev:48878]'
+ assert_in_out_err([], <<-INPUT, [], [], bug10885)
+ module M
+ refine Object do
+ def raise
+ # do nothing
+ end
+ end
+
+ class << self
+ using M
+ def m0
+ raise
+ end
+ end
+
+ using M
+ def M.m1
+ raise
+ end
+ end
+
+ M.dup.m0
+ M.dup.m1
+ INPUT
+ end
+
+ def test_check_funcall_undefined
+ bug11117 = '[ruby-core:69064] [Bug #11117]'
+
+ x = Class.new
+ Module.new do
+ refine x do
+ def to_regexp
+ //
+ end
+ end
+ end
+
+ assert_nothing_raised(NoMethodError, bug11117) {
+ assert_nil(Regexp.try_convert(x.new))
+ }
+ end
+
+ def test_funcall_inherited
+ bug11117 = '[ruby-core:69064] [Bug #11117]'
+
+ Module.new {refine(Dir) {def to_s; end}}
+ x = Class.new(Dir).allocate
+ assert_nothing_raised(NoMethodError, bug11117) {
+ x.inspect
+ }
+ end
+
+ private
+
+ def eval_using(mod, s)
+ eval("using #{mod}; #{s}", TOPLEVEL_BINDING)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_regexp.rb b/jni/ruby/test/ruby/test_regexp.rb
new file mode 100644
index 0000000..cef21b9
--- /dev/null
+++ b/jni/ruby/test/ruby/test_regexp.rb
@@ -0,0 +1,1102 @@
+# coding: US-ASCII
+require 'test/unit'
+
+class TestRegexp < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def test_has_NOENCODING
+ assert Regexp::NOENCODING
+ re = //n
+ assert_equal Regexp::NOENCODING, re.options
+ end
+
+ def test_ruby_dev_999
+ assert_match(/(?<=a).*b/, "aab")
+ assert_match(/(?<=\u3042).*b/, "\u3042ab")
+ end
+
+ def test_ruby_core_27247
+ assert_match(/(a){2}z/, "aaz")
+ end
+
+ def test_ruby_dev_24643
+ assert_nothing_raised("[ruby-dev:24643]") {
+ /(?:(?:[a]*[a])?b)*a*$/ =~ "aabaaca"
+ }
+ end
+
+ def test_ruby_talk_116455
+ assert_match(/^(\w{2,}).* ([A-Za-z\xa2\xc0-\xff]{2,}?)$/n, "Hallo Welt")
+ end
+
+ def test_ruby_dev_24887
+ assert_equal("a".gsub(/a\Z/, ""), "")
+ end
+
+ def test_yoshidam_net_20041111_1
+ s = "[\xC2\xA0-\xC3\xBE]"
+ assert_match(Regexp.new(s, nil, "u"), "\xC3\xBE")
+ end
+
+ def test_yoshidam_net_20041111_2
+ assert_raise(RegexpError) do
+ s = "[\xFF-\xFF]".force_encoding("utf-8")
+ Regexp.new(s, nil, "u")
+ end
+ end
+
+ def test_ruby_dev_31309
+ assert_equal('Ruby', 'Ruby'.sub(/[^a-z]/i, '-'))
+ end
+
+ def test_assert_normal_exit
+ # moved from knownbug. It caused core.
+ Regexp.union("a", "a")
+ end
+
+ def test_to_s
+ assert_equal '(?-mix:\x00)', Regexp.new("\0").to_s
+
+ str = "abcd\u3042"
+ [:UTF_16BE, :UTF_16LE, :UTF_32BE, :UTF_32LE].each do |es|
+ enc = Encoding.const_get(es)
+ rs = Regexp.new(str.encode(enc)).to_s
+ assert_equal("(?-mix:abcd\u3042)".encode(enc), rs)
+ assert_equal(enc, rs.encoding)
+ end
+ end
+
+ def test_union
+ assert_equal :ok, begin
+ Regexp.union(
+ "a",
+ Regexp.new("\xc2\xa1".force_encoding("euc-jp")),
+ Regexp.new("\xc2\xa1".force_encoding("utf-8")))
+ :ng
+ rescue ArgumentError
+ :ok
+ end
+ end
+
+ def test_word_boundary
+ assert_match(/\u3042\b /, "\u3042 ")
+ assert_not_match(/\u3042\ba/, "\u3042a")
+ end
+
+ def test_named_capture
+ m = /&(?<foo>.*?);/.match("aaa &amp; yyy")
+ assert_equal("amp", m["foo"])
+ assert_equal("amp", m[:foo])
+ assert_equal(5, m.begin(:foo))
+ assert_equal(8, m.end(:foo))
+ assert_equal([5,8], m.offset(:foo))
+
+ assert_equal("aaa [amp] yyy",
+ "aaa &amp; yyy".sub(/&(?<foo>.*?);/, '[\k<foo>]'))
+
+ assert_equal('#<MatchData "&amp; y" foo:"amp">',
+ /&(?<foo>.*?); (y)/.match("aaa &amp; yyy").inspect)
+ assert_equal('#<MatchData "&amp; y" 1:"amp" 2:"y">',
+ /&(.*?); (y)/.match("aaa &amp; yyy").inspect)
+ assert_equal('#<MatchData "&amp; y" foo:"amp" bar:"y">',
+ /&(?<foo>.*?); (?<bar>y)/.match("aaa &amp; yyy").inspect)
+ assert_equal('#<MatchData "&amp; y" foo:"amp" foo:"y">',
+ /&(?<foo>.*?); (?<foo>y)/.match("aaa &amp; yyy").inspect)
+
+ /(?<id>[A-Za-z_]+)/ =~ "!abc"
+ assert_equal("abc", Regexp.last_match(:id))
+
+ /a/ =~ "b" # doesn't match.
+ assert_equal(nil, Regexp.last_match)
+ assert_equal(nil, Regexp.last_match(1))
+ assert_equal(nil, Regexp.last_match(:foo))
+
+ assert_equal(["foo", "bar"], /(?<foo>.)(?<bar>.)/.names)
+ assert_equal(["foo"], /(?<foo>.)(?<foo>.)/.names)
+ assert_equal([], /(.)(.)/.names)
+
+ assert_equal(["foo", "bar"], /(?<foo>.)(?<bar>.)/.match("ab").names)
+ assert_equal(["foo"], /(?<foo>.)(?<foo>.)/.match("ab").names)
+ assert_equal([], /(.)(.)/.match("ab").names)
+
+ assert_equal({"foo"=>[1], "bar"=>[2]},
+ /(?<foo>.)(?<bar>.)/.named_captures)
+ assert_equal({"foo"=>[1, 2]},
+ /(?<foo>.)(?<foo>.)/.named_captures)
+ assert_equal({}, /(.)(.)/.named_captures)
+
+ assert_equal("a[b]c", "abc".sub(/(?<x>[bc])/, "[\\k<x>]"))
+
+ assert_equal("o", "foo"[/(?<bar>o)/, "bar"])
+
+ s = "foo"
+ s[/(?<bar>o)/, "bar"] = "baz"
+ assert_equal("fbazo", s)
+ end
+
+ def test_named_capture_with_nul
+ bug9902 = '[ruby-dev:48275] [Bug #9902]'
+
+ m = /(?<a>.*)/.match("foo")
+ assert_raise(IndexError, bug9902) {m["a\0foo"]}
+ assert_raise(IndexError, bug9902) {m["a\0foo".to_sym]}
+
+ m = Regexp.new("(?<foo\0bar>.*)").match("xxx")
+ assert_raise(IndexError, bug9902) {m["foo"]}
+ assert_raise(IndexError, bug9902) {m["foo".to_sym]}
+ assert_nothing_raised(IndexError, bug9902) {
+ assert_equal("xxx", m["foo\0bar"], bug9902)
+ assert_equal("xxx", m["foo\0bar".to_sym], bug9902)
+ }
+ end
+
+ def test_named_capture_nonascii
+ bug9903 = '[ruby-dev:48278] [Bug #9903]'
+
+ key = "\xb1\xb2".force_encoding(Encoding::EUC_JP)
+ m = /(?<#{key}>.*)/.match("xxx")
+ assert_equal("xxx", m[key])
+ assert_raise(IndexError, bug9903) {m[key.dup.force_encoding(Encoding::Shift_JIS)]}
+ end
+
+ def test_assign_named_capture
+ assert_equal("a", eval('/(?<foo>.)/ =~ "a"; foo'))
+ assert_equal("a", eval('foo = 1; /(?<foo>.)/ =~ "a"; foo'))
+ assert_equal("a", eval('1.times {|foo| /(?<foo>.)/ =~ "a"; break foo }'))
+ assert_nothing_raised { eval('/(?<Foo>.)/ =~ "a"') }
+ assert_nil(eval('/(?<Foo>.)/ =~ "a"; defined? Foo'))
+ end
+
+ def test_assign_named_capture_to_reserved_word
+ /(?<nil>.)/ =~ "a"
+ assert_not_include(local_variables, :nil, "[ruby-dev:32675]")
+ end
+
+ def test_match_regexp
+ r = /./
+ m = r.match("a")
+ assert_equal(r, m.regexp)
+ re = /foo/
+ assert_equal(re, re.match("foo").regexp)
+ end
+
+ def test_source
+ bug5484 = '[ruby-core:40364]'
+ assert_equal('', //.source)
+ assert_equal('\:', /\:/.source, bug5484)
+ assert_equal(':', %r:\::.source, bug5484)
+ end
+
+ def test_source_escaped
+ expected, result = "$*+.?^|".each_char.map {|c|
+ [
+ ["\\#{c}", "\\#{c}", 1],
+ begin
+ re = eval("%r#{c}\\#{c}#{c}", nil, __FILE__, __LINE__)
+ t = eval("/\\#{c}/", nil, __FILE__, __LINE__).source
+ rescue SyntaxError => e
+ [e, t, nil]
+ else
+ [re.source, t, re =~ "a#{c}a"]
+ end
+ ]
+ }.transpose
+ assert_equal(expected, result)
+ end
+
+ def test_source_escaped_paren
+ bug7610 = '[ruby-core:51088] [Bug #7610]'
+ bug8133 = '[ruby-core:53578] [Bug #8133]'
+ [
+ ["(", ")", bug7610], ["[", "]", bug8133],
+ ["{", "}", bug8133], ["<", ">", bug8133],
+ ].each do |lparen, rparen, bug|
+ s = "\\#{lparen}a\\#{rparen}"
+ assert_equal(/#{s}/, eval("%r#{lparen}#{s}#{rparen}"), bug)
+ end
+ end
+
+ def test_source_unescaped
+ expected, result = "!\"#%&',-/:;=@_`~".each_char.map {|c|
+ [
+ ["#{c}", "\\#{c}", 1],
+ begin
+ re = eval("%r#{c}\\#{c}#{c}", nil, __FILE__, __LINE__)
+ t = eval("%r{\\#{c}}", nil, __FILE__, __LINE__).source
+ rescue SyntaxError => e
+ [e, t, nil]
+ else
+ [re.source, t, re =~ "a#{c}a"]
+ end
+ ]
+ }.transpose
+ assert_equal(expected, result)
+ end
+
+ def test_inspect
+ assert_equal('//', //.inspect)
+ assert_equal('//i', //i.inspect)
+ assert_equal('/\//i', /\//i.inspect)
+ assert_equal('/\//i', %r"#{'/'}"i.inspect)
+ assert_equal('/\/x/i', /\/x/i.inspect)
+ assert_equal('/\x00/i', /#{"\0"}/i.inspect)
+ assert_equal("/\n/i", /#{"\n"}/i.inspect)
+ s = [0xf1, 0xf2, 0xf3].pack("C*")
+ assert_equal('/\/\xF1\xF2\xF3/i', /\/#{s}/i.inspect)
+ end
+
+ def test_char_to_option
+ assert_equal("BAR", "FOOBARBAZ"[/b../i])
+ assert_equal("bar", "foobarbaz"[/ b . . /x])
+ assert_equal("bar\n", "foo\nbar\nbaz"[/b.../m])
+ assert_raise(SyntaxError) { eval('//z') }
+ end
+
+ def test_char_to_option_kcode
+ assert_equal("bar", "foobarbaz"[/b../s])
+ assert_equal("bar", "foobarbaz"[/b../e])
+ assert_equal("bar", "foobarbaz"[/b../u])
+ end
+
+ def test_to_s2
+ assert_equal('(?-mix:foo)', /(?:foo)/.to_s)
+ assert_equal('(?m-ix:foo)', /(?:foo)/m.to_s)
+ assert_equal('(?mi-x:foo)', /(?:foo)/mi.to_s)
+ assert_equal('(?mix:foo)', /(?:foo)/mix.to_s)
+ assert_equal('(?m-ix:foo)', /(?m-ix:foo)/.to_s)
+ assert_equal('(?mi-x:foo)', /(?mi-x:foo)/.to_s)
+ assert_equal('(?mix:foo)', /(?mix:foo)/.to_s)
+ assert_equal('(?mix:)', /(?mix)/.to_s)
+ assert_equal('(?-mix:(?mix:foo) )', /(?mix:foo) /.to_s)
+ end
+
+ def test_casefold_p
+ assert_equal(false, /a/.casefold?)
+ assert_equal(true, /a/i.casefold?)
+ assert_equal(false, /(?i:a)/.casefold?)
+ end
+
+ def test_options
+ assert_equal(Regexp::IGNORECASE, /a/i.options)
+ assert_equal(Regexp::EXTENDED, /a/x.options)
+ assert_equal(Regexp::MULTILINE, /a/m.options)
+ end
+
+ def test_match_init_copy
+ m = /foo/.match("foo")
+ assert_equal(/foo/, m.dup.regexp)
+ assert_raise(TypeError) do
+ m.instance_eval { initialize_copy(nil) }
+ end
+ assert_equal([0, 3], m.offset(0))
+ assert_equal(/foo/, m.dup.regexp)
+ end
+
+ def test_match_size
+ m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ assert_equal(5, m.size)
+ end
+
+ def test_match_offset_begin_end
+ m = /(?<x>b..)/.match("foobarbaz")
+ assert_equal([3, 6], m.offset("x"))
+ assert_equal(3, m.begin("x"))
+ assert_equal(6, m.end("x"))
+ assert_raise(IndexError) { m.offset("y") }
+ assert_raise(IndexError) { m.offset(2) }
+ assert_raise(IndexError) { m.begin(2) }
+ assert_raise(IndexError) { m.end(2) }
+
+ m = /(?<x>q..)?/.match("foobarbaz")
+ assert_equal([nil, nil], m.offset("x"))
+ assert_equal(nil, m.begin("x"))
+ assert_equal(nil, m.end("x"))
+
+ m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044")
+ assert_equal([1, 2], m.offset(1))
+ assert_equal([nil, nil], m.offset(2))
+ assert_equal([2, 3], m.offset(3))
+ end
+
+ def test_match_to_s
+ m = /(?<x>b..)/.match("foobarbaz")
+ assert_equal("bar", m.to_s)
+ end
+
+ def test_match_pre_post
+ m = /(?<x>b..)/.match("foobarbaz")
+ assert_equal("foo", m.pre_match)
+ assert_equal("baz", m.post_match)
+ end
+
+ def test_match_array
+ m = /(...)(...)(...)(...)?/.match("foobarbaz")
+ assert_equal(["foobarbaz", "foo", "bar", "baz", nil], m.to_a)
+ end
+
+ def test_match_captures
+ m = /(...)(...)(...)(...)?/.match("foobarbaz")
+ assert_equal(["foo", "bar", "baz", nil], m.captures)
+ end
+
+ def test_match_aref
+ m = /(...)(...)(...)(...)?/.match("foobarbaz")
+ assert_equal("foo", m[1])
+ assert_equal(["foo", "bar", "baz"], m[1..3])
+ assert_nil(m[5])
+ assert_raise(IndexError) { m[:foo] }
+ end
+
+ def test_match_values_at
+ m = /(...)(...)(...)(...)?/.match("foobarbaz")
+ assert_equal(["foo", "bar", "baz"], m.values_at(1, 2, 3))
+ end
+
+ def test_match_string
+ m = /(?<x>b..)/.match("foobarbaz")
+ assert_equal("foobarbaz", m.string)
+ end
+
+ def test_match_inspect
+ m = /(...)(...)(...)(...)?/.match("foobarbaz")
+ assert_equal('#<MatchData "foobarbaz" 1:"foo" 2:"bar" 3:"baz" 4:nil>', m.inspect)
+ end
+
+ def test_initialize
+ assert_raise(ArgumentError) { Regexp.new }
+ assert_equal(/foo/, Regexp.new(/foo/, Regexp::IGNORECASE))
+
+ assert_equal(Encoding.find("US-ASCII"), Regexp.new("b..", nil, "n").encoding)
+ assert_equal("bar", "foobarbaz"[Regexp.new("b..", nil, "n")])
+ assert_equal(//n, Regexp.new("", nil, "n"))
+
+ arg_encoding_none = 32 # ARG_ENCODING_NONE is implementation defined value
+ assert_equal(arg_encoding_none, Regexp.new("", nil, "n").options)
+ assert_equal(arg_encoding_none, Regexp.new("", nil, "N").options)
+
+ assert_raise(RegexpError) { Regexp.new(")(") }
+ end
+
+ def test_unescape
+ assert_raise(ArgumentError) { s = '\\'; /#{ s }/ }
+ assert_equal(/\xFF/n, /#{ s="\\xFF" }/n)
+ assert_equal(/\177/, (s = '\177'; /#{ s }/))
+ assert_raise(ArgumentError) { s = '\u'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\u{ ffffffff }'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\u{ ffffff }'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\u{ ffff X }'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\u{ }'; /#{ s }/ }
+ assert_equal("b", "abc"[(s = '\u{0062}'; /#{ s }/)])
+ assert_equal("b", "abc"[(s = '\u0062'; /#{ s }/)])
+ assert_raise(ArgumentError) { s = '\u0'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\u000X'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = "\xff" + '\u3042'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\u3042' + [0xff].pack("C"); /#{ s }/ }
+ assert_raise(SyntaxError) { s = ''; eval(%q(/\u#{ s }/)) }
+
+ assert_equal(/a/, eval(%q(s="\u0061";/#{s}/n)))
+ assert_raise(RegexpError) { s = "\u3042"; eval(%q(/#{s}/n)) }
+ assert_raise(RegexpError) { s = "\u0061"; eval(%q(/\u3042#{s}/n)) }
+ assert_raise(RegexpError) { s1=[0xff].pack("C"); s2="\u3042"; eval(%q(/#{s1}#{s2}/)); [s1, s2] }
+
+ assert_raise(ArgumentError) { s = '\x'; /#{ s }/ }
+
+ assert_equal("\xe1", [0x00, 0xe1, 0xff].pack("C*")[/\M-a/])
+ assert_equal("\xdc", [0x00, 0xdc, 0xff].pack("C*")[/\M-\\/])
+ assert_equal("\x8a", [0x00, 0x8a, 0xff].pack("C*")[/\M-\n/])
+ assert_equal("\x89", [0x00, 0x89, 0xff].pack("C*")[/\M-\t/])
+ assert_equal("\x8d", [0x00, 0x8d, 0xff].pack("C*")[/\M-\r/])
+ assert_equal("\x8c", [0x00, 0x8c, 0xff].pack("C*")[/\M-\f/])
+ assert_equal("\x8b", [0x00, 0x8b, 0xff].pack("C*")[/\M-\v/])
+ assert_equal("\x87", [0x00, 0x87, 0xff].pack("C*")[/\M-\a/])
+ assert_equal("\x9b", [0x00, 0x9b, 0xff].pack("C*")[/\M-\e/])
+ assert_equal("\x01", [0x00, 0x01, 0xff].pack("C*")[/\C-a/])
+
+ assert_raise(ArgumentError) { s = '\M'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\M-\M-a'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\M-\\'; /#{ s }/ }
+
+ assert_raise(ArgumentError) { s = '\C'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\c'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\C-\C-a'; /#{ s }/ }
+
+ assert_raise(ArgumentError) { s = '\M-\z'; /#{ s }/ }
+ assert_raise(ArgumentError) { s = '\M-\777'; /#{ s }/ }
+
+ assert_equal("\u3042\u3042", "\u3042\u3042"[(s = "\u3042" + %q(\xe3\x81\x82); /#{s}/)])
+ assert_raise(ArgumentError) { s = "\u3042" + %q(\xe3); /#{s}/ }
+ assert_raise(ArgumentError) { s = "\u3042" + %q(\xe3\xe3); /#{s}/ }
+ assert_raise(ArgumentError) { s = '\u3042' + [0xff].pack("C"); /#{s}/ }
+
+ assert_raise(SyntaxError) { eval("/\u3042/n") }
+
+ s = ".........."
+ 5.times { s.sub!(".", "") }
+ assert_equal(".....", s)
+ end
+
+ def test_equal
+ bug5484 = '[ruby-core:40364]'
+ assert_equal(/abc/, /abc/)
+ assert_not_equal(/abc/, /abc/m)
+ assert_not_equal(/abc/, /abd/)
+ assert_equal(/\/foo/, Regexp.new('/foo'), bug5484)
+ end
+
+ def test_match
+ assert_nil(//.match(nil))
+ assert_equal("abc", /.../.match(:abc)[0])
+ assert_raise(TypeError) { /.../.match(Object.new)[0] }
+ assert_equal("bc", /../.match('abc', 1)[0])
+ assert_equal("bc", /../.match('abc', -2)[0])
+ assert_nil(/../.match("abc", -4))
+ assert_nil(/../.match("abc", 4))
+ assert_equal('\x', /../n.match("\u3042" + '\x', 1)[0])
+
+ r = nil
+ /.../.match("abc") {|m| r = m[0] }
+ assert_equal("abc", r)
+
+ $_ = "abc"; assert_equal(1, ~/bc/)
+ $_ = "abc"; assert_nil(~/d/)
+ $_ = nil; assert_nil(~/./)
+ end
+
+ def test_eqq
+ assert_equal(false, /../ === nil)
+ end
+
+ def test_quote
+ assert_equal("\xff", Regexp.quote([0xff].pack("C")))
+ assert_equal("\\ ", Regexp.quote("\ "))
+ assert_equal("\\t", Regexp.quote("\t"))
+ assert_equal("\\n", Regexp.quote("\n"))
+ assert_equal("\\r", Regexp.quote("\r"))
+ assert_equal("\\f", Regexp.quote("\f"))
+ assert_equal("\\v", Regexp.quote("\v"))
+ assert_equal("\u3042\\t", Regexp.quote("\u3042\t"))
+ assert_equal("\\t\xff", Regexp.quote("\t" + [0xff].pack("C")))
+ end
+
+ def test_try_convert
+ assert_equal(/re/, Regexp.try_convert(/re/))
+ assert_nil(Regexp.try_convert("re"))
+
+ o = Object.new
+ assert_nil(Regexp.try_convert(o))
+ def o.to_regexp() /foo/ end
+ assert_equal(/foo/, Regexp.try_convert(o))
+ end
+
+ def test_union2
+ assert_equal(/(?!)/, Regexp.union)
+ assert_equal(/foo/, Regexp.union(/foo/))
+ assert_equal(/foo/, Regexp.union([/foo/]))
+ assert_equal(/\t/, Regexp.union("\t"))
+ assert_equal(/(?-mix:\u3042)|(?-mix:\u3042)/, Regexp.union(/\u3042/, /\u3042/))
+ assert_equal("\u3041", "\u3041"[Regexp.union(/\u3042/, "\u3041")])
+ end
+
+ def test_dup
+ assert_equal(//, //.dup)
+ assert_raise(TypeError) { //.instance_eval { initialize_copy(nil) } }
+ end
+
+ def test_regsub
+ assert_equal("fooXXXbaz", "foobarbaz".sub!(/bar/, "XXX"))
+ s = [0xff].pack("C")
+ assert_equal(s, "X".sub!(/./, s))
+ assert_equal('\\' + s, "X".sub!(/./, '\\' + s))
+ assert_equal('\k', "foo".sub!(/.../, '\k'))
+ assert_raise(RuntimeError) { "foo".sub!(/(?<x>o)/, '\k<x') }
+ assert_equal('foo[bar]baz', "foobarbaz".sub!(/(b..)/, '[\0]'))
+ assert_equal('foo[foo]baz', "foobarbaz".sub!(/(b..)/, '[\`]'))
+ assert_equal('foo[baz]baz', "foobarbaz".sub!(/(b..)/, '[\\\']'))
+ assert_equal('foo[r]baz', "foobarbaz".sub!(/(b)(.)(.)/, '[\+]'))
+ assert_equal('foo[\\]baz', "foobarbaz".sub!(/(b..)/, '[\\\\]'))
+ assert_equal('foo[\z]baz', "foobarbaz".sub!(/(b..)/, '[\z]'))
+ end
+
+ def test_regsub_K
+ bug8856 = '[ruby-dev:47694] [Bug #8856]'
+ result = "foobarbazquux/foobarbazquux".gsub(/foo\Kbar/, "")
+ assert_equal('foobazquux/foobazquux', result, bug8856)
+ end
+
+ def test_KCODE
+ assert_nil($KCODE)
+ assert_nothing_raised { $KCODE = nil }
+ assert_equal(false, $=)
+ assert_nothing_raised { $= = nil }
+ end
+
+ def test_match_setter
+ /foo/ =~ "foo"
+ m = $~
+ /bar/ =~ "bar"
+ $~ = m
+ assert_equal("foo", $&)
+ end
+
+ def test_match_without_regexp
+ bug10877 = '[ruby-core:68209] [Bug #10877]'
+ "abc".sub("a", "")
+ assert_raise_with_message(IndexError, /foo/, bug10877) {$~["foo"]}
+ key = "\u{3042}"
+ [Encoding::UTF_8, Encoding::Shift_JIS, Encoding::EUC_JP].each do |enc|
+ idx = key.encode(enc)
+ EnvUtil.with_default_external(enc) do
+ assert_raise_with_message(IndexError, /#{idx}/, bug10877) {$~[idx]}
+ end
+ end
+ end
+
+ def test_last_match
+ /(...)(...)(...)(...)?/.match("foobarbaz")
+ assert_equal("foobarbaz", Regexp.last_match(0))
+ assert_equal("foo", Regexp.last_match(1))
+ assert_nil(Regexp.last_match(5))
+ assert_nil(Regexp.last_match(-1))
+ end
+
+ def test_getter
+ alias $__REGEXP_TEST_LASTMATCH__ $&
+ alias $__REGEXP_TEST_PREMATCH__ $`
+ alias $__REGEXP_TEST_POSTMATCH__ $'
+ alias $__REGEXP_TEST_LASTPARENMATCH__ $+
+ /(b)(.)(.)/.match("foobarbaz")
+ assert_equal("bar", $__REGEXP_TEST_LASTMATCH__)
+ assert_equal("foo", $__REGEXP_TEST_PREMATCH__)
+ assert_equal("baz", $__REGEXP_TEST_POSTMATCH__)
+ assert_equal("r", $__REGEXP_TEST_LASTPARENMATCH__)
+
+ /(...)(...)(...)/.match("foobarbaz")
+ assert_equal("baz", $+)
+ end
+
+ def test_rindex_regexp
+ assert_equal(3, "foobarbaz\u3042".rindex(/b../n, 5))
+ end
+
+ def test_taint
+ m = Thread.new do
+ "foo"[/foo/]
+ $SAFE = 3
+ /foo/.match("foo")
+ end.value
+ assert_predicate(m, :tainted?)
+ assert_nothing_raised('[ruby-core:26137]') {
+ m = proc {$SAFE = 3; %r"#{ }"o}.call
+ }
+ assert_predicate(m, :tainted?)
+ end
+
+ def assert_regexp(re, ss, fs = [], msg = nil)
+ re = Regexp.new(re) unless re.is_a?(Regexp)
+ ss = [ss] unless ss.is_a?(Array)
+ ss.each do |e, s|
+ s ||= e
+ assert_match(re, s, msg)
+ m = re.match(s)
+ assert_equal(e, m[0], msg)
+ end
+ fs = [fs] unless fs.is_a?(Array)
+ fs.each {|s| assert_no_match(re, s, msg) }
+ end
+ alias check assert_regexp
+
+ def assert_fail(re)
+ assert_raise(RegexpError) { %r"#{ re }" }
+ end
+ alias failcheck assert_fail
+
+ def test_parse
+ check(/\*\+\?\{\}\|\(\)\<\>\`\'/, "*+?{}|()<>`'")
+ check(/\A\w\W\z/, %w(a. b!), %w(.. ab))
+ check(/\A.\b.\b.\B.\B.\z/, %w(a.aaa .a...), %w(aaaaa .....))
+ check(/\A\s\S\z/, [' a', "\n."], [' ', "\n\n", 'a '])
+ check(/\A\d\D\z/, '0a', %w(00 aa))
+ check(/\A\h\H\z/, %w(0g ag BH), %w(a0 af GG))
+ check(/\Afoo\Z\s\z/, "foo\n", ["foo", "foo\nbar"])
+ assert_equal(%w(a b c), "abc def".scan(/\G\w/))
+ check(/\A\u3042\z/, "\u3042", ["", "\u3043", "a"])
+ check(/\A(..)\1\z/, %w(abab ....), %w(abba aba))
+ failcheck('\1')
+ check(/\A\80\z/, "80", ["\100", ""])
+ check(/\A\77\z/, "?")
+ check(/\A\78\z/, "\7" + '8', ["\100", ""])
+ check(eval('/\A\Qfoo\E\z/'), "QfooE")
+ check(/\Aa++\z/, "aaa")
+ check('\Ax]\z', "x]")
+ check(/x#foo/x, "x", "#foo")
+ check(/\Ax#foo#{ "\n" }x\z/x, "xx", ["x", "x#foo\nx"])
+ check(/\A\p{Alpha}\z/, ["a", "z"], [".", "", ".."])
+ check(/\A\p{^Alpha}\z/, [".", "!"], ["!a", ""])
+ check(/\A\n\z/, "\n")
+ check(/\A\t\z/, "\t")
+ check(/\A\r\z/, "\r")
+ check(/\A\f\z/, "\f")
+ check(/\A\a\z/, "\007")
+ check(/\A\e\z/, "\033")
+ check(/\A\v\z/, "\v")
+ failcheck('(')
+ failcheck('(?foo)')
+ failcheck('/\p{foobarbazqux}/')
+ failcheck('/\p{foobarbazqux' + 'a' * 1000 + '}/')
+ failcheck('/[1-\w]/')
+ end
+
+ def test_exec
+ check(/A*B/, %w(B AB AAB AAAB), %w(A))
+ check(/\w*!/, %w(! a! ab! abc!), %w(abc))
+ check(/\w*\W/, %w(! a" ab# abc$), %w(abc))
+ check(/\w*\w/, %w(z az abz abcz), %w(!))
+ check(/[a-z]*\w/, %w(z az abz abcz), %w(!))
+ check(/[a-z]*\W/, %w(! a" ab# abc$), %w(A))
+ check(/((a|bb|ccc|dddd)(1|22|333|4444))/i, %w(a1 bb1 a22), %w(a2 b1))
+ check(/\u0080/, (1..4).map {|i| ["\u0080", "\u0080" * i] }, ["\u0081"])
+ check(/\u0080\u0080/, (2..4).map {|i| ["\u0080" * 2, "\u0080" * i] }, ["\u0081"])
+ check(/\u0080\u0080\u0080/, (3..4).map {|i| ["\u0080" * 3, "\u0080" * i] }, ["\u0081"])
+ check(/\u0080\u0080\u0080\u0080/, (4..4).map {|i| ["\u0080" * 4, "\u0080" * i] }, ["\u0081"])
+ check(/[^\u3042\u3043\u3044]/, %W(a b \u0080 \u3041 \u3045), %W(\u3042 \u3043 \u3044))
+ check(/a.+/m, %W(a\u0080 a\u0080\u0080 a\u0080\u0080\u0080), %W(a))
+ check(/a.+z/m, %W(a\u0080z a\u0080\u0080z a\u0080\u0080\u0080z), %W(az))
+ check(/abc\B.\Bxyz/, %w(abcXxyz abc0xyz), %w(abc|xyz abc-xyz))
+ check(/\Bxyz/, [%w(xyz abcXxyz), %w(xyz abc0xyz)], %w(abc xyz abc-xyz))
+ check(/abc\B/, [%w(abc abcXxyz), %w(abc abc0xyz)], %w(abc xyz abc-xyz))
+ failcheck('(?<foo>abc)\1')
+ check(/^(A+|B+)(?>\g<1>)*[BC]$/, %w(AC BC ABC BAC AABBC), %w(AABB))
+ check(/^(A+|B(?>\g<1>)*)[AC]$/, %w(AAAC BBBAAAAC), %w(BBBAAA))
+ check(/^()(?>\g<1>)*$/, "", "a")
+ check(/^(?>(?=a)(#{ "a" * 1000 }|))++$/, ["a" * 1000, "a" * 2000, "a" * 3000], ["", "a" * 500, "b" * 1000])
+ check(eval('/^(?:a?)?$/'), ["", "a"], ["aa"])
+ check(eval('/^(?:a+)?$/'), ["", "a", "aa"], ["ab"])
+ check(/^(?:a?)+?$/, ["", "a", "aa"], ["ab"])
+ check(/^a??[ab]/, [["a", "a"], ["a", "aa"], ["b", "b"], ["a", "ab"]], ["c"])
+ check(/^(?:a*){3,5}$/, ["", "a", "aa", "aaa", "aaaa", "aaaaa", "aaaaaa"], ["b"])
+ check(/^(?:a+){3,5}$/, ["aaa", "aaaa", "aaaaa", "aaaaaa"], ["", "a", "aa", "b"])
+ end
+
+ def test_parse_look_behind
+ check(/(?<=A)B(?=C)/, [%w(B ABC)], %w(aBC ABc aBc))
+ check(/(?<!A)B(?!C)/, [%w(B aBc)], %w(ABC aBC ABc))
+ failcheck('(?<=.*)')
+ failcheck('(?<!.*)')
+ check(/(?<=A|B.)C/, [%w(C AC), %w(C BXC)], %w(C BC))
+ check(/(?<!A|B.)C/, [%w(C C), %w(C BC)], %w(AC BXC))
+
+ assert_not_match(/(?<!aa|b)c/i, "Aac")
+ assert_not_match(/(?<!b|aa)c/i, "Aac")
+ end
+
+ def test_parse_kg
+ check(/\A(.)(.)\k<1>(.)\z/, %w(abac abab ....), %w(abcd aaba xxx))
+ check(/\A(.)(.)\k<-1>(.)\z/, %w(abbc abba ....), %w(abcd aaba xxx))
+ check(/\A(?<n>.)(?<x>\g<n>){0}(?<y>\k<n+0>){0}\g<x>\g<y>\z/, "aba", "abb")
+ check(/\A(?<n>.)(?<x>\g<n>){0}(?<y>\k<n+1>){0}\g<x>\g<y>\z/, "abb", "aba")
+ check(/\A(?<x>..)\k<x>\z/, %w(abab ....), %w(abac abba xxx))
+ check(/\A(.)(..)\g<-1>\z/, "abcde", %w(.... ......))
+ failcheck('\k<x>')
+ failcheck('\k<')
+ failcheck('\k<>')
+ failcheck('\k<.>')
+ failcheck('\k<x.>')
+ failcheck('\k<1.>')
+ failcheck('\k<x')
+ failcheck('\k<x+')
+ failcheck('()\k<-2>')
+ failcheck('()\g<-2>')
+ check(/\A(?<x>.)(?<x>.)\k<x>\z/, %w(aba abb), %w(abc .. ....))
+ check(/\A(?<x>.)(?<x>.)\k<x>\z/i, %w(aba ABa abb ABb), %w(abc .. ....))
+ check('\k\g', "kg")
+ failcheck('(.\g<1>)')
+ failcheck('(.\g<2>)')
+ failcheck('(?=\g<1>)')
+ failcheck('((?=\g<1>))')
+ failcheck('(\g<1>|.)')
+ failcheck('(.|\g<1>)')
+ check(/(!)(?<=(a)|\g<1>)/, ["!"], %w(a))
+ check(/^(a|b\g<1>c)$/, %w(a bac bbacc bbbaccc), %w(bbac bacc))
+ check(/^(a|b\g<2>c)(B\g<1>C){0}$/, %w(a bBaCc bBbBaCcCc bBbBbBaCcCcCc), %w(bBbBaCcC BbBaCcCc))
+ check(/\A(?<n>.|X\g<n>)(?<x>\g<n>){0}(?<y>\k<n+0>){0}\g<x>\g<y>\z/, "XXaXbXXa", %w(XXabXa abb))
+ check(/\A(?<n>.|X\g<n>)(?<x>\g<n>){0}(?<y>\k<n+1>){0}\g<x>\g<y>\z/, "XaXXbXXb", %w(aXXbXb aba))
+ failcheck('(?<x>)(?<x>)(\g<x>)')
+ check(/^(?<x>foo)(bar)\k<x>/, %w(foobarfoo), %w(foobar barfoo))
+ check(/^(?<a>f)(?<a>o)(?<a>o)(?<a>b)(?<a>a)(?<a>r)(?<a>b)(?<a>a)(?<a>z)\k<a>{9}$/, %w(foobarbazfoobarbaz foobarbazbazbarfoo foobarbazzabraboof), %w(foobar barfoo))
+ end
+
+ def test_parse_curly_brace
+ check(/\A{/, ["{", ["{", "{x"]])
+ check(/\A{ /, ["{ ", ["{ ", "{ x"]])
+ check(/\A{,}\z/, "{,}")
+ check(/\A{}\z/, "{}")
+ check(/\Aa{0}+\z/, "", %w(a aa aab))
+ check(/\Aa{1}+\z/, %w(a aa), ["", "aab"])
+ check(/\Aa{1,2}b{1,2}\z/, %w(ab aab abb aabb), ["", "aaabb", "abbb"])
+ check(/(?!x){0,1}/, [ ['', 'ab'], ['', ''] ])
+ check(/c\z{0,1}/, [ ['c', 'abc'], ['c', 'cab']], ['abd'])
+ check(/\A{0,1}a/, [ ['a', 'abc'], ['a', '____abc']], ['bcd'])
+ failcheck('.{100001}')
+ failcheck('.{0,100001}')
+ failcheck('.{1,0}')
+ failcheck('{0}')
+ end
+
+ def test_parse_comment
+ check(/\A(?#foo\)bar)\z/, "", "a")
+ failcheck('(?#')
+ end
+
+ def test_char_type
+ check(/\u3042\d/, ["\u30421", "\u30422"])
+
+ # CClassTable cache test
+ assert_match(/\u3042\d/, "\u30421")
+ assert_match(/\u3042\d/, "\u30422")
+ end
+
+ def test_char_class
+ failcheck('[]')
+ failcheck('[x')
+ check('\A[]]\z', "]", "")
+ check('\A[]\.]+\z', %w(] . ]..]), ["", "["])
+ check(/\A[\u3042]\z/, "\u3042", "\u3042aa")
+ check(/\A[\u3042\x61]+\z/, ["aa\u3042aa", "\u3042\u3042", "a"], ["", "b"])
+ check(/\A[\u3042\x61\x62]+\z/, "abab\u3042abab\u3042")
+ check(/\A[abc]+\z/, "abcba", ["", "ada"])
+ check(/\A[\w][\W]\z/, %w(a. b!), %w(.. ab))
+ check(/\A[\s][\S]\z/, [' a', "\n."], [' ', "\n\n", 'a '])
+ check(/\A[\d][\D]\z/, '0a', %w(00 aa))
+ check(/\A[\h][\H]\z/, %w(0g ag BH), %w(a0 af GG))
+ check(/\A[\p{Alpha}]\z/, ["a", "z"], [".", "", ".."])
+ check(/\A[\p{^Alpha}]\z/, [".", "!"], ["!a", ""])
+ check(/\A[\xff]\z/, "\xff", ["", "\xfe"])
+ check(/\A[\80]+\z/, "8008", ["\\80", "\100", "\1000"])
+ check(/\A[\77]+\z/, "???")
+ check(/\A[\78]+\z/, "\788\7")
+ check(/\A[\0]\z/, "\0")
+ check(/\A[[:0]]\z/, [":", "0"], ["", ":0"])
+ check(/\A[0-]\z/, ["0", "-"], "0-")
+ check('\A[a-&&\w]\z', "a", "-")
+ check('\A[--0]\z', ["-", "/", "0"], ["", "1"])
+ check('\A[\'--0]\z', %w(* + \( \) 0 ,), ["", ".", "1"])
+ check(/\A[a-b-]\z/, %w(a b -), ["", "c"])
+ check('\A[a-b-&&\w]\z', %w(a b), ["", "-"])
+ check('\A[a-b-&&\W]\z', "-", ["", "a", "b"])
+ check('\A[a-c-e]\z', %w(a b c e -), %w(d))
+ check(/\A[a-f&&[^b-c]&&[^e]]\z/, %w(a d f), %w(b c e g 0))
+ check(/\A[[^b-c]&&[^e]&&a-f]\z/, %w(a d f), %w(b c e g 0))
+ check(/\A[\n\r\t]\z/, ["\n", "\r", "\t"])
+ failcheck('[9-1]')
+
+ assert_match(/\A\d+\z/, "0123456789")
+ assert_no_match(/\d/, "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19")
+ assert_match(/\A\w+\z/, "09azAZ_")
+ assert_no_match(/\w/, "\uff10\uff19\uff41\uff5a\uff21\uff3a")
+ assert_match(/\A\s+\z/, "\r\n\v\f\r\s")
+ assert_no_match(/\s/, "\u0085")
+ end
+
+ def test_posix_bracket
+ check(/\A[[:alpha:]0]\z/, %w(0 a), %w(1 .))
+ check(eval('/\A[[:^alpha:]0]\z/'), %w(0 1 .), "a")
+ check(eval('/\A[[:alpha\:]]\z/'), %w(a l p h a :), %w(b 0 1 .))
+ check(eval('/\A[[:alpha:foo]0]\z/'), %w(0 a), %w(1 .))
+ check(/\A[[:xdigit:]&&[:alpha:]]\z/, "a", %w(g 0))
+ check('\A[[:abcdefghijklmnopqrstu:]]+\z', "[]")
+ failcheck('[[:alpha')
+ failcheck('[[:alpha:')
+ failcheck('[[:alp:]]')
+
+ assert_match(/\A[[:digit:]]+\z/, "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19")
+ assert_match(/\A[[:alnum:]]+\z/, "\uff10\uff19\uff41\uff5a\uff21\uff3a")
+ assert_match(/\A[[:space:]]+\z/, "\r\n\v\f\r\s\u0085")
+ assert_match(/\A[[:ascii:]]+\z/, "\x00\x7F")
+ assert_no_match(/[[:ascii:]]/, "\x80\xFF")
+ end
+
+ def test_backward
+ assert_equal(3, "foobar".rindex(/b.r/i))
+ assert_equal(nil, "foovar".rindex(/b.r/i))
+ assert_equal(3, ("foo" + "bar" * 1000).rindex(/#{"bar"*1000}/))
+ assert_equal(4, ("foo\nbar\nbaz\n").rindex(/bar/i))
+ end
+
+ def test_uninitialized
+ assert_raise(TypeError) { Regexp.allocate.hash }
+ assert_raise(TypeError) { Regexp.allocate.eql? Regexp.allocate }
+ assert_raise(TypeError) { Regexp.allocate == Regexp.allocate }
+ assert_raise(TypeError) { Regexp.allocate =~ "" }
+ assert_equal(false, Regexp.allocate === Regexp.allocate)
+ assert_nil(~Regexp.allocate)
+ assert_raise(TypeError) { Regexp.allocate.match("") }
+ assert_raise(TypeError) { Regexp.allocate.to_s }
+ assert_match(/^#<Regexp:.*>$/, Regexp.allocate.inspect)
+ assert_raise(TypeError) { Regexp.allocate.source }
+ assert_raise(TypeError) { Regexp.allocate.casefold? }
+ assert_raise(TypeError) { Regexp.allocate.options }
+ assert_equal(Encoding.find("ASCII-8BIT"), Regexp.allocate.encoding)
+ assert_equal(false, Regexp.allocate.fixed_encoding?)
+ assert_raise(TypeError) { Regexp.allocate.names }
+ assert_raise(TypeError) { Regexp.allocate.named_captures }
+
+ assert_raise(TypeError) { MatchData.allocate.regexp }
+ assert_raise(TypeError) { MatchData.allocate.names }
+ assert_raise(TypeError) { MatchData.allocate.size }
+ assert_raise(TypeError) { MatchData.allocate.length }
+ assert_raise(TypeError) { MatchData.allocate.offset(0) }
+ assert_raise(TypeError) { MatchData.allocate.begin(0) }
+ assert_raise(TypeError) { MatchData.allocate.end(0) }
+ assert_raise(TypeError) { MatchData.allocate.to_a }
+ assert_raise(TypeError) { MatchData.allocate[:foo] }
+ assert_raise(TypeError) { MatchData.allocate.captures }
+ assert_raise(TypeError) { MatchData.allocate.values_at }
+ assert_raise(TypeError) { MatchData.allocate.pre_match }
+ assert_raise(TypeError) { MatchData.allocate.post_match }
+ assert_raise(TypeError) { MatchData.allocate.to_s }
+ assert_match(/^#<MatchData:.*>$/, MatchData.allocate.inspect)
+ assert_raise(TypeError) { MatchData.allocate.string }
+ $~ = MatchData.allocate
+ assert_raise(TypeError) { $& }
+ assert_raise(TypeError) { $` }
+ assert_raise(TypeError) { $' }
+ assert_raise(TypeError) { $+ }
+ end
+
+ def test_unicode
+ assert_match(/^\u3042{0}\p{Any}$/, "a")
+ assert_match(/^\u3042{0}\p{Any}$/, "\u3041")
+ assert_match(/^\u3042{0}\p{Any}$/, "\0")
+ assert_match(/^\p{Lo}{4}$/u, "\u3401\u4E01\u{20001}\u{2A701}")
+ assert_no_match(/^\u3042{0}\p{Any}$/, "\0\0")
+ assert_no_match(/^\u3042{0}\p{Any}$/, "")
+ assert_raise(SyntaxError) { eval('/^\u3042{0}\p{' + "\u3042" + '}$/') }
+ assert_raise(SyntaxError) { eval('/^\u3042{0}\p{' + 'a' * 1000 + '}$/') }
+ assert_raise(SyntaxError) { eval('/^\u3042{0}\p{foobarbazqux}$/') }
+ assert_match(/^(\uff21)(a)\1\2$/i, "\uff21A\uff41a")
+ assert_no_match(/^(\uff21)\1$/i, "\uff21A")
+ assert_no_match(/^(\uff41)\1$/i, "\uff41a")
+ assert_match(/^\u00df$/i, "\u00df")
+ assert_match(/^\u00df$/i, "ss")
+ #assert_match(/^(\u00df)\1$/i, "\u00dfss") # this must be bug...
+ assert_match(/^\u00df{2}$/i, "\u00dfss")
+ assert_match(/^\u00c5$/i, "\u00c5")
+ assert_match(/^\u00c5$/i, "\u00e5")
+ assert_match(/^\u00c5$/i, "\u212b")
+ assert_match(/^(\u00c5)\1\1$/i, "\u00c5\u00e5\u212b")
+ assert_match(/^\u0149$/i, "\u0149")
+ assert_match(/^\u0149$/i, "\u02bcn")
+ #assert_match(/^(\u0149)\1$/i, "\u0149\u02bcn") # this must be bug...
+ assert_match(/^\u0149{2}$/i, "\u0149\u02bcn")
+ assert_match(/^\u0390$/i, "\u0390")
+ assert_match(/^\u0390$/i, "\u03b9\u0308\u0301")
+ #assert_match(/^(\u0390)\1$/i, "\u0390\u03b9\u0308\u0301") # this must be bug...
+ assert_match(/^\u0390{2}$/i, "\u0390\u03b9\u0308\u0301")
+ assert_match(/^\ufb05$/i, "\ufb05")
+ assert_match(/^\ufb05$/i, "\ufb06")
+ assert_match(/^\ufb05$/i, "st")
+ #assert_match(/^(\ufb05)\1\1$/i, "\ufb05\ufb06st") # this must be bug...
+ assert_match(/^\ufb05{3}$/i, "\ufb05\ufb06st")
+ assert_match(/^\u03b9\u0308\u0301$/i, "\u0390")
+ end
+
+ def test_unicode_age
+ assert_match(/^\p{Age=6.0}$/u, "\u261c")
+ assert_match(/^\p{Age=1.1}$/u, "\u261c")
+ assert_no_match(/^\P{age=6.0}$/u, "\u261c")
+
+ assert_match(/^\p{age=6.0}$/u, "\u31f6")
+ assert_match(/^\p{age=3.2}$/u, "\u31f6")
+ assert_no_match(/^\p{age=3.1}$/u, "\u31f6")
+ assert_no_match(/^\p{age=3.0}$/u, "\u31f6")
+ assert_no_match(/^\p{age=1.1}$/u, "\u31f6")
+
+ assert_match(/^\p{age=6.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=5.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=4.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=3.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=2.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=1.1}$/u, "\u2754")
+ end
+
+ MatchData_A = eval("class MatchData_\u{3042} < MatchData; self; end")
+
+ def test_matchdata
+ a = "haystack".match(/hay/)
+ b = "haystack".match(/hay/)
+ assert_equal(a, b, '[ruby-core:24748]')
+ h = {a => 42}
+ assert_equal(42, h[b], '[ruby-core:24748]')
+ assert_match(/#<TestRegexp::MatchData_\u{3042}:/, MatchData_A.allocate.inspect)
+ end
+
+ def test_regexp_poped
+ assert_nothing_raised { eval("a = 1; /\#{ a }/; a") }
+ assert_nothing_raised { eval("a = 1; /\#{ a }/o; a") }
+ end
+
+ def test_invalid_fragment
+ bug2547 = '[ruby-core:27374]'
+ assert_raise(SyntaxError, bug2547) {eval('/#{"\\\\"}y/')}
+ end
+
+ def test_dup_warn
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3043\u3042]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3043\u3043]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3044\u3043]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3045\u3043]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3045\u3044]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3045\u3043-\u3044]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3045\u3042-\u3043]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3045\u3044-\u3045]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3046\u3044]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u1000-\u2000\u3042-\u3046\u3044]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3044\u3041-\u3047]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3044\u3046\u3041-\u3047]') }
+
+ bug7471 = '[ruby-core:50344]'
+ assert_warning('', bug7471) { Regexp.new('[\D]') =~ "\u3042" }
+
+ bug8151 = '[ruby-core:53649]'
+ assert_warning(/\A\z/, bug8151) { Regexp.new('(?:[\u{33}])').to_s }
+ end
+
+ def test_property_warn
+ assert_in_out_err('-w', 'x=/\p%s/', [], %r"warning: invalid Unicode Property \\p: /\\p%s/")
+ end
+
+ def test_invalid_escape_error
+ bug3539 = '[ruby-core:31048]'
+ error = assert_raise(SyntaxError) {eval('/\x/', nil, bug3539)}
+ assert_match(/invalid hex escape/, error.message)
+ assert_equal(1, error.message.scan(/.*invalid .*escape.*/i).size, bug3539)
+ end
+
+ def test_raw_hyphen_and_tk_char_type_after_range
+ bug6853 = '[ruby-core:47115]'
+ # use Regexp.new instead of literal to ignore a parser warning.
+ check(Regexp.new('[0-1-\\s]'), [' ', '-'], ['2', 'a'], bug6853)
+ end
+
+ def test_error_message_on_failed_conversion
+ bug7539 = '[ruby-core:50733]'
+ assert_equal false, /x/=== 42
+ assert_raise_with_message(TypeError, 'no implicit conversion of Fixnum into String', bug7539) {
+ Regexp.quote(42)
+ }
+ end
+
+ def test_conditional_expression
+ bug8583 = '[ruby-dev:47480] [Bug #8583]'
+
+ conds = {"xy"=>true, "yx"=>true, "xx"=>false, "yy"=>false}
+ assert_match_each(/\A((x)|(y))(?(2)y|x)\z/, conds, bug8583)
+ assert_match_each(/\A((?<x>x)|(?<y>y))(?(<x>)y|x)\z/, conds, bug8583)
+ end
+
+ def test_options_in_look_behind
+ assert_nothing_raised {
+ assert_match_at("(?<=(?i)ab)cd", "ABcd", [[2,4]])
+ assert_match_at("(?<=(?i:ab))cd", "ABcd", [[2,4]])
+ assert_match_at("(?<!(?i)ab)cd", "aacd", [[2,4]])
+ assert_match_at("(?<!(?i:ab))cd", "aacd", [[2,4]])
+
+ assert_not_match("(?<=(?i)ab)cd", "ABCD")
+ assert_not_match("(?<=(?i:ab))cd", "ABCD")
+ assert_not_match("(?<!(?i)ab)cd", "ABcd")
+ assert_not_match("(?<!(?i:ab))cd", "ABcd")
+ }
+ end
+
+ def test_once
+ pr1 = proc{|i| /#{i}/o}
+ assert_equal(/0/, pr1.call(0))
+ assert_equal(/0/, pr1.call(1))
+ assert_equal(/0/, pr1.call(2))
+ end
+
+ def test_once_recursive
+ pr2 = proc{|i|
+ if i > 0
+ /#{pr2.call(i-1).to_s}#{i}/
+ else
+ //
+ end
+ }
+ assert_equal(/(?-mix:(?-mix:(?-mix:)1)2)3/, pr2.call(3))
+ end
+
+ def test_once_multithread
+ m = Mutex.new
+ pr3 = proc{|i|
+ /#{m.unlock; sleep 0.5; i}/o
+ }
+ ary = []
+ n = 0
+ th1 = Thread.new{m.lock; ary << pr3.call(n+=1)}
+ th2 = Thread.new{m.lock; ary << pr3.call(n+=1)}
+ th1.join; th2.join
+ assert_equal([/1/, /1/], ary)
+ end
+
+ def test_once_escape
+ pr4 = proc{|i|
+ catch(:xyzzy){
+ /#{throw :xyzzy, i}/o =~ ""
+ :ng
+ }
+ }
+ assert_equal(0, pr4.call(0))
+ assert_equal(1, pr4.call(1))
+ end
+
+ def test_eq_tilde_can_be_overridden
+ assert_separately([], <<-RUBY)
+ class Regexp
+ undef =~
+ def =~(str)
+ "foo"
+ end
+ end
+
+ assert_equal("foo", // =~ "")
+ RUBY
+ end
+
+ # This assertion is for porting x2() tests in testpy.py of Onigmo.
+ def assert_match_at(re, str, positions, msg = nil)
+ re = Regexp.new(re) unless re.is_a?(Regexp)
+
+ match = re.match(str)
+
+ assert_not_nil match, message(msg) {
+ "Expected #{re.inspect} to match #{str.inspect}"
+ }
+
+ if match
+ actual_positions = (0...match.size).map { |i|
+ [match.begin(i), match.end(i)]
+ }
+
+ assert_equal positions, actual_positions, message(msg) {
+ "Expected #{re.inspect} to match #{str.inspect} at: #{positions.inspect}"
+ }
+ end
+ end
+
+ def assert_match_each(re, conds, msg = nil)
+ errs = conds.select {|str, match| match ^ (re =~ str)}
+ msg = message(msg) {
+ "Expected #{re.inspect} to\n" +
+ errs.map {|str, match| "\t#{'not ' unless match}match #{str.inspect}"}.join(",\n")
+ }
+ assert_empty(errs, msg)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_require.rb b/jni/ruby/test/ruby/test_require.rb
new file mode 100644
index 0000000..33aea60
--- /dev/null
+++ b/jni/ruby/test/ruby/test_require.rb
@@ -0,0 +1,704 @@
+require 'test/unit'
+
+require 'tempfile'
+require 'tmpdir'
+
+class TestRequire < Test::Unit::TestCase
+ def test_load_error_path
+ filename = "should_not_exist"
+ error = assert_raise(LoadError) do
+ require filename
+ end
+ assert_equal filename, error.path
+ end
+
+ def test_require_invalid_shared_object
+ Tempfile.create(["test_ruby_test_require", ".so"]) {|t|
+ t.puts "dummy"
+ t.close
+
+ assert_separately([], <<-INPUT)
+ $:.replace([IO::NULL])
+ assert_raise(LoadError) do
+ require \"#{ t.path }\"
+ end
+ INPUT
+ }
+ end
+
+ def test_require_too_long_filename
+ assert_separately(["RUBYOPT"=>nil], <<-INPUT)
+ $:.replace([IO::NULL])
+ assert_raise(LoadError) do
+ require '#{ "foo/" * 10000 }foo'
+ end
+ INPUT
+
+ begin
+ assert_in_out_err(["-S", "-w", "foo/" * 1024 + "foo"], "") do |r, e|
+ assert_equal([], r)
+ assert_operator(2, :<=, e.size)
+ assert_match(/warning: openpath: pathname too long \(ignored\)/, e.first)
+ assert_match(/\(LoadError\)/, e.last)
+ end
+ rescue Errno::EINVAL
+ # too long commandline may be blocked by OS.
+ end
+ end
+
+ def test_require_nonascii
+ bug3758 = '[ruby-core:31915]'
+ ["\u{221e}", "\x82\xa0".force_encoding("cp932")].each do |path|
+ assert_raise_with_message(LoadError, /#{path}\z/, bug3758) {require path}
+ end
+ end
+
+ def test_require_nonascii_path
+ bug8165 = '[ruby-core:53733] [Bug #8165]'
+ encoding = 'filesystem'
+ assert_require_nonascii_path(encoding, bug8165)
+ end
+
+ def test_require_nonascii_path_utf8
+ bug8676 = '[ruby-core:56136] [Bug #8676]'
+ encoding = Encoding::UTF_8
+ return if Encoding.find('filesystem') == encoding
+ assert_require_nonascii_path(encoding, bug8676)
+ end
+
+ def test_require_nonascii_path_shift_jis
+ bug8676 = '[ruby-core:56136] [Bug #8676]'
+ encoding = Encoding::Shift_JIS
+ return if Encoding.find('filesystem') == encoding
+ assert_require_nonascii_path(encoding, bug8676)
+ end
+
+ case RUBY_PLATFORM
+ when /cygwin/, /mswin/, /mingw/, /darwin/
+ def self.ospath_encoding(path)
+ Encoding::UTF_8
+ end
+ else
+ def self.ospath_encoding(path)
+ path.encoding
+ end
+ end
+
+ def assert_require_nonascii_path(encoding, bug)
+ Dir.mktmpdir {|tmp|
+ dir = "\u3042" * 5
+ begin
+ require_path = File.join(tmp, dir, 'foo.rb').encode(encoding)
+ rescue
+ skip "cannot convert path encoding to #{encoding}"
+ end
+ Dir.mkdir(File.dirname(require_path))
+ open(require_path, "wb") {|f| f.puts '$:.push __FILE__'}
+ begin
+ load_path = $:.dup
+ features = $".dup
+ # leave paths for require encoding objects
+ bug = "#{bug} require #{encoding} path"
+ require_path = "#{require_path}"
+ $:.clear
+ assert_nothing_raised(LoadError, bug) {
+ assert(require(require_path), bug)
+ assert_equal(self.class.ospath_encoding(require_path), $:.last.encoding, '[Bug #8753]')
+ assert(!require(require_path), bug)
+ }
+ ensure
+ $:.replace(load_path)
+ $".replace(features)
+ end
+ }
+ end
+
+ def test_require_path_home_1
+ env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"]
+ pathname_too_long = /pathname too long \(ignored\).*\(LoadError\)/m
+
+ ENV["RUBYPATH"] = "~"
+ ENV["HOME"] = "/foo" * 1024
+ assert_in_out_err(%w(-S -w test_ruby_test_require), "", [], pathname_too_long)
+
+ ensure
+ env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH")
+ env_home ? ENV["HOME"] = env_home : ENV.delete("HOME")
+ end
+
+ def test_require_path_home_2
+ env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"]
+ pathname_too_long = /pathname too long \(ignored\).*\(LoadError\)/m
+
+ ENV["RUBYPATH"] = "~" + "/foo" * 1024
+ ENV["HOME"] = "/foo"
+ assert_in_out_err(%w(-S -w test_ruby_test_require), "", [], pathname_too_long)
+
+ ensure
+ env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH")
+ env_home ? ENV["HOME"] = env_home : ENV.delete("HOME")
+ end
+
+ def test_require_path_home_3
+ env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"]
+
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "p :ok"
+ t.close
+
+ ENV["RUBYPATH"] = "~"
+ ENV["HOME"] = t.path
+ assert_in_out_err(%w(-S test_ruby_test_require), "", [], /\(LoadError\)/)
+
+ ENV["HOME"], name = File.split(t.path)
+ assert_in_out_err(["-S", name], "", %w(:ok), [])
+ }
+ ensure
+ env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH")
+ env_home ? ENV["HOME"] = env_home : ENV.delete("HOME")
+ end
+
+ def test_require_with_unc
+ ruby = File.expand_path(EnvUtil.rubybin).sub(/\A(\w):/, '//127.0.0.1/\1$/')
+ skip "local drive #$1: is not shared" unless File.exist?(ruby)
+ pid = nil
+ assert_nothing_raised {pid = spawn(ruby, "-rabbrev", "-e0")}
+ ret, status = Process.wait2(pid)
+ assert_equal(pid, ret)
+ assert_predicate(status, :success?)
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_require_twice
+ Dir.mktmpdir do |tmp|
+ req = File.join(tmp, "very_long_file_name.rb")
+ File.write(req, "p :ok\n")
+ assert_file.exist?(req)
+ req[/.rb$/i] = ""
+ assert_in_out_err(['--disable-gems'], <<-INPUT, %w(:ok), [])
+ require "#{req}"
+ require "#{req}"
+ INPUT
+ end
+ end
+
+ def test_define_class
+ begin
+ require "socket"
+ rescue LoadError
+ return
+ end
+
+ assert_separately([], <<-INPUT)
+ BasicSocket = 1
+ assert_raise(TypeError) do
+ require 'socket'
+ end
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ class BasicSocket; end
+ assert_raise(TypeError) do
+ require 'socket'
+ end
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ class BasicSocket < IO; end
+ assert_nothing_raised do
+ require 'socket'
+ end
+ INPUT
+ end
+
+ def test_define_class_under
+ begin
+ require "zlib"
+ rescue LoadError
+ return
+ end
+
+ assert_separately([], <<-INPUT)
+ module Zlib; end
+ Zlib::Error = 1
+ assert_raise(TypeError) do
+ require 'zlib'
+ end
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ module Zlib; end
+ class Zlib::Error; end
+ assert_raise(NameError) do
+ require 'zlib'
+ end
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ module Zlib; end
+ class Zlib::Error < StandardError; end
+ assert_nothing_raised do
+ require 'zlib'
+ end
+ INPUT
+ end
+
+ def test_define_module
+ begin
+ require "zlib"
+ rescue LoadError
+ return
+ end
+
+ assert_separately([], <<-INPUT)
+ Zlib = 1
+ assert_raise(TypeError) do
+ require 'zlib'
+ end
+ INPUT
+ end
+
+ def test_define_module_under
+ begin
+ require "socket"
+ rescue LoadError
+ return
+ end
+
+ assert_separately([], <<-INPUT)
+ class BasicSocket < IO; end
+ class Socket < BasicSocket; end
+ Socket::Constants = 1
+ assert_raise(TypeError) do
+ require 'socket'
+ end
+ INPUT
+ end
+
+ def test_load
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "module Foo; end"
+ t.puts "at_exit { p :wrap_end }"
+ t.puts "at_exit { raise 'error in at_exit test' }"
+ t.puts "p :ok"
+ t.close
+
+ assert_in_out_err([], <<-INPUT, %w(:ok :end :wrap_end), /error in at_exit test/)
+ load(#{ t.path.dump }, true)
+ GC.start
+ p :end
+ INPUT
+
+ assert_raise(ArgumentError) { at_exit }
+ }
+ end
+
+ def test_load_scope
+ bug1982 = '[ruby-core:25039] [Bug #1982]'
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "Hello = 'hello'"
+ t.puts "class Foo"
+ t.puts " p Hello"
+ t.puts "end"
+ t.close
+
+ assert_in_out_err([], <<-INPUT, %w("hello"), [], bug1982)
+ load(#{ t.path.dump }, true)
+ INPUT
+ }
+ end
+
+ def test_load_ospath
+ bug = '[ruby-list:49994] path in ospath'
+ base = "test_load\u{3042 3044 3046 3048 304a}".encode(Encoding::Windows_31J)
+ path = nil
+ Tempfile.create([base, ".rb"]) do |t|
+ path = t.path
+
+ assert_raise_with_message(LoadError, /#{base}/) {
+ load(File.join(File.dirname(path), base))
+ }
+
+ t.puts "warn 'ok'"
+ t.close
+ assert_include(path, base)
+ assert_warn("ok\n", bug) {
+ assert_nothing_raised(LoadError, bug) {
+ load(path)
+ }
+ }
+ end
+ end
+
+ def test_tainted_loadpath
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ abs_dir, file = File.split(t.path)
+ abs_dir = File.expand_path(abs_dir).untaint
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir
+ assert_nothing_raised {require "#{ file }"}
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir.taint
+ assert_nothing_raised {require "#{ file }"}
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir.taint
+ $SAFE = 1
+ assert_raise(SecurityError) {require "#{ file }"}
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir.taint
+ $SAFE = 1
+ assert_raise(SecurityError) {require "#{ file }"}
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir << 'elsewhere'.taint
+ assert_nothing_raised {require "#{ file }"}
+ INPUT
+ }
+ end
+
+ def test_relative
+ load_path = $:.dup
+ $:.delete(".")
+ Dir.mktmpdir do |tmp|
+ Dir.chdir(tmp) do
+ Dir.mkdir('x')
+ File.open('x/t.rb', 'wb') {}
+ File.open('x/a.rb', 'wb') {|f| f.puts("require_relative('t.rb')")}
+ assert require('./x/t.rb')
+ assert !require(File.expand_path('x/t.rb'))
+ assert_nothing_raised(LoadError) {require('./x/a.rb')}
+ assert_raise(LoadError) {require('x/t.rb')}
+ File.unlink(*Dir.glob('x/*'))
+ Dir.rmdir("#{tmp}/x")
+ $:.replace(load_path)
+ load_path = nil
+ assert(!require('tmpdir'))
+ end
+ end
+ ensure
+ $:.replace(load_path) if load_path
+ end
+
+ def test_relative_symlink
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ Dir.mkdir "a"
+ Dir.mkdir "b"
+ File.open("a/lib.rb", "w") {|f| f.puts 'puts "a/lib.rb"' }
+ File.open("b/lib.rb", "w") {|f| f.puts 'puts "b/lib.rb"' }
+ File.open("a/tst.rb", "w") {|f| f.puts 'require_relative "lib"' }
+ begin
+ File.symlink("../a/tst.rb", "b/tst.rb")
+ result = IO.popen([EnvUtil.rubybin, "b/tst.rb"], &:read)
+ assert_equal("a/lib.rb\n", result, "[ruby-dev:40040]")
+ rescue NotImplementedError
+ skip "File.symlink is not implemented"
+ end
+ }
+ }
+ end
+
+ def test_frozen_loaded_features
+ bug3756 = '[ruby-core:31913]'
+ assert_in_out_err(['-e', '$LOADED_FEATURES.freeze; require "ostruct"'], "",
+ [], /\$LOADED_FEATURES is frozen; cannot append feature \(RuntimeError\)$/,
+ bug3756)
+ end
+
+ def test_race_exception
+ bug5754 = '[ruby-core:41618]'
+ path = nil
+ stderr = $stderr
+ verbose = $VERBOSE
+ Tempfile.create(%w"bug5754 .rb") {|tmp|
+ path = tmp.path
+ tmp.print %{\
+ th = Thread.current
+ t = th[:t]
+ scratch = th[:scratch]
+
+ if scratch.empty?
+ scratch << :pre
+ Thread.pass until t.stop?
+ raise RuntimeError
+ else
+ scratch << :post
+ end
+ }
+ tmp.close
+
+ class << (output = "")
+ alias write concat
+ end
+ $stderr = output
+
+ start = false
+
+ scratch = []
+ t1_res = nil
+ t2_res = nil
+
+ t1 = Thread.new do
+ Thread.pass until start
+ begin
+ require(path)
+ rescue RuntimeError
+ end
+
+ t1_res = require(path)
+ end
+
+ t2 = Thread.new do
+ Thread.pass until scratch[0]
+ t2_res = require(path)
+ end
+
+ t1[:scratch] = t2[:scratch] = scratch
+ t1[:t] = t2
+ t2[:t] = t1
+
+ $VERBOSE = true
+ start = true
+
+ assert_nothing_raised(ThreadError, bug5754) {t1.join}
+ assert_nothing_raised(ThreadError, bug5754) {t2.join}
+
+ $VERBOSE = false
+
+ assert_equal(true, (t1_res ^ t2_res), bug5754 + " t1:#{t1_res} t2:#{t2_res}")
+ assert_equal([:pre, :post], scratch, bug5754)
+
+ assert_match(/circular require/, output)
+ assert_match(/in #{__method__}'$/o, output)
+ }
+ ensure
+ $VERBOSE = verbose
+ $stderr = stderr
+ $".delete(path)
+ end
+
+ def test_loaded_features_encoding
+ bug6377 = '[ruby-core:44750]'
+ loadpath = $:.dup
+ features = $".dup
+ $".clear
+ $:.clear
+ Dir.mktmpdir {|tmp|
+ $: << tmp
+ open(File.join(tmp, "foo.rb"), "w") {}
+ require "foo"
+ assert_send([Encoding, :compatible?, tmp, $"[0]], bug6377)
+ }
+ ensure
+ $:.replace(loadpath)
+ $".replace(features)
+ end
+
+ def test_require_changed_current_dir
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ Dir.mkdir("a")
+ Dir.mkdir("b")
+ open(File.join("a", "foo.rb"), "w") {}
+ open(File.join("b", "bar.rb"), "w") {|f|
+ f.puts "p :ok"
+ }
+ assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ $:.replace([IO::NULL])
+ $: << "."
+ Dir.chdir("a")
+ require "foo"
+ Dir.chdir("../b")
+ p :ng unless require "bar"
+ Dir.chdir("..")
+ p :ng if require "b/bar"
+ INPUT
+ }
+ }
+ end
+
+ def test_require_not_modified_load_path
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ $:.replace([IO::NULL])
+ a = Object.new
+ def a.to_str
+ "#{tmp}"
+ end
+ $: << a
+ require "foo"
+ last_path = $:.pop
+ p :ok if last_path == a && last_path.class == Object
+ INPUT
+ }
+ }
+ end
+
+ def test_require_changed_home
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ Dir.mkdir("a")
+ open(File.join("a", "bar.rb"), "w") {}
+ assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ $:.replace([IO::NULL])
+ $: << '~'
+ ENV['HOME'] = "#{tmp}"
+ require "foo"
+ ENV['HOME'] = "#{tmp}/a"
+ p :ok if require "bar"
+ INPUT
+ }
+ }
+ end
+
+ def test_require_to_path_redefined_in_load_path
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err(["RUBYOPT"=>nil], <<-INPUT, %w(:ok), [], bug7158)
+ $:.replace([IO::NULL])
+ a = Object.new
+ def a.to_path
+ "bar"
+ end
+ $: << a
+ begin
+ require "foo"
+ p [:ng, $LOAD_PATH, ENV['RUBYLIB']]
+ rescue LoadError
+ end
+ def a.to_path
+ "#{tmp}"
+ end
+ p :ok if require "foo"
+ INPUT
+ }
+ }
+ end
+
+ def test_require_to_str_redefined_in_load_path
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err(["RUBYOPT"=>nil], <<-INPUT, %w(:ok), [], bug7158)
+ $:.replace([IO::NULL])
+ a = Object.new
+ def a.to_str
+ "foo"
+ end
+ $: << a
+ begin
+ require "foo"
+ p [:ng, $LOAD_PATH, ENV['RUBYLIB']]
+ rescue LoadError
+ end
+ def a.to_str
+ "#{tmp}"
+ end
+ p :ok if require "foo"
+ INPUT
+ }
+ }
+ end
+
+ def assert_require_with_shared_array_modified(add, del)
+ bug7383 = '[ruby-core:49518]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ Dir.mkdir("a")
+ open(File.join("a", "bar.rb"), "w") {}
+ assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7383)
+ $:.replace([IO::NULL])
+ $:.#{add} "#{tmp}"
+ $:.#{add} "#{tmp}/a"
+ require "foo"
+ $:.#{del}
+ # Expanded load path cache should be rebuilt.
+ begin
+ require "bar"
+ rescue LoadError
+ p :ok
+ end
+ INPUT
+ }
+ }
+ end
+
+ def test_require_with_array_pop
+ assert_require_with_shared_array_modified("push", "pop")
+ end
+
+ def test_require_with_array_shift
+ assert_require_with_shared_array_modified("unshift", "shift")
+ end
+
+ def test_require_local_var_on_toplevel
+ bug7536 = '[ruby-core:50701]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("bar.rb", "w") {|f| f.puts 'TOPLEVEL_BINDING.eval("lib = 2")' }
+ assert_in_out_err(%w[-r./bar.rb], <<-INPUT, %w([:lib] 2), [], bug7536)
+ puts TOPLEVEL_BINDING.eval("local_variables").inspect
+ puts TOPLEVEL_BINDING.eval("lib").inspect
+ INPUT
+ }
+ }
+ end
+
+ def test_require_with_loaded_features_pop
+ bug7530 = '[ruby-core:50645]'
+ Tempfile.create(%w'bug-7530- .rb') {|script|
+ script.close
+ assert_in_out_err([{"RUBYOPT" => nil}, "-", script.path], <<-INPUT, %w(:ok), [], bug7530)
+ PATH = ARGV.shift
+ THREADS = 2
+ ITERATIONS_PER_THREAD = 1000
+
+ THREADS.times.map {
+ Thread.new do
+ ITERATIONS_PER_THREAD.times do
+ require PATH
+ $".pop
+ end
+ end
+ }.each(&:join)
+ p :ok
+ INPUT
+ }
+ end
+
+ def test_loading_fifo_threading
+ Tempfile.create(%w'fifo .rb') {|f|
+ f.close
+ File.unlink(f.path)
+ File.mkfifo(f.path)
+ assert_separately(["-", f.path], <<-END, timeout: 3)
+ th = Thread.current
+ Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)}
+ assert_raise(IOError) {load(ARGV[0])}
+ END
+ }
+ rescue Errno::ENOENT
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+end
diff --git a/jni/ruby/test/ruby/test_rubyoptions.rb b/jni/ruby/test/ruby/test_rubyoptions.rb
new file mode 100644
index 0000000..7cb1248
--- /dev/null
+++ b/jni/ruby/test/ruby/test_rubyoptions.rb
@@ -0,0 +1,796 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+
+require 'tmpdir'
+require 'tempfile'
+
+class TestRubyOptions < Test::Unit::TestCase
+ def write_file(filename, content)
+ File.open(filename, "w") {|f|
+ f << content
+ }
+ end
+
+ def with_tmpchdir
+ Dir.mktmpdir {|d|
+ d = File.realpath(d)
+ Dir.chdir(d) {
+ yield d
+ }
+ }
+ end
+
+ def test_source_file
+ assert_in_out_err([], "", [], [])
+ end
+
+ def test_usage
+ assert_in_out_err(%w(-h)) do |r, e|
+ assert_operator(r.size, :<=, 24)
+ longer = r[1..-1].select {|x| x.size > 80}
+ assert_equal([], longer)
+ assert_equal([], e)
+ end
+ end
+
+ def test_usage_long
+ assert_in_out_err(%w(--help)) do |r, e|
+ longer = r[1..-1].select {|x| x.size > 80}
+ assert_equal([], longer)
+ assert_equal([], e)
+ end
+ end
+
+ def test_option_variables
+ assert_in_out_err(["-e", 'p [$-p, $-l, $-a]']) do |r, e|
+ assert_equal(["[false, false, false]"], r)
+ assert_equal([], e)
+ end
+
+ assert_in_out_err(%w(-p -l -a -e) + ['p [$-p, $-l, $-a]'],
+ "foo\nbar\nbaz\n") do |r, e|
+ assert_equal(
+ [ '[true, true, true]', 'foo',
+ '[true, true, true]', 'bar',
+ '[true, true, true]', 'baz' ], r)
+ assert_equal([], e)
+ end
+ end
+
+ def test_warning
+ save_rubyopt = ENV['RUBYOPT']
+ ENV['RUBYOPT'] = nil
+ assert_in_out_err(%w(-W0 -e) + ['p $-W'], "", %w(0), [])
+ assert_in_out_err(%w(-W1 -e) + ['p $-W'], "", %w(1), [])
+ assert_in_out_err(%w(-Wx -e) + ['p $-W'], "", %w(1), [])
+ assert_in_out_err(%w(-W -e) + ['p $-W'], "", %w(2), [])
+ ensure
+ ENV['RUBYOPT'] = save_rubyopt
+ end
+
+ def test_safe_level
+ assert_in_out_err(%w(-T -e) + [""], "", [],
+ /no -e allowed in tainted mode \(SecurityError\)/)
+
+ assert_in_out_err(%w(-T4 -S foo.rb), "", [],
+ /no -S allowed in tainted mode \(SecurityError\)/)
+ end
+
+ def test_debug
+ assert_in_out_err(["--disable-gems", "-de", "p $DEBUG"], "", %w(true), [])
+
+ assert_in_out_err(["--disable-gems", "--debug", "-e", "p $DEBUG"],
+ "", %w(true), [])
+ end
+
+ def test_verbose
+ assert_in_out_err(["-vve", ""]) do |r, e|
+ assert_match(/^ruby #{RUBY_VERSION}(?:[p ]|dev|rc).*? \[#{RUBY_PLATFORM}\]$/, r.join)
+ assert_equal RUBY_DESCRIPTION, r.join.chomp
+ assert_equal([], e)
+ end
+
+ assert_in_out_err(%w(--verbose -e) + ["p $VERBOSE"], "", %w(true), [])
+
+ assert_in_out_err(%w(--verbose), "", [], [])
+ end
+
+ def test_copyright
+ assert_in_out_err(%w(--copyright), "",
+ /^ruby - Copyright \(C\) 1993-\d+ Yukihiro Matsumoto$/, [])
+
+ assert_in_out_err(%w(--verbose -e) + ["p $VERBOSE"], "", %w(true), [])
+ end
+
+ def test_enable
+ assert_in_out_err(%w(--enable all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--enable-all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--enable=all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--enable foobarbazqux -e) + [""], "", [],
+ /unknown argument for --enable: `foobarbazqux'/)
+ assert_in_out_err(%w(--enable), "", [], /missing argument for --enable/)
+ end
+
+ def test_disable
+ assert_in_out_err(%w(--disable all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--disable-all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--disable=all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--disable foobarbazqux -e) + [""], "", [],
+ /unknown argument for --disable: `foobarbazqux'/)
+ assert_in_out_err(%w(--disable), "", [], /missing argument for --disable/)
+ end
+
+ def test_kanji
+ assert_in_out_err(%w(-KU), "p '\u3042'") do |r, e|
+ assert_equal("\"\u3042\"", r.join.force_encoding(Encoding::UTF_8))
+ end
+ line = '-eputs"\xc2\xa1".encoding'
+ env = {'RUBYOPT' => nil}
+ assert_in_out_err([env, '-Ke', line], "", ["EUC-JP"], [])
+ assert_in_out_err([env, '-KE', line], "", ["EUC-JP"], [])
+ assert_in_out_err([env, '-Ks', line], "", ["Windows-31J"], [])
+ assert_in_out_err([env, '-KS', line], "", ["Windows-31J"], [])
+ assert_in_out_err([env, '-Ku', line], "", ["UTF-8"], [])
+ assert_in_out_err([env, '-KU', line], "", ["UTF-8"], [])
+ assert_in_out_err([env, '-Kn', line], "", ["ASCII-8BIT"], [])
+ assert_in_out_err([env, '-KN', line], "", ["ASCII-8BIT"], [])
+ assert_in_out_err([env, '-wKe', line], "", ["EUC-JP"], /-K/)
+ end
+
+ def test_version
+ assert_in_out_err(%w(--version)) do |r, e|
+ assert_match(/^ruby #{RUBY_VERSION}(?:[p ]|dev|rc).*? \[#{RUBY_PLATFORM}\]$/, r.join)
+ assert_equal RUBY_DESCRIPTION, r.join.chomp
+ assert_equal([], e)
+ end
+ end
+
+ def test_eval
+ assert_in_out_err(%w(-e), "", [], /no code specified for -e \(RuntimeError\)/)
+ end
+
+ def test_require
+ require "pp"
+ assert_in_out_err(%w(-r pp -e) + ["pp 1"], "", %w(1), [])
+ assert_in_out_err(%w(-rpp -e) + ["pp 1"], "", %w(1), [])
+ assert_in_out_err(%w(-ep\ 1 -r), "", %w(1), [])
+ assert_in_out_err(%w(-r), "", [], [])
+ rescue LoadError
+ end
+
+ def test_include
+ d = Dir.tmpdir
+ assert_in_out_err(["-I" + d, "-e", ""], "", [], [])
+ assert_in_out_err(["-I", d, "-e", ""], "", [], [])
+ end
+
+ def test_separator
+ assert_in_out_err(%w(-000 -e) + ["print gets"], "foo\nbar\0baz", %W(foo bar\0baz), [])
+
+ assert_in_out_err(%w(-0141 -e) + ["print gets"], "foo\nbar\0baz", %w(foo ba), [])
+
+ assert_in_out_err(%w(-0e) + ["print gets"], "foo\nbar\0baz", %W(foo bar\0), [])
+ end
+
+ def test_autosplit
+ assert_in_out_err(%w(-an -F: -e) + ["p $F"], "foo:bar:baz\nqux:quux:quuux\n",
+ ['["foo", "bar", "baz\n"]', '["qux", "quux", "quuux\n"]'], [])
+ end
+
+ def test_chdir
+ assert_in_out_err(%w(-C), "", [], /Can't chdir/)
+
+ assert_in_out_err(%w(-C test_ruby_test_rubyoptions_foobarbazqux), "", [], /Can't chdir/)
+
+ d = Dir.tmpdir
+ assert_in_out_err(["-C", d, "-e", "puts Dir.pwd"]) do |r, e|
+ assert_file.identical?(r.join, d)
+ assert_equal([], e)
+ end
+ end
+
+ def test_yydebug
+ assert_in_out_err(["-ye", ""]) do |r, e|
+ assert_equal([], r)
+ assert_not_equal([], e)
+ end
+
+ assert_in_out_err(%w(--yydebug -e) + [""]) do |r, e|
+ assert_equal([], r)
+ assert_not_equal([], e)
+ end
+ end
+
+ def test_encoding
+ assert_in_out_err(%w(--encoding), "", [], /missing argument for --encoding/)
+
+ assert_in_out_err(%w(--encoding test_ruby_test_rubyoptions_foobarbazqux), "", [],
+ /unknown encoding name - test_ruby_test_rubyoptions_foobarbazqux \(RuntimeError\)/)
+
+ if /mswin|mingw|aix/ =~ RUBY_PLATFORM &&
+ (str = "\u3042".force_encoding(Encoding.find("locale"))).valid_encoding?
+ # This result depends on locale because LANG=C doesn't affect locale
+ # on Windows.
+ # On AIX, the source encoding of stdin with LANG=C is ISO-8859-1,
+ # which allows \u3042.
+ out, err = [str], []
+ else
+ out, err = [], /invalid multibyte char/
+ end
+ assert_in_out_err(%w(-Eutf-8), "puts '\u3042'", out, err)
+ assert_in_out_err(%w(--encoding utf-8), "puts '\u3042'", out, err)
+ end
+
+ def test_syntax_check
+ assert_in_out_err(%w(-c -e a=1+1 -e !a), "", ["Syntax OK"], [])
+ end
+
+ def test_invalid_option
+ assert_in_out_err(%w(--foobarbazqux), "", [], /invalid option --foobarbazqux/)
+
+ assert_in_out_err(%W(-\r -e) + [""], "", [], [])
+
+ assert_in_out_err(%W(-\rx), "", [], /invalid option -\\x0D \(-h will show valid options\) \(RuntimeError\)/)
+
+ assert_in_out_err(%W(-\x01), "", [], /invalid option -\\x01 \(-h will show valid options\) \(RuntimeError\)/)
+
+ assert_in_out_err(%w(-Z), "", [], /invalid option -Z \(-h will show valid options\) \(RuntimeError\)/)
+ end
+
+ def test_rubyopt
+ rubyopt_orig = ENV['RUBYOPT']
+
+ ENV['RUBYOPT'] = ' - -'
+ assert_in_out_err([], "", [], [])
+
+ ENV['RUBYOPT'] = '-e "p 1"'
+ assert_in_out_err([], "", [], /invalid switch in RUBYOPT: -e \(RuntimeError\)/)
+
+ ENV['RUBYOPT'] = '-T1'
+ assert_in_out_err(["--disable-gems"], "", [], /no program input from stdin allowed in tainted mode \(SecurityError\)/)
+
+ ENV['RUBYOPT'] = '-T4'
+ assert_in_out_err(["--disable-gems"], "", [], /no program input from stdin allowed in tainted mode \(SecurityError\)/)
+
+ ENV['RUBYOPT'] = '-Eus-ascii -KN'
+ assert_in_out_err(%w(-Eutf-8 -KU), "p '\u3042'") do |r, e|
+ assert_equal("\"\u3042\"", r.join.force_encoding(Encoding::UTF_8))
+ assert_equal([], e)
+ end
+
+ ensure
+ if rubyopt_orig
+ ENV['RUBYOPT'] = rubyopt_orig
+ else
+ ENV.delete('RUBYOPT')
+ end
+ end
+
+ def test_search
+ rubypath_orig = ENV['RUBYPATH']
+ path_orig = ENV['PATH']
+
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
+ t.puts "p 1"
+ t.close
+
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+
+ ENV['PATH'] = File.dirname(t.path)
+
+ assert_in_out_err(%w(-S) + [File.basename(t.path)], "", %w(1), [])
+
+ ENV['RUBYPATH'] = File.dirname(t.path)
+
+ assert_in_out_err(%w(-S) + [File.basename(t.path)], "", %w(1), [])
+ }
+
+ ensure
+ if rubypath_orig
+ ENV['RUBYPATH'] = rubypath_orig
+ else
+ ENV.delete('RUBYPATH')
+ end
+ if path_orig
+ ENV['PATH'] = path_orig
+ else
+ ENV.delete('PATH')
+ end
+ $VERBOSE = @verbose
+ end
+
+ def test_shebang
+ assert_in_out_err([], "#! /test_r_u_b_y_test_r_u_b_y_options_foobarbazqux\r\np 1\r\n",
+ [], /: no Ruby script found in input/)
+
+ assert_in_out_err([], "#! /test_r_u_b_y_test_r_u_b_y_options_foobarbazqux -foo -bar\r\np 1\r\n",
+ [], /: no Ruby script found in input/)
+
+ assert_in_out_err([{'RUBYOPT' => nil}], "#!ruby -KU -Eutf-8\r\np \"\u3042\"\r\n") do |r, e|
+ assert_equal("\"\u3042\"", r.join.force_encoding(Encoding::UTF_8))
+ assert_equal([], e)
+ end
+
+ bug4118 = '[ruby-dev:42680]'
+ assert_in_out_err(%w[], "#!/bin/sh\n""#!shebang\n""#!ruby\n""puts __LINE__\n",
+ %w[4], [], bug4118)
+ assert_in_out_err(%w[-x], "#!/bin/sh\n""#!shebang\n""#!ruby\n""puts __LINE__\n",
+ %w[4], [], bug4118)
+ end
+
+ def test_sflag
+ assert_in_out_err(%w(- -abc -def=foo -ghi-jkl -- -xyz),
+ "#!ruby -s\np [$abc, $def, $ghi_jkl, defined?($xyz)]\n",
+ ['[true, "foo", true, nil]'], [])
+
+ assert_in_out_err(%w(- -#), "#!ruby -s\n", [],
+ /invalid name for global variable - -# \(NameError\)/)
+
+ assert_in_out_err(%w(- -#=foo), "#!ruby -s\n", [],
+ /invalid name for global variable - -# \(NameError\)/)
+ end
+
+ def test_assignment_in_conditional
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
+ t.puts "if a = 1"
+ t.puts "end"
+ t.puts "0.times do"
+ t.puts " if b = 2"
+ t.puts " a += b"
+ t.puts " end"
+ t.puts "end"
+ t.flush
+ warning = ' warning: found = in conditional, should be =='
+ err = ["#{t.path}:1:#{warning}",
+ "#{t.path}:4:#{warning}",
+ ]
+ bug2136 = '[ruby-dev:39363]'
+ assert_in_out_err(["-w", t.path], "", [], err, bug2136)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err, bug2136)
+
+ t.rewind
+ t.truncate(0)
+ t.puts "if a = ''; end"
+ t.puts "if a = []; end"
+ t.puts "if a = [1]; end"
+ t.puts "if a = [a]; end"
+ t.puts "if a = {}; end"
+ t.puts "if a = {1=>2}; end"
+ t.puts "if a = {3=>a}; end"
+ t.flush
+ err = ["#{t.path}:1:#{warning}",
+ "#{t.path}:2:#{warning}",
+ "#{t.path}:3:#{warning}",
+ "#{t.path}:5:#{warning}",
+ "#{t.path}:6:#{warning}",
+ ]
+ feature4299 = '[ruby-dev:43083]'
+ assert_in_out_err(["-w", t.path], "", [], err, feature4299)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err, feature4299)
+ }
+ end
+
+ def test_indentation_check
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
+ t.puts "begin"
+ t.puts " end"
+ t.flush
+ err = ["#{t.path}:2: warning: mismatched indentations at 'end' with 'begin' at 1"]
+ assert_in_out_err(["-w", t.path], "", [], err)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err)
+
+ t.rewind
+ t.puts "# -*- warn-indent: false -*-"
+ t.puts "begin"
+ t.puts " end"
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], [], '[ruby-core:25442]')
+
+ err = ["#{t.path}:4: warning: mismatched indentations at 'end' with 'begin' at 3"]
+ t.rewind
+ t.puts "# -*- warn-indent: false -*-"
+ t.puts "# -*- warn-indent: true -*-"
+ t.puts "begin"
+ t.puts " end"
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], err, '[ruby-core:25442]')
+
+ err = ["#{t.path}:4: warning: mismatched indentations at 'end' with 'begin' at 2"]
+ t.rewind
+ t.puts "# -*- warn-indent: true -*-"
+ t.puts "begin"
+ t.puts "# -*- warn-indent: false -*-"
+ t.puts " end"
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], [], '[ruby-core:25442]')
+ }
+ end
+
+ def test_notfound
+ notexist = "./notexist.rb"
+ rubybin = EnvUtil.rubybin.dup
+ rubybin.gsub!(%r(/), '\\') if /mswin|mingw/ =~ RUBY_PLATFORM
+ rubybin = Regexp.quote(rubybin)
+ pat = Regexp.quote(notexist)
+ bug1573 = '[ruby-core:23717]'
+ assert_file.not_exist?(notexist)
+ assert_in_out_err(["-r", notexist, "-ep"], "", [], /.* -- #{pat} \(LoadError\)/, bug1573)
+ assert_in_out_err([notexist], "", [], /#{rubybin}:.* -- #{pat} \(LoadError\)/, bug1573)
+ end
+
+ def test_program_name
+ ruby = EnvUtil.rubybin
+ IO.popen([ruby, '-e', 'print $0']) {|f|
+ assert_equal('-e', f.read)
+ }
+ IO.popen([ruby, '-'], 'r+') {|f|
+ f << 'print $0'
+ f.close_write
+ assert_equal('-', f.read)
+ }
+ Dir.mktmpdir {|d|
+ n1 = File.join(d, 't1')
+ open(n1, 'w') {|f| f << 'print $0' }
+ IO.popen([ruby, n1]) {|f|
+ assert_equal(n1, f.read)
+ }
+ if File.respond_to? :symlink
+ n2 = File.join(d, 't2')
+ File.symlink(n1, n2)
+ IO.popen([ruby, n2]) {|f|
+ assert_equal(n2, f.read)
+ }
+ end
+ Dir.chdir(d) {
+ n3 = '-e'
+ open(n3, 'w') {|f| f << 'print $0' }
+ IO.popen([ruby, '--', n3]) {|f|
+ assert_equal(n3, f.read)
+ }
+ n4 = '-'
+ IO.popen([ruby, '--', n4], 'r+') {|f|
+ f << 'print $0'
+ f.close_write
+ assert_equal(n4, f.read)
+ }
+ }
+ }
+ end
+
+ if /linux|freebsd|netbsd|openbsd|darwin/ =~ RUBY_PLATFORM
+ PSCMD = EnvUtil.find_executable("ps", "-o", "command", "-p", $$.to_s) {|out| /ruby/=~out}
+ PSCMD.pop if PSCMD
+ end
+
+ def test_set_program_name
+ skip "platform dependent feature" unless defined?(PSCMD) and PSCMD
+
+ with_tmpchdir do
+ write_file("test-script", "$0 = 'hello world'; /test-script/ =~ Process.argv0 or $0 = 'Process.argv0 changed!'; sleep 60")
+
+ pid = spawn(EnvUtil.rubybin, "test-script")
+ ps = nil
+ 10.times do
+ sleep 0.1
+ ps = `#{PSCMD.join(' ')} #{pid}`
+ break if /hello world/ =~ ps
+ end
+ assert_match(/hello world/, ps)
+ Process.kill :KILL, pid
+ Process.wait(pid)
+ end
+ end
+
+ def test_setproctitle
+ skip "platform dependent feature" unless defined?(PSCMD) and PSCMD
+
+ with_tmpchdir do
+ write_file("test-script", "$_0 = $0.dup; Process.setproctitle('hello world'); $0 == $_0 or Process.setproctitle('$0 changed!'); sleep 60")
+
+ pid = spawn(EnvUtil.rubybin, "test-script")
+ ps = nil
+ 10.times do
+ sleep 0.1
+ ps = `#{PSCMD.join(' ')} #{pid}`
+ break if /hello world/ =~ ps
+ end
+ assert_match(/hello world/, ps)
+ Process.kill :KILL, pid
+ Process.wait(pid)
+ end
+ end
+
+ module SEGVTest
+ opts = {}
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ additional = /[\s\w\.\']*/
+ else
+ opts[:rlimit_core] = 0
+ additional = nil
+ end
+ ExecOptions = opts.freeze
+
+ ExpectedStderrList = [
+ %r(
+ -e:(?:1:)?\s\[BUG\]\sSegmentation\sfault.*\n
+ )x,
+ %r(
+ #{ Regexp.quote(RUBY_DESCRIPTION) }\n\n
+ )x,
+ %r(
+ (?:--\s(?:.+\n)*\n)?
+ --\sControl\sframe\sinformation\s-+\n
+ (?:c:.*\n)*
+ )x,
+ %r(
+ (?:
+ --\sRuby\slevel\sbacktrace\sinformation\s----------------------------------------\n
+ -e:1:in\s\`<main>\'\n
+ -e:1:in\s\`kill\'\n
+ )?
+ )x,
+ %r(
+ (?:
+ --\sC\slevel\sbacktrace\sinformation\s-------------------------------------------\n
+ (?:(?:.*\s)?\[0x\h+\]\n)*\n
+ )?
+ )x,
+ :*,
+ %r(
+ \[NOTE\]\n
+ You\smay\shave\sencountered\sa\sbug\sin\sthe\sRuby\sinterpreter\sor\sextension\slibraries.\n
+ Bug\sreports\sare\swelcome.\n
+ (?:.*\n)?
+ For\sdetails:\shttp:\/\/.*\.ruby-lang\.org/.*\n
+ \n
+ )x,
+ ]
+ ExpectedStderrList << additional if additional
+ end
+
+ def assert_segv(args, message=nil)
+ test_stdin = ""
+ opt = SEGVTest::ExecOptions.dup
+
+ _, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, false, true, **opt)
+ stderr.force_encoding("ASCII-8BIT")
+
+ if signo = status.termsig
+ sleep 0.1
+ EnvUtil.diagnostic_reports(Signal.signame(signo), EnvUtil.rubybin, status.pid, Time.now)
+ end
+
+ assert_pattern_list(SEGVTest::ExpectedStderrList, stderr, message)
+
+ status
+ end
+
+ def test_segv_test
+ assert_segv(["--disable-gems", "-e", "Process.kill :SEGV, $$"])
+ end
+
+ def test_segv_loaded_features
+ opts = SEGVTest::ExecOptions.dup
+
+ bug7402 = '[ruby-core:49573]'
+
+ status = Dir.mktmpdir("segv_test") do |tmpdir|
+ assert_in_out_err(['-e', 'class Bogus; def to_str; exit true; end; end',
+ '-e', '$".clear',
+ '-e', '$".unshift Bogus.new',
+ '-e', '(p $"; abort) unless $".size == 1',
+ '-e', 'Process.kill :SEGV, $$',
+ '-C', tmpdir,
+ ],
+ "", [], //,
+ nil,
+ opts)
+ end
+ if signo = status.termsig
+ sleep 0.1
+ EnvUtil.diagnostic_reports(Signal.signame(signo), EnvUtil.rubybin, status.pid, Time.now)
+ end
+ assert_not_predicate(status, :success?, "segv but success #{bug7402}")
+ end
+
+ def test_segv_setproctitle
+ bug7597 = '[ruby-dev:46786]'
+ Tempfile.create(["test_ruby_test_bug7597", ".rb"]) {|t|
+ t.write "f" * 100
+ t.flush
+ assert_segv(["--disable-gems", "-e", "$0=ARGV[0]; Process.kill :SEGV, $$", t.path], bug7597)
+ }
+ end
+
+ def test_DATA
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
+ t.puts "puts DATA.read.inspect"
+ t.puts "__END__"
+ t.puts "foo"
+ t.puts "bar"
+ t.puts "baz"
+ t.flush
+ assert_in_out_err([t.path], "", %w("foo\\nbar\\nbaz\\n"), [])
+ }
+ end
+
+ def test_unused_variable
+ feature3446 = '[ruby-dev:41620]'
+ assert_in_out_err(["-we", "a=1"], "", [], [], feature3446)
+ assert_in_out_err(["-we", "def foo\n a=1\nend"], "", [], ["-e:2: warning: assigned but unused variable - a"], feature3446)
+ assert_in_out_err(["-we", "def foo\n eval('a=1')\nend"], "", [], [], feature3446)
+ assert_in_out_err(["-we", "1.times do\n a=1\nend"], "", [], [], feature3446)
+ assert_in_out_err(["-we", "def foo\n 1.times do\n a=1\n end\nend"], "", [], ["-e:3: warning: assigned but unused variable - a"], feature3446)
+ assert_in_out_err(["-we", "def foo\n"" 1.times do |a| end\n""end"], "", [], [])
+ feature6693 = '[ruby-core:46160]'
+ assert_in_out_err(["-we", "def foo\n _a=1\nend"], "", [], [], feature6693)
+ bug7408 = '[ruby-core:49659]'
+ assert_in_out_err(["-we", "def foo\n a=1\n :a\nend"], "", [], ["-e:2: warning: assigned but unused variable - a"], bug7408)
+ feature7730 = '[ruby-core:51580]'
+ assert_in_out_err(["-w", "-"], "a=1", [], ["-:1: warning: assigned but unused variable - a"], feature7730)
+ assert_in_out_err(["-w", "-"], "eval('a=1')", [], [], feature7730)
+ end
+
+ def test_shadowing_variable
+ bug4130 = '[ruby-dev:42718]'
+ assert_in_out_err(["-we", "def foo\n"" a=1\n"" 1.times do |a| end\n"" a\n""end"],
+ "", [], ["-e:3: warning: shadowing outer local variable - a"], bug4130)
+ assert_in_out_err(["-we", "def foo\n"" a=1\n"" 1.times do |a| end\n""end"],
+ "", [],
+ ["-e:3: warning: shadowing outer local variable - a",
+ "-e:2: warning: assigned but unused variable - a",
+ ], bug4130)
+ feature6693 = '[ruby-core:46160]'
+ assert_in_out_err(["-we", "def foo\n"" _a=1\n"" 1.times do |_a| end\n""end"],
+ "", [], [], feature6693)
+ end
+
+ def test_script_from_stdin
+ begin
+ require 'pty'
+ require 'io/console'
+ rescue LoadError
+ return
+ end
+ require 'timeout'
+ result = nil
+ IO.pipe {|r, w|
+ begin
+ PTY.open {|m, s|
+ s.echo = false
+ m.print("\C-d")
+ pid = spawn(EnvUtil.rubybin, :in => s, :out => w)
+ w.close
+ assert_nothing_raised('[ruby-dev:37798]') do
+ result = Timeout.timeout(3) {r.read}
+ end
+ Process.wait pid
+ }
+ rescue RuntimeError
+ skip $!
+ end
+ }
+ assert_equal("", result, '[ruby-dev:37798]')
+ IO.pipe {|r, w|
+ PTY.open {|m, s|
+ s.echo = false
+ pid = spawn(EnvUtil.rubybin, :in => s, :out => w)
+ w.close
+ m.print("$stdin.read; p $stdin.gets\n\C-d")
+ m.print("abc\n\C-d")
+ m.print("zzz\n")
+ result = r.read
+ Process.wait pid
+ }
+ }
+ assert_equal("\"zzz\\n\"\n", result, '[ruby-core:30910]')
+ end
+
+ def test_unmatching_glob
+ bug3851 = '[ruby-core:32478]'
+ a = "a[foo"
+ Dir.mktmpdir do |dir|
+ open(File.join(dir, a), "w") {|f| f.puts("p 42")}
+ assert_in_out_err(["-C", dir, a], "", ["42"], [], bug3851)
+ File.unlink(File.join(dir, a))
+ assert_in_out_err(["-C", dir, a], "", [], /LoadError/, bug3851)
+ end
+ end
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_command_line_glob_nonascii
+ bug10555 = '[ruby-dev:48752] [Bug #10555]'
+ name = "\u{3042}.txt"
+ expected = name.encode("locale") rescue "?.txt"
+ with_tmpchdir do |dir|
+ open(name, "w") {}
+ assert_in_out_err(["-e", "puts ARGV", "?.txt"], "", [expected], [],
+ bug10555, encoding: "locale")
+ end
+ end
+
+ def test_command_line_progname_nonascii
+ bug10555 = '[ruby-dev:48752] [Bug #10555]'
+ name = "\u{3042}.rb"
+ expected = name.encode("locale") rescue "?.rb"
+ with_tmpchdir do |dir|
+ open(name, "w") {|f| f.puts "puts File.basename($0)"}
+ assert_in_out_err([name], "", [expected], [],
+ bug10555, encoding: "locale")
+ end
+ end
+
+ def test_command_line_glob_with_dir
+ bug10941 = '[ruby-core:68430] [Bug #10941]'
+ with_tmpchdir do |dir|
+ Dir.mkdir('test')
+ assert_in_out_err(["-e", "", "test/*"], "", [], [], bug10941)
+ end
+ end
+ end
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ Ougai = %W[\u{68ee}O\u{5916}.txt \u{68ee 9d0e 5916}.txt \u{68ee 9dd7 5916}.txt]
+ def test_command_line_glob_noncodepage
+ with_tmpchdir do |dir|
+ Ougai.each {|f| open(f, "w") {}}
+ assert_in_out_err(["-Eutf-8", "-e", "puts ARGV", "*"], "", Ougai, encoding: "utf-8")
+ ougai = Ougai.map {|f| f.encode("locale", replace: "?")}
+ assert_in_out_err(["-e", "puts ARGV", "*.txt"], "", ougai)
+ end
+ end
+ end
+
+ def test_script_is_directory
+ feature2408 = '[ruby-core:26925]'
+ assert_in_out_err(%w[.], "", [], /Is a directory -- \./, feature2408)
+ end
+
+ def test_pflag_gsub
+ bug7157 = '[ruby-core:47967]'
+ assert_in_out_err(['-p', '-e', 'gsub(/t.*/){"TEST"}'], %[test], %w[TEST], [], bug7157)
+ end
+
+ def test_pflag_sub
+ bug7157 = '[ruby-core:47967]'
+ assert_in_out_err(['-p', '-e', 'sub(/t.*/){"TEST"}'], %[test], %w[TEST], [], bug7157)
+ end
+
+ def assert_norun_with_rflag(opt)
+ bug10435 = "[ruby-dev:48712] [Bug #10435]: should not run with #{opt} option"
+ stderr = []
+ Tempfile.create(%w"bug10435- .rb") do |script|
+ dir, base = File.split(script.path)
+ script.puts "abort ':run'"
+ script.close
+ opts = ['-C', dir, '-r', "./#{base}", opt]
+ assert_in_out_err([*opts, '-ep']) do |_, e|
+ stderr.concat(e)
+ end
+ stderr << "---"
+ assert_in_out_err([*opts, base]) do |_, e|
+ stderr.concat(e)
+ end
+ end
+ assert_not_include(stderr, ":run", bug10435)
+ end
+
+ def test_dump_syntax_with_rflag
+ assert_norun_with_rflag('-c')
+ assert_norun_with_rflag('--dump=syntax')
+ end
+
+ def test_dump_yydebug_with_rflag
+ assert_norun_with_rflag('-y')
+ assert_norun_with_rflag('--dump=yydebug')
+ end
+
+ def test_dump_parsetree_with_rflag
+ assert_norun_with_rflag('--dump=parsetree')
+ end
+
+ def test_dump_insns_with_rflag
+ assert_norun_with_rflag('--dump=insns')
+ end
+end
diff --git a/jni/ruby/test/ruby/test_rubyvm.rb b/jni/ruby/test/ruby/test_rubyvm.rb
new file mode 100644
index 0000000..580f3be
--- /dev/null
+++ b/jni/ruby/test/ruby/test_rubyvm.rb
@@ -0,0 +1,17 @@
+require 'test/unit'
+
+class TestRubyVM < Test::Unit::TestCase
+ def test_stat
+ assert_kind_of Hash, RubyVM.stat
+ assert_kind_of Fixnum, RubyVM.stat[:global_method_state]
+
+ RubyVM.stat(stat = {})
+ assert_not_empty stat
+ assert_equal stat[:global_method_state], RubyVM.stat(:global_method_state)
+ end
+
+ def test_stat_unknown
+ assert_raise(ArgumentError){ RubyVM.stat(:unknown) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {RubyVM.stat(:"\u{30eb 30d3 30fc}")}
+ end
+end
diff --git a/jni/ruby/test/ruby/test_settracefunc.rb b/jni/ruby/test/ruby/test_settracefunc.rb
new file mode 100644
index 0000000..1dcd2cc
--- /dev/null
+++ b/jni/ruby/test/ruby/test_settracefunc.rb
@@ -0,0 +1,1355 @@
+require 'test/unit'
+
+class TestSetTraceFunc < Test::Unit::TestCase
+ def setup
+ @original_compile_option = RubyVM::InstructionSequence.compile_option
+ RubyVM::InstructionSequence.compile_option = {
+ :trace_instruction => true,
+ :specialized_instruction => false
+ }
+ @target_thread = Thread.current
+ end
+
+ def teardown
+ set_trace_func(nil)
+ RubyVM::InstructionSequence.compile_option = @original_compile_option
+ @target_thread = nil
+ end
+
+ def target_thread?
+ Thread.current == @target_thread
+ end
+
+ def test_c_call
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 2: events << [event, lineno, mid, klass] if file == name
+ 3: })
+ 4: x = 1 + 1
+ 5: set_trace_func(nil)
+ EOF
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal(["line", 4, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 4, :+, Fixnum],
+ events.shift)
+ assert_equal(["c-return", 4, :+, Fixnum],
+ events.shift)
+ assert_equal(["line", 5, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 5, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal([], events)
+ end
+
+ def test_call
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 2: events << [event, lineno, mid, klass] if file == name
+ 3: })
+ 4: def add(x, y)
+ 5: x + y
+ 6: end
+ 7: x = add(1, 1)
+ 8: set_trace_func(nil)
+ EOF
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal(["line", 4, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 4, :method_added, self.class],
+ events.shift)
+ assert_equal(["c-return", 4, :method_added, self.class],
+ events.shift)
+ assert_equal(["line", 7, __method__, self.class],
+ events.shift)
+ assert_equal(["call", 4, :add, self.class],
+ events.shift)
+ assert_equal(["line", 5, :add, self.class],
+ events.shift)
+ assert_equal(["c-call", 5, :+, Fixnum],
+ events.shift)
+ assert_equal(["c-return", 5, :+, Fixnum],
+ events.shift)
+ assert_equal(["return", 6, :add, self.class],
+ events.shift)
+ assert_equal(["line", 8, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 8, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal([], events)
+ end
+
+ def test_class
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 2: events << [event, lineno, mid, klass] if file == name
+ 3: })
+ 4: class Foo
+ 5: def bar
+ 6: end
+ 7: end
+ 8: x = Foo.new.bar
+ 9: set_trace_func(nil)
+ EOF
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal(["line", 4, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 4, :inherited, Class],
+ events.shift)
+ assert_equal(["c-return", 4, :inherited, Class],
+ events.shift)
+ assert_equal(["class", 4, nil, nil],
+ events.shift)
+ assert_equal(["line", 5, nil, nil],
+ events.shift)
+ assert_equal(["c-call", 5, :method_added, Module],
+ events.shift)
+ assert_equal(["c-return", 5, :method_added, Module],
+ events.shift)
+ assert_equal(["end", 7, nil, nil],
+ events.shift)
+ assert_equal(["line", 8, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 8, :new, Class],
+ events.shift)
+ assert_equal(["c-call", 8, :initialize, BasicObject],
+ events.shift)
+ assert_equal(["c-return", 8, :initialize, BasicObject],
+ events.shift)
+ assert_equal(["c-return", 8, :new, Class],
+ events.shift)
+ assert_equal(["call", 5, :bar, Foo],
+ events.shift)
+ assert_equal(["return", 6, :bar, Foo],
+ events.shift)
+ assert_equal(["line", 9, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 9, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal([], events)
+ end
+
+ def test_return # [ruby-dev:38701]
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 2: events << [event, lineno, mid, klass] if file == name
+ 3: })
+ 4: def meth_return(a)
+ 5: return if a
+ 6: return
+ 7: end
+ 8: meth_return(true)
+ 9: meth_return(false)
+ 10: set_trace_func(nil)
+ EOF
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal(["line", 4, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 4, :method_added, self.class],
+ events.shift)
+ assert_equal(["c-return", 4, :method_added, self.class],
+ events.shift)
+ assert_equal(["line", 8, __method__, self.class],
+ events.shift)
+ assert_equal(["call", 4, :meth_return, self.class],
+ events.shift)
+ assert_equal(["line", 5, :meth_return, self.class],
+ events.shift)
+ assert_equal(["return", 5, :meth_return, self.class],
+ events.shift)
+ assert_equal(["line", 9, :test_return, self.class],
+ events.shift)
+ assert_equal(["call", 4, :meth_return, self.class],
+ events.shift)
+ assert_equal(["line", 5, :meth_return, self.class],
+ events.shift)
+ assert_equal(["return", 7, :meth_return, self.class],
+ events.shift)
+ assert_equal(["line", 10, :test_return, self.class],
+ events.shift)
+ assert_equal(["c-call", 10, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal([], events)
+ end
+
+ def test_return2 # [ruby-core:24463]
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 2: events << [event, lineno, mid, klass] if file == name
+ 3: })
+ 4: def meth_return2
+ 5: a = 5
+ 6: return a
+ 7: end
+ 8: meth_return2
+ 9: set_trace_func(nil)
+ EOF
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal(["line", 4, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 4, :method_added, self.class],
+ events.shift)
+ assert_equal(["c-return", 4, :method_added, self.class],
+ events.shift)
+ assert_equal(["line", 8, __method__, self.class],
+ events.shift)
+ assert_equal(["call", 4, :meth_return2, self.class],
+ events.shift)
+ assert_equal(["line", 5, :meth_return2, self.class],
+ events.shift)
+ assert_equal(["line", 6, :meth_return2, self.class],
+ events.shift)
+ assert_equal(["return", 7, :meth_return2, self.class],
+ events.shift)
+ assert_equal(["line", 9, :test_return2, self.class],
+ events.shift)
+ assert_equal(["c-call", 9, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal([], events)
+ end
+
+ def test_raise
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 2: events << [event, lineno, mid, klass] if file == name
+ 3: })
+ 4: begin
+ 5: raise TypeError, "error"
+ 6: rescue TypeError
+ 7: end
+ 8: set_trace_func(nil)
+ EOF
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal(["line", 4, __method__, self.class],
+ events.shift)
+ assert_equal(["line", 5, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 5, :raise, Kernel],
+ events.shift)
+ assert_equal(["c-call", 5, :exception, Exception],
+ events.shift)
+ assert_equal(["c-call", 5, :initialize, Exception],
+ events.shift)
+ assert_equal(["c-return", 5, :initialize, Exception],
+ events.shift)
+ assert_equal(["c-return", 5, :exception, Exception],
+ events.shift)
+ assert_equal(["c-return", 5, :raise, Kernel],
+ events.shift)
+ assert_equal(["c-call", 5, :backtrace, Exception],
+ events.shift)
+ assert_equal(["c-return", 5, :backtrace, Exception],
+ events.shift)
+ assert_equal(["raise", 5, :test_raise, TestSetTraceFunc],
+ events.shift)
+ assert_equal(["c-call", 6, :===, Module],
+ events.shift)
+ assert_equal(["c-return", 6, :===, Module],
+ events.shift)
+ assert_equal(["line", 8, __method__, self.class],
+ events.shift)
+ assert_equal(["c-call", 8, :set_trace_func, Kernel],
+ events.shift)
+ assert_equal([], events)
+ end
+
+ def test_break # [ruby-core:27606] [Bug #2610]
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 2: events << [event, lineno, mid, klass] if file == name
+ 3: })
+ 4: [1,2,3].any? {|n| n}
+ 8: set_trace_func(nil)
+ EOF
+
+ [["c-return", 1, :set_trace_func, Kernel],
+ ["line", 4, __method__, self.class],
+ ["c-call", 4, :any?, Array],
+ ["line", 4, __method__, self.class],
+ ["c-return", 4, :any?, Array],
+ ["line", 5, __method__, self.class],
+ ["c-call", 5, :set_trace_func, Kernel]].each{|e|
+ assert_equal(e, events.shift)
+ }
+ end
+
+ def test_invalid_proc
+ assert_raise(TypeError) { set_trace_func(1) }
+ end
+
+ def test_raise_in_trace
+ set_trace_func proc {raise rescue nil}
+ assert_equal(42, (raise rescue 42), '[ruby-core:24118]')
+ end
+
+ def test_thread_trace
+ events = {:set => [], :add => []}
+ prc = Proc.new { |event, file, lineno, mid, binding, klass|
+ events[:set] << [event, lineno, mid, klass, :set]
+ }
+ prc = prc # suppress warning
+ prc2 = Proc.new { |event, file, lineno, mid, binding, klass|
+ events[:add] << [event, lineno, mid, klass, :add]
+ }
+ prc2 = prc2 # suppress warning
+
+ th = Thread.new do
+ th = Thread.current
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: th.set_trace_func(prc)
+ 2: th.add_trace_func(prc2)
+ 3: class ThreadTraceInnerClass
+ 4: def foo
+ 5: _x = 1 + 1
+ 6: end
+ 7: end
+ 8: ThreadTraceInnerClass.new.foo
+ 9: th.set_trace_func(nil)
+ EOF
+ end
+ th.join
+
+ [["c-return", 1, :set_trace_func, Thread, :set],
+ ["line", 2, __method__, self.class, :set],
+ ["c-call", 2, :add_trace_func, Thread, :set]].each do |e|
+ assert_equal(e, events[:set].shift)
+ end
+
+ [["c-return", 2, :add_trace_func, Thread],
+ ["line", 3, __method__, self.class],
+ ["c-call", 3, :inherited, Class],
+ ["c-return", 3, :inherited, Class],
+ ["class", 3, nil, nil],
+ ["line", 4, nil, nil],
+ ["c-call", 4, :method_added, Module],
+ ["c-return", 4, :method_added, Module],
+ ["end", 7, nil, nil],
+ ["line", 8, __method__, self.class],
+ ["c-call", 8, :new, Class],
+ ["c-call", 8, :initialize, BasicObject],
+ ["c-return", 8, :initialize, BasicObject],
+ ["c-return", 8, :new, Class],
+ ["call", 4, :foo, ThreadTraceInnerClass],
+ ["line", 5, :foo, ThreadTraceInnerClass],
+ ["c-call", 5, :+, Fixnum],
+ ["c-return", 5, :+, Fixnum],
+ ["return", 6, :foo, ThreadTraceInnerClass],
+ ["line", 9, __method__, self.class],
+ ["c-call", 9, :set_trace_func, Thread]].each do |e|
+ [:set, :add].each do |type|
+ assert_equal(e + [type], events[type].shift)
+ end
+ end
+ assert_equal([], events[:set])
+ assert_equal([], events[:add])
+ end
+
+ def test_trace_defined_method
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: class FooBar; define_method(:foobar){}; end
+ 2: fb = FooBar.new
+ 3: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 4: events << [event, lineno, mid, klass] if file == name
+ 5: })
+ 6: fb.foobar
+ 7: set_trace_func(nil)
+ EOF
+
+ [["c-return", 3, :set_trace_func, Kernel],
+ ["line", 6, __method__, self.class],
+ ["call", 1, :foobar, FooBar],
+ ["return", 6, :foobar, FooBar],
+ ["line", 7, __method__, self.class],
+ ["c-call", 7, :set_trace_func, Kernel]].each{|e|
+ assert_equal(e, events.shift)
+ }
+ end
+
+ def test_remove_in_trace
+ bug3921 = '[ruby-dev:42350]'
+ ok = false
+ func = lambda{|e, f, l, i, b, k|
+ set_trace_func(nil)
+ ok = eval("self", b)
+ }
+
+ set_trace_func(func)
+ assert_equal(self, ok, bug3921)
+ end
+
+ class << self
+ define_method(:method_added, Module.method(:method_added))
+ end
+
+ def trace_by_tracepoint *trace_events
+ events = []
+ trace = nil
+ xyzzy = nil
+ _local_var = :outer
+ raised_exc = nil
+ method = :trace_by_tracepoint
+ _get_data = lambda{|tp|
+ case tp.event
+ when :return, :c_return
+ tp.return_value
+ when :raise
+ tp.raised_exception
+ else
+ :nothing
+ end
+ }
+ _defined_class = lambda{|tp|
+ klass = tp.defined_class
+ begin
+ # If it is singleton method, then return original class
+ # to make compatible with set_trace_func().
+ # This is very ad-hoc hack. I hope I can make more clean test on it.
+ case klass.inspect
+ when /Class:TracePoint/; return TracePoint
+ when /Class:Exception/; return Exception
+ else klass
+ end
+ rescue Exception => e
+ e
+ end if klass
+ }
+
+ trace = nil
+ begin
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
+ 1: trace = TracePoint.trace(*trace_events){|tp| next if !target_thread?
+ 2: events << [tp.event, tp.lineno, tp.path, _defined_class.(tp), tp.method_id, tp.self, tp.binding.eval("_local_var"), _get_data.(tp)] if tp.path == 'xyzzy'
+ 3: }
+ 4: 1.times{|;_local_var| _local_var = :inner
+ 5: tap{}
+ 6: }
+ 7: class XYZZY
+ 8: _local_var = :XYZZY_outer
+ 9: def foo
+ 10: _local_var = :XYZZY_foo
+ 11: bar
+ 12: end
+ 13: def bar
+ 14: _local_var = :XYZZY_bar
+ 15: tap{}
+ 16: end
+ 17: end
+ 18: xyzzy = XYZZY.new
+ 19: xyzzy.foo
+ 20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
+ 21: trace.disable
+ EOF
+ self.class.class_eval{remove_const(:XYZZY)}
+ ensure
+ trace.disable if trace && trace.enabled?
+ end
+
+ answer_events = [
+ #
+ [:c_return, 1, "xyzzy", TracePoint, :trace, TracePoint, :outer, trace],
+ [:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing],
+ [:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing],
+ [:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
+ [:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
+ [:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing],
+ [:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self],
+ [:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1],
+ [:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
+ [:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing],
+ [:c_return, 7, "xyzzy", Class, :inherited, Object, :outer, nil],
+ [:class, 7, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
+ [:line, 8, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
+ [:line, 9, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
+ [:c_call, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
+ [:c_return, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
+ [:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
+ [:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
+ [:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
+ [:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
+ [:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
+ [:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing],
+ [:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing],
+ [:c_return,18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, nil],
+ [:c_return,18, "xyzzy", Class, :new, xyzzy.class, :outer, xyzzy],
+ [:line, 19, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
+ [:call, 9, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
+ [:line, 10, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
+ [:line, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, :nothing],
+ [:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
+ [:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
+ [:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
+ [:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
+ [:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
+ [:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
+ [:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
+ [:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
+ [:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing],
+ [:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing],
+ [:c_call, 20, "xyzzy", Exception, :initialize, raised_exc, :outer, :nothing],
+ [:c_return,20, "xyzzy", Exception, :initialize, raised_exc, :outer, raised_exc],
+ [:c_return,20, "xyzzy", Exception, :exception, RuntimeError, :outer, raised_exc],
+ [:c_return,20, "xyzzy", Kernel, :raise, self, :outer, nil],
+ [:c_call, 20, "xyzzy", Exception, :backtrace, raised_exc, :outer, :nothing],
+ [:c_return,20, "xyzzy", Exception, :backtrace, raised_exc, :outer, nil],
+ [:raise, 20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc],
+ [:c_call, 20, "xyzzy", Module, :===, RuntimeError,:outer, :nothing],
+ [:c_return,20, "xyzzy", Module, :===, RuntimeError,:outer, true],
+ [:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
+ [:c_call, 21, "xyzzy", TracePoint, :disable, trace, :outer, :nothing],
+ ]
+
+ return events, answer_events
+ end
+
+ def trace_by_set_trace_func
+ events = []
+ trace = nil
+ trace = trace
+ xyzzy = nil
+ xyzzy = xyzzy
+ _local_var = :outer
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
+ 1: set_trace_func(lambda{|event, file, line, id, binding, klass|
+ 2: events << [event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")] if file == 'xyzzy'
+ 3: })
+ 4: 1.times{|;_local_var| _local_var = :inner
+ 5: tap{}
+ 6: }
+ 7: class XYZZY
+ 8: _local_var = :XYZZY_outer
+ 9: def foo
+ 10: _local_var = :XYZZY_foo
+ 11: bar
+ 12: end
+ 13: def bar
+ 14: _local_var = :XYZZY_bar
+ 15: tap{}
+ 16: end
+ 17: end
+ 18: xyzzy = XYZZY.new
+ 19: xyzzy.foo
+ 20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
+ 21: set_trace_func(nil)
+ EOF
+ self.class.class_eval{remove_const(:XYZZY)}
+ return events
+ end
+
+ def test_tracepoint
+ events1, answer_events = *trace_by_tracepoint(:line, :class, :end, :call, :return, :c_call, :c_return, :raise)
+
+ ms = [events1, answer_events].map{|evs|
+ evs.map{|e|
+ "#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}"
+ }
+ }
+
+ mesg = ms[0].zip(ms[1]).map{|a, b|
+ if a != b
+ "#{a} <-> #{b}"
+ end
+ }.compact.join("\n")
+
+ answer_events.zip(events1){|answer, event|
+ assert_equal answer, event, mesg
+ }
+
+ events2 = trace_by_set_trace_func
+ events1.zip(events2){|ev1, ev2|
+ ev2[0] = ev2[0].sub('-', '_').to_sym
+ assert_equal ev1[0..2], ev2[0..2], ev1.inspect
+
+ # event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")
+ assert_equal ev1[3].nil?, ev2[3].nil? # klass
+ assert_equal ev1[4].nil?, ev2[4].nil? # id
+ assert_equal ev1[6], ev2[6] # _local_var
+ }
+
+ [:line, :class, :end, :call, :return, :c_call, :c_return, :raise].each{|event|
+ events1, answer_events = *trace_by_tracepoint(event)
+ answer_events.find_all{|e| e[0] == event}.zip(events1){|answer_line, event_line|
+ assert_equal answer_line, event_line
+ }
+ }
+ end
+
+ def test_tracepoint_object_id
+ tps = []
+ trace = TracePoint.trace(){|tp|
+ next if !target_thread?
+ tps << tp
+ }
+ tap{}
+ tap{}
+ tap{}
+ trace.disable
+
+ # passed tp is unique, `trace' object which is generated by TracePoint.trace
+ tps.each{|tp|
+ assert_equal trace, tp
+ }
+ end
+
+ def test_tracepoint_access_from_outside
+ tp_store = nil
+ trace = TracePoint.trace(){|tp|
+ next if !target_thread?
+ tp_store = tp
+ }
+ tap{}
+ trace.disable
+
+ assert_raise(RuntimeError){tp_store.lineno}
+ assert_raise(RuntimeError){tp_store.event}
+ assert_raise(RuntimeError){tp_store.path}
+ assert_raise(RuntimeError){tp_store.method_id}
+ assert_raise(RuntimeError){tp_store.defined_class}
+ assert_raise(RuntimeError){tp_store.binding}
+ assert_raise(RuntimeError){tp_store.self}
+ assert_raise(RuntimeError){tp_store.return_value}
+ assert_raise(RuntimeError){tp_store.raised_exception}
+ end
+
+ def foo
+ end
+
+ def test_tracepoint_enable
+ ary = []
+ trace = TracePoint.new(:call){|tp|
+ next if !target_thread?
+ ary << tp.method_id
+ }
+ foo
+ trace.enable{
+ foo
+ }
+ foo
+ assert_equal([:foo], ary)
+
+ trace = TracePoint.new{}
+ begin
+ assert_equal(false, trace.enable)
+ assert_equal(true, trace.enable)
+ trace.enable{}
+ assert_equal(true, trace.enable)
+ ensure
+ trace.disable
+ end
+ end
+
+ def test_tracepoint_disable
+ ary = []
+ trace = TracePoint.trace(:call){|tp|
+ next if !target_thread?
+ ary << tp.method_id
+ }
+ foo
+ trace.disable{
+ foo
+ }
+ foo
+ trace.disable
+ assert_equal([:foo, :foo], ary)
+
+ trace = TracePoint.new{}
+ trace.enable{
+ assert_equal(true, trace.disable)
+ assert_equal(false, trace.disable)
+ trace.disable{}
+ assert_equal(false, trace.disable)
+ }
+ end
+
+ def test_tracepoint_enabled
+ trace = TracePoint.trace(:call){|tp|
+ #
+ }
+ assert_equal(true, trace.enabled?)
+ trace.disable{
+ assert_equal(false, trace.enabled?)
+ trace.enable{
+ assert_equal(true, trace.enabled?)
+ }
+ }
+ trace.disable
+ assert_equal(false, trace.enabled?)
+ end
+
+ def method_test_tracepoint_return_value obj
+ obj
+ end
+
+ def test_tracepoint_return_value
+ trace = TracePoint.new(:call, :return){|tp|
+ next if !target_thread?
+ next if tp.path != __FILE__
+ case tp.event
+ when :call
+ assert_raise(RuntimeError) {tp.return_value}
+ when :return
+ assert_equal("xyzzy", tp.return_value)
+ end
+ }
+ trace.enable{
+ method_test_tracepoint_return_value "xyzzy"
+ }
+ end
+
+ class XYZZYException < Exception; end
+ def method_test_tracepoint_raised_exception err
+ raise err
+ end
+
+ def test_tracepoint_raised_exception
+ trace = TracePoint.new(:call, :return){|tp|
+ next if !target_thread?
+ case tp.event
+ when :call, :return
+ assert_raise(RuntimeError) { tp.raised_exception }
+ when :raise
+ assert_equal(XYZZYError, tp.raised_exception)
+ end
+ }
+ trace.enable{
+ begin
+ method_test_tracepoint_raised_exception XYZZYException
+ rescue XYZZYException
+ # ok
+ else
+ raise
+ end
+ }
+ end
+
+ def method_for_test_tracepoint_block
+ yield
+ end
+
+ def test_tracepoint_block
+ events = []
+ TracePoint.new(:call, :return, :c_call, :b_call, :c_return, :b_return){|tp|
+ next if !target_thread?
+ events << [
+ tp.event, tp.method_id, tp.defined_class, tp.self.class,
+ /return/ =~ tp.event ? tp.return_value : nil
+ ]
+ }.enable{
+ 1.times{
+ 3
+ }
+ method_for_test_tracepoint_block{
+ 4
+ }
+ }
+ # pp events
+ # expected_events =
+ [[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
+ [:c_call, :times, Integer, Fixnum, nil],
+ [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
+ [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 3],
+ [:c_return, :times, Integer, Fixnum, 1],
+ [:call, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
+ [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
+ [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
+ [:return, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
+ [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4]
+ ].zip(events){|expected, actual|
+ assert_equal(expected, actual)
+ }
+ end
+
+ def test_tracepoint_thread
+ events = []
+ thread_self = nil
+ created_thread = nil
+ TracePoint.new(:thread_begin, :thread_end){|tp|
+ events << [Thread.current,
+ tp.event,
+ tp.lineno, #=> 0
+ tp.path, #=> nil
+ tp.binding, #=> nil
+ tp.defined_class, #=> nil,
+ tp.self.class # tp.self return creating/ending thread
+ ]
+ }.enable{
+ created_thread = Thread.new{thread_self = self}
+ created_thread.join
+ }
+ events.reject!{|i| i[0] != created_thread}
+ assert_equal(self, thread_self)
+ assert_equal([created_thread, :thread_begin, 0, nil, nil, nil, Thread], events[0])
+ assert_equal([created_thread, :thread_end, 0, nil, nil, nil, Thread], events[1])
+ assert_equal(2, events.size)
+ end
+
+ def test_tracepoint_inspect
+ events = []
+ trace = TracePoint.new{|tp|
+ next if !target_thread?
+ events << [tp.event, tp.inspect]
+ }
+ assert_equal("#<TracePoint:disabled>", trace.inspect)
+ trace.enable{
+ assert_equal("#<TracePoint:enabled>", trace.inspect)
+ Thread.new{}.join
+ }
+ assert_equal("#<TracePoint:disabled>", trace.inspect)
+ events.each{|(ev, str)|
+ case ev
+ when :line
+ assert_match(/ in /, str)
+ when :call, :c_call
+ assert_match(/call \`/, str) # #<TracePoint:c_call `inherited'@../trunk/test.rb:11>
+ when :return, :c_return
+ assert_match(/return \`/, str) # #<TracePoint:return `m'@../trunk/test.rb:3>
+ when /thread/
+ assert_match(/\#<Thread:/, str) # #<TracePoint:thread_end of #<Thread:0x87076c0>>
+ else
+ assert_match(/\#<TracePoint:/, str)
+ end
+ }
+ end
+
+ def test_tracepoint_exception_at_line
+ assert_raise(RuntimeError) do
+ TracePoint.new(:line) {
+ next if !target_thread?
+ raise
+ }.enable {
+ 1
+ }
+ end
+ end
+
+ def test_tracepoint_exception_at_return
+ assert_nothing_raised(Timeout::Error, 'infinite trace') do
+ assert_normal_exit('def m; end; TracePoint.new(:return) {raise}.enable {m}', '', timeout: 3)
+ end
+ end
+
+ def test_tracepoint_with_multithreads
+ assert_nothing_raised do
+ TracePoint.new{
+ 10.times{
+ Thread.pass
+ }
+ }.enable do
+ (1..10).map{
+ Thread.new{
+ 1000.times{
+ }
+ }
+ }.each{|th|
+ th.join
+ }
+ end
+ end
+ end
+
+ class FOO_ERROR < RuntimeError; end
+ class BAR_ERROR < RuntimeError; end
+ def m1_test_trace_point_at_return_when_exception
+ m2_test_trace_point_at_return_when_exception
+ end
+ def m2_test_trace_point_at_return_when_exception
+ raise BAR_ERROR
+ end
+
+ def test_trace_point_at_return_when_exception
+ bug_7624 = '[ruby-core:51128] [ruby-trunk - Bug #7624]'
+ TracePoint.new{|tp|
+ next if !target_thread?
+ if tp.event == :return &&
+ tp.method_id == :m2_test_trace_point_at_return_when_exception
+ raise FOO_ERROR
+ end
+ }.enable do
+ assert_raise(FOO_ERROR, bug_7624) do
+ m1_test_trace_point_at_return_when_exception
+ end
+ end
+
+ bug_7668 = '[Bug #7668]'
+ ary = []
+ trace = TracePoint.new{|tp|
+ next if !target_thread?
+ ary << tp.event
+ raise
+ }
+ begin
+ trace.enable{
+ 1.times{
+ raise
+ }
+ }
+ rescue
+ assert_equal([:b_call, :b_return], ary, bug_7668)
+ end
+ end
+
+ def m1_for_test_trace_point_binding_in_ifunc(arg)
+ arg + nil
+ rescue
+ end
+
+ def m2_for_test_trace_point_binding_in_ifunc(arg)
+ arg.inject(:+)
+ rescue
+ end
+
+ def test_trace_point_binding_in_ifunc
+ bug7774 = '[ruby-dev:46908]'
+ src = %q{
+ tp = TracePoint.new(:raise) do |tp|
+ tp.binding
+ end
+ tp.enable do
+ obj = Object.new
+ class << obj
+ include Enumerable
+ def each
+ yield 1
+ end
+ end
+ %s
+ end
+ }
+ assert_normal_exit src % %q{obj.zip({}) {}}, bug7774
+ assert_normal_exit src % %q{
+ require 'continuation'
+ begin
+ c = nil
+ obj.sort_by {|x| callcc {|c2| c ||= c2 }; x }
+ c.call
+ rescue RuntimeError
+ end
+ }, bug7774
+
+ # TracePoint
+ tp_b = nil
+ TracePoint.new(:raise) do |tp|
+ next if !target_thread?
+ tp_b = tp.binding
+ end.enable do
+ m1_for_test_trace_point_binding_in_ifunc(0)
+ assert_equal(self, eval('self', tp_b), '[ruby-dev:46960]')
+
+ m2_for_test_trace_point_binding_in_ifunc([0, nil])
+ assert_equal(self, eval('self', tp_b), '[ruby-dev:46960]')
+ end
+
+ # set_trace_func
+ stf_b = nil
+ set_trace_func ->(event, file, line, id, binding, klass) do
+ stf_b = binding if event == 'raise'
+ end
+ begin
+ m1_for_test_trace_point_binding_in_ifunc(0)
+ assert_equal(self, eval('self', stf_b), '[ruby-dev:46960]')
+
+ m2_for_test_trace_point_binding_in_ifunc([0, nil])
+ assert_equal(self, eval('self', stf_b), '[ruby-dev:46960]')
+ ensure
+ set_trace_func(nil)
+ end
+ end
+
+ def test_trace_point_binding_after_break
+ bug10689 = '[ruby-dev:48797]'
+ assert_in_out_err([], <<-INPUT, [], [], bug10689)
+ class Bug
+ include Enumerable
+
+ def each
+ [0].each do
+ yield
+ end
+ end
+ end
+
+ TracePoint.trace(:c_return) do |tp|
+ tp.binding
+ end
+
+ Bug.new.all? { false }
+ INPUT
+ end
+
+ def test_tracepoint_b_return_with_next
+ n = 0
+ TracePoint.new(:b_return){
+ next if !target_thread?
+ n += 1
+ }.enable{
+ 3.times{
+ next
+ } # 3 times b_retun
+ } # 1 time b_return
+
+ assert_equal 4, n
+ end
+
+ def test_tracepoint_b_return_with_lambda
+ n = 0
+ TracePoint.new(:b_return){
+ next if !target_thread?
+ n+=1
+ }.enable{
+ lambda{
+ return
+ }.call # n += 1 #=> 1
+ 3.times{
+ lambda{
+ return # n += 3 #=> 4
+ }.call
+ } # n += 3 #=> 7
+ begin
+ lambda{
+ raise
+ }.call # n += 1 #=> 8
+ rescue
+ # ignore
+ end # n += 1 #=> 9
+ }
+
+ assert_equal 9, n
+ end
+
+ def test_isolated_raise_in_trace
+ bug9088 = '[ruby-dev:47793] [Bug #9088]'
+ assert_ruby_status([], <<-END, bug9088)
+ set_trace_func proc {raise rescue nil}
+ 1.times {break}
+ END
+ end
+
+ def test_a_call
+ events = []
+ TracePoint.new(:a_call){|tp|
+ next if !target_thread?
+ events << tp.event
+ }.enable{
+ 1.times{
+ 3
+ }
+ method_for_test_tracepoint_block{
+ 4
+ }
+ }
+ assert_equal([
+ :b_call,
+ :c_call,
+ :b_call,
+ :call,
+ :b_call,
+ ], events)
+ end
+
+ def test_a_return
+ events = []
+ TracePoint.new(:a_return){|tp|
+ next if !target_thread?
+ events << tp.event
+ }.enable{
+ 1.times{
+ 3
+ }
+ method_for_test_tracepoint_block{
+ 4
+ }
+ }
+ assert_equal([
+ :b_return,
+ :c_return,
+ :b_return,
+ :return,
+ :b_return
+ ], events)
+ end
+
+ def test_const_missing
+ bug59398 = '[ruby-core:59398]'
+ events = []
+ assert !defined?(MISSING_CONSTANT_59398)
+ TracePoint.new(:c_call, :c_return, :call, :return){|tp|
+ next if !target_thread?
+ next unless tp.defined_class == Module
+ # rake/ext/module.rb aliases :const_missing and Ruby uses the aliased name
+ # but this only happens when running the full test suite
+ events << [tp.event,tp.method_id] if tp.method_id == :const_missing || tp.method_id == :rake_original_const_missing
+ }.enable{
+ MISSING_CONSTANT_59398 rescue nil
+ }
+ if events.map{|e|e[1]}.include?(:rake_original_const_missing)
+ assert_equal([
+ [:call, :const_missing],
+ [:c_call, :rake_original_const_missing],
+ [:c_return, :rake_original_const_missing],
+ [:return, :const_missing],
+ ], events, bug59398)
+ else
+ assert_equal([
+ [:c_call, :const_missing],
+ [:c_return, :const_missing]
+ ], events, bug59398)
+ end
+ end
+
+ class AliasedRubyMethod
+ def foo; 1; end;
+ alias bar foo
+ end
+ def test_aliased_ruby_method
+ events = []
+ aliased = AliasedRubyMethod.new
+ TracePoint.new(:call, :return){|tp|
+ next if !target_thread?
+ events << [tp.event, tp.method_id]
+ }.enable{
+ aliased.bar
+ }
+ assert_equal([
+ [:call, :foo],
+ [:return, :foo]
+ ], events, "should use original method name for tracing ruby methods")
+ end
+ class AliasedCMethod < Hash
+ alias original_size size
+ def size; original_size; end
+ end
+
+ def test_aliased_c_method
+ events = []
+ aliased = AliasedCMethod.new
+ TracePoint.new(:call, :return, :c_call, :c_return){|tp|
+ next if !target_thread?
+ events << [tp.event, tp.method_id]
+ }.enable{
+ aliased.size
+ }
+ assert_equal([
+ [:call, :size],
+ [:c_call, :original_size],
+ [:c_return, :original_size],
+ [:return, :size]
+ ], events, "should use alias method name for tracing c methods")
+ end
+
+ def test_method_missing
+ bug59398 = '[ruby-core:59398]'
+ events = []
+ assert !respond_to?(:missing_method_59398)
+ TracePoint.new(:c_call, :c_return, :call, :return){|tp|
+ next if !target_thread?
+ next unless tp.defined_class == BasicObject
+ # rake/ext/module.rb aliases :const_missing and Ruby uses the aliased name
+ # but this only happens when running the full test suite
+ events << [tp.event,tp.method_id] if tp.method_id == :method_missing
+ }.enable{
+ missing_method_59398 rescue nil
+ }
+ assert_equal([
+ [:c_call, :method_missing],
+ [:c_return, :method_missing]
+ ], events, bug59398)
+ end
+
+ class C9759
+ define_method(:foo){
+ raise
+ }
+ end
+
+ def test_define_method_on_exception
+ events = []
+ obj = C9759.new
+ TracePoint.new(:call, :return){|tp|
+ next unless target_thread?
+ events << [tp.event, tp.method_id]
+ }.enable{
+ obj.foo rescue nil
+ }
+ assert_equal([[:call, :foo], [:return, :foo]], events, 'Bug #9759')
+
+ events = []
+ begin
+ set_trace_func(lambda{|event, file, lineno, mid, binding, klass|
+ next unless target_thread?
+ case event
+ when 'call', 'return'
+ events << [event, mid]
+ end
+ })
+ obj.foo rescue nil
+ set_trace_func(nil)
+
+ assert_equal([['call', :foo], ['return', :foo]], events, 'Bug #9759')
+ ensure
+ end
+ end
+
+ def test_recursive
+ assert_ruby_status [], %q{
+ stack = []
+ TracePoint.new(:c_call){|tp|
+ p 2
+ stack << tp.method_id
+ }.enable{
+ p 1
+ }
+ raise if stack != [:p, :hash, :inspect]
+ }, '[Bug #9940]'
+ end
+
+ def method_prefix event
+ case event
+ when :call, :return
+ :n
+ when :c_call, :c_return
+ :c
+ when :b_call, :b_return
+ :b
+ end
+ end
+
+ def method_label tp
+ "#{method_prefix(tp.event)}##{tp.method_id}"
+ end
+
+ def assert_consistent_call_return message='', check_events: nil
+ check_events ||= %i(a_call a_return)
+ call_stack = []
+
+ TracePoint.new(*check_events){|tp|
+ next unless target_thread?
+
+ case tp.event.to_s
+ when /call/
+ call_stack << method_label(tp)
+ when /return/
+ frame = call_stack.pop
+ assert_equal(frame, method_label(tp))
+ end
+ }.enable do
+ yield
+ end
+
+ assert_equal true, call_stack.empty?
+ end
+
+ def method_test_rescue_should_not_cause_b_return
+ begin
+ raise
+ rescue
+ return
+ end
+ end
+
+ def method_test_ensure_should_not_cause_b_return
+ begin
+ raise
+ ensure
+ return
+ end
+ end
+
+ def test_rescue_and_ensure_should_not_cause_b_return
+ assert_consistent_call_return '[Bug #9957]' do
+ method_test_rescue_should_not_cause_b_return
+ begin
+ method_test_ensure_should_not_cause_b_return
+ rescue
+ # ignore
+ end
+ end
+ end
+
+ define_method(:method_test_argument_error_on_bmethod){|correct_key: 1|}
+
+ def test_argument_error_on_bmethod
+ assert_consistent_call_return '[Bug #9959]' do
+ begin
+ method_test_argument_error_on_bmethod(wrong_key: 2)
+ rescue => e
+ # ignore
+ end
+ end
+ end
+
+ def test_rb_rescue
+ assert_consistent_call_return '[Bug #9961]' do
+ begin
+ -Numeric.new
+ rescue => e
+ # ignore
+ end
+ end
+ end
+
+ def test_b_call_with_redo
+ assert_consistent_call_return '[Bug #9964]' do
+ i = 0
+ 1.times{
+ break if (i+=1) > 10
+ redo
+ }
+ end
+ end
+
+ def test_no_duplicate_line_events
+ lines = []
+ dummy = []
+
+ TracePoint.new(:line){|tp|
+ next unless target_thread?
+ lines << tp.lineno
+ }.enable{
+ dummy << (1) + (2)
+ dummy << (1) + (2)
+ }
+ assert_equal [__LINE__ - 3, __LINE__ - 2], lines, 'Bug #10449'
+ end
+
+ class Bug10724
+ def initialize
+ loop{return}
+ end
+ end
+
+ def test_throwing_return_with_finish_frame
+ target_th = Thread.current
+ evs = []
+
+ TracePoint.new(:call, :return){|tp|
+ return if Thread.current != target_th
+ evs << tp.event
+ }.enable{
+ a = Bug10724.new
+ }
+
+ assert_equal([:call, :return], evs)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_signal.rb b/jni/ruby/test/ruby/test_signal.rb
new file mode 100644
index 0000000..4a59aa6
--- /dev/null
+++ b/jni/ruby/test/ruby/test_signal.rb
@@ -0,0 +1,294 @@
+require 'test/unit'
+require 'timeout'
+require 'tempfile'
+
+class TestSignal < Test::Unit::TestCase
+ def test_signal
+ begin
+ x = 0
+ oldtrap = Signal.trap(:INT) {|sig| x = 2 }
+ Process.kill :INT, Process.pid
+ 10.times do
+ break if 2 == x
+ sleep 0.1
+ end
+ assert_equal 2, x
+
+ Signal.trap(:INT) { raise "Interrupt" }
+ assert_raise_with_message(RuntimeError, /Interrupt/) {
+ Process.kill :INT, Process.pid
+ sleep 0.1
+ }
+ ensure
+ Signal.trap :INT, oldtrap if oldtrap
+ end
+ end if Process.respond_to?(:kill)
+
+ def test_signal_process_group
+ bug4362 = '[ruby-dev:43169]'
+ assert_nothing_raised(bug4362) do
+ pid = Process.spawn(EnvUtil.rubybin, '-e', 'sleep 10', :pgroup => true)
+ Process.kill(:"-TERM", pid)
+ Process.waitpid(pid)
+ assert_equal(true, $?.signaled?)
+ assert_equal(Signal.list["TERM"], $?.termsig)
+ end
+ end if Process.respond_to?(:kill) and
+ Process.respond_to?(:pgroup) # for mswin32
+
+ def test_exit_action
+ if Signal.list[sig = "USR1"]
+ term = :TERM
+ else
+ sig = "INT"
+ term = :KILL
+ end
+ IO.popen([EnvUtil.rubybin, '-e', <<-"End"], 'r+') do |io|
+ Signal.trap(:#{sig}, "EXIT")
+ STDOUT.syswrite("a")
+ Thread.start { sleep(2) }
+ STDIN.sysread(4096)
+ End
+ pid = io.pid
+ io.sysread(1)
+ sleep 0.1
+ assert_nothing_raised("[ruby-dev:26128]") {
+ Process.kill(term, pid)
+ begin
+ Timeout.timeout(3) {
+ Process.waitpid pid
+ }
+ rescue Timeout::Error
+ if term
+ Process.kill(term, pid)
+ term = (:KILL if term != :KILL)
+ retry
+ end
+ raise
+ end
+ }
+ end
+ end if Process.respond_to?(:kill)
+
+ def test_invalid_signal_name
+ assert_raise(ArgumentError) { Process.kill(:XXXXXXXXXX, $$) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.kill("\u{30eb 30d3 30fc}", $$) }
+ end if Process.respond_to?(:kill)
+
+ def test_signal_exception
+ assert_raise(ArgumentError) { SignalException.new }
+ assert_raise(ArgumentError) { SignalException.new(-1) }
+ assert_raise(ArgumentError) { SignalException.new(:XXXXXXXXXX) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { SignalException.new("\u{30eb 30d3 30fc}") }
+ Signal.list.each do |signm, signo|
+ next if signm == "EXIT"
+ assert_equal(SignalException.new(signm).signo, signo)
+ assert_equal(SignalException.new(signm.to_sym).signo, signo)
+ assert_equal(SignalException.new(signo).signo, signo)
+ end
+ end
+
+ def test_interrupt
+ assert_raise(Interrupt) { raise Interrupt.new }
+ end
+
+ def test_signal2
+ begin
+ x = false
+ oldtrap = Signal.trap(:INT) {|sig| x = true }
+ GC.start
+
+ assert_raise(ArgumentError) { Process.kill }
+
+ Timeout.timeout(10) do
+ x = false
+ Process.kill(SignalException.new(:INT).signo, $$)
+ sleep(0.01) until x
+
+ x = false
+ Process.kill("INT", $$)
+ sleep(0.01) until x
+
+ x = false
+ Process.kill("SIGINT", $$)
+ sleep(0.01) until x
+
+ x = false
+ o = Object.new
+ def o.to_str; "SIGINT"; end
+ Process.kill(o, $$)
+ sleep(0.01) until x
+ end
+
+ assert_raise(ArgumentError) { Process.kill(Object.new, $$) }
+
+ ensure
+ Signal.trap(:INT, oldtrap) if oldtrap
+ end
+ end if Process.respond_to?(:kill)
+
+ def test_trap
+ begin
+ oldtrap = Signal.trap(:INT) {|sig| }
+
+ assert_raise(ArgumentError) { Signal.trap }
+
+ assert_raise(SecurityError) do
+ s = proc {}.taint
+ Signal.trap(:INT, s)
+ end
+
+ # FIXME!
+ Signal.trap(:INT, nil)
+ Signal.trap(:INT, "")
+ Signal.trap(:INT, "SIG_IGN")
+ Signal.trap(:INT, "IGNORE")
+
+ Signal.trap(:INT, "SIG_DFL")
+ Signal.trap(:INT, "SYSTEM_DEFAULT")
+
+ Signal.trap(:INT, "EXIT")
+
+ Signal.trap(:INT, "xxxxxx")
+ Signal.trap(:INT, "xxxx")
+
+ Signal.trap(SignalException.new(:INT).signo, "SIG_DFL")
+
+ assert_raise(ArgumentError) { Signal.trap(-1, "xxxx") }
+
+ o = Object.new
+ def o.to_str; "SIGINT"; end
+ Signal.trap(o, "SIG_DFL")
+
+ assert_raise(ArgumentError) { Signal.trap("XXXXXXXXXX", "SIG_DFL") }
+
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Signal.trap("\u{30eb 30d3 30fc}", "SIG_DFL") }
+ ensure
+ Signal.trap(:INT, oldtrap) if oldtrap
+ end
+ end if Process.respond_to?(:kill)
+
+ %w"KILL STOP".each do |sig|
+ if Signal.list.key?(sig)
+ define_method("test_trap_uncatchable_#{sig}") do
+ assert_raise(Errno::EINVAL, "SIG#{sig} is not allowed to be caught") { Signal.trap(sig) {} }
+ end
+ end
+ end
+
+ def test_sigexit
+ assert_in_out_err([], 'Signal.trap(:EXIT) {print "OK"}', ["OK"])
+ assert_in_out_err([], 'Signal.trap("EXIT") {print "OK"}', ["OK"])
+ assert_in_out_err([], 'Signal.trap(:SIGEXIT) {print "OK"}', ["OK"])
+ assert_in_out_err([], 'Signal.trap("SIGEXIT") {print "OK"}', ["OK"])
+ assert_in_out_err([], 'Signal.trap(0) {print "OK"}', ["OK"])
+ end
+
+ def test_kill_immediately_before_termination
+ Signal.list[sig = "USR1"] or sig = "INT"
+ assert_in_out_err(["-e", <<-"end;"], "", %w"foo")
+ Signal.trap(:#{sig}) { STDOUT.syswrite("foo") }
+ Process.kill :#{sig}, $$
+ end;
+ end if Process.respond_to?(:kill)
+
+ def test_trap_system_default
+ assert_separately([], <<-End)
+ trap(:QUIT, "SYSTEM_DEFAULT")
+ assert_equal("SYSTEM_DEFAULT", trap(:QUIT, "DEFAULT"))
+ End
+ end if Signal.list.key?('QUIT')
+
+ def test_reserved_signal
+ assert_raise(ArgumentError) {
+ Signal.trap(:SEGV) {}
+ }
+ assert_raise(ArgumentError) {
+ Signal.trap(:BUS) {}
+ }
+ assert_raise(ArgumentError) {
+ Signal.trap(:ILL) {}
+ }
+ assert_raise(ArgumentError) {
+ Signal.trap(:FPE) {}
+ }
+ assert_raise(ArgumentError) {
+ Signal.trap(:VTALRM) {}
+ }
+ end
+
+ def test_signame
+ 10.times do
+ IO.popen([EnvUtil.rubybin, "-e", <<EOS, :err => File::NULL]) do |child|
+ Signal.trap("INT") do |signo|
+ signame = Signal.signame(signo)
+ Marshal.dump(signame, STDOUT)
+ STDOUT.flush
+ exit 0
+ end
+ Process.kill("INT", $$)
+ sleep 1 # wait signal deliver
+EOS
+
+ signame = Marshal.load(child)
+ assert_equal(signame, "INT")
+ end
+ end
+ end if Process.respond_to?(:kill)
+
+ def test_trap_puts
+ assert_in_out_err([], <<-INPUT, ["a"*10000], [])
+ Signal.trap(:INT) {
+ # for enable internal io mutex
+ STDOUT.sync = false
+ # larger than internal io buffer
+ print "a"*10000
+ }
+ Process.kill :INT, $$
+ sleep 0.1
+ INPUT
+ end if Process.respond_to?(:kill)
+
+ def test_hup_me
+ # [Bug #7951] [ruby-core:52864]
+ # This is MRI specific spec. ruby has no guarantee
+ # that signal will be deliverd synchronously.
+ # This ugly workaround was introduced to don't break
+ # compatibility against silly example codes.
+ assert_separately([], <<-RUBY)
+ trap(:HUP, "DEFAULT")
+ assert_raise(SignalException) {
+ Process.kill('HUP', Process.pid)
+ }
+ RUBY
+ bug8137 = '[ruby-dev:47182] [Bug #8137]'
+ assert_nothing_raised(bug8137) {
+ Timeout.timeout(1) {
+ Process.kill(0, Process.pid)
+ }
+ }
+ end if Process.respond_to?(:kill) and Signal.list.key?('HUP')
+
+ def test_ignored_interrupt
+ bug9820 = '[ruby-dev:48203] [Bug #9820]'
+ assert_separately(['-', bug9820], <<-'end;') # begin
+ bug = ARGV.shift
+ trap(:INT, "IGNORE")
+ assert_nothing_raised(SignalException, bug) do
+ Process.kill(:INT, $$)
+ end
+ end;
+
+ if trap = Signal.list['TRAP']
+ bug9820 = '[ruby-dev:48592] [Bug #9820]'
+ status = assert_in_out_err(['-e', 'Process.kill(:TRAP, $$)'])
+ assert_predicate(status, :signaled?, bug9820)
+ assert_equal(trap, status.termsig, bug9820)
+ end
+
+ if Signal.list['CONT']
+ bug9820 = '[ruby-dev:48606] [Bug #9820]'
+ assert_ruby_status(['-e', 'Process.kill(:CONT, $$)'])
+ end
+ end if Process.respond_to?(:kill)
+end
diff --git a/jni/ruby/test/ruby/test_sleep.rb b/jni/ruby/test/ruby/test_sleep.rb
new file mode 100644
index 0000000..29490a7
--- /dev/null
+++ b/jni/ruby/test/ruby/test_sleep.rb
@@ -0,0 +1,16 @@
+require 'test/unit'
+require 'etc'
+
+class TestSleep < Test::Unit::TestCase
+ def test_sleep_5sec
+ GC.disable
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ sleep 5
+ slept = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
+ bottom = 5.0
+ assert_operator(slept, :>=, bottom)
+ assert_operator(slept, :<=, 6.0, "[ruby-core:18015]: longer than expected")
+ ensure
+ GC.enable
+ end
+end
diff --git a/jni/ruby/test/ruby/test_sprintf.rb b/jni/ruby/test/ruby/test_sprintf.rb
new file mode 100644
index 0000000..15c809c
--- /dev/null
+++ b/jni/ruby/test/ruby/test_sprintf.rb
@@ -0,0 +1,412 @@
+require 'test/unit'
+
+class TestSprintf < Test::Unit::TestCase
+ def test_positional
+ assert_equal(" 00001", sprintf("%*1$.*2$3$d", 10, 5, 1))
+ end
+
+ def test_binary
+ assert_equal("0", sprintf("%b", 0))
+ assert_equal("1", sprintf("%b", 1))
+ assert_equal("10", sprintf("%b", 2))
+ assert_equal("..1", sprintf("%b", -1))
+
+ assert_equal(" 0", sprintf("%4b", 0))
+ assert_equal(" 1", sprintf("%4b", 1))
+ assert_equal(" 10", sprintf("%4b", 2))
+ assert_equal(" ..1", sprintf("%4b", -1))
+
+ assert_equal("0000", sprintf("%04b", 0))
+ assert_equal("0001", sprintf("%04b", 1))
+ assert_equal("0010", sprintf("%04b", 2))
+ assert_equal("..11", sprintf("%04b", -1))
+
+ assert_equal("0000", sprintf("%.4b", 0))
+ assert_equal("0001", sprintf("%.4b", 1))
+ assert_equal("0010", sprintf("%.4b", 2))
+ assert_equal("..11", sprintf("%.4b", -1))
+
+ assert_equal(" 0000", sprintf("%6.4b", 0))
+ assert_equal(" 0001", sprintf("%6.4b", 1))
+ assert_equal(" 0010", sprintf("%6.4b", 2))
+ assert_equal(" ..11", sprintf("%6.4b", -1))
+
+ assert_equal(" 0", sprintf("%#4b", 0))
+ assert_equal(" 0b1", sprintf("%#4b", 1))
+ assert_equal("0b10", sprintf("%#4b", 2))
+ assert_equal("0b..1", sprintf("%#4b", -1))
+
+ assert_equal("0000", sprintf("%#04b", 0))
+ assert_equal("0b01", sprintf("%#04b", 1))
+ assert_equal("0b10", sprintf("%#04b", 2))
+ assert_equal("0b..1", sprintf("%#04b", -1))
+
+ assert_equal("0000", sprintf("%#.4b", 0))
+ assert_equal("0b0001", sprintf("%#.4b", 1))
+ assert_equal("0b0010", sprintf("%#.4b", 2))
+ assert_equal("0b..11", sprintf("%#.4b", -1))
+
+ assert_equal(" 0000", sprintf("%#6.4b", 0))
+ assert_equal("0b0001", sprintf("%#6.4b", 1))
+ assert_equal("0b0010", sprintf("%#6.4b", 2))
+ assert_equal("0b..11", sprintf("%#6.4b", -1))
+
+ assert_equal("+0", sprintf("%+b", 0))
+ assert_equal("+1", sprintf("%+b", 1))
+ assert_equal("+10", sprintf("%+b", 2))
+ assert_equal("-1", sprintf("%+b", -1))
+
+ assert_equal(" +0", sprintf("%+4b", 0))
+ assert_equal(" +1", sprintf("%+4b", 1))
+ assert_equal(" +10", sprintf("%+4b", 2))
+ assert_equal(" -1", sprintf("%+4b", -1))
+
+ assert_equal("+000", sprintf("%+04b", 0))
+ assert_equal("+001", sprintf("%+04b", 1))
+ assert_equal("+010", sprintf("%+04b", 2))
+ assert_equal("-001", sprintf("%+04b", -1))
+
+ assert_equal("+0000", sprintf("%+.4b", 0))
+ assert_equal("+0001", sprintf("%+.4b", 1))
+ assert_equal("+0010", sprintf("%+.4b", 2))
+ assert_equal("-0001", sprintf("%+.4b", -1))
+
+ assert_equal(" +0000", sprintf("%+6.4b", 0))
+ assert_equal(" +0001", sprintf("%+6.4b", 1))
+ assert_equal(" +0010", sprintf("%+6.4b", 2))
+ assert_equal(" -0001", sprintf("%+6.4b", -1))
+ end
+
+ def test_nan
+ nan = 0.0 / 0.0
+ assert_equal("NaN", sprintf("%f", nan))
+ assert_equal("NaN", sprintf("%-f", nan))
+ assert_equal("+NaN", sprintf("%+f", nan))
+
+ assert_equal(" NaN", sprintf("%8f", nan))
+ assert_equal("NaN ", sprintf("%-8f", nan))
+ assert_equal(" +NaN", sprintf("%+8f", nan))
+
+ assert_equal(" NaN", sprintf("%08f", nan))
+ assert_equal("NaN ", sprintf("%-08f", nan))
+ assert_equal(" +NaN", sprintf("%+08f", nan))
+
+ assert_equal(" NaN", sprintf("% 8f", nan))
+ assert_equal(" NaN ", sprintf("%- 8f", nan))
+ assert_equal(" +NaN", sprintf("%+ 8f", nan))
+
+ assert_equal(" NaN", sprintf("% 08f", nan))
+ assert_equal(" NaN ", sprintf("%- 08f", nan))
+ assert_equal(" +NaN", sprintf("%+ 08f", nan))
+ end
+
+ def test_inf
+ inf = 1.0 / 0.0
+ assert_equal("Inf", sprintf("%f", inf))
+ assert_equal("Inf", sprintf("%-f", inf))
+ assert_equal("+Inf", sprintf("%+f", inf))
+
+ assert_equal(" Inf", sprintf("%8f", inf))
+ assert_equal("Inf ", sprintf("%-8f", inf))
+ assert_equal(" +Inf", sprintf("%+8f", inf))
+
+ assert_equal(" Inf", sprintf("%08f", inf))
+ assert_equal("Inf ", sprintf("%-08f", inf))
+ assert_equal(" +Inf", sprintf("%+08f", inf))
+
+ assert_equal(" Inf", sprintf("% 8f", inf))
+ assert_equal(" Inf ", sprintf("%- 8f", inf))
+ assert_equal(" +Inf", sprintf("%+ 8f", inf))
+
+ assert_equal(" Inf", sprintf("% 08f", inf))
+ assert_equal(" Inf ", sprintf("%- 08f", inf))
+ assert_equal(" +Inf", sprintf("%+ 08f", inf))
+
+ assert_equal("-Inf", sprintf("%f", -inf))
+ assert_equal("-Inf", sprintf("%-f", -inf))
+ assert_equal("-Inf", sprintf("%+f", -inf))
+
+ assert_equal(" -Inf", sprintf("%8f", -inf))
+ assert_equal("-Inf ", sprintf("%-8f", -inf))
+ assert_equal(" -Inf", sprintf("%+8f", -inf))
+
+ assert_equal(" -Inf", sprintf("%08f", -inf))
+ assert_equal("-Inf ", sprintf("%-08f", -inf))
+ assert_equal(" -Inf", sprintf("%+08f", -inf))
+
+ assert_equal(" -Inf", sprintf("% 8f", -inf))
+ assert_equal("-Inf ", sprintf("%- 8f", -inf))
+ assert_equal(" -Inf", sprintf("%+ 8f", -inf))
+
+ assert_equal(" -Inf", sprintf("% 08f", -inf))
+ assert_equal("-Inf ", sprintf("%- 08f", -inf))
+ assert_equal(" -Inf", sprintf("%+ 08f", -inf))
+ assert_equal('..f00000000',
+ sprintf("%x", -2**32), '[ruby-dev:32351]')
+ assert_equal("..101111111111111111111111111111111",
+ sprintf("%b", -2147483649), '[ruby-dev:32365]')
+ assert_equal(" Inf", sprintf("% e", inf), '[ruby-dev:34002]')
+ end
+
+ def test_rational
+ assert_match(/\A0\.10+\z/, sprintf("%.60f", 0.1r))
+ assert_match(/\A0\.010+\z/, sprintf("%.60f", 0.01r))
+ assert_match(/\A0\.0010+\z/, sprintf("%.60f", 0.001r))
+ assert_match(/\A0\.3+\z/, sprintf("%.60f", 1/3r))
+ assert_match(/\A1\.20+\z/, sprintf("%.60f", 1.2r))
+
+ 0.upto(9) do |len|
+ -1.upto(9) do |prec|
+ ['', '+', '-', ' ', '0', '+0', '-0', ' 0', '+ ', '- ', '+ 0', '- 0'].each do |flags|
+ fmt = "%#{flags}#{len > 0 ? len : ''}#{prec >= 0 ? ".#{prec}" : ''}f"
+ [0, 0.1, 0.01, 0.001, 1.001, 100.0, 100.001, 10000000000.0, 0.00000000001, 1/3r, 2/3r, 1.2r, 10r].each do |num|
+ assert_equal(sprintf(fmt, num.to_f), sprintf(fmt, num.to_r), "sprintf(#{fmt.inspect}, #{num.inspect}.to_r)")
+ assert_equal(sprintf(fmt, -num.to_f), sprintf(fmt, -num.to_r), "sprintf(#{fmt.inspect}, #{(-num).inspect}.to_r)") if num > 0
+ end
+ end
+ end
+ end
+ end
+
+ def test_hash
+ options = {:capture=>/\d+/}
+ assert_equal("with options {:capture=>/\\d+/}", sprintf("with options %p" % options))
+ end
+
+ def test_invalid
+ # Star precision before star width:
+ assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.**d", 5, 10, 1)}
+
+ # Precision before flags and width:
+ assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.5+05d", 5)}
+ assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.5 5d", 5)}
+
+ # Overriding a star width with a numeric one:
+ assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%*1s", 5, 1)}
+
+ # Width before flags:
+ assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%5+0d", 1)}
+ assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%5 0d", 1)}
+
+ # Specifying width multiple times:
+ assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%50+30+20+10+5d", 5)}
+ assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%50 30 20 10 5d", 5)}
+
+ # Specifying the precision multiple times with negative star arguments:
+ assert_raise(ArgumentError, "[ruby-core:11570]") {sprintf("%.*.*.*.*f", -1, -1, -1, 5, 1)}
+
+ # Null bytes after percent signs are removed:
+ assert_equal("%\0x hello", sprintf("%\0x hello"), "[ruby-core:11571]")
+
+ assert_raise(ArgumentError, "[ruby-core:11573]") {sprintf("%.25555555555555555555555555555555555555s", "hello")}
+
+ assert_raise(ArgumentError) { sprintf("%\1", 1) }
+ assert_raise(ArgumentError) { sprintf("%!", 1) }
+ assert_raise(ArgumentError) { sprintf("%1$1$d", 1) }
+ assert_raise(ArgumentError) { sprintf("%0%") }
+
+ assert_raise_with_message(ArgumentError, /unnumbered\(1\) mixed with numbered/) { sprintf("%1$*d", 3) }
+ assert_raise_with_message(ArgumentError, /unnumbered\(1\) mixed with numbered/) { sprintf("%1$.*d", 3) }
+
+ verbose, $VERBOSE = $VERBOSE, nil
+ assert_nothing_raised { sprintf("", 1) }
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def test_float
+ assert_equal("36893488147419111424",
+ sprintf("%20.0f", 36893488147419107329.0))
+ assert_equal(" Inf", sprintf("% 0e", 1.0/0.0), "moved from btest/knownbug")
+ assert_equal(" -0.", sprintf("%#10.0f", -0.5), "[ruby-dev:42552]")
+ # out of spec
+ #assert_equal("0x1p+2", sprintf('%.0a', Float('0x1.fp+1')), "[ruby-dev:42551]")
+ #assert_equal("-0x1.0p+2", sprintf('%.1a', Float('-0x1.ffp+1')), "[ruby-dev:42551]")
+ end
+
+ def test_float_hex
+ assert_equal("-0x0p+0", sprintf("%a", -0.0))
+ assert_equal("0x0p+0", sprintf("%a", 0.0))
+ assert_equal("0x1p-1", sprintf("%a", 0.5))
+ assert_equal("0x1p+0", sprintf("%a", 1.0))
+ assert_equal("0x1p+1", sprintf("%a", 2.0))
+ assert_equal("0x1p+10", sprintf("%a", 1024))
+ assert_equal("0x1.23456p+789", sprintf("%a", 3.704450999893983e+237))
+ assert_equal("0x1p-1074", sprintf("%a", 4.9e-324))
+ assert_equal("Inf", sprintf("%e", Float::INFINITY))
+ assert_equal("Inf", sprintf("%E", Float::INFINITY))
+ assert_equal("NaN", sprintf("%e", Float::NAN))
+ assert_equal("NaN", sprintf("%E", Float::NAN))
+
+ assert_equal(" -0x1p+0", sprintf("%10a", -1))
+ assert_equal(" -0x1.8p+0", sprintf("%10a", -1.5))
+ assert_equal(" -0x1.4p+0", sprintf("%10a", -1.25))
+ assert_equal(" -0x1.2p+0", sprintf("%10a", -1.125))
+ assert_equal(" -0x1.1p+0", sprintf("%10a", -1.0625))
+ assert_equal("-0x1.08p+0", sprintf("%10a", -1.03125))
+
+ bug3962 = '[ruby-core:32841]'
+ assert_equal("-0x0001p+0", sprintf("%010a", -1), bug3962)
+ assert_equal("-0x01.8p+0", sprintf("%010a", -1.5), bug3962)
+ assert_equal("-0x01.4p+0", sprintf("%010a", -1.25), bug3962)
+ assert_equal("-0x01.2p+0", sprintf("%010a", -1.125), bug3962)
+ assert_equal("-0x01.1p+0", sprintf("%010a", -1.0625), bug3962)
+ assert_equal("-0x1.08p+0", sprintf("%010a", -1.03125), bug3962)
+
+ bug3964 = '[ruby-core:32848]'
+ assert_equal("0x000000000000000p+0", sprintf("%020a", 0), bug3964)
+ assert_equal("0x000000000000001p+0", sprintf("%020a", 1), bug3964)
+ assert_equal("-0x00000000000001p+0", sprintf("%020a", -1), bug3964)
+ assert_equal("0x00000000000000.p+0", sprintf("%#020a", 0), bug3964)
+
+ bug3965 = '[ruby-dev:42431]'
+ assert_equal("0x1.p+0", sprintf("%#.0a", 1), bug3965)
+ assert_equal("0x00000000000000.p+0", sprintf("%#020a", 0), bug3965)
+ assert_equal("0x0000.0000000000p+0", sprintf("%#020.10a", 0), bug3965)
+
+ bug3979 = '[ruby-dev:42453]'
+ assert_equal(" 0x0.000p+0", sprintf("%20.3a", 0), bug3979)
+ assert_equal(" 0x1.000p+0", sprintf("%20.3a", 1), bug3979)
+ end
+
+ BSIZ = 120
+
+ def test_skip
+ assert_equal(" " * BSIZ + "1", sprintf(" " * BSIZ + "%d", 1))
+ end
+
+ def test_char
+ assert_equal("a", sprintf("%c", 97))
+ assert_equal("a", sprintf("%c", ?a))
+ assert_raise(ArgumentError) { sprintf("%c", sprintf("%c%c", ?a, ?a)) }
+ assert_equal(" " * (BSIZ - 1) + "a", sprintf(" " * (BSIZ - 1) + "%c", ?a))
+ assert_equal(" " * (BSIZ - 1) + "a", sprintf(" " * (BSIZ - 1) + "%-1c", ?a))
+ assert_equal(" " * BSIZ + "a", sprintf("%#{ BSIZ + 1 }c", ?a))
+ assert_equal("a" + " " * BSIZ, sprintf("%-#{ BSIZ + 1 }c", ?a))
+ end
+
+ def test_string
+ assert_equal("foo", sprintf("%s", "foo"))
+ assert_equal("fo", sprintf("%.2s", "foo"))
+ assert_equal(" " * BSIZ, sprintf("%s", " " * BSIZ))
+ assert_equal(" " * (BSIZ - 1) + "foo", sprintf("%#{ BSIZ - 1 + 3 }s", "foo"))
+ assert_equal(" " * BSIZ + "foo", sprintf("%#{ BSIZ + 3 }s", "foo"))
+ assert_equal("foo" + " " * BSIZ, sprintf("%-#{ BSIZ + 3 }s", "foo"))
+ end
+
+ def test_integer
+ assert_equal("01", sprintf("%#o", 1))
+ assert_equal("0x1", sprintf("%#x", 1))
+ assert_equal("0X1", sprintf("%#X", 1))
+ assert_equal("0b1", sprintf("%#b", 1))
+ assert_equal("0B1", sprintf("%#B", 1))
+ assert_equal("1", sprintf("%d", 1.0))
+ assert_equal("4294967296", sprintf("%d", (2**32).to_f))
+ assert_equal("-2147483648", sprintf("%d", -(2**31).to_f))
+ assert_equal("18446744073709551616", sprintf("%d", (2**64).to_f))
+ assert_equal("-9223372036854775808", sprintf("%d", -(2**63).to_f))
+ assert_equal("1", sprintf("%d", "1"))
+ o = Object.new; def o.to_int; 1; end
+ assert_equal("1", sprintf("%d", o))
+ assert_equal("+1", sprintf("%+d", 1))
+ assert_equal(" 1", sprintf("% d", 1))
+ assert_equal("..f", sprintf("%x", -1))
+ assert_equal("..7", sprintf("%o", -1))
+ one = (2**32).coerce(1).first
+ mone = (2**32).coerce(-1).first
+ assert_equal("+1", sprintf("%+d", one))
+ assert_equal(" 1", sprintf("% d", one))
+ assert_equal("..f", sprintf("%x", mone))
+ assert_equal("..7", sprintf("%o", mone))
+ assert_equal(" " * BSIZ + "1", sprintf("%#{ BSIZ + 1 }d", one))
+ assert_equal(" " * (BSIZ - 1) + "1", sprintf(" " * (BSIZ - 1) + "%d", 1))
+ end
+
+ def test_float2
+ inf = 1.0 / 0.0
+ assert_equal(" " * BSIZ + "Inf", sprintf("%#{ BSIZ + 3 }.1f", inf))
+ assert_equal("+Inf", sprintf("%+-f", inf))
+ assert_equal(" " * BSIZ + "1.0", sprintf("%#{ BSIZ + 3 }.1f", 1.0))
+ end
+
+ class T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+ end
+
+ def test_star
+ assert_equal("-1 ", sprintf("%*d", -3, -1))
+ assert_raise_with_message(ArgumentError, /width too big/) {
+ sprintf("%*999999999999999999999999999999999999999999999999999999999999$d", 1)
+ }
+ assert_raise_with_message(ArgumentError, /prec too big/) {
+ sprintf("%.*999999999999999999999999999999999999999999999999999999999999$d", 1)
+ }
+ end
+
+ def test_escape
+ assert_equal("%" * BSIZ, sprintf("%%" * BSIZ))
+ end
+
+ def test_rb_sprintf
+ assert_match(/^#<TestSprintf::T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789:0x[0-9a-f]+>$/,
+ T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.new.inspect)
+ end
+
+ def test_negative_hex
+ s1 = sprintf("%0x", -0x40000000)
+ s2 = sprintf("%0x", -0x40000001)
+ b1 = (/\.\./ =~ s1) != nil
+ b2 = (/\.\./ =~ s2) != nil
+ assert_equal(b1, b2, "[ruby-dev:33224]")
+ end
+
+ def test_named_untyped
+ assert_equal("value", sprintf("%<key>s", :key => "value"))
+ assert_raise_with_message(ArgumentError, "named<key2> after numbered") {sprintf("%1$<key2>s", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named<key2> after unnumbered(2)") {sprintf("%s%s%<key2>s", "foo", "bar", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named<key2> after <key>") {sprintf("%<key><key2>s", :key => "value")}
+ assert_raise_with_message(KeyError, "key<key> not found") {sprintf("%<key>s", {})}
+ end
+
+ def test_named_untyped_enc
+ key = "\u{3012}"
+ [Encoding::UTF_8, Encoding::EUC_JP].each do |enc|
+ k = key.encode(enc)
+ e = assert_raise_with_message(ArgumentError, "named<#{k}> after numbered") {sprintf("%1$<#{k}>s", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named<#{k}> after unnumbered(2)") {sprintf("%s%s%<#{k}>s", "foo", "bar", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named<#{k}> after <key>") {sprintf("%<key><#{k}>s", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named<key> after <#{k}>") {sprintf("%<#{k}><key>s", k.to_sym => "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(KeyError, "key<#{k}> not found") {sprintf("%<#{k}>s", {})}
+ assert_equal(enc, e.message.encoding)
+ end
+ end
+
+ def test_named_typed
+ assert_equal("value", sprintf("%{key}", :key => "value"))
+ assert_raise_with_message(ArgumentError, "named{key2} after numbered") {sprintf("%1${key2}", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named{key2} after unnumbered(2)") {sprintf("%s%s%{key2}", "foo", "bar", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named{key2} after <key>") {sprintf("%<key>{key2}", :key => "value")}
+ assert_equal("value{key2}", sprintf("%{key}{key2}", :key => "value"))
+ assert_raise_with_message(KeyError, "key{key} not found") {sprintf("%{key}", {})}
+ end
+
+ def test_named_typed_enc
+ key = "\u{3012}"
+ [Encoding::UTF_8, Encoding::EUC_JP].each do |enc|
+ k = key.encode(enc)
+ e = assert_raise_with_message(ArgumentError, "named{#{k}} after numbered") {sprintf("%1${#{k}}s", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named{#{k}} after unnumbered(2)") {sprintf("%s%s%{#{k}}s", "foo", "bar", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named{#{k}} after <key>") {sprintf("%<key>{#{k}}s", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named{key} after <#{k}>") {sprintf("%<#{k}>{key}s", k.to_sym => "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(KeyError, "key{#{k}} not found") {sprintf("%{#{k}}", {})}
+ assert_equal(enc, e.message.encoding)
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_sprintf_comb.rb b/jni/ruby/test/ruby/test_sprintf_comb.rb
new file mode 100644
index 0000000..c58ddf4
--- /dev/null
+++ b/jni/ruby/test/ruby/test_sprintf_comb.rb
@@ -0,0 +1,553 @@
+require 'test/unit'
+require_relative 'allpairs'
+
+class TestSprintfComb < Test::Unit::TestCase
+ VS = [
+ #-0x1000000000000000000000000000000000000000000000002,
+ #-0x1000000000000000000000000000000000000000000000001,
+ #-0x1000000000000000000000000000000000000000000000000,
+ #-0xffffffffffffffffffffffffffffffffffffffffffffffff,
+ #-0x1000000000000000000000002,
+ #-0x1000000000000000000000001,
+ #-0x1000000000000000000000000,
+ #-0xffffffffffffffffffffffff,
+ -0x10000000000000002,
+ -0x10000000000000001,
+ -0x10000000000000000,
+ -0xffffffffffffffff,
+ -0x4000000000000002,
+ -0x4000000000000001,
+ -0x4000000000000000,
+ -0x3fffffffffffffff,
+ -0x100000002,
+ -0x100000001,
+ -0x100000000,
+ -0xffffffff,
+ #-0xc717a08d, # 0xc717a08d * 0x524b2245 = 0x4000000000000001
+ -0x80000002,
+ -0x80000001,
+ -0x80000000,
+ -0x7fffffff,
+ #-0x524b2245,
+ -0x40000002,
+ -0x40000001,
+ -0x40000000,
+ -0x3fffffff,
+ #-0x10002,
+ #-0x10001,
+ #-0x10000,
+ #-0xffff,
+ #-0x8101, # 0x8101 * 0x7f01 = 0x40000001
+ #-0x8002,
+ #-0x8001,
+ #-0x8000,
+ #-0x7fff,
+ #-0x7f01,
+ #-65,
+ #-64,
+ #-63,
+ #-62,
+ #-33,
+ #-32,
+ #-31,
+ #-30,
+ -3,
+ -2,
+ -1,
+ 0,
+ 1,
+ 2,
+ 3,
+ #30,
+ #31,
+ #32,
+ #33,
+ #62,
+ #63,
+ #64,
+ #65,
+ #0x7f01,
+ #0x7ffe,
+ #0x7fff,
+ #0x8000,
+ #0x8001,
+ #0x8101,
+ #0xfffe,
+ #0xffff,
+ #0x10000,
+ #0x10001,
+ 0x3ffffffe,
+ 0x3fffffff,
+ 0x40000000,
+ 0x40000001,
+ #0x524b2245,
+ 0x7ffffffe,
+ 0x7fffffff,
+ 0x80000000,
+ 0x80000001,
+ #0xc717a08d,
+ 0xfffffffe,
+ 0xffffffff,
+ 0x100000000,
+ 0x100000001,
+ 0x3ffffffffffffffe,
+ 0x3fffffffffffffff,
+ 0x4000000000000000,
+ 0x4000000000000001,
+ 0xfffffffffffffffe,
+ 0xffffffffffffffff,
+ 0x10000000000000000,
+ 0x10000000000000001,
+ #0xffffffffffffffffffffffff,
+ #0x1000000000000000000000000,
+ #0x1000000000000000000000001,
+ #0xffffffffffffffffffffffffffffffffffffffffffffffff,
+ #0x1000000000000000000000000000000000000000000000000,
+ #0x1000000000000000000000000000000000000000000000001
+ ]
+ VS.reverse!
+
+ FLAGS = [['', ' '], ['', '#'], ['', '+'], ['', '-'], ['', '0']]
+
+ def self.combination(*args, &b)
+ #AllPairs.exhaustive_each(*args, &b)
+ AllPairs.each(*args, &b)
+ end
+
+ def emu_int(format, v)
+ /\A%( )?(\#)?(\+)?(-)?(0)?(\d+)?(?:\.(\d*))?(.)\z/ =~ format
+ sp = $1
+ hs = $2
+ pl = $3
+ mi = $4
+ zr = $5
+ width = $6
+ precision = $7
+ type = $8
+ width = width.to_i if width
+ precision = precision.to_i if precision
+ prefix = ''
+
+ zr = nil if precision
+
+ zr = nil if mi && zr
+
+ case type
+ when 'B'
+ radix = 2
+ digitmap = {0 => '0', 1 => '1'}
+ complement = !pl && !sp
+ prefix = '0B' if hs && v != 0
+ when 'b'
+ radix = 2
+ digitmap = {0 => '0', 1 => '1'}
+ complement = !pl && !sp
+ prefix = '0b' if hs && v != 0
+ when 'd'
+ radix = 10
+ digitmap = {}
+ 10.times {|i| digitmap[i] = i.to_s }
+ complement = false
+ when 'o'
+ radix = 8
+ digitmap = {}
+ 8.times {|i| digitmap[i] = i.to_s }
+ complement = !pl && !sp
+ when 'X'
+ radix = 16
+ digitmap = {}
+ 16.times {|i| digitmap[i] = i.to_s(16).upcase }
+ complement = !pl && !sp
+ prefix = '0X' if hs && v != 0
+ when 'x'
+ radix = 16
+ digitmap = {}
+ 16.times {|i| digitmap[i] = i.to_s(16) }
+ complement = !pl && !sp
+ prefix = '0x' if hs && v != 0
+ else
+ raise "unexpected type: #{type.inspect}"
+ end
+
+ digits = []
+ abs = v.abs
+ sign = ''
+ while 0 < abs
+ digits << (abs % radix)
+ abs /= radix
+ end
+
+ if v < 0
+ if complement
+ digits.map! {|d| radix-1 - d }
+ carry = 1
+ digits.each_index {|i|
+ digits[i] += carry
+ carry = 0
+ if radix <= digits[i]
+ digits[i] -= radix
+ carry = 1
+ end
+ }
+ if digits.last != radix-1
+ digits << (radix-1)
+ end
+ sign = '..'
+ else
+ sign = '-'
+ end
+ else
+ if pl
+ sign = '+'
+ elsif sp
+ sign = ' '
+ end
+ end
+
+ dlen = digits.length
+ dlen += 2 if sign == '..'
+
+ if v < 0 && complement
+ d = radix - 1
+ else
+ d = 0
+ end
+ if precision
+ if dlen < precision
+ (precision - dlen).times {
+ digits << d
+ }
+ end
+ else
+ if dlen == 0
+ digits << d
+ end
+ end
+ if type == 'o' && hs
+ if digits.empty? || digits.last != d
+ digits << d
+ end
+ end
+
+ digits.reverse!
+
+ str = digits.map {|digit| digitmap[digit] }.join
+
+ pad = ''
+ nlen = prefix.length + sign.length + str.length
+ if width && nlen < width
+ len = width - nlen
+ if zr
+ if complement && v < 0
+ pad = digitmap[radix-1] * len
+ else
+ pad = '0' * len
+ end
+ else
+ pad = ' ' * len
+ end
+ end
+
+ if / / =~ pad
+ if sign == '..'
+ str = prefix + sign + str
+ else
+ str = sign + prefix + str
+ end
+ if mi
+ str = str + pad
+ else
+ str = pad + str
+ end
+ else
+ if sign == '..'
+ str = prefix + sign + pad + str
+ else
+ str = sign + prefix + pad + str
+ end
+ end
+
+ str
+ end
+
+ def self.assertions_format_integer(format)
+ proc {
+ VS.each {|v|
+ r = sprintf format, v
+ e = emu_int format, v
+ if true
+ assert_equal(e, r, "sprintf(#{format.dump}, #{v})")
+ else
+ if e != r
+ puts "#{e.dump}\t#{r.dump}\tsprintf(#{format.dump}, #{v})"
+ end
+ end
+ }
+ }
+ end
+
+ combination(%w[B b d o X x],
+ [nil, 0, 5, 20],
+ ["", ".", ".0", ".8", ".20"],
+ *FLAGS) {|type, width, precision, sp, hs, pl, mi, zr|
+ format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
+ define_method("test_format_integer(#{format})", assertions_format_integer(format))
+ }
+
+ FLOAT_VALUES = [
+ -1e100,
+ -123456789.0,
+ -1.0,
+ -0.0,
+ 0.0,
+ 0.01,
+ 1/3.0,
+ 2/3.0,
+ 1.0,
+ 2.0,
+ 9.99999999,
+ 123456789.0,
+ 1e100,
+ Float::MAX,
+ Float::MIN,
+ Float::EPSILON,
+ 1+Float::EPSILON,
+ #1-Float::EPSILON/2,
+ 10 + Float::EPSILON*10,
+ 10 - Float::EPSILON*5,
+ 1.0/0.0,
+ -1.0/0.0,
+ 0.0/0.0,
+ ]
+
+ def split_float10(v)
+ if v == 0
+ if 1/v < 0
+ sign = -1
+ v = -v
+ else
+ sign = 1
+ end
+ else
+ if v < 0
+ sign = -1
+ v = -v
+ else
+ sign = 1
+ end
+ end
+ exp = 0
+ int = v.floor
+ v -= int
+ while v != 0
+ v *= 2
+ int *= 2
+ i = v.floor
+ v -= i
+ int += i
+ exp -= 1
+ end
+ int *= 5 ** (-exp)
+ [sign, int, exp]
+ end
+
+ def emu_e(sp, hs, pl, mi, zr, width, precision, type, v, sign, int, exp)
+ precision = 6 unless precision
+ if int == 0
+ if precision == 0 && !hs
+ result = "0#{type}+00"
+ else
+ result = "0." + "0" * precision + "#{type}+00"
+ end
+ else
+ if int < 10**precision
+ int *= 10**precision
+ exp -= precision
+ end
+ digits = int.to_s.length
+ discard = digits - (precision+1)
+ if discard != 0
+ q, r = int.divmod(10**discard)
+ if r < 10**discard / 2
+ int = q
+ exp += discard
+ elsif (q+1).to_s.length == q.to_s.length
+ int = q+1
+ exp += discard
+ else
+ discard += 1
+ q, r = int.divmod(10**discard)
+ int = q+1
+ exp += discard
+ end
+ end
+ ints = int.to_s
+ frac = ints[1..-1]
+ result = ints[0,1]
+ e = exp + frac.length
+ if precision != 0 || hs
+ result << "."
+ if precision != 0
+ result << frac
+ end
+ end
+ result << type
+ if e == 0
+ if v.abs < 1
+ result << '-00' # glibc 2.7 causes '+00'
+ else
+ result << '+00'
+ end
+ else
+ result << sprintf("%+03d", e)
+ end
+ result
+ end
+ result
+ end
+
+ def emu_f(sp, hs, pl, mi, zr, width, precision, type, sign, int, exp)
+ precision = 6 unless precision
+ if int == 0
+ if precision == 0 && !hs
+ result = '0'
+ else
+ result = '0.' + '0' * precision
+ end
+ else
+ if -precision < exp
+ int *= 10 ** (precision+exp)
+ exp = -precision
+ end
+ if exp < -precision
+ discard = -exp - precision
+ q, r = int.divmod(10**discard)
+ if 10**discard / 2 <= r
+ q += 1
+ end
+ int = q
+ exp += discard
+ end
+ result = int.to_s
+ if result.length <= precision
+ result = '0' * (precision+1 - result.length) + result
+ end
+ if precision != 0 || hs
+ if precision == 0
+ result << '.'
+ else
+ result[-precision,0] = '.'
+ end
+ end
+ end
+ result
+ end
+
+ def emu_float(format, v)
+ /\A%( )?(\#)?(\+)?(-)?(0)?(\d+)?(?:\.(\d*))?(.)\z/ =~ format
+ sp = $1
+ hs = $2
+ pl = $3
+ mi = $4
+ zr = $5
+ width = $6
+ precision = $7
+ type = $8
+ width = width.to_i if width
+ precision = precision.to_i if precision
+
+ zr = nil if mi && zr
+
+ if v.infinite?
+ sign = v < 0 ? -1 : 1
+ int = :inf
+ hs = zr = nil
+ elsif v.nan?
+ sign = 1
+ int = :nan
+ hs = zr = nil
+ else
+ sign, int, exp = split_float10(v)
+ end
+
+ if sign < 0
+ sign = '-'
+ elsif sign == 0
+ sign = ''
+ elsif pl
+ sign = '+'
+ elsif sp
+ sign = ' '
+ else
+ sign = ''
+ end
+
+ if v.nan?
+ result = 'NaN'
+ elsif v.infinite?
+ result = 'Inf'
+ else
+ case type
+ when /[eE]/
+ result = emu_e(sp, hs, pl, mi, zr, width, precision, type, v, sign, int, exp)
+ when /f/
+ result = emu_f(sp, hs, pl, mi, zr, width, precision, type, sign, int, exp)
+ when /[gG]/
+ precision = 6 unless precision
+ precision = 1 if precision == 0
+ r = emu_e(sp, hs, pl, mi, zr, width, precision-1, type.tr('gG', 'eE'), v, sign, int, exp)
+ /[eE]([+-]\d+)/ =~ r
+ e = $1.to_i
+ if e < -4 || precision <= e
+ result = r
+ else
+ result = emu_f(sp, hs, pl, mi, zr, width, precision-1-e, type, sign, int, exp)
+ end
+ result.sub!(/\.[0-9]*/) { $&.sub(/\.?0*\z/, '') } if !hs
+ else
+ raise "unexpected type: #{type}"
+ end
+ end
+
+ pad = ''
+ if width && sign.length + result.length < width
+ if zr
+ pad = '0' * (width - sign.length - result.length)
+ else
+ pad = ' ' * (width - sign.length - result.length)
+ end
+ end
+ if mi
+ sign + result + pad
+ elsif zr
+ sign + pad + result
+ else
+ pad + sign + result
+ end
+
+ end
+
+ def self.assertions_format_float(format)
+ proc {
+ FLOAT_VALUES.each {|v|
+ r = sprintf format, v
+ e = emu_float format, v
+ if true
+ assert_equal(e, r, "sprintf(#{format.dump}, #{'%.20g' % v})")
+ else
+ if e != r
+ puts "#{e.dump}\t#{r.dump}\tsprintf(#{format.dump}, #{'%.20g' % v})"
+ end
+ end
+ }
+ }
+ end
+
+ combination(%w[e E f g G],
+ [nil, 0, 5, 20],
+ ["", ".", ".0", ".8", ".20", ".200", ".9999"],
+ *FLAGS) {|type, width, precision, sp, hs, pl, mi, zr|
+ format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
+ define_method("test_format_float(#{format})", assertions_format_float(format))
+ }
+end
diff --git a/jni/ruby/test/ruby/test_string.rb b/jni/ruby/test/ruby/test_string.rb
new file mode 100644
index 0000000..4cd6afd
--- /dev/null
+++ b/jni/ruby/test/ruby/test_string.rb
@@ -0,0 +1,2314 @@
+require 'test/unit'
+
+# use of $= is deprecated after 1.7.1
+def pre_1_7_1
+end
+
+class TestString < Test::Unit::TestCase
+ ENUMERATOR_WANTARRAY = RUBY_VERSION >= "3.0.0"
+
+ def initialize(*args)
+ @cls = String
+ @aref_re_nth = true
+ @aref_re_silent = false
+ @aref_slicebang_silent = true
+ super
+ end
+
+ def S(str)
+ @cls.new(str)
+ end
+
+ def test_s_new
+ assert_equal("RUBY", S("RUBY"))
+ end
+
+ def test_AREF # '[]'
+ assert_equal("A", S("AooBar")[0])
+ assert_equal("B", S("FooBaB")[-1])
+ assert_equal(nil, S("FooBar")[6])
+ assert_equal(nil, S("FooBar")[-7])
+
+ assert_equal(S("Foo"), S("FooBar")[0,3])
+ assert_equal(S("Bar"), S("FooBar")[-3,3])
+ assert_equal(S(""), S("FooBar")[6,2])
+ assert_equal(nil, S("FooBar")[-7,10])
+
+ assert_equal(S("Foo"), S("FooBar")[0..2])
+ assert_equal(S("Foo"), S("FooBar")[0...3])
+ assert_equal(S("Bar"), S("FooBar")[-3..-1])
+ assert_equal(S(""), S("FooBar")[6..2])
+ assert_equal(nil, S("FooBar")[-10..-7])
+
+ assert_equal(S("Foo"), S("FooBar")[/^F../])
+ assert_equal(S("Bar"), S("FooBar")[/..r$/])
+ assert_equal(nil, S("FooBar")[/xyzzy/])
+ assert_equal(nil, S("FooBar")[/plugh/])
+
+ assert_equal(S("Foo"), S("FooBar")[S("Foo")])
+ assert_equal(S("Bar"), S("FooBar")[S("Bar")])
+ assert_equal(nil, S("FooBar")[S("xyzzy")])
+ assert_equal(nil, S("FooBar")[S("plugh")])
+
+ if @aref_re_nth
+ assert_equal(S("Foo"), S("FooBar")[/([A-Z]..)([A-Z]..)/, 1])
+ assert_equal(S("Bar"), S("FooBar")[/([A-Z]..)([A-Z]..)/, 2])
+ assert_equal(nil, S("FooBar")[/([A-Z]..)([A-Z]..)/, 3])
+ assert_equal(S("Bar"), S("FooBar")[/([A-Z]..)([A-Z]..)/, -1])
+ assert_equal(S("Foo"), S("FooBar")[/([A-Z]..)([A-Z]..)/, -2])
+ assert_equal(nil, S("FooBar")[/([A-Z]..)([A-Z]..)/, -3])
+ end
+
+ o = Object.new
+ def o.to_int; 2; end
+ assert_equal("o", "foo"[o])
+
+ assert_raise(ArgumentError) { "foo"[] }
+ end
+
+ def test_ASET # '[]='
+ s = S("FooBar")
+ s[0] = S('A')
+ assert_equal(S("AooBar"), s)
+
+ s[-1]= S('B')
+ assert_equal(S("AooBaB"), s)
+ assert_raise(IndexError) { s[-7] = S("xyz") }
+ assert_equal(S("AooBaB"), s)
+ s[0] = S("ABC")
+ assert_equal(S("ABCooBaB"), s)
+
+ s = S("FooBar")
+ s[0,3] = S("A")
+ assert_equal(S("ABar"),s)
+ s[0] = S("Foo")
+ assert_equal(S("FooBar"), s)
+ s[-3,3] = S("Foo")
+ assert_equal(S("FooFoo"), s)
+ assert_raise(IndexError) { s[7,3] = S("Bar") }
+ assert_raise(IndexError) { s[-7,3] = S("Bar") }
+
+ s = S("FooBar")
+ s[0..2] = S("A")
+ assert_equal(S("ABar"), s)
+ s[1..3] = S("Foo")
+ assert_equal(S("AFoo"), s)
+ s[-4..-4] = S("Foo")
+ assert_equal(S("FooFoo"), s)
+ assert_raise(RangeError) { s[7..10] = S("Bar") }
+ assert_raise(RangeError) { s[-7..-10] = S("Bar") }
+
+ s = S("FooBar")
+ s[/^F../]= S("Bar")
+ assert_equal(S("BarBar"), s)
+ s[/..r$/] = S("Foo")
+ assert_equal(S("BarFoo"), s)
+ if @aref_re_silent
+ s[/xyzzy/] = S("None")
+ assert_equal(S("BarFoo"), s)
+ else
+ assert_raise(IndexError) { s[/xyzzy/] = S("None") }
+ end
+ if @aref_re_nth
+ s[/([A-Z]..)([A-Z]..)/, 1] = S("Foo")
+ assert_equal(S("FooFoo"), s)
+ s[/([A-Z]..)([A-Z]..)/, 2] = S("Bar")
+ assert_equal(S("FooBar"), s)
+ assert_raise(IndexError) { s[/([A-Z]..)([A-Z]..)/, 3] = "None" }
+ s[/([A-Z]..)([A-Z]..)/, -1] = S("Foo")
+ assert_equal(S("FooFoo"), s)
+ s[/([A-Z]..)([A-Z]..)/, -2] = S("Bar")
+ assert_equal(S("BarFoo"), s)
+ assert_raise(IndexError) { s[/([A-Z]..)([A-Z]..)/, -3] = "None" }
+ end
+
+ s = S("FooBar")
+ s[S("Foo")] = S("Bar")
+ assert_equal(S("BarBar"), s)
+
+ pre_1_7_1 do
+ s = S("FooBar")
+ s[S("Foo")] = S("xyz")
+ assert_equal(S("xyzBar"), s)
+
+ $= = true
+ s = S("FooBar")
+ s[S("FOO")] = S("Bar")
+ assert_equal(S("BarBar"), s)
+ s[S("FOO")] = S("xyz")
+ assert_equal(S("BarBar"), s)
+ $= = false
+ end
+
+ s = S("a string")
+ s[0..s.size] = S("another string")
+ assert_equal(S("another string"), s)
+
+ o = Object.new
+ def o.to_int; 2; end
+ s = "foo"
+ s[o] = "bar"
+ assert_equal("fobar", s)
+
+ assert_raise(ArgumentError) { "foo"[1, 2, 3] = "" }
+ end
+
+ def test_CMP # '<=>'
+ assert_equal(1, S("abcdef") <=> S("abcde"))
+ assert_equal(0, S("abcdef") <=> S("abcdef"))
+ assert_equal(-1, S("abcde") <=> S("abcdef"))
+
+ assert_equal(-1, S("ABCDEF") <=> S("abcdef"))
+
+ pre_1_7_1 do
+ $= = true
+ assert_equal(0, S("ABCDEF") <=> S("abcdef"))
+ $= = false
+ end
+
+ assert_nil("foo" <=> Object.new)
+
+ o = Object.new
+ def o.to_str; "bar"; end
+ assert_equal(1, "foo" <=> o)
+
+ class << o;remove_method :to_str;end
+ def o.<=>(x); nil; end
+ assert_nil("foo" <=> o)
+
+ class << o;remove_method :<=>;end
+ def o.<=>(x); 1; end
+ assert_equal(-1, "foo" <=> o)
+
+ class << o;remove_method :<=>;end
+ def o.<=>(x); 2**100; end
+ assert_equal(-1, "foo" <=> o)
+ end
+
+ def test_EQUAL # '=='
+ assert_not_equal(:foo, S("foo"))
+ assert_equal(S("abcdef"), S("abcdef"))
+
+ pre_1_7_1 do
+ $= = true
+ assert_equal(S("CAT"), S('cat'))
+ assert_equal(S("CaT"), S('cAt'))
+ $= = false
+ end
+
+ assert_not_equal(S("CAT"), S('cat'))
+ assert_not_equal(S("CaT"), S('cAt'))
+
+ o = Object.new
+ def o.to_str; end
+ def o.==(x); false; end
+ assert_equal(false, "foo" == o)
+ class << o;remove_method :==;end
+ def o.==(x); true; end
+ assert_equal(true, "foo" == o)
+ end
+
+ def test_LSHIFT # '<<'
+ assert_equal(S("world!"), S("world") << 33)
+ assert_equal(S("world!"), S("world") << S("!"))
+
+ s = "a"
+ 10.times {|i|
+ s << s
+ assert_equal("a" * (2 << i), s)
+ }
+
+ s = ["foo"].pack("p")
+ l = s.size
+ s << "bar"
+ assert_equal(l + 3, s.size)
+
+ bug = '[ruby-core:27583]'
+ assert_raise(RangeError, bug) {S("a".force_encoding(Encoding::UTF_8)) << -3}
+ assert_raise(RangeError, bug) {S("a".force_encoding(Encoding::UTF_8)) << -2}
+ assert_raise(RangeError, bug) {S("a".force_encoding(Encoding::UTF_8)) << -1}
+ assert_raise(RangeError, bug) {S("a".force_encoding(Encoding::UTF_8)) << 0x81308130}
+ assert_nothing_raised {S("a".force_encoding(Encoding::GB18030)) << 0x81308130}
+ end
+
+ def test_MATCH # '=~'
+ assert_equal(10, S("FeeFieFoo-Fum") =~ /Fum$/)
+ assert_equal(nil, S("FeeFieFoo-Fum") =~ /FUM$/)
+
+ pre_1_7_1 do
+ $= = true
+ assert_equal(10, S("FeeFieFoo-Fum") =~ /FUM$/)
+ $= = false
+ end
+
+ o = Object.new
+ def o.=~(x); x + "bar"; end
+ assert_equal("foobar", S("foo") =~ o)
+
+ assert_raise(TypeError) { S("foo") =~ "foo" }
+ end
+
+ def test_MOD # '%'
+ assert_equal(S("00123"), S("%05d") % 123)
+ assert_equal(S("123 |00000001"), S("%-5s|%08x") % [123, 1])
+ x = S("%3s %-4s%%foo %.0s%5d %#x%c%3.1f %b %x %X %#b %#x %#X") %
+ [S("hi"),
+ 123,
+ S("never seen"),
+ 456,
+ 0,
+ ?A,
+ 3.0999,
+ 11,
+ 171,
+ 171,
+ 11,
+ 171,
+ 171]
+
+ assert_equal(S(' hi 123 %foo 456 0A3.1 1011 ab AB 0b1011 0xab 0XAB'), x)
+ end
+
+ def test_MUL # '*'
+ assert_equal(S("XXX"), S("X") * 3)
+ assert_equal(S("HOHO"), S("HO") * 2)
+ end
+
+ def test_PLUS # '+'
+ assert_equal(S("Yodel"), S("Yo") + S("del"))
+ end
+
+ def casetest(a, b, rev=false)
+ msg = proc {"#{a} should#{' not' if rev} match #{b}"}
+ case a
+ when b
+ assert(!rev, msg)
+ else
+ assert(rev, msg)
+ end
+ end
+
+ def test_VERY_EQUAL # '==='
+ # assert_equal(true, S("foo") === :foo)
+ casetest(S("abcdef"), S("abcdef"))
+
+ pre_1_7_1 do
+ $= = true
+ casetest(S("CAT"), S('cat'))
+ casetest(S("CaT"), S('cAt'))
+ $= = false
+ end
+
+ casetest(S("CAT"), S('cat'), true) # Reverse the test - we don't want to
+ casetest(S("CaT"), S('cAt'), true) # find these in the case.
+ end
+
+ def test_capitalize
+ assert_equal(S("Hello"), S("hello").capitalize)
+ assert_equal(S("Hello"), S("hELLO").capitalize)
+ assert_equal(S("123abc"), S("123ABC").capitalize)
+ end
+
+ def test_capitalize!
+ a = S("hello"); a.capitalize!
+ assert_equal(S("Hello"), a)
+
+ a = S("hELLO"); a.capitalize!
+ assert_equal(S("Hello"), a)
+
+ a = S("123ABC"); a.capitalize!
+ assert_equal(S("123abc"), a)
+
+ assert_equal(nil, S("123abc").capitalize!)
+ assert_equal(S("123abc"), S("123ABC").capitalize!)
+ assert_equal(S("Abc"), S("ABC").capitalize!)
+ assert_equal(S("Abc"), S("abc").capitalize!)
+ assert_equal(nil, S("Abc").capitalize!)
+
+ a = S("hello")
+ b = a.dup
+ assert_equal(S("Hello"), a.capitalize!)
+ assert_equal(S("hello"), b)
+
+ end
+
+ Bug2463 = '[ruby-dev:39856]'
+ def test_center
+ assert_equal(S("hello"), S("hello").center(4))
+ assert_equal(S(" hello "), S("hello").center(11))
+ assert_equal(S("ababaababa"), S("").center(10, "ab"), Bug2463)
+ assert_equal(S("ababaababab"), S("").center(11, "ab"), Bug2463)
+ end
+
+ def test_chomp
+ assert_equal(S("hello"), S("hello").chomp("\n"))
+ assert_equal(S("hello"), S("hello\n").chomp("\n"))
+ save = $/
+
+ $/ = "\n"
+
+ assert_equal(S("hello"), S("hello").chomp)
+ assert_equal(S("hello"), S("hello\n").chomp)
+
+ $/ = "!"
+ assert_equal(S("hello"), S("hello").chomp)
+ assert_equal(S("hello"), S("hello!").chomp)
+ $/ = save
+
+ assert_equal(S("a").hash, S("a\u0101").chomp(S("\u0101")).hash, '[ruby-core:22414]')
+ end
+
+ def test_chomp!
+ a = S("hello")
+ a.chomp!(S("\n"))
+
+ assert_equal(S("hello"), a)
+ assert_equal(nil, a.chomp!(S("\n")))
+
+ a = S("hello\n")
+ a.chomp!(S("\n"))
+ assert_equal(S("hello"), a)
+ save = $/
+
+ $/ = "\n"
+ a = S("hello")
+ a.chomp!
+ assert_equal(S("hello"), a)
+
+ a = S("hello\n")
+ a.chomp!
+ assert_equal(S("hello"), a)
+
+ $/ = "!"
+ a = S("hello")
+ a.chomp!
+ assert_equal(S("hello"), a)
+
+ a="hello!"
+ a.chomp!
+ assert_equal(S("hello"), a)
+
+ $/ = save
+
+ a = S("hello\n")
+ b = a.dup
+ assert_equal(S("hello"), a.chomp!)
+ assert_equal(S("hello\n"), b)
+
+ s = "foo\r\n"
+ s.chomp!
+ assert_equal("foo", s)
+
+ s = "foo\r"
+ s.chomp!
+ assert_equal("foo", s)
+
+ s = "foo\r\n"
+ s.chomp!("")
+ assert_equal("foo", s)
+
+ s = "foo\r"
+ s.chomp!("")
+ assert_equal("foo\r", s)
+
+ assert_equal(S("a").hash, S("a\u0101").chomp!(S("\u0101")).hash, '[ruby-core:22414]')
+ end
+
+ def test_chop
+ assert_equal(S("hell"), S("hello").chop)
+ assert_equal(S("hello"), S("hello\r\n").chop)
+ assert_equal(S("hello\n"), S("hello\n\r").chop)
+ assert_equal(S(""), S("\r\n").chop)
+ assert_equal(S(""), S("").chop)
+ assert_equal(S("a").hash, S("a\u00d8").chop.hash)
+ end
+
+ def test_chop!
+ a = S("hello").chop!
+ assert_equal(S("hell"), a)
+
+ a = S("hello\r\n").chop!
+ assert_equal(S("hello"), a)
+
+ a = S("hello\n\r").chop!
+ assert_equal(S("hello\n"), a)
+
+ a = S("\r\n").chop!
+ assert_equal(S(""), a)
+
+ a = S("").chop!
+ assert_nil(a)
+
+ a = S("a\u00d8")
+ a.chop!
+ assert_equal(S("a").hash, a.hash)
+
+ a = S("hello\n")
+ b = a.dup
+ assert_equal(S("hello"), a.chop!)
+ assert_equal(S("hello\n"), b)
+ end
+
+ def test_clone
+ for taint in [ false, true ]
+ for frozen in [ false, true ]
+ a = S("Cool")
+ a.taint if taint
+ a.freeze if frozen
+ b = a.clone
+
+ assert_equal(a, b)
+ assert_not_same(a, b)
+ assert_equal(a.frozen?, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
+ end
+ end
+
+ null = File.exist?("/dev/null") ? "/dev/null" : "NUL" # maybe DOSISH
+ assert_equal("", File.read(null).clone, '[ruby-dev:32819] reported by Kazuhiro NISHIYAMA')
+ end
+
+ def test_concat
+ assert_equal(S("world!"), S("world").concat(33))
+ assert_equal(S("world!"), S("world").concat(S('!')))
+
+ bug7090 = '[ruby-core:47751]'
+ result = S("").force_encoding(Encoding::UTF_16LE)
+ result << 0x0300
+ expected = S("\u0300".encode(Encoding::UTF_16LE))
+ assert_equal(expected, result, bug7090)
+ assert_raise(TypeError) { 'foo' << :foo }
+ end
+
+ def test_count
+ a = S("hello world")
+ assert_equal(5, a.count(S("lo")))
+ assert_equal(2, a.count(S("lo"), S("o")))
+ assert_equal(4, a.count(S("hello"), S("^l")))
+ assert_equal(4, a.count(S("ej-m")))
+ assert_equal(0, S("y").count(S("a\\-z")))
+ assert_equal(5, "abc\u{3042 3044 3046}".count("^a"))
+ assert_equal(1, "abc\u{3042 3044 3046}".count("\u3042"))
+ assert_equal(5, "abc\u{3042 3044 3046}".count("^\u3042"))
+ assert_equal(2, "abc\u{3042 3044 3046}".count("a-z", "^a"))
+ assert_equal(0, "abc\u{3042 3044 3046}".count("a", "\u3042"))
+ assert_equal(0, "abc\u{3042 3044 3046}".count("\u3042", "a"))
+ assert_equal(0, "abc\u{3042 3044 3046}".count("\u3042", "\u3044"))
+ assert_equal(4, "abc\u{3042 3044 3046}".count("^a", "^\u3044"))
+ assert_equal(4, "abc\u{3042 3044 3046}".count("^\u3044", "^a"))
+ assert_equal(4, "abc\u{3042 3044 3046}".count("^\u3042", "^\u3044"))
+
+ assert_raise(ArgumentError) { "foo".count }
+ end
+
+ def test_crypt
+ assert_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("aa")))
+ assert_not_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("ab")))
+ assert_raise(ArgumentError) {S("mypassword").crypt(S(""))}
+ assert_raise(ArgumentError) {S("mypassword").crypt(S("\0a"))}
+ assert_raise(ArgumentError) {S("mypassword").crypt(S("a\0"))}
+ [Encoding::UTF_16BE, Encoding::UTF_16LE,
+ Encoding::UTF_32BE, Encoding::UTF_32LE].each do |enc|
+ assert_raise(ArgumentError) {S("mypassword").crypt(S("aa".encode(enc)))}
+ assert_raise(ArgumentError) {S("mypassword".encode(enc)).crypt(S("aa"))}
+ end
+ end
+
+ def test_delete
+ assert_equal(S("heo"), S("hello").delete(S("l"), S("lo")))
+ assert_equal(S("he"), S("hello").delete(S("lo")))
+ assert_equal(S("hell"), S("hello").delete(S("aeiou"), S("^e")))
+ assert_equal(S("ho"), S("hello").delete(S("ej-m")))
+
+ assert_equal("a".hash, "a\u0101".delete("\u0101").hash, '[ruby-talk:329267]')
+ assert_equal(true, "a\u0101".delete("\u0101").ascii_only?)
+ assert_equal(true, "a\u3041".delete("\u3041").ascii_only?)
+ assert_equal(false, "a\u3041\u3042".tr("\u3041", "a").ascii_only?)
+
+ assert_equal("a", "abc\u{3042 3044 3046}".delete("^a"))
+ assert_equal("bc\u{3042 3044 3046}", "abc\u{3042 3044 3046}".delete("a"))
+ assert_equal("\u3042", "abc\u{3042 3044 3046}".delete("^\u3042"))
+
+ bug6160 = '[ruby-dev:45374]'
+ assert_equal("", '\\'.delete('\\'), bug6160)
+ end
+
+ def test_delete!
+ a = S("hello")
+ a.delete!(S("l"), S("lo"))
+ assert_equal(S("heo"), a)
+
+ a = S("hello")
+ a.delete!(S("lo"))
+ assert_equal(S("he"), a)
+
+ a = S("hello")
+ a.delete!(S("aeiou"), S("^e"))
+ assert_equal(S("hell"), a)
+
+ a = S("hello")
+ a.delete!(S("ej-m"))
+ assert_equal(S("ho"), a)
+
+ a = S("hello")
+ assert_nil(a.delete!(S("z")))
+
+ a = S("hello")
+ b = a.dup
+ a.delete!(S("lo"))
+ assert_equal(S("he"), a)
+ assert_equal(S("hello"), b)
+
+ a = S("hello")
+ a.delete!(S("^el"))
+ assert_equal(S("ell"), a)
+
+ assert_raise(ArgumentError) { S("foo").delete! }
+ end
+
+
+ def test_downcase
+ assert_equal(S("hello"), S("helLO").downcase)
+ assert_equal(S("hello"), S("hello").downcase)
+ assert_equal(S("hello"), S("HELLO").downcase)
+ assert_equal(S("abc hello 123"), S("abc HELLO 123").downcase)
+ end
+
+ def test_downcase!
+ a = S("helLO")
+ b = a.dup
+ assert_equal(S("hello"), a.downcase!)
+ assert_equal(S("hello"), a)
+ assert_equal(S("helLO"), b)
+
+ a=S("hello")
+ assert_nil(a.downcase!)
+ assert_equal(S("hello"), a)
+ end
+
+ def test_dump
+ a= S("Test") << 1 << 2 << 3 << 9 << 13 << 10
+ assert_equal(S('"Test\\x01\\x02\\x03\\t\\r\\n"'), a.dump)
+ end
+
+ def test_dup
+ for taint in [ false, true ]
+ for frozen in [ false, true ]
+ a = S("hello")
+ a.taint if taint
+ a.freeze if frozen
+ b = a.dup
+
+ assert_equal(a, b)
+ assert_not_same(a, b)
+ assert_not_predicate(b, :frozen?)
+ assert_equal(a.tainted?, b.tainted?)
+ end
+ end
+ end
+
+ def test_each
+ save = $/
+ $/ = "\n"
+ res=[]
+ S("hello\nworld").lines.each {|x| res << x}
+ assert_equal(S("hello\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res=[]
+ S("hello\n\n\nworld").lines(S('')).each {|x| res << x}
+ assert_equal(S("hello\n\n\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ $/ = "!"
+ res=[]
+ S("hello!world").lines.each {|x| res << x}
+ assert_equal(S("hello!"), res[0])
+ assert_equal(S("world"), res[1])
+ $/ = save
+ end
+
+ def test_each_byte
+ s = S("ABC")
+
+ res = []
+ assert_equal s.object_id, s.each_byte {|x| res << x }.object_id
+ assert_equal(65, res[0])
+ assert_equal(66, res[1])
+ assert_equal(67, res[2])
+
+ assert_equal 65, s.each_byte.next
+ end
+
+ def test_bytes
+ s = S("ABC")
+ assert_equal [65, 66, 67], s.bytes
+
+ if ENUMERATOR_WANTARRAY
+ assert_warn(/block not used/) {
+ assert_equal [65, 66, 67], s.bytes {}
+ }
+ else
+ assert_warning(/deprecated/) {
+ res = []
+ assert_equal s.object_id, s.bytes {|x| res << x }.object_id
+ assert_equal(65, res[0])
+ assert_equal(66, res[1])
+ assert_equal(67, res[2])
+ }
+ end
+ end
+
+ def test_each_codepoint
+ # Single byte optimization
+ assert_equal 65, S("ABC").each_codepoint.next
+
+ s = S("\u3042\u3044\u3046")
+
+ res = []
+ assert_equal s.object_id, s.each_codepoint {|x| res << x }.object_id
+ assert_equal(0x3042, res[0])
+ assert_equal(0x3044, res[1])
+ assert_equal(0x3046, res[2])
+
+ assert_equal 0x3042, s.each_codepoint.next
+ end
+
+ def test_codepoints
+ # Single byte optimization
+ assert_equal [65, 66, 67], S("ABC").codepoints
+
+ s = S("\u3042\u3044\u3046")
+ assert_equal [0x3042, 0x3044, 0x3046], s.codepoints
+
+ if ENUMERATOR_WANTARRAY
+ assert_warn(/block not used/) {
+ assert_equal [0x3042, 0x3044, 0x3046], s.codepoints {}
+ }
+ else
+ assert_warning(/deprecated/) {
+ res = []
+ assert_equal s.object_id, s.codepoints {|x| res << x }.object_id
+ assert_equal(0x3042, res[0])
+ assert_equal(0x3044, res[1])
+ assert_equal(0x3046, res[2])
+ }
+ end
+ end
+
+ def test_each_char
+ s = S("ABC")
+
+ res = []
+ assert_equal s.object_id, s.each_char {|x| res << x }.object_id
+ assert_equal("A", res[0])
+ assert_equal("B", res[1])
+ assert_equal("C", res[2])
+
+ assert_equal "A", S("ABC").each_char.next
+ end
+
+ def test_chars
+ s = S("ABC")
+ assert_equal ["A", "B", "C"], s.chars
+
+ if ENUMERATOR_WANTARRAY
+ assert_warn(/block not used/) {
+ assert_equal ["A", "B", "C"], s.chars {}
+ }
+ else
+ assert_warning(/deprecated/) {
+ res = []
+ assert_equal s.object_id, s.chars {|x| res << x }.object_id
+ assert_equal("A", res[0])
+ assert_equal("B", res[1])
+ assert_equal("C", res[2])
+ }
+ end
+ end
+
+ def test_each_line
+ save = $/
+ $/ = "\n"
+ res=[]
+ S("hello\nworld").each_line {|x| res << x}
+ assert_equal(S("hello\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res=[]
+ S("hello\n\n\nworld").each_line(S('')) {|x| res << x}
+ assert_equal(S("hello\n\n\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ $/ = "!"
+
+ res=[]
+ S("hello!world").each_line {|x| res << x}
+ assert_equal(S("hello!"), res[0])
+ assert_equal(S("world"), res[1])
+
+ $/ = "ab"
+
+ res=[]
+ S("a").lines.each {|x| res << x}
+ assert_equal(1, res.size)
+ assert_equal(S("a"), res[0])
+
+ $/ = save
+
+ s = nil
+ "foo\nbar".each_line(nil) {|s2| s = s2 }
+ assert_equal("foo\nbar", s)
+
+ assert_equal "hello\n", S("hello\nworld").each_line.next
+ assert_equal "hello\nworld", S("hello\nworld").each_line(nil).next
+
+ bug7646 = "[ruby-dev:46827]"
+ assert_nothing_raised(bug7646) do
+ "\n\u0100".each_line("\n") {}
+ end
+ end
+
+ def test_lines
+ s = S("hello\nworld")
+ assert_equal ["hello\n", "world"], s.lines
+ assert_equal ["hello\nworld"], s.lines(nil)
+
+ if ENUMERATOR_WANTARRAY
+ assert_warn(/block not used/) {
+ assert_equal ["hello\n", "world"], s.lines {}
+ }
+ else
+ assert_warning(/deprecated/) {
+ res = []
+ assert_equal s.object_id, s.lines {|x| res << x }.object_id
+ assert_equal(S("hello\n"), res[0])
+ assert_equal(S("world"), res[1])
+ }
+ end
+ end
+
+ def test_empty?
+ assert_empty(S(""))
+ assert_not_empty(S("not"))
+ end
+
+ def test_end_with?
+ assert_send([S("hello"), :end_with?, S("llo")])
+ assert_not_send([S("hello"), :end_with?, S("ll")])
+ assert_send([S("hello"), :end_with?, S("el"), S("lo")])
+
+ bug5536 = '[ruby-core:40623]'
+ assert_raise(TypeError, bug5536) {S("str").end_with? :not_convertible_to_string}
+ end
+
+ def test_eql?
+ a = S("hello")
+ assert_operator(a, :eql?, S("hello"))
+ assert_operator(a, :eql?, a)
+ end
+
+ def test_gsub
+ assert_equal(S("h*ll*"), S("hello").gsub(/[aeiou]/, S('*')))
+ assert_equal(S("h<e>ll<o>"), S("hello").gsub(/([aeiou])/, S('<\1>')))
+ assert_equal(S("h e l l o "),
+ S("hello").gsub(/./) { |s| s[0].to_s + S(' ')})
+ assert_equal(S("HELL-o"),
+ S("hello").gsub(/(hell)(.)/) { |s| $1.upcase + S('-') + $2 })
+ assert_equal(S("<>h<>e<>l<>l<>o<>"), S("hello").gsub(S(''), S('<\0>')))
+
+ a = S("hello")
+ a.taint
+ assert_predicate(a.gsub(/./, S('X')), :tainted?)
+
+ assert_equal("z", "abc".gsub(/./, "a" => "z"), "moved from btest/knownbug")
+
+ assert_raise(ArgumentError) { "foo".gsub }
+ end
+
+ def test_gsub_encoding
+ a = S("hello world")
+ a.force_encoding Encoding::UTF_8
+
+ b = S("hi")
+ b.force_encoding Encoding::US_ASCII
+
+ assert_equal Encoding::UTF_8, a.gsub(/hello/, b).encoding
+
+ c = S("everybody")
+ c.force_encoding Encoding::US_ASCII
+
+ assert_equal Encoding::UTF_8, a.gsub(/world/, c).encoding
+
+ assert_equal S("a\u{e9}apos&lt;"), S("a\u{e9}'&lt;").gsub("'", "apos")
+
+ bug9849 = '[ruby-core:62669] [Bug #9849]'
+ assert_equal S("\u{3042 3042 3042}!foo!"), S("\u{3042 3042 3042}/foo/").gsub("/", "!"), bug9849
+ end
+
+ def test_gsub!
+ a = S("hello")
+ b = a.dup
+ a.gsub!(/[aeiou]/, S('*'))
+ assert_equal(S("h*ll*"), a)
+ assert_equal(S("hello"), b)
+
+ a = S("hello")
+ a.gsub!(/([aeiou])/, S('<\1>'))
+ assert_equal(S("h<e>ll<o>"), a)
+
+ a = S("hello")
+ a.gsub!(/./) { |s| s[0].to_s + S(' ')}
+ assert_equal(S("h e l l o "), a)
+
+ a = S("hello")
+ a.gsub!(/(hell)(.)/) { |s| $1.upcase + S('-') + $2 }
+ assert_equal(S("HELL-o"), a)
+
+ r = S('X')
+ r.taint
+ a.gsub!(/./, r)
+ assert_predicate(a, :tainted?)
+
+ a = S("hello")
+ assert_nil(a.sub!(S('X'), S('Y')))
+ end
+
+ def test_sub_hash
+ assert_equal('azc', 'abc'.sub(/b/, "b" => "z"))
+ assert_equal('ac', 'abc'.sub(/b/, {}))
+ assert_equal('a1c', 'abc'.sub(/b/, "b" => 1))
+ assert_equal('aBc', 'abc'.sub(/b/, Hash.new {|h, k| k.upcase }))
+ assert_equal('a[\&]c', 'abc'.sub(/b/, "b" => '[\&]'))
+ assert_equal('aBcabc', 'abcabc'.sub(/b/, Hash.new {|h, k| h[k] = k.upcase }))
+ assert_equal('aBcdef', 'abcdef'.sub(/de|b/, "b" => "B", "de" => "DE"))
+ end
+
+ def test_gsub_hash
+ assert_equal('azc', 'abc'.gsub(/b/, "b" => "z"))
+ assert_equal('ac', 'abc'.gsub(/b/, {}))
+ assert_equal('a1c', 'abc'.gsub(/b/, "b" => 1))
+ assert_equal('aBc', 'abc'.gsub(/b/, Hash.new {|h, k| k.upcase }))
+ assert_equal('a[\&]c', 'abc'.gsub(/b/, "b" => '[\&]'))
+ assert_equal('aBcaBc', 'abcabc'.gsub(/b/, Hash.new {|h, k| h[k] = k.upcase }))
+ assert_equal('aBcDEf', 'abcdef'.gsub(/de|b/, "b" => "B", "de" => "DE"))
+ end
+
+ def test_hash
+ assert_equal(S("hello").hash, S("hello").hash)
+ assert_not_equal(S("hello").hash, S("helLO").hash)
+ bug4104 = '[ruby-core:33500]'
+ assert_not_equal(S("a").hash, S("a\0").hash, bug4104)
+ bug9172 = '[ruby-core:58658] [Bug #9172]'
+ assert_not_equal(S("sub-setter").hash, S("discover").hash, bug9172)
+ end
+
+ def test_hash_random
+ str = 'abc'
+ a = [str.hash.to_s]
+ 3.times {
+ assert_in_out_err(["-e", "print #{str.dump}.hash"], "") do |r, e|
+ a += r
+ assert_equal([], e)
+ end
+ }
+ assert_not_equal([str.hash.to_s], a.uniq)
+ end
+
+ def test_hex
+ assert_equal(255, S("0xff").hex)
+ assert_equal(-255, S("-0xff").hex)
+ assert_equal(255, S("ff").hex)
+ assert_equal(-255, S("-ff").hex)
+ assert_equal(0, S("-ralph").hex)
+ assert_equal(-15, S("-fred").hex)
+ assert_equal(15, S("fred").hex)
+ end
+
+ def test_include?
+ assert_include(S("foobar"), ?f)
+ assert_include(S("foobar"), S("foo"))
+ assert_not_include(S("foobar"), S("baz"))
+ assert_not_include(S("foobar"), ?z)
+ end
+
+ def test_index
+ assert_equal(0, S("hello").index(?h))
+ assert_equal(1, S("hello").index(S("ell")))
+ assert_equal(2, S("hello").index(/ll./))
+
+ assert_equal(3, S("hello").index(?l, 3))
+ assert_equal(3, S("hello").index(S("l"), 3))
+ assert_equal(3, S("hello").index(/l./, 3))
+
+ assert_nil(S("hello").index(?z, 3))
+ assert_nil(S("hello").index(S("z"), 3))
+ assert_nil(S("hello").index(/z./, 3))
+
+ assert_nil(S("hello").index(?z))
+ assert_nil(S("hello").index(S("z")))
+ assert_nil(S("hello").index(/z./))
+
+ assert_equal(0, S("").index(S("")))
+ assert_equal(0, S("").index(//))
+ assert_nil(S("").index(S("hello")))
+ assert_nil(S("").index(/hello/))
+ assert_equal(0, S("hello").index(S("")))
+ assert_equal(0, S("hello").index(//))
+
+ s = S("long") * 1000 << "x"
+ assert_nil(s.index(S("y")))
+ assert_equal(4 * 1000, s.index(S("x")))
+ s << "yx"
+ assert_equal(4 * 1000, s.index(S("x")))
+ assert_equal(4 * 1000, s.index(S("xyx")))
+
+ o = Object.new
+ def o.to_str; "bar"; end
+ assert_equal(3, "foobarbarbaz".index(o))
+ assert_raise(TypeError) { "foo".index(Object.new) }
+
+ assert_nil("foo".index(//, -100))
+ assert_nil($~)
+ end
+
+ def test_intern
+ assert_equal(:koala, S("koala").intern)
+ assert_not_equal(:koala, S("Koala").intern)
+ end
+
+ def test_length
+ assert_equal(0, S("").length)
+ assert_equal(4, S("1234").length)
+ assert_equal(6, S("1234\r\n").length)
+ assert_equal(7, S("\0011234\r\n").length)
+ end
+
+ def test_ljust
+ assert_equal(S("hello"), S("hello").ljust(4))
+ assert_equal(S("hello "), S("hello").ljust(11))
+ assert_equal(S("ababababab"), S("").ljust(10, "ab"), Bug2463)
+ assert_equal(S("abababababa"), S("").ljust(11, "ab"), Bug2463)
+ end
+
+ def test_next
+ assert_equal(S("abd"), S("abc").next)
+ assert_equal(S("z"), S("y").next)
+ assert_equal(S("aaa"), S("zz").next)
+
+ assert_equal(S("124"), S("123").next)
+ assert_equal(S("1000"), S("999").next)
+
+ assert_equal(S("2000aaa"), S("1999zzz").next)
+ assert_equal(S("AAAAA000"), S("ZZZZ999").next)
+
+ assert_equal(S("*+"), S("**").next)
+ end
+
+ def test_next!
+ a = S("abc")
+ b = a.dup
+ assert_equal(S("abd"), a.next!)
+ assert_equal(S("abd"), a)
+ assert_equal(S("abc"), b)
+
+ a = S("y")
+ assert_equal(S("z"), a.next!)
+ assert_equal(S("z"), a)
+
+ a = S("zz")
+ assert_equal(S("aaa"), a.next!)
+ assert_equal(S("aaa"), a)
+
+ a = S("123")
+ assert_equal(S("124"), a.next!)
+ assert_equal(S("124"), a)
+
+ a = S("999")
+ assert_equal(S("1000"), a.next!)
+ assert_equal(S("1000"), a)
+
+ a = S("1999zzz")
+ assert_equal(S("2000aaa"), a.next!)
+ assert_equal(S("2000aaa"), a)
+
+ a = S("ZZZZ999")
+ assert_equal(S("AAAAA000"), a.next!)
+ assert_equal(S("AAAAA000"), a)
+
+ a = S("**")
+ assert_equal(S("*+"), a.next!)
+ assert_equal(S("*+"), a)
+ end
+
+ def test_oct
+ assert_equal(255, S("0377").oct)
+ assert_equal(255, S("377").oct)
+ assert_equal(-255, S("-0377").oct)
+ assert_equal(-255, S("-377").oct)
+ assert_equal(0, S("OO").oct)
+ assert_equal(24, S("030OO").oct)
+ end
+
+ def test_replace
+ a = S("foo")
+ assert_equal(S("f"), a.replace(S("f")))
+
+ a = S("foo")
+ assert_equal(S("foobar"), a.replace(S("foobar")))
+
+ a = S("foo")
+ a.taint
+ b = a.replace(S("xyz"))
+ assert_equal(S("xyz"), b)
+ assert_predicate(b, :tainted?)
+
+ s = "foo" * 100
+ s2 = ("bar" * 100).dup
+ s.replace(s2)
+ assert_equal(s2, s)
+
+ s2 = ["foo"].pack("p")
+ s.replace(s2)
+ assert_equal(s2, s)
+
+ fs = "".freeze
+ assert_raise(RuntimeError) { fs.replace("a") }
+ assert_raise(RuntimeError) { fs.replace(fs) }
+ assert_raise(ArgumentError) { fs.replace() }
+ assert_raise(RuntimeError) { fs.replace(42) }
+ end
+
+ def test_reverse
+ assert_equal(S("beta"), S("ateb").reverse)
+ assert_equal(S("madamImadam"), S("madamImadam").reverse)
+
+ a=S("beta")
+ assert_equal(S("ateb"), a.reverse)
+ assert_equal(S("beta"), a)
+ end
+
+ def test_reverse!
+ a = S("beta")
+ b = a.dup
+ assert_equal(S("ateb"), a.reverse!)
+ assert_equal(S("ateb"), a)
+ assert_equal(S("beta"), b)
+
+ assert_equal(S("madamImadam"), S("madamImadam").reverse!)
+
+ a = S("madamImadam")
+ assert_equal(S("madamImadam"), a.reverse!) # ??
+ assert_equal(S("madamImadam"), a)
+ end
+
+ def test_rindex
+ assert_equal(3, S("hello").rindex(?l))
+ assert_equal(6, S("ell, hello").rindex(S("ell")))
+ assert_equal(7, S("ell, hello").rindex(/ll./))
+
+ assert_equal(3, S("hello,lo").rindex(?l, 3))
+ assert_equal(3, S("hello,lo").rindex(S("l"), 3))
+ assert_equal(3, S("hello,lo").rindex(/l./, 3))
+
+ assert_nil(S("hello").rindex(?z, 3))
+ assert_nil(S("hello").rindex(S("z"), 3))
+ assert_nil(S("hello").rindex(/z./, 3))
+
+ assert_nil(S("hello").rindex(?z))
+ assert_nil(S("hello").rindex(S("z")))
+ assert_nil(S("hello").rindex(/z./))
+
+ o = Object.new
+ def o.to_str; "bar"; end
+ assert_equal(6, "foobarbarbaz".rindex(o))
+ assert_raise(TypeError) { "foo".rindex(Object.new) }
+
+ assert_nil("foo".rindex(//, -100))
+ assert_nil($~)
+ end
+
+ def test_rjust
+ assert_equal(S("hello"), S("hello").rjust(4))
+ assert_equal(S(" hello"), S("hello").rjust(11))
+ assert_equal(S("ababababab"), S("").rjust(10, "ab"), Bug2463)
+ assert_equal(S("abababababa"), S("").rjust(11, "ab"), Bug2463)
+ end
+
+ def test_scan
+ a = S("cruel world")
+ assert_equal([S("cruel"), S("world")],a.scan(/\w+/))
+ assert_equal([S("cru"), S("el "), S("wor")],a.scan(/.../))
+ assert_equal([[S("cru")], [S("el ")], [S("wor")]],a.scan(/(...)/))
+
+ res = []
+ a.scan(/\w+/) { |w| res << w }
+ assert_equal([S("cruel"), S("world") ],res)
+
+ res = []
+ a.scan(/.../) { |w| res << w }
+ assert_equal([S("cru"), S("el "), S("wor")],res)
+
+ res = []
+ a.scan(/(...)/) { |w| res << w }
+ assert_equal([[S("cru")], [S("el ")], [S("wor")]],res)
+
+ a = S("hello")
+ a.taint
+ res = []
+ a.scan(/./) { |w| res << w }
+ assert_predicate(res[0], :tainted?, '[ruby-core:33338] #4087')
+
+ /h/ =~ a
+ a.scan(/x/)
+ assert_nil($~)
+
+ /h/ =~ a
+ a.scan('x')
+ assert_nil($~)
+
+ assert_equal(3, S("hello hello hello").scan("hello".taint).count(&:tainted?))
+ end
+
+ def test_size
+ assert_equal(0, S("").size)
+ assert_equal(4, S("1234").size)
+ assert_equal(6, S("1234\r\n").size)
+ assert_equal(7, S("\0011234\r\n").size)
+ end
+
+ def test_slice
+ assert_equal(?A, S("AooBar").slice(0))
+ assert_equal(?B, S("FooBaB").slice(-1))
+ assert_nil(S("FooBar").slice(6))
+ assert_nil(S("FooBar").slice(-7))
+
+ assert_equal(S("Foo"), S("FooBar").slice(0,3))
+ assert_equal(S(S("Bar")), S("FooBar").slice(-3,3))
+ assert_nil(S("FooBar").slice(7,2)) # Maybe should be six?
+ assert_nil(S("FooBar").slice(-7,10))
+
+ assert_equal(S("Foo"), S("FooBar").slice(0..2))
+ assert_equal(S("Bar"), S("FooBar").slice(-3..-1))
+ assert_equal(S(""), S("FooBar").slice(6..2))
+ assert_nil(S("FooBar").slice(-10..-7))
+
+ assert_equal(S("Foo"), S("FooBar").slice(/^F../))
+ assert_equal(S("Bar"), S("FooBar").slice(/..r$/))
+ assert_nil(S("FooBar").slice(/xyzzy/))
+ assert_nil(S("FooBar").slice(/plugh/))
+
+ assert_equal(S("Foo"), S("FooBar").slice(S("Foo")))
+ assert_equal(S("Bar"), S("FooBar").slice(S("Bar")))
+ assert_nil(S("FooBar").slice(S("xyzzy")))
+ assert_nil(S("FooBar").slice(S("plugh")))
+
+ bug9882 = '[ruby-core:62842] [Bug #9882]'
+ substr = S("\u{30c6 30b9 30c8 2019}#{bug9882}").slice(4..-1)
+ assert_equal(S(bug9882).hash, substr.hash, bug9882)
+ assert_predicate(substr, :ascii_only?, bug9882)
+ end
+
+ def test_slice!
+ a = S("AooBar")
+ b = a.dup
+ assert_equal(?A, a.slice!(0))
+ assert_equal(S("ooBar"), a)
+ assert_equal(S("AooBar"), b)
+
+ a = S("FooBar")
+ assert_equal(?r,a.slice!(-1))
+ assert_equal(S("FooBa"), a)
+
+ a = S("FooBar")
+ if @aref_slicebang_silent
+ assert_nil( a.slice!(6) )
+ else
+ assert_raise(IndexError) { a.slice!(6) }
+ end
+ assert_equal(S("FooBar"), a)
+
+ if @aref_slicebang_silent
+ assert_nil( a.slice!(-7) )
+ else
+ assert_raise(IndexError) { a.slice!(-7) }
+ end
+ assert_equal(S("FooBar"), a)
+
+ a = S("FooBar")
+ assert_equal(S("Foo"), a.slice!(0,3))
+ assert_equal(S("Bar"), a)
+
+ a = S("FooBar")
+ assert_equal(S("Bar"), a.slice!(-3,3))
+ assert_equal(S("Foo"), a)
+
+ a=S("FooBar")
+ if @aref_slicebang_silent
+ assert_nil(a.slice!(7,2)) # Maybe should be six?
+ else
+ assert_raise(IndexError) {a.slice!(7,2)} # Maybe should be six?
+ end
+ assert_equal(S("FooBar"), a)
+ if @aref_slicebang_silent
+ assert_nil(a.slice!(-7,10))
+ else
+ assert_raise(IndexError) {a.slice!(-7,10)}
+ end
+ assert_equal(S("FooBar"), a)
+
+ a=S("FooBar")
+ assert_equal(S("Foo"), a.slice!(0..2))
+ assert_equal(S("Bar"), a)
+
+ a=S("FooBar")
+ assert_equal(S("Bar"), a.slice!(-3..-1))
+ assert_equal(S("Foo"), a)
+
+ a=S("FooBar")
+ if @aref_slicebang_silent
+ assert_equal(S(""), a.slice!(6..2))
+ else
+ assert_raise(RangeError) {a.slice!(6..2)}
+ end
+ assert_equal(S("FooBar"), a)
+ if @aref_slicebang_silent
+ assert_nil(a.slice!(-10..-7))
+ else
+ assert_raise(RangeError) {a.slice!(-10..-7)}
+ end
+ assert_equal(S("FooBar"), a)
+
+ a=S("FooBar")
+ assert_equal(S("Foo"), a.slice!(/^F../))
+ assert_equal(S("Bar"), a)
+
+ a=S("FooBar")
+ assert_equal(S("Bar"), a.slice!(/..r$/))
+ assert_equal(S("Foo"), a)
+
+ a=S("FooBar")
+ if @aref_slicebang_silent
+ assert_nil(a.slice!(/xyzzy/))
+ else
+ assert_raise(IndexError) {a.slice!(/xyzzy/)}
+ end
+ assert_equal(S("FooBar"), a)
+ if @aref_slicebang_silent
+ assert_nil(a.slice!(/plugh/))
+ else
+ assert_raise(IndexError) {a.slice!(/plugh/)}
+ end
+ assert_equal(S("FooBar"), a)
+
+ a=S("FooBar")
+ assert_equal(S("Foo"), a.slice!(S("Foo")))
+ assert_equal(S("Bar"), a)
+
+ a=S("FooBar")
+ assert_equal(S("Bar"), a.slice!(S("Bar")))
+ assert_equal(S("Foo"), a)
+
+ pre_1_7_1 do
+ a=S("FooBar")
+ assert_nil(a.slice!(S("xyzzy")))
+ assert_equal(S("FooBar"), a)
+ assert_nil(a.slice!(S("plugh")))
+ assert_equal(S("FooBar"), a)
+ end
+
+ assert_raise(ArgumentError) { "foo".slice! }
+ end
+
+ def test_split
+ assert_nil($;)
+ assert_equal([S("a"), S("b"), S("c")], S(" a b\t c ").split)
+ assert_equal([S("a"), S("b"), S("c")], S(" a b\t c ").split(S(" ")))
+
+ assert_equal([S(" a "), S(" b "), S(" c ")], S(" a | b | c ").split(S("|")))
+
+ assert_equal([S("a"), S("b"), S("c")], S("aXXbXXcXX").split(/X./))
+
+ assert_equal([S("a"), S("b"), S("c")], S("abc").split(//))
+
+ assert_equal([S("a|b|c")], S("a|b|c").split(S('|'), 1))
+
+ assert_equal([S("a"), S("b|c")], S("a|b|c").split(S('|'), 2))
+ assert_equal([S("a"), S("b"), S("c")], S("a|b|c").split(S('|'), 3))
+
+ assert_equal([S("a"), S("b"), S("c"), S("")], S("a|b|c|").split(S('|'), -1))
+ assert_equal([S("a"), S("b"), S("c"), S(""), S("")], S("a|b|c||").split(S('|'), -1))
+
+ assert_equal([S("a"), S(""), S("b"), S("c")], S("a||b|c|").split(S('|')))
+ assert_equal([S("a"), S(""), S("b"), S("c"), S("")], S("a||b|c|").split(S('|'), -1))
+
+ assert_equal([], "".split(//, 1))
+
+ assert_equal("[2, 3]", [1,2,3].slice!(1,10000).inspect, "moved from btest/knownbug")
+ end
+
+ def test_split_encoding
+ bug6206 = '[ruby-dev:45441]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ s = S("a:".force_encoding(enc))
+ assert_equal([enc]*2, s.split(":", 2).map(&:encoding), bug6206)
+ end
+ end
+
+ def test_split_wchar
+ bug8642 = '[ruby-core:56036] [Bug #8642]'
+ [
+ Encoding::UTF_16BE, Encoding::UTF_16LE,
+ Encoding::UTF_32BE, Encoding::UTF_32LE,
+ ].each do |enc|
+ s = S("abc,def".encode(enc))
+ assert_equal(["abc", "def"].map {|c| c.encode(enc)},
+ s.split(",".encode(enc)),
+ "#{bug8642} in #{enc.name}")
+ end
+ end
+
+ def test_split_invalid_sequence
+ bug10886 = '[ruby-core:68229] [Bug #10886]'
+ broken = S("\xa1".force_encoding("utf-8"))
+ assert_raise(ArgumentError, bug10886) {
+ S("a,b").split(broken)
+ }
+ end
+
+ def test_split_invalid_argument
+ assert_raise(TypeError) {
+ S("a,b").split(BasicObject.new)
+ }
+ end
+
+ def test_squeeze
+ assert_equal(S("abc"), S("aaabbbbccc").squeeze)
+ assert_equal(S("aa bb cc"), S("aa bb cc").squeeze(S(" ")))
+ assert_equal(S("BxTyWz"), S("BxxxTyyyWzzzzz").squeeze(S("a-z")))
+ end
+
+ def test_squeeze!
+ a = S("aaabbbbccc")
+ b = a.dup
+ assert_equal(S("abc"), a.squeeze!)
+ assert_equal(S("abc"), a)
+ assert_equal(S("aaabbbbccc"), b)
+
+ a = S("aa bb cc")
+ assert_equal(S("aa bb cc"), a.squeeze!(S(" ")))
+ assert_equal(S("aa bb cc"), a)
+
+ a = S("BxxxTyyyWzzzzz")
+ assert_equal(S("BxTyWz"), a.squeeze!(S("a-z")))
+ assert_equal(S("BxTyWz"), a)
+
+ a=S("The quick brown fox")
+ assert_nil(a.squeeze!)
+ end
+
+ def test_start_with?
+ assert_send([S("hello"), :start_with?, S("hel")])
+ assert_not_send([S("hello"), :start_with?, S("el")])
+ assert_send([S("hello"), :start_with?, S("el"), S("he")])
+
+ bug5536 = '[ruby-core:40623]'
+ assert_raise(TypeError, bug5536) {S("str").start_with? :not_convertible_to_string}
+ end
+
+ def test_strip
+ assert_equal(S("x"), S(" x ").strip)
+ assert_equal(S("x"), S(" \n\r\t x \t\r\n\n ").strip)
+
+ assert_equal("0b0 ".force_encoding("UTF-16BE"),
+ "\x00 0b0 ".force_encoding("UTF-16BE").strip)
+ assert_equal("0\x000b0 ".force_encoding("UTF-16BE"),
+ "0\x000b0 ".force_encoding("UTF-16BE").strip)
+ end
+
+ def test_strip!
+ a = S(" x ")
+ b = a.dup
+ assert_equal(S("x") ,a.strip!)
+ assert_equal(S("x") ,a)
+ assert_equal(S(" x "), b)
+
+ a = S(" \n\r\t x \t\r\n\n ")
+ assert_equal(S("x"), a.strip!)
+ assert_equal(S("x"), a)
+
+ a = S("x")
+ assert_nil(a.strip!)
+ assert_equal(S("x") ,a)
+ end
+
+ def test_sub
+ assert_equal(S("h*llo"), S("hello").sub(/[aeiou]/, S('*')))
+ assert_equal(S("h<e>llo"), S("hello").sub(/([aeiou])/, S('<\1>')))
+ assert_equal(S("h ello"), S("hello").sub(/./) {
+ |s| s[0].to_s + S(' ')})
+ assert_equal(S("HELL-o"), S("hello").sub(/(hell)(.)/) {
+ |s| $1.upcase + S('-') + $2
+ })
+ assert_equal(S("h<e>llo"), S("hello").sub('e', S('<\0>')))
+
+ assert_equal(S("a\\aba"), S("ababa").sub(/b/, '\\'))
+ assert_equal(S("ab\\aba"), S("ababa").sub(/(b)/, '\1\\'))
+ assert_equal(S("ababa"), S("ababa").sub(/(b)/, '\1'))
+ assert_equal(S("ababa"), S("ababa").sub(/(b)/, '\\1'))
+ assert_equal(S("a\\1aba"), S("ababa").sub(/(b)/, '\\\1'))
+ assert_equal(S("a\\1aba"), S("ababa").sub(/(b)/, '\\\\1'))
+ assert_equal(S("a\\baba"), S("ababa").sub(/(b)/, '\\\\\1'))
+
+ assert_equal(S("a--ababababababababab"),
+ S("abababababababababab").sub(/(b)/, '-\9-'))
+ assert_equal(S("1-b-0"),
+ S("1b2b3b4b5b6b7b8b9b0").
+ sub(/(b).(b).(b).(b).(b).(b).(b).(b).(b)/, '-\9-'))
+ assert_equal(S("1-b-0"),
+ S("1b2b3b4b5b6b7b8b9b0").
+ sub(/(b).(b).(b).(b).(b).(b).(b).(b).(b)/, '-\\9-'))
+ assert_equal(S("1-\\9-0"),
+ S("1b2b3b4b5b6b7b8b9b0").
+ sub(/(b).(b).(b).(b).(b).(b).(b).(b).(b)/, '-\\\9-'))
+ assert_equal(S("k"),
+ S("1a2b3c4d5e6f7g8h9iAjBk").
+ sub(/.(.).(.).(.).(.).(.).(.).(.).(.).(.).(.).(.)/, '\+'))
+
+ assert_equal(S("ab\\aba"), S("ababa").sub(/b/, '\&\\'))
+ assert_equal(S("ababa"), S("ababa").sub(/b/, '\&'))
+ assert_equal(S("ababa"), S("ababa").sub(/b/, '\\&'))
+ assert_equal(S("a\\&aba"), S("ababa").sub(/b/, '\\\&'))
+ assert_equal(S("a\\&aba"), S("ababa").sub(/b/, '\\\\&'))
+ assert_equal(S("a\\baba"), S("ababa").sub(/b/, '\\\\\&'))
+
+ a = S("hello")
+ a.taint
+ x = a.sub(/./, S('X'))
+ assert_predicate(x, :tainted?)
+
+ o = Object.new
+ def o.to_str; "bar"; end
+ assert_equal("fooBARbaz", "foobarbaz".sub(o, "BAR"))
+
+ assert_raise(TypeError) { "foo".sub(Object.new, "") }
+
+ assert_raise(ArgumentError) { "foo".sub }
+
+ assert_raise(IndexError) { "foo"[/(?:(o$)|(x))/, 2] = 'bar' }
+
+ o = Object.new
+ def o.to_s; self; end
+ assert_match(/^foo#<Object:0x.*>baz$/, "foobarbaz".sub("bar") { o })
+
+ assert_equal(S("Abc"), S("abc").sub("a", "A"))
+ m = nil
+ assert_equal(S("Abc"), S("abc").sub("a") {m = $~; "A"})
+ assert_equal(S("a"), m[0])
+ assert_equal(/a/, m.regexp)
+ end
+
+ def test_sub!
+ a = S("hello")
+ b = a.dup
+ a.sub!(/[aeiou]/, S('*'))
+ assert_equal(S("h*llo"), a)
+ assert_equal(S("hello"), b)
+
+ a = S("hello")
+ a.sub!(/([aeiou])/, S('<\1>'))
+ assert_equal(S("h<e>llo"), a)
+
+ a = S("hello")
+ a.sub!(/./) { |s| s[0].to_s + S(' ')}
+ assert_equal(S("h ello"), a)
+
+ a = S("hello")
+ a.sub!(/(hell)(.)/) { |s| $1.upcase + S('-') + $2 }
+ assert_equal(S("HELL-o"), a)
+
+ a=S("hello")
+ assert_nil(a.sub!(/X/, S('Y')))
+
+ r = S('X')
+ r.taint
+ a.sub!(/./, r)
+ assert_predicate(a, :tainted?)
+ end
+
+ def test_succ
+ assert_equal(S("abd"), S("abc").succ)
+ assert_equal(S("z"), S("y").succ)
+ assert_equal(S("aaa"), S("zz").succ)
+
+ assert_equal(S("124"), S("123").succ)
+ assert_equal(S("1000"), S("999").succ)
+ assert_equal(S("2.000"), S("1.999").succ)
+
+ assert_equal(S("No.10"), S("No.9").succ)
+ assert_equal(S("2000aaa"), S("1999zzz").succ)
+ assert_equal(S("AAAAA000"), S("ZZZZ999").succ)
+ assert_equal(S("*+"), S("**").succ)
+
+ assert_equal("abce", "abcd".succ)
+ assert_equal("THX1139", "THX1138".succ)
+ assert_equal("<\<koalb>>", "<\<koala>>".succ)
+ assert_equal("2000aaa", "1999zzz".succ)
+ assert_equal("AAAA0000", "ZZZ9999".succ)
+ assert_equal("**+", "***".succ)
+ end
+
+ def test_succ!
+ a = S("abc")
+ b = a.dup
+ assert_equal(S("abd"), a.succ!)
+ assert_equal(S("abd"), a)
+ assert_equal(S("abc"), b)
+
+ a = S("y")
+ assert_equal(S("z"), a.succ!)
+ assert_equal(S("z"), a)
+
+ a = S("zz")
+ assert_equal(S("aaa"), a.succ!)
+ assert_equal(S("aaa"), a)
+
+ a = S("123")
+ assert_equal(S("124"), a.succ!)
+ assert_equal(S("124"), a)
+
+ a = S("999")
+ assert_equal(S("1000"), a.succ!)
+ assert_equal(S("1000"), a)
+
+ a = S("1999zzz")
+ assert_equal(S("2000aaa"), a.succ!)
+ assert_equal(S("2000aaa"), a)
+
+ a = S("ZZZZ999")
+ assert_equal(S("AAAAA000"), a.succ!)
+ assert_equal(S("AAAAA000"), a)
+
+ a = S("**")
+ assert_equal(S("*+"), a.succ!)
+ assert_equal(S("*+"), a)
+
+ a = S("No.9")
+ assert_equal(S("No.10"), a.succ!)
+ assert_equal(S("No.10"), a)
+
+ assert_equal("aaaaaaaaaaaa", "zzzzzzzzzzz".succ!)
+ assert_equal("aaaaaaaaaaaaaaaaaaaaaaaa", "zzzzzzzzzzzzzzzzzzzzzzz".succ!)
+ end
+
+ def test_sum
+ n = S("\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001")
+ assert_equal(15, n.sum)
+ n += S("\001")
+ assert_equal(16, n.sum(17))
+ n[0] = 2.chr
+ assert_not_equal(15, n.sum)
+ assert_equal(17, n.sum(0))
+ assert_equal(17, n.sum(-1))
+ end
+
+ def check_sum(str, bits=16)
+ sum = 0
+ str.each_byte {|c| sum += c}
+ sum = sum & ((1 << bits) - 1) if bits != 0
+ assert_equal(sum, str.sum(bits))
+ end
+
+ def test_sum_2
+ assert_equal(0, "".sum)
+ assert_equal(294, "abc".sum)
+ check_sum("abc")
+ check_sum("\x80")
+ -3.upto(70) {|bits|
+ check_sum("xyz", bits)
+ }
+ end
+
+ def test_sum_long
+ s8421505 = "\xff" * 8421505
+ assert_equal(127, s8421505.sum(31))
+ assert_equal(2147483775, s8421505.sum(0))
+ s16843010 = ("\xff" * 16843010)
+ assert_equal(254, s16843010.sum(32))
+ assert_equal(4294967550, s16843010.sum(0))
+ end
+
+ def test_swapcase
+ assert_equal(S("hi&LOW"), S("HI&low").swapcase)
+ end
+
+ def test_swapcase!
+ a = S("hi&LOW")
+ b = a.dup
+ assert_equal(S("HI&low"), a.swapcase!)
+ assert_equal(S("HI&low"), a)
+ assert_equal(S("hi&LOW"), b)
+
+ a = S("$^#^%$#!!")
+ assert_nil(a.swapcase!)
+ assert_equal(S("$^#^%$#!!"), a)
+ end
+
+ def test_to_f
+ assert_equal(344.3, S("344.3").to_f)
+ assert_equal(5.9742e24, S("5.9742e24").to_f)
+ assert_equal(98.6, S("98.6 degrees").to_f)
+ assert_equal(0.0, S("degrees 100.0").to_f)
+ assert_equal([ 0.0].pack('G'), [S(" 0.0").to_f].pack('G'))
+ assert_equal([-0.0].pack('G'), [S("-0.0").to_f].pack('G'))
+ end
+
+ def test_to_i
+ assert_equal(1480, S("1480ft/sec").to_i)
+ assert_equal(0, S("speed of sound in water @20C = 1480ft/sec)").to_i)
+ assert_equal(0, " 0".to_i)
+ assert_equal(0, "+0".to_i)
+ assert_equal(0, "-0".to_i)
+ assert_equal(0, "--0".to_i)
+ assert_equal(16, "0x10".to_i(0))
+ assert_equal(16, "0X10".to_i(0))
+ assert_equal(2, "0b10".to_i(0))
+ assert_equal(2, "0B10".to_i(0))
+ assert_equal(8, "0o10".to_i(0))
+ assert_equal(8, "0O10".to_i(0))
+ assert_equal(10, "0d10".to_i(0))
+ assert_equal(10, "0D10".to_i(0))
+ assert_equal(8, "010".to_i(0))
+ assert_raise(ArgumentError) { "010".to_i(-10) }
+ 2.upto(36) {|radix|
+ assert_equal(radix, "10".to_i(radix))
+ assert_equal(radix**2, "100".to_i(radix))
+ }
+ assert_raise(ArgumentError) { "0".to_i(1) }
+ assert_raise(ArgumentError) { "0".to_i(37) }
+ assert_equal(0, "z".to_i(10))
+ assert_equal(12, "1_2".to_i(10))
+ assert_equal(0x40000000, "1073741824".to_i(10))
+ assert_equal(0x4000000000000000, "4611686018427387904".to_i(10))
+ assert_equal(1, "1__2".to_i(10))
+ assert_equal(1, "1_z".to_i(10))
+
+ bug6192 = '[ruby-core:43566]'
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("utf-16be").to_i}
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("utf-16le").to_i}
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("utf-32be").to_i}
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("utf-32le").to_i}
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("iso-2022-jp").to_i}
+ end
+
+ def test_to_s
+ a = S("me")
+ assert_equal("me", a.to_s)
+ assert_equal(a.__id__, a.to_s.__id__) if @cls == String
+ end
+
+ def test_to_str
+ a = S("me")
+ assert_equal("me", a.to_s)
+ assert_equal(a.__id__, a.to_s.__id__) if @cls == String
+
+ o = Object.new
+ def o.to_str
+ "at"
+ end
+ assert_equal("meat", a.concat(o))
+
+ o = Object.new
+ def o.to_str
+ foo_bar()
+ end
+ assert_match(/foo_bar/, assert_raise(NoMethodError) {a.concat(o)}.message)
+ end
+
+ def test_tr
+ assert_equal(S("hippo"), S("hello").tr(S("el"), S("ip")))
+ assert_equal(S("*e**o"), S("hello").tr(S("^aeiou"), S("*")))
+ assert_equal(S("hal"), S("ibm").tr(S("b-z"), S("a-z")))
+
+ a = "abc".force_encoding(Encoding::US_ASCII)
+ assert_equal(Encoding::US_ASCII, a.tr(S("z"), S("\u0101")).encoding, '[ruby-core:22326]')
+
+ assert_equal("a".hash, "a".tr("a", "\u0101").tr("\u0101", "a").hash, '[ruby-core:22328]')
+ assert_equal(true, "\u0101".tr("\u0101", "a").ascii_only?)
+ assert_equal(true, "\u3041".tr("\u3041", "a").ascii_only?)
+ assert_equal(false, "\u3041\u3042".tr("\u3041", "a").ascii_only?)
+
+ bug6156 = '[ruby-core:43335]'
+ str, range, star = %w[b a-z *].map{|s|s.encode("utf-16le")}
+ assert_equal(star, str.tr(range, star), bug6156)
+ end
+
+ def test_tr!
+ a = S("hello")
+ b = a.dup
+ assert_equal(S("hippo"), a.tr!(S("el"), S("ip")))
+ assert_equal(S("hippo"), a)
+ assert_equal(S("hello"),b)
+
+ a = S("hello")
+ assert_equal(S("*e**o"), a.tr!(S("^aeiou"), S("*")))
+ assert_equal(S("*e**o"), a)
+
+ a = S("IBM")
+ assert_equal(S("HAL"), a.tr!(S("B-Z"), S("A-Z")))
+ assert_equal(S("HAL"), a)
+
+ a = S("ibm")
+ assert_nil(a.tr!(S("B-Z"), S("A-Z")))
+ assert_equal(S("ibm"), a)
+
+ a = "abc".force_encoding(Encoding::US_ASCII)
+ assert_nil(a.tr!(S("z"), S("\u0101")), '[ruby-core:22326]')
+ assert_equal(Encoding::US_ASCII, a.encoding, '[ruby-core:22326]')
+ end
+
+ def test_tr_s
+ assert_equal(S("hypo"), S("hello").tr_s(S("el"), S("yp")))
+ assert_equal(S("h*o"), S("hello").tr_s(S("el"), S("*")))
+ assert_equal("a".hash, "\u0101\u0101".tr_s("\u0101", "a").hash)
+ assert_equal(true, "\u3041\u3041".tr("\u3041", "a").ascii_only?)
+ end
+
+ def test_tr_s!
+ a = S("hello")
+ b = a.dup
+ assert_equal(S("hypo"), a.tr_s!(S("el"), S("yp")))
+ assert_equal(S("hypo"), a)
+ assert_equal(S("hello"), b)
+
+ a = S("hello")
+ assert_equal(S("h*o"), a.tr_s!(S("el"), S("*")))
+ assert_equal(S("h*o"), a)
+ end
+
+ def test_unpack
+ a = [S("cat"), S("wom"), S("x"), S("yy")]
+ assert_equal(a, S("catwomx yy ").unpack(S("A3A3A3A3")))
+
+ assert_equal([S("cat")], S("cat \000\000").unpack(S("A*")))
+ assert_equal([S("cwx"), S("wx"), S("x"), S("yy")],
+ S("cwx yy ").unpack(S("A3@1A3@2A3A3")))
+ assert_equal([S("cat"), S("wom"), S("x\000\000"), S("yy\000")],
+ S("catwomx\000\000yy\000").unpack(S("a3a3a3a3")))
+ assert_equal([S("cat \000\000")], S("cat \000\000").unpack(S("a*")))
+ assert_equal([S("ca")], S("catdog").unpack(S("a2")))
+
+ assert_equal([S("cat\000\000")],
+ S("cat\000\000\000\000\000dog").unpack(S("a5")))
+
+ assert_equal([S("01100001")], S("\x61").unpack(S("B8")))
+ assert_equal([S("01100001")], S("\x61").unpack(S("B*")))
+ assert_equal([S("0110000100110111")], S("\x61\x37").unpack(S("B16")))
+ assert_equal([S("01100001"), S("00110111")], S("\x61\x37").unpack(S("B8B8")))
+ assert_equal([S("0110")], S("\x60").unpack(S("B4")))
+
+ assert_equal([S("01")], S("\x40").unpack(S("B2")))
+
+ assert_equal([S("01100001")], S("\x86").unpack(S("b8")))
+ assert_equal([S("01100001")], S("\x86").unpack(S("b*")))
+
+ assert_equal([S("0110000100110111")], S("\x86\xec").unpack(S("b16")))
+ assert_equal([S("01100001"), S("00110111")], S("\x86\xec").unpack(S("b8b8")))
+
+ assert_equal([S("0110")], S("\x06").unpack(S("b4")))
+ assert_equal([S("01")], S("\x02").unpack(S("b2")))
+
+ assert_equal([ 65, 66, 67 ], S("ABC").unpack(S("C3")))
+ assert_equal([ 255, 66, 67 ], S("\377BC").unpack("C*"))
+ assert_equal([ 65, 66, 67 ], S("ABC").unpack("c3"))
+ assert_equal([ -1, 66, 67 ], S("\377BC").unpack("c*"))
+
+
+ assert_equal([S("4142"), S("0a"), S("1")], S("AB\n\x10").unpack(S("H4H2H1")))
+ assert_equal([S("1424"), S("a0"), S("2")], S("AB\n\x02").unpack(S("h4h2h1")))
+
+ assert_equal([S("abc\002defcat\001"), S(""), S("")],
+ S("abc=02def=\ncat=\n=01=\n").unpack(S("M9M3M4")))
+
+ assert_equal([S("hello\n")], S("aGVsbG8K\n").unpack(S("m")))
+
+ assert_equal([S("hello\nhello\n")], S(",:&5L;&\\*:&5L;&\\*\n").unpack(S("u")))
+
+ assert_equal([0xa9, 0x42, 0x2260], S("\xc2\xa9B\xe2\x89\xa0").unpack(S("U*")))
+
+=begin
+ skipping "Not tested:
+ D,d & double-precision float, native format\\
+ E & double-precision float, little-endian byte order\\
+ e & single-precision float, little-endian byte order\\
+ F,f & single-precision float, native format\\
+ G & double-precision float, network (big-endian) byte order\\
+ g & single-precision float, network (big-endian) byte order\\
+ I & unsigned integer\\
+ i & integer\\
+ L & unsigned long\\
+ l & long\\
+
+ m & string encoded in base64 (uuencoded)\\
+ N & long, network (big-endian) byte order\\
+ n & short, network (big-endian) byte-order\\
+ P & pointer to a structure (fixed-length string)\\
+ p & pointer to a null-terminated string\\
+ S & unsigned short\\
+ s & short\\
+ V & long, little-endian byte order\\
+ v & short, little-endian byte order\\
+ X & back up a byte\\
+ x & null byte\\
+ Z & ASCII string (null padded, count is width)\\
+"
+=end
+ end
+
+ def test_upcase
+ assert_equal(S("HELLO"), S("hello").upcase)
+ assert_equal(S("HELLO"), S("hello").upcase)
+ assert_equal(S("HELLO"), S("HELLO").upcase)
+ assert_equal(S("ABC HELLO 123"), S("abc HELLO 123").upcase)
+ end
+
+ def test_upcase!
+ a = S("hello")
+ b = a.dup
+ assert_equal(S("HELLO"), a.upcase!)
+ assert_equal(S("HELLO"), a)
+ assert_equal(S("hello"), b)
+
+ a = S("HELLO")
+ assert_nil(a.upcase!)
+ assert_equal(S("HELLO"), a)
+ end
+
+ def test_upto
+ a = S("aa")
+ start = S("aa")
+ count = 0
+ assert_equal(S("aa"), a.upto(S("zz")) {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(676, count)
+ end
+
+ def test_upto_numeric
+ a = S("00")
+ start = S("00")
+ count = 0
+ assert_equal(S("00"), a.upto(S("23")) {|s|
+ assert_equal(start, s, "[ruby-dev:39361]")
+ assert_equal(Encoding::US_ASCII, s.encoding)
+ start.succ!
+ count += 1
+ })
+ assert_equal(24, count, "[ruby-dev:39361]")
+ end
+
+ def test_upto_nonalnum
+ first = S("\u3041")
+ last = S("\u3093")
+ count = 0
+ assert_equal(first, first.upto(last) {|s|
+ count += 1
+ s.replace(last)
+ })
+ assert_equal(83, count, "[ruby-dev:39626]")
+ end
+
+ def test_mod_check
+ assert_raise(RuntimeError) {
+ s = ""
+ s.sub!(/\A/) { s.replace "z" * 2000; "zzz" }
+ }
+ end
+
+ def test_frozen_check
+ assert_raise(RuntimeError) {
+ s = ""
+ s.sub!(/\A/) { s.freeze; "zzz" }
+ }
+ end
+
+ class S2 < String
+ end
+ def test_str_new4
+ s = (0..54).to_a.join # length = 100
+ s2 = S2.new(s[10,90])
+ s3 = s2[10,80]
+ assert_equal((10..54).to_a.to_a.join, s2)
+ assert_equal((15..54).to_a.to_a.join, s3)
+ end
+
+ def test_rb_str_new4
+ s = "a" * 100
+ s2 = s[10,90]
+ assert_equal("a" * 90, s2)
+ s3 = s2[10,80]
+ assert_equal("a" * 80, s3)
+ end
+
+ class StringLike
+ def initialize(str)
+ @str = str
+ end
+
+ def to_str
+ @str
+ end
+ end
+
+ def test_rb_str_to_str
+ assert_equal("ab", "a" + StringLike.new("b"))
+ end
+
+ def test_rb_str_shared_replace
+ s = "a" * 100
+ s.succ!
+ assert_equal("a" * 99 + "b", s)
+ s = ""
+ s.succ!
+ assert_equal("", s)
+ end
+
+ def test_times
+ assert_raise(ArgumentError) { "a" * (-1) }
+ end
+
+ def test_splice!
+ l = S("1234\n234\n34\n4\n")
+ assert_equal(S("1234\n"), l.slice!(/\A.*\n/), "[ruby-dev:31665]")
+ assert_equal(S("234\n"), l.slice!(/\A.*\n/), "[ruby-dev:31665]")
+ assert_equal(S("34\n"), l.slice!(/\A.*\n/), "[ruby-dev:31665]")
+ assert_equal(S("4\n"), l.slice!(/\A.*\n/), "[ruby-dev:31665]")
+ assert_nil(l.slice!(/\A.*\n/), "[ruby-dev:31665]")
+ end
+
+ def test_times2
+ s1 = ''
+ 100.times {|n|
+ s2 = "a" * n
+ assert_equal(s1, s2)
+ s1 << 'a'
+ }
+
+ assert_raise(ArgumentError) { "foo" * (-1) }
+ end
+
+ def test_respond_to
+ o = Object.new
+ def o.respond_to?(arg) [:to_str].include?(arg) ? nil : super end
+ def o.to_str() "" end
+ def o.==(other) "" == other end
+ assert_equal(false, "" == o)
+ end
+
+ def test_match_method
+ assert_equal("bar", "foobarbaz".match(/bar/).to_s)
+
+ o = Regexp.new('foo')
+ def o.match(x, y, z); x + y + z; end
+ assert_equal("foobarbaz", "foo".match(o, "bar", "baz"))
+ x = nil
+ "foo".match(o, "bar", "baz") {|y| x = y }
+ assert_equal("foobarbaz", x)
+
+ assert_raise(ArgumentError) { "foo".match }
+ end
+
+ def test_clear
+ s = "foo" * 100
+ s.clear
+ assert_equal("", s)
+ end
+
+ def test_to_s_2
+ c = Class.new(String)
+ s = c.new
+ s.replace("foo")
+ assert_equal("foo", s.to_s)
+ assert_instance_of(String, s.to_s)
+ end
+
+ def test_inspect_nul
+ bug8290 = '[ruby-core:54458]'
+ s = "\0" + "12"
+ assert_equal '"\u000012"', s.inspect, bug8290
+ s = "\0".b + "12"
+ assert_equal '"\x0012"', s.inspect, bug8290
+ end
+
+ def test_partition
+ assert_equal(%w(he l lo), "hello".partition(/l/))
+ assert_equal(%w(he l lo), "hello".partition("l"))
+ assert_raise(TypeError) { "hello".partition(1) }
+ def (hyphen = Object.new).to_str; "-"; end
+ assert_equal(%w(foo - bar), "foo-bar".partition(hyphen), '[ruby-core:23540]')
+
+ bug6206 = '[ruby-dev:45441]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ s = S("a:".force_encoding(enc))
+ assert_equal([enc]*3, s.partition("|").map(&:encoding), bug6206)
+ end
+
+ assert_equal(["\u30E6\u30FC\u30B6", "@", "\u30C9\u30E1.\u30A4\u30F3"],
+ "\u30E6\u30FC\u30B6@\u30C9\u30E1.\u30A4\u30F3".partition(/[@.]/))
+ end
+
+ def test_rpartition
+ assert_equal(%w(hel l o), "hello".rpartition(/l/))
+ assert_equal(%w(hel l o), "hello".rpartition("l"))
+ assert_raise(TypeError) { "hello".rpartition(1) }
+ def (hyphen = Object.new).to_str; "-"; end
+ assert_equal(%w(foo - bar), "foo-bar".rpartition(hyphen), '[ruby-core:23540]')
+
+ bug6206 = '[ruby-dev:45441]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ s = S("a:".force_encoding(enc))
+ assert_equal([enc]*3, s.rpartition("|").map(&:encoding), bug6206)
+ end
+
+ bug8138 = '[ruby-dev:47183]'
+ assert_equal(["\u30E6\u30FC\u30B6@\u30C9\u30E1", ".", "\u30A4\u30F3"],
+ "\u30E6\u30FC\u30B6@\u30C9\u30E1.\u30A4\u30F3".rpartition(/[@.]/), bug8138)
+ end
+
+ def test_setter
+ assert_raise(TypeError) { $/ = 1 }
+ name = "\u{5206 884c}"
+ assert_separately([], <<-"end;") # do
+ alias $#{name} $/
+ assert_raise_with_message(TypeError, /\\$#{name}/) { $#{name} = 1 }
+ end;
+ end
+
+ def test_to_id
+ c = Class.new
+ c.class_eval do
+ def initialize
+ @foo = :foo
+ end
+ end
+
+ assert_raise(TypeError) do
+ c.class_eval { attr 1 }
+ end
+
+ o = Object.new
+ def o.to_str; :foo; end
+ assert_raise(TypeError) do
+ c.class_eval { attr 1 }
+ end
+
+ class << o;remove_method :to_str;end
+ def o.to_str; "foo"; end
+ assert_nothing_raised do
+ c.class_eval { attr o }
+ end
+ assert_equal(:foo, c.new.foo)
+ end
+
+ def test_gsub_enumerator
+ assert_normal_exit %q{"abc".gsub(/./).next}, "[ruby-dev:34828]"
+ end
+
+ def test_clear_nonasciicompat
+ assert_equal("", "\u3042".encode("ISO-2022-JP").clear)
+ end
+
+ def test_try_convert
+ assert_equal(nil, String.try_convert(1))
+ assert_equal("foo", String.try_convert("foo"))
+ end
+
+ def test_substr_negative_begin
+ assert_equal("\u3042", ("\u3042" * 100)[-1])
+ end
+
+=begin
+ def test_compare_different_encoding_string
+ s1 = "\xff".force_encoding("UTF-8")
+ s2 = "\xff".force_encoding("ISO-2022-JP")
+ assert_equal([-1, 1], [s1 <=> s2, s2 <=> s1].sort)
+ end
+=end
+
+ def test_casecmp
+ assert_equal(1, "\u3042B".casecmp("\u3042a"))
+ end
+
+ def test_upcase2
+ assert_equal("\u3042AB", "\u3042aB".upcase)
+ end
+
+ def test_downcase2
+ assert_equal("\u3042ab", "\u3042aB".downcase)
+ end
+
+ def test_rstrip
+ assert_equal("\u3042", "\u3042 ".rstrip)
+ assert_raise(Encoding::CompatibilityError) { "\u3042".encode("ISO-2022-JP").rstrip }
+ end
+
+=begin
+ def test_symbol_table_overflow
+ assert_in_out_err([], <<-INPUT, [], /symbol table overflow \(symbol [a-z]{8}\) \(RuntimeError\)/)
+ ("aaaaaaaa".."zzzzzzzz").each {|s| s.to_sym }
+ INPUT
+ end
+=end
+
+ def test_shared_force_encoding
+ s = "\u{3066}\u{3059}\u{3068}".gsub(//, '')
+ h = {}
+ h[s] = nil
+ k = h.keys[0]
+ assert_equal(s, k, '[ruby-dev:39068]')
+ assert_equal(Encoding::UTF_8, k.encoding, '[ruby-dev:39068]')
+ s.dup.force_encoding(Encoding::ASCII_8BIT).gsub(//, '')
+ k = h.keys[0]
+ assert_equal(s, k, '[ruby-dev:39068]')
+ assert_equal(Encoding::UTF_8, k.encoding, '[ruby-dev:39068]')
+ end
+
+ def test_ascii_incomat_inspect
+ bug4081 = '[ruby-core:33283]'
+ [Encoding::UTF_16LE, Encoding::UTF_16BE,
+ Encoding::UTF_32LE, Encoding::UTF_32BE].each do |e|
+ assert_equal('"abc"', "abc".encode(e).inspect)
+ assert_equal('"\\u3042\\u3044\\u3046"', "\u3042\u3044\u3046".encode(e).inspect)
+ assert_equal('"ab\\"c"', "ab\"c".encode(e).inspect, bug4081)
+ end
+ begin
+ verbose, $VERBOSE = $VERBOSE, nil
+ ext = Encoding.default_external
+ Encoding.default_external = "us-ascii"
+ $VERBOSE = verbose
+ i = "abc\"\\".force_encoding("utf-8").inspect
+ ensure
+ $VERBOSE = nil
+ Encoding.default_external = ext
+ $VERBOSE = verbose
+ end
+ assert_equal('"abc\\"\\\\"', i, bug4081)
+ end
+
+ def test_dummy_inspect
+ assert_equal('"\e\x24\x42\x22\x4C\x22\x68\e\x28\x42"',
+ "\u{ffe2}\u{2235}".encode("cp50220").inspect)
+ end
+
+ def test_prepend
+ assert_equal(S("hello world!"), "world!".prepend("hello "))
+
+ foo = Object.new
+ def foo.to_str
+ "b"
+ end
+ assert_equal(S("ba"), "a".prepend(foo))
+
+ a = S("world")
+ b = S("hello ")
+ a.prepend(b)
+ assert_equal(S("hello world"), a)
+ assert_equal(S("hello "), b)
+ end
+
+ def u(str)
+ str.force_encoding(Encoding::UTF_8)
+ end
+
+ def test_byteslice
+ assert_equal("h", "hello".byteslice(0))
+ assert_equal(nil, "hello".byteslice(5))
+ assert_equal("o", "hello".byteslice(-1))
+ assert_equal(nil, "hello".byteslice(-6))
+
+ assert_equal("", "hello".byteslice(0, 0))
+ assert_equal("hello", "hello".byteslice(0, 6))
+ assert_equal("hello", "hello".byteslice(0, 6))
+ assert_equal("", "hello".byteslice(5, 1))
+ assert_equal("o", "hello".byteslice(-1, 6))
+ assert_equal(nil, "hello".byteslice(-6, 1))
+ assert_equal(nil, "hello".byteslice(0, -1))
+
+ assert_equal("h", "hello".byteslice(0..0))
+ assert_equal("", "hello".byteslice(5..0))
+ assert_equal("o", "hello".byteslice(4..5))
+ assert_equal(nil, "hello".byteslice(6..0))
+ assert_equal("", "hello".byteslice(-1..0))
+ assert_equal("llo", "hello".byteslice(-3..5))
+
+ assert_equal(u("\x81"), "\u3042".byteslice(1))
+ assert_equal(u("\x81\x82"), "\u3042".byteslice(1, 2))
+ assert_equal(u("\x81\x82"), "\u3042".byteslice(1..2))
+
+ assert_equal(u("\x82")+("\u3042"*9), ("\u3042"*10).byteslice(2, 28))
+
+ bug7954 = '[ruby-dev:47108]'
+ assert_equal(false, "\u3042".byteslice(0, 2).valid_encoding?, bug7954)
+ assert_equal(false, ("\u3042"*10).byteslice(0, 20).valid_encoding?, bug7954)
+ end
+
+ def test_unknown_string_option
+ str = nil
+ assert_nothing_raised(SyntaxError) do
+ eval(%{
+ str = begin"hello"end
+ })
+ end
+ assert_equal "hello", str
+ end
+
+ def test_eq_tilde_can_be_overridden
+ assert_separately([], <<-RUBY)
+ class String
+ undef =~
+ def =~(str)
+ "foo"
+ end
+ end
+
+ assert_equal("foo", "" =~ //)
+ RUBY
+ end
+
+ class Bug9581 < String
+ def =~ re; :foo end
+ end
+
+ def test_regexp_match_subclass
+ s = Bug9581.new("abc")
+ r = /abc/
+ assert_equal(:foo, s =~ r)
+ assert_equal(:foo, s.send(:=~, r))
+ assert_equal(:foo, s.send(:=~, /abc/))
+ assert_equal(:foo, s =~ /abc/, "should not use optimized instruction")
+ end
+
+ def test_LSHIFT_neary_long_max
+ return unless @cls == String
+ assert_ruby_status([], <<-'end;', '[ruby-core:61886] [Bug #9709]', timeout: 20)
+ begin
+ a = "a" * 0x4000_0000
+ a << "a" * 0x1_0000
+ rescue NoMemoryError
+ end
+ end;
+ end if [0].pack("l!").bytesize < [nil].pack("p").bytesize
+ # enable only when string size range is smaller than memory space
+end
+
+class TestString2 < TestString
+ def initialize(*args)
+ super
+ @cls = S2
+ end
+end
diff --git a/jni/ruby/test/ruby/test_stringchar.rb b/jni/ruby/test/ruby/test_stringchar.rb
new file mode 100644
index 0000000..7f7342c
--- /dev/null
+++ b/jni/ruby/test/ruby/test_stringchar.rb
@@ -0,0 +1,181 @@
+require 'test/unit'
+
+class TestStringchar < Test::Unit::TestCase
+ def test_string
+ assert_equal("abcd", "abcd")
+ assert_match(/abcd/, "abcd")
+ assert_operator("abcd", :===, "abcd")
+ # compile time string concatenation
+ assert_equal("abcd", "ab" "cd")
+ assert_equal("22aacd44", "#{22}aa" "cd#{44}")
+ assert_equal("22aacd445566", "#{22}aa" "cd#{44}" "55" "#{66}")
+ assert_operator("abc", :!~, /^$/)
+ assert_operator("abc\n", :!~, /^$/)
+ assert_operator("abc", :!~, /^d*$/)
+ assert_equal(3, ("abc" =~ /d*$/))
+ assert("" =~ /^$/)
+ assert("\n" =~ /^$/)
+ assert("a\n\n" =~ /^$/)
+ assert("abcabc" =~ /.*a/); assert_equal("abca", $&)
+ assert("abcabc" =~ /.*c/); assert_equal("abcabc", $&)
+ assert("abcabc" =~ /.*?a/); assert_equal("a", $&)
+ assert("abcabc" =~ /.*?c/); assert_equal("abc", $&)
+ assert(/(.|\n)*?\n(b|\n)/ =~ "a\nb\n\n"); assert_equal("a\nb", $&)
+
+ assert(/^(ab+)+b/ =~ "ababb"); assert_equal("ababb", $&)
+ assert(/^(?:ab+)+b/ =~ "ababb"); assert_equal("ababb", $&)
+ assert(/^(ab+)+/ =~ "ababb"); assert_equal("ababb", $&)
+ assert(/^(?:ab+)+/ =~ "ababb"); assert_equal("ababb", $&)
+
+ assert(/(\s+\d+){2}/ =~ " 1 2"); assert_equal(" 1 2", $&)
+ assert(/(?:\s+\d+){2}/ =~ " 1 2"); assert_equal(" 1 2", $&)
+
+ x = <<END;
+ABCD
+ABCD
+END
+ x.gsub!(/((.|\n)*?)B((.|\n)*?)D/m ,'\1\3')
+ assert_equal("AC\nAC\n", x)
+
+ assert_match(/foo(?=(bar)|(baz))/, "foobar")
+ assert_match(/foo(?=(bar)|(baz))/, "foobaz")
+
+ $foo = "abc"
+ assert_equal("abc = abc", "#$foo = abc")
+ assert_equal("abc = abc", "#{$foo} = abc")
+
+ foo = "abc"
+ assert_equal("abc = abc", "#{foo} = abc")
+
+ assert_equal('-----', '-' * 5)
+ assert_equal('-', '-' * 1)
+ assert_equal('', '-' * 0)
+
+ foo = '-'
+ assert_equal('-----', foo * 5)
+ assert_equal('-', foo * 1)
+ assert_equal('', foo * 0)
+
+ x = "a.gif"
+ assert_equal("gif", x.sub(/.*\.([^\.]+)$/, '\1'))
+ assert_equal("b.gif", x.sub(/.*\.([^\.]+)$/, 'b.\1'))
+ assert_equal("", x.sub(/.*\.([^\.]+)$/, '\2'))
+ assert_equal("ab", x.sub(/.*\.([^\.]+)$/, 'a\2b'))
+ assert_equal("<a.gif>", x.sub(/.*\.([^\.]+)$/, '<\&>'))
+ end
+
+ def test_char
+ # character constants(assumes ASCII)
+ assert_equal(?a, "a"[0])
+ assert_equal(?a, ?a)
+ assert_equal("\1", ?\C-a)
+ assert_equal("\341", ?\M-a)
+ assert_equal("\201", ?\M-\C-a)
+ assert_equal(?A, "a".upcase![0])
+ assert_equal(?a, "A".downcase![0])
+ assert_equal("ABC", "abc".tr!("a-z", "A-Z"))
+ assert_equal("ABC", "aabbcccc".tr_s!("a-z", "A-Z"))
+ assert_equal("abc", "abcc".squeeze!("a-z"))
+ assert_equal("ad", "abcd".delete!("bc"))
+
+ x = "abcdef"
+ y = [ ?a, ?b, ?c, ?d, ?e, ?f ]
+ bad = false
+ x.each_byte {|i|
+ if i.chr != y.shift
+ bad = true
+ break
+ end
+ }
+ assert(!bad)
+
+ s = "a string"
+ s[0..s.size]="another string"
+ assert_equal("another string", s)
+
+ s = <<EOS
+#{
+[1,2,3].join(",")
+}
+EOS
+ assert_equal("1,2,3\n", s)
+ assert_equal(926381, "Just".to_i(36))
+ assert_equal(-23200231779, "-another".to_i(36))
+ assert_equal("ruby", 1299022.to_s(36))
+ assert_equal("-hacker", -1045307475.to_s(36))
+ assert_equal(265419172580680477752431643787347, "Just_another_Ruby_hacker".to_i(36))
+ assert_equal("-justanotherrubyhacker", -265419172580680477752431643787347.to_s(36))
+
+ a = []
+ (0..255).each {|n|
+ ch = [n].pack("C")
+ a.push ch if /a#{Regexp.quote ch}b/x =~ "ab"
+ }
+ assert_equal(0, a.size)
+ end
+
+ def test_bang
+ s = "aBc"
+ s.upcase
+ assert_equal("aBc", s)
+ s.upcase!
+ assert_equal("ABC", s)
+
+ s = "aBc"
+ s.downcase
+ assert_equal("aBc", s)
+ s.downcase!
+ assert_equal("abc", s)
+
+ s = "aBc"
+ s.swapcase
+ assert_equal("aBc", s)
+ s.swapcase!
+ assert_equal("AbC", s)
+
+ s = "aBc"
+ s.capitalize
+ assert_equal("aBc", s)
+ s.capitalize!
+ assert_equal("Abc", s)
+
+ s = "aBc"
+ s.tr("a-z", "A-Z")
+ assert_equal("aBc", s)
+ s.tr!("a-z", "A-Z")
+ assert_equal("ABC", s)
+
+ s = "aaBBcc"
+ s.tr_s("a-z", "A-Z")
+ assert_equal("aaBBcc", s)
+ s.tr_s!("a-z", "A-Z")
+ assert_equal("ABBC", s)
+
+ s = "aaBBcc"
+ s.squeeze("a-z")
+ assert_equal("aaBBcc", s)
+ s.squeeze!("a-z")
+ assert_equal("aBBc", s)
+
+ s = "aaBBcc"
+ s.delete("a-z")
+ assert_equal("aaBBcc", s)
+ s.delete!("a-z")
+ assert_equal("BB", s)
+ end
+
+ def test_dump
+ bug3996 = '[ruby-core:32935]'
+ Encoding.list.find_all {|enc| enc.ascii_compatible?}.each do |enc|
+ (0..256).map do |c|
+ begin
+ s = c.chr(enc)
+ rescue RangeError, ArgumentError
+ break
+ else
+ assert_not_match(/\0/, s.dump, bug3996)
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_struct.rb b/jni/ruby/test/ruby/test_struct.rb
new file mode 100644
index 0000000..df859f7
--- /dev/null
+++ b/jni/ruby/test/ruby/test_struct.rb
@@ -0,0 +1,374 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+require 'timeout'
+
+module TestStruct
+ def test_struct
+ struct_test = @Struct.new("Test", :foo, :bar)
+ assert_equal(@Struct::Test, struct_test)
+
+ test = struct_test.new(1, 2)
+ assert_equal(1, test.foo)
+ assert_equal(2, test.bar)
+ assert_equal(1, test[0])
+ assert_equal(2, test[1])
+
+ a, b = test.to_a
+ assert_equal(1, a)
+ assert_equal(2, b)
+
+ test[0] = 22
+ assert_equal(22, test.foo)
+
+ test.bar = 47
+ assert_equal(47, test.bar)
+ end
+
+ # [ruby-dev:26247] more than 10 struct members causes segmentation fault
+ def test_morethan10members
+ list = %w( a b c d e f g h i j k l m n o p )
+ until list.empty?
+ c = @Struct.new(* list.map {|ch| ch.intern }).new
+ list.each do |ch|
+ c.__send__(ch)
+ end
+ list.pop
+ end
+ end
+
+ def test_small_structs
+ names = [:a, :b, :c, :d]
+ 1.upto(4) {|n|
+ fields = names[0, n]
+ klass = @Struct.new(*fields)
+ o = klass.new(*(0...n).to_a)
+ fields.each_with_index {|name, i|
+ assert_equal(i, o[name])
+ }
+ o = klass.new(*(0...n).to_a.reverse)
+ fields.each_with_index {|name, i|
+ assert_equal(n-i-1, o[name])
+ }
+ }
+ end
+
+ def test_inherit
+ klass = @Struct.new(:a)
+ klass2 = Class.new(klass)
+ o = klass2.new(1)
+ assert_equal(1, o.a)
+ end
+
+ def test_members
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ assert_equal([:a], klass.members)
+ assert_equal([:a], o.members)
+ end
+
+ def test_ref
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ assert_equal(1, o[:a])
+ assert_raise(NameError) { o[:b] }
+ end
+
+ def test_set
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ o[:a] = 2
+ assert_equal(2, o[:a])
+ assert_raise(NameError) { o[:b] = 3 }
+ end
+
+ def test_struct_new
+ assert_raise(NameError) { @Struct.new("foo") }
+ assert_nothing_raised { @Struct.new("Foo") }
+ @Struct.instance_eval { remove_const(:Foo) }
+ assert_nothing_raised { @Struct.new(:a) { } }
+ assert_raise(RuntimeError) { @Struct.new(:a) { raise } }
+
+ assert_equal([:utime, :stime, :cutime, :cstime], Process.times.members)
+ end
+
+ def test_initialize
+ klass = @Struct.new(:a)
+ assert_raise(ArgumentError) { klass.new(1, 2) }
+ end
+
+ def test_each
+ klass = @Struct.new(:a, :b)
+ o = klass.new(1, 2)
+ assert_equal([1, 2], o.each.to_a)
+ end
+
+ def test_each_pair
+ klass = @Struct.new(:a, :b)
+ o = klass.new(1, 2)
+ assert_equal([[:a, 1], [:b, 2]], o.each_pair.to_a)
+ bug7382 = '[ruby-dev:46533]'
+ a = []
+ o.each_pair {|x| a << x}
+ assert_equal([[:a, 1], [:b, 2]], a, bug7382)
+ end
+
+ def test_inspect
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ assert_equal("#<struct a=1>", o.inspect)
+ o.a = o
+ assert_match(/^#<struct a=#<struct #<.*?>:...>>$/, o.inspect)
+
+ @Struct.new("Foo", :a)
+ o = @Struct::Foo.new(1)
+ assert_equal("#<struct #@Struct::Foo a=1>", o.inspect)
+ @Struct.instance_eval { remove_const(:Foo) }
+
+ klass = @Struct.new(:a, :b)
+ o = klass.new(1, 2)
+ assert_equal("#<struct a=1, b=2>", o.inspect)
+
+ klass = @Struct.new(:@a)
+ o = klass.new(1)
+ assert_equal(1, o.__send__(:@a))
+ assert_equal("#<struct :@a=1>", o.inspect)
+ o.__send__(:"@a=", 2)
+ assert_equal(2, o.__send__(:@a))
+ assert_equal("#<struct :@a=2>", o.inspect)
+ o.__send__("@a=", 3)
+ assert_equal(3, o.__send__(:@a))
+ assert_equal("#<struct :@a=3>", o.inspect)
+
+ methods = klass.instance_methods(false)
+ assert_equal([:@a, :"@a="].inspect, methods.inspect, '[Bug #8756]')
+ assert_include(methods, :@a)
+ assert_include(methods, :"@a=")
+ end
+
+ def test_init_copy
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ assert_equal(o, o.dup)
+ end
+
+ def test_aref
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ assert_equal(1, o[0])
+ assert_raise(IndexError) { o[-2] }
+ assert_raise(IndexError) { o[1] }
+ assert_raise_with_message(NameError, /foo/) {o["foo"]}
+ assert_raise_with_message(NameError, /foo/) {o[:foo]}
+ end
+
+ def test_aset
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ o[0] = 2
+ assert_equal(2, o[:a])
+ assert_raise(IndexError) { o[-2] = 3 }
+ assert_raise(IndexError) { o[1] = 3 }
+ assert_raise_with_message(NameError, /foo/) {o["foo"] = 3}
+ assert_raise_with_message(NameError, /foo/) {o[:foo] = 3}
+ end
+
+ def test_values_at
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal([2, 4, 6], o.values_at(1, 3, 5))
+ assert_equal([2, 3, 4, 3, 4, 5], o.values_at(1..3, 2...5))
+ end
+
+ def test_select
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal([1, 3, 5], o.select {|v| v % 2 != 0 })
+ assert_raise(ArgumentError) { o.select(1) }
+ end
+
+ def test_big_struct
+ klass1 = @Struct.new(*('a'..'z').map(&:to_sym))
+ o = klass1.new
+ assert_nil o.z
+ assert_equal(:foo, o.z = :foo)
+ assert_equal(:foo, o.z)
+ assert_equal(:foo, o[25])
+ end
+
+ def test_overridden_aset
+ bug10601 = '[ruby-core:66846] [Bug #10601]: should not be affected by []= method'
+
+ struct = Class.new(Struct.new(*(:a..:z), :result)) do
+ def []=(*args)
+ raise args.inspect
+ end
+ end
+
+ obj = struct.new
+ assert_nothing_raised(RuntimeError, bug10601) do
+ obj.result = 42
+ end
+ assert_equal(42, obj.result, bug10601)
+ end
+
+ def test_overridden_aref
+ bug10601 = '[ruby-core:66846] [Bug #10601]: should not be affected by [] method'
+
+ struct = Class.new(Struct.new(*(:a..:z), :result)) do
+ def [](*args)
+ raise args.inspect
+ end
+ end
+
+ obj = struct.new
+ obj.result = 42
+ result = assert_nothing_raised(RuntimeError, bug10601) do
+ break obj.result
+ end
+ assert_equal(42, result, bug10601)
+ end
+
+ def test_equal
+ klass1 = @Struct.new(:a)
+ klass2 = @Struct.new(:a, :b)
+ o1 = klass1.new(1)
+ o2 = klass1.new(1)
+ o3 = klass2.new(1)
+ assert_equal(o1, o2)
+ assert_not_equal(o1, o3)
+ end
+
+ def test_hash
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ assert_kind_of(Fixnum, o.hash)
+ end
+
+ def test_eql
+ klass1 = @Struct.new(:a)
+ klass2 = @Struct.new(:a, :b)
+ o1 = klass1.new(1)
+ o2 = klass1.new(1)
+ o3 = klass2.new(1)
+ assert_operator(o1, :eql?, o2)
+ assert_not_operator(o1, :eql?, o3)
+ end
+
+ def test_size
+ klass = @Struct.new(:a)
+ o = klass.new(1)
+ assert_equal(1, o.size)
+ end
+
+ def test_error
+ assert_raise(TypeError){
+ @Struct.new(0)
+ }
+ end
+
+ def test_redefinition_warning
+ @Struct.new("RedefinitionWarning")
+ e = EnvUtil.verbose_warning do
+ @Struct.new("RedefinitionWarning")
+ end
+ assert_match(/redefining constant #@Struct::RedefinitionWarning/, e)
+ end
+
+ def test_nonascii
+ struct_test = @Struct.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
+ assert_equal(@Struct.const_get("R\u{e9}sum\u{e9}"), struct_test, '[ruby-core:24849]')
+ a = struct_test.new(42)
+ assert_equal("#<struct #@Struct::R\u{e9}sum\u{e9} r\u{e9}sum\u{e9}=42>", a.inspect, '[ruby-core:24849]')
+ e = EnvUtil.verbose_warning do
+ @Struct.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
+ end
+ assert_nothing_raised(Encoding::CompatibilityError) do
+ assert_match(/redefining constant #@Struct::R\u{e9}sum\u{e9}/, e)
+ end
+ end
+
+ def test_junk
+ struct_test = @Struct.new("Foo", "a\000")
+ o = struct_test.new(1)
+ assert_equal(1, o.send("a\000"))
+ @Struct.instance_eval { remove_const(:Foo) }
+ end
+
+ def test_comparison_when_recursive
+ klass1 = @Struct.new(:a, :b, :c)
+
+ x = klass1.new(1, 2, nil); x.c = x
+ y = klass1.new(1, 2, nil); y.c = y
+ Timeout.timeout(1) {
+ assert_equal x, y
+ assert_operator x, :eql?, y
+ }
+
+ z = klass1.new(:something, :other, nil); z.c = z
+ Timeout.timeout(1) {
+ assert_not_equal x, z
+ assert_not_operator x, :eql?, z
+ }
+
+ x.c = y; y.c = x
+ Timeout.timeout(1) {
+ assert_equal x, y
+ assert_operator x, :eql?, y
+ }
+
+ x.c = z; z.c = x
+ Timeout.timeout(1) {
+ assert_not_equal x, z
+ assert_not_operator x, :eql?, z
+ }
+ end
+
+ def test_to_h
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal({a:1, b:2, c:3, d:4, e:5, f:6}, o.to_h)
+ end
+
+ def test_question_mark_in_member
+ klass = @Struct.new(:a, :b?)
+ x = Object.new
+ o = klass.new("test", x)
+ assert_same(x, o.b?)
+ o.send("b?=", 42)
+ assert_equal(42, o.b?)
+ end
+
+ def test_bang_mark_in_member
+ klass = @Struct.new(:a, :b!)
+ x = Object.new
+ o = klass.new("test", x)
+ assert_same(x, o.b!)
+ o.send("b!=", 42)
+ assert_equal(42, o.b!)
+ end
+
+ def test_setter_method_returns_value
+ klass = @Struct.new(:a)
+ x = klass.new
+ assert_equal "[Bug #9353]", x.send(:a=, "[Bug #9353]")
+ end
+
+ class TopStruct < Test::Unit::TestCase
+ include TestStruct
+
+ def initialize(*)
+ super
+ @Struct = Struct
+ end
+ end
+
+ class SubStruct < Test::Unit::TestCase
+ include TestStruct
+ SubStruct = Class.new(Struct)
+
+ def initialize(*)
+ super
+ @Struct = SubStruct
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_super.rb b/jni/ruby/test/ruby/test_super.rb
new file mode 100644
index 0000000..7d4048b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_super.rb
@@ -0,0 +1,528 @@
+require 'test/unit'
+
+class TestSuper < Test::Unit::TestCase
+ class Base
+ def single(a) a end
+ def double(a, b) [a,b] end
+ def array(*a) a end
+ def optional(a = 0) a end
+ def keyword(**a) a end
+ end
+ class Single1 < Base
+ def single(*) super end
+ end
+ class Single2 < Base
+ def single(a,*) super end
+ end
+ class Double1 < Base
+ def double(*) super end
+ end
+ class Double2 < Base
+ def double(a,*) super end
+ end
+ class Double3 < Base
+ def double(a,b,*) super end
+ end
+ class Array1 < Base
+ def array(*) super end
+ end
+ class Array2 < Base
+ def array(a,*) super end
+ end
+ class Array3 < Base
+ def array(a,b,*) super end
+ end
+ class Array4 < Base
+ def array(a,b,c,*) super end
+ end
+ class Optional1 < Base
+ def optional(a = 1) super end
+ end
+ class Optional2 < Base
+ def optional(a, b = 1) super end
+ end
+ class Optional3 < Base
+ def single(a = 1) super end
+ end
+ class Optional4 < Base
+ def array(a = 1, *) super end
+ end
+ class Optional5 < Base
+ def array(a = 1, b = 2, *) super end
+ end
+ class Keyword1 < Base
+ def keyword(foo: "keyword1") super end
+ end
+ class Keyword2 < Base
+ def keyword(foo: "keyword2")
+ foo = "changed1"
+ x = super
+ foo = "changed2"
+ y = super
+ [x, y]
+ end
+ end
+
+ def test_single1
+ assert_equal(1, Single1.new.single(1))
+ end
+ def test_single2
+ assert_equal(1, Single2.new.single(1))
+ end
+ def test_double1
+ assert_equal([1, 2], Double1.new.double(1, 2))
+ end
+ def test_double2
+ assert_equal([1, 2], Double2.new.double(1, 2))
+ end
+ def test_double3
+ assert_equal([1, 2], Double3.new.double(1, 2))
+ end
+ def test_array1
+ assert_equal([], Array1.new.array())
+ assert_equal([1], Array1.new.array(1))
+ end
+ def test_array2
+ assert_equal([1], Array2.new.array(1))
+ assert_equal([1,2], Array2.new.array(1, 2))
+ end
+ def test_array3
+ assert_equal([1,2], Array3.new.array(1, 2))
+ assert_equal([1,2,3], Array3.new.array(1, 2, 3))
+ end
+ def test_array4
+ assert_equal([1,2,3], Array4.new.array(1, 2, 3))
+ assert_equal([1,2,3,4], Array4.new.array(1, 2, 3, 4))
+ end
+ def test_optional1
+ assert_equal(9, Optional1.new.optional(9))
+ assert_equal(1, Optional1.new.optional)
+ end
+ def test_optional2
+ assert_raise(ArgumentError) do
+ # call Base#optional with 2 arguments; the 2nd arg is supplied
+ assert_equal(9, Optional2.new.optional(9))
+ end
+ assert_raise(ArgumentError) do
+ # call Base#optional with 2 arguments
+ assert_equal(9, Optional2.new.optional(9, 2))
+ end
+ end
+ def test_optional3
+ assert_equal(9, Optional3.new.single(9))
+ # call Base#single with 1 argument; the arg is supplied
+ assert_equal(1, Optional3.new.single)
+ end
+ def test_optional4
+ assert_equal([1], Optional4.new.array)
+ assert_equal([9], Optional4.new.array(9))
+ assert_equal([9, 8], Optional4.new.array(9, 8))
+ end
+ def test_optional5
+ assert_equal([1, 2], Optional5.new.array)
+ assert_equal([9, 2], Optional5.new.array(9))
+ assert_equal([9, 8], Optional5.new.array(9, 8))
+ assert_equal([9, 8, 7], Optional5.new.array(9, 8, 7))
+ end
+ def test_keyword1
+ assert_equal({foo: "keyword1"}, Keyword1.new.keyword)
+ bug8008 = '[ruby-core:53114] [Bug #8008]'
+ assert_equal({foo: bug8008}, Keyword1.new.keyword(foo: bug8008))
+ end
+ def test_keyword2
+ assert_equal([{foo: "changed1"}, {foo: "changed2"}], Keyword2.new.keyword)
+ end
+
+ class A
+ def tt(aa)
+ "A#tt"
+ end
+
+ def uu(a)
+ class << self
+ define_method(:tt) do |sym|
+ super(sym)
+ end
+ end
+ end
+ end
+
+ def test_define_method
+ a = A.new
+ a.uu(12)
+ assert_equal("A#tt", a.tt(12), "[ruby-core:3856]")
+ assert_raise_with_message(RuntimeError, /implicit argument passing of super from method defined by define_method/, "[ruby-core:24244]") {
+ lambda {
+ Class.new {
+ define_method(:a) {super}
+ }.new.a
+ }.call
+ }
+ end
+
+ class SubSeq
+ def initialize
+ @first=11
+ @first or fail
+ end
+
+ def subseq
+ @first or fail
+ end
+ end
+
+ class Indexed
+ def subseq
+ SubSeq.new
+ end
+ end
+
+ Overlaid = proc do
+ class << self
+ def subseq
+ super.instance_eval(& Overlaid)
+ end
+ end
+ end
+
+ def test_overlaid
+ assert_nothing_raised('[ruby-dev:40959]') do
+ overlaid = proc do |obj|
+ def obj.reverse
+ super
+ end
+ end
+ overlaid.call(str = "123")
+ overlaid.call([1,2,3])
+ str.reverse
+ end
+
+ assert_nothing_raised('[ruby-core:27230]') do
+ mid=Indexed.new
+ mid.instance_eval(&Overlaid)
+ mid.subseq
+ mid.subseq
+ end
+ end
+
+ module DoubleInclude
+ class Base
+ def foo
+ [:Base]
+ end
+ end
+
+ module Override
+ def foo
+ super << :Override
+ end
+ end
+
+ class A < Base
+ end
+
+ class B < A
+ end
+
+ B.send(:include, Override)
+ A.send(:include, Override)
+ end
+
+ # [Bug #3351]
+ def test_double_include
+ assert_equal([:Base, :Override], DoubleInclude::B.new.foo)
+ # should be changed as follows?
+ # assert_equal([:Base, :Override, :Override], DoubleInclude::B.new.foo)
+ end
+
+ module DoubleInclude2
+ class Base
+ def foo
+ [:Base]
+ end
+ end
+
+ module Override
+ def foo
+ super << :Override
+ end
+ end
+
+ class A < Base
+ def foo
+ super << :A
+ end
+ end
+
+ class B < A
+ def foo
+ super << :B
+ end
+ end
+
+ B.send(:include, Override)
+ A.send(:include, Override)
+ end
+
+ def test_double_include2
+ assert_equal([:Base, :Override, :A, :Override, :B],
+ DoubleInclude2::B.new.foo)
+ end
+
+ def test_super_in_instance_eval
+ super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+ def foo
+ return [:super, self]
+ end
+ }
+ sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+ def foo
+ x = Object.new
+ x.instance_eval do
+ super()
+ end
+ end
+ }
+ obj = sub_class.new
+ assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
+ obj.foo
+ end
+ end
+
+ def test_super_in_instance_eval_with_define_method
+ super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+ def foo
+ return [:super, self]
+ end
+ }
+ sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+ define_method(:foo) do
+ x = Object.new
+ x.instance_eval do
+ super()
+ end
+ end
+ }
+ obj = sub_class.new
+ assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
+ obj.foo
+ end
+ end
+
+ def test_super_in_orphan_block
+ super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+ def foo
+ return [:super, self]
+ end
+ }
+ sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+ def foo
+ lambda { super() }
+ end
+ }
+ obj = sub_class.new
+ assert_equal([:super, obj], obj.foo.call)
+ end
+
+ def test_super_in_orphan_block_with_instance_eval
+ super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+ def foo
+ return [:super, self]
+ end
+ }
+ sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+ def foo
+ x = Object.new
+ x.instance_eval do
+ lambda { super() }
+ end
+ end
+ }
+ obj = sub_class.new
+ assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
+ obj.foo.call
+ end
+ end
+
+ def test_yielding_super
+ a = Class.new { def yielder; yield; end }
+ x = Class.new { define_singleton_method(:hello) { 'hi' } }
+ y = Class.new(x) {
+ define_singleton_method(:hello) {
+ m = a.new
+ m.yielder { super() }
+ }
+ }
+ assert_equal 'hi', y.hello
+ end
+
+ def test_super_in_thread
+ hoge = Class.new {
+ def bar; 'hoge'; end
+ }
+ foo = Class.new(hoge) {
+ def bar; Thread.new { super }.join.value; end
+ }
+
+ assert_equal 'hoge', foo.new.bar
+ end
+
+ def assert_super_in_block(type)
+ bug7064 = '[ruby-core:47680]'
+ assert_normal_exit "#{type} {super}", bug7064
+ end
+
+ def test_super_in_at_exit
+ assert_super_in_block("at_exit")
+ end
+ def test_super_in_END
+ assert_super_in_block("END")
+ end
+
+ def test_super_in_BEGIN
+ assert_super_in_block("BEGIN")
+ end
+
+ class X
+ def foo(*args)
+ args
+ end
+ end
+
+ class Y < X
+ define_method(:foo) do |*args|
+ super(*args)
+ end
+ end
+
+ def test_super_splat
+ # [ruby-list:49575]
+ y = Y.new
+ assert_equal([1, 2], y.foo(1, 2))
+ assert_equal([1, false], y.foo(1, false))
+ assert_equal([1, 2, 3, 4, 5], y.foo(1, 2, 3, 4, 5))
+ assert_equal([false, true], y.foo(false, true))
+ assert_equal([false, false], y.foo(false, false))
+ assert_equal([1, 2, 3, false, 5], y.foo(1, 2, 3, false, 5))
+ end
+
+ def test_missing_super_in_method_module
+ bug9315 = '[ruby-core:59358] [Bug #9315]'
+ a = Module.new do
+ def foo
+ super
+ end
+ end
+ b = Class.new do
+ include a
+ end
+ assert_raise(NoMethodError, bug9315) do
+ b.new.method(:foo).call
+ end
+ end
+
+ def test_module_super_in_method_module
+ bug9315 = '[ruby-core:59589] [Bug #9315]'
+ a = Module.new do
+ def foo
+ super
+ end
+ end
+ c = Class.new do
+ def foo
+ :ok
+ end
+ end
+ o = c.new.extend(a)
+ assert_nothing_raised(NoMethodError, bug9315) do
+ assert_equal(:ok, o.method(:foo).call, bug9315)
+ end
+ end
+
+ def test_missing_super_in_module_unbound_method
+ bug9377 = '[ruby-core:59619] [Bug #9377]'
+
+ a = Module.new do
+ def foo; super end
+ end
+
+ m = a.instance_method(:foo).bind(Object.new)
+ assert_raise(NoMethodError, bug9377) do
+ m.call
+ end
+ end
+
+ def test_super_in_module_unbound_method
+ bug9721 = '[ruby-core:61936] [Bug #9721]'
+
+ a = Module.new do
+ def foo(result)
+ result << "A"
+ end
+ end
+
+ b = Module.new do
+ def foo(result)
+ result << "B"
+ super
+ end
+ end
+
+ um = b.instance_method(:foo)
+
+ m = um.bind(Object.new.extend(a))
+ result = []
+ assert_nothing_raised(NoMethodError, bug9721) do
+ m.call(result)
+ end
+ assert_equal(%w[B A], result, bug9721)
+
+ bug9740 = '[ruby-core:62017] [Bug #9740]'
+
+ b.module_eval do
+ define_method(:foo) do |result|
+ um.bind(self).call(result)
+ end
+ end
+
+ result.clear
+ o = Object.new.extend(a).extend(b)
+ assert_nothing_raised(NoMethodError, SystemStackError, bug9740) do
+ o.foo(result)
+ end
+ assert_equal(%w[B A], result, bug9721)
+ end
+
+ def test_from_eval
+ bug10263 = '[ruby-core:65122] [Bug #10263a]'
+ a = Class.new do
+ def foo
+ "A"
+ end
+ end
+ b = Class.new(a) do
+ def foo
+ binding.eval("super")
+ end
+ end
+ assert_equal("A", b.new.foo, bug10263)
+ end
+
+ def test_super_with_block
+ a = Class.new do
+ def foo
+ yield
+ end
+ end
+
+ b = Class.new(a) do
+ def foo
+ super{
+ "b"
+ }
+ end
+ end
+
+ assert_equal "b", b.new.foo{"c"}
+ end
+end
diff --git a/jni/ruby/test/ruby/test_symbol.rb b/jni/ruby/test/ruby/test_symbol.rb
new file mode 100644
index 0000000..ee29cd5
--- /dev/null
+++ b/jni/ruby/test/ruby/test_symbol.rb
@@ -0,0 +1,289 @@
+require 'test/unit'
+
+class TestSymbol < Test::Unit::TestCase
+ # [ruby-core:3573]
+
+ def assert_eval_inspected(sym, valid = true)
+ n = sym.inspect
+ if valid
+ bug5136 = '[ruby-dev:44314]'
+ assert_not_match(/\A:"/, n, bug5136)
+ end
+ assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(n))}
+ end
+
+ def test_inspect_invalid
+ # 2) Symbol#inspect sometimes returns invalid symbol representations:
+ assert_eval_inspected(:"!")
+ assert_eval_inspected(:"=", false)
+ assert_eval_inspected(:"0", false)
+ assert_eval_inspected(:"$1")
+ assert_eval_inspected(:"@1", false)
+ assert_eval_inspected(:"@@1", false)
+ assert_eval_inspected(:"@", false)
+ assert_eval_inspected(:"@@", false)
+ end
+
+ def assert_inspect_evaled(n)
+ assert_nothing_raised(SyntaxError) {assert_equal(n, eval(n).inspect)}
+ end
+
+ def test_inspect_suboptimal
+ # 3) Symbol#inspect sometimes returns suboptimal symbol representations:
+ assert_inspect_evaled(':foo')
+ assert_inspect_evaled(':foo!')
+ assert_inspect_evaled(':bar?')
+ assert_inspect_evaled(":<<")
+ assert_inspect_evaled(':>>')
+ assert_inspect_evaled(':<=')
+ assert_inspect_evaled(':>=')
+ assert_inspect_evaled(':=~')
+ assert_inspect_evaled(':==')
+ assert_inspect_evaled(':===')
+ assert_raise(SyntaxError) {eval ':='}
+ assert_inspect_evaled(':*')
+ assert_inspect_evaled(':**')
+ assert_raise(SyntaxError) {eval ':***'}
+ assert_inspect_evaled(':+')
+ assert_inspect_evaled(':-')
+ assert_inspect_evaled(':+@')
+ assert_inspect_evaled(':-@')
+ assert_inspect_evaled(':|')
+ assert_inspect_evaled(':^')
+ assert_inspect_evaled(':&')
+ assert_inspect_evaled(':/')
+ assert_inspect_evaled(':%')
+ assert_inspect_evaled(':~')
+ assert_inspect_evaled(':`')
+ assert_inspect_evaled(':[]')
+ assert_inspect_evaled(':[]=')
+ assert_raise(SyntaxError) {eval ':||'}
+ assert_raise(SyntaxError) {eval ':&&'}
+ assert_raise(SyntaxError) {eval ':['}
+ end
+
+ def test_inspect_dollar
+ # 4) :$- always treats next character literally:
+ assert_raise(SyntaxError) {eval ':$-'}
+ assert_raise(SyntaxError) {eval ":$-\n"}
+ assert_raise(SyntaxError) {eval ":$- "}
+ assert_raise(SyntaxError) {eval ":$-#"}
+ assert_raise(SyntaxError) {eval ':$-('}
+ end
+
+ def test_inspect_number
+ # 5) Inconsistency between :$0 and :$1? The first one is valid, but the
+ # latter isn't.
+ assert_inspect_evaled(':$0')
+ assert_inspect_evaled(':$1')
+ end
+
+ def test_inspect
+ valid = %W{$a @a @@a < << <= <=> > >> >= =~ == === * ** + +@ - -@
+ | ^ & / % ~ \` [] []= ! != !~ a a? a! a= A A? A! A=}
+ valid.each do |sym|
+ assert_equal(':' + sym, sym.intern.inspect)
+ end
+
+ invalid = %w{$a? $a! $a= @a? @a! @a= @@a? @@a! @@a= =}
+ invalid.each do |sym|
+ assert_equal(':"' + sym + '"', sym.intern.inspect)
+ end
+ end
+
+ def test_to_proc
+ assert_equal %w(1 2 3), (1..3).map(&:to_s)
+ [
+ [],
+ [1],
+ [1, 2],
+ [1, [2, 3]],
+ ].each do |ary|
+ ary_id = ary.object_id
+ assert_equal ary_id, :object_id.to_proc.call(ary)
+ ary_ids = ary.collect{|x| x.object_id }
+ assert_equal ary_ids, ary.collect(&:object_id)
+ end
+ end
+
+ def test_call
+ o = Object.new
+ def o.foo(x, y); x + y; end
+
+ assert_equal(3, :foo.to_proc.call(o, 1, 2))
+ assert_raise(ArgumentError) { :foo.to_proc.call }
+ end
+
+ def m_block_given?
+ block_given?
+ end
+
+ def m2_block_given?(m = nil)
+ if m
+ [block_given?, m.call(self)]
+ else
+ block_given?
+ end
+ end
+
+ def test_block_given_to_proc
+ bug8531 = '[Bug #8531]'
+ m = :m_block_given?.to_proc
+ assert(!m.call(self), "#{bug8531} without block")
+ assert(m.call(self) {}, "#{bug8531} with block")
+ assert(!m.call(self), "#{bug8531} without block second")
+ end
+
+ def test_block_persist_between_calls
+ bug8531 = '[Bug #8531]'
+ m2 = :m2_block_given?.to_proc
+ assert_equal([true, false], m2.call(self, m2) {}, "#{bug8531} nested with block")
+ assert_equal([false, false], m2.call(self, m2), "#{bug8531} nested without block")
+ end
+
+ def test_succ
+ assert_equal(:fop, :foo.succ)
+ end
+
+ def test_cmp
+ assert_equal(0, :FoO <=> :FoO)
+ assert_equal(-1, :FoO <=> :fOO)
+ assert_equal(1, :fOO <=> :FoO)
+ assert_nil(:foo <=> "foo")
+ end
+
+ def test_casecmp
+ assert_equal(0, :FoO.casecmp(:fOO))
+ assert_equal(1, :FoO.casecmp(:BaR))
+ assert_equal(-1, :baR.casecmp(:FoO))
+ assert_nil(:foo.casecmp("foo"))
+ end
+
+ def test_length
+ assert_equal(3, :FoO.length)
+ assert_equal(3, :FoO.size)
+ end
+
+ def test_empty
+ assert_equal(false, :FoO.empty?)
+ assert_equal(true, :"".empty?)
+ end
+
+ def test_case
+ assert_equal(:FOO, :FoO.upcase)
+ assert_equal(:foo, :FoO.downcase)
+ assert_equal(:Foo, :foo.capitalize)
+ assert_equal(:fOo, :FoO.swapcase)
+ end
+
+ def test_symbol_poped
+ assert_nothing_raised { eval('a = 1; :"#{ a }"; 1') }
+ end
+
+ def test_ascii_incomat_inspect
+ [Encoding::UTF_16LE, Encoding::UTF_16BE,
+ Encoding::UTF_32LE, Encoding::UTF_32BE].each do |e|
+ assert_equal(':"abc"', "abc".encode(e).to_sym.inspect)
+ assert_equal(':"\\u3042\\u3044\\u3046"', "\u3042\u3044\u3046".encode(e).to_sym.inspect)
+ end
+ end
+
+ def test_symbol_encoding
+ assert_equal(Encoding::US_ASCII, "$-A".force_encoding("iso-8859-15").intern.encoding)
+ assert_equal(Encoding::US_ASCII, "foobar~!".force_encoding("iso-8859-15").intern.encoding)
+ assert_equal(Encoding::UTF_8, "\u{2192}".intern.encoding)
+ assert_raise(EncodingError) {"\xb0a".force_encoding("utf-8").intern}
+ end
+
+ def test_singleton_method
+ assert_raise(TypeError) { a = :foo; def a.foo; end }
+ end
+
+ SymbolsForEval = [
+ :foo,
+ "dynsym_#{Random.rand(10000)}_#{Time.now}".to_sym
+ ]
+
+ def test_instance_eval
+ bug11086 = '[ruby-core:68961] [Bug #11086]'
+ SymbolsForEval.each do |sym|
+ assert_nothing_raised(TypeError, sym, bug11086) {
+ sym.instance_eval {}
+ }
+ assert_raise(TypeError, sym, bug11086) {
+ sym.instance_eval {def foo; end}
+ }
+ end
+ end
+
+ def test_instance_exec
+ bug11086 = '[ruby-core:68961] [Bug #11086]'
+ SymbolsForEval.each do |sym|
+ assert_nothing_raised(TypeError, sym, bug11086) {
+ sym.instance_exec {}
+ }
+ assert_raise(TypeError, sym, bug11086) {
+ sym.instance_exec {def foo; end}
+ }
+ end
+ end
+
+ def test_frozen_symbol
+ assert_equal(true, :foo.frozen?)
+ assert_equal(true, :each.frozen?)
+ assert_equal(true, :+.frozen?)
+ assert_equal(true, "foo#{Time.now.to_i}".to_sym.frozen?)
+ assert_equal(true, :foo.to_sym.frozen?)
+ end
+
+ def test_symbol_gc_1
+ assert_normal_exit('".".intern;GC.start(immediate_sweep:false);eval %[GC.start;".".intern]',
+ '',
+ child_env: '--disable-gems')
+ assert_normal_exit('".".intern;GC.start(immediate_sweep:false);eval %[GC.start;:"."]',
+ '',
+ child_env: '--disable-gems')
+ assert_normal_exit('".".intern;GC.start(immediate_sweep:false);eval %[GC.start;%i"."]',
+ '',
+ child_env: '--disable-gems')
+ assert_normal_exit('tap{".".intern};GC.start(immediate_sweep:false);' +
+ 'eval %[syms=Symbol.all_symbols;GC.start;syms.each(&:to_sym)]',
+ '',
+ child_env: '--disable-gems')
+ end
+
+ def test_dynamic_attrset_id
+ bug10259 = '[ruby-dev:48559] [Bug #10259]'
+ class << (obj = Object.new)
+ attr_writer :unagi
+ end
+ assert_nothing_raised(NoMethodError, bug10259) {obj.send("unagi=".intern, 1)}
+ end
+
+ def test_symbol_fstr_leak
+ bug10686 = '[ruby-core:67268] [Bug #10686]'
+ x = 0
+ assert_no_memory_leak([], '', <<-"end;", bug10686, limit: 1.65)
+ 200_000.times { |i| i.to_s.to_sym }
+ end;
+ end
+
+ def test_hash_redefinition
+ assert_separately([], <<-'end;')
+ bug11035 = '[ruby-core:68767] [Bug #11035]'
+ class Symbol
+ def hash
+ raise
+ end
+ end
+
+ h = {}
+ assert_nothing_raised(RuntimeError, bug11035) {
+ h[:foo] = 1
+ }
+ assert_nothing_raised(RuntimeError, bug11035) {
+ h['bar'.to_sym] = 2
+ }
+ end;
+ end
+end
diff --git a/jni/ruby/test/ruby/test_syntax.rb b/jni/ruby/test/ruby/test_syntax.rb
new file mode 100644
index 0000000..8df641d
--- /dev/null
+++ b/jni/ruby/test/ruby/test_syntax.rb
@@ -0,0 +1,622 @@
+require 'test/unit'
+
+class TestSyntax < Test::Unit::TestCase
+ def assert_syntax_files(test)
+ srcdir = File.expand_path("../../..", __FILE__)
+ srcdir = File.join(srcdir, test)
+ assert_separately(%W[--disable-gem - #{srcdir}],
+ __FILE__, __LINE__, <<-'eom', timeout: Float::INFINITY)
+ dir = ARGV.shift
+ for script in Dir["#{dir}/**/*.rb"].sort
+ assert_valid_syntax(IO::read(script), script)
+ end
+ eom
+ end
+
+ def test_syntax_lib; assert_syntax_files("lib"); end
+ def test_syntax_sample; assert_syntax_files("sample"); end
+ def test_syntax_ext; assert_syntax_files("ext"); end
+ def test_syntax_test; assert_syntax_files("test"); end
+
+ def test_defined_empty_argument
+ bug8220 = '[ruby-core:53999] [Bug #8220]'
+ assert_ruby_status(%w[--disable-gem], 'puts defined? ()', bug8220)
+ end
+
+ def test_must_ascii_compatible
+ require 'tempfile'
+ f = Tempfile.new("must_ac_")
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ make_tmpsrc(f, "# -*- coding: #{enc.name} -*-")
+ assert_nothing_raised(ArgumentError, enc.name) {load(f.path)}
+ end
+ Encoding.list.each do |enc|
+ next if enc.ascii_compatible?
+ make_tmpsrc(f, "# -*- coding: #{enc.name} -*-")
+ assert_raise(ArgumentError, enc.name) {load(f.path)}
+ end
+ ensure
+ f.close! if f
+ end
+
+ def test_script_lines
+ require 'tempfile'
+ f = Tempfile.new("bug4361_")
+ bug4361 = '[ruby-dev:43168]'
+ with_script_lines do |debug_lines|
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ make_tmpsrc(f, "# -*- coding: #{enc.name} -*-\n#----------------")
+ load(f.path)
+ assert_equal([f.path], debug_lines.keys)
+ assert_equal([enc, enc], debug_lines[f.path].map(&:encoding), bug4361)
+ end
+ end
+ ensure
+ f.close! if f
+ end
+
+ def test_newline_in_block_parameters
+ bug = '[ruby-dev:45292]'
+ ["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params|
+ params = ["|", *params, "|"].join("\n")
+ assert_valid_syntax("1.times{#{params}}", __FILE__, "#{bug} #{params.inspect}")
+ end
+ end
+
+ tap do |_,
+ bug6115 = '[ruby-dev:45308]',
+ blockcall = '["elem"].each_with_object [] do end',
+ methods = [['map', 'no'], ['inject([])', 'with']],
+ blocks = [['do end', 'do'], ['{}', 'brace']],
+ *|
+ [%w'. dot', %w':: colon'].product(methods, blocks) do |(c, n1), (m, n2), (b, n3)|
+ m = m.tr_s('()', ' ').strip if n2 == 'do'
+ name = "test_#{n3}_block_after_blockcall_#{n1}_#{n2}_arg"
+ code = "#{blockcall}#{c}#{m} #{b}"
+ define_method(name) {assert_valid_syntax(code, bug6115)}
+ end
+ end
+
+ def test_do_block_in_cmdarg
+ bug9726 = '[ruby-core:61950] [Bug #9726]'
+ assert_valid_syntax("tap (proc do end)", __FILE__, bug9726)
+ end
+
+ def test_keyword_rest
+ bug5989 = '[ruby-core:42455]'
+ assert_valid_syntax("def kwrest_test(**a) a; end", __FILE__, bug5989)
+ assert_valid_syntax("def kwrest_test2(**a, &b) end", __FILE__, bug5989)
+ o = Object.new
+ def o.kw(**a) a end
+ assert_equal({}, o.kw, bug5989)
+ assert_equal({foo: 1}, o.kw(foo: 1), bug5989)
+ assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989)
+ EnvUtil.under_gc_stress do
+ eval("def o.m(k: 0) k end")
+ end
+ assert_equal(42, o.m(k: 42), '[ruby-core:45744]')
+ bug7922 = '[ruby-core:52744] [Bug #7922]'
+ def o.bug7922(**) end
+ assert_nothing_raised(ArgumentError, bug7922) {o.bug7922(foo: 42)}
+ end
+
+ def test_keyword_splat
+ assert_valid_syntax("foo(**h)", __FILE__)
+ o = Object.new
+ def o.kw(k1: 1, k2: 2) [k1, k2] end
+ h = {k1: 11, k2: 12}
+ assert_equal([11, 12], o.kw(**h))
+ assert_equal([11, 12], o.kw(k2: 22, **h))
+ assert_equal([11, 22], o.kw(**h, **{k2: 22}))
+ assert_equal([11, 12], o.kw(**{k2: 22}, **h))
+
+ bug10315 = '[ruby-core:65368] [Bug #10315]'
+ assert_equal([23, 2], o.kw(**{k1: 22}, **{k1: 23}), bug10315)
+
+ h = {k3: 31}
+ assert_raise(ArgumentError) {o.kw(**h)}
+ h = {"k1"=>11, k2: 12}
+ assert_raise(TypeError) {o.kw(**h)}
+
+ bug10315 = '[ruby-core:65625] [Bug #10315]'
+ a = []
+ def a.add(x) push(x); x; end
+ def a.f(k:) k; end
+ a.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), k: a.add(2))")}
+ assert_equal(2, r)
+ assert_equal([1, 2], a, bug10315)
+ a.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("a.f({k: a.add(1), k: a.add(2)})")}
+ assert_equal(2, r)
+ assert_equal([1, 2], a, bug10315)
+ end
+
+ def test_keyword_empty_splat
+ assert_separately([], <<-'end;')
+ bug10719 = '[ruby-core:67446] [Bug #10719]'
+ assert_valid_syntax("foo(a: 1, **{})", bug10719)
+ end;
+ end
+
+ def test_keyword_self_reference
+ bug9593 = '[ruby-core:61299] [Bug #9593]'
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var: defined?(var)) var end")
+ end
+ assert_equal(42, o.foo(var: 42))
+ assert_equal("local-variable", o.foo, bug9593)
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var: var) var end")
+ end
+ assert_nil(o.foo, bug9593)
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var: bar(var)) var end")
+ end
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var: bar {var}) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("def foo(var: bar {|var| var}) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("def foo(var: def bar(var) var; end) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("proc {|var: 1| var}")
+ end
+ end
+
+ def test_optional_self_reference
+ bug9593 = '[ruby-core:61299] [Bug #9593]'
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = defined?(var)) var end")
+ end
+ assert_equal(42, o.foo(42))
+ assert_equal("local-variable", o.foo, bug9593)
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = var) var end")
+ end
+ assert_nil(o.foo, bug9593)
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = bar(var)) var end")
+ end
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = bar {var}) var end")
+ end
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = (def bar;end; var)) var end")
+ end
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = (def self.bar;end; var)) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("def foo(var = bar {|var| var}) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("def foo(var = def bar(var) var; end) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("proc {|var = 1| var}")
+ end
+ end
+
+ def test_warn_grouped_expression
+ bug5214 = '[ruby-core:39050]'
+ assert_warning("", bug5214) do
+ assert_valid_syntax("foo \\\n(\n true)", "test", verbose: true)
+ end
+ end
+
+ def test_warn_unreachable
+ assert_warning("test:3: warning: statement not reached\n") do
+ code = "loop do\n" "break\n" "foo\n" "end"
+ assert_valid_syntax(code, "test", verbose: true)
+ end
+ end
+
+ def test_warn_balanced
+ warning = <<WARN
+test:1: warning: `%s' after local variable or literal is interpreted as binary operator
+test:1: warning: even though it seems like %s
+WARN
+ [
+ [:**, "argument prefix"],
+ [:*, "argument prefix"],
+ [:<<, "here document"],
+ [:&, "argument prefix"],
+ [:+, "unary operator"],
+ [:-, "unary operator"],
+ [:/, "regexp literal"],
+ [:%, "string literal"],
+ ].each do |op, syn|
+ assert_warning(warning % [op, syn]) do
+ assert_valid_syntax("puts 1 #{op}0", "test", verbose: true)
+ end
+ end
+ end
+
+ def test_cmd_symbol_after_keyword
+ bug6347 = '[ruby-dev:45563]'
+ assert_not_label(:foo, 'if true then not_label:foo end', bug6347)
+ assert_not_label(:foo, 'if false; else not_label:foo end', bug6347)
+ assert_not_label(:foo, 'begin not_label:foo end', bug6347)
+ assert_not_label(:foo, 'begin ensure not_label:foo end', bug6347)
+ end
+
+ def test_cmd_symbol_in_string
+ bug6347 = '[ruby-dev:45563]'
+ assert_not_label(:foo, '"#{not_label:foo}"', bug6347)
+ end
+
+ def test_cmd_symbol_singleton_class
+ bug6347 = '[ruby-dev:45563]'
+ @not_label = self
+ assert_not_label(:foo, 'class << not_label:foo; end', bug6347)
+ end
+
+ def test_cmd_symbol_superclass
+ bug6347 = '[ruby-dev:45563]'
+ @not_label = Object
+ assert_not_label(:foo, 'class Foo < not_label:foo; end', bug6347)
+ end
+
+ def test_duplicated_arg
+ assert_syntax_error("def foo(a, a) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_, _) end }
+ end
+
+ def test_duplicated_rest
+ assert_syntax_error("def foo(a, *a) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_, *_) end }
+ end
+
+ def test_duplicated_opt
+ assert_syntax_error("def foo(a, a=1) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_, _=1) end }
+ end
+
+ def test_duplicated_opt_rest
+ assert_syntax_error("def foo(a=1, *a) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_=1, *_) end }
+ end
+
+ def test_duplicated_rest_opt
+ assert_syntax_error("def foo(*a, a=1) end", /duplicated argument name/)
+ end
+
+ def test_duplicated_rest_post
+ assert_syntax_error("def foo(*a, a) end", /duplicated argument name/)
+ end
+
+ def test_duplicated_opt_post
+ assert_syntax_error("def foo(a=1, a) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_=1, _) end }
+ end
+
+ def test_duplicated_kw
+ assert_syntax_error("def foo(a, a: 1) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_, _: 1) end }
+ end
+
+ def test_duplicated_rest_kw
+ assert_syntax_error("def foo(*a, a: 1) end", /duplicated argument name/)
+ assert_nothing_raised {def foo(*_, _: 1) end}
+ end
+
+ def test_duplicated_opt_kw
+ assert_syntax_error("def foo(a=1, a: 1) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_=1, _: 1) end }
+ end
+
+ def test_duplicated_kw_kwrest
+ assert_syntax_error("def foo(a: 1, **a) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_: 1, **_) end }
+ end
+
+ def test_duplicated_rest_kwrest
+ assert_syntax_error("def foo(*a, **a) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(*_, **_) end }
+ end
+
+ def test_duplicated_opt_kwrest
+ assert_syntax_error("def foo(a=1, **a) end", /duplicated argument name/)
+ assert_nothing_raised { def foo(_=1, **_) end }
+ end
+
+ def test_duplicated_when
+ w = 'warning: duplicated when clause is ignored'
+ assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
+ eval %q{
+ case 1
+ when 1, 1
+ when 1, 1
+ when 1, 1
+ end
+ }
+ }
+ assert_warning(/#{w}/){#/3: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
+ a = 1
+ eval %q{
+ case 1
+ when 1, 1
+ when 1, a
+ when 1, 1
+ end
+ }
+ }
+ end
+
+ def test_invalid_next
+ assert_syntax_error("def m; next; end", /Invalid next/)
+ end
+
+ def test_lambda_with_space
+ feature6390 = '[ruby-dev:45605]'
+ assert_valid_syntax("-> (x, y) {}", __FILE__, feature6390)
+ end
+
+ def test_do_block_in_cmdarg_begin
+ bug6419 = '[ruby-dev:45631]'
+ assert_valid_syntax("p begin 1.times do 1 end end", __FILE__, bug6419)
+ end
+
+ def test_do_block_in_call_args
+ bug9308 = '[ruby-core:59342] [Bug #9308]'
+ assert_valid_syntax("bar def foo; self.each do end end", bug9308)
+ end
+
+ def test_do_block_in_lambda
+ bug11107 = '[ruby-core:69017] [Bug #11107]'
+ assert_valid_syntax('p ->() do a() do end end', bug11107)
+ end
+
+ def test_do_block_after_lambda
+ bug11380 = '[ruby-core:70067] [Bug #11380]'
+ assert_valid_syntax('p -> { :hello }, a: 1 do end', bug11380)
+ end
+
+ def test_reserved_method_no_args
+ bug6403 = '[ruby-dev:45626]'
+ assert_valid_syntax("def self; :foo; end", __FILE__, bug6403)
+ end
+
+ def test_unassignable
+ gvar = global_variables
+ %w[self nil true false __FILE__ __LINE__ __ENCODING__].each do |kwd|
+ assert_raise(SyntaxError) {eval("#{kwd} = nil")}
+ assert_equal(gvar, global_variables)
+ end
+ end
+
+ Bug7559 = '[ruby-dev:46737]'
+
+ def test_lineno_command_call_quote
+ expected = __LINE__ + 1
+ actual = caller_lineno "a
+b
+c
+d
+e"
+ assert_equal(expected, actual, "#{Bug7559}: ")
+ end
+
+ def test_lineno_after_heredoc
+ bug7559 = '[ruby-dev:46737]'
+ expected, _, actual = __LINE__, <<eom, __LINE__
+ a
+ b
+ c
+ d
+eom
+ assert_equal(expected, actual, bug7559)
+ end
+
+ def test_lineno_operation_brace_block
+ expected = __LINE__ + 1
+ actual = caller_lineno\
+ {}
+ assert_equal(expected, actual)
+ end
+
+ def assert_constant_reassignment_nested(preset, op, expected, err = [], bug = '[Bug #5449]')
+ [
+ ["p ", ""], # no-pop
+ ["", "p Foo::Bar"], # pop
+ ].each do |p1, p2|
+ src = <<-EOM.gsub(/^\s*\n/, '')
+ class Foo
+ #{"Bar = " + preset if preset}
+ end
+ #{p1}Foo::Bar #{op}= 42
+ #{p2}
+ EOM
+ msg = "\# #{bug}\n#{src}"
+ assert_valid_syntax(src, caller_locations(1, 1)[0].path, msg)
+ assert_in_out_err([], src, expected, err, msg)
+ end
+ end
+
+ def test_constant_reassignment_nested
+ already = /already initialized constant Foo::Bar/
+ uninitialized = /uninitialized constant Foo::Bar/
+ assert_constant_reassignment_nested(nil, "||", %w[42])
+ assert_constant_reassignment_nested("false", "||", %w[42], already)
+ assert_constant_reassignment_nested("true", "||", %w[true])
+ assert_constant_reassignment_nested(nil, "&&", [], uninitialized)
+ assert_constant_reassignment_nested("false", "&&", %w[false])
+ assert_constant_reassignment_nested("true", "&&", %w[42], already)
+ assert_constant_reassignment_nested(nil, "+", [], uninitialized)
+ assert_constant_reassignment_nested("false", "+", [], /undefined method/)
+ assert_constant_reassignment_nested("11", "+", %w[53], already)
+ end
+
+ def assert_constant_reassignment_toplevel(preset, op, expected, err = [], bug = '[Bug #5449]')
+ [
+ ["p ", ""], # no-pop
+ ["", "p ::Bar"], # pop
+ ].each do |p1, p2|
+ src = <<-EOM.gsub(/^\s*\n/, '')
+ #{"Bar = " + preset if preset}
+ class Foo
+ #{p1}::Bar #{op}= 42
+ #{p2}
+ end
+ EOM
+ msg = "\# #{bug}\n#{src}"
+ assert_valid_syntax(src, caller_locations(1, 1)[0].path, msg)
+ assert_in_out_err([], src, expected, err, msg)
+ end
+ end
+
+ def test_constant_reassignment_toplevel
+ already = /already initialized constant Bar/
+ uninitialized = /uninitialized constant Bar/
+ assert_constant_reassignment_toplevel(nil, "||", %w[42])
+ assert_constant_reassignment_toplevel("false", "||", %w[42], already)
+ assert_constant_reassignment_toplevel("true", "||", %w[true])
+ assert_constant_reassignment_toplevel(nil, "&&", [], uninitialized)
+ assert_constant_reassignment_toplevel("false", "&&", %w[false])
+ assert_constant_reassignment_toplevel("true", "&&", %w[42], already)
+ assert_constant_reassignment_toplevel(nil, "+", [], uninitialized)
+ assert_constant_reassignment_toplevel("false", "+", [], /undefined method/)
+ assert_constant_reassignment_toplevel("11", "+", %w[53], already)
+ end
+
+ def test_integer_suffix
+ ["1if true", "begin 1end"].each do |src|
+ assert_valid_syntax(src)
+ assert_equal(1, eval(src), src)
+ end
+ end
+
+ def test_value_of_def
+ assert_separately [], <<-EOS
+ assert_equal(:foo, (def foo; end))
+ assert_equal(:foo, (def (Object.new).foo; end))
+ EOS
+ end
+
+ def test_heredoc_cr
+ assert_syntax_error("puts <<""EOS\n""ng\n""EOS\r""NO\n", /can't find string "EOS" anywhere before EOF/)
+ end
+
+ def test__END___cr
+ assert_syntax_error("__END__\r<<<<<\n", /unexpected <</)
+ end
+
+ def test_warning_for_cr
+ feature8699 = '[ruby-core:56240] [Feature #8699]'
+ assert_warning(/encountered \\r/, feature8699) do
+ eval("\r""__id__\r")
+ end
+ end
+
+ def test_unexpected_fraction
+ msg = /unexpected fraction/
+ assert_syntax_error("0x0.0", msg)
+ assert_syntax_error("0b0.0", msg)
+ assert_syntax_error("0d0.0", msg)
+ assert_syntax_error("0o0.0", msg)
+ assert_syntax_error("0.0.0", msg)
+ end
+
+ def test_error_message_encoding
+ bug10114 = '[ruby-core:64228] [Bug #10114]'
+ code = "# -*- coding: utf-8 -*-\n" "def n \"\u{2208}\"; end"
+ assert_syntax_error(code, /def n "\u{2208}"; end/, bug10114)
+ end
+
+ def test_bad_kwarg
+ bug10545 = '[ruby-dev:48742] [Bug #10545]'
+ src = 'def foo(A: a) end'
+ assert_syntax_error(src, /formal argument/, bug10545)
+ end
+
+ def test_null_range_cmdarg
+ bug10957 = '[ruby-core:68477] [Bug #10957]'
+ assert_ruby_status(['-c', '-e', 'p ()..0'], "", bug10957)
+ assert_ruby_status(['-c', '-e', 'p ()...0'], "", bug10957)
+ assert_syntax_error('0..%w.', /unterminated string/, bug10957)
+ assert_syntax_error('0...%w.', /unterminated string/, bug10957)
+ end
+
+ def test_too_big_nth_ref
+ bug11192 = '[ruby-core:69393] [Bug #11192]'
+ assert_warn(/too big/, bug11192) do
+ eval('$99999999999999999')
+ end
+ end
+
+ private
+
+ def not_label(x) @result = x; @not_label ||= nil end
+ def assert_not_label(expected, src, message = nil)
+ @result = nil
+ assert_nothing_raised(SyntaxError, message) {eval(src)}
+ assert_equal(expected, @result, message)
+ end
+
+ def make_tmpsrc(f, src)
+ f.open
+ f.truncate(0)
+ f.puts(src)
+ f.close
+ end
+
+ def with_script_lines
+ script_lines = nil
+ debug_lines = {}
+ Object.class_eval do
+ if defined?(SCRIPT_LINES__)
+ script_lines = SCRIPT_LINES__
+ remove_const :SCRIPT_LINES__
+ end
+ const_set(:SCRIPT_LINES__, debug_lines)
+ end
+ yield debug_lines
+ ensure
+ Object.class_eval do
+ remove_const :SCRIPT_LINES__
+ const_set(:SCRIPT_LINES__, script_lines) if script_lines
+ end
+ end
+
+ def caller_lineno(*)
+ caller_locations(1, 1)[0].lineno
+ end
+end
diff --git a/jni/ruby/test/ruby/test_system.rb b/jni/ruby/test/ruby/test_system.rb
new file mode 100644
index 0000000..b328cbc
--- /dev/null
+++ b/jni/ruby/test/ruby/test_system.rb
@@ -0,0 +1,162 @@
+require 'test/unit'
+require 'tmpdir'
+
+class TestSystem < Test::Unit::TestCase
+ def test_system
+ ruby = EnvUtil.rubybin
+ assert_equal("foobar\n", `echo foobar`)
+ assert_equal('foobar', `#{ruby} -e 'print "foobar"'`)
+
+ Dir.mktmpdir("ruby_script_tmp") {|tmpdir|
+ tmpfilename = "#{tmpdir}/ruby_script_tmp.#{$$}"
+
+ tmp = open(tmpfilename, "w")
+ tmp.print "print $zzz\n";
+ tmp.close
+
+ assert_equal('true', `#{ruby} -s #{tmpfilename} -zzz`)
+ assert_equal('555', `#{ruby} -s #{tmpfilename} -zzz=555`)
+
+ tmp = open(tmpfilename, "w")
+ tmp.print "#! /usr/local/bin/ruby -s\n";
+ tmp.print "print $zzz\n";
+ tmp.close
+
+ assert_equal('678', `#{ruby} #{tmpfilename} -zzz=678`)
+
+ tmp = open(tmpfilename, "w")
+ tmp.print "this is a leading junk\n";
+ tmp.print "#! /usr/local/bin/ruby -s\n";
+ tmp.print "print $zzz if defined? $zzz\n";
+ tmp.print "__END__\n";
+ tmp.print "this is a trailing junk\n";
+ tmp.close
+
+ assert_equal('', `#{ruby} -x #{tmpfilename}`)
+ assert_equal('555', `#{ruby} -x #{tmpfilename} -zzz=555`)
+
+ tmp = open(tmpfilename, "w")
+ tmp.print "#! /non/exist\\interpreter?/./to|be:ignored\n";
+ tmp.print "this is a leading junk\n";
+ tmp.print "#! /usr/local/bin/ruby -s\n";
+ tmp.print "print $zzz if defined? $zzz\n";
+ tmp.print "__END__\n";
+ tmp.print "this is a trailing junk\n";
+ tmp.close
+
+ assert_equal('', `#{ruby} #{tmpfilename}`)
+ assert_equal('555', `#{ruby} #{tmpfilename} -zzz=555`)
+
+ tmp = open(tmpfilename, "w")
+ for i in 1..5
+ tmp.print i, "\n"
+ end
+ tmp.close
+
+ `#{ruby} -i.bak -pe '$_.sub!(/^[0-9]+$/){$&.to_i * 5}' #{tmpfilename}`
+ tmp = open(tmpfilename, "r")
+ while tmp.gets
+ assert_equal(0, $_.to_i % 5)
+ end
+ tmp.close
+
+ File.unlink tmpfilename or `/bin/rm -f "#{tmpfilename}"`
+ File.unlink "#{tmpfilename}.bak" or `/bin/rm -f "#{tmpfilename}.bak"`
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ testname = '[ruby-dev:38588]'
+ batch = "batch_tmp.#{$$}"
+ tmpfilename = "#{tmpdir}/#{batch}.bat"
+ open(tmpfilename, "wb") {|f| f.print "\r\n"}
+ assert(system(tmpfilename), testname)
+ assert(system("#{tmpdir}/#{batch}"), testname)
+ assert(system(tmpfilename, "1"), testname)
+ assert(system("#{tmpdir}/#{batch}", "1"), testname)
+ begin
+ path = ENV["PATH"]
+ ENV["PATH"] = "#{tmpdir.tr(File::SEPARATOR, File::ALT_SEPARATOR)}#{File::PATH_SEPARATOR + path if path}"
+ assert(system("#{batch}.bat"), testname)
+ assert(system(batch), testname)
+ assert(system("#{batch}.bat", "1"), testname)
+ assert(system(batch, "1"), testname)
+ ensure
+ ENV["PATH"] = path
+ end
+ File.unlink tmpfilename
+
+ testname = '[ruby-core:44505]'
+ assert_match(/Windows/, `ver`, testname)
+ assert_equal 0, $?.to_i, testname
+ end
+ }
+ end
+
+ def test_system_at
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ bug4393 = '[ruby-core:35218]'
+
+ # @ + builtin command
+ assert_equal("foo\n", `@echo foo`, bug4393);
+ assert_equal("foo\n", `@@echo foo`, bug4393);
+ assert_equal("@@foo\n", `@@echo @@foo`, bug4393);
+
+ # @ + non builtin command
+ Dir.mktmpdir("ruby_script_tmp") {|tmpdir|
+ tmpfilename = "#{tmpdir}/ruby_script_tmp.#{$$}"
+
+ tmp = open(tmpfilename, "w")
+ tmp.print "foo\nbar\nbaz\n@foo";
+ tmp.close
+
+ assert_match(/\Abar\nbaz\n?\z/, `@@findstr "ba" #{tmpfilename.gsub("/", "\\")}`, bug4393);
+ }
+ end
+ end
+
+ def test_system_redirect_win
+ if /mswin|mingw/ !~ RUBY_PLATFORM
+ return
+ end
+
+ Dir.mktmpdir("ruby_script_tmp") do |tmpdir|
+ cmd = nil
+ message = proc do
+ [
+ '[ruby-talk:258939]',
+ "out.txt:",
+ *File.readlines("out.txt").map{|s|" "+s.inspect},
+ "err.txt:",
+ *File.readlines("err.txt").map{|s|" "+s.inspect},
+ "system(#{cmd.inspect})"
+ ].join("\n")
+ end
+ class << message
+ alias to_s call
+ end
+ Dir.chdir(tmpdir) do
+ open("input.txt", "w") {|f| f.puts "BFI3CHL671"}
+ cmd = "%WINDIR%/system32/find.exe \"BFI3CHL671\" input.txt > out.txt 2>err.txt"
+ assert_equal(true, system(cmd), message)
+ cmd = "\"%WINDIR%/system32/find.exe\" \"BFI3CHL671\" input.txt > out.txt 2>err.txt"
+ assert_equal(true, system(cmd), message)
+ cmd = "\"%WINDIR%/system32/find.exe BFI3CHL671\" input.txt > out.txt 2>err.txt"
+ assert_equal(false, system(cmd), message)
+ end
+ end
+ end
+
+ def test_empty_evstr
+ assert_equal("", eval('"#{}"', nil, __FILE__, __LINE__), "[ruby-dev:25113]")
+ end
+
+ def test_fallback_to_sh
+ Dir.mktmpdir("ruby_script_tmp") {|tmpdir|
+ tmpfilename = "#{tmpdir}/ruby_script_tmp.#{$$}"
+ open(tmpfilename, "w") {|f|
+ f.puts ": ;"
+ f.chmod(0755)
+ }
+ assert_equal(true, system(tmpfilename), '[ruby-core:32745]')
+ }
+ end if File.executable?("/bin/sh")
+end
diff --git a/jni/ruby/test/ruby/test_thread.rb b/jni/ruby/test/ruby/test_thread.rb
new file mode 100644
index 0000000..87d5f0f
--- /dev/null
+++ b/jni/ruby/test/ruby/test_thread.rb
@@ -0,0 +1,1036 @@
+# -*- coding: us-ascii -*-
+require 'test/unit'
+require 'thread'
+
+class TestThread < Test::Unit::TestCase
+ class Thread < ::Thread
+ Threads = []
+ def self.new(*)
+ th = super
+ Threads << th
+ th
+ end
+ end
+
+ def setup
+ Thread::Threads.clear
+ end
+
+ def teardown
+ Thread::Threads.each do |t|
+ t.kill if t.alive?
+ begin
+ t.join
+ rescue Exception
+ end
+ end
+ end
+
+ def test_inspect
+ th = Module.new {break module_eval("class C\u{30b9 30ec 30c3 30c9} < Thread; self; end")}.start{}
+ assert_match(/::C\u{30b9 30ec 30c3 30c9}:/, th.inspect)
+ ensure
+ th.join
+ end
+
+ def test_main_thread_variable_in_enumerator
+ assert_equal Thread.main, Thread.current
+
+ Thread.current.thread_variable_set :foo, "bar"
+
+ thread, value = Fiber.new {
+ Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
+ }.resume
+
+ assert_equal Thread.current, thread
+ assert_equal Thread.current.thread_variable_get(:foo), value
+ end
+
+ def test_thread_variable_in_enumerator
+ Thread.new {
+ Thread.current.thread_variable_set :foo, "bar"
+
+ thread, value = Fiber.new {
+ Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
+ }.resume
+
+ assert_equal Thread.current, thread
+ assert_equal Thread.current.thread_variable_get(:foo), value
+ }.join
+ end
+
+ def test_thread_variables
+ assert_equal [], Thread.new { Thread.current.thread_variables }.join.value
+
+ t = Thread.new {
+ Thread.current.thread_variable_set(:foo, "bar")
+ Thread.current.thread_variables
+ }
+ assert_equal [:foo], t.join.value
+ end
+
+ def test_thread_variable?
+ Thread.new { assert_not_send([Thread.current, :thread_variable?, "foo"]) }.value
+ t = Thread.new {
+ Thread.current.thread_variable_set("foo", "bar")
+ }.join
+
+ assert_send([t, :thread_variable?, "foo"])
+ assert_send([t, :thread_variable?, :foo])
+ assert_not_send([t, :thread_variable?, :bar])
+ end
+
+ def test_thread_variable_strings_and_symbols_are_the_same_key
+ t = Thread.new {}.join
+ t.thread_variable_set("foo", "bar")
+ assert_equal "bar", t.thread_variable_get(:foo)
+ end
+
+ def test_thread_variable_frozen
+ t = Thread.new { }.join
+ t.freeze
+ assert_raise(RuntimeError) do
+ t.thread_variable_set(:foo, "bar")
+ end
+ end
+
+ def test_mutex_synchronize
+ m = Mutex.new
+ r = 0
+ max = 10
+ (1..max).map{
+ Thread.new{
+ i=0
+ while i<max*max
+ i+=1
+ m.synchronize{
+ r += 1
+ }
+ end
+ }
+ }.each{|e|
+ e.join
+ }
+ assert_equal(max * max * max, r)
+ end
+
+ def test_mutex_synchronize_yields_no_block_params
+ bug8097 = '[ruby-core:53424] [Bug #8097]'
+ assert_empty(Mutex.new.synchronize {|*params| break params}, bug8097)
+ end
+
+ def test_local_barrier
+ dir = File.dirname(__FILE__)
+ lbtest = File.join(dir, "lbtest.rb")
+ $:.unshift File.join(File.dirname(dir), 'ruby')
+ $:.shift
+ 3.times {
+ `#{EnvUtil.rubybin} #{lbtest}`
+ assert_not_predicate($?, :coredump?, '[ruby-dev:30653]')
+ }
+ end
+
+ def test_priority
+ c1 = c2 = 0
+ run = true
+ t1 = Thread.new { c1 += 1 while run }
+ t1.priority = 3
+ t2 = Thread.new { c2 += 1 while run }
+ t2.priority = -3
+ assert_equal(3, t1.priority)
+ assert_equal(-3, t2.priority)
+ sleep 0.5
+ 5.times do
+ assert_not_predicate(t1, :stop?)
+ assert_not_predicate(t2, :stop?)
+ break if c1 > c2
+ sleep 0.1
+ end
+ run = false
+ t1.kill
+ t2.kill
+ assert_operator(c1, :>, c2, "[ruby-dev:33124]") # not guaranteed
+ end
+
+ def test_new
+ assert_raise(ThreadError) do
+ Thread.new
+ end
+
+ t1 = Thread.new { sleep }
+ assert_raise(ThreadError) do
+ t1.instance_eval { initialize { } }
+ end
+
+ t2 = Thread.new(&method(:sleep).to_proc)
+ assert_raise(ThreadError) do
+ t2.instance_eval { initialize { } }
+ end
+
+ ensure
+ t1.kill if t1
+ t2.kill if t2
+ end
+
+ def test_join
+ t = Thread.new { sleep }
+ assert_nil(t.join(0.05))
+
+ ensure
+ t.kill if t
+ end
+
+ def test_join2
+ ok = false
+ t1 = Thread.new { ok = true; sleep }
+ Thread.pass until ok
+ Thread.pass until t1.stop?
+ t2 = Thread.new do
+ Thread.pass while ok
+ t1.join(0.01)
+ end
+ t3 = Thread.new do
+ ok = false
+ t1.join
+ end
+ assert_nil(t2.value)
+ t1.wakeup
+ assert_equal(t1, t3.value)
+
+ ensure
+ t1.kill if t1
+ t2.kill if t2
+ t3.kill if t3
+ end
+
+ def test_kill_main_thread
+ assert_in_out_err([], <<-INPUT, %w(1), [])
+ p 1
+ Thread.kill Thread.current
+ p 2
+ INPUT
+ end
+
+ def test_kill_wrong_argument
+ bug4367 = '[ruby-core:35086]'
+ assert_raise(TypeError, bug4367) {
+ Thread.kill(nil)
+ }
+ o = Object.new
+ assert_raise(TypeError, bug4367) {
+ Thread.kill(o)
+ }
+ end
+
+ def test_kill_thread_subclass
+ c = Class.new(Thread)
+ t = c.new { sleep 10 }
+ assert_nothing_raised { Thread.kill(t) }
+ assert_equal(nil, t.value)
+ end
+
+ def test_exit
+ s = 0
+ Thread.new do
+ s += 1
+ Thread.exit
+ s += 2
+ end.join
+ assert_equal(1, s)
+ end
+
+ def test_wakeup
+ s = 0
+ t = Thread.new do
+ s += 1
+ Thread.stop
+ s += 1
+ end
+ Thread.pass until t.stop?
+ assert_equal(1, s)
+ t.wakeup
+ Thread.pass while t.alive?
+ assert_equal(2, s)
+ assert_raise(ThreadError) { t.wakeup }
+
+ ensure
+ t.kill if t
+ end
+
+ def test_stop
+ assert_in_out_err([], <<-INPUT, %w(2), [])
+ begin
+ Thread.stop
+ p 1
+ rescue ThreadError
+ p 2
+ end
+ INPUT
+ end
+
+ def test_list
+ assert_in_out_err([], <<-INPUT) do |r, e|
+ t1 = Thread.new { sleep }
+ Thread.pass
+ t2 = Thread.new { loop { Thread.pass } }
+ Thread.new { }.join
+ p [Thread.current, t1, t2].map{|t| t.object_id }.sort
+ p Thread.list.map{|t| t.object_id }.sort
+ INPUT
+ assert_equal(r.first, r.last)
+ assert_equal([], e)
+ end
+ end
+
+ def test_main
+ assert_in_out_err([], <<-INPUT, %w(true false), [])
+ p Thread.main == Thread.current
+ Thread.new { p Thread.main == Thread.current }.join
+ INPUT
+ end
+
+ def test_abort_on_exception
+ assert_in_out_err([], <<-INPUT, %w(false 1), [])
+ p Thread.abort_on_exception
+ begin
+ t = Thread.new { raise }
+ Thread.pass until t.stop?
+ p 1
+ rescue
+ p 2
+ end
+ INPUT
+
+ assert_in_out_err([], <<-INPUT, %w(true 2), [])
+ Thread.abort_on_exception = true
+ p Thread.abort_on_exception
+ begin
+ Thread.new { raise }
+ sleep 0.5
+ p 1
+ rescue
+ p 2
+ end
+ INPUT
+
+ assert_in_out_err(%w(--disable-gems -d), <<-INPUT, %w(false 2), %r".+")
+ p Thread.abort_on_exception
+ begin
+ t = Thread.new { raise }
+ Thread.pass until t.stop?
+ p 1
+ rescue
+ p 2
+ end
+ INPUT
+
+ assert_in_out_err([], <<-INPUT, %w(false true 2), [])
+ p Thread.abort_on_exception
+ begin
+ ok = false
+ t = Thread.new { Thread.pass until ok; raise }
+ t.abort_on_exception = true
+ p t.abort_on_exception
+ ok = 1
+ sleep 1
+ p 1
+ rescue
+ p 2
+ end
+ INPUT
+ end
+
+ def test_status_and_stop_p
+ a = ::Thread.new { raise("die now") }
+ b = Thread.new { Thread.stop }
+ c = Thread.new { Thread.exit }
+ e = Thread.current
+ Thread.pass while a.alive? or !b.stop? or c.alive?
+
+ assert_equal(nil, a.status)
+ assert_predicate(a, :stop?)
+
+ assert_equal("sleep", b.status)
+ assert_predicate(b, :stop?)
+
+ assert_equal(false, c.status)
+ assert_match(/^#<TestThread::Thread:.* dead>$/, c.inspect)
+ assert_predicate(c, :stop?)
+
+ es1 = e.status
+ es2 = e.stop?
+ assert_equal(["run", false], [es1, es2])
+
+ ensure
+ a.kill if a
+ b.kill if b
+ c.kill if c
+ end
+
+ def test_switch_while_busy_loop
+ bug1402 = "[ruby-dev:38319] [Bug #1402]"
+ flag = true
+ th = Thread.current
+ waiter = Thread.start {
+ sleep 0.1
+ flag = false
+ sleep 1
+ th.raise(bug1402)
+ }
+ assert_nothing_raised(RuntimeError, bug1402) do
+ nil while flag
+ end
+ assert(!flag, bug1402)
+ ensure
+ waiter.kill.join
+ end
+
+ def test_safe_level
+ ok = false
+ t = Thread.new do
+ EnvUtil.suppress_warning do
+ $SAFE = 3
+ end
+ ok = true
+ sleep
+ end
+ Thread.pass until ok
+ assert_equal(0, Thread.current.safe_level)
+ assert_equal(3, t.safe_level)
+
+ ensure
+ t.kill if t
+ end
+
+ def test_thread_local
+ t = Thread.new { sleep }
+
+ assert_equal(false, t.key?(:foo))
+
+ t["foo"] = "foo"
+ t["bar"] = "bar"
+ t["baz"] = "baz"
+
+ assert_equal(true, t.key?(:foo))
+ assert_equal(true, t.key?("foo"))
+ assert_equal(false, t.key?(:qux))
+ assert_equal(false, t.key?("qux"))
+
+ assert_equal([:foo, :bar, :baz], t.keys)
+
+ ensure
+ t.kill if t
+ end
+
+ def test_thread_local_security
+ assert_raise(RuntimeError) do
+ Thread.new do
+ Thread.current[:foo] = :bar
+ Thread.current.freeze
+ Thread.current[:foo] = :baz
+ end.join
+ end
+ end
+
+ def test_thread_local_dynamic_symbol
+ bug10667 = '[ruby-core:67185] [Bug #10667]'
+ t = Thread.new {}.join
+ key_str = "foo#{rand}"
+ key_sym = key_str.to_sym
+ t.thread_variable_set(key_str, "bar")
+ assert_equal("bar", t.thread_variable_get(key_str), "#{bug10667}: string key")
+ assert_equal("bar", t.thread_variable_get(key_sym), "#{bug10667}: symbol key")
+ end
+
+ def test_select_wait
+ assert_nil(IO.select(nil, nil, nil, 0.001))
+ t = Thread.new do
+ IO.select(nil, nil, nil, nil)
+ end
+ Thread.pass until t.stop?
+ assert_predicate(t, :alive?)
+ t.kill
+ end
+
+ def test_mutex_deadlock
+ m = Mutex.new
+ m.synchronize do
+ assert_raise(ThreadError) do
+ m.synchronize do
+ assert(false)
+ end
+ end
+ end
+ end
+
+ def test_mutex_interrupt
+ m = Mutex.new
+ m.lock
+ t = Thread.new do
+ m.lock
+ :foo
+ end
+ Thread.pass until t.stop?
+ t.kill
+ assert_nil(t.value)
+ end
+
+ def test_mutex_illegal_unlock
+ m = Mutex.new
+ m.lock
+ assert_raise(ThreadError) do
+ Thread.new do
+ m.unlock
+ end.join
+ end
+ end
+
+ def test_mutex_fifo_like_lock
+ m1 = Mutex.new
+ m2 = Mutex.new
+ m1.lock
+ m2.lock
+ m1.unlock
+ m2.unlock
+ assert_equal(false, m1.locked?)
+ assert_equal(false, m2.locked?)
+
+ m3 = Mutex.new
+ m1.lock
+ m2.lock
+ m3.lock
+ m1.unlock
+ m2.unlock
+ m3.unlock
+ assert_equal(false, m1.locked?)
+ assert_equal(false, m2.locked?)
+ assert_equal(false, m3.locked?)
+ end
+
+ def test_mutex_trylock
+ m = Mutex.new
+ assert_equal(true, m.try_lock)
+ assert_equal(false, m.try_lock, '[ruby-core:20943]')
+
+ Thread.new{
+ assert_equal(false, m.try_lock)
+ }.join
+
+ m.unlock
+ end
+
+ def test_recursive_outer
+ arr = []
+ obj = Struct.new(:foo, :visited).new(arr, false)
+ arr << obj
+ def obj.hash
+ self[:visited] = true
+ super
+ raise "recursive_outer should short circuit intermediate calls"
+ end
+ assert_nothing_raised {arr.hash}
+ assert(obj[:visited], "obj.hash was not called")
+ end
+
+ def test_thread_instance_variable
+ bug4389 = '[ruby-core:35192]'
+ assert_in_out_err([], <<-INPUT, %w(), [], bug4389)
+ class << Thread.current
+ @data = :data
+ end
+ INPUT
+ end
+
+ def test_no_valid_cfp
+ skip 'with win32ole, cannot run this testcase because win32ole redefines Thread#intialize' if defined?(WIN32OLE)
+ bug5083 = '[ruby-dev:44208]'
+ assert_equal([], Thread.new(&Module.method(:nesting)).value, bug5083)
+ assert_instance_of(Thread, Thread.new(:to_s, &Class.new.method(:undef_method)).join, bug5083)
+ end
+
+ def make_handle_interrupt_test_thread1 flag
+ r = []
+ ready_p = false
+ done = false
+ th = Thread.new{
+ begin
+ Thread.handle_interrupt(RuntimeError => flag){
+ begin
+ ready_p = true
+ sleep 0.01 until done
+ rescue
+ r << :c1
+ end
+ }
+ rescue
+ r << :c2
+ end
+ }
+ Thread.pass until ready_p
+ th.raise
+ begin
+ done = true
+ th.join
+ rescue
+ r << :c3
+ end
+ r
+ end
+
+ def test_handle_interrupt
+ [[:never, :c2],
+ [:immediate, :c1],
+ [:on_blocking, :c1]].each{|(flag, c)|
+ assert_equal([flag, c], [flag] + make_handle_interrupt_test_thread1(flag))
+ }
+ # TODO: complex cases are needed.
+ end
+
+ def test_handle_interrupt_invalid_argument
+ assert_raise(ArgumentError) {
+ Thread.handle_interrupt(RuntimeError => :immediate) # no block
+ }
+ assert_raise(ArgumentError) {
+ Thread.handle_interrupt(RuntimeError => :xyzzy) {}
+ }
+ assert_raise(TypeError) {
+ Thread.handle_interrupt([]) {} # array
+ }
+ end
+
+ def for_test_handle_interrupt_with_return
+ Thread.handle_interrupt(Object => :never){
+ Thread.current.raise RuntimeError.new("have to be rescured")
+ return
+ }
+ rescue
+ end
+
+ def test_handle_interrupt_with_return
+ assert_nothing_raised do
+ for_test_handle_interrupt_with_return
+ _dummy_for_check_ints=nil
+ end
+ end
+
+ def test_handle_interrupt_with_break
+ assert_nothing_raised do
+ begin
+ Thread.handle_interrupt(Object => :never){
+ Thread.current.raise RuntimeError.new("have to be rescured")
+ break
+ }
+ rescue
+ end
+ _dummy_for_check_ints=nil
+ end
+ end
+
+ def test_handle_interrupt_blocking
+ r=:ng
+ e=Class.new(Exception)
+ th_s = Thread.current
+ begin
+ th = Thread.start{
+ Thread.handle_interrupt(Object => :on_blocking){
+ begin
+ Thread.current.raise RuntimeError
+ r=:ok
+ sleep
+ ensure
+ th_s.raise e
+ end
+ }
+ }
+ sleep 1
+ r=:ng
+ th.raise RuntimeError
+ th.join
+ rescue e
+ end
+ assert_equal(:ok,r)
+ end
+
+ def test_handle_interrupt_and_io
+ assert_in_out_err([], <<-INPUT, %w(ok), [])
+ th_waiting = true
+
+ t = Thread.new {
+ Thread.handle_interrupt(RuntimeError => :on_blocking) {
+ nil while th_waiting
+ # async interrupt should be raised _before_ writing puts arguments
+ puts "ng"
+ }
+ }
+
+ Thread.pass while t.stop?
+ t.raise RuntimeError
+ th_waiting = false
+ t.join rescue nil
+ puts "ok"
+ INPUT
+ end
+
+ def test_handle_interrupt_and_p
+ assert_in_out_err([], <<-INPUT, %w(:ok :ok), [])
+ th_waiting = false
+
+ t = Thread.new {
+ Thread.handle_interrupt(RuntimeError => :on_blocking) {
+ th_waiting = true
+ nil while th_waiting
+ # p shouldn't provide interruptible point
+ p :ok
+ p :ok
+ }
+ }
+
+ Thread.pass until th_waiting
+ t.raise RuntimeError
+ th_waiting = false
+ t.join rescue nil
+ INPUT
+ end
+
+ def test_handle_interrupted?
+ q = Queue.new
+ Thread.handle_interrupt(RuntimeError => :never){
+ done = false
+ th = Thread.new{
+ q.push :e
+ begin
+ begin
+ Thread.pass until done
+ rescue => e
+ q.push :ng1
+ end
+ begin
+ Thread.handle_interrupt(Object => :immediate){} if Thread.pending_interrupt?
+ rescue RuntimeError => e
+ q.push :ok
+ end
+ rescue => e
+ q.push :ng2
+ ensure
+ q.push :ng3
+ end
+ }
+ q.pop
+ th.raise
+ done = true
+ th.join
+ assert_equal(:ok, q.pop)
+ }
+ end
+
+ def test_thread_timer_and_ensure
+ assert_normal_exit(<<_eom, 'r36492', timeout: 3)
+ flag = false
+ t = Thread.new do
+ begin
+ sleep
+ ensure
+ 1 until flag
+ end
+ end
+
+ Thread.pass until t.status == "sleep"
+
+ t.kill
+ t.alive? == true
+ flag = true
+ t.join
+_eom
+ end
+
+ def test_uninitialized
+ c = Class.new(Thread)
+ c.class_eval { def initialize; end }
+ assert_raise(ThreadError) { c.new.start }
+ end
+
+ def test_backtrace
+ Thread.new{
+ assert_equal(Array, Thread.main.backtrace.class)
+ }.join
+
+ t = Thread.new{}
+ t.join
+ assert_equal(nil, t.backtrace)
+ end
+
+ def test_thread_timer_and_interrupt
+ bug5757 = '[ruby-dev:44985]'
+ t0 = Time.now.to_f
+ pid = nil
+ cmd = 'Signal.trap(:INT, "DEFAULT"); r,=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; r.read'
+ opt = {}
+ opt[:new_pgroup] = true if /mswin|mingw/ =~ RUBY_PLATFORM
+ s, _err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true, opt) do |in_p, out_p, err_p, cpid|
+ out_p.gets
+ pid = cpid
+ Process.kill(:SIGINT, pid)
+ Process.wait(pid)
+ [$?, err_p.read]
+ end
+ t1 = Time.now.to_f
+ assert_equal(pid, s.pid, bug5757)
+ assert_equal([false, true, false, Signal.list["INT"]],
+ [s.exited?, s.signaled?, s.stopped?, s.termsig],
+ "[s.exited?, s.signaled?, s.stopped?, s.termsig]")
+ assert_in_delta(t1 - t0, 1, 1, bug5757)
+ end
+
+ def test_thread_join_in_trap
+ assert_separately [], <<-'EOS'
+ Signal.trap(:INT, "DEFAULT")
+ t0 = Thread.current
+ assert_nothing_raised{
+ t = Thread.new {Thread.pass until t0.stop?; Process.kill(:INT, $$)}
+
+ Signal.trap :INT do
+ t.join
+ end
+
+ t.join
+ }
+ EOS
+ end
+
+ def test_thread_value_in_trap
+ assert_separately [], <<-'EOS'
+ Signal.trap(:INT, "DEFAULT")
+ t0 = Thread.current
+ t = Thread.new {Thread.pass until t0.stop?; Process.kill(:INT, $$); :normal_end}
+
+ Signal.trap :INT do
+ t.value
+ end
+ assert_equal(:normal_end, t.value)
+ EOS
+ end
+
+ def test_thread_join_current
+ assert_raise(ThreadError) do
+ Thread.current.join
+ end
+ end
+
+ def test_thread_join_main_thread
+ assert_raise(ThreadError) do
+ Thread.new(Thread.current) {|t|
+ t.join
+ }.join
+ end
+ end
+
+ def test_main_thread_status_at_exit
+ assert_in_out_err([], <<-'INPUT', ["false false aborting"], [])
+require 'thread'
+q = Queue.new
+Thread.new(Thread.current) {|mth|
+ begin
+ q.push nil
+ mth.run
+ Thread.pass until mth.stop?
+ p :mth_stopped # don't run if killed by rb_thread_terminate_all
+ ensure
+ puts "#{mth.alive?} #{mth.status} #{Thread.current.status}"
+ end
+}
+q.pop
+ INPUT
+ end
+
+ def test_thread_status_in_trap
+ # when running trap handler, Thread#status must show "run"
+ # Even though interrupted from sleeping function
+ assert_in_out_err([], <<-INPUT, %w(sleep run), [])
+ Signal.trap(:INT) {
+ puts Thread.current.status
+ exit
+ }
+ t = Thread.current
+
+ Thread.new(Thread.current) {|mth|
+ Thread.pass until t.stop?
+ puts mth.status
+ Process.kill(:INT, $$)
+ }
+ sleep 0.1
+ INPUT
+ end
+
+ # Bug #7450
+ def test_thread_status_raise_after_kill
+ ary = []
+
+ t = Thread.new {
+ begin
+ ary << Thread.current.status
+ sleep #1
+ ensure
+ begin
+ ary << Thread.current.status
+ sleep #2
+ ensure
+ ary << Thread.current.status
+ end
+ end
+ }
+
+ begin
+ Thread.pass until ary.size >= 1
+ Thread.pass until t.stop?
+ t.kill # wake up sleep #1
+ Thread.pass until ary.size >= 2
+ Thread.pass until t.stop?
+ t.raise "wakeup" # wake up sleep #2
+ Thread.pass while t.alive?
+ assert_equal(ary, ["run", "aborting", "aborting"])
+ ensure
+ t.join rescue nil
+ end
+ end
+
+ def test_mutex_owned
+ mutex = Mutex.new
+
+ assert_equal(mutex.owned?, false)
+ mutex.synchronize {
+ # Now, I have the mutex
+ assert_equal(mutex.owned?, true)
+ }
+ assert_equal(mutex.owned?, false)
+ end
+
+ def test_mutex_owned2
+ begin
+ mutex = Mutex.new
+ th = Thread.new {
+ # lock forever
+ mutex.lock
+ sleep
+ }
+
+ Thread.pass until th.status == "sleep"
+ # acquired another thread.
+ assert_equal(mutex.locked?, true)
+ assert_equal(mutex.owned?, false)
+ ensure
+ th.kill if th
+ end
+ end
+
+ def test_mutex_unlock_on_trap
+ assert_in_out_err([], <<-INPUT, %w(locked unlocked false), [])
+ m = Mutex.new
+
+ trapped = false
+ Signal.trap("INT") { |signo|
+ m.unlock
+ trapped = true
+ puts "unlocked"
+ }
+
+ m.lock
+ puts "locked"
+ Process.kill("INT", $$)
+ Thread.pass until trapped
+ puts m.locked?
+ INPUT
+ end
+
+ def invoke_rec script, vm_stack_size, machine_stack_size, use_length = true
+ env = {}
+ env['RUBY_THREAD_VM_STACK_SIZE'] = vm_stack_size.to_s if vm_stack_size
+ env['RUBY_THREAD_MACHINE_STACK_SIZE'] = machine_stack_size.to_s if machine_stack_size
+ out, = EnvUtil.invoke_ruby([env, '-e', script], '', true, true)
+ use_length ? out.length : out
+ end
+
+ def test_stack_size
+ h_default = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', nil, nil, false))
+ h_0 = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', 0, 0, false))
+ h_large = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', 1024 * 1024 * 10, 1024 * 1024 * 10, false))
+
+ assert_operator(h_default[:thread_vm_stack_size], :>, h_0[:thread_vm_stack_size],
+ "0 thread_vm_stack_size")
+ assert_operator(h_default[:thread_vm_stack_size], :<, h_large[:thread_vm_stack_size],
+ "large thread_vm_stack_size")
+ assert_operator(h_default[:thread_machine_stack_size], :>=, h_0[:thread_machine_stack_size],
+ "0 thread_machine_stack_size")
+ assert_operator(h_default[:thread_machine_stack_size], :<=, h_large[:thread_machine_stack_size],
+ "large thread_machine_stack_size")
+ end
+
+ def test_vm_machine_stack_size
+ script = 'def rec; print "."; STDOUT.flush; rec; end; rec'
+ size_default = invoke_rec script, nil, nil
+ assert_operator(size_default, :>, 0, "default size")
+ size_0 = invoke_rec script, 0, nil
+ assert_operator(size_default, :>, size_0, "0 size")
+ size_large = invoke_rec script, 1024 * 1024 * 10, nil
+ assert_operator(size_default, :<, size_large, "large size")
+ end
+
+ def test_machine_stack_size
+ # check machine stack size
+ # Note that machine stack size may not change size (depend on OSs)
+ script = 'def rec; print "."; STDOUT.flush; 1.times{1.times{1.times{rec}}}; end; Thread.new{rec}.join'
+ vm_stack_size = 1024 * 1024
+ size_default = invoke_rec script, vm_stack_size, nil
+ size_0 = invoke_rec script, vm_stack_size, 0
+ assert_operator(size_default, :>=, size_0, "0 size")
+ size_large = invoke_rec script, vm_stack_size, 1024 * 1024 * 10
+ assert_operator(size_default, :<=, size_large, "large size")
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_blocking_mutex_unlocked_on_fork
+ bug8433 = '[ruby-core:55102] [Bug #8433]'
+
+ mutex = Mutex.new
+ flag = false
+ mutex.lock
+
+ th = Thread.new do
+ mutex.synchronize do
+ flag = true
+ sleep
+ end
+ end
+
+ Thread.pass until th.stop?
+ mutex.unlock
+
+ pid = Process.fork do
+ exit(mutex.locked?)
+ end
+
+ th.kill
+
+ pid, status = Process.waitpid2(pid)
+ assert_equal(false, status.success?, bug8433)
+ end if Process.respond_to?(:fork)
+
+ def test_fork_in_thread
+ bug9751 = '[ruby-core:62070] [Bug #9751]'
+ f = nil
+ th = Thread.start do
+ unless f = IO.popen("-")
+ STDERR.reopen(STDOUT)
+ exit
+ end
+ Process.wait2(f.pid)
+ end
+ unless th.join(3)
+ Process.kill(:QUIT, f.pid)
+ Process.kill(:KILL, f.pid) unless th.join(1)
+ end
+ _, status = th.value
+ output = f.read
+ f.close
+ assert_not_predicate(status, :signaled?, FailDesc[status, bug9751, output])
+ assert_predicate(status, :success?, bug9751)
+ end if Process.respond_to?(:fork)
+end
diff --git a/jni/ruby/test/ruby/test_threadgroup.rb b/jni/ruby/test/ruby/test_threadgroup.rb
new file mode 100644
index 0000000..e158f5a
--- /dev/null
+++ b/jni/ruby/test/ruby/test_threadgroup.rb
@@ -0,0 +1,57 @@
+require 'test/unit'
+require 'thread'
+
+class TestThreadGroup < Test::Unit::TestCase
+ def test_thread_init
+ thgrp = ThreadGroup.new
+ th = Thread.new{
+ thgrp.add(Thread.current)
+ Thread.new{sleep 1}
+ }.value
+ assert_equal(thgrp, th.group)
+ ensure
+ th.join
+ end
+
+ def test_frozen_thgroup
+ thgrp = ThreadGroup.new
+
+ t = Thread.new{1}
+ Thread.new{
+ thgrp.add(Thread.current)
+ thgrp.freeze
+ assert_raise(ThreadError) do
+ Thread.new{1}.join
+ end
+ assert_raise(ThreadError) do
+ thgrp.add(t)
+ end
+ assert_raise(ThreadError) do
+ ThreadGroup.new.add Thread.current
+ end
+ }.join
+ t.join
+ end
+
+ def test_enclosed_thgroup
+ thgrp = ThreadGroup.new
+ assert_equal(false, thgrp.enclosed?)
+
+ t = Thread.new{1}
+ Thread.new{
+ thgrp.add(Thread.current)
+ thgrp.enclose
+ assert_equal(true, thgrp.enclosed?)
+ assert_nothing_raised do
+ Thread.new{1}.join
+ end
+ assert_raise(ThreadError) do
+ thgrp.add t
+ end
+ assert_raise(ThreadError) do
+ ThreadGroup.new.add Thread.current
+ end
+ }.join
+ t.join
+ end
+end
diff --git a/jni/ruby/test/ruby/test_time.rb b/jni/ruby/test/ruby/test_time.rb
new file mode 100644
index 0000000..f4fb5bf
--- /dev/null
+++ b/jni/ruby/test/ruby/test_time.rb
@@ -0,0 +1,1049 @@
+require 'test/unit'
+require 'delegate'
+require 'timeout'
+require 'delegate'
+
+class TestTime < Test::Unit::TestCase
+ def setup
+ @verbose = $VERBOSE
+ $VERBOSE = nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose
+ end
+
+ def in_timezone(zone)
+ orig_zone = ENV['TZ']
+
+ ENV['TZ'] = zone
+ yield
+ ensure
+ ENV['TZ'] = orig_zone
+ end
+
+ def no_leap_seconds?
+ # 1972-06-30T23:59:60Z is the first leap second.
+ Time.utc(1972, 7, 1, 0, 0, 0) - Time.utc(1972, 6, 30, 23, 59, 59) == 1
+ end
+
+ def get_t2000
+ if no_leap_seconds?
+ # Sat Jan 01 00:00:00 UTC 2000
+ Time.at(946684800).gmtime
+ else
+ Time.utc(2000, 1, 1)
+ end
+ end
+
+ def test_new
+ assert_equal(Time.utc(2000,2,10), Time.new(2000,2,10, 11,0,0, 3600*11))
+ assert_equal(Time.utc(2000,2,10), Time.new(2000,2,9, 13,0,0, -3600*11))
+ assert_equal(Time.utc(2000,2,10), Time.new(2000,2,10, 11,0,0, "+11:00"))
+ assert_equal(Rational(1,2), Time.new(2000,2,10, 11,0,5.5, "+11:00").subsec)
+ bug4090 = '[ruby-dev:42631]'
+ tm = [2001,2,28,23,59,30]
+ t = Time.new(*tm, "-12:00")
+ assert_equal([2001,2,28,23,59,30,-43200], [t.year, t.month, t.mday, t.hour, t.min, t.sec, t.gmt_offset], bug4090)
+ assert_raise(ArgumentError) { Time.new(2000,1,1, 0,0,0, "+01:60") }
+ end
+
+ def test_time_add()
+ assert_equal(Time.utc(2000, 3, 21, 3, 30) + 3 * 3600,
+ Time.utc(2000, 3, 21, 6, 30))
+ assert_equal(Time.utc(2000, 3, 21, 3, 30) + (-3 * 3600),
+ Time.utc(2000, 3, 21, 0, 30))
+ assert_equal(0, (Time.at(1.1) + 0.9).usec)
+
+ assert_predicate((Time.utc(2000, 4, 1) + 24), :utc?)
+ assert_not_predicate((Time.local(2000, 4, 1) + 24), :utc?)
+
+ t = Time.new(2000, 4, 1, 0, 0, 0, "+01:00") + 24
+ assert_not_predicate(t, :utc?)
+ assert_equal(3600, t.utc_offset)
+ t = Time.new(2000, 4, 1, 0, 0, 0, "+02:00") + 24
+ assert_not_predicate(t, :utc?)
+ assert_equal(7200, t.utc_offset)
+ end
+
+ def test_time_subt()
+ assert_equal(Time.utc(2000, 3, 21, 3, 30) - 3 * 3600,
+ Time.utc(2000, 3, 21, 0, 30))
+ assert_equal(Time.utc(2000, 3, 21, 3, 30) - (-3 * 3600),
+ Time.utc(2000, 3, 21, 6, 30))
+ assert_equal(900000, (Time.at(1.1) - 0.2).usec)
+ end
+
+ def test_time_time()
+ assert_equal(Time.utc(2000, 3, 21, 3, 30) \
+ -Time.utc(2000, 3, 21, 0, 30), 3*3600)
+ assert_equal(Time.utc(2000, 3, 21, 0, 30) \
+ -Time.utc(2000, 3, 21, 3, 30), -3*3600)
+ end
+
+ def negative_time_t?
+ begin
+ Time.at(-1)
+ true
+ rescue ArgumentError
+ false
+ end
+ end
+
+ def test_timegm
+ if negative_time_t?
+ assert_equal(-0x80000000, Time.utc(1901, 12, 13, 20, 45, 52).tv_sec)
+ assert_equal(-2, Time.utc(1969, 12, 31, 23, 59, 58).tv_sec)
+ assert_equal(-1, Time.utc(1969, 12, 31, 23, 59, 59).tv_sec)
+ end
+
+ assert_equal(0, Time.utc(1970, 1, 1, 0, 0, 0).tv_sec) # the Epoch
+ assert_equal(1, Time.utc(1970, 1, 1, 0, 0, 1).tv_sec)
+ assert_equal(31535999, Time.utc(1970, 12, 31, 23, 59, 59).tv_sec)
+ assert_equal(31536000, Time.utc(1971, 1, 1, 0, 0, 0).tv_sec)
+ assert_equal(78796799, Time.utc(1972, 6, 30, 23, 59, 59).tv_sec)
+
+ if no_leap_seconds?
+ assert_equal(78796800, Time.utc(1972, 7, 1, 0, 0, 0).tv_sec)
+ assert_equal(78796801, Time.utc(1972, 7, 1, 0, 0, 1).tv_sec)
+ assert_equal(946684800, Time.utc(2000, 1, 1, 0, 0, 0).tv_sec)
+ assert_equal(0x7fffffff, Time.utc(2038, 1, 19, 3, 14, 7).tv_sec)
+ assert_equal(0x80000000, Time.utc(2038, 1, 19, 3, 14, 8).tv_sec)
+ else
+ assert_equal(2, Time.utc(1972, 7, 1, 0, 0, 0) - Time.utc(1972, 6, 30, 23, 59, 59))
+ assert_equal(78796800, Time.utc(1972, 6, 30, 23, 59, 60).tv_sec)
+ assert_equal(78796801, Time.utc(1972, 7, 1, 0, 0, 0).tv_sec)
+ assert_equal(78796802, Time.utc(1972, 7, 1, 0, 0, 1).tv_sec)
+ assert_equal(946684822, Time.utc(2000, 1, 1, 0, 0, 0).tv_sec)
+ end
+ end
+
+ def test_strtime
+ t = nil
+ assert_nothing_raised { t = Time.utc("2000", "1", "2" , "3", "4", "5") }
+ assert_equal(Time.utc(2000,1,2,3,4,5), t)
+ end
+
+ def test_huge_difference
+ if negative_time_t?
+ assert_equal(Time.at(-0x80000000), Time.at(0x7fffffff) - 0xffffffff, "[ruby-dev:22619]")
+ assert_equal(Time.at(-0x80000000), Time.at(0x7fffffff) + (-0xffffffff))
+ assert_equal(Time.at(0x7fffffff), Time.at(-0x80000000) + 0xffffffff, "[ruby-dev:22619]")
+ assert_equal(Time.at(0x7fffffff), Time.at(-0x80000000) - (-0xffffffff))
+ end
+ end
+
+ def test_big_minus
+ begin
+ bigtime0 = Time.at(2**60)
+ bigtime1 = Time.at(2**60+1)
+ rescue RangeError
+ return
+ end
+ assert_equal(1.0, bigtime1 - bigtime0)
+ end
+
+ def test_at
+ assert_equal(100000, Time.at("0.1".to_r).usec)
+ assert_equal(10000, Time.at("0.01".to_r).usec)
+ assert_equal(1000, Time.at("0.001".to_r).usec)
+ assert_equal(100, Time.at("0.0001".to_r).usec)
+ assert_equal(10, Time.at("0.00001".to_r).usec)
+ assert_equal(1, Time.at("0.000001".to_r).usec)
+ assert_equal(100000000, Time.at("0.1".to_r).nsec)
+ assert_equal(10000000, Time.at("0.01".to_r).nsec)
+ assert_equal(1000000, Time.at("0.001".to_r).nsec)
+ assert_equal(100000, Time.at("0.0001".to_r).nsec)
+ assert_equal(10000, Time.at("0.00001".to_r).nsec)
+ assert_equal(1000, Time.at("0.000001".to_r).nsec)
+ assert_equal(100, Time.at("0.0000001".to_r).nsec)
+ assert_equal(10, Time.at("0.00000001".to_r).nsec)
+ assert_equal(1, Time.at("0.000000001".to_r).nsec)
+ assert_equal(100000, Time.at(0.1).usec)
+ assert_equal(10000, Time.at(0.01).usec)
+ assert_equal(1000, Time.at(0.001).usec)
+ assert_equal(100, Time.at(0.0001).usec)
+ assert_equal(10, Time.at(0.00001).usec)
+ assert_equal(3, Time.at(0.000003).usec)
+ assert_equal(100000000, Time.at(0.1).nsec)
+ assert_equal(10000000, Time.at(0.01).nsec)
+ assert_equal(1000000, Time.at(0.001).nsec)
+ assert_equal(100000, Time.at(0.0001).nsec)
+ assert_equal(10000, Time.at(0.00001).nsec)
+ assert_equal(3000, Time.at(0.000003).nsec)
+ assert_equal(200, Time.at(0.0000002r).nsec)
+ assert_equal(199, Time.at(0.0000002).nsec)
+ assert_equal(10, Time.at(0.00000001).nsec)
+ assert_equal(1, Time.at(0.000000001).nsec)
+
+ assert_equal(0, Time.at(1e-10).nsec)
+ assert_equal(0, Time.at(4e-10).nsec)
+ assert_equal(0, Time.at(6e-10).nsec)
+ assert_equal(1, Time.at(14e-10).nsec)
+ assert_equal(1, Time.at(16e-10).nsec)
+ if negative_time_t?
+ assert_equal(999999999, Time.at(-1e-10).nsec)
+ assert_equal(999999999, Time.at(-4e-10).nsec)
+ assert_equal(999999999, Time.at(-6e-10).nsec)
+ assert_equal(999999998, Time.at(-14e-10).nsec)
+ assert_equal(999999998, Time.at(-16e-10).nsec)
+ end
+
+ t = Time.at(-4611686019).utc
+ assert_equal(1823, t.year)
+
+ t = Time.at(4611686018, 999999).utc
+ assert_equal(2116, t.year)
+ assert_equal("0.999999".to_r, t.subsec)
+
+ t = Time.at(2**40 + "1/3".to_r, 9999999999999).utc
+ assert_equal(36812, t.year)
+
+ t = Time.at(-0x3fff_ffff_ffff_ffff)
+ assert_equal(-146138510344, t.year)
+ t = Time.at(-0x4000_0000_0000_0000)
+ assert_equal(-146138510344, t.year)
+ t = Time.at(-0x4000_0000_0000_0001)
+ assert_equal(-146138510344, t.year)
+ t = Time.at(-0x5000_0000_0000_0001)
+ assert_equal(-182673138422, t.year)
+ t = Time.at(-0x6000_0000_0000_0000)
+ assert_equal(-219207766501, t.year)
+
+ t = Time.at(0).utc
+ assert_equal([1970,1,1, 0,0,0], [t.year, t.mon, t.day, t.hour, t.min, t.sec])
+ t = Time.at(-86400).utc
+ assert_equal([1969,12,31, 0,0,0], [t.year, t.mon, t.day, t.hour, t.min, t.sec])
+ t = Time.at(-86400 * (400 * 365 + 97)).utc
+ assert_equal([1970-400,1,1, 0,0,0], [t.year, t.mon, t.day, t.hour, t.min, t.sec])
+ t = Time.at(-86400 * (400 * 365 + 97)*1000).utc
+ assert_equal([1970-400*1000,1,1, 0,0,0], [t.year, t.mon, t.day, t.hour, t.min, t.sec])
+ t = Time.at(-86400 * (400 * 365 + 97)*2421).utc
+ assert_equal([1970-400*2421,1,1, 0,0,0], [t.year, t.mon, t.day, t.hour, t.min, t.sec])
+ t = Time.at(-86400 * (400 * 365 + 97)*1000000).utc
+ assert_equal([1970-400*1000000,1,1, 0,0,0], [t.year, t.mon, t.day, t.hour, t.min, t.sec])
+
+ t = Time.at(-30613683110400).utc
+ assert_equal([-968139,1,1, 0,0,0], [t.year, t.mon, t.day, t.hour, t.min, t.sec])
+ t = Time.at(-30613683110401).utc
+ assert_equal([-968140,12,31, 23,59,59], [t.year, t.mon, t.day, t.hour, t.min, t.sec])
+ end
+
+ def test_at2
+ assert_equal(100, Time.at(0, 0.1).nsec)
+ assert_equal(10, Time.at(0, 0.01).nsec)
+ assert_equal(1, Time.at(0, 0.001).nsec)
+ end
+
+ def test_at_rational
+ assert_equal(1, Time.at(Rational(1,1) / 1000000000).nsec)
+ assert_equal(1, Time.at(1167609600 + Rational(1,1) / 1000000000).nsec)
+ end
+
+ def test_utc_subsecond
+ assert_equal(500000, Time.utc(2007,1,1,0,0,1.5).usec)
+ assert_equal(100000, Time.utc(2007,1,1,0,0,Rational(11,10)).usec)
+ end
+
+ def test_eq_nsec
+ assert_equal(Time.at(0, 0.123), Time.at(0, 0.123))
+ assert_not_equal(Time.at(0, 0.123), Time.at(0, 0.124))
+ end
+
+ def assert_marshal_roundtrip(t)
+ iv_names = t.instance_variables
+ iv_vals1 = iv_names.map {|n| t.instance_variable_get n }
+ m = Marshal.dump(t)
+ t2 = Marshal.load(m)
+ iv_vals2 = iv_names.map {|n| t2.instance_variable_get n }
+ assert_equal(t, t2)
+ assert_equal(iv_vals1, iv_vals2)
+ t2
+ end
+
+ def test_marshal_nsec
+ assert_marshal_roundtrip(Time.at(0, 0.123))
+ assert_marshal_roundtrip(Time.at(0, 0.120))
+ end
+
+ def test_marshal_nsec_191
+ # generated by ruby 1.9.1p376
+ m = "\x04\bIu:\tTime\r \x80\x11\x80@\xE2\x01\x00\x06:\rsubmicro\"\ax\x90"
+ t = Marshal.load(m)
+ assert_equal(Time.at(Rational(123456789, 1000000000)), t, "[ruby-dev:40133]")
+ end
+
+ def test_marshal_rational
+ assert_marshal_roundtrip(Time.at(0, Rational(1,3)))
+ assert_not_match(/Rational/, Marshal.dump(Time.at(0, Rational(1,3))))
+ end
+
+ def test_marshal_ivar
+ t = Time.at(123456789, 987654.321)
+ t.instance_eval { @var = 135 }
+ assert_marshal_roundtrip(t)
+ assert_marshal_roundtrip(Marshal.load(Marshal.dump(t)))
+ end
+
+ def test_marshal_timezone
+ bug = '[ruby-dev:40063]'
+
+ t1 = Time.gm(2000)
+ m = Marshal.dump(t1.getlocal("-02:00"))
+ t2 = Marshal.load(m)
+ assert_equal(t1, t2)
+ assert_equal(-7200, t2.utc_offset, bug)
+ m = Marshal.dump(t1.getlocal("+08:15"))
+ t2 = Marshal.load(m)
+ assert_equal(t1, t2)
+ assert_equal(29700, t2.utc_offset, bug)
+ end
+
+ def test_marshal_zone
+ t = Time.utc(2013, 2, 24)
+ assert_equal('UTC', t.zone)
+ assert_equal('UTC', Marshal.load(Marshal.dump(t)).zone)
+
+ in_timezone('JST-9') do
+ t = Time.local(2013, 2, 24)
+ assert_equal('JST', Time.local(2013, 2, 24).zone)
+ assert_equal('JST', Marshal.load(Marshal.dump(t)).zone)
+ end
+ end
+
+ def test_marshal_zone_gc
+ assert_separately(%w(--disable-gems), <<-'end;', timeout: 30)
+ ENV["TZ"] = "JST-9"
+ s = Marshal.dump(Time.now)
+ t = Marshal.load(s)
+ n = 0
+ done = 100000
+ while t.zone.dup == "JST" && n < done
+ n += 1
+ end
+ assert_equal done, n, "Bug #9652"
+ assert_equal "JST", t.zone, "Bug #9652"
+ end;
+ end
+
+ def test_marshal_to_s
+ t1 = Time.new(2011,11,8, 0,42,25, 9*3600)
+ t2 = Time.at(Marshal.load(Marshal.dump(t1)))
+ assert_equal("2011-11-08 00:42:25 +0900", t2.to_s,
+ "[ruby-dev:44827] [Bug #5586]")
+ end
+
+ Bug8795 = '[ruby-core:56648] [Bug #8795]'
+
+ def test_marshal_broken_offset
+ data = "\x04\bIu:\tTime\r\xEFF\x1C\x80\x00\x00\x00\x00\x06:\voffset"
+ t1 = t2 = nil
+ in_timezone('UTC') do
+ assert_nothing_raised(TypeError, ArgumentError, Bug8795) do
+ t1 = Marshal.load(data + "T")
+ t2 = Marshal.load(data + "\"\x0ebadoffset")
+ end
+ assert_equal(0, t1.utc_offset)
+ assert_equal(0, t2.utc_offset)
+ end
+ end
+
+ def test_marshal_broken_zone
+ data = "\x04\bIu:\tTime\r\xEFF\x1C\x80\x00\x00\x00\x00\x06:\tzone"
+ t1 = t2 = nil
+ in_timezone('UTC') do
+ assert_nothing_raised(TypeError, ArgumentError, Bug8795) do
+ t1 = Marshal.load(data + "T")
+ t2 = Marshal.load(data + "\"\b\0\0\0")
+ end
+ assert_equal('UTC', t1.zone)
+ assert_equal('UTC', t2.zone)
+ end
+ end
+
+ def test_at3
+ t2000 = get_t2000
+ assert_equal(t2000, Time.at(t2000))
+# assert_raise(RangeError) do
+# Time.at(2**31-1, 1_000_000)
+# Time.at(2**63-1, 1_000_000)
+# end
+# assert_raise(RangeError) do
+# Time.at(-2**31, -1_000_000)
+# Time.at(-2**63, -1_000_000)
+# end
+ end
+
+ def test_utc_or_local
+ t2000 = get_t2000
+ assert_equal(t2000, Time.gm(2000))
+ assert_equal(t2000, Time.gm(0, 0, 0, 1, 1, 2000, :foo, :bar, false, :baz))
+ assert_equal(t2000, Time.gm(2000, "jan"))
+ assert_equal(t2000, Time.gm(2000, "1"))
+ assert_equal(t2000, Time.gm(2000, 1, 1, 0, 0, 0, 0))
+ assert_equal(t2000, Time.gm(2000, 1, 1, 0, 0, 0, "0"))
+ assert_equal(t2000, Time.gm(2000, 1, 1, 0, 0, "0", :foo, :foo))
+ assert_raise(ArgumentError) { Time.gm(2000, 1, 1, 0, 0, -1, :foo, :foo) }
+ assert_raise(ArgumentError) { Time.gm(2000, 1, 1, 0, 0, -1.0, :foo, :foo) }
+ assert_raise(RangeError) do
+ Time.gm(2000, 1, 1, 0, 0, 10_000_000_000_000_000_001.0, :foo, :foo)
+ end
+ assert_raise(ArgumentError) { Time.gm(2000, 1, 1, 0, 0, -(2**31), :foo, :foo) }
+ o = Object.new
+ def o.to_int; 0; end
+ def o.to_r; nil; end
+ assert_raise(TypeError) { Time.gm(2000, 1, 1, 0, 0, o, :foo, :foo) }
+ def o.to_r; ""; end
+ assert_raise(TypeError) { Time.gm(2000, 1, 1, 0, 0, o, :foo, :foo) }
+ def o.to_r; Rational(11); end
+ assert_equal(11, Time.gm(2000, 1, 1, 0, 0, o).sec)
+ o = Object.new
+ def o.to_int; 10; end
+ assert_equal(10, Time.gm(2000, 1, 1, 0, 0, o).sec)
+ assert_raise(ArgumentError) { Time.gm(2000, 13) }
+
+ t = Time.local(2000)
+ assert_equal(t.gmt_offset, t2000 - t)
+
+ assert_equal(-4427700000, Time.utc(-4427700000,12,1).year)
+ assert_equal(-2**30+10, Time.utc(-2**30+10,1,1).year)
+ end
+
+ def test_time_interval
+ m = Mutex.new.lock
+ assert_nothing_raised {
+ Timeout.timeout(10) {
+ m.sleep(0)
+ }
+ }
+ assert_raise(ArgumentError) { m.sleep(-1) }
+ end
+
+ def test_to_f
+ t2000 = Time.at(946684800).gmtime
+ assert_equal(946684800.0, t2000.to_f)
+ end
+
+ def test_to_f_accuracy
+ # https://bugs.ruby-lang.org/issues/10135#note-1
+ f = 1381089302.195
+ assert_equal(f, Time.at(f).to_f, "[ruby-core:64373] [Bug #10135] note-1")
+ end
+
+ def test_cmp
+ t2000 = get_t2000
+ assert_equal(-1, t2000 <=> Time.gm(2001))
+ assert_equal(1, t2000 <=> Time.gm(1999))
+ assert_nil(t2000 <=> 0)
+ end
+
+ def test_eql
+ t2000 = get_t2000
+ assert_operator(t2000, :eql?, t2000)
+ assert_not_operator(t2000, :eql?, Time.gm(2001))
+ end
+
+ def test_utc_p
+ assert_predicate(Time.gm(2000), :gmt?)
+ assert_not_predicate(Time.local(2000), :gmt?)
+ assert_not_predicate(Time.at(0), :gmt?)
+ end
+
+ def test_hash
+ t2000 = get_t2000
+ assert_kind_of(Integer, t2000.hash)
+ end
+
+ def test_reinitialize
+ bug8099 = '[ruby-core:53436] [Bug #8099]'
+ t2000 = get_t2000
+ assert_raise(TypeError, bug8099) {
+ t2000.send(:initialize, 2013, 03, 14)
+ }
+ assert_equal(get_t2000, t2000, bug8099)
+ end
+
+ def test_init_copy
+ t2000 = get_t2000
+ assert_equal(t2000, t2000.dup)
+ assert_raise(TypeError) do
+ t2000.instance_eval { initialize_copy(nil) }
+ end
+ end
+
+ def test_localtime_gmtime
+ assert_nothing_raised do
+ t = Time.gm(2000)
+ assert_predicate(t, :gmt?)
+ t.localtime
+ assert_not_predicate(t, :gmt?)
+ t.localtime
+ assert_not_predicate(t, :gmt?)
+ t.gmtime
+ assert_predicate(t, :gmt?)
+ t.gmtime
+ assert_predicate(t, :gmt?)
+ end
+
+ t1 = Time.gm(2000)
+ t2 = t1.getlocal
+ assert_equal(t1, t2)
+ t3 = t1.getlocal("-02:00")
+ assert_equal(t1, t3)
+ assert_equal(-7200, t3.utc_offset)
+ assert_equal([1999, 12, 31, 22, 0, 0], [t3.year, t3.mon, t3.mday, t3.hour, t3.min, t3.sec])
+ t1.localtime
+ assert_equal(t1, t2)
+ assert_equal(t1.gmt?, t2.gmt?)
+ assert_equal(t1, t3)
+
+ t1 = Time.local(2000)
+ t2 = t1.getgm
+ assert_equal(t1, t2)
+ t3 = t1.getlocal("-02:00")
+ assert_equal(t1, t3)
+ assert_equal(-7200, t3.utc_offset)
+ t1.gmtime
+ assert_equal(t1, t2)
+ assert_equal(t1.gmt?, t2.gmt?)
+ assert_equal(t1, t3)
+ end
+
+ def test_asctime
+ t2000 = get_t2000
+ assert_equal("Sat Jan 1 00:00:00 2000", t2000.asctime)
+ assert_equal(Encoding::US_ASCII, t2000.asctime.encoding)
+ assert_kind_of(String, Time.at(0).asctime)
+ end
+
+ def test_to_s
+ t2000 = get_t2000
+ assert_equal("2000-01-01 00:00:00 UTC", t2000.to_s)
+ assert_equal(Encoding::US_ASCII, t2000.to_s.encoding)
+ assert_kind_of(String, Time.at(946684800).getlocal.to_s)
+ assert_equal(Time.at(946684800).getlocal.to_s, Time.at(946684800).to_s)
+ end
+
+ def assert_zone_encoding(time)
+ zone = time.zone
+ assert_predicate(zone, :valid_encoding?)
+ if zone.ascii_only?
+ assert_equal(Encoding::US_ASCII, zone.encoding)
+ else
+ enc = Encoding.default_internal || Encoding.find('locale')
+ assert_equal(enc, zone.encoding)
+ end
+ end
+
+ def test_zone
+ assert_zone_encoding Time.now
+ end
+
+ def test_plus_minus_succ
+ t2000 = get_t2000
+ # assert_raise(RangeError) { t2000 + 10000000000 }
+ # assert_raise(RangeError) t2000 - 3094168449 }
+ # assert_raise(RangeError) { t2000 + 1200798848 }
+ assert_raise(TypeError) { t2000 + Time.now }
+ assert_equal(t2000 + 1, t2000.succ)
+ end
+
+ def test_plus_type
+ t0 = Time.utc(2000,1,1)
+ n0 = t0.to_i
+ n1 = n0+1
+ t1 = Time.at(n1)
+ assert_equal(t1, t0 + 1)
+ assert_equal(t1, t0 + 1.0)
+ assert_equal(t1, t0 + Rational(1,1))
+ assert_equal(t1, t0 + SimpleDelegator.new(1))
+ assert_equal(t1, t0 + SimpleDelegator.new(1.0))
+ assert_equal(t1, t0 + SimpleDelegator.new(Rational(1,1)))
+ assert_raise(TypeError) { t0 + nil }
+ assert_raise(TypeError) { t0 + "1" }
+ assert_raise(TypeError) { t0 + SimpleDelegator.new("1") }
+ assert_equal(0.5, (t0 + 1.5).subsec)
+ assert_equal(Rational(1,3), (t0 + Rational(4,3)).subsec)
+ assert_equal(0.5, (t0 + SimpleDelegator.new(1.5)).subsec)
+ assert_equal(Rational(1,3), (t0 + SimpleDelegator.new(Rational(4,3))).subsec)
+ end
+
+ def test_minus
+ t = Time.at(-4611686018).utc - 100
+ assert_equal(1823, t.year)
+ end
+
+ def test_readers
+ t2000 = get_t2000
+ assert_equal(0, t2000.sec)
+ assert_equal(0, t2000.min)
+ assert_equal(0, t2000.hour)
+ assert_equal(1, t2000.mday)
+ assert_equal(1, t2000.mon)
+ assert_equal(2000, t2000.year)
+ assert_equal(6, t2000.wday)
+ assert_equal(1, t2000.yday)
+ assert_equal(false, t2000.isdst)
+ assert_equal("UTC", t2000.zone)
+ assert_zone_encoding(t2000)
+ assert_equal(0, t2000.gmt_offset)
+ assert_not_predicate(t2000, :sunday?)
+ assert_not_predicate(t2000, :monday?)
+ assert_not_predicate(t2000, :tuesday?)
+ assert_not_predicate(t2000, :wednesday?)
+ assert_not_predicate(t2000, :thursday?)
+ assert_not_predicate(t2000, :friday?)
+ assert_predicate(t2000, :saturday?)
+ assert_equal([0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], t2000.to_a)
+
+ t = Time.at(946684800).getlocal
+ assert_equal(t.sec, Time.at(946684800).sec)
+ assert_equal(t.min, Time.at(946684800).min)
+ assert_equal(t.hour, Time.at(946684800).hour)
+ assert_equal(t.mday, Time.at(946684800).mday)
+ assert_equal(t.mon, Time.at(946684800).mon)
+ assert_equal(t.year, Time.at(946684800).year)
+ assert_equal(t.wday, Time.at(946684800).wday)
+ assert_equal(t.yday, Time.at(946684800).yday)
+ assert_equal(t.isdst, Time.at(946684800).isdst)
+ assert_equal(t.zone, Time.at(946684800).zone)
+ assert_zone_encoding(Time.at(946684800))
+ assert_equal(t.gmt_offset, Time.at(946684800).gmt_offset)
+ assert_equal(t.sunday?, Time.at(946684800).sunday?)
+ assert_equal(t.monday?, Time.at(946684800).monday?)
+ assert_equal(t.tuesday?, Time.at(946684800).tuesday?)
+ assert_equal(t.wednesday?, Time.at(946684800).wednesday?)
+ assert_equal(t.thursday?, Time.at(946684800).thursday?)
+ assert_equal(t.friday?, Time.at(946684800).friday?)
+ assert_equal(t.saturday?, Time.at(946684800).saturday?)
+ assert_equal(t.to_a, Time.at(946684800).to_a)
+ end
+
+ def test_strftime
+ t2000 = get_t2000
+ t = Time.at(946684800).getlocal
+ assert_equal("Sat", t2000.strftime("%a"))
+ assert_equal("Saturday", t2000.strftime("%A"))
+ assert_equal("Jan", t2000.strftime("%b"))
+ assert_equal("January", t2000.strftime("%B"))
+ assert_kind_of(String, t2000.strftime("%c"))
+ assert_equal("01", t2000.strftime("%d"))
+ assert_equal("00", t2000.strftime("%H"))
+ assert_equal("12", t2000.strftime("%I"))
+ assert_equal("001", t2000.strftime("%j"))
+ assert_equal("01", t2000.strftime("%m"))
+ assert_equal("00", t2000.strftime("%M"))
+ assert_equal("AM", t2000.strftime("%p"))
+ assert_equal("00", t2000.strftime("%S"))
+ assert_equal("00", t2000.strftime("%U"))
+ assert_equal("00", t2000.strftime("%W"))
+ assert_equal("6", t2000.strftime("%w"))
+ assert_equal("01/01/00", t2000.strftime("%x"))
+ assert_equal("00:00:00", t2000.strftime("%X"))
+ assert_equal("00", t2000.strftime("%y"))
+ assert_equal("2000", t2000.strftime("%Y"))
+ assert_equal("UTC", t2000.strftime("%Z"))
+ assert_equal("%", t2000.strftime("%%"))
+ assert_equal("0", t2000.strftime("%-S"))
+
+ assert_equal("", t2000.strftime(""))
+ assert_equal("foo\0bar\x0000\x0000\x0000", t2000.strftime("foo\0bar\0%H\0%M\0%S"))
+ assert_equal("foo" * 1000, t2000.strftime("foo" * 1000))
+
+ t = Time.mktime(2000, 1, 1)
+ assert_equal("Sat", t.strftime("%a"))
+ end
+
+ def test_strftime_subsec
+ t = Time.at(946684800, 123456.789)
+ assert_equal("123", t.strftime("%3N"))
+ assert_equal("123456", t.strftime("%6N"))
+ assert_equal("123456789", t.strftime("%9N"))
+ assert_equal("1234567890", t.strftime("%10N"))
+ assert_equal("123456789", t.strftime("%0N"))
+ end
+
+ def test_strftime_sec
+ t = get_t2000.getlocal
+ assert_equal("000", t.strftime("%3S"))
+ end
+
+ def test_strftime_seconds_from_epoch
+ t = Time.at(946684800, 123456.789)
+ assert_equal("946684800", t.strftime("%s"))
+ assert_equal("946684800", t.utc.strftime("%s"))
+ end
+
+ def test_strftime_zone
+ t = Time.mktime(2001, 10, 1)
+ assert_equal("2001-10-01", t.strftime("%F"))
+ assert_equal(Encoding::UTF_8, t.strftime("\u3042%Z").encoding)
+ assert_equal(true, t.strftime("\u3042%Z").valid_encoding?)
+ end
+
+ def test_strftime_flags
+ t = Time.mktime(2001, 10, 1, 2, 0, 0)
+ assert_equal("01", t.strftime("%d"))
+ assert_equal("01", t.strftime("%0d"))
+ assert_equal(" 1", t.strftime("%_d"))
+ assert_equal(" 1", t.strftime("%e"))
+ assert_equal("01", t.strftime("%0e"))
+ assert_equal(" 1", t.strftime("%_e"))
+ assert_equal("AM", t.strftime("%p"))
+ assert_equal("am", t.strftime("%#p"))
+ assert_equal("am", t.strftime("%P"))
+ assert_equal("AM", t.strftime("%#P"))
+ assert_equal("02", t.strftime("%H"))
+ assert_equal("02", t.strftime("%0H"))
+ assert_equal(" 2", t.strftime("%_H"))
+ assert_equal("02", t.strftime("%I"))
+ assert_equal("02", t.strftime("%0I"))
+ assert_equal(" 2", t.strftime("%_I"))
+ assert_equal(" 2", t.strftime("%k"))
+ assert_equal("02", t.strftime("%0k"))
+ assert_equal(" 2", t.strftime("%_k"))
+ assert_equal(" 2", t.strftime("%l"))
+ assert_equal("02", t.strftime("%0l"))
+ assert_equal(" 2", t.strftime("%_l"))
+ t = Time.mktime(2001, 10, 1, 14, 0, 0)
+ assert_equal("PM", t.strftime("%p"))
+ assert_equal("pm", t.strftime("%#p"))
+ assert_equal("pm", t.strftime("%P"))
+ assert_equal("PM", t.strftime("%#P"))
+ assert_equal("14", t.strftime("%H"))
+ assert_equal("14", t.strftime("%0H"))
+ assert_equal("14", t.strftime("%_H"))
+ assert_equal("02", t.strftime("%I"))
+ assert_equal("02", t.strftime("%0I"))
+ assert_equal(" 2", t.strftime("%_I"))
+ assert_equal("14", t.strftime("%k"))
+ assert_equal("14", t.strftime("%0k"))
+ assert_equal("14", t.strftime("%_k"))
+ assert_equal(" 2", t.strftime("%l"))
+ assert_equal("02", t.strftime("%0l"))
+ assert_equal(" 2", t.strftime("%_l"))
+ end
+
+ def test_strftime_invalid_flags
+ t = Time.mktime(2001, 10, 1, 2, 0, 0)
+ assert_equal("%4^p", t.strftime("%4^p"), 'prec after flag')
+ end
+
+ def test_strftime_year
+ t = Time.utc(1,1,4)
+ assert_equal("0001", t.strftime("%Y"))
+ assert_equal("0001", t.strftime("%G"))
+
+ t = Time.utc(0,1,4)
+ assert_equal("0000", t.strftime("%Y"))
+ assert_equal("0000", t.strftime("%G"))
+
+ t = Time.utc(-1,1,4)
+ assert_equal("-0001", t.strftime("%Y"))
+ assert_equal("-0001", t.strftime("%G"))
+ end
+
+ def test_strftime_weeknum
+ # [ruby-dev:37155]
+ t = Time.mktime(1970, 1, 18)
+ assert_equal("0", t.strftime("%w"))
+ assert_equal("7", t.strftime("%u"))
+ end
+
+ def test_strftime_ctrlchar
+ # [ruby-dev:37160]
+ t2000 = get_t2000
+ assert_equal("\t", t2000.strftime("%t"))
+ assert_equal("\t", t2000.strftime("%0t"))
+ assert_equal("\t", t2000.strftime("%1t"))
+ assert_equal(" \t", t2000.strftime("%3t"))
+ assert_equal("00\t", t2000.strftime("%03t"))
+ assert_equal("\n", t2000.strftime("%n"))
+ assert_equal("\n", t2000.strftime("%0n"))
+ assert_equal("\n", t2000.strftime("%1n"))
+ assert_equal(" \n", t2000.strftime("%3n"))
+ assert_equal("00\n", t2000.strftime("%03n"))
+ end
+
+ def test_strftime_weekflags
+ # [ruby-dev:37162]
+ t2000 = get_t2000
+ assert_equal("SAT", t2000.strftime("%#a"))
+ assert_equal("SATURDAY", t2000.strftime("%#A"))
+ assert_equal("JAN", t2000.strftime("%#b"))
+ assert_equal("JANUARY", t2000.strftime("%#B"))
+ assert_equal("JAN", t2000.strftime("%#h"))
+ assert_equal("FRIDAY", Time.local(2008,1,4).strftime("%#A"))
+ end
+
+ def test_strftime_rational
+ t = Time.utc(2000,3,14, 6,53,"58.979323846".to_r) # Pi Day
+ assert_equal("03/14/2000 6:53:58.97932384600000000000000000000",
+ t.strftime("%m/%d/%Y %l:%M:%S.%29N"))
+ assert_equal("03/14/2000 6:53:58.9793238460",
+ t.strftime("%m/%d/%Y %l:%M:%S.%10N"))
+ assert_equal("03/14/2000 6:53:58.979323846",
+ t.strftime("%m/%d/%Y %l:%M:%S.%9N"))
+ assert_equal("03/14/2000 6:53:58.97932384",
+ t.strftime("%m/%d/%Y %l:%M:%S.%8N"))
+
+ t = Time.utc(1592,3,14, 6,53,"58.97932384626433832795028841971".to_r) # Pi Day
+ assert_equal("03/14/1592 6:53:58.97932384626433832795028841971",
+ t.strftime("%m/%d/%Y %l:%M:%S.%29N"))
+ assert_equal("03/14/1592 6:53:58.9793238462",
+ t.strftime("%m/%d/%Y %l:%M:%S.%10N"))
+ assert_equal("03/14/1592 6:53:58.979323846",
+ t.strftime("%m/%d/%Y %l:%M:%S.%9N"))
+ assert_equal("03/14/1592 6:53:58.97932384",
+ t.strftime("%m/%d/%Y %l:%M:%S.%8N"))
+ end
+
+ def test_strftime_far_future
+ # [ruby-core:33985]
+ assert_equal("3000000000", Time.at(3000000000).strftime('%s'))
+ end
+
+ def test_strftime_too_wide
+ bug4457 = '[ruby-dev:43285]'
+ assert_raise(Errno::ERANGE, bug4457) {Time.now.strftime('%8192z')}
+ end
+
+ def test_strfimte_zoneoffset
+ t2000 = get_t2000
+ t = t2000.getlocal("+09:00:00")
+ assert_equal("+0900", t.strftime("%z"))
+ assert_equal("+09:00", t.strftime("%:z"))
+ assert_equal("+09:00:00", t.strftime("%::z"))
+ assert_equal("+09", t.strftime("%:::z"))
+
+ t = t2000.getlocal("+09:00:01")
+ assert_equal("+0900", t.strftime("%z"))
+ assert_equal("+09:00", t.strftime("%:z"))
+ assert_equal("+09:00:01", t.strftime("%::z"))
+ assert_equal("+09:00:01", t.strftime("%:::z"))
+ end
+
+ def test_strftime_padding
+ bug4458 = '[ruby-dev:43287]'
+ t2000 = get_t2000
+ t = t2000.getlocal("+09:00")
+ assert_equal("+0900", t.strftime("%z"))
+ assert_equal("+09:00", t.strftime("%:z"))
+ assert_equal(" +900", t.strftime("%_10z"), bug4458)
+ assert_equal("+000000900", t.strftime("%10z"), bug4458)
+ assert_equal(" +9:00", t.strftime("%_10:z"), bug4458)
+ assert_equal("+000009:00", t.strftime("%10:z"), bug4458)
+ assert_equal(" +9:00:00", t.strftime("%_10::z"), bug4458)
+ assert_equal("+009:00:00", t.strftime("%10::z"), bug4458)
+ assert_equal("+000000009", t.strftime("%10:::z"))
+ t = t2000.getlocal("-05:00")
+ assert_equal("-0500", t.strftime("%z"))
+ assert_equal("-05:00", t.strftime("%:z"))
+ assert_equal(" -500", t.strftime("%_10z"), bug4458)
+ assert_equal("-000000500", t.strftime("%10z"), bug4458)
+ assert_equal(" -5:00", t.strftime("%_10:z"), bug4458)
+ assert_equal("-000005:00", t.strftime("%10:z"), bug4458)
+ assert_equal(" -5:00:00", t.strftime("%_10::z"), bug4458)
+ assert_equal("-005:00:00", t.strftime("%10::z"), bug4458)
+ assert_equal("-000000005", t.strftime("%10:::z"))
+
+ bug6323 = '[ruby-core:44447]'
+ t = t2000.getlocal("+00:36")
+ assert_equal(" +036", t.strftime("%_10z"), bug6323)
+ assert_equal("+000000036", t.strftime("%10z"), bug6323)
+ assert_equal(" +0:36", t.strftime("%_10:z"), bug6323)
+ assert_equal("+000000:36", t.strftime("%10:z"), bug6323)
+ assert_equal(" +0:36:00", t.strftime("%_10::z"), bug6323)
+ assert_equal("+000:36:00", t.strftime("%10::z"), bug6323)
+ assert_equal("+000000:36", t.strftime("%10:::z"))
+ t = t2000.getlocal("-00:55")
+ assert_equal(" -055", t.strftime("%_10z"), bug6323)
+ assert_equal("-000000055", t.strftime("%10z"), bug6323)
+ assert_equal(" -0:55", t.strftime("%_10:z"), bug6323)
+ assert_equal("-000000:55", t.strftime("%10:z"), bug6323)
+ assert_equal(" -0:55:00", t.strftime("%_10::z"), bug6323)
+ assert_equal("-000:55:00", t.strftime("%10::z"), bug6323)
+ assert_equal("-000000:55", t.strftime("%10:::z"))
+ end
+
+ def test_strftime_invalid_modifier
+ t2000 = get_t2000
+ t = t2000.getlocal("+09:00")
+ assert_equal("%:y", t.strftime("%:y"), 'invalid conversion after : modifier')
+ assert_equal("%:0z", t.strftime("%:0z"), 'flag after : modifier')
+ assert_equal("%:10z", t.strftime("%:10z"), 'prec after : modifier')
+ assert_equal("%Ob", t.strftime("%Ob"), 'invalid conversion after locale modifier')
+ assert_equal("%Eb", t.strftime("%Eb"), 'invalid conversion after locale modifier')
+ assert_equal("%O0y", t.strftime("%O0y"), 'flag after locale modifier')
+ assert_equal("%E0y", t.strftime("%E0y"), 'flag after locale modifier')
+ assert_equal("%O10y", t.strftime("%O10y"), 'prec after locale modifier')
+ assert_equal("%E10y", t.strftime("%E10y"), 'prec after locale modifier')
+ end
+
+ def test_delegate
+ d1 = SimpleDelegator.new(t1 = Time.utc(2000))
+ d2 = SimpleDelegator.new(t2 = Time.utc(2001))
+ assert_equal(-1, t1 <=> t2)
+ assert_equal(1, t2 <=> t1)
+ assert_equal(-1, d1 <=> d2)
+ assert_equal(1, d2 <=> d1)
+ end
+
+ def test_to_r
+ assert_kind_of(Rational, Time.new(2000,1,1,0,0,Rational(4,3)).to_r)
+ assert_kind_of(Rational, Time.utc(1970).to_r)
+ end
+
+ def test_round
+ t = Time.utc(1999,12,31, 23,59,59)
+ t2 = (t+0.4).round
+ assert_equal([59,59,23, 31,12,1999, 5,365,false,"UTC"], t2.to_a)
+ assert_equal(0, t2.subsec)
+ t2 = (t+0.49).round
+ assert_equal([59,59,23, 31,12,1999, 5,365,false,"UTC"], t2.to_a)
+ assert_equal(0, t2.subsec)
+ t2 = (t+0.5).round
+ assert_equal([0,0,0, 1,1,2000, 6,1,false,"UTC"], t2.to_a)
+ assert_equal(0, t2.subsec)
+ t2 = (t+1.4).round
+ assert_equal([0,0,0, 1,1,2000, 6,1,false,"UTC"], t2.to_a)
+ assert_equal(0, t2.subsec)
+ t2 = (t+1.49).round
+ assert_equal([0,0,0, 1,1,2000, 6,1,false,"UTC"], t2.to_a)
+ assert_equal(0, t2.subsec)
+ t2 = (t+1.5).round
+ assert_equal([1,0,0, 1,1,2000, 6,1,false,"UTC"], t2.to_a)
+ assert_equal(0, t2.subsec)
+
+ t2 = (t+0.123456789).round(4)
+ assert_equal([59,59,23, 31,12,1999, 5,365,false,"UTC"], t2.to_a)
+ assert_equal(Rational(1235,10000), t2.subsec)
+
+ off = 0.0
+ 100.times {|i|
+ t2 = (t+off).round(1)
+ assert_equal(Rational(i % 10, 10), t2.subsec)
+ off += 0.1
+ }
+ end
+
+ def test_getlocal_dont_share_eigenclass
+ bug5012 = "[ruby-dev:44071]"
+
+ t0 = Time.now
+ class << t0; end
+ t1 = t0.getlocal
+
+ def t0.m
+ 0
+ end
+
+ assert_raise(NoMethodError, bug5012) { t1.m }
+ end
+
+ def test_sec_str
+ bug6193 = '[ruby-core:43569]'
+ t = nil
+ assert_nothing_raised(bug6193) {t = Time.new(2012, 1, 2, 3, 4, "5")}
+ assert_equal(Time.new(2012, 1, 2, 3, 4, 5), t, bug6193)
+ end
+
+ def test_past
+ [
+ [-(1 << 100), 1, 1, 0, 0, 0],
+ [-4000, 1, 1, 0, 0, 0],
+ [-3000, 1, 1, 0, 0, 0],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ end
+
+ def test_1901
+ assert_equal(-0x80000001, Time.utc(1901, 12, 13, 20, 45, 51).tv_sec)
+ [
+ [1901, 12, 13, 20, 45, 50],
+ [1901, 12, 13, 20, 45, 51],
+ [1901, 12, 13, 20, 45, 52], # -0x80000000
+ [1901, 12, 13, 20, 45, 53],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ end
+
+ def test_1970
+ assert_equal(0, Time.utc(1970, 1, 1, 0, 0, 0).tv_sec)
+ [
+ [1969, 12, 31, 23, 59, 59],
+ [1970, 1, 1, 0, 0, 0],
+ [1970, 1, 1, 0, 0, 1],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ end
+
+ def test_2038
+ if no_leap_seconds?
+ assert_equal(0x80000000, Time.utc(2038, 1, 19, 3, 14, 8).tv_sec)
+ end
+ [
+ [2038, 1, 19, 3, 14, 7],
+ [2038, 1, 19, 3, 14, 8],
+ [2038, 1, 19, 3, 14, 9],
+ [2039, 1, 1, 0, 0, 0],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ end
+
+ def test_future
+ [
+ [3000, 1, 1, 0, 0, 0],
+ [4000, 1, 1, 0, 0, 0],
+ [1 << 100, 1, 1, 0, 0, 0],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ end
+
+ def test_getlocal_nil
+ now = Time.now
+ now2 = nil
+ now3 = nil
+ assert_nothing_raised {
+ now2 = now.getlocal
+ now3 = now.getlocal(nil)
+ }
+ assert_equal now2, now3
+ assert_equal now2.zone, now3.zone
+ end
+end
diff --git a/jni/ruby/test/ruby/test_time_tz.rb b/jni/ruby/test/ruby/test_time_tz.rb
new file mode 100644
index 0000000..fb1ab9b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_time_tz.rb
@@ -0,0 +1,415 @@
+require 'test/unit'
+
+class TestTimeTZ < Test::Unit::TestCase
+ has_right_tz = true
+ has_lisbon_tz = true
+ force_tz_test = ENV["RUBY_FORCE_TIME_TZ_TEST"] == "yes"
+ case RUBY_PLATFORM
+ when /linux/
+ force_tz_test = true
+ when /darwin|freebsd/
+ has_lisbon_tz = false
+ force_tz_test = true
+ end
+
+ if force_tz_test
+ module Util
+ def with_tz(tz)
+ old = ENV["TZ"]
+ begin
+ ENV["TZ"] = tz
+ yield
+ ensure
+ ENV["TZ"] = old
+ end
+ end
+ end
+ else
+ module Util
+ def with_tz(tz)
+ if ENV["TZ"] == tz
+ yield
+ end
+ end
+ end
+ end
+
+ module Util
+ def have_tz_offset?(tz)
+ with_tz(tz) {!Time.now.utc_offset.zero?}
+ end
+
+ def format_gmtoff(gmtoff, colon=false)
+ if gmtoff < 0
+ expected = "-"
+ gmtoff = -gmtoff
+ else
+ expected = "+"
+ end
+ gmtoff /= 60
+ expected << "%02d" % [gmtoff / 60]
+ expected << ":" if colon
+ expected << "%02d" % [gmtoff % 60]
+ expected
+ end
+
+ def format_gmtoff2(gmtoff)
+ if gmtoff < 0
+ expected = "-"
+ gmtoff = -gmtoff
+ else
+ expected = "+"
+ end
+ expected << "%02d:%02d:%02d" % [gmtoff / 3600, gmtoff % 3600 / 60, gmtoff % 60]
+ expected
+ end
+
+ def group_by(e, &block)
+ if e.respond_to? :group_by
+ e.group_by(&block)
+ else
+ h = {}
+ e.each {|o|
+ (h[yield(o)] ||= []) << o
+ }
+ h
+ end
+ end
+
+ end
+
+ include Util
+ extend Util
+
+ has_right_tz &&= have_tz_offset?("right/America/Los_Angeles")
+ has_lisbon_tz &&= have_tz_offset?("Europe/Lisbon")
+
+ def time_to_s(t)
+ t.to_s
+ end
+
+
+ def assert_time_constructor(tz, expected, method, args, message=nil)
+ m = message ? "#{message}\n" : ""
+ m << "TZ=#{tz} Time.#{method}(#{args.map {|arg| arg.inspect }.join(', ')})"
+ real = time_to_s(Time.send(method, *args))
+ assert_equal(expected, real, m)
+ end
+
+ def test_america_los_angeles
+ with_tz(tz="America/Los_Angeles") {
+ assert_time_constructor(tz, "2007-03-11 03:00:00 -0700", :local, [2007,3,11,2,0,0])
+ assert_time_constructor(tz, "2007-03-11 03:59:59 -0700", :local, [2007,3,11,2,59,59])
+ assert_equal("PST", Time.new(0x1_0000_0000_0000_0000, 1).zone)
+ assert_equal("PDT", Time.new(0x1_0000_0000_0000_0000, 8).zone)
+ assert_equal(false, Time.new(0x1_0000_0000_0000_0000, 1).isdst)
+ assert_equal(true, Time.new(0x1_0000_0000_0000_0000, 8).isdst)
+ }
+ end
+
+ def test_america_managua
+ with_tz(tz="America/Managua") {
+ assert_time_constructor(tz, "1993-01-01 01:00:00 -0500", :local, [1993,1,1,0,0,0])
+ assert_time_constructor(tz, "1993-01-01 01:59:59 -0500", :local, [1993,1,1,0,59,59])
+ }
+ end
+
+ def test_asia_singapore
+ with_tz(tz="Asia/Singapore") {
+ assert_time_constructor(tz, "1981-12-31 23:59:59 +0730", :local, [1981,12,31,23,59,59])
+ assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,0,0])
+ assert_time_constructor(tz, "1982-01-01 00:59:59 +0800", :local, [1982,1,1,0,29,59])
+ assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,30,0])
+ }
+ end
+
+ def test_asia_tokyo
+ with_tz(tz="Asia/Tokyo") {
+ assert_time_constructor(tz, "1951-05-06 03:00:00 +1000", :local, [1951,5,6,2,0,0])
+ assert_time_constructor(tz, "1951-05-06 03:59:59 +1000", :local, [1951,5,6,2,59,59])
+ assert_time_constructor(tz, "2010-06-10 06:13:28 +0900", :local, [2010,6,10,6,13,28])
+ }
+ end
+
+ def test_canada_newfoundland
+ with_tz(tz="America/St_Johns") {
+ assert_time_constructor(tz, "2007-11-03 23:00:59 -0230", :new, [2007,11,3,23,0,59,:dst])
+ assert_time_constructor(tz, "2007-11-03 23:01:00 -0230", :new, [2007,11,3,23,1,0,:dst])
+ assert_time_constructor(tz, "2007-11-03 23:59:59 -0230", :new, [2007,11,3,23,59,59,:dst])
+ assert_time_constructor(tz, "2007-11-04 00:00:00 -0230", :new, [2007,11,4,0,0,0,:dst])
+ assert_time_constructor(tz, "2007-11-04 00:00:59 -0230", :new, [2007,11,4,0,0,59,:dst])
+ assert_time_constructor(tz, "2007-11-03 23:01:00 -0330", :new, [2007,11,3,23,1,0,:std])
+ assert_time_constructor(tz, "2007-11-03 23:59:59 -0330", :new, [2007,11,3,23,59,59,:std])
+ assert_time_constructor(tz, "2007-11-04 00:00:59 -0330", :new, [2007,11,4,0,0,59,:std])
+ assert_time_constructor(tz, "2007-11-04 00:01:00 -0330", :new, [2007,11,4,0,1,0,:std])
+ }
+ end
+
+ def test_europe_brussels
+ with_tz(tz="Europe/Brussels") {
+ assert_time_constructor(tz, "1916-04-30 23:59:59 +0100", :local, [1916,4,30,23,59,59])
+ assert_time_constructor(tz, "1916-05-01 01:00:00 +0200", :local, [1916,5,1], "[ruby-core:30672]")
+ assert_time_constructor(tz, "1916-05-01 01:59:59 +0200", :local, [1916,5,1,0,59,59])
+ assert_time_constructor(tz, "1916-05-01 01:00:00 +0200", :local, [1916,5,1,1,0,0])
+ assert_time_constructor(tz, "1916-05-01 01:59:59 +0200", :local, [1916,5,1,1,59,59])
+ }
+ end
+
+ def test_europe_berlin
+ with_tz(tz="Europe/Berlin") {
+ assert_time_constructor(tz, "2011-10-30 02:00:00 +0100", :local, [2011,10,30,2,0,0], "[ruby-core:67345] [Bug #10698]")
+ assert_time_constructor(tz, "2011-10-30 02:00:00 +0100", :local, [0,0,2,30,10,2011,nil,nil,false,nil])
+ assert_time_constructor(tz, "2011-10-30 02:00:00 +0200", :local, [0,0,2,30,10,2011,nil,nil,true,nil])
+ }
+ end
+
+ def test_europe_lisbon
+ with_tz("Europe/Lisbon") {
+ assert_equal("LMT", Time.new(-0x1_0000_0000_0000_0000).zone)
+ }
+ end if has_lisbon_tz
+
+ def test_europe_moscow
+ with_tz(tz="Europe/Moscow") {
+ assert_time_constructor(tz, "1992-03-29 00:00:00 +0400", :local, [1992,3,28,23,0,0])
+ assert_time_constructor(tz, "1992-03-29 00:59:59 +0400", :local, [1992,3,28,23,59,59])
+ }
+ end
+
+ def test_pacific_kiritimati
+ with_tz(tz="Pacific/Kiritimati") {
+ assert_time_constructor(tz, "1994-12-31 23:59:59 -1000", :local, [1994,12,31,23,59,59])
+ assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,1,0,0,0])
+ assert_time_constructor(tz, "1995-01-02 23:59:59 +1400", :local, [1995,1,1,23,59,59])
+ assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,2,0,0,0])
+ }
+ end
+
+ def test_right_utc
+ with_tz(tz="right/UTC") {
+ assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59])
+ assert_time_constructor(tz, "2008-12-31 23:59:60 UTC", :utc, [2008,12,31,23,59,60])
+ assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0])
+ assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0])
+ }
+ end if has_right_tz
+
+ def test_right_america_los_angeles
+ with_tz(tz="right/America/Los_Angeles") {
+ assert_time_constructor(tz, "2008-12-31 15:59:59 -0800", :local, [2008,12,31,15,59,59])
+ assert_time_constructor(tz, "2008-12-31 15:59:60 -0800", :local, [2008,12,31,15,59,60])
+ assert_time_constructor(tz, "2008-12-31 16:00:00 -0800", :local, [2008,12,31,16,0,0])
+ }
+ end if has_right_tz
+
+ MON2NUM = {
+ "Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6,
+ "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10, "Nov" => 11, "Dec" => 12
+ }
+
+ @testnum = 0
+ def self.gen_test_name(hint)
+ @testnum += 1
+ s = "test_gen_#{@testnum}"
+ s.sub(/gen_/) { "gen" + "_#{hint}_".gsub(/[^0-9A-Za-z]+/, '_') }
+ end
+
+ def self.parse_zdump_line(line)
+ return nil if /\A\#/ =~ line || /\A\s*\z/ =~ line
+ if /\A(\S+)\s+
+ \S+\s+(\S+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d+)\s+UTC?
+ \s+=\s+
+ \S+\s+(\S+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d+)\s+\S+
+ \s+isdst=\d+\s+gmtoff=(-?\d+)\n
+ \z/x !~ line
+ raise "unexpected zdump line: #{line.inspect}"
+ end
+ tz, u_mon, u_day, u_hour, u_min, u_sec, u_year,
+ l_mon, l_day, l_hour, l_min, l_sec, l_year, gmtoff = $~.captures
+ u_year = u_year.to_i
+ u_mon = MON2NUM[u_mon]
+ u_day = u_day.to_i
+ u_hour = u_hour.to_i
+ u_min = u_min.to_i
+ u_sec = u_sec.to_i
+ l_year = l_year.to_i
+ l_mon = MON2NUM[l_mon]
+ l_day = l_day.to_i
+ l_hour = l_hour.to_i
+ l_min = l_min.to_i
+ l_sec = l_sec.to_i
+ gmtoff = gmtoff.to_i
+ [tz,
+ [u_year, u_mon, u_day, u_hour, u_min, u_sec],
+ [l_year, l_mon, l_day, l_hour, l_min, l_sec],
+ gmtoff]
+ end
+
+ def self.gen_zdump_test(data)
+ sample = []
+ data.each_line {|line|
+ s = parse_zdump_line(line)
+ sample << s if s
+ }
+ sample.each {|tz, u, l, gmtoff|
+ expected_utc = "%04d-%02d-%02d %02d:%02d:%02d UTC" % u
+ expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)])
+ mesg_utc = "TZ=#{tz} Time.utc(#{u.map {|arg| arg.inspect }.join(', ')})"
+ mesg = "#{mesg_utc}.localtime"
+ define_method(gen_test_name(tz)) {
+ with_tz(tz) {
+ t = nil
+ assert_nothing_raised(mesg) { t = Time.utc(*u) }
+ assert_equal(expected_utc, time_to_s(t), mesg_utc)
+ assert_nothing_raised(mesg) { t.localtime }
+ assert_equal(expected, time_to_s(t), mesg)
+ assert_equal(gmtoff, t.gmtoff)
+ assert_equal(format_gmtoff(gmtoff), t.strftime("%z"))
+ assert_equal(format_gmtoff(gmtoff, true), t.strftime("%:z"))
+ assert_equal(format_gmtoff2(gmtoff), t.strftime("%::z"))
+ assert_equal(Encoding::US_ASCII, t.zone.encoding)
+ }
+ }
+ }
+
+ group_by(sample) {|tz, _, _, _| tz }.each {|tz, a|
+ a.each_with_index {|(_, _, l, gmtoff), i|
+ expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)])
+ monotonic_to_past = i == 0 || (a[i-1][2] <=> l) < 0
+ monotonic_to_future = i == a.length-1 || (l <=> a[i+1][2]) < 0
+ if monotonic_to_past && monotonic_to_future
+ define_method(gen_test_name(tz)) {
+ with_tz(tz) {
+ assert_time_constructor(tz, expected, :local, l)
+ assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, false, nil])
+ assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, true, nil])
+ assert_time_constructor(tz, expected, :new, l)
+ assert_time_constructor(tz, expected, :new, l+[:std])
+ assert_time_constructor(tz, expected, :new, l+[:dst])
+ }
+ }
+ elsif monotonic_to_past && !monotonic_to_future
+ define_method(gen_test_name(tz)) {
+ with_tz(tz) {
+ assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, true, nil])
+ assert_time_constructor(tz, expected, :new, l+[:dst])
+ }
+ }
+ elsif !monotonic_to_past && monotonic_to_future
+ define_method(gen_test_name(tz)) {
+ with_tz(tz) {
+ assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, false, nil])
+ assert_time_constructor(tz, expected, :new, l+[:std])
+ }
+ }
+ else
+ define_method(gen_test_name(tz)) {
+ flunk("time in reverse order: TZ=#{tz} #{expected}")
+ }
+ end
+ }
+ }
+ end
+
+ gen_zdump_test <<'End'
+America/Lima Sun Apr 1 03:59:59 1990 UTC = Sat Mar 31 23:59:59 1990 PEST isdst=1 gmtoff=-14400
+America/Lima Sun Apr 1 04:00:00 1990 UTC = Sat Mar 31 23:00:00 1990 PET isdst=0 gmtoff=-18000
+America/Lima Sat Jan 1 04:59:59 1994 UTC = Fri Dec 31 23:59:59 1993 PET isdst=0 gmtoff=-18000
+America/Lima Sat Jan 1 05:00:00 1994 UTC = Sat Jan 1 01:00:00 1994 PEST isdst=1 gmtoff=-14400
+America/Lima Fri Apr 1 03:59:59 1994 UTC = Thu Mar 31 23:59:59 1994 PEST isdst=1 gmtoff=-14400
+America/Lima Fri Apr 1 04:00:00 1994 UTC = Thu Mar 31 23:00:00 1994 PET isdst=0 gmtoff=-18000
+America/Los_Angeles Sun Apr 2 09:59:59 2006 UTC = Sun Apr 2 01:59:59 2006 PST isdst=0 gmtoff=-28800
+America/Los_Angeles Sun Apr 2 10:00:00 2006 UTC = Sun Apr 2 03:00:00 2006 PDT isdst=1 gmtoff=-25200
+America/Los_Angeles Sun Oct 29 08:59:59 2006 UTC = Sun Oct 29 01:59:59 2006 PDT isdst=1 gmtoff=-25200
+America/Los_Angeles Sun Oct 29 09:00:00 2006 UTC = Sun Oct 29 01:00:00 2006 PST isdst=0 gmtoff=-28800
+America/Los_Angeles Sun Mar 11 09:59:59 2007 UTC = Sun Mar 11 01:59:59 2007 PST isdst=0 gmtoff=-28800
+America/Los_Angeles Sun Mar 11 10:00:00 2007 UTC = Sun Mar 11 03:00:00 2007 PDT isdst=1 gmtoff=-25200
+America/Los_Angeles Sun Nov 4 08:59:59 2007 UTC = Sun Nov 4 01:59:59 2007 PDT isdst=1 gmtoff=-25200
+America/Los_Angeles Sun Nov 4 09:00:00 2007 UTC = Sun Nov 4 01:00:00 2007 PST isdst=0 gmtoff=-28800
+America/Managua Thu Sep 24 04:59:59 1992 UTC = Wed Sep 23 23:59:59 1992 EST isdst=0 gmtoff=-18000
+America/Managua Thu Sep 24 05:00:00 1992 UTC = Wed Sep 23 23:00:00 1992 CST isdst=0 gmtoff=-21600
+America/Managua Fri Jan 1 05:59:59 1993 UTC = Thu Dec 31 23:59:59 1992 CST isdst=0 gmtoff=-21600
+America/Managua Fri Jan 1 06:00:00 1993 UTC = Fri Jan 1 01:00:00 1993 EST isdst=0 gmtoff=-18000
+America/Managua Wed Jan 1 04:59:59 1997 UTC = Tue Dec 31 23:59:59 1996 EST isdst=0 gmtoff=-18000
+America/Managua Wed Jan 1 05:00:00 1997 UTC = Tue Dec 31 23:00:00 1996 CST isdst=0 gmtoff=-21600
+Asia/Singapore Sun Aug 8 16:30:00 1965 UTC = Mon Aug 9 00:00:00 1965 SGT isdst=0 gmtoff=27000
+Asia/Singapore Thu Dec 31 16:29:59 1981 UTC = Thu Dec 31 23:59:59 1981 SGT isdst=0 gmtoff=27000
+Asia/Singapore Thu Dec 31 16:30:00 1981 UTC = Fri Jan 1 00:30:00 1982 SGT isdst=0 gmtoff=28800
+Asia/Tokyo Sat May 5 16:59:59 1951 UTC = Sun May 6 01:59:59 1951 JST isdst=0 gmtoff=32400
+Asia/Tokyo Sat May 5 17:00:00 1951 UTC = Sun May 6 03:00:00 1951 JDT isdst=1 gmtoff=36000
+Asia/Tokyo Fri Sep 7 15:59:59 1951 UTC = Sat Sep 8 01:59:59 1951 JDT isdst=1 gmtoff=36000
+Asia/Tokyo Fri Sep 7 16:00:00 1951 UTC = Sat Sep 8 01:00:00 1951 JST isdst=0 gmtoff=32400
+America/St_Johns Sun Mar 11 03:30:59 2007 UTC = Sun Mar 11 00:00:59 2007 NST isdst=0 gmtoff=-12600
+America/St_Johns Sun Mar 11 03:31:00 2007 UTC = Sun Mar 11 01:01:00 2007 NDT isdst=1 gmtoff=-9000
+America/St_Johns Sun Nov 4 02:30:59 2007 UTC = Sun Nov 4 00:00:59 2007 NDT isdst=1 gmtoff=-9000
+America/St_Johns Sun Nov 4 02:31:00 2007 UTC = Sat Nov 3 23:01:00 2007 NST isdst=0 gmtoff=-12600
+Europe/Brussels Sun Apr 30 22:59:59 1916 UTC = Sun Apr 30 23:59:59 1916 CET isdst=0 gmtoff=3600
+Europe/Brussels Sun Apr 30 23:00:00 1916 UTC = Mon May 1 01:00:00 1916 CEST isdst=1 gmtoff=7200
+Europe/Brussels Sat Sep 30 22:59:59 1916 UTC = Sun Oct 1 00:59:59 1916 CEST isdst=1 gmtoff=7200
+Europe/Brussels Sat Sep 30 23:00:00 1916 UTC = Sun Oct 1 00:00:00 1916 CET isdst=0 gmtoff=3600
+Europe/London Sun Mar 16 01:59:59 1947 UTC = Sun Mar 16 01:59:59 1947 GMT isdst=0 gmtoff=0
+Europe/London Sun Mar 16 02:00:00 1947 UTC = Sun Mar 16 03:00:00 1947 BST isdst=1 gmtoff=3600
+Europe/London Sun Apr 13 00:59:59 1947 UTC = Sun Apr 13 01:59:59 1947 BST isdst=1 gmtoff=3600
+Europe/London Sun Apr 13 01:00:00 1947 UTC = Sun Apr 13 03:00:00 1947 BDST isdst=1 gmtoff=7200
+Europe/London Sun Aug 10 00:59:59 1947 UTC = Sun Aug 10 02:59:59 1947 BDST isdst=1 gmtoff=7200
+Europe/London Sun Aug 10 01:00:00 1947 UTC = Sun Aug 10 02:00:00 1947 BST isdst=1 gmtoff=3600
+Europe/London Sun Nov 2 01:59:59 1947 UTC = Sun Nov 2 02:59:59 1947 BST isdst=1 gmtoff=3600
+Europe/London Sun Nov 2 02:00:00 1947 UTC = Sun Nov 2 02:00:00 1947 GMT isdst=0 gmtoff=0
+Europe/Moscow Sat Jan 18 23:59:59 1992 UTC = Sun Jan 19 01:59:59 1992 MSK isdst=0 gmtoff=7200
+Europe/Moscow Sun Jan 19 00:00:00 1992 UTC = Sun Jan 19 03:00:00 1992 MSK isdst=0 gmtoff=10800
+Europe/Moscow Sat Mar 28 19:59:59 1992 UTC = Sat Mar 28 22:59:59 1992 MSK isdst=0 gmtoff=10800
+Europe/Moscow Sat Mar 28 20:00:00 1992 UTC = Sun Mar 29 00:00:00 1992 MSD isdst=1 gmtoff=14400
+Europe/Moscow Sat Sep 26 18:59:59 1992 UTC = Sat Sep 26 22:59:59 1992 MSD isdst=1 gmtoff=14400
+Europe/Moscow Sat Sep 26 19:00:00 1992 UTC = Sat Sep 26 22:00:00 1992 MSK isdst=0 gmtoff=10800
+Pacific/Kiritimati Sun Jan 1 09:59:59 1995 UTC = Sat Dec 31 23:59:59 1994 LINT isdst=0 gmtoff=-36000
+Pacific/Kiritimati Sun Jan 1 10:00:00 1995 UTC = Mon Jan 2 00:00:00 1995 LINT isdst=0 gmtoff=50400
+End
+ gen_zdump_test <<'End' if has_right_tz
+right/America/Los_Angeles Fri Jun 30 23:59:60 1972 UTC = Fri Jun 30 16:59:60 1972 PDT isdst=1 gmtoff=-25200
+right/America/Los_Angeles Wed Dec 31 23:59:60 2008 UTC = Wed Dec 31 15:59:60 2008 PST isdst=0 gmtoff=-28800
+#right/Asia/Tokyo Fri Jun 30 23:59:60 1972 UTC = Sat Jul 1 08:59:60 1972 JST isdst=0 gmtoff=32400
+#right/Asia/Tokyo Sat Dec 31 23:59:60 2005 UTC = Sun Jan 1 08:59:60 2006 JST isdst=0 gmtoff=32400
+right/Europe/Paris Fri Jun 30 23:59:60 1972 UTC = Sat Jul 1 00:59:60 1972 CET isdst=0 gmtoff=3600
+right/Europe/Paris Wed Dec 31 23:59:60 2008 UTC = Thu Jan 1 00:59:60 2009 CET isdst=0 gmtoff=3600
+End
+
+ def self.gen_variational_zdump_test(hint, data)
+ sample = []
+ data.each_line {|line|
+ s = parse_zdump_line(line)
+ sample << s if s
+ }
+
+ define_method(gen_test_name(hint)) {
+ results = []
+ sample.each {|tz, u, l, gmtoff|
+ expected_utc = "%04d-%02d-%02d %02d:%02d:%02d UTC" % u
+ expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)])
+ mesg_utc = "TZ=#{tz} Time.utc(#{u.map {|arg| arg.inspect }.join(', ')})"
+ mesg = "#{mesg_utc}.localtime"
+ with_tz(tz) {
+ t = nil
+ assert_nothing_raised(mesg) { t = Time.utc(*u) }
+ assert_equal(expected_utc, time_to_s(t), mesg_utc)
+ assert_nothing_raised(mesg) { t.localtime }
+
+ results << [
+ expected == time_to_s(t),
+ gmtoff == t.gmtoff,
+ format_gmtoff(gmtoff) == t.strftime("%z"),
+ format_gmtoff(gmtoff, true) == t.strftime("%:z"),
+ format_gmtoff2(gmtoff) == t.strftime("%::z")
+ ]
+ }
+ }
+ assert_includes(results, [true, true, true, true, true])
+ }
+ end
+
+ # tzdata-2014g fixed the offset for lisbon from -0:36:32 to -0:36:45.
+ # [ruby-core:65058] [Bug #10245]
+ gen_variational_zdump_test "lisbon", <<'End' if has_lisbon_tz
+Europe/Lisbon Mon Jan 1 00:36:31 1912 UTC = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2192
+Europe/Lisbon Mon Jan 1 00:36:44 1912 UT = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2205
+End
+end
diff --git a/jni/ruby/test/ruby/test_trace.rb b/jni/ruby/test/ruby/test_trace.rb
new file mode 100644
index 0000000..775c458
--- /dev/null
+++ b/jni/ruby/test/ruby/test_trace.rb
@@ -0,0 +1,61 @@
+require 'test/unit'
+
+class TestTrace < Test::Unit::TestCase
+ def test_trace
+ $x = 1234
+ $y = 0
+ trace_var :$x, proc{$y = $x}
+ $x = 40414
+ assert_equal($x, $y)
+
+ untrace_var :$x
+ $x = 19660208
+ assert_not_equal($x, $y)
+
+ trace_var :$x, proc{$x *= 2}
+ $x = 5
+ assert_equal(10, $x)
+
+ untrace_var :$x
+ end
+
+ def test_trace_tainted_proc
+ $x = 1234
+ s = proc { $y = :foo }
+ trace_var :$x, s
+ s.taint
+ $x = 42
+ assert_equal(:foo, $y)
+ ensure
+ untrace_var :$x
+ end
+
+ def test_trace_proc_that_raises_exception
+ $x = 1234
+ trace_var :$x, proc { raise }
+ assert_raise(RuntimeError) { $x = 42 }
+ ensure
+ untrace_var :$x
+ end
+
+ def test_trace_string
+ $x = 1234
+ trace_var :$x, "$y = :bar"
+ $x = 42
+ assert_equal(:bar, $y)
+ ensure
+ untrace_var :$x
+ end
+
+ def test_trace_break
+ bug2722 = '[ruby-core:31783]'
+ a = Object.new.extend(Enumerable)
+ def a.each
+ yield
+ end
+ assert(Thread.start {
+ Thread.current.add_trace_func(proc{})
+ a.any? {true}
+ }.value, bug2722)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_transcode.rb b/jni/ruby/test/ruby/test_transcode.rb
new file mode 100644
index 0000000..4bade11
--- /dev/null
+++ b/jni/ruby/test/ruby/test_transcode.rb
@@ -0,0 +1,2121 @@
+# encoding: ASCII-8BIT # make sure this runs in binary mode
+# some of the comments are in UTF-8
+
+require 'test/unit'
+
+class TestTranscode < Test::Unit::TestCase
+ def test_errors
+ assert_raise(Encoding::ConverterNotFoundError) { 'abc'.encode('foo', 'bar') }
+ assert_raise(Encoding::ConverterNotFoundError) { 'abc'.encode!('foo', 'bar') }
+ assert_raise(Encoding::ConverterNotFoundError) { 'abc'.force_encoding('utf-8').encode('foo') }
+ assert_raise(Encoding::ConverterNotFoundError) { 'abc'.force_encoding('utf-8').encode!('foo') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x80".encode('utf-8','ASCII-8BIT') }
+ assert_raise(Encoding::InvalidByteSequenceError) { "\x80".encode('utf-8','US-ASCII') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA5".encode('utf-8','iso-8859-3') }
+ assert_raise(RuntimeError) { 'hello'.freeze.encode!('iso-8859-1') }
+ assert_raise(RuntimeError) { '\u3053\u3093\u306b\u3061\u306f'.freeze.encode!('iso-8859-1') } # こんにちは
+ end
+
+ def test_arguments
+ assert_equal('abc', 'abc'.force_encoding('utf-8').encode('iso-8859-1'))
+ # check that encoding is kept when no conversion is done
+ assert_equal('abc'.force_encoding('Shift_JIS'), 'abc'.force_encoding('Shift_JIS').encode('Shift_JIS'))
+ assert_equal('abc'.force_encoding('Shift_JIS'), 'abc'.force_encoding('Shift_JIS').encode!('Shift_JIS'))
+ # assert that encoding is correctly set
+ assert_equal("D\u00FCrst".encoding, "D\xFCrst".force_encoding('iso-8859-1').encode('utf-8').encoding)
+ # check that Encoding can be used as parameter
+ assert_equal("D\u00FCrst", "D\xFCrst".encode('utf-8', Encoding.find('ISO-8859-1')))
+ assert_equal("D\u00FCrst", "D\xFCrst".encode(Encoding.find('utf-8'), 'ISO-8859-1'))
+ assert_equal("D\u00FCrst", "D\xFCrst".encode(Encoding.find('utf-8'), Encoding.find('ISO-8859-1')))
+ end
+
+ def test_noargument
+ EnvUtil.with_default_internal(nil) do
+ assert_equal("\u3042".encode, "\u3042")
+ assert_equal("\xE3\x81\x82\x81".force_encoding("utf-8").encode,
+ "\xE3\x81\x82\x81".force_encoding("utf-8"))
+ end
+ EnvUtil.with_default_internal('EUC-JP') do
+ assert_equal("\u3042".encode, "\xA4\xA2".force_encoding('EUC-JP'))
+ assert_equal("\xE3\x81\x82\x81".force_encoding("utf-8").encode,
+ "\xA4\xA2?".force_encoding('EUC-JP'))
+ end
+ end
+
+ def test_length
+ assert_equal("\u20AC"*20, ("\xA4"*20).encode('utf-8', 'iso-8859-15'))
+ assert_equal("\u20AC"*20, ("\xA4"*20).encode!('utf-8', 'iso-8859-15'))
+ assert_equal("\u20AC"*2000, ("\xA4"*2000).encode('utf-8', 'iso-8859-15'))
+ assert_equal("\u20AC"*2000, ("\xA4"*2000).encode!('utf-8', 'iso-8859-15'))
+ assert_equal("\u20AC"*200000, ("\xA4"*200000).encode('utf-8', 'iso-8859-15'))
+ assert_equal("\u20AC"*200000, ("\xA4"*200000).encode!('utf-8', 'iso-8859-15'))
+ end
+
+ def check_both_ways(utf8, raw, encoding)
+ assert_equal(utf8.force_encoding('utf-8'), raw.encode('utf-8', encoding),utf8.dump+raw.dump)
+ assert_equal(raw.force_encoding(encoding), utf8.encode(encoding, 'utf-8'))
+ end
+
+ def check_both_ways2(str1, enc1, str2, enc2)
+ assert_equal(str1.force_encoding(enc1), str2.encode(enc1, enc2))
+ assert_equal(str2.force_encoding(enc2), str1.encode(enc2, enc1))
+ end
+
+ def test_encoding_of_ascii_originating_from_binary
+ binary_string = [0x82, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+ 0x61, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x6c, 0x6f,
+ 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67]
+ class << binary_string
+ # create a copy on write substring that contains
+ # just the ascii characters (i.e. this is...), in JRuby
+ # the underlying string have the same buffer backing
+ # it up, but the offset of the string will be 1 instead
+ # of 0.
+ def make_cow_substring
+ pack('C27').slice(1, 26)
+ end
+ end
+
+ ascii_string = binary_string.make_cow_substring
+ assert_equal("this is a very long string", ascii_string)
+ assert_equal(Encoding::ASCII_8BIT, ascii_string.encoding)
+ utf8_string = nil
+ assert_nothing_raised("JRUBY-6764") do
+ utf8_string = ascii_string.encode(Encoding::UTF_8)
+ end
+ assert_equal("this is a very long string", utf8_string)
+ assert_equal(Encoding::UTF_8, utf8_string.encoding)
+ end
+
+ def test_encodings
+ check_both_ways("\u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D",
+ "\x82\xdc\x82\xc2\x82\xe0\x82\xc6 \x82\xe4\x82\xab\x82\xd0\x82\xeb", 'shift_jis') # まつもと ゆきひろ
+ check_both_ways("\u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D",
+ "\xa4\xde\xa4\xc4\xa4\xe2\xa4\xc8 \xa4\xe6\xa4\xad\xa4\xd2\xa4\xed", 'euc-jp')
+ check_both_ways("\u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D",
+ "\xa4\xde\xa4\xc4\xa4\xe2\xa4\xc8 \xa4\xe6\xa4\xad\xa4\xd2\xa4\xed", 'euc-jis-2004')
+ check_both_ways("\u677E\u672C\u884C\u5F18", "\x8f\xbc\x96\x7b\x8d\x73\x8d\x4f", 'shift_jis') # 松本行弘
+ check_both_ways("\u677E\u672C\u884C\u5F18", "\xbe\xbe\xcb\xdc\xb9\xd4\xb9\xb0", 'euc-jp')
+ check_both_ways("\u677E\u672C\u884C\u5F18", "\xbe\xbe\xcb\xdc\xb9\xd4\xb9\xb0", 'euc-jis-2004')
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-1') # Dürst
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-2')
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-3')
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-4')
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-9')
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-10')
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-13')
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-14')
+ check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-15')
+ check_both_ways("r\u00E9sum\u00E9", "r\xE9sum\xE9", 'iso-8859-1') # résumé
+ check_both_ways("\u0065\u006C\u0151\u00ED\u0072\u00E1\u0073", "el\xF5\xEDr\xE1s", 'iso-8859-2') # előírás
+ check_both_ways("\u043F\u0435\u0440\u0435\u0432\u043E\u0434",
+ "\xDF\xD5\xE0\xD5\xD2\xDE\xD4", 'iso-8859-5') # перевод
+ check_both_ways("\u0643\u062A\u0628", "\xE3\xCA\xC8", 'iso-8859-6') # كتب
+ check_both_ways("\u65E5\u8A18", "\x93\xFA\x8BL", 'shift_jis') # 日記
+ check_both_ways("\u65E5\u8A18", "\xC6\xFC\xB5\xAD", 'euc-jp')
+ check_both_ways("\u65E5\u8A18", "\xC6\xFC\xB5\xAD", 'euc-jis-2004')
+ check_both_ways("\uC560\uC778\uAD6C\uD568\u0020\u6734\uC9C0\uC778",
+ "\xBE\xD6\xC0\xCE\xB1\xB8\xC7\xD4\x20\xDA\xD3\xC1\xF6\xC0\xCE", 'euc-kr') # 애인구함 朴지인
+ check_both_ways("\uC544\uD58F\uD58F\u0020\uB620\uBC29\uD6BD\uB2D8\u0020\uC0AC\uB791\uD716",
+ "\xBE\xC6\xC1\x64\xC1\x64\x20\x8C\x63\xB9\xE6\xC4\x4F\xB4\xD4\x20\xBB\xE7\xB6\xFB\xC5\x42", 'cp949') # 아햏햏 똠방횽님 사랑휖
+ assert_equal(Encoding::ISO_8859_1, "D\xFCrst".force_encoding('iso-8859-2').encode('iso-8859-1', 'iso-8859-1').encoding)
+ end
+
+ def test_twostep
+ assert_equal("D\xFCrst".force_encoding('iso-8859-2'), "D\xFCrst".encode('iso-8859-2', 'iso-8859-1'))
+ end
+
+ def test_ascii_range
+ encodings = [
+ 'US-ASCII', 'ASCII-8BIT',
+ 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3',
+ 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6',
+ 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9',
+ 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-13',
+ 'ISO-8859-14', 'ISO-8859-15',
+ 'EUC-JP', 'SHIFT_JIS', 'EUC-KR'
+ ]
+ all_ascii = (0..127).to_a.pack 'C*'
+ encodings.each do |enc|
+ test_start = all_ascii
+ assert_equal(test_start, test_start.encode('UTF-8',enc).encode(enc).force_encoding('ASCII-8BIT'))
+ end
+ end
+
+ def test_all_bytes
+ encodings_8859 = [
+ 'ISO-8859-1', 'ISO-8859-2',
+ #'ISO-8859-3', # not all bytes used
+ 'ISO-8859-4', 'ISO-8859-5',
+ #'ISO-8859-6', # not all bytes used
+ #'ISO-8859-7', # not all bytes used
+ #'ISO-8859-8', # not all bytes used
+ 'ISO-8859-9', 'ISO-8859-10',
+ #'ISO-8859-11', # not all bytes used
+ #'ISO-8859-12', # not available
+ 'ISO-8859-13','ISO-8859-14','ISO-8859-15',
+ #'ISO-8859-16', # not available
+ ]
+ all_bytes = (0..255).to_a.pack 'C*'
+ encodings_8859.each do |enc|
+ test_start = all_bytes
+ assert_equal(test_start, test_start.encode('UTF-8',enc).encode(enc).force_encoding('ASCII-8BIT'))
+ end
+ end
+
+ def test_windows_874
+ check_both_ways("\u20AC", "\x80", 'windows-874') # €
+ assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-874') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x84".encode("utf-8", 'windows-874') }
+ check_both_ways("\u2026", "\x85", 'windows-874') # …
+ assert_raise(Encoding::UndefinedConversionError) { "\x86".encode("utf-8", 'windows-874') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-874') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-874') }
+ check_both_ways("\u2018", "\x91", 'windows-874') # ‘
+ check_both_ways("\u2014", "\x97", 'windows-874') # —
+ assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-874') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'windows-874') }
+ check_both_ways("\u00A0", "\xA0", 'windows-874') # non-breaking space
+ check_both_ways("\u0E0F", "\xAF", 'windows-874') # ฏ
+ check_both_ways("\u0E10", "\xB0", 'windows-874') # ฐ
+ check_both_ways("\u0E1F", "\xBF", 'windows-874') # ฟ
+ check_both_ways("\u0E20", "\xC0", 'windows-874') # ภ
+ check_both_ways("\u0E2F", "\xCF", 'windows-874') # ฯ
+ check_both_ways("\u0E30", "\xD0", 'windows-874') # ะ
+ check_both_ways("\u0E3A", "\xDA", 'windows-874') # ฺ
+ assert_raise(Encoding::UndefinedConversionError) { "\xDB".encode("utf-8", 'windows-874') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xDE".encode("utf-8", 'windows-874') }
+ check_both_ways("\u0E3F", "\xDF", 'windows-874') # ฿
+ check_both_ways("\u0E40", "\xE0", 'windows-874') # เ
+ check_both_ways("\u0E4F", "\xEF", 'windows-874') # ๏
+ check_both_ways("\u0E50", "\xF0", 'windows-874') # ๐
+ check_both_ways("\u0E5B", "\xFB", 'windows-874') # ๛
+ assert_raise(Encoding::UndefinedConversionError) { "\xFC".encode("utf-8", 'windows-874') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'windows-874') }
+ end
+
+ def test_windows_1250
+ check_both_ways("\u20AC", "\x80", 'windows-1250') # €
+ assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1250') }
+ check_both_ways("\u201A", "\x82", 'windows-1250') # ‚
+ assert_raise(Encoding::UndefinedConversionError) { "\x83".encode("utf-8", 'windows-1250') }
+ check_both_ways("\u201E", "\x84", 'windows-1250') # „
+ check_both_ways("\u2021", "\x87", 'windows-1250') # ‡
+ assert_raise(Encoding::UndefinedConversionError) { "\x88".encode("utf-8", 'windows-1250') }
+ check_both_ways("\u2030", "\x89", 'windows-1250') # ‰
+ check_both_ways("\u0179", "\x8F", 'windows-1250') # Ź
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1250') }
+ check_both_ways("\u2018", "\x91", 'windows-1250') # ‘
+ check_both_ways("\u2014", "\x97", 'windows-1250') # —
+ assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-1250') }
+ check_both_ways("\u2122", "\x99", 'windows-1250') # ™
+ check_both_ways("\u00A0", "\xA0", 'windows-1250') # non-breaking space
+ check_both_ways("\u017B", "\xAF", 'windows-1250') # Ż
+ check_both_ways("\u00B0", "\xB0", 'windows-1250') # °
+ check_both_ways("\u017C", "\xBF", 'windows-1250') # ż
+ check_both_ways("\u0154", "\xC0", 'windows-1250') # Ŕ
+ check_both_ways("\u010E", "\xCF", 'windows-1250') # Ď
+ check_both_ways("\u0110", "\xD0", 'windows-1250') # Đ
+ check_both_ways("\u00DF", "\xDF", 'windows-1250') # ß
+ check_both_ways("\u0155", "\xE0", 'windows-1250') # ŕ
+ check_both_ways("\u010F", "\xEF", 'windows-1250') # ď
+ check_both_ways("\u0111", "\xF0", 'windows-1250') # đ
+ check_both_ways("\u02D9", "\xFF", 'windows-1250') # ˙
+ end
+
+ def test_windows_1251
+ check_both_ways("\u0402", "\x80", 'windows-1251') # Ђ
+ check_both_ways("\u20AC", "\x88", 'windows-1251') # €
+ check_both_ways("\u040F", "\x8F", 'windows-1251') # Џ
+ check_both_ways("\u0452", "\x90", 'windows-1251') # ђ
+ assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-1251') }
+ check_both_ways("\u045F", "\x9F", 'windows-1251') # џ
+ check_both_ways("\u00A0", "\xA0", 'windows-1251') # non-breaking space
+ check_both_ways("\u0407", "\xAF", 'windows-1251') # Ї
+ check_both_ways("\u00B0", "\xB0", 'windows-1251') # °
+ check_both_ways("\u0457", "\xBF", 'windows-1251') # ї
+ check_both_ways("\u0410", "\xC0", 'windows-1251') # А
+ check_both_ways("\u041F", "\xCF", 'windows-1251') # П
+ check_both_ways("\u0420", "\xD0", 'windows-1251') # Р
+ check_both_ways("\u042F", "\xDF", 'windows-1251') # Я
+ check_both_ways("\u0430", "\xE0", 'windows-1251') # а
+ check_both_ways("\u043F", "\xEF", 'windows-1251') # п
+ check_both_ways("\u0440", "\xF0", 'windows-1251') # р
+ check_both_ways("\u044F", "\xFF", 'windows-1251') # я
+ end
+
+ def test_windows_1252
+ check_both_ways("\u20AC", "\x80", 'windows-1252') # €
+ assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1252') }
+ check_both_ways("\u201A", "\x82", 'windows-1252') # ‚
+ check_both_ways("\u0152", "\x8C", 'windows-1252') # >Œ
+ assert_raise(Encoding::UndefinedConversionError) { "\x8D".encode("utf-8", 'windows-1252') }
+ check_both_ways("\u017D", "\x8E", 'windows-1252') # Ž
+ assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-1252') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1252') }
+ check_both_ways("\u2018", "\x91", 'windows-1252') #‘
+ check_both_ways("\u0153", "\x9C", 'windows-1252') # œ
+ assert_raise(Encoding::UndefinedConversionError) { "\x9D".encode("utf-8", 'windows-1252') }
+ check_both_ways("\u017E", "\x9E", 'windows-1252') # ž
+ check_both_ways("\u00A0", "\xA0", 'windows-1252') # non-breaking space
+ check_both_ways("\u00AF", "\xAF", 'windows-1252') # ¯
+ check_both_ways("\u00B0", "\xB0", 'windows-1252') # °
+ check_both_ways("\u00BF", "\xBF", 'windows-1252') # ¿
+ check_both_ways("\u00C0", "\xC0", 'windows-1252') # À
+ check_both_ways("\u00CF", "\xCF", 'windows-1252') # Ï
+ check_both_ways("\u00D0", "\xD0", 'windows-1252') # Ð
+ check_both_ways("\u00DF", "\xDF", 'windows-1252') # ß
+ check_both_ways("\u00E0", "\xE0", 'windows-1252') # à
+ check_both_ways("\u00EF", "\xEF", 'windows-1252') # ï
+ check_both_ways("\u00F0", "\xF0", 'windows-1252') # ð
+ check_both_ways("\u00FF", "\xFF", 'windows-1252') # ÿ
+ end
+
+ def test_windows_1253
+ check_both_ways("\u20AC", "\x80", 'windows-1253') # €
+ assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1253') }
+ check_both_ways("\u201A", "\x82", 'windows-1253') # ‚
+ check_both_ways("\u2021", "\x87", 'windows-1253') # ‡
+ assert_raise(Encoding::UndefinedConversionError) { "\x88".encode("utf-8", 'windows-1253') }
+ check_both_ways("\u2030", "\x89", 'windows-1253') # ‰
+ assert_raise(Encoding::UndefinedConversionError) { "\x8A".encode("utf-8", 'windows-1253') }
+ check_both_ways("\u2039", "\x8B", 'windows-1253') # ‹
+ assert_raise(Encoding::UndefinedConversionError) { "\x8C".encode("utf-8", 'windows-1253') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-1253') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1253') }
+ check_both_ways("\u2018", "\x91", 'windows-1253') # ‘
+ check_both_ways("\u2014", "\x97", 'windows-1253') # —
+ assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-1253') }
+ check_both_ways("\u2122", "\x99", 'windows-1253') # ™
+ assert_raise(Encoding::UndefinedConversionError) { "\x9A".encode("utf-8", 'windows-1253') }
+ check_both_ways("\u203A", "\x9B", 'windows-1253') # ›
+ assert_raise(Encoding::UndefinedConversionError) { "\x9C".encode("utf-8", 'windows-1253') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'windows-1253') }
+ check_both_ways("\u00A0", "\xA0", 'windows-1253') # non-breaking space
+ check_both_ways("\u2015", "\xAF", 'windows-1253') # ―
+ check_both_ways("\u00B0", "\xB0", 'windows-1253') # °
+ check_both_ways("\u038F", "\xBF", 'windows-1253') # Ώ
+ check_both_ways("\u0390", "\xC0", 'windows-1253') # ΐ
+ check_both_ways("\u039F", "\xCF", 'windows-1253') # Ο
+ check_both_ways("\u03A0", "\xD0", 'windows-1253') # Π
+ check_both_ways("\u03A1", "\xD1", 'windows-1253') # Ρ
+ assert_raise(Encoding::UndefinedConversionError) { "\xD2".encode("utf-8", 'windows-1253') }
+ check_both_ways("\u03A3", "\xD3", 'windows-1253') # Σ
+ check_both_ways("\u03AF", "\xDF", 'windows-1253') # ί
+ check_both_ways("\u03B0", "\xE0", 'windows-1253') # ΰ
+ check_both_ways("\u03BF", "\xEF", 'windows-1253') # ο
+ check_both_ways("\u03C0", "\xF0", 'windows-1253') # π
+ check_both_ways("\u03CE", "\xFE", 'windows-1253') # ώ
+ assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'windows-1253') }
+ end
+
+ def test_windows_1254
+ check_both_ways("\u20AC", "\x80", 'windows-1254') # €
+ assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1254') }
+ check_both_ways("\u201A", "\x82", 'windows-1254') # ‚
+ check_both_ways("\u0152", "\x8C", 'windows-1254') # Œ
+ assert_raise(Encoding::UndefinedConversionError) { "\x8D".encode("utf-8", 'windows-1254') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-1254') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1254') }
+ check_both_ways("\u2018", "\x91", 'windows-1254') # ‘
+ check_both_ways("\u0153", "\x9C", 'windows-1254') # œ
+ assert_raise(Encoding::UndefinedConversionError) { "\x9D".encode("utf-8", 'windows-1254') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x9E".encode("utf-8", 'windows-1254') }
+ check_both_ways("\u0178", "\x9F", 'windows-1254') # Ÿ
+ check_both_ways("\u00A0", "\xA0", 'windows-1254') # non-breaking space
+ check_both_ways("\u00AF", "\xAF", 'windows-1254') # ¯
+ check_both_ways("\u00B0", "\xB0", 'windows-1254') # °
+ check_both_ways("\u00BF", "\xBF", 'windows-1254') # ¿
+ check_both_ways("\u00C0", "\xC0", 'windows-1254') # À
+ check_both_ways("\u00CF", "\xCF", 'windows-1254') # Ï
+ check_both_ways("\u011E", "\xD0", 'windows-1254') # Ğ
+ check_both_ways("\u00DF", "\xDF", 'windows-1254') # ß
+ check_both_ways("\u00E0", "\xE0", 'windows-1254') # à
+ check_both_ways("\u00EF", "\xEF", 'windows-1254') # ï
+ check_both_ways("\u011F", "\xF0", 'windows-1254') # ğ
+ check_both_ways("\u00FF", "\xFF", 'windows-1254') # ÿ
+ end
+
+ def test_windows_1255
+ check_both_ways("\u20AC", "\x80", 'windows-1255') # €
+ assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u201A", "\x82", 'windows-1255') # ‚
+ check_both_ways("\u2030", "\x89", 'windows-1255') # ‰
+ assert_raise(Encoding::UndefinedConversionError) { "\x8A".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u2039", "\x8B", 'windows-1255') # ‹
+ assert_raise(Encoding::UndefinedConversionError) { "\x8C".encode("utf-8", 'windows-1255') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'windows-1255') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u2018", "\x91", 'windows-1255') # ‘
+ check_both_ways("\u2122", "\x99", 'windows-1255') # ™
+ assert_raise(Encoding::UndefinedConversionError) { "\x9A".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u203A", "\x9B", 'windows-1255') # ›
+ assert_raise(Encoding::UndefinedConversionError) { "\x9C".encode("utf-8", 'windows-1255') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u00A0", "\xA0", 'windows-1255') # non-breaking space
+ check_both_ways("\u00A1", "\xA1", 'windows-1255') # ¡
+ check_both_ways("\u00D7", "\xAA", 'windows-1255') # ×
+ check_both_ways("\u00AF", "\xAF", 'windows-1255') # ¯
+ check_both_ways("\u00B0", "\xB0", 'windows-1255') # °
+ check_both_ways("\u00B8", "\xB8", 'windows-1255') # ¸
+ check_both_ways("\u00F7", "\xBA", 'windows-1255') # ÷
+ check_both_ways("\u00BF", "\xBF", 'windows-1255') # ¿
+ check_both_ways("\u05B0", "\xC0", 'windows-1255') # ְ
+ check_both_ways("\u05B9", "\xC9", 'windows-1255') # ֹ
+ assert_raise(Encoding::UndefinedConversionError) { "\xCA".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u05BB", "\xCB", 'windows-1255') # ֻ
+ check_both_ways("\u05BF", "\xCF", 'windows-1255') # ֿ
+ check_both_ways("\u05C0", "\xD0", 'windows-1255') # ׀
+ check_both_ways("\u05F3", "\xD7", 'windows-1255') # ׳
+ check_both_ways("\u05F4", "\xD8", 'windows-1255') # ״
+ assert_raise(Encoding::UndefinedConversionError) { "\xD9".encode("utf-8", 'windows-1255') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xDF".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u05D0", "\xE0", 'windows-1255') # א
+ check_both_ways("\u05DF", "\xEF", 'windows-1255') # ן
+ check_both_ways("\u05E0", "\xF0", 'windows-1255') # נ
+ check_both_ways("\u05EA", "\xFA", 'windows-1255') # ת
+ assert_raise(Encoding::UndefinedConversionError) { "\xFB".encode("utf-8", 'windows-1255') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xFC".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u200E", "\xFD", 'windows-1255') # left-to-right mark
+ check_both_ways("\u200F", "\xFE", 'windows-1255') # right-to-left mark
+ assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'windows-1255') }
+ end
+
+ def test_windows_1256
+ check_both_ways("\u20AC", "\x80", 'windows-1256') # €
+ check_both_ways("\u0679", "\x8A", 'windows-1256') # ٹ
+ check_both_ways("\u0688", "\x8F", 'windows-1256') # ڈ
+ check_both_ways("\u06AF", "\x90", 'windows-1256') # گ
+ check_both_ways("\u06A9", "\x98", 'windows-1256') # ک
+ check_both_ways("\u0691", "\x9A", 'windows-1256') # ڑ
+ check_both_ways("\u06BA", "\x9F", 'windows-1256') # ں
+ check_both_ways("\u00A0", "\xA0", 'windows-1256') # non-breaking space
+ check_both_ways("\u06BE", "\xAA", 'windows-1256') # ھ
+ check_both_ways("\u00AF", "\xAF", 'windows-1256') # ¯
+ check_both_ways("\u00B0", "\xB0", 'windows-1256') # °
+ check_both_ways("\u061F", "\xBF", 'windows-1256') # ؟
+ check_both_ways("\u06C1", "\xC0", 'windows-1256') # ہ
+ check_both_ways("\u062F", "\xCF", 'windows-1256') # د
+ check_both_ways("\u0630", "\xD0", 'windows-1256') # ذ
+ check_both_ways("\u0643", "\xDF", 'windows-1256') # ك
+ check_both_ways("\u00E0", "\xE0", 'windows-1256') # à
+ check_both_ways("\u00EF", "\xEF", 'windows-1256') # ï
+ check_both_ways("\u064B", "\xF0", 'windows-1256') # ًً
+ check_both_ways("\u06D2", "\xFF", 'windows-1256') # ے
+ end
+
+ def test_windows_1257
+ check_both_ways("\u20AC", "\x80", 'windows-1257') # €
+ assert_raise(Encoding::UndefinedConversionError) { "\x81".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u201A", "\x82", 'windows-1257') # ‚
+ assert_raise(Encoding::UndefinedConversionError) { "\x83".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u201E", "\x84", 'windows-1257') # „
+ check_both_ways("\u2021", "\x87", 'windows-1257') # ‡
+ assert_raise(Encoding::UndefinedConversionError) { "\x88".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u2030", "\x89", 'windows-1257') # ‰
+ assert_raise(Encoding::UndefinedConversionError) { "\x8A".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u2039", "\x8B", 'windows-1257') # ‹
+ assert_raise(Encoding::UndefinedConversionError) { "\x8C".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u00A8", "\x8D", 'windows-1257') # ¨
+ check_both_ways("\u02C7", "\x8E", 'windows-1257') # ˇ
+ check_both_ways("\u00B8", "\x8F", 'windows-1257') # ¸
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u2018", "\x91", 'windows-1257') # ‘
+ check_both_ways("\u2014", "\x97", 'windows-1257') # —
+ assert_raise(Encoding::UndefinedConversionError) { "\x98".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u2122", "\x99", 'windows-1257') # ™
+ assert_raise(Encoding::UndefinedConversionError) { "\x9A".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u203A", "\x9B", 'windows-1257') # ›
+ assert_raise(Encoding::UndefinedConversionError) { "\x9C".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u00AF", "\x9D", 'windows-1257') # ¯
+ check_both_ways("\u02DB", "\x9E", 'windows-1257') # ˛
+ assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u00A0", "\xA0", 'windows-1257') # non-breaking space
+ assert_raise(Encoding::UndefinedConversionError) { "\xA1".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u00A2", "\xA2", 'windows-1257') # ¢
+ check_both_ways("\u00A4", "\xA4", 'windows-1257') # ¤
+ assert_raise(Encoding::UndefinedConversionError) { "\xA5".encode("utf-8", 'windows-1257') }
+ check_both_ways("\u00A6", "\xA6", 'windows-1257') # ¦
+ check_both_ways("\u00C6", "\xAF", 'windows-1257') # Æ
+ check_both_ways("\u00B0", "\xB0", 'windows-1257') # °
+ check_both_ways("\u00E6", "\xBF", 'windows-1257') # æ
+ check_both_ways("\u0104", "\xC0", 'windows-1257') # Ą
+ check_both_ways("\u013B", "\xCF", 'windows-1257') # Ļ
+ check_both_ways("\u0160", "\xD0", 'windows-1257') # Š
+ check_both_ways("\u00DF", "\xDF", 'windows-1257') # ß
+ check_both_ways("\u0105", "\xE0", 'windows-1257') # ą
+ check_both_ways("\u013C", "\xEF", 'windows-1257') # ļ
+ check_both_ways("\u0161", "\xF0", 'windows-1257') # š
+ check_both_ways("\u02D9", "\xFF", 'windows-1257') # ˙
+ end
+
+ def test_IBM437
+ check_both_ways("\u00C7", "\x80", 'IBM437') # Ç
+ check_both_ways("\u00C5", "\x8F", 'IBM437') # Å
+ check_both_ways("\u00C9", "\x90", 'IBM437') # É
+ check_both_ways("\u0192", "\x9F", 'IBM437') # ƒ
+ check_both_ways("\u00E1", "\xA0", 'IBM437') # á
+ check_both_ways("\u00BB", "\xAF", 'IBM437') # »
+ check_both_ways("\u2591", "\xB0", 'IBM437') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM437') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM437') # └
+ check_both_ways("\u2567", "\xCF", 'IBM437') # ╧
+ check_both_ways("\u2568", "\xD0", 'IBM437') # ╨
+ check_both_ways("\u2580", "\xDF", 'IBM437') # ▀
+ check_both_ways("\u03B1", "\xE0", 'IBM437') # α
+ check_both_ways("\u2229", "\xEF", 'IBM437') # ∩
+ check_both_ways("\u2261", "\xF0", 'IBM437') # ≡
+ check_both_ways("\u00A0", "\xFF", 'IBM437') # non-breaking space
+ end
+
+ def test_IBM775
+ check_both_ways("\u0106", "\x80", 'IBM775') # Ć
+ check_both_ways("\u00C5", "\x8F", 'IBM775') # Å
+ check_both_ways("\u00C9", "\x90", 'IBM775') # É
+ check_both_ways("\u00A4", "\x9F", 'IBM775') # ¤
+ check_both_ways("\u0100", "\xA0", 'IBM775') # Ā
+ check_both_ways("\u00BB", "\xAF", 'IBM775') # »
+ check_both_ways("\u2591", "\xB0", 'IBM775') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM775') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM775') # └
+ check_both_ways("\u017D", "\xCF", 'IBM775') # Ž
+ check_both_ways("\u0105", "\xD0", 'IBM775') # ą
+ check_both_ways("\u2580", "\xDF", 'IBM775') # ▀
+ check_both_ways("\u00D3", "\xE0", 'IBM775') # Ó
+ check_both_ways("\u2019", "\xEF", 'IBM775') # ’
+ check_both_ways("\u00AD", "\xF0", 'IBM775') # osft hyphen
+ check_both_ways("\u00A0", "\xFF", 'IBM775') # non-breaking space
+ end
+
+ def test_IBM852
+ check_both_ways("\u00C7", "\x80", 'IBM852') # Ç
+ check_both_ways("\u0106", "\x8F", 'IBM852') # Ć
+ check_both_ways("\u00C9", "\x90", 'IBM852') # É
+ check_both_ways("\u010D", "\x9F", 'IBM852') # č
+ check_both_ways("\u00E1", "\xA0", 'IBM852') # á
+ check_both_ways("\u00BB", "\xAF", 'IBM852') # »
+ check_both_ways("\u2591", "\xB0", 'IBM852') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM852') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM852') # └
+ check_both_ways("\u00A4", "\xCF", 'IBM852') # ¤
+ check_both_ways("\u0111", "\xD0", 'IBM852') # đ
+ check_both_ways("\u2580", "\xDF", 'IBM852') # ▀
+ check_both_ways("\u00D3", "\xE0", 'IBM852') # Ó
+ check_both_ways("\u00B4", "\xEF", 'IBM852') # ´
+ check_both_ways("\u00AD", "\xF0", 'IBM852') # osft hyphen
+ check_both_ways("\u00A0", "\xFF", 'IBM852') # non-breaking space
+ end
+
+ def test_IBM855
+ check_both_ways("\u0452", "\x80", 'IBM855') # ђ
+ check_both_ways("\u0408", "\x8F", 'IBM855') # Ј
+ check_both_ways("\u0459", "\x90", 'IBM855') # љ
+ check_both_ways("\u042A", "\x9F", 'IBM855') # Ъ
+ check_both_ways("\u0430", "\xA0", 'IBM855') # а
+ check_both_ways("\u00BB", "\xAF", 'IBM855') # »
+ check_both_ways("\u2591", "\xB0", 'IBM855') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM855') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM855') # └
+ check_both_ways("\u00A4", "\xCF", 'IBM855') # ¤
+ check_both_ways("\u043B", "\xD0", 'IBM855') # л
+ check_both_ways("\u2580", "\xDF", 'IBM855') # ▀
+ check_both_ways("\u042F", "\xE0", 'IBM855') # Я
+ check_both_ways("\u2116", "\xEF", 'IBM855') # №
+ check_both_ways("\u00AD", "\xF0", 'IBM855') # osft hyphen
+ check_both_ways("\u00A0", "\xFF", 'IBM855') # non-breaking space
+ end
+
+ def test_IBM857
+ check_both_ways("\u00C7", "\x80", 'IBM857') # Ç
+ check_both_ways("\u00C5", "\x8F", 'IBM857') # Å
+ check_both_ways("\u00C9", "\x90", 'IBM857') # É
+ check_both_ways("\u015F", "\x9F", 'IBM857') # ş
+ check_both_ways("\u00E1", "\xA0", 'IBM857') # á
+ check_both_ways("\u00BB", "\xAF", 'IBM857') # »
+ check_both_ways("\u2591", "\xB0", 'IBM857') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM857') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM857') # └
+ check_both_ways("\u00A4", "\xCF", 'IBM857') # ¤
+ check_both_ways("\u00BA", "\xD0", 'IBM857') # º
+ check_both_ways("\u00C8", "\xD4", 'IBM857') # È
+ assert_raise(Encoding::UndefinedConversionError) { "\xD5".encode("utf-8", 'IBM857') }
+ check_both_ways("\u00CD", "\xD6", 'IBM857') # Í
+ check_both_ways("\u2580", "\xDF", 'IBM857') # ▀
+ check_both_ways("\u00D3", "\xE0", 'IBM857') # Ó
+ check_both_ways("\u00B5", "\xE6", 'IBM857') # µ
+ assert_raise(Encoding::UndefinedConversionError) { "\xE7".encode("utf-8", 'IBM857') }
+ check_both_ways("\u00D7", "\xE8", 'IBM857') # ×
+ check_both_ways("\u00B4", "\xEF", 'IBM857') # ´
+ check_both_ways("\u00AD", "\xF0", 'IBM857') # soft hyphen
+ check_both_ways("\u00B1", "\xF1", 'IBM857') # ±
+ assert_raise(Encoding::UndefinedConversionError) { "\xF2".encode("utf-8", 'IBM857') }
+ check_both_ways("\u00BE", "\xF3", 'IBM857') # ¾
+ check_both_ways("\u00A0", "\xFF", 'IBM857') # non-breaking space
+ end
+
+ def test_IBM860
+ check_both_ways("\u00C7", "\x80", 'IBM860') # Ç
+ check_both_ways("\u00C2", "\x8F", 'IBM860') # Â
+ check_both_ways("\u00C9", "\x90", 'IBM860') # É
+ check_both_ways("\u00D3", "\x9F", 'IBM860') # Ó
+ check_both_ways("\u00E1", "\xA0", 'IBM860') # á
+ check_both_ways("\u00BB", "\xAF", 'IBM860') # »
+ check_both_ways("\u2591", "\xB0", 'IBM860') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM860') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM860') # └
+ check_both_ways("\u2567", "\xCF", 'IBM860') # ╧
+ check_both_ways("\u2568", "\xD0", 'IBM860') # ╨
+ check_both_ways("\u2580", "\xDF", 'IBM860') # ▀
+ check_both_ways("\u03B1", "\xE0", 'IBM860') # α
+ check_both_ways("\u2229", "\xEF", 'IBM860') # ∩
+ check_both_ways("\u2261", "\xF0", 'IBM860') # ≡
+ check_both_ways("\u00A0", "\xFF", 'IBM860') # non-breaking space
+ end
+
+ def test_IBM861
+ check_both_ways("\u00C7", "\x80", 'IBM861') # Ç
+ check_both_ways("\u00C5", "\x8F", 'IBM861') # Å
+ check_both_ways("\u00C9", "\x90", 'IBM861') # É
+ check_both_ways("\u0192", "\x9F", 'IBM861') # ƒ
+ check_both_ways("\u00E1", "\xA0", 'IBM861') # á
+ check_both_ways("\u00BB", "\xAF", 'IBM861') # »
+ check_both_ways("\u2591", "\xB0", 'IBM861') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM861') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM861') # └
+ check_both_ways("\u2567", "\xCF", 'IBM861') # ╧
+ check_both_ways("\u2568", "\xD0", 'IBM861') # ╨
+ check_both_ways("\u2580", "\xDF", 'IBM861') # ▀
+ check_both_ways("\u03B1", "\xE0", 'IBM861') # α
+ check_both_ways("\u2229", "\xEF", 'IBM861') # ∩
+ check_both_ways("\u2261", "\xF0", 'IBM861') # ≡
+ check_both_ways("\u00A0", "\xFF", 'IBM861') # non-breaking space
+ end
+
+ def test_IBM862
+ check_both_ways("\u05D0", "\x80", 'IBM862') # א
+ check_both_ways("\u05DF", "\x8F", 'IBM862') # ן
+ check_both_ways("\u05E0", "\x90", 'IBM862') # נ
+ check_both_ways("\u0192", "\x9F", 'IBM862') # ƒ
+ check_both_ways("\u00E1", "\xA0", 'IBM862') # á
+ check_both_ways("\u00BB", "\xAF", 'IBM862') # »
+ check_both_ways("\u2591", "\xB0", 'IBM862') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM862') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM862') # └
+ check_both_ways("\u2567", "\xCF", 'IBM862') # ╧
+ check_both_ways("\u2568", "\xD0", 'IBM862') # ╨
+ check_both_ways("\u2580", "\xDF", 'IBM862') # ▀
+ check_both_ways("\u03B1", "\xE0", 'IBM862') # α
+ check_both_ways("\u2229", "\xEF", 'IBM862') # ∩
+ check_both_ways("\u2261", "\xF0", 'IBM862') # ≡
+ check_both_ways("\u00A0", "\xFF", 'IBM862') # non-breaking space
+ end
+
+ def test_IBM863
+ check_both_ways("\u00C7", "\x80", 'IBM863') # Ç
+ check_both_ways("\u00A7", "\x8F", 'IBM863') # §
+ check_both_ways("\u00C9", "\x90", 'IBM863') # É
+ check_both_ways("\u0192", "\x9F", 'IBM863') # ƒ
+ check_both_ways("\u00A6", "\xA0", 'IBM863') # ¦
+ check_both_ways("\u00BB", "\xAF", 'IBM863') # »
+ check_both_ways("\u2591", "\xB0", 'IBM863') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM863') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM863') # └
+ check_both_ways("\u2567", "\xCF", 'IBM863') # ╧
+ check_both_ways("\u2568", "\xD0", 'IBM863') # ╨
+ check_both_ways("\u2580", "\xDF", 'IBM863') # ▀
+ check_both_ways("\u03B1", "\xE0", 'IBM863') # α
+ check_both_ways("\u2229", "\xEF", 'IBM863') # ∩
+ check_both_ways("\u2261", "\xF0", 'IBM863') # ≡
+ check_both_ways("\u00A0", "\xFF", 'IBM863') # non-breaking space
+ end
+
+ def test_IBM865
+ check_both_ways("\u00C7", "\x80", 'IBM865') # Ç
+ check_both_ways("\u00C5", "\x8F", 'IBM865') # Å
+ check_both_ways("\u00C9", "\x90", 'IBM865') # É
+ check_both_ways("\u0192", "\x9F", 'IBM865') # ƒ
+ check_both_ways("\u00E1", "\xA0", 'IBM865') # á
+ check_both_ways("\u00A4", "\xAF", 'IBM865') # ¤
+ check_both_ways("\u2591", "\xB0", 'IBM865') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM865') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM865') # └
+ check_both_ways("\u2567", "\xCF", 'IBM865') # ╧
+ check_both_ways("\u2568", "\xD0", 'IBM865') # ╨
+ check_both_ways("\u2580", "\xDF", 'IBM865') # ▀
+ check_both_ways("\u03B1", "\xE0", 'IBM865') # α
+ check_both_ways("\u2229", "\xEF", 'IBM865') # ∩
+ check_both_ways("\u2261", "\xF0", 'IBM865') # ≡
+ check_both_ways("\u00A0", "\xFF", 'IBM865') # non-breaking space
+ end
+
+ def test_IBM866
+ check_both_ways("\u0410", "\x80", 'IBM866') # А
+ check_both_ways("\u041F", "\x8F", 'IBM866') # П
+ check_both_ways("\u0420", "\x90", 'IBM866') # Р
+ check_both_ways("\u042F", "\x9F", 'IBM866') # Я
+ check_both_ways("\u0430", "\xA0", 'IBM866') # а
+ check_both_ways("\u043F", "\xAF", 'IBM866') # п
+ check_both_ways("\u2591", "\xB0", 'IBM866') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM866') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM866') # └
+ check_both_ways("\u2567", "\xCF", 'IBM866') # ╧
+ check_both_ways("\u2568", "\xD0", 'IBM866') # ╨
+ check_both_ways("\u2580", "\xDF", 'IBM866') # ▀
+ check_both_ways("\u0440", "\xE0", 'IBM866') # р
+ check_both_ways("\u044F", "\xEF", 'IBM866') # я
+ check_both_ways("\u0401", "\xF0", 'IBM866') # Ё
+ check_both_ways("\u00A0", "\xFF", 'IBM866') # non-breaking space
+ end
+
+ def test_IBM869
+ assert_raise(Encoding::UndefinedConversionError) { "\x80".encode("utf-8", 'IBM869') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x85".encode("utf-8", 'IBM869') }
+ check_both_ways("\u0386", "\x86", 'IBM869') # Ά
+ assert_raise(Encoding::UndefinedConversionError) { "\x87".encode("utf-8", 'IBM869') }
+ check_both_ways("\u00B7", "\x88", 'IBM869') # ·
+ check_both_ways("\u0389", "\x8F", 'IBM869') # Ή
+ check_both_ways("\u038A", "\x90", 'IBM869') # Ί
+ check_both_ways("\u038C", "\x92", 'IBM869') # Ό
+ assert_raise(Encoding::UndefinedConversionError) { "\x93".encode("utf-8", 'IBM869') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x94".encode("utf-8", 'IBM869') }
+ check_both_ways("\u038E", "\x95", 'IBM869') # Ύ
+ check_both_ways("\u03AF", "\x9F", 'IBM869') # ί
+ check_both_ways("\u03CA", "\xA0", 'IBM869') # ϊ
+ check_both_ways("\u00BB", "\xAF", 'IBM869') # »
+ check_both_ways("\u2591", "\xB0", 'IBM869') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM869') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM869') # └
+ check_both_ways("\u03A3", "\xCF", 'IBM869') # Σ
+ check_both_ways("\u03A4", "\xD0", 'IBM869') # Τ
+ check_both_ways("\u2580", "\xDF", 'IBM869') # ▀
+ check_both_ways("\u03B6", "\xE0", 'IBM869') # ζ
+ check_both_ways("\u0384", "\xEF", 'IBM869') # ΄
+ check_both_ways("\u00AD", "\xF0", 'IBM869') # soft hyphen
+ check_both_ways("\u00A0", "\xFF", 'IBM869') # non-breaking space
+ end
+
+ def test_macCroatian
+ check_both_ways("\u00C4", "\x80", 'macCroatian') # Ä
+ check_both_ways("\u00E8", "\x8F", 'macCroatian') # è
+ check_both_ways("\u00EA", "\x90", 'macCroatian') # ê
+ check_both_ways("\u00FC", "\x9F", 'macCroatian') # ü
+ check_both_ways("\u2020", "\xA0", 'macCroatian') # †
+ check_both_ways("\u00D8", "\xAF", 'macCroatian') # Ø
+ check_both_ways("\u221E", "\xB0", 'macCroatian') # ∞
+ check_both_ways("\u00F8", "\xBF", 'macCroatian') # ø
+ check_both_ways("\u00BF", "\xC0", 'macCroatian') # ¿
+ check_both_ways("\u0153", "\xCF", 'macCroatian') # œ
+ check_both_ways("\u0110", "\xD0", 'macCroatian') # Đ
+ check_both_ways("\u00A9", "\xD9", 'macCroatian') # ©
+ check_both_ways("\u2044", "\xDA", 'macCroatian') # ⁄
+ check_both_ways("\u203A", "\xDD", 'macCroatian') # ›
+ check_both_ways("\u00C6", "\xDE", 'macCroatian') # Æ
+ check_both_ways("\u00BB", "\xDF", 'macCroatian') # »
+ check_both_ways("\u2013", "\xE0", 'macCroatian') # –
+ check_both_ways("\u00B7", "\xE1", 'macCroatian') # ·
+ check_both_ways("\u00C2", "\xE5", 'macCroatian') # Â
+ check_both_ways("\u0107", "\xE6", 'macCroatian') # ć
+ check_both_ways("\u00C1", "\xE7", 'macCroatian') # Á
+ check_both_ways("\u010D", "\xE8", 'macCroatian') # č
+ check_both_ways("\u00C8", "\xE9", 'macCroatian') # È
+ check_both_ways("\u00D4", "\xEF", 'macCroatian') # Ô
+ check_both_ways("\u0111", "\xF0", 'macCroatian') # đ
+ check_both_ways("\u00D2", "\xF1", 'macCroatian') # Ò
+ check_both_ways("\u00AF", "\xF8", 'macCroatian') # ¯
+ check_both_ways("\u03C0", "\xF9", 'macCroatian') # π
+ check_both_ways("\u00CB", "\xFA", 'macCroatian') # Ë
+ check_both_ways("\u00CA", "\xFD", 'macCroatian') # Ê
+ check_both_ways("\u00E6", "\xFE", 'macCroatian') # æ
+ check_both_ways("\u02C7", "\xFF", 'macCroatian') # ˇ
+ end
+
+ def test_macCyrillic
+ check_both_ways("\u0410", "\x80", 'macCyrillic') # А
+ check_both_ways("\u041F", "\x8F", 'macCyrillic') # П
+ check_both_ways("\u0420", "\x90", 'macCyrillic') # Р
+ check_both_ways("\u042F", "\x9F", 'macCyrillic') # Я
+ check_both_ways("\u2020", "\xA0", 'macCyrillic') # †
+ check_both_ways("\u0453", "\xAF", 'macCyrillic') # ѓ
+ check_both_ways("\u221E", "\xB0", 'macCyrillic') # ∞
+ check_both_ways("\u045A", "\xBF", 'macCyrillic') # њ
+ check_both_ways("\u0458", "\xC0", 'macCyrillic') # ј
+ check_both_ways("\u0455", "\xCF", 'macCyrillic') # ѕ
+ check_both_ways("\u2013", "\xD0", 'macCyrillic') # –
+ check_both_ways("\u044F", "\xDF", 'macCyrillic') # я
+ check_both_ways("\u0430", "\xE0", 'macCyrillic') # а
+ check_both_ways("\u043F", "\xEF", 'macCyrillic') # п
+ check_both_ways("\u0440", "\xF0", 'macCyrillic') # р
+ check_both_ways("\u00A4", "\xFF", 'macCyrillic') # ¤
+ end
+
+ def test_macGreek
+ check_both_ways("\u00C4", "\x80", 'macGreek') # Ä
+ check_both_ways("\u00E8", "\x8F", 'macGreek') # è
+ check_both_ways("\u00EA", "\x90", 'macGreek') # ê
+ check_both_ways("\u00FC", "\x9F", 'macGreek') # ü
+ check_both_ways("\u2020", "\xA0", 'macGreek') # †
+ check_both_ways("\u0393", "\xA1", 'macGreek') # Γ
+ check_both_ways("\u0387", "\xAF", 'macGreek') # ·
+ check_both_ways("\u0391", "\xB0", 'macGreek') # Α
+ check_both_ways("\u03A9", "\xBF", 'macGreek') # Ω
+ check_both_ways("\u03AC", "\xC0", 'macGreek') # ά
+ check_both_ways("\u0153", "\xCF", 'macGreek') # œ
+ check_both_ways("\u2013", "\xD0", 'macGreek') # –
+ check_both_ways("\u038F", "\xDF", 'macGreek') # Ώ
+ check_both_ways("\u03CD", "\xE0", 'macGreek') # ύ
+ check_both_ways("\u03BF", "\xEF", 'macGreek') # ο
+ check_both_ways("\u03C0", "\xF0", 'macGreek') # π
+ check_both_ways("\u03B0", "\xFE", 'macGreek') # ΰ
+ assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'macGreek') }
+ end
+
+ def test_macIceland
+ check_both_ways("\u00C4", "\x80", 'macIceland') # Ä
+ check_both_ways("\u00E8", "\x8F", 'macIceland') # è
+ check_both_ways("\u00EA", "\x90", 'macIceland') # ê
+ check_both_ways("\u00FC", "\x9F", 'macIceland') # ü
+ check_both_ways("\u00DD", "\xA0", 'macIceland') # Ý
+ check_both_ways("\u00D8", "\xAF", 'macIceland') # Ø
+ check_both_ways("\u221E", "\xB0", 'macIceland') # ∞
+ check_both_ways("\u00F8", "\xBF", 'macIceland') # ø
+ check_both_ways("\u00BF", "\xC0", 'macIceland') # ¿
+ check_both_ways("\u0153", "\xCF", 'macIceland') # œ
+ check_both_ways("\u2013", "\xD0", 'macIceland') # –
+ check_both_ways("\u00FE", "\xDF", 'macIceland') # þ
+ check_both_ways("\u00FD", "\xE0", 'macIceland') # ý
+ check_both_ways("\u00D4", "\xEF", 'macIceland') # Ô
+ #check_both_ways("\uF8FF", "\xF0", 'macIceland') # Apple logo
+ check_both_ways("\u02C7", "\xFF", 'macIceland') # ˇ
+ end
+
+ def test_macRoman
+ check_both_ways("\u00C4", "\x80", 'macRoman') # Ä
+ check_both_ways("\u00E8", "\x8F", 'macRoman') # è
+ check_both_ways("\u00EA", "\x90", 'macRoman') # ê
+ check_both_ways("\u00FC", "\x9F", 'macRoman') # ü
+ check_both_ways("\u2020", "\xA0", 'macRoman') # †
+ #check_both_ways("\u00DB", "\xAF", 'macRoman') # Ø
+ check_both_ways("\u221E", "\xB0", 'macRoman') # ∞
+ check_both_ways("\u00F8", "\xBF", 'macRoman') # ø
+ check_both_ways("\u00BF", "\xC0", 'macRoman') # ¿
+ check_both_ways("\u0153", "\xCF", 'macRoman') # œ
+ check_both_ways("\u2013", "\xD0", 'macRoman') # –
+ check_both_ways("\u00A4", "\xDB", 'macRoman') # ¤
+ check_both_ways("\uFB02", "\xDF", 'macRoman') # fl
+ check_both_ways("\u2021", "\xE0", 'macRoman') # ‡
+ check_both_ways("\u00D4", "\xEF", 'macRoman') # Ô
+ #check_both_ways("\uF8FF", "\xF0", 'macRoman') # Apple logo
+ check_both_ways("\u02C7", "\xFF", 'macRoman') # ˇ
+ end
+
+ def test_macRomania
+ check_both_ways("\u00C4", "\x80", 'macRomania') # Ä
+ check_both_ways("\u00E8", "\x8F", 'macRomania') # è
+ check_both_ways("\u00EA", "\x90", 'macRomania') # ê
+ check_both_ways("\u00FC", "\x9F", 'macRomania') # ü
+ check_both_ways("\u2020", "\xA0", 'macRomania') # †
+ check_both_ways("\u015E", "\xAF", 'macRomania') # Ş
+ check_both_ways("\u221E", "\xB0", 'macRomania') # ∞
+ check_both_ways("\u015F", "\xBF", 'macRomania') # ş
+ check_both_ways("\u00BF", "\xC0", 'macRomania') # ¿
+ check_both_ways("\u0153", "\xCF", 'macRomania') # œ
+ check_both_ways("\u2013", "\xD0", 'macRomania') # –
+ check_both_ways("\u00A4", "\xDB", 'macRomania') # €
+ check_both_ways("\u0163", "\xDF", 'macRomania') # ţ
+ check_both_ways("\u2021", "\xE0", 'macRomania') # ‡
+ check_both_ways("\u00D4", "\xEF", 'macRomania') # Ô
+ #check_both_ways("\uF8FF", "\xF0", 'macRomania') # Apple logo
+ check_both_ways("\u02C7", "\xFF", 'macRomania') # ˇ
+ end
+
+ def test_macTurkish
+ check_both_ways("\u00C4", "\x80", 'macTurkish') # Ä
+ check_both_ways("\u00E8", "\x8F", 'macTurkish') # è
+ check_both_ways("\u00EA", "\x90", 'macTurkish') # ê
+ check_both_ways("\u00FC", "\x9F", 'macTurkish') # ü
+ check_both_ways("\u2020", "\xA0", 'macTurkish') # †
+ check_both_ways("\u00D8", "\xAF", 'macTurkish') # Ø
+ check_both_ways("\u221E", "\xB0", 'macTurkish') # ∞
+ check_both_ways("\u00F8", "\xBF", 'macTurkish') # ø
+ check_both_ways("\u00BF", "\xC0", 'macTurkish') # ¿
+ check_both_ways("\u0153", "\xCF", 'macTurkish') # œ
+ check_both_ways("\u2013", "\xD0", 'macTurkish') # –
+ check_both_ways("\u015F", "\xDF", 'macTurkish') # ş
+ check_both_ways("\u2021", "\xE0", 'macTurkish') # ‡
+ check_both_ways("\u00D4", "\xEF", 'macTurkish') # Ô
+ #check_both_ways("\uF8FF", "\xF0", 'macTurkish') # Apple logo
+ check_both_ways("\u00D9", "\xF4", 'macTurkish') # Ù
+ assert_raise(Encoding::UndefinedConversionError) { "\xF5".encode("utf-8", 'macTurkish') }
+ check_both_ways("\u02C6", "\xF6", 'macTurkish') # ˆ
+ check_both_ways("\u02C7", "\xFF", 'macTurkish') # ˇ
+ end
+
+ def test_macUkraine
+ check_both_ways("\u0410", "\x80", 'macUkraine') # А
+ check_both_ways("\u041F", "\x8F", 'macUkraine') # П
+ check_both_ways("\u0420", "\x90", 'macUkraine') # Р
+ check_both_ways("\u042F", "\x9F", 'macUkraine') # Я
+ check_both_ways("\u2020", "\xA0", 'macUkraine') # †
+ check_both_ways("\u0453", "\xAF", 'macUkraine') # ѓ
+ check_both_ways("\u221E", "\xB0", 'macUkraine') # ∞
+ check_both_ways("\u045A", "\xBF", 'macUkraine') # њ
+ check_both_ways("\u0458", "\xC0", 'macUkraine') # ј
+ check_both_ways("\u0455", "\xCF", 'macUkraine') # ѕ
+ check_both_ways("\u2013", "\xD0", 'macUkraine') # –
+ check_both_ways("\u044F", "\xDF", 'macUkraine') # я
+ check_both_ways("\u0430", "\xE0", 'macUkraine') # а
+ check_both_ways("\u043F", "\xEF", 'macUkraine') # п
+ check_both_ways("\u0440", "\xF0", 'macUkraine') # р
+ check_both_ways("\u00A4", "\xFF", 'macUkraine') # ¤
+ end
+
+ def test_koi8_u
+ check_both_ways("\u2500", "\x80", 'KOI8-U') # ─
+ check_both_ways("\u2590", "\x8F", 'KOI8-U') # ▐
+ check_both_ways("\u2591", "\x90", 'KOI8-U') # ░
+ check_both_ways("\u00F7", "\x9F", 'KOI8-U') # ÷
+ check_both_ways("\u2550", "\xA0", 'KOI8-U') # ═
+ check_both_ways("\u0454", "\xA4", 'KOI8-U') # є
+ check_both_ways("\u0456", "\xA6", 'KOI8-U') # і
+ check_both_ways("\u0457", "\xA7", 'KOI8-U') # ї
+ check_both_ways("\u0491", "\xAD", 'KOI8-U') # ґ
+ check_both_ways("\u255E", "\xAF", 'KOI8-U') # ╞
+ check_both_ways("\u255F", "\xB0", 'KOI8-U') # ╟
+ check_both_ways("\u0404", "\xB4", 'KOI8-U') # Є
+ check_both_ways("\u0406", "\xB6", 'KOI8-U') # І
+ check_both_ways("\u0407", "\xB7", 'KOI8-U') # Ї
+ check_both_ways("\u0490", "\xBD", 'KOI8-U') # Ґ
+ check_both_ways("\u00A9", "\xBF", 'KOI8-U') # ©
+ check_both_ways("\u044E", "\xC0", 'KOI8-U') # ю
+ check_both_ways("\u043E", "\xCF", 'KOI8-U') # о
+ check_both_ways("\u043F", "\xD0", 'KOI8-U') # п
+ check_both_ways("\u044A", "\xDF", 'KOI8-U') # ъ
+ check_both_ways("\u042E", "\xE0", 'KOI8-U') # Ю
+ check_both_ways("\u041E", "\xEF", 'KOI8-U') # О
+ check_both_ways("\u041F", "\xF0", 'KOI8-U') # П
+ check_both_ways("\u042A", "\xFF", 'KOI8-U') # Ъ
+ end
+
+ def test_koi8_r
+ check_both_ways("\u2500", "\x80", 'KOI8-R') # ─
+ check_both_ways("\u2590", "\x8F", 'KOI8-R') # ▐
+ check_both_ways("\u2591", "\x90", 'KOI8-R') # ░
+ check_both_ways("\u00F7", "\x9F", 'KOI8-R') # ÷
+ check_both_ways("\u2550", "\xA0", 'KOI8-R') # ═
+ check_both_ways("\u255E", "\xAF", 'KOI8-R') # ╞
+ check_both_ways("\u255F", "\xB0", 'KOI8-R') # ╟
+ check_both_ways("\u00A9", "\xBF", 'KOI8-R') # ©
+ check_both_ways("\u044E", "\xC0", 'KOI8-R') # ю
+ check_both_ways("\u043E", "\xCF", 'KOI8-R') # о
+ check_both_ways("\u043F", "\xD0", 'KOI8-R') # п
+ check_both_ways("\u044A", "\xDF", 'KOI8-R') # ъ
+ check_both_ways("\u042E", "\xE0", 'KOI8-R') # Ю
+ check_both_ways("\u041E", "\xEF", 'KOI8-R') # О
+ check_both_ways("\u041F", "\xF0", 'KOI8-R') # П
+ check_both_ways("\u042A", "\xFF", 'KOI8-R') # Ъ
+ end
+
+ def test_TIS_620
+ assert_raise(Encoding::UndefinedConversionError) { "\x80".encode("utf-8", 'TIS-620') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'TIS-620') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'TIS-620') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x9F".encode("utf-8", 'TIS-620') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA0".encode("utf-8", 'TIS-620') }
+ check_both_ways("\u0E01", "\xA1", 'TIS-620') # ก
+ check_both_ways("\u0E0F", "\xAF", 'TIS-620') # ฏ
+ check_both_ways("\u0E10", "\xB0", 'TIS-620') # ฐ
+ check_both_ways("\u0E1F", "\xBF", 'TIS-620') # ฟ
+ check_both_ways("\u0E20", "\xC0", 'TIS-620') # ภ
+ check_both_ways("\u0E2F", "\xCF", 'TIS-620') # ฯ
+ check_both_ways("\u0E30", "\xD0", 'TIS-620') # ะ
+ check_both_ways("\u0E3A", "\xDA", 'TIS-620') # ฺ
+ assert_raise(Encoding::UndefinedConversionError) { "\xDB".encode("utf-8", 'TIS-620') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xDE".encode("utf-8", 'TIS-620') }
+ check_both_ways("\u0E3F", "\xDF", 'TIS-620') # ฿
+ check_both_ways("\u0E40", "\xE0", 'TIS-620') # เ
+ check_both_ways("\u0E4F", "\xEF", 'TIS-620') # ๏
+ check_both_ways("\u0E50", "\xF0", 'TIS-620') # ๐
+ check_both_ways("\u0E5B", "\xFB", 'TIS-620') # ๛
+ assert_raise(Encoding::UndefinedConversionError) { "\xFC".encode("utf-8", 'TIS-620') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xFF".encode("utf-8", 'TIS-620') }
+ end
+
+ def test_CP850
+ check_both_ways("\u00C7", "\x80", 'CP850') # Ç
+ check_both_ways("\u00C5", "\x8F", 'CP850') # Å
+ check_both_ways("\u00C9", "\x90", 'CP850') # É
+ check_both_ways("\u0192", "\x9F", 'CP850') # ƒ
+ check_both_ways("\u00E1", "\xA0", 'CP850') # á
+ check_both_ways("\u00BB", "\xAF", 'CP850') # »
+ check_both_ways("\u2591", "\xB0", 'CP850') # ░
+ check_both_ways("\u2510", "\xBF", 'CP850') # ┐
+ check_both_ways("\u2514", "\xC0", 'CP850') # └
+ check_both_ways("\u00A4", "\xCF", 'CP850') # ¤
+ check_both_ways("\u00F0", "\xD0", 'CP850') # ð
+ check_both_ways("\u2580", "\xDF", 'CP850') # ▀
+ check_both_ways("\u00D3", "\xE0", 'CP850') # Ó
+ check_both_ways("\u00B4", "\xEF", 'CP850') # ´
+ check_both_ways("\u00AD", "\xF0", 'CP850') # soft hyphen
+ check_both_ways("\u00A0", "\xFF", 'CP850') # non-breaking space
+ end
+
+ def test_CP852
+ check_both_ways("\u00C7", "\x80", 'CP852') # Ç
+ check_both_ways("\u0106", "\x8F", 'CP852') # Ć
+ check_both_ways("\u00C9", "\x90", 'CP852') # É
+ check_both_ways("\u010D", "\x9F", 'CP852') # č
+ check_both_ways("\u00E1", "\xA0", 'CP852') # á
+ check_both_ways("\u00BB", "\xAF", 'CP852') # »
+ check_both_ways("\u2591", "\xB0", 'CP852') # ░
+ check_both_ways("\u2510", "\xBF", 'CP852') # ┐
+ check_both_ways("\u2514", "\xC0", 'CP852') # └
+ check_both_ways("\u00A4", "\xCF", 'CP852') # ¤
+ check_both_ways("\u0111", "\xD0", 'CP852') # đ
+ check_both_ways("\u2580", "\xDF", 'CP852') # ▀
+ check_both_ways("\u00D3", "\xE0", 'CP852') # Ó
+ check_both_ways("\u00B4", "\xEF", 'CP852') # ´
+ check_both_ways("\u00AD", "\xF0", 'CP852') # soft hyphen
+ check_both_ways("\u00A0", "\xFF", 'CP852') # non-breaking space
+ end
+
+ def test_CP855
+ check_both_ways("\u0452", "\x80", 'CP855') # ђ
+ check_both_ways("\u0408", "\x8F", 'CP855') # Ј
+ check_both_ways("\u0459", "\x90", 'CP855') # љ
+ check_both_ways("\u042A", "\x9F", 'CP855') # Ъ
+ check_both_ways("\u0430", "\xA0", 'CP855') # а
+ check_both_ways("\u00BB", "\xAF", 'CP855') # »
+ check_both_ways("\u2591", "\xB0", 'CP855') # ░
+ check_both_ways("\u2510", "\xBF", 'CP855') # ┐
+ check_both_ways("\u2514", "\xC0", 'CP855') # └
+ check_both_ways("\u00A4", "\xCF", 'CP855') # ¤
+ check_both_ways("\u043B", "\xD0", 'CP855') # л
+ check_both_ways("\u2580", "\xDF", 'CP855') # ▀
+ check_both_ways("\u042F", "\xE0", 'CP855') # Я
+ check_both_ways("\u2116", "\xEF", 'CP855') # №
+ check_both_ways("\u00AD", "\xF0", 'CP855') # soft hyphen
+ check_both_ways("\u00A0", "\xFF", 'CP855') # non-breaking space
+ end
+
+ def check_utf_16_both_ways(utf8, raw)
+ copy = raw.dup
+ 0.step(copy.length-1, 2) { |i| copy[i+1], copy[i] = copy[i], copy[i+1] }
+ check_both_ways(utf8, raw, 'utf-16be')
+ check_both_ways(utf8, copy, 'utf-16le')
+ end
+
+ def test_utf_16
+ check_utf_16_both_ways("abc", "\x00a\x00b\x00c")
+ check_utf_16_both_ways("\u00E9", "\x00\xE9");
+ check_utf_16_both_ways("\u00E9\u0070\u00E9\u0065", "\x00\xE9\x00\x70\x00\xE9\x00\x65") # épée
+ check_utf_16_both_ways("\u677E\u672C\u884C\u5F18", "\x67\x7E\x67\x2C\x88\x4C\x5F\x18") # 松本行弘
+ check_utf_16_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\x97\x52\x5C\x71\x5B\x66\x96\x62\x59\x27\x5B\x66") # 青山学院大学
+ check_utf_16_both_ways("Martin D\u00FCrst", "\x00M\x00a\x00r\x00t\x00i\x00n\x00 \x00D\x00\xFC\x00r\x00s\x00t") # Martin Dürst
+ # BMP
+ check_utf_16_both_ways("\u0000", "\x00\x00")
+ check_utf_16_both_ways("\u007F", "\x00\x7F")
+ check_utf_16_both_ways("\u0080", "\x00\x80")
+ check_utf_16_both_ways("\u0555", "\x05\x55")
+ check_utf_16_both_ways("\u04AA", "\x04\xAA")
+ check_utf_16_both_ways("\u0333", "\x03\x33")
+ check_utf_16_both_ways("\u04CC", "\x04\xCC")
+ check_utf_16_both_ways("\u00F0", "\x00\xF0")
+ check_utf_16_both_ways("\u070F", "\x07\x0F")
+ check_utf_16_both_ways("\u07FF", "\x07\xFF")
+ check_utf_16_both_ways("\u0800", "\x08\x00")
+ check_utf_16_both_ways("\uD7FF", "\xD7\xFF")
+ check_utf_16_both_ways("\uE000", "\xE0\x00")
+ check_utf_16_both_ways("\uFFFF", "\xFF\xFF")
+ check_utf_16_both_ways("\u5555", "\x55\x55")
+ check_utf_16_both_ways("\uAAAA", "\xAA\xAA")
+ check_utf_16_both_ways("\u3333", "\x33\x33")
+ check_utf_16_both_ways("\uCCCC", "\xCC\xCC")
+ check_utf_16_both_ways("\uF0F0", "\xF0\xF0")
+ check_utf_16_both_ways("\u0F0F", "\x0F\x0F")
+ check_utf_16_both_ways("\uFF00", "\xFF\x00")
+ check_utf_16_both_ways("\u00FF", "\x00\xFF")
+ # outer planes
+ check_utf_16_both_ways("\u{10000}", "\xD8\x00\xDC\x00")
+ check_utf_16_both_ways("\u{FFFFF}", "\xDB\xBF\xDF\xFF")
+ check_utf_16_both_ways("\u{100000}", "\xDB\xC0\xDC\x00")
+ check_utf_16_both_ways("\u{10FFFF}", "\xDB\xFF\xDF\xFF")
+ check_utf_16_both_ways("\u{105555}", "\xDB\xD5\xDD\x55")
+ check_utf_16_both_ways("\u{55555}", "\xD9\x15\xDD\x55")
+ check_utf_16_both_ways("\u{AAAAA}", "\xDA\x6A\xDE\xAA")
+ check_utf_16_both_ways("\u{33333}", "\xD8\x8C\xDF\x33")
+ check_utf_16_both_ways("\u{CCCCC}", "\xDA\xF3\xDC\xCC")
+ check_utf_16_both_ways("\u{8F0F0}", "\xD9\xFC\xDC\xF0")
+ check_utf_16_both_ways("\u{F0F0F}", "\xDB\x83\xDF\x0F")
+ check_utf_16_both_ways("\u{8FF00}", "\xD9\xFF\xDF\x00")
+ check_utf_16_both_ways("\u{F00FF}", "\xDB\x80\xDC\xFF")
+ end
+
+ def test_utf_16_bom
+ expected = "\u{3042}\u{3044}\u{20bb7}"
+ assert_equal(expected, %w/fffe4230443042d8b7df/.pack("H*").encode("UTF-8","UTF-16"))
+ check_both_ways(expected, %w/feff30423044d842dfb7/.pack("H*"), "UTF-16")
+ assert_raise(Encoding::InvalidByteSequenceError){%w/feffdfb7/.pack("H*").encode("UTF-8","UTF-16")}
+ assert_raise(Encoding::InvalidByteSequenceError){%w/fffeb7df/.pack("H*").encode("UTF-8","UTF-16")}
+ end
+
+ def test_utf_32_bom
+ expected = "\u{3042}\u{3044}\u{20bb7}"
+ assert_equal(expected, %w/fffe00004230000044300000b70b0200/.pack("H*").encode("UTF-8","UTF-32"))
+ check_both_ways(expected, %w/0000feff000030420000304400020bb7/.pack("H*"), "UTF-32")
+ assert_raise(Encoding::InvalidByteSequenceError){%w/0000feff00110000/.pack("H*").encode("UTF-8","UTF-32")}
+ end
+
+ def check_utf_32_both_ways(utf8, raw)
+ copy = raw.dup
+ 0.step(copy.length-1, 4) do |i|
+ copy[i+3], copy[i+2], copy[i+1], copy[i] = copy[i], copy[i+1], copy[i+2], copy[i+3]
+ end
+ check_both_ways(utf8, raw, 'utf-32be')
+ #check_both_ways(utf8, copy, 'utf-32le')
+ end
+
+ def test_utf_32
+ check_utf_32_both_ways("abc", "\x00\x00\x00a\x00\x00\x00b\x00\x00\x00c")
+ check_utf_32_both_ways("\u00E9", "\x00\x00\x00\xE9");
+ check_utf_32_both_ways("\u00E9\u0070\u00E9\u0065",
+ "\x00\x00\x00\xE9\x00\x00\x00\x70\x00\x00\x00\xE9\x00\x00\x00\x65") # épée
+ check_utf_32_both_ways("\u677E\u672C\u884C\u5F18",
+ "\x00\x00\x67\x7E\x00\x00\x67\x2C\x00\x00\x88\x4C\x00\x00\x5F\x18") # 松本行弘
+ check_utf_32_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66",
+ "\x00\x00\x97\x52\x00\x00\x5C\x71\x00\x00\x5B\x66\x00\x00\x96\x62\x00\x00\x59\x27\x00\x00\x5B\x66") # 青山学院大学
+ check_utf_32_both_ways("Martin D\u00FCrst",
+ "\x00\x00\x00M\x00\x00\x00a\x00\x00\x00r\x00\x00\x00t\x00\x00\x00i\x00\x00\x00n\x00\x00\x00 \x00\x00\x00D\x00\x00\x00\xFC\x00\x00\x00r\x00\x00\x00s\x00\x00\x00t") # Martin Dürst
+ # BMP
+ check_utf_32_both_ways("\u0000", "\x00\x00\x00\x00")
+ check_utf_32_both_ways("\u007F", "\x00\x00\x00\x7F")
+ check_utf_32_both_ways("\u0080", "\x00\x00\x00\x80")
+ check_utf_32_both_ways("\u0555", "\x00\x00\x05\x55")
+ check_utf_32_both_ways("\u04AA", "\x00\x00\x04\xAA")
+ check_utf_32_both_ways("\u0333", "\x00\x00\x03\x33")
+ check_utf_32_both_ways("\u04CC", "\x00\x00\x04\xCC")
+ check_utf_32_both_ways("\u00F0", "\x00\x00\x00\xF0")
+ check_utf_32_both_ways("\u070F", "\x00\x00\x07\x0F")
+ check_utf_32_both_ways("\u07FF", "\x00\x00\x07\xFF")
+ check_utf_32_both_ways("\u0800", "\x00\x00\x08\x00")
+ check_utf_32_both_ways("\uD7FF", "\x00\x00\xD7\xFF")
+ check_utf_32_both_ways("\uE000", "\x00\x00\xE0\x00")
+ check_utf_32_both_ways("\uFFFF", "\x00\x00\xFF\xFF")
+ check_utf_32_both_ways("\u5555", "\x00\x00\x55\x55")
+ check_utf_32_both_ways("\uAAAA", "\x00\x00\xAA\xAA")
+ check_utf_32_both_ways("\u3333", "\x00\x00\x33\x33")
+ check_utf_32_both_ways("\uCCCC", "\x00\x00\xCC\xCC")
+ check_utf_32_both_ways("\uF0F0", "\x00\x00\xF0\xF0")
+ check_utf_32_both_ways("\u0F0F", "\x00\x00\x0F\x0F")
+ check_utf_32_both_ways("\uFF00", "\x00\x00\xFF\x00")
+ check_utf_32_both_ways("\u00FF", "\x00\x00\x00\xFF")
+ # outer planes
+ check_utf_32_both_ways("\u{10000}", "\x00\x01\x00\x00")
+ check_utf_32_both_ways("\u{FFFFF}", "\x00\x0F\xFF\xFF")
+ check_utf_32_both_ways("\u{100000}","\x00\x10\x00\x00")
+ check_utf_32_both_ways("\u{10FFFF}","\x00\x10\xFF\xFF")
+ check_utf_32_both_ways("\u{105555}","\x00\x10\x55\x55")
+ check_utf_32_both_ways("\u{55555}", "\x00\x05\x55\x55")
+ check_utf_32_both_ways("\u{AAAAA}", "\x00\x0A\xAA\xAA")
+ check_utf_32_both_ways("\u{33333}", "\x00\x03\x33\x33")
+ check_utf_32_both_ways("\u{CCCCC}", "\x00\x0C\xCC\xCC")
+ check_utf_32_both_ways("\u{8F0F0}", "\x00\x08\xF0\xF0")
+ check_utf_32_both_ways("\u{F0F0F}", "\x00\x0F\x0F\x0F")
+ check_utf_32_both_ways("\u{8FF00}", "\x00\x08\xFF\x00")
+ check_utf_32_both_ways("\u{F00FF}", "\x00\x0F\x00\xFF")
+ end
+
+ def test_invalid_ignore
+ # arguments only
+ assert_nothing_raised { 'abc'.encode('utf-8', invalid: :replace, replace: "") }
+ # check handling of UTF-8 ill-formed subsequences
+ assert_equal("\x00\x41\x00\x3E\x00\x42".force_encoding('UTF-16BE'),
+ "\x41\xC2\x3E\x42".encode('UTF-16BE', 'UTF-8', invalid: :replace, replace: ""))
+ assert_equal("\x00\x41\x00\xF1\x00\x42".force_encoding('UTF-16BE'),
+ "\x41\xC2\xC3\xB1\x42".encode('UTF-16BE', 'UTF-8', invalid: :replace, replace: ""))
+ assert_equal("\x00\x42".force_encoding('UTF-16BE'),
+ "\xF0\x80\x80\x42".encode('UTF-16BE', 'UTF-8', invalid: :replace, replace: ""))
+ assert_equal(''.force_encoding('UTF-16BE'),
+ "\x82\xAB".encode('UTF-16BE', 'UTF-8', invalid: :replace, replace: ""))
+
+ assert_equal("\e$B!!\e(B".force_encoding("ISO-2022-JP"),
+ "\xA1\xA1\xFF".encode("ISO-2022-JP", "EUC-JP", invalid: :replace, replace: ""))
+ assert_equal("\e$B\x24\x22\x24\x24\e(B".force_encoding("ISO-2022-JP"),
+ "\xA4\xA2\xFF\xA4\xA4".encode("ISO-2022-JP", "EUC-JP", invalid: :replace, replace: ""))
+ assert_equal("\e$B\x24\x22\x24\x24\e(B".force_encoding("ISO-2022-JP"),
+ "\xA4\xA2\xFF\xFF\xA4\xA4".encode("ISO-2022-JP", "EUC-JP", invalid: :replace, replace: ""))
+ end
+
+ def test_invalid_replace
+ # arguments only
+ assert_nothing_raised { 'abc'.encode('UTF-8', invalid: :replace) }
+ assert_equal("\xEF\xBF\xBD".force_encoding("UTF-8"),
+ "\x80".encode("UTF-8", "UTF-16BE", invalid: :replace))
+ assert_equal("\xFF\xFD".force_encoding("UTF-16BE"),
+ "\x80".encode("UTF-16BE", "UTF-8", invalid: :replace))
+ assert_equal("\xFD\xFF".force_encoding("UTF-16LE"),
+ "\x80".encode("UTF-16LE", "UTF-8", invalid: :replace))
+ assert_equal("\x00\x00\xFF\xFD".force_encoding("UTF-32BE"),
+ "\x80".encode("UTF-32BE", "UTF-8", invalid: :replace))
+ assert_equal("\xFD\xFF\x00\x00".force_encoding("UTF-32LE"),
+ "\x80".encode("UTF-32LE", "UTF-8", invalid: :replace))
+
+ assert_equal("\uFFFD!",
+ "\xdc\x00\x00!".encode("utf-8", "utf-16be", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\xd8\x00\x00!".encode("utf-8", "utf-16be", :invalid=>:replace))
+
+ assert_equal("\uFFFD!",
+ "\x00\xdc!\x00".encode("utf-8", "utf-16le", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\x00\xd8!\x00".encode("utf-8", "utf-16le", :invalid=>:replace))
+
+ assert_equal("\uFFFD!",
+ "\x01\x00\x00\x00\x00\x00\x00!".encode("utf-8", "utf-32be", :invalid=>:replace), "[ruby-dev:35726]")
+ assert_equal("\uFFFD!",
+ "\x00\xff\x00\x00\x00\x00\x00!".encode("utf-8", "utf-32be", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\x00\x00\xd8\x00\x00\x00\x00!".encode("utf-8", "utf-32be", :invalid=>:replace))
+
+ assert_equal("\uFFFD!",
+ "\x00\x00\x00\xff!\x00\x00\x00".encode("utf-8", "utf-32le", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\x00\x00\xff\x00!\x00\x00\x00".encode("utf-8", "utf-32le", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\x00\xd8\x00\x00!\x00\x00\x00".encode("utf-8", "utf-32le", :invalid=>:replace))
+
+ assert_equal("\uFFFD!",
+ "\xff!".encode("utf-8", "euc-jp", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\xff!".encode("utf-8", "euc-jis-2004", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\xa1!".encode("utf-8", "euc-jp", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\xa1!".encode("utf-8", "euc-jis-2004", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\x8f\xa1!".encode("utf-8", "euc-jp", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\x8f\xa1!".encode("utf-8", "euc-jis-2004", :invalid=>:replace))
+
+ assert_equal("?",
+ "\xdc\x00".encode("EUC-JP", "UTF-16BE", :invalid=>:replace), "[ruby-dev:35776]")
+ assert_equal("ab?cd?ef",
+ "\0a\0b\xdc\x00\0c\0d\xdf\x00\0e\0f".encode("EUC-JP", "UTF-16BE", :invalid=>:replace))
+
+ assert_equal("\e$B!!\e(B?".force_encoding("ISO-2022-JP"),
+ "\xA1\xA1\xFF".encode("ISO-2022-JP", "EUC-JP", invalid: :replace))
+ assert_equal("\e$B\x24\x22\e(B?\e$B\x24\x24\e(B".force_encoding("ISO-2022-JP"),
+ "\xA4\xA2\xFF\xA4\xA4".encode("ISO-2022-JP", "EUC-JP", invalid: :replace))
+ assert_equal("\e$B\x24\x22\e(B??\e$B\x24\x24\e(B".force_encoding("ISO-2022-JP"),
+ "\xA4\xA2\xFF\xFF\xA4\xA4".encode("ISO-2022-JP", "EUC-JP", invalid: :replace))
+ end
+
+ def test_invalid_replace_string
+ assert_equal("a<x>A", "a\x80A".encode("us-ascii", "euc-jp", :invalid=>:replace, :replace=>"<x>"))
+ assert_equal("a<x>A", "a\x80A".encode("us-ascii", "euc-jis-2004", :invalid=>:replace, :replace=>"<x>"))
+ end
+
+ def test_undef_replace
+ assert_equal("?", "\u20AC".encode("EUC-JP", :undef=>:replace), "[ruby-dev:35709]")
+ end
+
+ def test_undef_replace_string
+ assert_equal("a<x>A", "a\u3042A".encode("us-ascii", :undef=>:replace, :replace=>"<x>"))
+ end
+
+ def test_shift_jis
+ check_both_ways("\u3000", "\x81\x40", 'shift_jis') # full-width space
+ check_both_ways("\u00D7", "\x81\x7E", 'shift_jis') # ×
+ check_both_ways("\u00F7", "\x81\x80", 'shift_jis') # ÷
+ check_both_ways("\u25C7", "\x81\x9E", 'shift_jis') # ◇
+ check_both_ways("\u25C6", "\x81\x9F", 'shift_jis') # ◆
+ check_both_ways("\u25EF", "\x81\xFC", 'shift_jis') # ◯
+ check_both_ways("\u6A97", "\x9F\x40", 'shift_jis') # 檗
+ check_both_ways("\u6BEF", "\x9F\x7E", 'shift_jis') # 毯
+ check_both_ways("\u9EBE", "\x9F\x80", 'shift_jis') # 麾
+ check_both_ways("\u6CBE", "\x9F\x9E", 'shift_jis') # 沾
+ check_both_ways("\u6CBA", "\x9F\x9F", 'shift_jis') # 沺
+ check_both_ways("\u6ECC", "\x9F\xFC", 'shift_jis') # 滌
+ check_both_ways("\u6F3E", "\xE0\x40", 'shift_jis') # 漾
+ check_both_ways("\u70DD", "\xE0\x7E", 'shift_jis') # 烝
+ check_both_ways("\u70D9", "\xE0\x80", 'shift_jis') # 烙
+ check_both_ways("\u71FC", "\xE0\x9E", 'shift_jis') # 燼
+ check_both_ways("\u71F9", "\xE0\x9F", 'shift_jis') # 燹
+ check_both_ways("\u73F1", "\xE0\xFC", 'shift_jis') # 珱
+ assert_raise(Encoding::UndefinedConversionError) { "\xEF\x40".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xEF\x7E".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xEF\x80".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xEF\x9E".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xEF\x9F".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xEF\xFC".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xF0\x40".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xF0\x7E".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xF0\x80".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xF0\x9E".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xF0\x9F".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xF0\xFC".encode("utf-8", 'shift_jis') }
+ #check_both_ways("\u9ADC", "\xFC\x40", 'shift_jis') # 髜 (IBM extended)
+ assert_raise(Encoding::UndefinedConversionError) { "\xFC\x7E".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xFC\x80".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xFC\x9E".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xFC\x9F".encode("utf-8", 'shift_jis') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xFC\xFC".encode("utf-8", 'shift_jis') }
+ check_both_ways("\u677E\u672C\u884C\u5F18", "\x8f\xbc\x96\x7b\x8d\x73\x8d\x4f", 'shift_jis') # 松本行弘
+ check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\x90\xC2\x8E\x52\x8A\x77\x89\x40\x91\xE5\x8A\x77", 'shift_jis') # 青山学院大学
+ check_both_ways("\u795E\u6797\u7FA9\u535A", "\x90\x5F\x97\xD1\x8B\x60\x94\x8E", 'shift_jis') # 神林義博
+ end
+
+ def test_windows_31j
+ check_both_ways("\u222A", "\x81\xBE", 'Windows-31J') # Union
+ check_both_ways("\uFFE2", "\x81\xCA", 'Windows-31J') # Fullwidth Not Sign
+ check_both_ways("\u2235", "\x81\xE6", 'Windows-31J') # Because
+ check_both_ways("\u2160", "\x87\x54", 'Windows-31J') # Roman Numeral One
+ check_both_ways("\u2170", "\xFA\x40", 'Windows-31J') # Small Roman Numeral One
+ end
+
+ def test_euc_jp
+ check_both_ways("\u3000", "\xA1\xA1", 'euc-jp') # full-width space
+ check_both_ways("\u00D7", "\xA1\xDF", 'euc-jp') # ×
+ check_both_ways("\u00F7", "\xA1\xE0", 'euc-jp') # ÷
+ check_both_ways("\u25C7", "\xA1\xFE", 'euc-jp') # ◇
+ check_both_ways("\u25C6", "\xA2\xA1", 'euc-jp') # ◆
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xAF".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xB9".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xC2".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xC9".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xD1".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xDB".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xEB".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xF1".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xFA".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xFD".encode("utf-8", 'euc-jp') }
+ check_both_ways("\u25EF", "\xA2\xFE", 'euc-jp') # ◯
+ assert_raise(Encoding::UndefinedConversionError) { "\xA3\xAF".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA3\xBA".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA3\xC0".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA3\xDB".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA3\xE0".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA3\xFB".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA4\xF4".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA5\xF7".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA6\xB9".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA6\xC0".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA6\xD9".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA7\xC2".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA7\xD0".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA7\xF2".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA8\xC1".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xCF\xD4".encode("utf-8", 'euc-jp') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xCF\xFE".encode("utf-8", 'euc-jp') }
+ check_both_ways("\u6A97", "\xDD\xA1", 'euc-jp') # 檗
+ check_both_ways("\u6BEF", "\xDD\xDF", 'euc-jp') # 毯
+ check_both_ways("\u9EBE", "\xDD\xE0", 'euc-jp') # 麾
+ check_both_ways("\u6CBE", "\xDD\xFE", 'euc-jp') # 沾
+ check_both_ways("\u6CBA", "\xDE\xA1", 'euc-jp') # 沺
+ check_both_ways("\u6ECC", "\xDE\xFE", 'euc-jp') # 滌
+ check_both_ways("\u6F3E", "\xDF\xA1", 'euc-jp') # 漾
+ check_both_ways("\u70DD", "\xDF\xDF", 'euc-jp') # 烝
+ check_both_ways("\u70D9", "\xDF\xE0", 'euc-jp') # 烙
+ check_both_ways("\u71FC", "\xDF\xFE", 'euc-jp') # 燼
+ check_both_ways("\u71F9", "\xE0\xA1", 'euc-jp') # 燹
+ check_both_ways("\u73F1", "\xE0\xFE", 'euc-jp') # 珱
+ assert_raise(Encoding::UndefinedConversionError) { "\xF4\xA7".encode("utf-8", 'euc-jp') }
+ #check_both_ways("\u9ADC", "\xFC\xE3", 'euc-jp') # 髜 (IBM extended)
+
+ check_both_ways("\u677E\u672C\u884C\u5F18", "\xBE\xBE\xCB\xDC\xB9\xD4\xB9\xB0", 'euc-jp') # 松本行弘
+ check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\xC0\xC4\xBB\xB3\xB3\xD8\xB1\xA1\xC2\xE7\xB3\xD8", 'euc-jp') # 青山学院大学
+ check_both_ways("\u795E\u6797\u7FA9\u535A", "\xBF\xC0\xCE\xD3\xB5\xC1\xC7\xEE", 'euc-jp') # 神林義博
+ end
+
+ def test_euc_jis_2004
+ check_both_ways("\u3000", "\xA1\xA1", 'euc-jis-2004') # full-width space
+ check_both_ways("\u00D7", "\xA1\xDF", 'euc-jis-2004') # ×
+ check_both_ways("\u00F7", "\xA1\xE0", 'euc-jis-2004') # ÷
+ check_both_ways("\u25C7", "\xA1\xFE", 'euc-jis-2004') # ◇
+ check_both_ways("\u25C6", "\xA2\xA1", 'euc-jis-2004') # ◆
+ check_both_ways("\uFF07", "\xA2\xAF", 'euc-jis-2004') # '
+ check_both_ways("\u309F", "\xA2\xB9", 'euc-jis-2004') # ゟ
+ check_both_ways("\u2284", "\xA2\xC2", 'euc-jis-2004') # ⊄
+ check_both_ways("\u2306", "\xA2\xC9", 'euc-jis-2004') # ⌆
+ check_both_ways("\u2295", "\xA2\xD1", 'euc-jis-2004') # ⊕
+ check_both_ways("\u3017", "\xA2\xDB", 'euc-jis-2004') # 〗
+ check_both_ways("\u2262", "\xA2\xEB", 'euc-jis-2004') # ≢
+ check_both_ways("\u2194", "\xA2\xF1", 'euc-jis-2004') # ↔
+ check_both_ways("\u266E", "\xA2\xFA", 'euc-jis-2004') # ♮
+ check_both_ways("\u2669", "\xA2\xFD", 'euc-jis-2004') # ♩
+ check_both_ways("\u25EF", "\xA2\xFE", 'euc-jis-2004') # ◯
+ check_both_ways("\u2935", "\xA3\xAF", 'euc-jis-2004') # ⤵
+ check_both_ways("\u29BF", "\xA3\xBA", 'euc-jis-2004') # ⦿
+ check_both_ways("\u2022", "\xA3\xC0", 'euc-jis-2004') # •
+ check_both_ways("\u2213", "\xA3\xDB", 'euc-jis-2004') # ∓
+ check_both_ways("\u2127", "\xA3\xE0", 'euc-jis-2004') # ℧
+ check_both_ways("\u30A0", "\xA3\xFB", 'euc-jis-2004') # ゠
+ check_both_ways("\uFF54", "\xA3\xF4", 'euc-jis-2004') # t
+ assert_raise(Encoding::UndefinedConversionError) { "\xA5\xF7".encode("utf-8", 'euc-jis-2004') }
+ check_both_ways("\u2664", "\xA6\xB9", 'euc-jis-2004') # ♤
+ check_both_ways("\u2663", "\xA6\xC0", 'euc-jis-2004') # ♣
+ check_both_ways("\u03C2", "\xA6\xD9", 'euc-jis-2004') # ς
+ check_both_ways("\u23BE", "\xA7\xC2", 'euc-jis-2004') # ⎾
+ check_both_ways("\u23CC", "\xA7\xD0", 'euc-jis-2004') # ⏌
+ check_both_ways("\u30F7", "\xA7\xF2", 'euc-jis-2004') # ヷ
+ check_both_ways("\u3251", "\xA8\xC1", 'euc-jis-2004') # ㉑
+ check_both_ways("\u{20B9F}", "\xCF\xD4", 'euc-jis-2004') # 𠮑
+ check_both_ways("\u541E", "\xCF\xFE", 'euc-jis-2004') # 吞
+ check_both_ways("\u6A97", "\xDD\xA1", 'euc-jis-2004') # 檗
+ check_both_ways("\u6BEF", "\xDD\xDF", 'euc-jis-2004') # 毯
+ check_both_ways("\u9EBE", "\xDD\xE0", 'euc-jis-2004') # 麾
+ check_both_ways("\u6CBE", "\xDD\xFE", 'euc-jis-2004') # 沾
+ check_both_ways("\u6CBA", "\xDE\xA1", 'euc-jis-2004') # 沺
+ check_both_ways("\u6ECC", "\xDE\xFE", 'euc-jis-2004') # 滌
+ check_both_ways("\u6F3E", "\xDF\xA1", 'euc-jis-2004') # 漾
+ check_both_ways("\u70DD", "\xDF\xDF", 'euc-jis-2004') # 烝
+ check_both_ways("\u70D9", "\xDF\xE0", 'euc-jis-2004') # 烙
+ check_both_ways("\u71FC", "\xDF\xFE", 'euc-jis-2004') # 燼
+ check_both_ways("\u71F9", "\xE0\xA1", 'euc-jis-2004') # 燹
+ check_both_ways("\u73F1", "\xE0\xFE", 'euc-jis-2004') # 珱
+ check_both_ways("\u5653", "\xF4\xA7", 'euc-jis-2004') # 噓
+ #check_both_ways("\u9ADC", "\xFC\xE3", 'euc-jp') # 髜 (IBM extended)
+
+ check_both_ways("\u9DD7", "\xFE\xE5", 'euc-jis-2004') # 鷗
+ check_both_ways("\u{2000B}", "\xAE\xA2", 'euc-jis-2004') # 𠀋
+ check_both_ways("\u{2A6B2}", "\x8F\xFE\xF6", 'euc-jis-2004') # 𪚲
+
+ check_both_ways("\u677E\u672C\u884C\u5F18", "\xBE\xBE\xCB\xDC\xB9\xD4\xB9\xB0", 'euc-jis-2004') # 松本行弘
+ check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\xC0\xC4\xBB\xB3\xB3\xD8\xB1\xA1\xC2\xE7\xB3\xD8", 'euc-jis-2004') # 青山学院大学
+ check_both_ways("\u795E\u6797\u7FA9\u535A", "\xBF\xC0\xCE\xD3\xB5\xC1\xC7\xEE", 'euc-jis-2004') # 神林義博
+ end
+
+ def test_eucjp_ms
+ check_both_ways("\u2116", "\xAD\xE2", 'eucJP-ms') # NUMERO SIGN
+ check_both_ways("\u221A", "\xA2\xE5", 'eucJP-ms') # SQUARE ROOT
+ check_both_ways("\u3231", "\xAD\xEA", 'eucJP-ms') # PARENTHESIZED IDEOGRAPH STOCK
+ check_both_ways("\uFF5E", "\xA1\xC1", 'eucJP-ms') # WAVE DASH
+ end
+
+ def test_eucjp_sjis
+ check_both_ways2("\xa1\xa1", "EUC-JP", "\x81\x40", "Shift_JIS")
+ check_both_ways2("\xa1\xdf", "EUC-JP", "\x81\x7e", "Shift_JIS")
+ check_both_ways2("\xa1\xe0", "EUC-JP", "\x81\x80", "Shift_JIS")
+ check_both_ways2("\xa1\xfe", "EUC-JP", "\x81\x9e", "Shift_JIS")
+ check_both_ways2("\xa2\xa1", "EUC-JP", "\x81\x9f", "Shift_JIS")
+ check_both_ways2("\xa2\xfe", "EUC-JP", "\x81\xfc", "Shift_JIS")
+
+ check_both_ways2("\xdd\xa1", "EUC-JP", "\x9f\x40", "Shift_JIS")
+ check_both_ways2("\xdd\xdf", "EUC-JP", "\x9f\x7e", "Shift_JIS")
+ check_both_ways2("\xdd\xe0", "EUC-JP", "\x9f\x80", "Shift_JIS")
+ check_both_ways2("\xdd\xfe", "EUC-JP", "\x9f\x9e", "Shift_JIS")
+ check_both_ways2("\xde\xa1", "EUC-JP", "\x9f\x9f", "Shift_JIS")
+ check_both_ways2("\xde\xfe", "EUC-JP", "\x9f\xfc", "Shift_JIS")
+
+ check_both_ways2("\xdf\xa1", "EUC-JP", "\xe0\x40", "Shift_JIS")
+ check_both_ways2("\xdf\xdf", "EUC-JP", "\xe0\x7e", "Shift_JIS")
+ check_both_ways2("\xdf\xe0", "EUC-JP", "\xe0\x80", "Shift_JIS")
+ check_both_ways2("\xdf\xfe", "EUC-JP", "\xe0\x9e", "Shift_JIS")
+ check_both_ways2("\xe0\xa1", "EUC-JP", "\xe0\x9f", "Shift_JIS")
+ check_both_ways2("\xe0\xfe", "EUC-JP", "\xe0\xfc", "Shift_JIS")
+
+ check_both_ways2("\xf4\xa1", "EUC-JP", "\xea\x9f", "Shift_JIS")
+ check_both_ways2("\xf4\xa2", "EUC-JP", "\xea\xa0", "Shift_JIS")
+ check_both_ways2("\xf4\xa3", "EUC-JP", "\xea\xa1", "Shift_JIS")
+ check_both_ways2("\xf4\xa4", "EUC-JP", "\xea\xa2", "Shift_JIS") # end of JIS X 0208 1983
+ check_both_ways2("\xf4\xa5", "EUC-JP", "\xea\xa3", "Shift_JIS")
+ check_both_ways2("\xf4\xa6", "EUC-JP", "\xea\xa4", "Shift_JIS") # end of JIS X 0208 1990
+
+ check_both_ways2("\x8e\xa1", "EUC-JP", "\xa1", "Shift_JIS")
+ check_both_ways2("\x8e\xdf", "EUC-JP", "\xdf", "Shift_JIS")
+ end
+
+ def test_eucjp_sjis_unassigned
+ check_both_ways2("\xfd\xa1", "EUC-JP", "\xef\x40", "Shift_JIS")
+ check_both_ways2("\xfd\xa1", "EUC-JP", "\xef\x40", "Shift_JIS")
+ check_both_ways2("\xfd\xdf", "EUC-JP", "\xef\x7e", "Shift_JIS")
+ check_both_ways2("\xfd\xe0", "EUC-JP", "\xef\x80", "Shift_JIS")
+ check_both_ways2("\xfd\xfe", "EUC-JP", "\xef\x9e", "Shift_JIS")
+ check_both_ways2("\xfe\xa1", "EUC-JP", "\xef\x9f", "Shift_JIS")
+ check_both_ways2("\xfe\xfe", "EUC-JP", "\xef\xfc", "Shift_JIS")
+ end
+
+ def test_eucjp_sjis_undef
+ assert_raise(Encoding::UndefinedConversionError) { "\x8e\xe0".encode("Shift_JIS", "EUC-JP") }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8e\xfe".encode("Shift_JIS", "EUC-JP") }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8f\xa1\xa1".encode("Shift_JIS", "EUC-JP") }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8f\xa1\xfe".encode("Shift_JIS", "EUC-JP") }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8f\xfe\xa1".encode("Shift_JIS", "EUC-JP") }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8f\xfe\xfe".encode("Shift_JIS", "EUC-JP") }
+
+ assert_raise(Encoding::UndefinedConversionError) { "\xf0\x40".encode("EUC-JP", "Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\xf0\x7e".encode("EUC-JP", "Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\xf0\x80".encode("EUC-JP", "Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\xf0\xfc".encode("EUC-JP", "Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\xfc\x40".encode("EUC-JP", "Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\xfc\x7e".encode("EUC-JP", "Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\xfc\x80".encode("EUC-JP", "Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\xfc\xfc".encode("EUC-JP", "Shift_JIS") }
+ end
+
+ def test_iso_2022_jp
+ assert_raise(Encoding::InvalidByteSequenceError) { "\x1b(A".encode("utf-8", "iso-2022-jp") }
+ assert_raise(Encoding::InvalidByteSequenceError) { "\x1b$(A".encode("utf-8", "iso-2022-jp") }
+ assert_raise(Encoding::InvalidByteSequenceError) { "\x1b$C".encode("utf-8", "iso-2022-jp") }
+ assert_raise(Encoding::InvalidByteSequenceError) { "\x0e".encode("utf-8", "iso-2022-jp") }
+ assert_raise(Encoding::InvalidByteSequenceError) { "\x80".encode("utf-8", "iso-2022-jp") }
+ assert_raise(Encoding::InvalidByteSequenceError) { "\x1b$(Dd!\x1b(B".encode("utf-8", "iso-2022-jp") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u9299".encode("iso-2022-jp") }
+ assert_raise(Encoding::UndefinedConversionError) { "\uff71\uff72\uff73\uff74\uff75".encode("iso-2022-jp") }
+ assert_raise(Encoding::InvalidByteSequenceError) { "\x1b(I12345\x1b(B".encode("utf-8", "iso-2022-jp") }
+ assert_equal("\xA1\xA1".force_encoding("euc-jp"),
+ "\e$B!!\e(B".encode("EUC-JP", "ISO-2022-JP"))
+ assert_equal("\e$B!!\e(B".force_encoding("ISO-2022-JP"),
+ "\xA1\xA1".encode("ISO-2022-JP", "EUC-JP"))
+ end
+
+ def test_from_cp50221
+ assert_equal("!", "\e(B\x21".encode("utf-8", "cp50221"))
+ assert_equal("!", "\e(J\x21".encode("utf-8", "cp50221"))
+ assert_equal("\uFF71", "\xB1".encode("utf-8", "cp50221"))
+ assert_equal("\uFF71", "\e(B\xB1".encode("utf-8", "cp50221"))
+ assert_equal("\uFF71", "\e(J\xB1".encode("utf-8", "cp50221"))
+ assert_equal("\uFF71", "\e(I\xB1".encode("utf-8", "cp50221"))
+ assert_equal("\uFF71", "\e(I\x31".encode("utf-8", "cp50221"))
+ assert_equal("\uFF71", "\x0E\xB1".encode("utf-8", "cp50221"))
+ assert_equal("\u3000", "\e$@\x21\x21".encode("utf-8", "cp50221"))
+ assert_equal("\u3000", "\e$B\x21\x21".encode("utf-8", "cp50221"))
+ assert_equal("\u2460", "\e$B\x2D\x21".encode("utf-8", "cp50221"))
+ assert_equal("\u7e8a", "\e$B\x79\x21".encode("utf-8", "cp50221"))
+ assert_equal("\u5fde", "\e$B\x7A\x21".encode("utf-8", "cp50221"))
+ assert_equal("\u72be", "\e$B\x7B\x21".encode("utf-8", "cp50221"))
+ assert_equal("\u91d7", "\e$B\x7C\x21".encode("utf-8", "cp50221"))
+ assert_equal("\xA1\xDF".force_encoding("sjis"),
+ "\e(I!_\e(B".encode("sjis","cp50220"))
+ end
+
+ def test_to_cp50221
+ assert_equal("\e$B!#!,\e(B".force_encoding("cp50220"),
+ "\xA1\xDF".encode("cp50220","sjis"))
+ assert_equal("\e$B%*!+%,%I%J!+%N!+%P%\\%^!+%Q%]%\"\e(B".force_encoding("cp50220"),
+ "\xB5\xDE\xB6\xDE\xC4\xDE\xC5\xDE\xC9\xDE\xCA\xDE\xCE\xDE\xCF\xDE\xCA\xDF\xCE\xDF\xB1".
+ encode("cp50220", "sjis"))
+ end
+
+ def test_iso_2022_jp_1
+ # check_both_ways("\u9299", "\x1b$(Dd!\x1b(B", "iso-2022-jp-1") # JIS X 0212 区68 点01 銙
+ end
+
+ def test_unicode_public_review_issue_121 # see http://www.unicode.org/review/pr-121.html
+ assert_equal("\x00\x61\xFF\xFD\xFF\xFD\xFF\xFD\x00\x62".force_encoding('UTF-16BE'),
+ "\x61\xF1\x80\x80\xE1\x80\xC2\x62".encode('UTF-16BE', 'UTF-8', invalid: :replace)) # option 2
+ assert_equal("\x61\x00\xFD\xFF\xFD\xFF\xFD\xFF\x62\x00".force_encoding('UTF-16LE'),
+ "\x61\xF1\x80\x80\xE1\x80\xC2\x62".encode('UTF-16LE', 'UTF-8', invalid: :replace)) # option 2
+
+ # additional clarification
+ assert_equal("\xFF\xFD\xFF\xFD\xFF\xFD\xFF\xFD".force_encoding('UTF-16BE'),
+ "\xF0\x80\x80\x80".encode('UTF-16BE', 'UTF-8', invalid: :replace))
+ assert_equal("\xFD\xFF\xFD\xFF\xFD\xFF\xFD\xFF".force_encoding('UTF-16LE'),
+ "\xF0\x80\x80\x80".encode('UTF-16LE', 'UTF-8', invalid: :replace))
+ end
+
+ def test_yen_sign
+ check_both_ways("\u005C", "\x5C", "Shift_JIS")
+ check_both_ways("\u005C", "\x5C", "Windows-31J")
+ check_both_ways("\u005C", "\x5C", "EUC-JP")
+ check_both_ways("\u005C", "\x5C", "eucJP-ms")
+ check_both_ways("\u005C", "\x5C", "CP51932")
+ check_both_ways("\u005C", "\x5C", "ISO-2022-JP")
+ assert_equal("\u005C", "\e(B\x5C\e(B".encode("UTF-8", "ISO-2022-JP"))
+ assert_equal("\u005C", "\e(J\x5C\e(B".encode("UTF-8", "ISO-2022-JP"))
+ assert_equal("\u005C", "\x5C".encode("stateless-ISO-2022-JP", "ISO-2022-JP"))
+ assert_equal("\u005C", "\e(J\x5C\e(B".encode("stateless-ISO-2022-JP", "ISO-2022-JP"))
+ assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("Windows-31J") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("EUC-JP") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("eucJP-ms") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u00A5".encode("CP51932") }
+
+ # FULLWIDTH REVERSE SOLIDUS
+ check_both_ways("\uFF3C", "\x81\x5F", "Shift_JIS")
+ check_both_ways("\uFF3C", "\x81\x5F", "Windows-31J")
+ check_both_ways("\uFF3C", "\xA1\xC0", "EUC-JP")
+ check_both_ways("\uFF3C", "\xA1\xC0", "eucJP-ms")
+ check_both_ways("\uFF3C", "\xA1\xC0", "CP51932")
+ end
+
+ def test_tilde_overline
+ check_both_ways("\u007E", "\x7E", "Shift_JIS")
+ check_both_ways("\u007E", "\x7E", "Windows-31J")
+ check_both_ways("\u007E", "\x7E", "EUC-JP")
+ check_both_ways("\u007E", "\x7E", "eucJP-ms")
+ check_both_ways("\u007E", "\x7E", "CP51932")
+ check_both_ways("\u007E", "\x7E", "ISO-2022-JP")
+ assert_equal("\u007E", "\e(B\x7E\e(B".encode("UTF-8", "ISO-2022-JP"))
+ assert_equal("\u007E", "\e(J\x7E\e(B".encode("UTF-8", "ISO-2022-JP"))
+ assert_equal("\u007E", "\x7E".encode("stateless-ISO-2022-JP", "ISO-2022-JP"))
+ assert_equal("\u007E", "\e(J\x7E\e(B".encode("stateless-ISO-2022-JP", "ISO-2022-JP"))
+ assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("Shift_JIS") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("Windows-31J") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("EUC-JP") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("eucJP-ms") }
+ assert_raise(Encoding::UndefinedConversionError) { "\u203E".encode("CP51932") }
+ end
+
+ def test_gb2312
+ check_both_ways("\u3000", "\xA1\xA1", 'GB2312') # full-width space
+ check_both_ways("\u3013", "\xA1\xFE", 'GB2312') # 〓
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xB0".encode("utf-8", 'GB2312') }
+ check_both_ways("\u2488", "\xA2\xB1", 'GB2312') # ⒈
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xE4".encode("utf-8", 'GB2312') }
+ check_both_ways("\u3220", "\xA2\xE5", 'GB2312') # ㈠
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xF0".encode("utf-8", 'GB2312') }
+ check_both_ways("\u2160", "\xA2\xF1", 'GB2312') # Ⅰ
+ check_both_ways("\uFF01", "\xA3\xA1", 'GB2312') # !
+ check_both_ways("\uFFE3", "\xA3\xFE", 'GB2312') #  ̄
+ check_both_ways("\u3041", "\xA4\xA1", 'GB2312') # ぁ
+ check_both_ways("\u30A1", "\xA5\xA1", 'GB2312') # ァ
+ check_both_ways("\u0391", "\xA6\xA1", 'GB2312') # Α
+ check_both_ways("\u03B1", "\xA6\xC1", 'GB2312') # α
+ check_both_ways("\u0410", "\xA7\xA1", 'GB2312') # А
+ check_both_ways("\u0430", "\xA7\xD1", 'GB2312') # а
+ check_both_ways("\u0101", "\xA8\xA1", 'GB2312') # ā
+ assert_raise(Encoding::UndefinedConversionError) { "\xA8\xC4".encode("utf-8", 'GB2312') }
+ check_both_ways("\u3105", "\xA8\xC5", 'GB2312') # ㄅ
+ assert_raise(Encoding::UndefinedConversionError) { "\xA9\xA3".encode("utf-8", 'GB2312') }
+ check_both_ways("\u2500", "\xA9\xA4", 'GB2312') # ─
+ check_both_ways("\u554A", "\xB0\xA1", 'GB2312') # 啊
+ check_both_ways("\u5265", "\xB0\xFE", 'GB2312') # 剥
+ check_both_ways("\u4FCA", "\xBF\xA1", 'GB2312') # 俊
+ check_both_ways("\u5080", "\xBF\xFE", 'GB2312') # 傀
+ check_both_ways("\u9988", "\xC0\xA1", 'GB2312') # 馈
+ check_both_ways("\u4FD0", "\xC0\xFE", 'GB2312') # 俐
+ check_both_ways("\u7A00", "\xCF\xA1", 'GB2312') # 稀
+ check_both_ways("\u6653", "\xCF\xFE", 'GB2312') # 晓
+ check_both_ways("\u5C0F", "\xD0\xA1", 'GB2312') # 小
+ check_both_ways("\u7384", "\xD0\xFE", 'GB2312') # 玄
+ check_both_ways("\u4F4F", "\xD7\xA1", 'GB2312') # 住
+ check_both_ways("\u5EA7", "\xD7\xF9", 'GB2312') # 座
+ assert_raise(Encoding::UndefinedConversionError) { "\xD7\xFA".encode("utf-8", 'GB2312') }
+ check_both_ways("\u647A", "\xDF\xA1", 'GB2312') # 摺
+ check_both_ways("\u553C", "\xDF\xFE", 'GB2312') # 唼
+ check_both_ways("\u5537", "\xE0\xA1", 'GB2312') # 唷
+ check_both_ways("\u5E3C", "\xE0\xFE", 'GB2312') # 帼
+ check_both_ways("\u94E9", "\xEF\xA1", 'GB2312') # 铩
+ check_both_ways("\u7A14", "\xEF\xFE", 'GB2312') # 稔
+ check_both_ways("\u7A39", "\xF0\xA1", 'GB2312') # 稹
+ check_both_ways("\u7619", "\xF0\xFE", 'GB2312') # 瘙
+ check_both_ways("\u9CCC", "\xF7\xA1", 'GB2312') # 鳌
+ check_both_ways("\u9F44", "\xF7\xFE", 'GB2312') # 齄
+ check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\xC7\xE0\xC9\xBD\xD1\xA7\xD4\xBA\xB4\xF3\xD1\xA7", 'GB2312') # 青山学院大学
+ end
+
+ def test_gbk
+ check_both_ways("\u4E02", "\x81\x40", 'GBK') # 丂
+ check_both_ways("\u4E8A", "\x81\x7E", 'GBK') # 亊
+ check_both_ways("\u4E90", "\x81\x80", 'GBK') # 亐
+ check_both_ways("\u4FA2", "\x81\xFE", 'GBK') # 侢
+ check_both_ways("\u5EC6", "\x8F\x40", 'GBK') # 廆
+ check_both_ways("\u5F24", "\x8F\x7E", 'GBK') # 弤
+ check_both_ways("\u5F28", "\x8F\x80", 'GBK') # 弨
+ check_both_ways("\u6007", "\x8F\xFE", 'GBK') # 怇
+ check_both_ways("\u6008", "\x90\x40", 'GBK') # 怈
+ check_both_ways("\u6080", "\x90\x7E", 'GBK') # 悀
+ check_both_ways("\u6081", "\x90\x80", 'GBK') # 悁
+ check_both_ways("\u6146", "\x90\xFE", 'GBK') # 慆
+ check_both_ways("\u70DC", "\x9F\x40", 'GBK') # 烜
+ check_both_ways("\u7134", "\x9F\x7E", 'GBK') # 焴
+ check_both_ways("\u7135", "\x9F\x80", 'GBK') # 焵
+ check_both_ways("\u71D3", "\x9F\xFE", 'GBK') # 燓
+ check_both_ways("\u71D6", "\xA0\x40", 'GBK') # 燖
+ check_both_ways("\u721A", "\xA0\x7E", 'GBK') # 爚
+ check_both_ways("\u721B", "\xA0\x80", 'GBK') # 爛
+ check_both_ways("\u72DB", "\xA0\xFE", 'GBK') # 狛
+ check_both_ways("\u3000", "\xA1\xA1", 'GBK') # full-width space
+ check_both_ways("\u3001", "\xA1\xA2", 'GBK') # 、
+ check_both_ways("\u3013", "\xA1\xFE", 'GBK') # 〓
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xA0".encode("utf-8", 'GBK') }
+ check_both_ways("\u2170", "\xA2\xA1", 'GBK') # ⅰ
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xB0".encode("utf-8", 'GBK') }
+ check_both_ways("\u2488", "\xA2\xB1", 'GBK') # ⒈
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xE4".encode("utf-8", 'GBK') }
+ check_both_ways("\u3220", "\xA2\xE5", 'GBK') # ㈠
+ assert_raise(Encoding::UndefinedConversionError) { "\xA2\xF0".encode("utf-8", 'GBK') }
+ check_both_ways("\u2160", "\xA2\xF1", 'GBK') # Ⅰ
+ assert_raise(Encoding::UndefinedConversionError) { "\xA3\xA0".encode("utf-8", 'GBK') }
+ check_both_ways("\uFF01", "\xA3\xA1", 'GBK') # !
+ check_both_ways("\uFFE3", "\xA3\xFE", 'GBK') #  ̄
+ assert_raise(Encoding::UndefinedConversionError) { "\xA4\xA0".encode("utf-8", 'GBK') }
+ check_both_ways("\u3041", "\xA4\xA1", 'GBK') # ぁ
+ assert_raise(Encoding::UndefinedConversionError) { "\xA5\xA0".encode("utf-8", 'GBK') }
+ check_both_ways("\u30A1", "\xA5\xA1", 'GBK') # ァ
+ check_both_ways("\u0391", "\xA6\xA1", 'GBK') # Α
+ check_both_ways("\u03B1", "\xA6\xC1", 'GBK') # α
+ assert_raise(Encoding::UndefinedConversionError) { "\xA6\xED".encode("utf-8", 'GBK') }
+ check_both_ways("\uFE3B", "\xA6\xEE", 'GBK') # ︻
+ check_both_ways("\u0410", "\xA7\xA1", 'GBK') # А
+ check_both_ways("\u0430", "\xA7\xD1", 'GBK') # а
+ check_both_ways("\u02CA", "\xA8\x40", 'GBK') # ˊ
+ check_both_ways("\u2587", "\xA8\x7E", 'GBK') # ▇
+ assert_raise(Encoding::UndefinedConversionError) { "\xA8\x96".encode("utf-8", 'GBK') }
+ check_both_ways("\u0101", "\xA8\xA1", 'GBK') # ā
+ assert_raise(Encoding::UndefinedConversionError) { "\xA8\xBC".encode("utf-8", 'GBK') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA8\xBF".encode("utf-8", 'GBK') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA8\xC4".encode("utf-8", 'GBK') }
+ check_both_ways("\u3105", "\xA8\xC5", 'GBK') # ㄅ
+ check_both_ways("\u3021", "\xA9\x40", 'GBK') # 〡
+ assert_raise(Encoding::UndefinedConversionError) { "\xA9\x58".encode("utf-8", 'GBK') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA9\x5B".encode("utf-8", 'GBK') }
+ assert_raise(Encoding::UndefinedConversionError) { "\xA9\x5D".encode("utf-8", 'GBK') }
+ check_both_ways("\u3007", "\xA9\x96", 'GBK') # 〇
+ assert_raise(Encoding::UndefinedConversionError) { "\xA9\xA3".encode("utf-8", 'GBK') }
+ check_both_ways("\u2500", "\xA9\xA4", 'GBK') # ─
+ assert_raise(Encoding::UndefinedConversionError) { "\xA9\xF0".encode("utf-8", 'GBK') }
+ check_both_ways("\u7588", "\xAF\x40", 'GBK') # 疈
+ check_both_ways("\u7607", "\xAF\x7E", 'GBK') # 瘇
+ check_both_ways("\u7608", "\xAF\x80", 'GBK') # 瘈
+ check_both_ways("\u7644", "\xAF\xA0", 'GBK') # 癄
+ assert_raise(Encoding::UndefinedConversionError) { "\xAF\xA1".encode("utf-8", 'GBK') }
+ check_both_ways("\u7645", "\xB0\x40", 'GBK') # 癅
+ check_both_ways("\u769B", "\xB0\x7E", 'GBK') # 皛
+ check_both_ways("\u769C", "\xB0\x80", 'GBK') # 皜
+ check_both_ways("\u5265", "\xB0\xFE", 'GBK') # 剥
+ check_both_ways("\u7DFB", "\xBF\x40", 'GBK') # 緻
+ check_both_ways("\u7E39", "\xBF\x7E", 'GBK') # 縹
+ check_both_ways("\u7E3A", "\xBF\x80", 'GBK') # 縺
+ check_both_ways("\u5080", "\xBF\xFE", 'GBK') # 傀
+ check_both_ways("\u7E5E", "\xC0\x40", 'GBK') # 繞
+ check_both_ways("\u7E9E", "\xC0\x7E", 'GBK') # 纞
+ check_both_ways("\u7EAE", "\xC0\x80", 'GBK') # 纮
+ check_both_ways("\u4FD0", "\xC0\xFE", 'GBK') # 俐
+ check_both_ways("\u87A5", "\xCF\x40", 'GBK') # 螥
+ check_both_ways("\u87F8", "\xCF\x7E", 'GBK') # 蟸
+ check_both_ways("\u87FA", "\xCF\x80", 'GBK') # 蟺
+ check_both_ways("\u6653", "\xCF\xFE", 'GBK') # 晓
+ check_both_ways("\u8824", "\xD0\x40", 'GBK') # 蠤
+ check_both_ways("\u887A", "\xD0\x7E", 'GBK') # 衺
+ check_both_ways("\u887B", "\xD0\x80", 'GBK') # 衻
+ check_both_ways("\u7384", "\xD0\xFE", 'GBK') # 玄
+ check_both_ways("\u9019", "\xDF\x40", 'GBK') # 這
+ check_both_ways("\u9081", "\xDF\x7E", 'GBK') # 邁
+ check_both_ways("\u9084", "\xDF\x80", 'GBK') # 還
+ check_both_ways("\u553C", "\xDF\xFE", 'GBK') # 唼
+ check_both_ways("\u90C2", "\xE0\x40", 'GBK') # 郂
+ check_both_ways("\u911C", "\xE0\x7E", 'GBK') # 鄜
+ check_both_ways("\u911D", "\xE0\x80", 'GBK') # 鄝
+ check_both_ways("\u5E3C", "\xE0\xFE", 'GBK') # 帼
+ check_both_ways("\u986F", "\xEF\x40", 'GBK') # 顯
+ check_both_ways("\u98E4", "\xEF\x7E", 'GBK') # 飤
+ check_both_ways("\u98E5", "\xEF\x80", 'GBK') # 飥
+ check_both_ways("\u7A14", "\xEF\xFE", 'GBK') # 稔
+ check_both_ways("\u9908", "\xF0\x40", 'GBK') # 餈
+ check_both_ways("\u9949", "\xF0\x7E", 'GBK') # 饉
+ check_both_ways("\u994A", "\xF0\x80", 'GBK') # 饊
+ check_both_ways("\u7619", "\xF0\xFE", 'GBK') # 瘙
+ check_both_ways("\u9F32", "\xFD\x40", 'GBK') # 鼲
+ check_both_ways("\u9F78", "\xFD\x7E", 'GBK') # 齸
+ check_both_ways("\u9F79", "\xFD\x80", 'GBK') # 齹
+ check_both_ways("\uF9F1", "\xFD\xA0", 'GBK') # 隣
+ assert_raise(Encoding::UndefinedConversionError) { "\xFD\xA1".encode("utf-8", 'GBK') }
+ check_both_ways("\uFA0C", "\xFE\x40", 'GBK') # 兀
+ check_both_ways("\uFA29", "\xFE\x4F", 'GBK') # 﨩
+ assert_raise(Encoding::UndefinedConversionError) { "\xFE\x50".encode("utf-8", 'GBK') }
+ check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\xC7\xE0\xC9\xBD\xD1\xA7\xD4\xBA\xB4\xF3\xD1\xA7", 'GBK') # 青山学院大学
+ check_both_ways("\u795E\u6797\u7FA9\u535A", "\xC9\xF1\xC1\xD6\xC1\x78\xB2\xA9", 'GBK') # 神林義博
+ end
+
+ def test_gb18030
+ # overall roundtrip test
+ all_unicode = (0x0..0xD7FF).to_a.pack 'U*' #追加
+ all_unicode << (0xE000..0xFFFF).to_a.pack("U*") #追加
+
+ assert_equal(all_unicode, all_unicode.encode("gb18030").encode("UTF-8")) #追加
+
+ # tests from GBK
+ check_both_ways("\u4E02", "\x81\x40", 'GB18030') #
+ check_both_ways("\u4E8A", "\x81\x7E", 'GB18030') #
+ check_both_ways("\u4E90", "\x81\x80", 'GB18030') #
+ check_both_ways("\u4FA2", "\x81\xFE", 'GB18030') # 侢
+ check_both_ways("\u5EC6", "\x8F\x40", 'GB18030') #
+ check_both_ways("\u5F24", "\x8F\x7E", 'GB18030') # 弤
+ check_both_ways("\u5F28", "\x8F\x80", 'GB18030') # 弨
+ check_both_ways("\u6007", "\x8F\xFE", 'GB18030') #
+ check_both_ways("\u6008", "\x90\x40", 'GB18030') #
+ check_both_ways("\u6080", "\x90\x7E", 'GB18030') # 悀
+ check_both_ways("\u6081", "\x90\x80", 'GB18030') #
+ check_both_ways("\u6146", "\x90\xFE", 'GB18030') #
+ check_both_ways("\u70DC", "\x9F\x40", 'GB18030') #
+ check_both_ways("\u7134", "\x9F\x7E", 'GB18030') # 焴
+ check_both_ways("\u7135", "\x9F\x80", 'GB18030') # 焵
+ check_both_ways("\u71D3", "\x9F\xFE", 'GB18030') #
+ check_both_ways("\u71D6", "\xA0\x40", 'GB18030') #
+ check_both_ways("\u721A", "\xA0\x7E", 'GB18030') #
+ check_both_ways("\u721B", "\xA0\x80", 'GB18030') #
+ check_both_ways("\u72DB", "\xA0\xFE", 'GB18030') #
+ check_both_ways("\u3000", "\xA1\xA1", 'GB18030') # full-width space
+ check_both_ways("\u3001", "\xA1\xA2", 'GB18030') #
+ check_both_ways("\u3013", "\xA1\xFE", 'GB18030') #
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA2\xA0".encode("utf-8", 'GB18030') }
+ check_both_ways("\u2170", "\xA2\xA1", 'GB18030') # ⅰ
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA2\xB0".encode("utf-8", 'GB18030') }
+ check_both_ways("\u2488", "\xA2\xB1", 'GB18030') #
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA2\xE4".encode("utf-8", 'GB18030') }
+ check_both_ways("\u3220", "\xA2\xE5", 'GB18030') # ㈠
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA2\xF0".encode("utf-8", 'GB18030') }
+ check_both_ways("\u2160", "\xA2\xF1", 'GB18030') # Ⅰ
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA3\xA0".encode("utf-8", 'GB18030') }
+ check_both_ways("\uFF01", "\xA3\xA1", 'GB18030') # E
+ check_both_ways("\uFFE3", "\xA3\xFE", 'GB18030') # E
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA4\xA0".encode("utf-8", 'GB18030') }
+ check_both_ways("\u3041", "\xA4\xA1", 'GB18030') #
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA5\xA0".encode("utf-8", 'GB18030') }
+ check_both_ways("\u30A1", "\xA5\xA1", 'GB18030') # ァ
+ check_both_ways("\u0391", "\xA6\xA1", 'GB18030') #
+ check_both_ways("\u03B1", "\xA6\xC1", 'GB18030') # α
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA6\xED".encode("utf-8", 'GB18030') }
+ check_both_ways("\uFE3B", "\xA6\xEE", 'GB18030') # E
+ check_both_ways("\u0410", "\xA7\xA1", 'GB18030') #
+ check_both_ways("\u0430", "\xA7\xD1", 'GB18030') # а
+ check_both_ways("\u02CA", "\xA8\x40", 'GB18030') #
+ check_both_ways("\u2587", "\xA8\x7E", 'GB18030') #
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA8\x96".encode("utf-8", 'GB18030') }
+ check_both_ways("\u0101", "\xA8\xA1", 'GB18030') #
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA8\xBC".encode("utf-8", 'GB18030') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA8\xBF".encode("utf-8", 'GB18030') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA8\xC4".encode("utf-8", 'GB18030') }
+ check_both_ways("\u3105", "\xA8\xC5", 'GB18030') #
+ check_both_ways("\u3021", "\xA9\x40", 'GB18030') # 〡
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA9\x58".encode("utf-8", 'GB18030') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA9\x5B".encode("utf-8", 'GB18030') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA9\x5D".encode("utf-8", 'GB18030') }
+ check_both_ways("\u3007", "\xA9\x96", 'GB18030') #
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA9\xA3".encode("utf-8", 'GB18030') }
+ check_both_ways("\u2500", "\xA9\xA4", 'GB18030') # ─
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA9\xF0".encode("utf-8", 'GB18030') }
+ check_both_ways("\u7588", "\xAF\x40", 'GB18030') #
+ check_both_ways("\u7607", "\xAF\x7E", 'GB18030') #
+ check_both_ways("\u7608", "\xAF\x80", 'GB18030') #
+ check_both_ways("\u7644", "\xAF\xA0", 'GB18030') #
+ #assert_raise(Encoding::UndefinedConversionError) { "\xAF\xA1".encode("utf-8", 'GB18030') }
+ check_both_ways("\u7645", "\xB0\x40", 'GB18030') #
+ check_both_ways("\u769B", "\xB0\x7E", 'GB18030') #
+ check_both_ways("\u769C", "\xB0\x80", 'GB18030') #
+ check_both_ways("\u5265", "\xB0\xFE", 'GB18030') # 剥
+ check_both_ways("\u7DFB", "\xBF\x40", 'GB18030') # 緻
+ check_both_ways("\u7E39", "\xBF\x7E", 'GB18030') # 縹
+ check_both_ways("\u7E3A", "\xBF\x80", 'GB18030') # 縺
+ check_both_ways("\u5080", "\xBF\xFE", 'GB18030') # 傀
+ check_both_ways("\u7E5E", "\xC0\x40", 'GB18030') #
+ check_both_ways("\u7E9E", "\xC0\x7E", 'GB18030') #
+ check_both_ways("\u7EAE", "\xC0\x80", 'GB18030') # 纮
+ check_both_ways("\u4FD0", "\xC0\xFE", 'GB18030') #
+ check_both_ways("\u87A5", "\xCF\x40", 'GB18030') # 螥
+ check_both_ways("\u87F8", "\xCF\x7E", 'GB18030') # 蟸
+ check_both_ways("\u87FA", "\xCF\x80", 'GB18030') # 蟺
+ check_both_ways("\u6653", "\xCF\xFE", 'GB18030') #
+ check_both_ways("\u8824", "\xD0\x40", 'GB18030') # 蠤
+ check_both_ways("\u887A", "\xD0\x7E", 'GB18030') # 衺
+ check_both_ways("\u887B", "\xD0\x80", 'GB18030') # 衻
+ check_both_ways("\u7384", "\xD0\xFE", 'GB18030') #
+ check_both_ways("\u9019", "\xDF\x40", 'GB18030') #
+ check_both_ways("\u9081", "\xDF\x7E", 'GB18030') #
+ check_both_ways("\u9084", "\xDF\x80", 'GB18030') #
+ check_both_ways("\u553C", "\xDF\xFE", 'GB18030') # 唼
+ check_both_ways("\u90C2", "\xE0\x40", 'GB18030') #
+ check_both_ways("\u911C", "\xE0\x7E", 'GB18030') #
+ check_both_ways("\u911D", "\xE0\x80", 'GB18030') #
+ check_both_ways("\u5E3C", "\xE0\xFE", 'GB18030') # 帼
+ check_both_ways("\u986F", "\xEF\x40", 'GB18030') # 顯
+ check_both_ways("\u98E4", "\xEF\x7E", 'GB18030') # 飤
+ check_both_ways("\u98E5", "\xEF\x80", 'GB18030') # 飥
+ check_both_ways("\u7A14", "\xEF\xFE", 'GB18030') #
+ check_both_ways("\u9908", "\xF0\x40", 'GB18030') #
+ check_both_ways("\u9949", "\xF0\x7E", 'GB18030') #
+ check_both_ways("\u994A", "\xF0\x80", 'GB18030') #
+ check_both_ways("\u7619", "\xF0\xFE", 'GB18030') #
+ check_both_ways("\u9F32", "\xFD\x40", 'GB18030') # 鼲
+ check_both_ways("\u9F78", "\xFD\x7E", 'GB18030') # 齸
+ check_both_ways("\u9F79", "\xFD\x80", 'GB18030') # 齹
+ check_both_ways("\uF9F1", "\xFD\xA0", 'GB18030') # E
+ #assert_raise(Encoding::UndefinedConversionError) { "\xFD\xA1".encode("utf-8", 'GB18030') }
+ check_both_ways("\uFA0C", "\xFE\x40", 'GB18030') # E
+ check_both_ways("\uFA29", "\xFE\x4F", 'GB18030') # E
+ #assert_raise(Encoding::UndefinedConversionError) { "\xFE\x50".encode("utf-8", 'GB18030') }
+ check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\xC7\xE0\xC9\xBD\xD1\xA7\xD4\xBA\xB4\xF3\xD1\xA7", 'GB18030') # 青山学院大学
+ check_both_ways("\u795E\u6797\u7FA9\u535A", "\xC9\xF1\xC1\xD6\xC1\x78\xB2\xA9", 'GB18030') # 神林義
+
+ # new tests for GB18030
+ check_both_ways("\u9FA6", "\x82\x35\x8F\x33", 'GB18030') # 龦
+ check_both_ways("\uD7FF", "\x83\x36\xC7\x38", 'GB18030') # No name ()
+
+ check_both_ways("\u0452", "\x81\x30\xD3\x30", 'GB18030') #
+ check_both_ways("\u200F", "\x81\x36\xA5\x31", 'GB18030') # RIGHT-TO-LEFT MARK
+
+ check_both_ways("\uE865", "\x83\x36\xD0\x30", 'GB18030') # No name (Private Use Area)
+ check_both_ways("\uF92B", "\x84\x30\x85\x34", 'GB18030') # E
+
+ check_both_ways("\u2643", "\x81\x37\xA8\x39", 'GB18030') #
+ check_both_ways("\u2E80", "\x81\x38\xFD\x38", 'GB18030') # ⺀
+
+ check_both_ways("\uFA2A", "\x84\x30\x9C\x38", 'GB18030') # E
+ check_both_ways("\uFE2F", "\x84\x31\x85\x37", 'GB18030') # No name (Combining Half Marks)
+
+ check_both_ways("\u3CE1", "\x82\x31\xD4\x38", 'GB18030') # 㳡
+ check_both_ways("\u4055", "\x82\x32\xAF\x32", 'GB18030') #
+
+ check_both_ways("\u361B", "\x82\x30\xA6\x33", 'GB18030') #
+ check_both_ways("\u3917", "\x82\x30\xF2\x37", 'GB18030') #
+
+ check_both_ways("\u49B8", "\x82\x34\xA1\x31", 'GB18030') # 䦸
+ check_both_ways("\u4C76", "\x82\x34\xE7\x33", 'GB18030') # 䱶
+
+ check_both_ways("\u4160", "\x82\x32\xC9\x37", 'GB18030') # 䅠
+ check_both_ways("\u4336", "\x82\x32\xF8\x37", 'GB18030') # 䌶
+
+ check_both_ways("\u478E", "\x82\x33\xE8\x38", 'GB18030') #
+ check_both_ways("\u4946", "\x82\x34\x96\x38", 'GB18030') #
+
+ check_both_ways("\u44D7", "\x82\x33\xA3\x39", 'GB18030') #
+ check_both_ways("\u464B", "\x82\x33\xC9\x31", 'GB18030') #
+
+ check_both_ways("\uFFE6", "\x84\x31\xA2\x34", 'GB18030') # E
+ check_both_ways("\uFFFF", "\x84\x31\xA4\x39", 'GB18030') # not a character
+
+ check_both_ways("\u{10000}", "\x90\x30\x81\x30", 'GB18030') # 𐀀
+ check_both_ways("\u{10FFFE}", "\xE3\x32\x9A\x34", 'GB18030') # No name (Not a character)
+ check_both_ways("\u{10FFFF}", "\xE3\x32\x9A\x35", 'GB18030') # No name (Not a character)
+ end
+
+ def test_Big5
+ check_both_ways("\u3000", "\xA1\x40", 'Big5') # full-width space
+ check_both_ways("\uFE5A", "\xA1\x7E", 'Big5') # ﹚
+ check_both_ways("\uFE5B", "\xA1\xA1", 'Big5') # ﹛
+ #check_both_ways("\uFF0F", "\xA1\xFE", 'Big5') # /
+ check_both_ways("\uFF57", "\xA3\x40", 'Big5') # w
+ check_both_ways("\u310F", "\xA3\x7E", 'Big5') # ㄏ
+ check_both_ways("\u3110", "\xA3\xA1", 'Big5') # ㄐ
+ check_both_ways("\u02CB", "\xA3\xBF", 'Big5') # ˋ
+ assert_raise(Encoding::UndefinedConversionError) { "\xA3\xC0".encode("utf-8", 'Big5') }
+ check_both_ways("\u6D6C", "\xAF\x40", 'Big5') # 浬
+ check_both_ways("\u7837", "\xAF\x7E", 'Big5') # 砷
+ check_both_ways("\u7825", "\xAF\xA1", 'Big5') # 砥
+ check_both_ways("\u8343", "\xAF\xFE", 'Big5') # 荃
+ check_both_ways("\u8654", "\xB0\x40", 'Big5') # 虔
+ check_both_ways("\u9661", "\xB0\x7E", 'Big5') # 陡
+ check_both_ways("\u965B", "\xB0\xA1", 'Big5') # 陛
+ check_both_ways("\u5A40", "\xB0\xFE", 'Big5') # 婀
+ check_both_ways("\u6FC3", "\xBF\x40", 'Big5') # 濃
+ check_both_ways("\u7E0A", "\xBF\x7E", 'Big5') # 縊
+ check_both_ways("\u7E11", "\xBF\xA1", 'Big5') # 縑
+ check_both_ways("\u931A", "\xBF\xFE", 'Big5') # 錚
+ check_both_ways("\u9310", "\xC0\x40", 'Big5') # 錐
+ check_both_ways("\u5687", "\xC0\x7E", 'Big5') # 嚇
+ check_both_ways("\u568F", "\xC0\xA1", 'Big5') # 嚏
+ check_both_ways("\u77AC", "\xC0\xFE", 'Big5') # 瞬
+ check_both_ways("\u8B96", "\xC6\x40", 'Big5') # 讖
+ check_both_ways("\u7C72", "\xC6\x7E", 'Big5') # 籲
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC6\xA1".encode("utf-8", 'Big5') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC7\x40".encode("utf-8", 'Big5') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC8\x40".encode("utf-8", 'Big5') }
+ check_both_ways("\u4E42", "\xC9\x40", 'Big5') # 乂
+ check_both_ways("\u6C15", "\xC9\x7E", 'Big5') # 氕
+ check_both_ways("\u6C36", "\xC9\xA1", 'Big5') # 氶
+ check_both_ways("\u6C4B", "\xC9\xFE", 'Big5') # 汋
+ check_both_ways("\u67DC", "\xCF\x40", 'Big5') # 柜
+ check_both_ways("\u6D42", "\xCF\x7E", 'Big5') # 浂
+ check_both_ways("\u6D01", "\xCF\xA1", 'Big5') # 洁
+ check_both_ways("\u7A80", "\xCF\xFE", 'Big5') # 窀
+ check_both_ways("\u7A7E", "\xD0\x40", 'Big5') # 穾
+ check_both_ways("\u82EA", "\xD0\x7E", 'Big5') # 苪
+ check_both_ways("\u82E4", "\xD0\xA1", 'Big5') # 苤
+ check_both_ways("\u54F1", "\xD0\xFE", 'Big5') # 哱
+ check_both_ways("\u7A1B", "\xDF\x40", 'Big5') # 稛
+ check_both_ways("\u816F", "\xDF\x7E", 'Big5') # 腯
+ check_both_ways("\u8144", "\xDF\xA1", 'Big5') # 腄
+ check_both_ways("\u89E4", "\xDF\xFE", 'Big5') # 觤
+ check_both_ways("\u89E1", "\xE0\x40", 'Big5') # 觡
+ check_both_ways("\u903F", "\xE0\x7E", 'Big5') # 逿
+ check_both_ways("\u9044", "\xE0\xA1", 'Big5') # 遄
+ check_both_ways("\u50E0", "\xE0\xFE", 'Big5') # 僠
+ check_both_ways("\u979E", "\xEF\x40", 'Big5') # 鞞
+ check_both_ways("\u9D30", "\xEF\x7E", 'Big5') # 鴰
+ check_both_ways("\u9D45", "\xEF\xA1", 'Big5') # 鵅
+ check_both_ways("\u7376", "\xEF\xFE", 'Big5') # 獶
+ check_both_ways("\u74B8", "\xF0\x40", 'Big5') # 璸
+ check_both_ways("\u81D2", "\xF0\x7E", 'Big5') # 臒
+ check_both_ways("\u81D0", "\xF0\xA1", 'Big5') # 臐
+ check_both_ways("\u8E67", "\xF0\xFE", 'Big5') # 蹧
+ check_both_ways("\u7E98", "\xF9\x40", 'Big5') # 纘
+ check_both_ways("\u9F0A", "\xF9\x7E", 'Big5') # 鼊
+ check_both_ways("\u9FA4", "\xF9\xA1", 'Big5') # 龤
+ check_both_ways("\u9F98", "\xF9\xD5", 'Big5') # 龘
+ #assert_raise(Encoding::UndefinedConversionError) { "\xF9\xD6".encode("utf-8", 'Big5') }
+ check_both_ways("\u795E\u6797\u7FA9\u535A", "\xAF\xAB\xAA\x4C\xB8\x71\xB3\xD5", 'Big5') # 神林義博
+ end
+
+ def test_Big5_Hkscs
+ check_both_ways("\u3000", "\xA1\x40", 'Big5-HKSCS') # full-width space
+ check_both_ways("\uFE5A", "\xA1\x7E", 'Big5-HKSCS') # ﹚
+ check_both_ways("\uFE5B", "\xA1\xA1", 'Big5-HKSCS') # ﹛
+ #check_both_ways("\uFF0F", "\xA1\xFE", 'Big5-HKSCS') # /
+ check_both_ways("\uFF57", "\xA3\x40", 'Big5-HKSCS') # w
+ check_both_ways("\u310F", "\xA3\x7E", 'Big5-HKSCS') # ㄏ
+ check_both_ways("\u3110", "\xA3\xA1", 'Big5-HKSCS') # ㄐ
+ check_both_ways("\u02CB", "\xA3\xBF", 'Big5-HKSCS') # ˋ
+ #assert_raise(Encoding::UndefinedConversionError) { "\xA3\xC0".encode("utf-8", 'Big5-HKSCS') }
+ check_both_ways("\u6D6C", "\xAF\x40", 'Big5-HKSCS') # 浬
+ check_both_ways("\u7837", "\xAF\x7E", 'Big5-HKSCS') # 砷
+ check_both_ways("\u7825", "\xAF\xA1", 'Big5-HKSCS') # 砥
+ check_both_ways("\u8343", "\xAF\xFE", 'Big5-HKSCS') # 荃
+ check_both_ways("\u8654", "\xB0\x40", 'Big5-HKSCS') # 虔
+ check_both_ways("\u9661", "\xB0\x7E", 'Big5-HKSCS') # 陡
+ check_both_ways("\u965B", "\xB0\xA1", 'Big5-HKSCS') # 陛
+ check_both_ways("\u5A40", "\xB0\xFE", 'Big5-HKSCS') # 婀
+ check_both_ways("\u6FC3", "\xBF\x40", 'Big5-HKSCS') # 濃
+ check_both_ways("\u7E0A", "\xBF\x7E", 'Big5-HKSCS') # 縊
+ check_both_ways("\u7E11", "\xBF\xA1", 'Big5-HKSCS') # 縑
+ check_both_ways("\u931A", "\xBF\xFE", 'Big5-HKSCS') # 錚
+ check_both_ways("\u9310", "\xC0\x40", 'Big5-HKSCS') # 錐
+ check_both_ways("\u5687", "\xC0\x7E", 'Big5-HKSCS') # 嚇
+ check_both_ways("\u568F", "\xC0\xA1", 'Big5-HKSCS') # 嚏
+ check_both_ways("\u77AC", "\xC0\xFE", 'Big5-HKSCS') # 瞬
+ check_both_ways("\u8B96", "\xC6\x40", 'Big5-HKSCS') # 讖
+ check_both_ways("\u7C72", "\xC6\x7E", 'Big5-HKSCS') # 籲
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC6\xA1".encode("utf-8", 'Big5-HKSCS') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC7\x40".encode("utf-8", 'Big5-HKSCS') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC8\x40".encode("utf-8", 'Big5-HKSCS') }
+ check_both_ways("\u4E42", "\xC9\x40", 'Big5-HKSCS') # 乂
+ check_both_ways("\u6C15", "\xC9\x7E", 'Big5-HKSCS') # 氕
+ check_both_ways("\u6C36", "\xC9\xA1", 'Big5-HKSCS') # 氶
+ check_both_ways("\u6C4B", "\xC9\xFE", 'Big5-HKSCS') # 汋
+ check_both_ways("\u67DC", "\xCF\x40", 'Big5-HKSCS') # 柜
+ check_both_ways("\u6D42", "\xCF\x7E", 'Big5-HKSCS') # 浂
+ check_both_ways("\u6D01", "\xCF\xA1", 'Big5-HKSCS') # 洁
+ check_both_ways("\u7A80", "\xCF\xFE", 'Big5-HKSCS') # 窀
+ check_both_ways("\u7A7E", "\xD0\x40", 'Big5-HKSCS') # 穾
+ check_both_ways("\u82EA", "\xD0\x7E", 'Big5-HKSCS') # 苪
+ check_both_ways("\u82E4", "\xD0\xA1", 'Big5-HKSCS') # 苤
+ check_both_ways("\u54F1", "\xD0\xFE", 'Big5-HKSCS') # 哱
+ check_both_ways("\u7A1B", "\xDF\x40", 'Big5-HKSCS') # 稛
+ check_both_ways("\u816F", "\xDF\x7E", 'Big5-HKSCS') # 腯
+ check_both_ways("\u8144", "\xDF\xA1", 'Big5-HKSCS') # 腄
+ check_both_ways("\u89E4", "\xDF\xFE", 'Big5-HKSCS') # 觤
+ check_both_ways("\u89E1", "\xE0\x40", 'Big5-HKSCS') # 觡
+ check_both_ways("\u903F", "\xE0\x7E", 'Big5-HKSCS') # 逿
+ check_both_ways("\u9044", "\xE0\xA1", 'Big5-HKSCS') # 遄
+ check_both_ways("\u50E0", "\xE0\xFE", 'Big5-HKSCS') # 僠
+ check_both_ways("\u979E", "\xEF\x40", 'Big5-HKSCS') # 鞞
+ check_both_ways("\u9D30", "\xEF\x7E", 'Big5-HKSCS') # 鴰
+ check_both_ways("\u9D45", "\xEF\xA1", 'Big5-HKSCS') # 鵅
+ check_both_ways("\u7376", "\xEF\xFE", 'Big5-HKSCS') # 獶
+ check_both_ways("\u74B8", "\xF0\x40", 'Big5-HKSCS') # 璸
+ check_both_ways("\u81D2", "\xF0\x7E", 'Big5-HKSCS') # 臒
+ check_both_ways("\u81D0", "\xF0\xA1", 'Big5-HKSCS') # 臐
+ check_both_ways("\u8E67", "\xF0\xFE", 'Big5-HKSCS') # 蹧
+ check_both_ways("\u7E98", "\xF9\x40", 'Big5-HKSCS') # 纘
+ check_both_ways("\u9F0A", "\xF9\x7E", 'Big5-HKSCS') # 鼊
+ check_both_ways("\u9FA4", "\xF9\xA1", 'Big5-HKSCS') # 龤
+ check_both_ways("\u9F98", "\xF9\xD5", 'Big5-HKSCS') # 龘
+ #check_both_ways("\u{23ED7}", "\x8E\x40", 'Big5-HKSCS') # 𣻗
+ #assert_raise(Encoding::UndefinedConversionError) { "\xF9\xD6".encode("utf-8", 'Big5-HKSCS') }
+ check_both_ways("\u795E\u6797\u7FA9\u535A", "\xAF\xAB\xAA\x4C\xB8\x71\xB3\xD5", 'Big5-HKSCS') # 神林義博
+ end
+
+ def test_Big5_UAO
+ check_both_ways("\u4e17", "\x81\x40", 'Big5-UAO') # 丗
+ end
+
+ def test_nothing_changed
+ a = "James".force_encoding("US-ASCII")
+ b = a.encode("Shift_JIS")
+ assert_equal(Encoding::US_ASCII, a.encoding)
+ assert_equal(Encoding::Shift_JIS, b.encoding)
+ end
+
+ def test_utf8_mac
+ # composition exclusion
+ assert_equal("\u05DB\u05BF", "\u05DB\u05BF".encode("UTF-8", "UTF8-MAC")) #"\u{fb4d}"
+
+ assert_equal("\u{1ff7}", "\u03C9\u0342\u0345".encode("UTF-8", "UTF8-MAC"))
+
+ assert_equal("\u05DB\u05BF", "\u{fb4d}".encode("UTF8-MAC").force_encoding("UTF-8"))
+ assert_equal("\u03C9\u0342\u0345", "\u{1ff7}".encode("UTF8-MAC").force_encoding("UTF-8"))
+
+ check_both_ways("\u{e9 74 e8}", "e\u0301te\u0300", 'UTF8-MAC')
+ end
+
+ def test_fallback
+ assert_equal("\u3042".encode("EUC-JP"), "\u{20000}".encode("EUC-JP",
+ fallback: {"\u{20000}" => "\u3042".encode("EUC-JP")}))
+ assert_equal("\u3042".encode("EUC-JP"), "\u{20000}".encode("EUC-JP",
+ fallback: {"\u{20000}" => "\u3042"}))
+ assert_equal("[ISU]", "\u{1F4BA}".encode("SJIS-KDDI",
+ fallback: {"\u{1F4BA}" => "[ISU]"}))
+ end
+
+ def test_fallback_hash_default
+ fallback = Hash.new {|h, x| "U+%.4X" % x.unpack("U")}
+ assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback))
+ end
+
+ def test_fallback_proc
+ fallback = proc {|x| "U+%.4X" % x.unpack("U")}
+ assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback))
+ end
+
+ def test_fallback_method
+ def (fallback = "U+%.4X").escape(x)
+ self % x.unpack("U")
+ end
+ assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback.method(:escape)))
+ end
+
+ bug8940 = '[ruby-core:57318] [Bug #8940]'
+ %w[UTF-32 UTF-16].each do |enc|
+ define_method("test_pseudo_encoding_inspect(#{enc})") do
+ assert_normal_exit("'aaa'.encode('#{enc}').inspect", bug8940)
+ assert_equal(4, 'aaa'.encode(enc).length, "should count in #{enc} with BOM")
+ end
+ end
+
+ def test_encode_with_invalid_chars
+ bug8995 = '[ruby-dev:47747]'
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ str = "\xff".force_encoding('utf-8')
+ assert_equal str, str.encode, bug8995
+ assert_equal "\ufffd", str.encode(invalid: :replace), bug8995
+ end
+ end
+
+ def test_valid_dummy_encoding
+ bug9314 = '[ruby-core:59354] [Bug #9314]'
+ assert_separately(%W[- -- #{bug9314}], <<-'end;')
+ bug = ARGV.shift
+ result = assert_nothing_raised(TypeError, bug) {break "test".encode(Encoding::UTF_16)}
+ assert_equal("\xFE\xFF\x00t\x00e\x00s\x00t", result.b, bug)
+ result = assert_nothing_raised(TypeError, bug) {break "test".encode(Encoding::UTF_32)}
+ assert_equal("\x00\x00\xFE\xFF\x00\x00\x00t\x00\x00\x00e\x00\x00\x00s\x00\x00\x00t", result.b, bug)
+ end;
+ end
+
+ def test_loading_race
+ assert_separately([], <<-'end;') #do
+ bug11277 = '[ruby-dev:49106] [Bug #11277]'
+ num = 2
+ th = (0...num).map do |i|
+ Thread.new {"\u3042".encode("EUC-JP")}
+ end
+ result = nil
+ assert_warning("", bug11277) do
+ assert_nothing_raised(Encoding::ConverterNotFoundError, bug11277) do
+ result = th.map(&:value)
+ end
+ end
+ expected = "\xa4\xa2".force_encoding(Encoding::EUC_JP)
+ assert_equal([expected]*num, result, bug11277)
+ end;
+ end
+
+ def test_universal_newline
+ bug11324 = '[ruby-core:69841] [Bug #11324]'
+ usascii = Encoding::US_ASCII
+ s = "A\nB\r\nC".force_encoding(usascii)
+ assert_equal("A\nB\nC", s.encode(usascii, universal_newline: true), bug11324)
+ assert_equal("A\nB\nC", s.encode(usascii, universal_newline: true, undef: :replace), bug11324)
+ assert_equal("A\nB\nC", s.encode(usascii, universal_newline: true, undef: :replace, replace: ''), bug11324)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_undef.rb b/jni/ruby/test/ruby/test_undef.rb
new file mode 100644
index 0000000..e1c9807
--- /dev/null
+++ b/jni/ruby/test/ruby/test_undef.rb
@@ -0,0 +1,37 @@
+require 'test/unit'
+
+class TestUndef < Test::Unit::TestCase
+ class Undef0
+ def foo
+ "foo"
+ end
+ undef foo
+ end
+
+ class Undef1
+ def bar
+ "bar"
+ end
+ end
+
+ class Undef2 < Undef1
+ undef bar
+ end
+
+ def test_undef
+ x = Undef0.new
+ assert_raise(NoMethodError) { x.foo }
+ y = Undef1.new
+ assert_equal "bar", y.bar
+ z = Undef2.new
+ assert_raise(NoMethodError) { z.foo }
+ end
+
+ def test_special_const_undef
+ assert_raise(TypeError) do
+ 1.instance_eval do
+ undef to_s
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/ruby/test_unicode_escape.rb b/jni/ruby/test/ruby/test_unicode_escape.rb
new file mode 100644
index 0000000..9d0d787
--- /dev/null
+++ b/jni/ruby/test/ruby/test_unicode_escape.rb
@@ -0,0 +1,270 @@
+# -*- coding: utf-8 -*-
+
+require 'test/unit'
+
+class TestUnicodeEscape < Test::Unit::TestCase
+ def test_basic
+ assert_equal('Matz - 松本行弘',
+ "Matz - \u677E\u672C\u884C\u5F18")
+ assert_equal('Matz - まつもと ゆきひろ',
+ "Matz - \u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D")
+ assert_equal('Matz - まつもと ゆきひろ',
+ "Matz - \u{307E}\u{3064}\u{3082}\u{3068} \u{3086}\u{304D}\u{3072}\u{308D}")
+ assert_equal('Matz - まつもと ゆきひろ',
+ "Matz - \u{307E 3064 3082 3068 20 3086 304D 3072 308D}")
+ assert_equal("Aoyama Gakuin University - \xE9\x9D\x92\xE5\xB1\xB1\xE5\xAD\xA6\xE9\x99\xA2\xE5\xA4\xA7\xE5\xAD\xA6",
+ "Aoyama Gakuin University - \u9752\u5C71\u5B66\u9662\u5927\u5B66")
+ assert_equal('Aoyama Gakuin University - 青山学院大学',
+ "Aoyama Gakuin University - \u9752\u5C71\u5B66\u9662\u5927\u5B66")
+ assert_equal('青山学院大学', "\u9752\u5C71\u5B66\u9662\u5927\u5B66")
+ assert_equal("Martin D\xC3\xBCrst", "Martin D\u00FCrst")
+ assert_equal('Martin Dürst', "Martin D\u00FCrst")
+ assert_equal('ü', "\u00FC")
+ assert_equal("Martin D\xC3\xBCrst", "Martin D\u{FC}rst")
+ assert_equal('Martin Dürst', "Martin D\u{FC}rst")
+ assert_equal('ü', "\u{FC}")
+ assert_equal('ü', %Q|\u{FC}|)
+ assert_equal('ü', %W{\u{FC}}[0])
+
+ # \u escapes in here documents
+ assert_equal('Matz - まつもと ゆきひろ', <<EOS.chop)
+Matz - \u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D
+EOS
+
+ assert_equal('Matz - まつもと ゆきひろ', <<"EOS".chop)
+Matz - \u{307E 3064 3082 3068} \u{3086 304D 3072 308D}
+EOS
+ assert_not_equal('Matz - まつもと ゆきひろ', <<'EOS'.chop)
+Matz - \u{307E 3064 3082 3068} \u{3086 304D 3072 308D}
+EOS
+
+ # single-quoted things don't expand \u
+ assert_not_equal('ü', '\u{FC}')
+ assert_not_equal('ü', %q|\u{FC}|)
+ assert_not_equal('ü', %w{\u{FC}}[0])
+ assert_equal('\u00fc', "\\" + "u00fc")
+
+ # \u in %x strings
+ assert_match(/^("?)A\1$/, `echo "\u0041"`) #"
+ assert_match(/^("?)A\1$/, %x{echo "\u0041"}) #"
+ assert_match(/^("?)ü\1$/,
+ `#{EnvUtil.rubybin} -e "#coding:utf-8\nputs \\"\\u{FC}\\""`.force_encoding("utf-8")) #"
+
+ # \u in quoted symbols
+ assert_equal(:A, :"\u0041")
+ assert_equal(:a, :"\u0061")
+ assert_equal(:ま, :ま)
+ assert_equal(:ü, :ü)
+ assert_equal(:"\u{41}", :"\u0041")
+ assert_equal(:ü, :"\u{fc}")
+
+ # the NUL character is allowed in symbols
+ bug = '[ruby-dev:41447]'
+ sym = "\0".to_sym
+ assert_nothing_raised(SyntaxError, bug) {assert_equal(sym, eval(%q(:"\u{0}")))}
+ assert_nothing_raised(SyntaxError, bug) {assert_equal(sym, eval(%q(:"\u0000")))}
+ assert_nothing_raised(SyntaxError, bug) {assert_equal("\u{fc}\0A".to_sym, eval(%q(:"\u{fc 0 0041}")))}
+ assert_nothing_raised(SyntaxError, bug) {assert_equal(sym, eval(%q(:"\x00")))}
+ assert_nothing_raised(SyntaxError, bug) {assert_equal(sym, eval(%q(:"\0")))}
+ end
+
+ def test_regexp
+
+ # Compare regexps to regexps
+ assert_not_equal(/Yukihiro Matsumoto - 松本行弘/,
+ /Yukihiro Matsumoto - \u677E\u672C\u884C\u5F18/)
+ assert_not_equal(/Yukihiro Matsumoto - 松本行弘/,
+ /Yukihiro Matsumoto - \u{677E 672C 884C 5F18}/)
+ assert_not_equal(/Matz - まつもと ゆきひろ/,
+ /Matz - \u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D/)
+ assert_not_equal(/Aoyama Gakuin University - 青山学院大学/,
+ /Aoyama Gakuin University - \u9752\u5C71\u5B66\u9662\u5927\u5B66/)
+ assert_not_equal(/青山学院大学/, /\u9752\u5C71\u5B66\u9662\u5927\u5B66/)
+ assert_not_equal(/Martin Dürst/, /Martin D\u00FCrst/)
+ assert_not_equal(/ü/, /\u00FC/)
+ assert_not_equal(/Martin Dürst/, /Martin D\u{FC}rst/)
+ assert_not_equal(/ü/, /\u{FC}/)
+ assert_not_equal(/ü/, %r{\u{FC}})
+ assert_not_equal(/ü/i, %r{\u00FC}i)
+
+ assert_equal('Yukihiro Matsumoto - \u677E\u672C\u884C\u5F18',
+ /Yukihiro Matsumoto - \u677E\u672C\u884C\u5F18/.source)
+ assert_equal('Yukihiro Matsumoto - \u{677E 672C 884C 5F18}',
+ /Yukihiro Matsumoto - \u{677E 672C 884C 5F18}/.source)
+ assert_equal('Matz - \u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D',
+ /Matz - \u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D/.source)
+ assert_equal('Aoyama Gakuin University - \u9752\u5C71\u5B66\u9662\u5927\u5B66',
+ /Aoyama Gakuin University - \u9752\u5C71\u5B66\u9662\u5927\u5B66/.source)
+ assert_equal('\u9752\u5C71\u5B66\u9662\u5927\u5B66',
+ /\u9752\u5C71\u5B66\u9662\u5927\u5B66/.source)
+ assert_equal('Martin D\u00FCrst', /Martin D\u00FCrst/.source)
+ assert_equal('\u00FC', /\u00FC/.source)
+ assert_equal('Martin D\u{FC}rst', /Martin D\u{FC}rst/.source)
+ assert_equal('\u{FC}', /\u{FC}/.source)
+ assert_equal('\u{FC}', %r{\u{FC}}.source)
+ assert_equal('\u00FC', %r{\u00FC}i.source)
+
+ # match strings to regexps
+ assert_equal(0, "Yukihiro Matsumoto - 松本行弘" =~ /Yukihiro Matsumoto - \u677E\u672C\u884C\u5F18/)
+ assert_equal(0, "Yukihiro Matsumoto - \u677E\u672C\u884C\u5F18" =~ /Yukihiro Matsumoto - \u677E\u672C\u884C/)
+ assert_equal(0, "Yukihiro Matsumoto - 松本行弘" =~ /Yukihiro Matsumoto - \u{677E 672C 884C 5F18}/)
+ assert_equal(0, %Q{Yukihiro Matsumoto - \u{677E 672C 884C 5F18}} =~ /Yukihiro Matsumoto - \u{677E 672C 884C 5F18}/)
+ assert_equal(0, "Matz - まつもと ゆきひろ" =~ /Matz - \u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D/)
+ assert_equal(0, "Aoyama Gakuin University - 青山学院大学" =~ /Aoyama Gakuin University - \u9752\u5C71\u5B66\u9662\u5927\u5B66/)
+ assert_equal(0, "青山学院大学" =~ /\u9752\u5C71\u5B66\u9662\u5927\u5B66/)
+ assert_equal(0, "Martin Dürst" =~ /Martin D\u00FCrst/)
+ assert_equal(0, "ü" =~ /\u00FC/)
+ assert_equal(0, "Martin Dürst" =~ /Martin D\u{FC}rst/)
+ assert_equal(0, "ü" =~ %r{\u{FC}})
+ assert_equal(0, "ü" =~ %r{\u00FC}i)
+
+ # Flip order of the two operands
+ assert_equal(0, /Martin D\u00FCrst/ =~ "Martin Dürst")
+ assert_equal(4, /\u00FC/ =~ "testü")
+ assert_equal(3, /Martin D\u{FC}rst/ =~ "fooMartin Dürstbar")
+ assert_equal(3, %r{\u{FC}} =~ "fooübar")
+
+ # Put \u in strings, literal character in regexp
+ assert_equal(0, "Martin D\u00FCrst" =~ /Martin Dürst/)
+ assert_equal(4, "test\u00FC" =~ /ü/)
+ assert_equal(3, "fooMartin D\u{FC}rstbar" =~ /Martin Dürst/)
+ assert_equal(3, %Q{foo\u{FC}bar} =~ %r<ü>)
+
+ assert_match(eval('/\u{2a}/'), "*")
+ assert_raise(SyntaxError) { eval('/\u{6666}/n') }
+ assert_raise(SyntaxError) { eval('/\u{6666}/e') }
+ assert_raise(SyntaxError) { eval('/\u{6666}/s') }
+ assert_nothing_raised { eval('/\u{6666}/u') }
+ end
+
+ def test_dynamic_regexp
+ assert_match(Regexp.new("Martin D\\u{FC}rst"), "Martin Dürst")
+ end
+
+ def test_syntax_variants
+ # all hex digits
+ assert_equal("\xC4\xA3\xE4\x95\xA7\xE8\xA6\xAB\xEC\xB7\xAF", "\u0123\u4567\u89AB\uCDEF")
+ assert_equal("\xC4\xA3\xE4\x95\xA7\xE8\xA6\xAB\xEC\xB7\xAF", "\u0123\u4567\u89AB\uCDEF")
+ assert_equal("\xC4\xA3\xE4\x95\xA7\xE8\xA6\xAB\xEC\xB7\xAF", "\u0123\u4567\u89ab\ucdef")
+ assert_equal("\xC4\xA3\xE4\x95\xA7\xE8\xA6\xAB\xEC\xB7\xAF", "\u0123\u4567\u89ab\ucdef")
+ assert_equal("\xC4\xA3\xE4\x95\xA7\xE8\xA6\xAB\xEC\xB7\xAF", "\u0123\u4567\u89aB\uCdEf")
+ assert_equal("\xC4\xA3\xE4\x95\xA7\xE8\xA6\xAB\xEC\xB7\xAF", "\u0123\u4567\u89aB\ucDEF")
+ end
+
+ def test_fulton
+ # examples from Hal Fulton's book (second edition), chapter 4
+ # precomposed e'pe'e
+ assert_equal('épée', "\u00E9\u0070\u00E9\u0065")
+ assert_equal('épée', "\u00E9p\u00E9e")
+ assert_equal("\xC3\xA9\x70\xC3\xA9\x65", "\u00E9\u0070\u00E9\u0065")
+ assert_equal("\xC3\xA9\x70\xC3\xA9\x65", "\u00E9p\u00E9e")
+ # decomposed e'pe'e
+ assert_equal('épée', "\u0065\u0301\u0070\u0065\u0301\u0065")
+ assert_equal('épée', "e\u0301pe\u0301e")
+ assert_equal("\x65\xCC\x81\x70\x65\xCC\x81\x65", "\u0065\u0301\u0070\u0065\u0301\u0065")
+ assert_equal("\x65\xCC\x81\x70\x65\xCC\x81\x65", "e\u0301pe\u0301e")
+ # combinations of NFC/D, NFKC/D
+ assert_equal('öffnen', "\u00F6\u0066\u0066\u006E\u0065\u006E")
+ assert_equal("\xC3\xB6ffnen", "\u00F6\u0066\u0066\u006E\u0065\u006E")
+ assert_equal('öffnen', "\u00F6ffnen")
+ assert_equal("\xC3\xB6ffnen", "\u00F6ffnen")
+ assert_equal('öffnen', "\u006F\u0308\u0066\u0066\u006E\u0065\u006E")
+ assert_equal("\x6F\xCC\x88ffnen", "\u006F\u0308\u0066\u0066\u006E\u0065\u006E")
+ assert_equal('öffnen', "o\u0308ffnen")
+ assert_equal("\x6F\xCC\x88ffnen", "o\u0308ffnen")
+ assert_equal('öffnen', "\u00F6\uFB00\u006E\u0065\u006E")
+ assert_equal("\xC3\xB6\xEF\xAC\x80nen", "\u00F6\uFB00\u006E\u0065\u006E")
+ assert_equal('öffnen', "\u00F6\uFB00nen")
+ assert_equal("\xC3\xB6\xEF\xAC\x80nen", "\u00F6\uFB00nen")
+ assert_equal('öffnen', "\u006F\u0308\uFB00\u006E\u0065\u006E")
+ assert_equal("\x6F\xCC\x88\xEF\xAC\x80nen", "\u006F\u0308\uFB00\u006E\u0065\u006E")
+ assert_equal('öffnen', "o\u0308\uFB00nen")
+ assert_equal("\x6F\xCC\x88\xEF\xAC\x80nen", "o\u0308\uFB00nen")
+ # German sharp s (sz)
+ assert_equal('Straße', "\u0053\u0074\u0072\u0061\u00DF\u0065")
+ assert_equal("\x53\x74\x72\x61\xC3\x9F\x65", "\u0053\u0074\u0072\u0061\u00DF\u0065")
+ assert_equal('Straße', "Stra\u00DFe")
+ assert_equal("\x53\x74\x72\x61\xC3\x9F\x65", "Stra\u00DFe")
+ assert_equal('Straße', "\u{53}\u{74}\u{72}\u{61}\u{DF}\u{65}")
+ assert_equal("\x53\x74\x72\x61\xC3\x9F\x65", "\u{53}\u{74}\u{72}\u{61}\u{DF}\u{65}")
+ assert_equal("\x53\x74\x72\x61\xC3\x9F\x65", "\u{53 74 72 61 DF 65}")
+ assert_equal('Straße', "Stra\u{DF}e")
+ assert_equal("\x53\x74\x72\x61\xC3\x9F\x65", "Stra\u{DF}e")
+ end
+
+ def test_edge_cases
+ # start and end of each outer plane
+ assert_equal("\xF4\x8F\xBF\xBF", "\u{10FFFF}")
+ assert_equal("\xF4\x80\x80\x80", "\u{100000}")
+ assert_equal("\xF3\xBF\xBF\xBF", "\u{FFFFF}")
+ assert_equal("\xF3\xB0\x80\x80", "\u{F0000}")
+ assert_equal("\xF3\xAF\xBF\xBF", "\u{EFFFF}")
+ assert_equal("\xF3\xA0\x80\x80", "\u{E0000}")
+ assert_equal("\xF3\x9F\xBF\xBF", "\u{DFFFF}")
+ assert_equal("\xF3\x90\x80\x80", "\u{D0000}")
+ assert_equal("\xF3\x8F\xBF\xBF", "\u{CFFFF}")
+ assert_equal("\xF3\x80\x80\x80", "\u{C0000}")
+ assert_equal("\xF2\xBF\xBF\xBF", "\u{BFFFF}")
+ assert_equal("\xF2\xB0\x80\x80", "\u{B0000}")
+ assert_equal("\xF2\xAF\xBF\xBF", "\u{AFFFF}")
+ assert_equal("\xF2\xA0\x80\x80", "\u{A0000}")
+ assert_equal("\xF2\x9F\xBF\xBF", "\u{9FFFF}")
+ assert_equal("\xF2\x90\x80\x80", "\u{90000}")
+ assert_equal("\xF2\x8F\xBF\xBF", "\u{8FFFF}")
+ assert_equal("\xF2\x80\x80\x80", "\u{80000}")
+ assert_equal("\xF1\xBF\xBF\xBF", "\u{7FFFF}")
+ assert_equal("\xF1\xB0\x80\x80", "\u{70000}")
+ assert_equal("\xF1\xAF\xBF\xBF", "\u{6FFFF}")
+ assert_equal("\xF1\xA0\x80\x80", "\u{60000}")
+ assert_equal("\xF1\x9F\xBF\xBF", "\u{5FFFF}")
+ assert_equal("\xF1\x90\x80\x80", "\u{50000}")
+ assert_equal("\xF1\x8F\xBF\xBF", "\u{4FFFF}")
+ assert_equal("\xF1\x80\x80\x80", "\u{40000}")
+ assert_equal("\xF0\xBF\xBF\xBF", "\u{3FFFF}")
+ assert_equal("\xF0\xB0\x80\x80", "\u{30000}")
+ assert_equal("\xF0\xAF\xBF\xBF", "\u{2FFFF}")
+ assert_equal("\xF0\xA0\x80\x80", "\u{20000}")
+ assert_equal("\xF0\x9F\xBF\xBF", "\u{1FFFF}")
+ assert_equal("\xF0\x90\x80\x80", "\u{10000}")
+ # BMP
+ assert_equal("\xEF\xBF\xBF", "\uFFFF")
+ assert_equal("\xEE\x80\x80", "\uE000")
+ assert_equal("\xED\x9F\xBF", "\uD7FF")
+ assert_equal("\xE0\xA0\x80", "\u0800")
+ assert_equal("\xDF\xBF", "\u07FF")
+ assert_equal("\xC2\x80", "\u0080")
+ assert_equal("\x7F", "\u007F")
+ assert_equal("\x00", "\u0000")
+ end
+
+ def test_chars
+ assert_equal(?\u0041, ?A)
+ assert_equal(?\u{79}, ?\x79)
+ assert_equal(?\u{0}, ?\000)
+ assert_equal(?\u0000, ?\000)
+ end
+
+ # Tests to make sure that disallowed cases fail
+ def test_fail
+ assert_raise(SyntaxError) { eval %q("\uabc") } # too short
+ assert_raise(SyntaxError) { eval %q("\uab") } # too short
+ assert_raise(SyntaxError) { eval %q("\ua") } # too short
+ assert_raise(SyntaxError) { eval %q("\u") } # too short
+ assert_raise(SyntaxError) { eval %q("\u{110000}") } # too high
+ assert_raise(SyntaxError) { eval %q("\u{abcdeff}") } # too long
+ assert_raise(SyntaxError) { eval %q("\ughij") } # bad hex digits
+ assert_raise(SyntaxError) { eval %q("\u{ghij}") } # bad hex digits
+
+ assert_raise(SyntaxError) { eval %q("\u{123 456 }")} # extra space
+ assert_raise(SyntaxError) { eval %q("\u{ 123 456}")} # extra space
+ assert_raise(SyntaxError) { eval %q("\u{123 456}")} # extra space
+
+# The utf-8 encoding object currently does not object to codepoints
+# in the surrogate blocks, so these do not raise an error.
+# assert_raise(SyntaxError) { "\uD800" } # surrogate block
+# assert_raise(SyntaxError) { "\uDCBA" } # surrogate block
+# assert_raise(SyntaxError) { "\uDFFF" } # surrogate block
+# assert_raise(SyntaxError) { "\uD847\uDD9A" } # surrogate pair
+
+ end
+end
diff --git a/jni/ruby/test/ruby/test_variable.rb b/jni/ruby/test/ruby/test_variable.rb
new file mode 100644
index 0000000..8f5329b
--- /dev/null
+++ b/jni/ruby/test/ruby/test_variable.rb
@@ -0,0 +1,121 @@
+require 'test/unit'
+
+class TestVariable < Test::Unit::TestCase
+ class Gods
+ @@rule = "Uranus"
+ def ruler0
+ @@rule
+ end
+
+ def self.ruler1 # <= per method definition style
+ @@rule
+ end
+ class << self # <= multiple method definition style
+ def ruler2
+ @@rule
+ end
+ end
+ end
+
+ module Olympians
+ @@rule ="Zeus"
+ def ruler3
+ @@rule
+ end
+ end
+
+ class Titans < Gods
+ @@rule = "Cronus" # modifies @@rule in Gods
+ include Olympians
+ def ruler4
+ EnvUtil.suppress_warning {
+ @@rule
+ }
+ end
+ end
+
+ def test_variable
+ assert_instance_of(Fixnum, $$)
+
+ # read-only variable
+ assert_raise(NameError) do
+ $$ = 5
+ end
+ assert_normal_exit("$*=0; $*", "[ruby-dev:36698]")
+
+ foobar = "foobar"
+ $_ = foobar
+ assert_equal(foobar, $_)
+
+ assert_equal("Cronus", Gods.new.ruler0)
+ assert_equal("Cronus", Gods.ruler1)
+ assert_equal("Cronus", Gods.ruler2)
+ assert_equal("Cronus", Titans.ruler1)
+ assert_equal("Cronus", Titans.ruler2)
+ atlas = Titans.new
+ assert_equal("Cronus", atlas.ruler0)
+ assert_equal("Zeus", atlas.ruler3)
+ assert_equal("Cronus", atlas.ruler4)
+ assert_nothing_raised do
+ class << Gods
+ defined?(@@rule) && @@rule
+ end
+ end
+ end
+
+ def test_local_variables
+ lvar = 1
+ assert_instance_of(Symbol, local_variables[0], "[ruby-dev:34008]")
+ lvar
+ end
+
+ def test_local_variables2
+ x = 1
+ proc do |y|
+ assert_equal([:x, :y], local_variables.sort)
+ end.call
+ x
+ end
+
+ def test_local_variables3
+ x = 1
+ proc do |y|
+ 1.times do |z|
+ assert_equal([:x, :y, :z], local_variables.sort)
+ end
+ end.call
+ x
+ end
+
+ def test_shadowing_local_variables
+ bug9486 = '[ruby-core:60501] [Bug #9486]'
+ x = tap {|x| break local_variables}
+ assert_equal([:x, :bug9486], x)
+ end
+
+ def test_shadowing_block_local_variables
+ bug9486 = '[ruby-core:60501] [Bug #9486]'
+ x = tap {|;x| break local_variables}
+ assert_equal([:x, :bug9486], x)
+ end
+
+ def test_global_variable_0
+ assert_in_out_err(["-e", "$0='t'*1000;print $0"], "", /\At+\z/, [])
+ end
+
+ def test_global_variable_poped
+ assert_nothing_raised {
+ EnvUtil.suppress_warning {
+ eval("$foo; 1")
+ }
+ }
+ end
+
+ def test_constant_poped
+ assert_nothing_raised {
+ EnvUtil.suppress_warning {
+ eval("TestVariable::Gods; 1")
+ }
+ }
+ end
+end
diff --git a/jni/ruby/test/ruby/test_weakmap.rb b/jni/ruby/test/ruby/test_weakmap.rb
new file mode 100644
index 0000000..1279944
--- /dev/null
+++ b/jni/ruby/test/ruby/test_weakmap.rb
@@ -0,0 +1,133 @@
+require 'test/unit'
+
+class TestWeakMap < Test::Unit::TestCase
+ def setup
+ @wm = ObjectSpace::WeakMap.new
+ end
+
+ def test_map
+ x = Object.new
+ k = "foo"
+ @wm[k] = x
+ assert_same(x, @wm[k])
+ assert_not_same(x, @wm["FOO".downcase])
+ end
+
+ def test_aset_const
+ x = Object.new
+ assert_raise(ArgumentError) {@wm[true] = x}
+ assert_raise(ArgumentError) {@wm[false] = x}
+ assert_raise(ArgumentError) {@wm[nil] = x}
+ assert_raise(ArgumentError) {@wm[42] = x}
+ assert_raise(ArgumentError) {@wm[:foo] = x}
+ assert_raise(ArgumentError) {@wm[x] = true}
+ assert_raise(ArgumentError) {@wm[x] = false}
+ assert_raise(ArgumentError) {@wm[x] = nil}
+ assert_raise(ArgumentError) {@wm[x] = 42}
+ assert_raise(ArgumentError) {@wm[x] = :foo}
+ end
+
+ def test_include?
+ m = __callee__[/test_(.*)/, 1]
+ k = "foo"
+ 1.times do
+ x = Object.new
+ @wm[k] = x
+ assert_send([@wm, m, k])
+ assert_not_send([@wm, m, "FOO".downcase])
+ x = nil
+ end
+ GC.start
+ assert_not_send([@wm, m, k])
+ end
+ alias test_member? test_include?
+ alias test_key? test_include?
+
+ def test_inspect
+ x = Object.new
+ k = BasicObject.new
+ @wm[k] = x
+ assert_match(/\A\#<#{@wm.class.name}:[^:]+:\s\#<BasicObject:[^:]*>\s=>\s\#<Object:[^:]*>>\z/,
+ @wm.inspect)
+ end
+
+ def test_each
+ m = __callee__[/test_(.*)/, 1]
+ x1 = Object.new
+ k1 = "foo"
+ @wm[k1] = x1
+ x2 = Object.new
+ k2 = "bar"
+ @wm[k2] = x2
+ n = 0
+ @wm.__send__(m) do |k, v|
+ assert_match(/\A(?:foo|bar)\z/, k)
+ case k
+ when /foo/
+ assert_same(k1, k)
+ assert_same(x1, v)
+ when /bar/
+ assert_same(k2, k)
+ assert_same(x2, v)
+ end
+ n += 1
+ end
+ assert_equal(2, n)
+ end
+
+ def test_each_key
+ x1 = Object.new
+ k1 = "foo"
+ @wm[k1] = x1
+ x2 = Object.new
+ k2 = "bar"
+ @wm[k2] = x2
+ n = 0
+ @wm.each_key do |k|
+ assert_match(/\A(?:foo|bar)\z/, k)
+ case k
+ when /foo/
+ assert_same(k1, k)
+ when /bar/
+ assert_same(k2, k)
+ end
+ n += 1
+ end
+ assert_equal(2, n)
+ end
+
+ def test_each_value
+ x1 = "foo"
+ k1 = Object.new
+ @wm[k1] = x1
+ x2 = "bar"
+ k2 = Object.new
+ @wm[k2] = x2
+ n = 0
+ @wm.each_value do |v|
+ assert_match(/\A(?:foo|bar)\z/, v)
+ case v
+ when /foo/
+ assert_same(x1, v)
+ when /bar/
+ assert_same(x2, v)
+ end
+ n += 1
+ end
+ assert_equal(2, n)
+ end
+
+ def test_size
+ m = __callee__[/test_(.*)/, 1]
+ assert_equal(0, @wm.__send__(m))
+ x1 = "foo"
+ k1 = Object.new
+ @wm[k1] = x1
+ assert_equal(1, @wm.__send__(m))
+ x2 = "bar"
+ k2 = Object.new
+ @wm[k2] = x2
+ assert_equal(2, @wm.__send__(m))
+ end
+ alias test_length test_size
+end
diff --git a/jni/ruby/test/ruby/test_whileuntil.rb b/jni/ruby/test/ruby/test_whileuntil.rb
new file mode 100644
index 0000000..3d8dbee
--- /dev/null
+++ b/jni/ruby/test/ruby/test_whileuntil.rb
@@ -0,0 +1,82 @@
+require 'test/unit'
+require 'tmpdir'
+
+class TestWhileuntil < Test::Unit::TestCase
+ def test_while
+ Dir.mktmpdir("ruby_while_tmp") {|tmpdir|
+ tmpfilename = "#{tmpdir}/ruby_while_tmp.#{$$}"
+
+ tmp = open(tmpfilename, "w")
+ tmp.print "tvi925\n";
+ tmp.print "tvi920\n";
+ tmp.print "vt100\n";
+ tmp.print "Amiga\n";
+ tmp.print "paper\n";
+ tmp.close
+
+ tmp = open(tmpfilename, "r")
+ assert_instance_of(File, tmp)
+
+ while line = tmp.gets()
+ break if /vt100/ =~ line
+ end
+
+ assert_not_predicate(tmp, :eof?)
+ assert_match(/vt100/, line)
+ tmp.close
+
+ tmp = open(tmpfilename, "r")
+ while line = tmp.gets()
+ next if /vt100/ =~ line
+ assert_no_match(/vt100/, line)
+ end
+ assert_predicate(tmp, :eof?)
+ assert_no_match(/vt100/, line)
+ tmp.close
+
+ tmp = open(tmpfilename, "r")
+ while line = tmp.gets()
+ lastline = line
+ line = line.gsub(/vt100/, 'VT100')
+ if lastline != line
+ line.gsub!('VT100', 'Vt100')
+ redo
+ end
+ assert_no_match(/vt100/, line)
+ assert_no_match(/VT100/, line)
+ end
+ assert_predicate(tmp, :eof?)
+ tmp.close
+
+ sum=0
+ for i in 1..10
+ sum += i
+ i -= 1
+ if i > 0
+ redo
+ end
+ end
+ assert_equal(220, sum)
+
+ tmp = open(tmpfilename, "r")
+ while line = tmp.gets()
+ break if 3
+ assert_no_match(/vt100/, line)
+ assert_no_match(/Amiga/, line)
+ assert_no_match(/paper/, line)
+ end
+ tmp.close
+
+ File.unlink tmpfilename or `/bin/rm -f "#{tmpfilename}"`
+ assert_file.not_exist?(tmpfilename)
+ }
+ end
+
+ def test_until
+ i = 0
+ until i>4
+ i+=1
+ end
+ assert_operator(i, :>, 4)
+ end
+end
diff --git a/jni/ruby/test/ruby/test_yield.rb b/jni/ruby/test/ruby/test_yield.rb
new file mode 100644
index 0000000..82378d4
--- /dev/null
+++ b/jni/ruby/test/ruby/test_yield.rb
@@ -0,0 +1,393 @@
+require 'test/unit'
+require 'stringio'
+
+class TestRubyYield < Test::Unit::TestCase
+
+ def test_ary_each
+ ary = [1]
+ ary.each {|a, b, c, d| assert_equal [1,nil,nil,nil], [a,b,c,d] }
+ ary.each {|a, b, c| assert_equal [1,nil,nil], [a,b,c] }
+ ary.each {|a, b| assert_equal [1,nil], [a,b] }
+ ary.each {|a| assert_equal 1, a }
+ end
+
+ def test_hash_each
+ h = {:a => 1}
+ h.each do |k, v|
+ assert_equal :a, k
+ assert_equal 1, v
+ end
+ h.each do |kv|
+ assert_equal [:a, 1], kv
+ end
+ end
+
+ def test_yield_0
+ assert_equal 1, iter0 { 1 }
+ assert_equal 2, iter0 { 2 }
+ end
+
+ def iter0
+ yield
+ end
+
+ def test_yield_1
+ iter1([]) {|a, b| assert_equal [nil,nil], [a, b] }
+ iter1([1]) {|a, b| assert_equal [1,nil], [a, b] }
+ iter1([1, 2]) {|a, b| assert_equal [1,2], [a,b] }
+ iter1([1, 2, 3]) {|a, b| assert_equal [1,2], [a,b] }
+
+ iter1([]) {|a| assert_equal [], a }
+ iter1([1]) {|a| assert_equal [1], a }
+ iter1([1, 2]) {|a| assert_equal [1,2], a }
+ iter1([1, 2, 3]) {|a| assert_equal [1,2,3], a }
+ end
+
+ def iter1(args)
+ yield args
+ end
+
+ def test_yield2
+ def iter2_1() yield 1, *[2, 3] end
+ iter2_1 {|a, b, c| assert_equal [1,2,3], [a,b,c] }
+ def iter2_2() yield 1, *[] end
+ iter2_2 {|a, b, c| assert_equal [1,nil,nil], [a,b,c] }
+ def iter2_3() yield 1, *[2] end
+ iter2_3 {|a, b, c| assert_equal [1,2,nil], [a,b,c] }
+ end
+
+ def test_yield_nested
+ [[1, [2, 3]]].each {|a, (b, c)|
+ assert_equal [1,2,3], [a,b,c]
+ }
+ [[1, [2, 3]]].map {|a, (b, c)|
+ assert_equal [1,2,3], [a,b,c]
+ }
+ end
+
+ def test_with_enum
+ obj = Object.new
+ def obj.each
+ yield(*[])
+ end
+ obj.each{|*v| assert_equal([], [], '[ruby-dev:32392]')}
+ obj.to_enum.each{|*v| assert_equal([], [], '[ruby-dev:32392]')}
+ end
+
+ def block_args_unleashed
+ yield(1,2,3,4,5)
+ end
+
+ def test_block_args_unleashed
+ r = block_args_unleashed {|a,b=1,*c,d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
+ end
+end
+
+require_relative 'sentence'
+class TestRubyYieldGen < Test::Unit::TestCase
+ Syntax = {
+ :exp => [["0"],
+ ["nil"],
+ ["false"],
+ ["[]"],
+ ["[",:exps,"]"]],
+ :exps => [[:exp],
+ [:exp,",",:exps]],
+ :opt_block_param => [[],
+ [:block_param_def]],
+ :block_param_def => [['|', '|'],
+ ['|', :block_param, '|']],
+ :block_param => [[:f_arg, ",", :f_rest_arg, :opt_f_block_arg],
+ [:f_arg, ","],
+ [:f_arg, ',', :f_rest_arg, ",", :f_arg, :opt_f_block_arg],
+ [:f_arg, :opt_f_block_arg],
+ [:f_rest_arg, :opt_f_block_arg],
+ [:f_rest_arg, ',', :f_arg, :opt_f_block_arg],
+ [:f_block_arg]],
+ :f_arg => [[:f_arg_item],
+ [:f_arg, ',', :f_arg_item]],
+ :f_rest_arg => [['*', "var"],
+ ['*']],
+ :opt_f_block_arg => [[',', :f_block_arg],
+ []],
+ :f_block_arg => [['&', 'var']],
+ :f_arg_item => [[:f_norm_arg],
+ ['(', :f_margs, ')']],
+ :f_margs => [[:f_marg_list],
+ [:f_marg_list, ',', '*', :f_norm_arg],
+ [:f_marg_list, ',', '*', :f_norm_arg, ',', :f_marg_list],
+ [:f_marg_list, ',', '*'],
+ [:f_marg_list, ',', '*', ',', :f_marg_list],
+ [ '*', :f_norm_arg],
+ [ '*', :f_norm_arg, ',', :f_marg_list],
+ [ '*'],
+ [ '*', ',', :f_marg_list]],
+ :f_marg_list => [[:f_marg],
+ [:f_marg_list, ',', :f_marg]],
+ :f_marg => [[:f_norm_arg],
+ ['(', :f_margs, ')']],
+ :f_norm_arg => [['var']],
+
+ :command_args => [[:open_args]],
+ :open_args => [[' ',:call_args],
+ ['(', ')'],
+ ['(', :call_args2, ')']],
+ :call_args => [[:command],
+ [ :args, :opt_block_arg],
+ [ :assocs, :opt_block_arg],
+ [ :args, ',', :assocs, :opt_block_arg],
+ [ :block_arg]],
+ :call_args2 => [[:arg, ',', :args, :opt_block_arg],
+ [:arg, ',', :block_arg],
+ [ :assocs, :opt_block_arg],
+ [:arg, ',', :assocs, :opt_block_arg],
+ [:arg, ',', :args, ',', :assocs, :opt_block_arg],
+ [ :block_arg]],
+
+ :command_args_noblock => [[:open_args_noblock]],
+ :open_args_noblock => [[' ',:call_args_noblock],
+ ['(', ')'],
+ ['(', :call_args2_noblock, ')']],
+ :call_args_noblock => [[:command],
+ [ :args],
+ [ :assocs],
+ [ :args, ',', :assocs]],
+ :call_args2_noblock => [[:arg, ',', :args],
+ [ :assocs],
+ [:arg, ',', :assocs],
+ [:arg, ',', :args, ',', :assocs]],
+
+ :command => [],
+ :args => [[:arg],
+ ["*",:arg],
+ [:args,",",:arg],
+ [:args,",","*",:arg]],
+ :arg => [[:exp]],
+ :assocs => [[:assoc],
+ [:assocs, ',', :assoc]],
+ :assoc => [[:arg, '=>', :arg],
+ ['label', ':', :arg]],
+ :opt_block_arg => [[',', :block_arg],
+ []],
+ :block_arg => [['&', :arg]],
+ #:test => [['def m() yield', :command_args_noblock, ' end; r = m {', :block_param_def, 'vars', '}; undef m; r']]
+ :test_proc => [['def m() yield', :command_args_noblock, ' end; r = m {', :block_param_def, 'vars', '}; undef m; r']],
+ :test_lambda => [['def m() yield', :command_args_noblock, ' end; r = m(&lambda {', :block_param_def, 'vars', '}); undef m; r']],
+ :test_enum => [['o = Object.new; def o.each() yield', :command_args_noblock, ' end; r1 = r2 = nil; o.each {|*x| r1 = x }; o.to_enum.each {|*x| r2 = x }; [r1, r2]']]
+ }
+
+ def rename_var(obj)
+ vars = []
+ r = obj.subst('var') {
+ var = "v#{vars.length}"
+ vars << var
+ var
+ }
+ return r, vars
+ end
+
+ def split_by_comma(ary)
+ return [] if ary.empty?
+ result = [[]]
+ ary.each {|e|
+ if e == ','
+ result << []
+ else
+ result.last << e
+ end
+ }
+ result
+ end
+
+ def emu_return_args(*vs)
+ vs
+ end
+
+ def emu_eval_args(args)
+ if args.last == []
+ args = args[0...-1]
+ end
+ code = "emu_return_args(#{args.map {|a| a.join('') }.join(",")})"
+ eval code, nil, 'generated_code_in_emu_eval_args'
+ end
+
+ def emu_bind_single(arg, param, result_binding)
+ #p [:emu_bind_single, arg, param]
+ if param.length == 1 && String === param[0] && /\A[a-z0-9]+\z/ =~ param[0]
+ result_binding[param[0]] = arg
+ elsif param.length == 1 && Array === param[0] && param[0][0] == '(' && param[0][-1] == ')'
+ arg = [arg] unless Array === arg
+ emu_bind_params(arg, split_by_comma(param[0][1...-1]), false, result_binding)
+ else
+ raise "unexpected param: #{param.inspect}"
+ end
+ result_binding
+ end
+
+ def emu_bind_params(args, params, islambda, result_binding={})
+ #p [:emu_bind_params, args, params]
+ if params.last == [] # extra comma
+ params.pop
+ end
+
+ star_index = nil
+ params.each_with_index {|par, i|
+ star_index = i if par[0] == '*'
+ }
+
+ if islambda
+ if star_index
+ if args.length < params.length - 1
+ throw :emuerror, ArgumentError
+ end
+ else
+ if args.length != params.length and !(args.length == 1 and Array === args[0] and args[0].length == params.length)
+ throw :emuerror, ArgumentError
+ end
+ end
+ end
+
+ # TRICK #2 : adjust mismatch on number of arguments
+ if star_index
+ pre_params = params[0...star_index]
+ rest_param = params[star_index]
+ post_params = params[(star_index+1)..-1]
+ pre_params.each {|par| emu_bind_single(args.shift, par, result_binding) }
+ if post_params.length <= args.length
+ post_params.reverse_each {|par| emu_bind_single(args.pop, par, result_binding) }
+ else
+ post_params.each {|par| emu_bind_single(args.shift, par, result_binding) }
+ end
+ if rest_param != ['*']
+ emu_bind_single(args, rest_param[1..-1], result_binding)
+ end
+ else
+ params.each_with_index {|par, i|
+ emu_bind_single(args[i], par, result_binding)
+ }
+ end
+
+ #p [args, params, result_binding]
+
+ result_binding
+ end
+
+ def emu_bind(t, islambda)
+ #puts
+ #p t
+ command_args_noblock = t[1]
+ block_param_def = t[3]
+ command_args_noblock = command_args_noblock.expand {|a| !(a[0] == '[' && a[-1] == ']') }
+ block_param_def = block_param_def.expand {|a| !(a[0] == '(' && a[-1] == ')') }
+
+ if command_args_noblock.to_a[0] == ' '
+ args = command_args_noblock.to_a[1..-1]
+ elsif command_args_noblock.to_a[0] == '(' && command_args_noblock.to_a[-1] == ')'
+ args = command_args_noblock.to_a[1...-1]
+ else
+ raise "unexpected command_args_noblock: #{command_args_noblock.inspect}"
+ end
+ args = emu_eval_args(split_by_comma(args))
+
+ params = block_param_def.to_a[1...-1]
+ params = split_by_comma(params)
+
+ #p [:emu0, args, params]
+
+ result_binding = {}
+
+ if params.last && params.last[0] == '&'
+ result_binding[params.last[1]] = nil
+ params.pop
+ end
+
+ if !islambda
+ # TRICK #1 : single array argument is expanded if there are two or more params.
+ # * block parameter is not counted.
+ # * extra comma after single param forces the expansion.
+ if args.length == 1 && Array === args[0] && 1 < params.length
+ args = args[0]
+ end
+ end
+
+ emu_bind_params(args, params, islambda, result_binding)
+ #p result_binding
+ result_binding
+ end
+
+ def emu(t, vars, islambda)
+ catch(:emuerror) {
+ emu_binding = emu_bind(t, islambda)
+ vars.map {|var| emu_binding.fetch(var, "NOVAL") }
+ }
+ end
+
+ def disable_stderr
+ begin
+ save_stderr = $stderr
+ $stderr = StringIO.new
+ yield
+ ensure
+ $stderr = save_stderr
+ end
+ end
+
+ def check_nofork(t, islambda=false)
+ t, vars = rename_var(t)
+ t = t.subst('vars') { " [#{vars.join(",")}]" }
+ emu_values = emu(t, vars, islambda)
+ s = t.to_s
+ #print "#{s}\t\t"
+ #STDOUT.flush
+ eval_values = disable_stderr {
+ begin
+ eval(s, nil, 'generated_code_in_check_nofork')
+ rescue ArgumentError
+ ArgumentError
+ end
+ }
+ #success = emu_values == eval_values ? 'succ' : 'fail'
+ #puts "eval:#{vs_ev.inspect[1...-1].delete(' ')}\temu:#{vs_emu.inspect[1...-1].delete(' ')}\t#{success}"
+ assert_equal(emu_values, eval_values, s)
+ end
+
+ def test_yield
+ syntax = Sentence.expand_syntax(Syntax)
+ Sentence.each(syntax, :test_proc, 4) {|t|
+ check_nofork(t)
+ }
+ end
+
+ def test_yield_lambda
+ syntax = Sentence.expand_syntax(Syntax)
+ Sentence.each(syntax, :test_lambda, 4) {|t|
+ check_nofork(t, true)
+ }
+ end
+
+ def test_yield_enum
+ syntax = Sentence.expand_syntax(Syntax)
+ Sentence.each(syntax, :test_enum, 4) {|t|
+ code = t.to_s
+ r1, r2 = disable_stderr {
+ eval(code, nil, 'generated_code_in_test_yield_enum')
+ }
+ assert_equal(r1, r2, "#{t}")
+ }
+ end
+
+ def test_block_with_mock
+ y = Object.new
+ def y.s(a)
+ yield(a)
+ end
+ m = Object.new
+ def m.method_missing(*a)
+ super
+ end
+ assert_equal [m, nil], y.s(m){|a,b|[a,b]}
+ end
+end
diff --git a/jni/ruby/test/ruby/ut_eof.rb b/jni/ruby/test/ruby/ut_eof.rb
new file mode 100644
index 0000000..83325f2
--- /dev/null
+++ b/jni/ruby/test/ruby/ut_eof.rb
@@ -0,0 +1,128 @@
+require 'test/unit'
+
+module TestEOF
+ def test_eof_0
+ open_file("") {|f|
+ assert_equal("", f.read(0))
+ assert_equal("", f.read(0))
+ assert_equal("", f.read)
+ assert_equal("", f.read(0))
+ assert_equal("", f.read(0))
+ }
+ open_file("") {|f|
+ assert_nil(f.read(1))
+ assert_equal("", f.read)
+ assert_nil(f.read(1))
+ }
+ open_file("") {|f|
+ s = "x"
+ assert_equal("", f.read(nil, s))
+ assert_equal("", s)
+ }
+ open_file("") {|f|
+ s = "x"
+ assert_nil(f.read(10, s))
+ assert_equal("", s)
+ }
+ end
+
+ def test_eof_0_rw
+ return unless respond_to? :open_file_rw
+ open_file_rw("") {|f|
+ assert_equal("", f.read)
+ assert_equal("", f.read)
+ assert_equal(0, f.syswrite(""))
+ assert_equal("", f.read)
+ }
+ end
+
+ def test_eof_1
+ open_file("a") {|f|
+ assert_equal("", f.read(0))
+ assert_equal("a", f.read(1))
+ assert_equal("" , f.read(0))
+ assert_equal("" , f.read(0))
+ assert_equal("", f.read)
+ assert_equal("", f.read(0))
+ assert_equal("", f.read(0))
+ }
+ open_file("a") {|f|
+ assert_equal("a", f.read(1))
+ assert_nil(f.read(1))
+ }
+ open_file("a") {|f|
+ assert_equal("a", f.read(2))
+ assert_nil(f.read(1))
+ assert_equal("", f.read)
+ assert_nil(f.read(1))
+ }
+ open_file("a") {|f|
+ assert_equal("a", f.read)
+ assert_nil(f.read(1))
+ assert_equal("", f.read)
+ assert_nil(f.read(1))
+ }
+ open_file("a") {|f|
+ assert_equal("a", f.read(2))
+ assert_equal("", f.read)
+ assert_equal("", f.read)
+ }
+ open_file("a") {|f|
+ assert_equal("a", f.read)
+ assert_equal("", f.read(0))
+ }
+ open_file("a") {|f|
+ s = "x"
+ assert_equal("a", f.read(nil, s))
+ assert_equal("a", s)
+ }
+ open_file("a") {|f|
+ s = "x"
+ assert_equal("a", f.read(10, s))
+ assert_equal("a", s)
+ }
+ end
+
+ def test_eof_2
+ open_file("") {|f|
+ assert_equal("", f.read)
+ assert_predicate(f, :eof?)
+ }
+ end
+
+ def test_eof_3
+ open_file("") {|f|
+ assert_predicate(f, :eof?)
+ }
+ end
+
+ module Seek
+ def open_file_seek(content, pos)
+ open_file(content) do |f|
+ f.seek(pos)
+ yield f
+ end
+ end
+
+ def test_eof_0_seek
+ open_file_seek("", 10) {|f|
+ assert_equal(10, f.pos)
+ assert_equal("", f.read(0))
+ assert_equal("", f.read)
+ assert_equal("", f.read(0))
+ assert_equal("", f.read)
+ }
+ end
+
+ def test_eof_1_seek
+ open_file_seek("a", 10) {|f|
+ assert_equal("", f.read)
+ assert_equal("", f.read)
+ }
+ open_file_seek("a", 1) {|f|
+ assert_equal("", f.read)
+ assert_equal("", f.read)
+ }
+ end
+ end
+end
diff --git a/jni/ruby/test/rubygems/alternate_cert.pem b/jni/ruby/test/rubygems/alternate_cert.pem
new file mode 100644
index 0000000..a5a8291
--- /dev/null
+++ b/jni/ruby/test/rubygems/alternate_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9zCCAd+gAwIBAgIBADANBgkqhkiG9w0BAQUFADAtMRIwEAYDVQQDDAlhbHRl
+cm5hdGUxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTEyMTIwODAwMDAwMFoY
+Dzk5OTkxMjMxMjM1OTU5WjAtMRIwEAYDVQQDDAlhbHRlcm5hdGUxFzAVBgoJkiaJ
+k/IsZAEZFgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+vZQipBa1xH3M9OonkrUYhGZeX9UHAcJhe6jJbUr/uHXkh1Tu2ERWNnblm85upqBf
+jyZEnKer7uBcwwkkvmisVgC8uBECymsBxuEIw0rfiKYEnLu0B6SiWFYz3dYPS92b
+BK7Vks2/kNyXUmLLGoZ3id2K0eK5C/AJ0j+p84OqPnVhylsjrZmXfIZrh7lkHhgC
+IrzPefjE3pOloi/tz6fh2ktb0FYKQMfweT3Ba2TMeflG13PEOW80AD5w0THxDutG
+G0zPNCDyDEoT7UU1a3B3RMHYuUxEk1GUEYWq9L6a6SMpZISWTSpCp0Ww1QB55PON
+iCCn+o6vcIy46jI71dATAQIDAQABoyAwHjAcBgNVHREEFTATgRFhbHRlcm5hdGVA
+ZXhhbXBsZTANBgkqhkiG9w0BAQUFAAOCAQEAtiOe4Ws0hQdxlPCHcngMoWeWMg/d
+EtYHy1vD9oTOEGax6y319ShFNzCTG5Mk9dVXdVTrwml9+SrqmLbcckzr05TpSq3t
+JcNjx+qMwU31655DY+r2Le1mbrhhXilLJ++KjhaIzeqmA55MFLNBdMrApL0v2Tf9
+e9aiL8W8jXPX24uJd5eaFTsPV3NfcYV/iDyf1zv2Fe3NO8e0V1luQxJd3v+ILlRo
+YAkm2DK83G++YkvgsopRdOFfFsY3Gb6XtL87iGOWRbdNK90pThpfORcKoTus7QLx
+l7vsHsOJF5NZwLiBF9ufKH6fUV3Fy25JwB/z8kR8Bkplcl0F8jpJYrd6NQ==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/alternate_cert_32.pem b/jni/ruby/test/rubygems/alternate_cert_32.pem
new file mode 100644
index 0000000..6ef8999
--- /dev/null
+++ b/jni/ruby/test/rubygems/alternate_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9TCCAd2gAwIBAgIBATANBgkqhkiG9w0BAQUFADAtMRIwEAYDVQQDDAlhbHRl
+cm5hdGUxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTEyMTIwODAwMDAwMFoX
+DTM4MDExOTAzMTQwN1owLTESMBAGA1UEAwwJYWx0ZXJuYXRlMRcwFQYKCZImiZPy
+LGQBGRYHZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2U
+IqQWtcR9zPTqJ5K1GIRmXl/VBwHCYXuoyW1K/7h15IdU7thEVjZ25ZvObqagX48m
+RJynq+7gXMMJJL5orFYAvLgRAsprAcbhCMNK34imBJy7tAekolhWM93WD0vdmwSu
+1ZLNv5Dcl1JiyxqGd4nditHiuQvwCdI/qfODqj51YcpbI62Zl3yGa4e5ZB4YAiK8
+z3n4xN6TpaIv7c+n4dpLW9BWCkDH8Hk9wWtkzHn5RtdzxDlvNAA+cNEx8Q7rRhtM
+zzQg8gxKE+1FNWtwd0TB2LlMRJNRlBGFqvS+mukjKWSElk0qQqdFsNUAeeTzjYgg
+p/qOr3CMuOoyO9XQEwECAwEAAaMgMB4wHAYDVR0RBBUwE4ERYWx0ZXJuYXRlQGV4
+YW1wbGUwDQYJKoZIhvcNAQEFBQADggEBABB+jdfwANRtIQKCvv+1VnYm+3QSfshy
+TI5Kk50TphheDJ7VbYyTmOxPUTsFzn0cX8zD1jBOtdVdyy6u7qUHOw6ZrDKdgg4I
+95lJJibFrVXeazTByHhe31u08lMfp/6kdYwSornHUWmEHhAT60nvSbkLblW/jgPo
+i1pFY5hcGJAE6kFQLQpjgl8TbUTioA8wYOKJA5ITItBXO7fYMQKDS2jYR3JMiNLR
+FxgN8SzSUB0S7deI5O9fMLYL006zHP5u0qcZjVFvFJdtt3WsqDUu8E6O4QgVwvq1
+MPNUBfuNdMek1LnIUZgr7Jek+0wvbnZCGyEaVakcV74cq8fjEgWr3C0=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/alternate_key.pem b/jni/ruby/test/rubygems/alternate_key.pem
new file mode 100644
index 0000000..14ca734
--- /dev/null
+++ b/jni/ruby/test/rubygems/alternate_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAvZQipBa1xH3M9OonkrUYhGZeX9UHAcJhe6jJbUr/uHXkh1Tu
+2ERWNnblm85upqBfjyZEnKer7uBcwwkkvmisVgC8uBECymsBxuEIw0rfiKYEnLu0
+B6SiWFYz3dYPS92bBK7Vks2/kNyXUmLLGoZ3id2K0eK5C/AJ0j+p84OqPnVhylsj
+rZmXfIZrh7lkHhgCIrzPefjE3pOloi/tz6fh2ktb0FYKQMfweT3Ba2TMeflG13PE
+OW80AD5w0THxDutGG0zPNCDyDEoT7UU1a3B3RMHYuUxEk1GUEYWq9L6a6SMpZISW
+TSpCp0Ww1QB55PONiCCn+o6vcIy46jI71dATAQIDAQABAoIBAArXgfOoaNTH7QTE
+r2awfKp1wEfywufS2ghcasiZVW6TL3Kd5NrxbYzH1/HFKIbW/SAOrDXZUPfkVOnC
+iBtrmQ+CE0jjkClLXVqmW/3vNkF2XSUphu44+B/dLjItn8pS7h6icQxoP+Bk/TJ0
++/CUaBm2Vc4TDUolfCpOAcYvbXkM3BL+N/XN2JHX52D2ljXtryrNm/sFnabUVo96
+ghWqln8TqpYTagcs/JkEQ5YxwqFuBLofz3SgzCnf8ub8WTIpYhWzWZ4yHjZSL7AS
+54mkJarKWMUYcL/Qeuy1U9vxLrbC9V7cPzSkzYxPZF7XlYaJcAbItX182ufZ1uNX
+3JlQS5ECgYEA+6fbg+WKy5AazEs8YokPjq1X1P01o95KUWFg+68ALowQXEYcExew
+PG0BKW11WrR6Bnn41++13k8Qsrq7Tl8ynCO6ANhoWAxUksdJDAuEgQqpFuRXwa/D
+d++8WlWD4XYqLwiE+h72alE/Ce/SdfPPsyBeHtXo7fih378WyZn7K9cCgYEAwNnw
+zjndLtj9bxd4awHHWgQ7QpKCmtLMGlg7Teo9aODMO80G3h8NEEG6Ou6LHn88tqgH
+yu0WcjJmhINAzNzmABdw+WuV4C94glwtXctQ0w4byuLOaKSh3ggWUnKf56A2KyPh
+JHPe/+A1DTKAgBvU/i5Vx0kZBkUMiiEVcIOgHOcCgYBNkt6998IjIdbA5uhET4+2
+IYUTqMIiM2GhWG026CkcMBzS9OGumPzAg7F5/b3RKhT7bhnhJolfb+vrzFf0vq+x
+JeouXIc9rP9dB4Vi6yH7TTf2UIkksXOFwybCid3PYEd8nBmxqF25RDY0b/LmXTPH
+OdEJnFLjGGN9vz/dAVRFnQKBgQC8hE8hSO8uHG+haRANim+VTw2exhllvypFlnpi
+b9gX7ae3zXQpLbFXcujZMtZLuZVf+GGlvJ10hFAyuRtfJ5CuBjwplUGtJLpotDKk
+vVsE9YW1joC3SjfxE3a+oc4uXi6VfT1YpOwYtNMnU3bJxGsxDZpMdOhBeL4JSM3s
+br7VgQKBgBDdJHRQOkP41Iq7CjcheTJMeXsQJt+HLHSIpEkxi8v/9bLKPbRVRo7e
+8mmEr9mvjrNLVZMrQpgngRGbFzcdi9iDv+4m0OKU7BGZyWy1gtlUV77FqsL7EEl3
+gdM670c2kkrni5DdpTLpNgF6zRKK7ArZ6kSgmuEYJCGHHlpbkg3f
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/bad_rake.rb b/jni/ruby/test/rubygems/bad_rake.rb
new file mode 100644
index 0000000..379a4c9
--- /dev/null
+++ b/jni/ruby/test/rubygems/bad_rake.rb
@@ -0,0 +1 @@
+exit 1
diff --git a/jni/ruby/test/rubygems/bogussources.rb b/jni/ruby/test/rubygems/bogussources.rb
new file mode 100644
index 0000000..008e3a1
--- /dev/null
+++ b/jni/ruby/test/rubygems/bogussources.rb
@@ -0,0 +1,8 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems'
+Gem.use_paths("test/mock/gems")
diff --git a/jni/ruby/test/rubygems/ca_cert.pem b/jni/ruby/test/rubygems/ca_cert.pem
new file mode 100644
index 0000000..5207531
--- /dev/null
+++ b/jni/ruby/test/rubygems/ca_cert.pem
@@ -0,0 +1,68 @@
+-----BEGIN CERTIFICATE-----
+MIID0DCCArigAwIBAgIBADANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES
+MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X
+DTA0MDEzMDAwNDIzMloXDTM2MDEyMjAwNDIzMlowPDELMAkGA1UEBgwCSlAxEjAQ
+BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQswCQYDVQQDDAJDQTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANbv0x42BTKFEQOE+KJ2XmiSdZpR
+wjzQLAkPLRnLB98tlzs4xo+y4RyY/rd5TT9UzBJTIhP8CJi5GbS1oXEerQXB3P0d
+L5oSSMwGGyuIzgZe5+vZ1kgzQxMEKMMKlzA73rbMd4Jx3u5+jdbP0EDrPYfXSvLY
+bS04n2aX7zrN3x5KdDrNBfwBio2/qeaaj4+9OxnwRvYP3WOvqdW0h329eMfHw0pi
+JI0drIVdsEqClUV4pebT/F+CPUPkEh/weySgo9wANockkYu5ujw2GbLFcO5LXxxm
+dEfcVr3r6t6zOA4bJwL0W/e6LBcrwiG/qPDFErhwtgTLYf6Er67SzLyA66UCAwEA
+AaOB3DCB2TAPBgNVHRMBAf8EBTADAQH/MDEGCWCGSAGG+EIBDQQkFiJSdWJ5L09w
+ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRJ7Xd380KzBV7f
+USKIQ+O/vKbhDzAOBgNVHQ8BAf8EBAMCAQYwZAYDVR0jBF0wW4AUSe13d/NCswVe
+31EiiEPjv7ym4Q+hQKQ+MDwxCzAJBgNVBAYMAkpQMRIwEAYDVQQKDAlKSU4uR1Iu
+SlAxDDAKBgNVBAsMA1JSUjELMAkGA1UEAwwCQ0GCAQAwDQYJKoZIhvcNAQEFBQAD
+ggEBAIu/mfiez5XN5tn2jScgShPgHEFJBR0BTJBZF6xCk0jyqNx/g9HMj2ELCuK+
+r/Y7KFW5c5M3AQ+xWW0ZSc4kvzyTcV7yTVIwj2jZ9ddYMN3nupZFgBK1GB4Y05GY
+MJJFRkSu6d/Ph5ypzBVw2YMT/nsOo5VwMUGLgS7YVjU+u/HNWz80J3oO17mNZllj
+PvORJcnjwlroDnS58KoJ7GDgejv3ESWADvX1OHLE4cRkiQGeLoEU4pxdCxXRqX0U
+PbwIkZN9mXVcrmPHq8MWi4eC/V7hnbZETMHuWhUoiNdOEfsAXr3iP4KjyyRdwc7a
+d/xgcK06UVQRL/HbEYGiQL056mc=
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIBATANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES
+MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X
+DTA0MDEzMDAwNDMyN1oXDTM1MDEyMjAwNDMyN1owPzELMAkGA1UEBgwCSlAxEjAQ
+BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQ4wDAYDVQQDDAVTdWJDQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ0Ou7AyRcRXnB/kVHv/6kwe
+ANzgg/DyJfsAUqW90m7Lu1nqyug8gK0RBd77yU0w5HOAMHTVSdpjZK0g2sgx4Mb1
+d/213eL9TTl5MRVEChTvQr8q5DVG/8fxPPE7fMI8eOAzd98/NOAChk+80r4Sx7fC
+kGVEE1bKwY1MrUsUNjOY2d6t3M4HHV3HX1V8ShuKfsHxgCmLzdI8U+5CnQedFgkm
+3e+8tr8IX5RR1wA1Ifw9VadF7OdI/bGMzog/Q8XCLf+WPFjnK7Gcx6JFtzF6Gi4x
+4dp1Xl45JYiVvi9zQ132wu8A1pDHhiNgQviyzbP+UjcB/tsOpzBQF8abYzgEkWEC
+AwEAAaNyMHAwDwYDVR0TAQH/BAUwAwEB/zAxBglghkgBhvhCAQ0EJBYiUnVieS9P
+cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUlCjXWLsReYzH
+LzsxwVnCXmKoB/owCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCJ/OyN
+rT8Cq2Y+G2yA/L1EMRvvxwFBqxavqaqHl/6rwsIBFlB3zbqGA/0oec6MAVnYynq4
+c4AcHTjx3bQ/S4r2sNTZq0DH4SYbQzIobx/YW8PjQUJt8KQdKMcwwi7arHP7A/Ha
+LKu8eIC2nsUBnP4NhkYSGhbmpJK+PFD0FVtD0ZIRlY/wsnaZNjWWcnWF1/FNuQ4H
+ySjIblqVQkPuzebv3Ror6ZnVDukn96Mg7kP4u6zgxOeqlJGRe1M949SS9Vudjl8X
+SF4aZUUB9pQGhsqQJVqaz2OlhGOp9D0q54xko/rekjAIcuDjl1mdX4F2WRrzpUmZ
+uY/bPeOBYiVsOYVe
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIJANz6ehBcVuuiMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTMwNTAxMTQ0NTQxWhcNMjMwMzEwMTQ0NTQxWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAzlpZwhEYoALOEKU4lmMw5l3YI/gadzDOoELtdcidvVvovKK8IIOTDwbA
+3XcjwV0UPGEPOK4Uk1aD0EKkOQVg8ivSre2a3FFGffs2kXck+doJMzAA+pf8tvFk
+QsETVOurOp74GN+er2xbbRSDVxQKq6d+QTe1E60btyXQS5M1Nt5SvLn8dazZJgvv
+3yzJQ1IOQl+xeEO0WVVhPIx5Mx3VtjjcDyl8aewPkYkzia6UOrAyQZnl5sIzWGOb
+kYKCNeKjTPepzlbMx0dN6jBupPYGNB+4FYY9GezInjGbRP5np5382wd3EWwsVzic
+Nau8kXHTL2r7GzNvoy0p//iPCqx9FQIDAQABo4GnMIGkMB0GA1UdDgQWBBS7B027
+H/ZIkW3ngm1SrR0X/aTCwDB1BgNVHSMEbjBsgBS7B027H/ZIkW3ngm1SrR0X/aTC
+wKFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV
+BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJANz6ehBcVuuiMAwGA1UdEwQF
+MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAC0glUrUiylTfuOWlwkQvi74oiYC5CzW
+Jfusg6o/Gg1XEuJhaHiYMsK/do16gSc6Za3934rHQbYu3mesyFkCWF9kD4J6/hEO
+OQL8xmmgN7wS6GXy6oIODpny0MgnFrV4gd1aEx69NIfL/wXaM8Gw2sj1TnuGLs8+
+HFmWLRRH3WSR7ZLnqYzPVJwhHu8vtZBL9HZk1J6xyq00Nwi2Cz5WdiHamgaza3TS
+OgBdWwDeSClwhrTJni4d30dbq+eNMByIZ7QNGBQivpFzDxeNV/2UBrTU0CilKG5Q
+j7ZwknfKeA4xUTd8TMK3vKab5JJCfjbXOTHZQsYUcEEGSjOMS8/YVQs=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/child_cert.pem b/jni/ruby/test/rubygems/child_cert.pem
new file mode 100644
index 0000000..3079a9e
--- /dev/null
+++ b/jni/ruby/test/rubygems/child_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCAdSgAwIBAgIBAjANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTEyMTIwODAwMDAwMFoYDzk5
+OTkxMjMxMjM1OTU5WjApMQ4wDAYDVQQDDAVjaGlsZDEXMBUGCgmSJomT8ixkARkW
+B2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKf7+1q0cR
+MhC/RM6Whpon/5IxuSve3CCxxdN0LcSbUisFMlf28Zj+S545SKFc6gHFcnYLZZsy
+Gm4FWEd4DhNpg/kgoDPuPGwQp2DEOvKnnneB/Shj8V+6oLrjXaZFAu8Q916c5/BL
+z+PlHIIsO/Q865XOK+5z1sZi0xval8QT7u4Usrcy86gevflCbpBAWkNPa/DZDqA9
+nk0vB2XDSHvhavcrYLfDrYAnFz3wiZ70LYQrmdeOqkPpaiw//Qpzqp+vtuF2br6U
+iYWpN+dhdFsIxAwIE5kWZ1kk6OBJ4kHvr+Sh8Oqbf6WFBhW/lQa9wldA0xhNwhGr
+1FDEfC+0g/BvAgMBAAGjHDAaMBgGA1UdEQQRMA+BDWNoaWxkQGV4YW1wbGUwDQYJ
+KoZIhvcNAQEFBQADggEBAGuRKUhUhtmObY00za/qw71wjqtNZad5+k9acM/xh/my
++YEBSlj8ZIWD1H0kUmAO0Pmwab6ziFA8IrAo0N1D897o7eP83KzVeVxoJi1g+8rS
+u16bF7cC5zT9S/N/ZNTVz9ERjRAD7MdcbXEikKPaqjqzZBViClqpO8YQ6Z9bFgXk
+gV9plX1ki17H+EuXNs21gqsy+8NAxTlXKuF5JKriBOQ9XW62496F/yZPw6XQ50My
+R21cOLiDdoXDmS4Wo7bJ+sbR3N1JbhqZ7Wt9DkQnMtXw4xOcFM5PsVIsmn1KhGGp
+v8GAxxUMzFoPvsx+XcV9TWjR1seYlXnvdlx65YYtZg8=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/child_cert_32.pem b/jni/ruby/test/rubygems/child_cert_32.pem
new file mode 100644
index 0000000..35ba720
--- /dev/null
+++ b/jni/ruby/test/rubygems/child_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6jCCAdKgAwIBAgIBAzANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTEyMTIwODAwMDAwMFoXDTM4
+MDExOTAzMTQwN1owKTEOMAwGA1UEAwwFY2hpbGQxFzAVBgoJkiaJk/IsZAEZFgdl
+eGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyn+/tatHETIQ
+v0TOloaaJ/+SMbkr3twgscXTdC3Em1IrBTJX9vGY/kueOUihXOoBxXJ2C2WbMhpu
+BVhHeA4TaYP5IKAz7jxsEKdgxDryp553gf0oY/FfuqC6412mRQLvEPdenOfwS8/j
+5RyCLDv0POuVzivuc9bGYtMb2pfEE+7uFLK3MvOoHr35Qm6QQFpDT2vw2Q6gPZ5N
+Lwdlw0h74Wr3K2C3w62AJxc98Ime9C2EK5nXjqpD6WosP/0Kc6qfr7bhdm6+lImF
+qTfnYXRbCMQMCBOZFmdZJOjgSeJB76/kofDqm3+lhQYVv5UGvcJXQNMYTcIRq9RQ
+xHwvtIPwbwIDAQABoxwwGjAYBgNVHREEETAPgQ1jaGlsZEBleGFtcGxlMA0GCSqG
+SIb3DQEBBQUAA4IBAQAzsudxe1TkFtYmCmnT2LVeafo6dVinRJGmdSWeUfNfqRM4
+fgDVg21ym/4Y/bIW3W2lPe9aYNk/leCkh1rewf5XWAQtJTD0+2Ssn/YsxFi62H2l
+RLdDC/t21zG2rNAlYhGc1P4gPFdrqRVkGMYy4Wl6QFBG9U6431NcUKfA+o3uYveh
+8ANbIItQF1FWKGClKeg4FpbPfHRzLtBV+zR8hXX0pxi7Eqwn6IME9jyAoAI2QOqU
+5QVCToPWFFKmn29djnLIq6oG8AS0o1dtiJbyqNgB5yarJFX+P6ym1jvTEmWAzk8v
+N5++ztI1NWdWhtzhEJkJrzRu3Q0yYIPJaJ+mY9vp
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/child_key.pem b/jni/ruby/test/rubygems/child_key.pem
new file mode 100644
index 0000000..c56d069
--- /dev/null
+++ b/jni/ruby/test/rubygems/child_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAyn+/tatHETIQv0TOloaaJ/+SMbkr3twgscXTdC3Em1IrBTJX
+9vGY/kueOUihXOoBxXJ2C2WbMhpuBVhHeA4TaYP5IKAz7jxsEKdgxDryp553gf0o
+Y/FfuqC6412mRQLvEPdenOfwS8/j5RyCLDv0POuVzivuc9bGYtMb2pfEE+7uFLK3
+MvOoHr35Qm6QQFpDT2vw2Q6gPZ5NLwdlw0h74Wr3K2C3w62AJxc98Ime9C2EK5nX
+jqpD6WosP/0Kc6qfr7bhdm6+lImFqTfnYXRbCMQMCBOZFmdZJOjgSeJB76/kofDq
+m3+lhQYVv5UGvcJXQNMYTcIRq9RQxHwvtIPwbwIDAQABAoIBAEJkNgerG0bRAlqj
+hZQml35LtbPlwTN1OqbudFCf/NjrzL0Mb3jAZ2fghQTFAlrDQkdpKri73HFF5xKL
+igsbmlO6+EAxJjWSy9998SUFKq+4LfiJduelqLw4nG2VM8Fmn9kRMY0CIm/IvjBM
+84TrNz2OA/SvxKNoJG0cSAKYRao+00s+xNIu8lr6CqvXqnFO6gzbxQvJ0J0TnKVf
+AezArZZI3VaPXmC8yo2U6v9r7/2txr12QnDPDk9YMwJ7OR+7qzujAPvtHy44t4en
+AsTetle9AXveIBC7zpl0pp27wN8tKIdh8n+jxPfES9ecn4FjfreWiwzzZSSCitqQ
+p7cQdTkCgYEA9U/y38KUjV05FO7AeeMJYmy/o3WxjcZF8lUtuCsGzogD0JbnNj7R
+BF9TwlNnkeSJsPYKMG17dnoZhgY3J96mWhQbEH9CyXNdgQladE9/qH9gCCW9BXyo
+z3urNc77F/44J+1OoegpWGS8Hdm7OGsESLF1wLet+5cRbVHtU2brqQMCgYEA01JK
+AnATj+vACcAtr1Gu9eGE/6EqAM+H/bfQzGtqkxEmY8QihW//XWH/vOZDVZZYLUoc
+9MkSUHNGwZ7ESAgoZWc1D5xxp3sT2+vV192TS+QBe3TT5AXhAGH9uL+qz7Gz4ihH
+ebt4p49u5SJVY+3vv+nck/YgEiBw4PrfwSdugSUCgYB86U/XpoH0FaMKSKRTrErM
+BmnytuxJL8vQIJVeMPKPWezvWtey5HuUCWJiEgwr2r5OEIqRrD3wzy2N9D5Dm/kC
+5zf8x4BfidHz8apQjWaIiwuAOo8saxSeSe+dP57V0coQcqLWiJv8+ZZccNEHYl7V
+ER/PmPgLoxnpm40IKeEXtwKBgCwUEAfuJMZyYD4obd8R5LK49ar0jPRaVX1gqBbb
+mQFQJHfO43x93gA2fseCKC1kDMR1nxCYGE/bm7irSznTKcns+y5kbXiHvZ6z1IkQ
+WLcNuhlsRv5bE5Gm3ut4X0KvSFw2FqKXrhUVYAY/YRxU9xtKxo2+WvYs+h6TdbSu
+auhZAoGAThhKJW0Rf+LX1zlVaq+GXrj2rkYVSBwChMHbmmp49q6crldfLi15KbI/
+LRoUwjnQLQVNT0j090/rlNVv+pcQLqZ/pDHXQOMwrYuhbbLsda/FqTo3Qb/XnwHX
+qRrjdgGk5OC3gJt8EaHHdq+ty/eF4xQ0fUPMvIj8fwowxGyextI=
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/client.pem b/jni/ruby/test/rubygems/client.pem
new file mode 100644
index 0000000..63a52c5
--- /dev/null
+++ b/jni/ruby/test/rubygems/client.pem
@@ -0,0 +1,49 @@
+-----BEGIN CERTIFICATE-----
+MIIDgTCCAmmgAwIBAgICEAIwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUx
+EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg
+UHR5IEx0ZDAeFw0xMzA1MDExNTAxMzFaFw0yMzAzMTAxNTAxMzFaMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw
++lcrpdWcloQCgAlxcM3GjvBxZ3yjzi6SgXKRBSi54i0J1LXxznWKcJ5P/O1+j+7i
+LjHK+OWqsa0+EbKTwSu+0tx20h0z++YJF9GWEoCwT5aH1kor/0+EQLgYnxBaF8GC
+2xAbkRkWmbSu2aLDIey3lg7lqAazYqdS2wH0UjSDjFKDLxz9LwpfFm0yGL3DgwLW
++dobYkgt1A6F/8Pz6D2FjwYKcM8JE6w7KJSJDUvXcv2E18wmhZ/qF/MtFAF4coB1
+f5ALnz8YqY6eyDF5aY/VfaHZvXdirLlMH6/miie9GBVMnJWF0ah5ssbsMvcpmnDJ
+qkiYju2e1oLFEE7zztU/AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN
+BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTcOELj
+hSUdiLrdRF3CFZDZkWaGzDAfBgNVHSMEGDAWgBS7B027H/ZIkW3ngm1SrR0X/aTC
+wDANBgkqhkiG9w0BAQUFAAOCAQEAlQMzHlnT6L1qqA4hL6tABPbiMsVwXyKCcfNB
+zBn82Wkxgbg7Mp31fbR6/qvGeXOtaX6IdPdgtVf8nh1NURk0MmFBP+gfnwfNBD+m
+Q1cldDt9kY2LGIrPii40xbugF1/xqEYcZMgXU08aEvQ2IHX46J8wZoqMa2KhrU8/
+mzY0F+UEFOGWtKDgUzz3dyBPsdzVrX+SXULwH0lqZX8Nsw5LyfrlVt3xQvS5Ogm4
+kYlt8kqhF8lUS3WTbuADrIs3NaDPRWSs1iLRRFgosgUtHN7tkrkrVaHeBo0KbAJG
+mMqtxSY0XZI9WBxffP9UtoY3EiTWNVWLtuCN3OSvryP6NDe4BA==
+-----END CERTIFICATE-----
+
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAsPpXK6XVnJaEAoAJcXDNxo7wcWd8o84ukoFykQUoueItCdS1
+8c51inCeT/ztfo/u4i4xyvjlqrGtPhGyk8ErvtLcdtIdM/vmCRfRlhKAsE+Wh9ZK
+K/9PhEC4GJ8QWhfBgtsQG5EZFpm0rtmiwyHst5YO5agGs2KnUtsB9FI0g4xSgy8c
+/S8KXxZtMhi9w4MC1vnaG2JILdQOhf/D8+g9hY8GCnDPCROsOyiUiQ1L13L9hNfM
+JoWf6hfzLRQBeHKAdX+QC58/GKmOnsgxeWmP1X2h2b13Yqy5TB+v5oonvRgVTJyV
+hdGoebLG7DL3KZpwyapImI7tntaCxRBO887VPwIDAQABAoIBAFOpdG3gzlNg3/Ti
+nBQxdEVqKwYhGs3A2UlOwl8F5lPBNPNRx9UQeYZBaMV9VrQezJnFpqpB8Sg5KCGQ
+ci/hAJIL0kalW0LI0Nz5ko10H7u5U/rQ9W1JG0j041JYV32Pf14husKdXBPQA5co
+sQW30tSSrmYogUpp15mWiJz8A3EvqiCTlQv5JwwMFGnjVl8+HNfuLghK/vqY/Eb9
+YmwTKxPFejqN7E0Mud2ylNiuPTSLwBy8UvV9uxOlDc6lMyZjVRO0woiEzrjw5dKF
+yf5tUkICRcPkekcx+XtpGrCMlRLl770bZBZX+YNmbYXVWhFp09cNR+U0KZqPNcDp
+jg73vXECgYEA3huOKzfHGt3qUdMlEHd1FvQhW9fYIrmUSnuVYQJOnY8lFfKfmrOH
+gpwOIHDNiVHYlhAJaNocCLYx4hWHgZXarY7NKxmlY2+Vp8mcCIf2Cw3Kr/sFklUJ
+KpiRxqEPGR7U4C/E31kkH/C+w7m9Zh3ndhltU2Pki9/Eq0lk8YClMMkCgYEAy/vU
+jxzviIk8bll5uCIuXJyCfao7ywaZABbL6a20kdVGKrHj57O/OJ2WZVwBihhB7OS+
+QsKC/J8LrUJkobOFtQvQ8O23uep5rB6kqCkXsXCG4SCl2L5xZySBp/qhiqbuMwvp
+EAWPSIA6UNoR0J2rDYVmq6jtY526wQf5ivE8IccCgYEAphfzJAyNH2FOZixArmS2
+shiUjasG3UjsRRrP5YClK5wtPpF2m2if8KMkyUux2HvVPLr3XmqkxjsBaLFy6QwY
+QOvmL9H45Tg/sP7KaXLLIw8IQLu2OezPcwQvF1u//6gXxyLR1bhClIQjFBjlMuUv
+/xgasl6kPZlz6Cd1jkgGwEkCgYAI1IT2EQWZfn9cM4leXDRvk+LeN8FQ35897r6z
+Be78JSRdcsfv3ssXU1MQXjQ+2x/3dkt6LltnPidOP8KFcXUHSlSoKVI7vRe5SLZO
+BUFeUAW2tygWwt+73Eu0jtfxXZqQISLcq7DxLYPYvifpRPoDotO3+J8WIdzUwFig
+GCNHPwKBgHqXOyRef7ykVUCptMf61/BvNU8NP1f9PkKQBMYQZC39UwqEQ675QBUh
+hSG9t/kyc44zUVmBeKIlWHVyLQ83Dv+ennz/D9t7tstet0VMKvALNdiVT0sjFKN7
+1VINygCeFkqrlTXlOwFcRSo1gHn3/JIrhSgRuYKHSf0GZOcN6d9l
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/data/gem-private_key.pem b/jni/ruby/test/rubygems/data/gem-private_key.pem
new file mode 100644
index 0000000..3e4be4c
--- /dev/null
+++ b/jni/ruby/test/rubygems/data/gem-private_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAz0tTOtsJuHDKAEXrQx0f6DUEzBEUTSLR1fk0iEHsY9rDCQxm
+sw5Bf2UnVhdD03B4/XzIK+pat2CMQc37/vLIBuVgS7g/fzatGiM0m5rAHtycr0XU
+8Ek6zjx4iSv70OLjybY+/utHCEc838awGDMCFR21jYxgATPVwqAIyasvwbKh/Vhw
+uErFPqT9G8BKTHsaX+H+ADIRH001OmWkjB6EyjF05114kNMa0+2C7daV9hoBL3md
+hCt6zOGcapl/9LkGxhcNEUB/So16V1ZQldg9macGyWktyNTSfctlF+f8okAmicG3
+XIwaW8UTmjFCmvDs/h1R/uKpe2IOHz87n29d2QIDAQABAoIBAQCR6n/nyg+JmTtX
+/d+hGns/RTLfQpZ7xarXZ9gmoeD4WSE42VXhbIOGXXnXDAFecKl6Jb/xycGZm4if
+OZPM3rEWyZeDNWrc7WvkHiwF7GSYVMqmRg2iJqoSSla+mAtl+pBFiNfHMW6K0Tp0
+erOyFRW+L2+A9/MMZaRun6AP9URkn0jz2kwmMFf+6szmzVn6fPFzZDRI+hEeaDmi
+LBzSrfrddrIBX+xGEoBj6RmfnKBCSUVSSxOauYjd4mVjVYxvMH4SV1hXDUS5GPl5
+MbCiBb7bpNIg/8ljMoRrQiqk0XwwS7MaCqPtMhUtpSmC/zSjAfmoN7AOc/Xh69cQ
+OCMNZH9BAoGBAPBlsuuU6fg0gVTKDdR12jHx03uRRt8/nPxHnpJkZCIh9XKh1LtY
+bkumi9HZpp3mzDiaGg/rwfCwNckKx8NLhICLgkric6ClrKftxTu6C8tBAb5YDi6u
+74KYnV8lMY/unzBtIloPgM3uluS292POmrWZpKwhvHLD71MewzMor5HFAoGBANy/
+mwsBs8i3Gzk8Twjq8effhPpE7kpxhC7bhwmjX3q41EjQWDT8M6xb1P9dRSsCIebi
+kqP1yhl27dJpA8r5WqE/z89xhBvObAGRv41eXxOI0LaH2k5lJQrUeSC+51dy+BEB
+T3GXD4C5ezZHQ8Wz/oL73uikrfhD+AqOZT2YbMEFAoGBAJvWEWpOGm3f+4bvhI+Z
+5lxCG4oa3wqRvj58XvsfQRovUWGCLtlTtgwsZq8enLf3iaOXohV4Czzvva4Z4u1i
+4v5BcbEBo1scixRBOn5BWKvl9C9j/a2dkX3jWQD4p2xaj69gz8f6DNFyPTb+tNhq
+cjgO5YUASZ1MDrSfWIKteULRAoGAZkZv8x2KyofrmQ0UITGZerDYz4t4TA1kDMGx
+QwnqhtVzpXjCJWpkFotFmDsCfPaz9mErR8PtKvcrIL1/AF+fWe5Sve3+I1P0PpXk
+hf8fVdGhwbAXuRKrouTmagGI9b9Sp65PvHUcvasyJufFwqeuV8mScX87CzeSiHGI
+/ozMdnECgYEAq4+losrhe0DEmiC9zVPvwRXjbSixDsSJxHfOcqIsZqhUgBiZ4TJD
+SrkuukrMZib6BAD+PtCJS1TBbJyyvL3QecizhHSIh3ZnT0HnaRPatLEYmU65+3kE
+kTqL4ik92bJnnWowy677sydl1lzBJDVa9ZlTs7BFSd8y/0DZaUxGg2I=
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/data/gem-public_cert.pem b/jni/ruby/test/rubygems/data/gem-public_cert.pem
new file mode 100644
index 0000000..885bf7f
--- /dev/null
+++ b/jni/ruby/test/rubygems/data/gem-public_cert.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMREwDwYDVQQDDAhydWJ5
+Z2VtczEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUxEzARBgoJkiaJk/IsZAEZFgNj
+b20wHhcNMDcwODAyMDMyNTQyWhcNMDgwODAxMDMyNTQyWjBBMREwDwYDVQQDDAhy
+dWJ5Z2VtczEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUxEzARBgoJkiaJk/IsZAEZ
+FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPS1M62wm4cMoA
+RetDHR/oNQTMERRNItHV+TSIQexj2sMJDGazDkF/ZSdWF0PTcHj9fMgr6lq3YIxB
+zfv+8sgG5WBLuD9/Nq0aIzSbmsAe3JyvRdTwSTrOPHiJK/vQ4uPJtj7+60cIRzzf
+xrAYMwIVHbWNjGABM9XCoAjJqy/BsqH9WHC4SsU+pP0bwEpMexpf4f4AMhEfTTU6
+ZaSMHoTKMXTnXXiQ0xrT7YLt1pX2GgEveZ2EK3rM4ZxqmX/0uQbGFw0RQH9KjXpX
+VlCV2D2ZpwbJaS3I1NJ9y2UX5/yiQCaJwbdcjBpbxROaMUKa8Oz+HVH+4ql7Yg4f
+Pzufb13ZAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
+BBRYTAoj4cn8CWZMHFnHGQgoO5jyFTANBgkqhkiG9w0BAQUFAAOCAQEATRrJC05l
+dOmx67Sy3bU+AVXkOr7B9nn2Myqo9uSIAncPoElN6aHr/Q8wOOjtok4r0JcHPe1e
+eotDCZUE1Jkl13Tpv26rOfOOUHtGlyAIAtpsUGOraaJkSut4WKLr1/KckyAAEtgP
+c13A0s0mEiWFRuYxIdEi54561pTT2qQBE/DUPGoYD5rUg9XYAlSovMMwG99Oca7L
+cI6vCymr1bzzddExoywBNOy0fbBT62I3ICBGbH5yOVVKVmlxeo2Zp10FCj0kDrnq
+OuMJSDr5I2XPYqoC+W4YSbwn55o2jGIUX1lOq2Hvj4tFgSxlnJZn0tUhBfR3gSOn
+IFnrqu8PlZsLFw==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/data/null-type.gemspec.rz b/jni/ruby/test/rubygems/data/null-type.gemspec.rz
new file mode 100644
index 0000000..bad9928
--- /dev/null
+++ b/jni/ruby/test/rubygems/data/null-type.gemspec.rz
Binary files differ
diff --git a/jni/ruby/test/rubygems/encrypted_private_key.pem b/jni/ruby/test/rubygems/encrypted_private_key.pem
new file mode 100644
index 0000000..2a9affd
--- /dev/null
+++ b/jni/ruby/test/rubygems/encrypted_private_key.pem
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-CBC,27887B3B3BAA3B18
+
+GUZSuxjWdx6b35JMzppCBpAfK3l9IV9D3Oculgz8yT8+qFY5iXiij+fdBQ1fgUdl
+nT3f1wC5Cj1adIQq3UYo4+MK1p3HGKYB/H600YVHNvOgnLaMybSW0uyeKwoweZrC
+mRqN41O8slS6tFY3/BdKXV8qnT7SDl28rYFejVm3Ocb9PtrREA7H48089hME2+yF
+xm8VvGWsHTfdMO9gei4aAU6OVNxvOttc6fMOV3JZYmuKRiVX8Y6y4m+YLA8rTGG7
+kiUi/Ik1YjNa4aVef8kS/xX9sfO3+Q14vE/eOU6qt0u+6zYQcyJvC9grkWolokK0
+ak/yRIDW+irVAK1niEtwnPFSs3koKemwuh2VDMX5GddLJCQmV5Ne4beI6obWVXJY
+6qaKQTMK/BulsoBnxc8Ql9izulfqpRUpWBNUBllhWg/wxnzRxzraPIlLchV6j8aa
+klRVJy1kxgnlk5+RYsDNiNyWBTB0y81Op3svA5oB05dX0AcWEoFoQruLl448IQiC
+WlQV/uDZrqqDu6JAA4D9VNpZuHB6IsGEqaRi4veWwkICbgblOLFpYS4TIxbpNqMT
+8AX7IpDEPL3Rv1NMaByfbBA2VI2HeEPELU0ietwU0KOHcxHJv8QV9ZtppeTiL3pb
+cqYdfcw89eI9h4gy5p4zrrUJM60ONC2DppRmCPZzaFqVajX2DpoEuNZGW+ZUMp3g
+l300ChvAIRjriU/ju6qmVCrJqJNG5zThAvlK/SapBSKly7vSV7q7HlVzM7Dkanqh
++aYl5MwbaSKCcQ7F2uGNgsdollQpAS3iRC1FRe06IkIaL+BzdqFc++qt36ALPiBa
+zhgjT6dTP0iRIoc1dANsJ13rmlLrEEetmIWTeEpKiVCRBHRVu8BPLtIGmiDT+0c9
+d5lwrtdha3SOq5tafqufTQ7Yxi2XteuVSFwDSmzP9l+fBXMYRWPW1otHwg42nPhn
+9SvXl3MJtYKpbzvO5IeqZ0OdTNz+gZwroCBy0ZaIPSYi88LRUzWKDp7gbqBA/ouL
+UEX5T5J4vnXJfPdTmISorPmQblqdFG+A2qCmyou6RumdKHYg/uCeMFNkLDzexTC4
+LooO0clIKKlNFktdkIq3mEaZbSf4UdVSfxRfvbLWXR1orE6ObHpJamuDFWYwWrMY
+qH4asefD8j3lmB1lwpsAbyWeOtIMGnxO6ayo2jzpQQuTVduMCR/HREuoT/6klKiT
+T58OWLa7/GHdoPv0HFNktPNXsgpmdC37IoRZhgbiTSJV6y1gEgdM3reJFXPNXTN6
+q/QCAl5UZWBEmTA2CHvrmekN58X5dv1JEl/RIKtevP/7SZp/TtJMjkKqc1Nvx8EZ
+4pvkMdlbY8cwATORSUdGPCGtPy5x+aDuQWgHOz3G/gGNmGQy98CD3AucI9tcbTiz
+VPdsnumUvkhD5suZCTEBa8JI5d/nCY7/hA6n58fC2eojIFchgtUuJoOFb6GYHItA
+V1couQWj89PubyDPbS0vjxkiCxEk3CK1eDPsjHs/8NEcn582DXkKThZZvfwUu4sR
+EzPlmAeU38pCpJ7jWZtpaAttMTRXMzIY9O0bzU+K3DrtG85OQ6c48g==
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/expired_cert.pem b/jni/ruby/test/rubygems/expired_cert.pem
new file mode 100644
index 0000000..3578be8
--- /dev/null
+++ b/jni/ruby/test/rubygems/expired_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCAdSgAwIBAgIBBDANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTcwMDEwMTAwMDAwMFoXDTcw
+MDEwMTAwMDAwMFowKjEPMA0GA1UEAwwGbm9ib2R5MRcwFQYKCZImiZPyLGQBGRYH
+ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdlNYcvFB6x
+gpT8QcnMybVelGLUKyQL/hQPTK9J5FyqB8hEDLt2ruMwKsHU6fu/mgG+sLvHF1nq
+WhZADhn6rwCfNP03025vktOZ6iJfU60vytY/f3gkiGNkP21l0hz0+Ms15f+52gk+
+sFXqwGBbDteI0x57UsHi+KAN67QuPDnthYDtwhXIA5pcdx2wH+NW8F82HEZvm1hc
+pA75BDVk4vPxnpDfvPOKSYn9dWghmUtaPmqyVvs8XkDxHDfY54/D3ziPehP+zQzE
+g8C07Sq/z6vLSOQ3uaYn0nBDuNE6XP3ijJ4Zvs2eJEdOMyS1H3CsnkWiePCrLtKd
+w8d/F+D5bocCAwEAAaMdMBswGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwDQYJ
+KoZIhvcNAQEFBQADggEBABksVqMxMq/VKjS5Z1tr6G5GlcgvpNjUb+OOEfBc66vK
+Bv3aPrmNR6IE/+o2r0tTIJh4npeE9ASL+Ht0/sphdpOuzfOToPnmptIxHEWg4ub4
+n2qDasmQGDi96xLBZe4DV/5I4WkIQiuyXubVkriRLygng4VRohgFWVfLok9OXhYB
+1NAEIXj9NVKIwFmrOgQEPNYdCVL0U82GhbhbB79gSzvRA2hJOuXhTGLgiAp+lstn
+ppWlcNsg/t1JhJ2unBk9XhhrqUEBQxQcvA2jt1KnlpY/x+ubfB86J6D6l4ve9plE
+uXBltZ2qNFM70SWsPzohQxHfyzmIRKQLM3PXjbma/Lg=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/fake_certlib/openssl.rb b/jni/ruby/test/rubygems/fake_certlib/openssl.rb
new file mode 100644
index 0000000..9de90c0
--- /dev/null
+++ b/jni/ruby/test/rubygems/fake_certlib/openssl.rb
@@ -0,0 +1,7 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+raise LoadError, "no such file to load -- openssl"
diff --git a/jni/ruby/test/rubygems/fix_openssl_warnings.rb b/jni/ruby/test/rubygems/fix_openssl_warnings.rb
new file mode 100644
index 0000000..66d6cdf
--- /dev/null
+++ b/jni/ruby/test/rubygems/fix_openssl_warnings.rb
@@ -0,0 +1,12 @@
+##
+# HACK: this drives me BONKERS
+
+if defined? OpenSSL then
+ class OpenSSL::X509::ExtensionFactory
+ alias :old_create_ext :create_ext
+ def create_ext(*args)
+ @config ||= nil
+ old_create_ext(*args)
+ end
+ end
+end if RUBY_VERSION < "1.9"
diff --git a/jni/ruby/test/rubygems/foo/discover.rb b/jni/ruby/test/rubygems/foo/discover.rb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/jni/ruby/test/rubygems/foo/discover.rb
diff --git a/jni/ruby/test/rubygems/future_cert.pem b/jni/ruby/test/rubygems/future_cert.pem
new file mode 100644
index 0000000..4f3d860
--- /dev/null
+++ b/jni/ruby/test/rubygems/future_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8DCCAdigAwIBAgIBBjANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCIYDzk5OTkxMjMxMjM1OTU5WhgP
+OTk5OTEyMzEyMzU5NTlaMCoxDzANBgNVBAMMBm5vYm9keTEXMBUGCgmSJomT8ixk
+ARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnZTWH
+LxQesYKU/EHJzMm1XpRi1CskC/4UD0yvSeRcqgfIRAy7dq7jMCrB1On7v5oBvrC7
+xxdZ6loWQA4Z+q8AnzT9N9Nub5LTmeoiX1OtL8rWP394JIhjZD9tZdIc9PjLNeX/
+udoJPrBV6sBgWw7XiNMee1LB4vigDeu0Ljw57YWA7cIVyAOaXHcdsB/jVvBfNhxG
+b5tYXKQO+QQ1ZOLz8Z6Q37zzikmJ/XVoIZlLWj5qslb7PF5A8Rw32OePw984j3oT
+/s0MxIPAtO0qv8+ry0jkN7mmJ9JwQ7jROlz94oyeGb7NniRHTjMktR9wrJ5Fonjw
+qy7SncPHfxfg+W6HAgMBAAGjHTAbMBkGA1UdEQQSMBCBDm5vYm9keUBleGFtcGxl
+MA0GCSqGSIb3DQEBBQUAA4IBAQAErYGXM7S3B8i9n4U+RJoxSel/BmQ6lS09uqqr
+2wWTwzkAmupsBuXJmNt1/Z2CZ/ogf/YPYeD5KBuUNtxNGchD+ngws6TnKWZ3kyOi
+MVX7Ec7gTMFSuXSNOz/9MCMjIF4elglB6X2M5IVRqoTP/FHwlGxLx1+aIGnls5uJ
+HELoCi7uaWPHjzF9pfDmqV3FXG2jQuqIngGwIJR1/JzxcNfWW4g2fGOJ1eTNJmRK
+nDXHd2hotbar4+QIoFwMh1nSGx3v3FdqzM6bWvt2r3VK4a9E5FIyNpQ0gC4JInjL
+TlIXGWEwzE3I0sWH6EbnE74VTi96oFXntWqqMcmMJYFLHcun
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/future_cert_32.pem b/jni/ruby/test/rubygems/future_cert_32.pem
new file mode 100644
index 0000000..b7325f1
--- /dev/null
+++ b/jni/ruby/test/rubygems/future_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCAdSgAwIBAgIBBzANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTM4MDExOTAzMTQwN1oXDTM4
+MDExOTAzMTQwN1owKjEPMA0GA1UEAwwGbm9ib2R5MRcwFQYKCZImiZPyLGQBGRYH
+ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdlNYcvFB6x
+gpT8QcnMybVelGLUKyQL/hQPTK9J5FyqB8hEDLt2ruMwKsHU6fu/mgG+sLvHF1nq
+WhZADhn6rwCfNP03025vktOZ6iJfU60vytY/f3gkiGNkP21l0hz0+Ms15f+52gk+
+sFXqwGBbDteI0x57UsHi+KAN67QuPDnthYDtwhXIA5pcdx2wH+NW8F82HEZvm1hc
+pA75BDVk4vPxnpDfvPOKSYn9dWghmUtaPmqyVvs8XkDxHDfY54/D3ziPehP+zQzE
+g8C07Sq/z6vLSOQ3uaYn0nBDuNE6XP3ijJ4Zvs2eJEdOMyS1H3CsnkWiePCrLtKd
+w8d/F+D5bocCAwEAAaMdMBswGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwDQYJ
+KoZIhvcNAQEFBQADggEBAGrtVuj/iourBMAH2bQI6sQsuCJcL5HN6IXCoAcQyQEI
+LR/roZyHI3fg+FfTzUII7wxPSneo4soSKXYxQYuE1JYPxTcQE5lFv9jbzl6eplsN
+LcAMnrjB4xJoRkSg4mbPxwEICngnWRQuY+lOsquSkRXAAl+5bW/OBfxr8XWcGjmG
+ZRGmOwKzUiv7p7zyJkkEFb7m6ugg0GKop23jtv6Rou5+2lXLBqm8T0de30pM0arM
+vAnd2a8UbPnmR0DzrCtksU62UIqhVoFFicntdaEiBF/Pk9YhLCfTXwInWL8+p48r
+PBzrKMTb56YQZXVAG9aypn42pnPCMPC4ojCxfC0N3n0=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/good_rake.rb b/jni/ruby/test/rubygems/good_rake.rb
new file mode 100644
index 0000000..ca916d0
--- /dev/null
+++ b/jni/ruby/test/rubygems/good_rake.rb
@@ -0,0 +1 @@
+exit 0
diff --git a/jni/ruby/test/rubygems/grandchild_cert.pem b/jni/ruby/test/rubygems/grandchild_cert.pem
new file mode 100644
index 0000000..5e5fe01
--- /dev/null
+++ b/jni/ruby/test/rubygems/grandchild_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9TCCAd2gAwIBAgIBCDANBgkqhkiG9w0BAQUFADApMQ4wDAYDVQQDDAVjaGls
+ZDEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwIBcNMTIxMjA4MDAwMDAwWhgPOTk5
+OTEyMzEyMzU5NTlaMC4xEzARBgNVBAMMCmdyYW5kY2hpbGQxFzAVBgoJkiaJk/Is
+ZAEZFgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkfgy
+EbzcRpXvpNA0s75R8gOVk7ENEPX5uXUElbzHbVEmHkC/NFsWZXV0vxGFfZrrnEkT
+Y2kDaMMkfZX9rriRIvsZpxyqrdX87QfQTZ1ktDoytVnd+gw9A6AXB6PR7uoPymso
+f/lZYJ8BWP9fIU39nogiptFqsgkpOtKSFjJfMILkcMAeBPs2B5HV5l4oLrpJ7Ns/
+0vazCXGakTByAXNKBagJWR43gh+RUQWF6Uh04VQTQ7ENGWI83088SKAPtCCcgKxr
+ROHI025S7o7vEfDEqEn+gtu+4ndaLuRp+2AmF3YK8dEDiLXrrvEvG1r4+gIB/6tS
+MUfkkJtBleZrDoIAgQIDAQABoyEwHzAdBgNVHREEFjAUgRJncmFuZGNoaWxkQGV4
+YW1wbGUwDQYJKoZIhvcNAQEFBQADggEBAFNdoYo7A9eThXpNy1buoVpeVR19VpEG
+nvzen8ipQ7uGQ/62aBvJlqgj2/xFKWidtMNH8769SY94ePembHWABFvVBZpMU5ZO
+LYuC5rUSpJcxfw6T5eLytYHOAr56kWjQB6AVF4mQ5IavQ0MoHsm1RZ7L9amNQY1J
+zFIJpN4/T4wJ/+M58zCUFKg0aC3uUcYRgc44xhxmzceUoI3H8Bx1gqfuVM9KleLA
+Bi/BdD6GTQ16stbQP/fso5kjwAUr2x0NttwkzO0Sd7y3G7RXCmJux3zQrdu+3HoF
+edxj9I39tX+v8AbCFl2vO162ZGwLGewdk1FImb28c96t3B17jL1U/H4=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/grandchild_cert_32.pem b/jni/ruby/test/rubygems/grandchild_cert_32.pem
new file mode 100644
index 0000000..e4486a7
--- /dev/null
+++ b/jni/ruby/test/rubygems/grandchild_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdugAwIBAgIBCTANBgkqhkiG9w0BAQUFADApMQ4wDAYDVQQDDAVjaGls
+ZDEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwHhcNMTIxMjA4MDAwMDAwWhcNMzgw
+MTE5MDMxNDA3WjAuMRMwEQYDVQQDDApncmFuZGNoaWxkMRcwFQYKCZImiZPyLGQB
+GRYHZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJH4MhG8
+3EaV76TQNLO+UfIDlZOxDRD1+bl1BJW8x21RJh5AvzRbFmV1dL8RhX2a65xJE2Np
+A2jDJH2V/a64kSL7Gaccqq3V/O0H0E2dZLQ6MrVZ3foMPQOgFwej0e7qD8prKH/5
+WWCfAVj/XyFN/Z6IIqbRarIJKTrSkhYyXzCC5HDAHgT7NgeR1eZeKC66SezbP9L2
+swlxmpEwcgFzSgWoCVkeN4IfkVEFhelIdOFUE0OxDRliPN9PPEigD7QgnICsa0Th
+yNNuUu6O7xHwxKhJ/oLbvuJ3Wi7kaftgJhd2CvHRA4i1667xLxta+PoCAf+rUjFH
+5JCbQZXmaw6CAIECAwEAAaMhMB8wHQYDVR0RBBYwFIESZ3JhbmRjaGlsZEBleGFt
+cGxlMA0GCSqGSIb3DQEBBQUAA4IBAQAu14Hgglh3K914FUFC+O2HRQdgK+JaeZJ+
+vjaPxzpXcwlIMqTTphjWMk0b55iOQohXHYewYg9ToErYGfqGURH4MVSCJr+LecZX
+xuty8JC6KvTaVGfZNPIkCF92LupleV7YVg9Ew4KvCZGrodXr+XlWHpJWx7KJT+1Z
+yUC36boJUj+VDtnqq9weUgtfkh/RFoZdlRK76WiaGzKuNFBJz3n0yBhzDcMo+av3
+GeSEx8C1eu/01U8ZO5PK49HuMdNJFpB3W18gGOzT9eqDfTIEdEsrTG5cxlEgQUBW
+jHFnqSBK5LgPAqRP6GRdBW4ZtZ4yS/7+n8otsvFGwXPHOSSvtoNu
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/grandchild_key.pem b/jni/ruby/test/rubygems/grandchild_key.pem
new file mode 100644
index 0000000..a9b9aef
--- /dev/null
+++ b/jni/ruby/test/rubygems/grandchild_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAkfgyEbzcRpXvpNA0s75R8gOVk7ENEPX5uXUElbzHbVEmHkC/
+NFsWZXV0vxGFfZrrnEkTY2kDaMMkfZX9rriRIvsZpxyqrdX87QfQTZ1ktDoytVnd
++gw9A6AXB6PR7uoPymsof/lZYJ8BWP9fIU39nogiptFqsgkpOtKSFjJfMILkcMAe
+BPs2B5HV5l4oLrpJ7Ns/0vazCXGakTByAXNKBagJWR43gh+RUQWF6Uh04VQTQ7EN
+GWI83088SKAPtCCcgKxrROHI025S7o7vEfDEqEn+gtu+4ndaLuRp+2AmF3YK8dED
+iLXrrvEvG1r4+gIB/6tSMUfkkJtBleZrDoIAgQIDAQABAoIBAQCFbg4+vpfQghBM
+ZPI399oqUvJwziA2h9Kdn4TwZ18Y41vnvaHKdxUS63orihWvSmTjOL1bWsv+AJuj
+nO8GvroU8tlxM7glLX2FImZb/GrogGaH9bz+bB995+IFXs9xCE4k5y1fRgxYUSDH
+PLC13ffe6WxbdwSD9/HTTlaxqZvv1+UWxyYD0CSwopww5YdqISkVHq2UsmszK49X
+hn6zzK+DT4YA04Tbv2Go9kCYLmsgrL2/dPJulDtJhX3AckbdkodSlBAmxe7XsKEO
+TEzNDGgPZyZ+MXttBnLt1vk8ZrSJWcFG+E6DMbGUZ7rz6g98bUS1LI6PiqIp5BfS
+sr0cGQl9AoGBAMGj7SCp1GMo8wOJpzzSGJ4PCc/fpG9NcTBqtmoUTuvMk4frkGXJ
+dSS68KB0t0EGStbUFIZuylchC7RSzXs0uOZxkgaGcJT5qXXFP0Djy3/qoQMnJ2Yl
+uhD6UsetPXbozK6MPs3mh9VqSDNbf2AM034nTod3I9sV471HZLwAhQk7AoGBAMD6
+Mmvy8DEa62VDTW6P1f4b6Pi6dOiZhGbNz5Xlh5jHplSMYReQGBVmr9szrV7qytGP
+ZcBhEqTc53u2mEhSmRXQflRxJ7U2m8Xl3DClhxELHNGCJ9jEY52M4ZDJkvGj5v3t
+pbTbE/g3zxmAaYZCOKIzYv5bSSStNpauxdomxuFzAoGBAJFohH97qEZSELJ+YrwU
+VHIUfty/Zt5BvBaMe7CK0XzWIY72gHc+4Z2UV29WVeoZTIenuEX+2ii1YvGlIDI9
+s/8wF2SY/d+Q3wTV+prCtCS5TvFsLHTTLbbkEtdoqvgo9tK3881wKF5FMjSGp867
+svFPmPO2rpEtDdgrzWQzy7LTAoGBAK077Sea3qQ2VjqBQHGQDbofs/QU7f4gUgs3
+lrIpaqBsGZSssDxGzlfn5tYQfgJHI+sbn2wjuGjnJaaZM/s4qtQ6Zi3Hpq22aAAv
+aIsDDUzvfN9WyA5/vi0g2xzu10q0qBgrziWcxUB+WRu7ev9bUxvIpYVQzUhvdiGu
+o05CoSahAoGBAKoCGMGKkub+LnWazPkN2BAS6LblV+JIYWRI+DSGpz0UBk4Br546
+ozZq2GsLCQYWJabJ5RE9Are6rl9AvFQXMaWywOBe3TUz7SmLIxMjWpXKiX5YIFkS
+tOiEEmET4ZYS87flEmldnmeDFLHHbMLOw5S0dJa4PyFRn6j9su8d8mWw
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/invalid_client.pem b/jni/ruby/test/rubygems/invalid_client.pem
new file mode 100644
index 0000000..e0d3951
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalid_client.pem
@@ -0,0 +1,49 @@
+-----BEGIN CERTIFICATE-----
+MIIDgTCCAmmgAwIBAgICEAIwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUx
+EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg
+UHR5IEx0ZDAeFw0xMzA1MDExNTAxMzFaFw0yMzAzMTAxNTAxMzFaMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eXXXdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw
++lcrpdWcloQCgAlxcM3GjvBxZ3yjzi6SgXKRBSi54i0J1LXxznWKcJ5P/O1+j+7i
+LjHK+OWqsa0+EbKTwSu+0tx20h0z++YJF9GWEoCwT5aH1kor/0+EQLgYnxBaF8GC
+2xAbkRkWmbSu2aLDIey3lg7lqAazYqdS2wH0UjSDjFKDLxz9LwpfFm0yGL3DgwLW
++dobYkgt1A6F/8Pz6D2FjwYKcM8JE6w7KJSJDUvXcv2E18wmhZ/qF/MtFAF4coB1
+f5ALnz8YqY6eyDF5aY/VfaHZvXdirLlMH6/miie9GBVMnJWF0ah5ssbsMvcpmnDJ
+qkiYju2e1oLFEE7zztU/AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN
+BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTcOELj
+hSUdiLrdRF3CFZDZkWaGzDAfBgNVHSMEGDAWgBS7B027H/ZIkW3ngm1SrR0X/aTC
+wDANBgkqhkiG9w0BAQUFAAOCAQEAlQMzHlnT6L1qqA4hL6tABPbiMsVwXyKCcfNB
+zBn82Wkxgbg7Mp31fbR6/qvGeXOtaX6IdPdgtVf8nh1NURk0MmFBP+gfnwfNBD+m
+Q1cldDt9kY2LGIrPii40xbugF1/xqEYcZMgXU08aEvQ2IHX46J8wZoqMa2KhrU8/
+mzY0F+UEFOGWtKDgUzz3dyBPsdzVrX+SXULwH0lqZX8Nsw5LyfrlVt3xQvS5Ogm4
+kYlt8kqhF8lUS3WTbuADrIs3NaDPRWSs1iLRRFgosgUtHN7tkrkrVaHeBo0KbAJG
+mMqtxSY0XZI9WBxffP9UtoY3EiTWNVWLtuCN3OSvryP6NDe4BA==
+-----END CERTIFICATE-----
+
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAsPpXK6XVnJaEAoAJcXDNxo7wcWd8o84ukoFykQUoueItCdS1
+8c51inCeT/ztfo/u4i4xyvjlqrGtPhGyk8ErvtLcdtIdM/vmCRfRlhKAsE+Wh9ZK
+K/9PhEC4GJ8QWhfBgtsQG5EZFpm0rtmiwyHst5YO5agGs2KnUtsB9FI0g4xSgy8c
+/S8KXxZtMhi9w4MC1vnaG2JILdQOhf/D8+g9hY8GCnDPCROsOyiUiQ1L13L9hNfM
+JoWf6hfzLRQBeHKAdX+QC58/GKmOnsgxeWmP1X2h2b13Yqy5TB+v5oonvRgVTJyV
+hdGoebLG7DL3KZpwyapImI7tntaCxRBO887VPwIDAQABAoIBAFOpdG3gzlNg3/Ti
+nBQxdEVqKwYhGs3A2UlOwl8F5lPBNPNRx9UQeYZBaMV9VrQezJnFpqpB8Sg5KCGQ
+ci/hAJIL0kalW0LI0Nz5ko10H7u5U/rQ9W1JG0j041JYV32Pf14husKdXBPQA5co
+sQW30tSSrmYogUpp15mWiJz8A3EvqiCTlQv5JwwMFGnjVl8+HNfuLghK/vqY/Eb9
+YmwTKxPFejqN7E0Mud2ylNiuPTSLwBy8UvV9uxOlDc6lMyZjVRO0woiEzrjw5dKF
+yf5tUkICRcPkekcx+XtpGrCMlRLl770bZBZX+YNmbYXVWhFp09cNR+U0KZqPNcDp
+jg73vXECgYEA3huOKzfHGt3qUdMlEHd1FvQhW9fYIrmUSnuVYQJOnY8lFfKfmrOH
+gpwOIHDNiVHYlhAJaNocCLYx4hWHgZXarY7NKxmlY2+Vp8mcCIf2Cw3Kr/sFklUJ
+KpiRxqEPGR7U4C/E31kkH/C+w7m9Zh3ndhltU2Pki9/Eq0lk8YClMMkCgYEAy/vU
+jxzviIk8bll5uCIuXJyCfao7ywaZABbL6a20kdVGKrHj57O/OJ2WZVwBihhB7OS+
+QsKC/J8LrUJkobOFtQvQ8O23uep5rB6kqCkXsXCG4SCl2L5xZySBp/qhiqbuMwvp
+EAWPSIA6UNoR0J2rDYVmq6jtY526wQf5ivE8IccCgYEAphfzJAyNH2FOZixArmS2
+shiUjasG3UjsRRrP5YClK5wtPpF2m2if8KMkyUux2HvVPLr3XmqkxjsBaLFy6QwY
+QOvmL9H45Tg/sP7KaXLLIw8IQLu2OezPcwQvF1u//6gXxyLR1bhClIQjFBjlMuUv
+/xgasl6kPZlz6Cd1jkgGwEkCgYAI1IT2EQWZfn9cM4leXDRvk+LeN8FQ35897r6z
+Be78JSRdcsfv3ssXU1MQXjQ+2x/3dkt6LltnPidOP8KFcXUHSlSoKVI7vRe5SLZO
+BUFeUAW2tygWwt+73Eu0jtfxXZqQISLcq7DxLYPYvifpRPoDotO3+J8WIdzUwFig
+GCNHPwKBgHqXOyRef7ykVUCptMf61/BvNU8NP1f9PkKQBMYQZC39UwqEQ675QBUh
+hSG9t/kyc44zUVmBeKIlWHVyLQ83Dv+ennz/D9t7tstet0VMKvALNdiVT0sjFKN7
+1VINygCeFkqrlTXlOwFcRSo1gHn3/JIrhSgRuYKHSf0GZOcN6d9l
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/invalid_issuer_cert.pem b/jni/ruby/test/rubygems/invalid_issuer_cert.pem
new file mode 100644
index 0000000..31812aa
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalid_issuer_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8DCCAdigAwIBAgIBCjANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTEyMTIwODAwMDAwMFoYDzk5
+OTkxMjMxMjM1OTU5WjArMRAwDgYDVQQDDAdpbnZhbGlkMRcwFQYKCZImiZPyLGQB
+GRYHZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM0ycOSh
+SOKDGhDlPZ3+JAWwp7KB2ps/VlLhIsZl0uAHzCAGPNI1I46gmla1g7Gh429fYeJ0
+VgeRTxI/rRWNJ0HX2I9jhaJqAkiOqV5Rk6RkJv/0lU1VSu1IBjKRmV6qXiNeBWA8
+duEPNdd6zKX7UoKcYgmG3BMDuEy67AqWUgZOjc9WUnd6i+mwWciMuNqul69vMvB5
+go4c/rgtp1Y3PhLDIrheYP9s+Bza1MNp0FUFlrPnL5gzZTsP/IX2u7kF3CEhKCZX
+ZPX0oZc/pbqIS2OuQ9T6ft6StSwA+9IhAyCeJ9bGyBYK78SyiSfELKyGKbk74SmR
+AqjpN2PJX3o/gk8CAwEAAaMeMBwwGgYDVR0RBBMwEYEPaW52YWxpZEBleGFtcGxl
+MA0GCSqGSIb3DQEBBQUAA4IBAQAfnWXhF0gpfdJPgYtgA8gUdezW9pImBWlWaZmX
+DJhdGBe4pAtsGYHCC1B2lcyRYhQQEibfFrtHeJk1O/vmVw7cuIltVxkpof/0hWA2
+zqpO7UWjYuVxxaZuWGF6MhFWLHLHq1Q8ppXEcMPyP/mGcf3bjYmyFk0f1IslTMVn
+2CJPXO2k8v07dH1ljgcM9X0wmPILQzDzLAJzwPD1iB602ODuie9xXH6ySaVU0n9p
++EJfW1GZysdbcUJ36osY0AO8kjjFbgJG/kM34WFGDixm+jDfMvZTsxlRZc2XNBgS
+2PWvb4CJ1kDwIoMzdSt9MavbvSHkxJllplpUpQqv0Or1Dh9y
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/invalid_issuer_cert_32.pem b/jni/ruby/test/rubygems/invalid_issuer_cert_32.pem
new file mode 100644
index 0000000..3a06ceb
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalid_issuer_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7jCCAdagAwIBAgIBCzANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTEyMTIwODAwMDAwMFoXDTM4
+MDExOTAzMTQwN1owKzEQMA4GA1UEAwwHaW52YWxpZDEXMBUGCgmSJomT8ixkARkW
+B2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNMnDkoUji
+gxoQ5T2d/iQFsKeygdqbP1ZS4SLGZdLgB8wgBjzSNSOOoJpWtYOxoeNvX2HidFYH
+kU8SP60VjSdB19iPY4WiagJIjqleUZOkZCb/9JVNVUrtSAYykZleql4jXgVgPHbh
+DzXXesyl+1KCnGIJhtwTA7hMuuwKllIGTo3PVlJ3eovpsFnIjLjarpevbzLweYKO
+HP64LadWNz4SwyK4XmD/bPgc2tTDadBVBZaz5y+YM2U7D/yF9ru5BdwhISgmV2T1
+9KGXP6W6iEtjrkPU+n7ekrUsAPvSIQMgnifWxsgWCu/EsoknxCyshim5O+EpkQKo
+6TdjyV96P4JPAgMBAAGjHjAcMBoGA1UdEQQTMBGBD2ludmFsaWRAZXhhbXBsZTAN
+BgkqhkiG9w0BAQUFAAOCAQEAPNSZx48cauKsTdhSwdzYSnIYjzPT6ZxoJXCpX8hu
+uOwyZ85i5nSX8rQvjv3IAtQpKQ+1G6odOXkt+zMz1z4dtpo3uEydpWOFKESna1vy
+d3qF+iWUxY8varWOi0lE6ZfFVDsrruVz9m9dcTm2MlGspBeUZrHH2BixEr1/pUAI
+DdBtptZ5qNIxZ4GQJGqrw9j410hjKwm0xV8eWP/D6N640bTzZu06iZbO49exmMSc
+eH3jJMmynviDK3H+GqXIrnSbjbo5hRbEGv0FlYBQijYQw0ttp416Go7kS6cCPSy3
+nUPSMSPr+o5WqU1BKxIdJJSHVZIUk7LfKDOo7LUOJhygwA==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/invalid_key.pem b/jni/ruby/test/rubygems/invalid_key.pem
new file mode 100644
index 0000000..74bedab
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalid_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAzTJw5KFI4oMaEOU9nf4kBbCnsoHamz9WUuEixmXS4AfMIAY8
+0jUjjqCaVrWDsaHjb19h4nRWB5FPEj+tFY0nQdfYj2OFomoCSI6pXlGTpGQm//SV
+TVVK7UgGMpGZXqpeI14FYDx24Q8113rMpftSgpxiCYbcEwO4TLrsCpZSBk6Nz1ZS
+d3qL6bBZyIy42q6Xr28y8HmCjhz+uC2nVjc+EsMiuF5g/2z4HNrUw2nQVQWWs+cv
+mDNlOw/8hfa7uQXcISEoJldk9fShlz+luohLY65D1Pp+3pK1LAD70iEDIJ4n1sbI
+FgrvxLKJJ8QsrIYpuTvhKZECqOk3Y8lfej+CTwIDAQABAoIBAFo5I4pjoDh4jK2B
+HmapqA0Yb6P9lLFOWBZ5B2FUxDPdOdOa6oNC+i9sTnBxv0YLeIUv20pG/My3B51u
+ghxHxEsfLQlfKRMQqZBdqfrew5w0rTE9yagHKLrMQG1bt6P4JQxH+vUloN+0YGgu
+hm005EKpoDGwKnPx3sdeKQs+rTI37fGLw40ZPSod2B7UkSna4/u/uXa1yFbONlqq
+P4ozsDd1xoCaBJfwie2h93KneSPwyHHumlVgbFAJcQycuD5qproYbea9PppppQ+4
+17M9ccuQpS2j9rZWgk7+KqwLrYqzajmtHoQH4zkBYd0JQ4ywGKg307xDhppMZpq3
+uMqnXgECgYEA9iYB7vX91Q3HsRtUAd0aZ91lDIv9RXBP+PuK3OUrtChdtZx7Mih3
+uTgtfVEnP7bxgnPpwecnF4Ncginuu7TBtxjoztFjuFNzk/8Fz/J3i/H3oB0SdjhL
+uqVa6P4hPak7FnWynZHivuuIe+eKYLvK0TCMbYaz6G81xi0Whtbm498CgYEA1Wja
+PgscP/9YykCC6eRP+ixo6chGzSNJR+WRW9CJYpXEicPlaT5AI3nSZVXXSLvXIbq9
+NoExPu47pDzr9Gd02qYmFWUOolUa21W4s/x4d7lU+wJzS6ZNTFoC8woZagL2kZ5G
++we5ifbUz7eG+ahZODGMGA9BJVT3PI6zPdKtr5ECgYEAy0ORnypGBXUOrUMa+TsD
+fjfGJTlI2dmoQLw/7K/Wijw3PizNUxs12p74eZ7VYXkKMKbVpwjiMDmK3/YOrbTT
+rwaD4Z3p0iIftFwJCbJ5Y/hZez/mqfdNGgFIdFS/UHL6V060RAhfjTdlCqSmkcEh
+9+M2Y4+z60JCzrcW/hxiqFMCgYEAj9ntwoSatkjZAPwbQq2ze18UGQH3N6/hZaVJ
+JiqbcOijYnm52gcsFL25JLWIOG7lxMarZGIRX+oWKc8m/cf+7KOyaBmGk8XqJI7T
+wf8c9RboQYqVTRj8YcsK0eis2NjGe8HE9tFuL6FCMgHz6bWg7k/3rwAZWaC8RwWp
+rLKmgQECgYBXGjEvogVeYMgnpzjoaa99wvfp6FtbRx1jZ+FOSBoH5uCRDalD5Q16
+0UVnoPcnj0Hi7Hvvl6jTLesRW/LDra5Hqyxs4yuSBagEUFv/PvY0eYGZ5egGZgaa
+PlVmxgk33xYXar8wGHLkstwqZY/OqT89cKvJqeLKMb0G2Re13oPVww==
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/invalid_signer_cert.pem b/jni/ruby/test/rubygems/invalid_signer_cert.pem
new file mode 100644
index 0000000..6d6c648
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalid_signer_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIBDDANBgkqhkiG9w0BAQUFADArMRAwDgYDVQQDDAdpbnZh
+bGlkMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTAgFw0xMjEyMDgwMDAwMDBaGA85
+OTk5MTIzMTIzNTk1OVowKzEQMA4GA1UEAwwHaW52YWxpZDEXMBUGCgmSJomT8ixk
+ARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNMnDk
+oUjigxoQ5T2d/iQFsKeygdqbP1ZS4SLGZdLgB8wgBjzSNSOOoJpWtYOxoeNvX2Hi
+dFYHkU8SP60VjSdB19iPY4WiagJIjqleUZOkZCb/9JVNVUrtSAYykZleql4jXgVg
+PHbhDzXXesyl+1KCnGIJhtwTA7hMuuwKllIGTo3PVlJ3eovpsFnIjLjarpevbzLw
+eYKOHP64LadWNz4SwyK4XmD/bPgc2tTDadBVBZaz5y+YM2U7D/yF9ru5BdwhISgm
+V2T19KGXP6W6iEtjrkPU+n7ekrUsAPvSIQMgnifWxsgWCu/EsoknxCyshim5O+Ep
+kQKo6TdjyV96P4JPAgMBAAGjHjAcMBoGA1UdEQQTMBGBD2ludmFsaWRAZXhhbXBs
+ZTANBgkqhkiG9w0BAQUFAAOCAQEAd0sGT1z4eppqGMxGdx5ZjXRbgKJZNwz2jHGJ
+CbceA4slPLQOwLXKscXm7QkhMg7L0SJWX2WgLbY/Re5jrexgXnM/fxiIW/wLo5Bv
+V+ajcbZt0uC5scX9DSpwUftlQiMIARbwWb5Lg4UDzT3nB44BgrUIx/YC2084BSsb
+Lrr3YNW4ZGyxN6qVIILCemg3YpsmFspQtsCdRUsn/5Hjc0J05qA6XDSNXRZYYGA2
+tstMcYuwmhp6WjIEWU0i6t84ZKzOrNm2qwjhT6nYYJvvKQtexa1W/WTM5IHKcxiA
+oWnrRCZWt33UtHF4//zjXSJm0S8gb8FDRicxS5CbDiVe20GDkA==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/invalid_signer_cert_32.pem b/jni/ruby/test/rubygems/invalid_signer_cert_32.pem
new file mode 100644
index 0000000..0f4ddcf
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalid_signer_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7zCCAdegAwIBAgIBDTANBgkqhkiG9w0BAQUFADArMRAwDgYDVQQDDAdpbnZh
+bGlkMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTAeFw0xMjEyMDgwMDAwMDBaFw0z
+ODAxMTkwMzE0MDdaMCsxEDAOBgNVBAMMB2ludmFsaWQxFzAVBgoJkiaJk/IsZAEZ
+FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTJw5KFI
+4oMaEOU9nf4kBbCnsoHamz9WUuEixmXS4AfMIAY80jUjjqCaVrWDsaHjb19h4nRW
+B5FPEj+tFY0nQdfYj2OFomoCSI6pXlGTpGQm//SVTVVK7UgGMpGZXqpeI14FYDx2
+4Q8113rMpftSgpxiCYbcEwO4TLrsCpZSBk6Nz1ZSd3qL6bBZyIy42q6Xr28y8HmC
+jhz+uC2nVjc+EsMiuF5g/2z4HNrUw2nQVQWWs+cvmDNlOw/8hfa7uQXcISEoJldk
+9fShlz+luohLY65D1Pp+3pK1LAD70iEDIJ4n1sbIFgrvxLKJJ8QsrIYpuTvhKZEC
+qOk3Y8lfej+CTwIDAQABox4wHDAaBgNVHREEEzARgQ9pbnZhbGlkQGV4YW1wbGUw
+DQYJKoZIhvcNAQEFBQADggEBAE8RJTY1E6DBOEt5azE9wQGJ7yrWJNHLhtP0nmkd
+eaIraloJcqss86qgbH4NY81mvm7nhB6/UEcbm218b6roTDOEjvBp1sKtZ7sqt+J0
+gFqAocBStTkPucmbsDr0B6bUmeHxgpCt+QoaOh6Fwh5yizfpl9i7oMU4QLhf1eZ3
+K1PrPvUle2JFfzJ3SFDlU9C/oA9yDQGnJ7efUCFKvg9M9CzgAHFJyQNb/47tmqHF
+2uxSwEy+ADbD0fPw0r5zkejEimBHWcaTHxqQ12GhS5PkUBYm/qW9a6wyBBO2nO6u
+Tr1zrCDc728aPjN4Qh76xUy/hyCcSgXalhz1LMHgv0VDx/M=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/invalidchild_cert.pem b/jni/ruby/test/rubygems/invalidchild_cert.pem
new file mode 100644
index 0000000..5a79c83
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalidchild_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAeGgAwIBAgIBDjANBgkqhkiG9w0BAQUFADApMQ4wDAYDVQQDDAVjaGls
+ZDEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwIBcNMTIxMjA4MDAwMDAwWhgPOTk5
+OTEyMzEyMzU5NTlaMDAxFTATBgNVBAMMDGludmFsaWRjaGlsZDEXMBUGCgmSJomT
+8ixkARkWB2V4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDl
+rDAlDZwmP6Zxc4sSaOdSRRXJBmbQubxmWpqU8bXrTKCkvg1l/2U6weJIS52zW6Te
+Zok7Uus5jywyeNSJ/MBHb7X5ytAPQsvLu/3WFwaVfHJzimQI4vtmx9+CDgZzipYp
+ett7IF18He8DPiFur1xCn8pp0qOOV8mXL/8TpUgJfmaOJosqgFqorj5niqF52IwJ
+vtur/gwpq2xpCtYaCUB/dFzzefLV37kt58S6jTmZnYf4kIdFKhhyTeGmDRf/wOz+
+kK/H/aKtpsYgzI//bo+bsuWNFceIdWrdCBr63cVs4ql7VN7p2xfn9ckEfwH6wFut
+QLquA/6fRkgUFF8fxUiHAgMBAAGjIzAhMB8GA1UdEQQYMBaBFGludmFsaWRjaGls
+ZEBleGFtcGxlMA0GCSqGSIb3DQEBBQUAA4IBAQBvNQFrG8ye8vyHmnfX5n3++OEu
+k6a+Jr8BlLL3jqI8LPmANGgd4May7HXua4bVZjAx+EtChXd1jcCP8pJ1AZaSHZIF
+icdhxfKjD6JfvgVQOKQluOfDJ0Tg1GyONZK29m7fOrsHbB+OIaJhsce3MEXdGVpF
+cppM3lEaI2PGaASWf3gqVOytTdRFqMStaDWY5rfy+LxgBeGkw9GjUkdMxdFZBs00
+dunhXTI8pk/QyqGhoDH3/iHansIB6CCH9C5gysEuFErp/mpvobCVn1aLcs7EY6vC
+1BZL1LMVJ8fL1gGwpgDe6R+X5oba/wzwt3wXrC2e4r9zy2qtWgfS/REYA49B
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/invalidchild_cert_32.pem b/jni/ruby/test/rubygems/invalidchild_cert_32.pem
new file mode 100644
index 0000000..126b60a
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalidchild_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9zCCAd+gAwIBAgIBDzANBgkqhkiG9w0BAQUFADApMQ4wDAYDVQQDDAVjaGls
+ZDEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwHhcNMTIxMjA4MDAwMDAwWhcNMzgw
+MTE5MDMxNDA3WjAwMRUwEwYDVQQDDAxpbnZhbGlkY2hpbGQxFzAVBgoJkiaJk/Is
+ZAEZFgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5aww
+JQ2cJj+mcXOLEmjnUkUVyQZm0Lm8ZlqalPG160ygpL4NZf9lOsHiSEuds1uk3maJ
+O1LrOY8sMnjUifzAR2+1+crQD0LLy7v91hcGlXxyc4pkCOL7Zsffgg4Gc4qWKXrb
+eyBdfB3vAz4hbq9cQp/KadKjjlfJly//E6VICX5mjiaLKoBaqK4+Z4qhediMCb7b
+q/4MKatsaQrWGglAf3Rc83ny1d+5LefEuo05mZ2H+JCHRSoYck3hpg0X/8Ds/pCv
+x/2irabGIMyP/26Pm7LljRXHiHVq3Qga+t3FbOKpe1Te6dsX5/XJBH8B+sBbrUC6
+rgP+n0ZIFBRfH8VIhwIDAQABoyMwITAfBgNVHREEGDAWgRRpbnZhbGlkY2hpbGRA
+ZXhhbXBsZTANBgkqhkiG9w0BAQUFAAOCAQEAn81wRiztOz+u74TbUqBfkpG4QMZW
+4mjGbM9eF6swyQ3fpdJO9hM9VK8V46IIoxqp5sqd9j2ZB7DAB1Fr84LD1NopWPZD
+Ui07O/qfwX360alIBwRsRKdZJdH0YykXZErPbMkQOk8SM4SVD0yUoGea6HwHBwKs
+UXK9LDhTm73o7k0md34dwa0s/rG0cUhfVuDWB2cZ6o5Y6B4ZXcTMG60QAoH763NP
+eyBzrlfIz/03O6K7HKGgQc35WaMeNXovrZcOWHgtpSjev5Ugu7LnLiTYOwn82c3r
+7bjGiFG4g6k7aSzHigUasqG0v7NLz1ZQ1mgMTIqwXWRap0RCuIJl11UF4g==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/invalidchild_key.pem b/jni/ruby/test/rubygems/invalidchild_key.pem
new file mode 100644
index 0000000..9706c95
--- /dev/null
+++ b/jni/ruby/test/rubygems/invalidchild_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEA5awwJQ2cJj+mcXOLEmjnUkUVyQZm0Lm8ZlqalPG160ygpL4N
+Zf9lOsHiSEuds1uk3maJO1LrOY8sMnjUifzAR2+1+crQD0LLy7v91hcGlXxyc4pk
+COL7Zsffgg4Gc4qWKXrbeyBdfB3vAz4hbq9cQp/KadKjjlfJly//E6VICX5mjiaL
+KoBaqK4+Z4qhediMCb7bq/4MKatsaQrWGglAf3Rc83ny1d+5LefEuo05mZ2H+JCH
+RSoYck3hpg0X/8Ds/pCvx/2irabGIMyP/26Pm7LljRXHiHVq3Qga+t3FbOKpe1Te
+6dsX5/XJBH8B+sBbrUC6rgP+n0ZIFBRfH8VIhwIDAQABAoIBAQC59hllZwev0Ims
+AqnwVhA2hMmG4zAMgNcS6PmQ78Ukp/7KZTfkBk6orKPTdaZSuzla+hrTdegPyuU2
+WK9+qq/lJ4ZootakBKmOZMC6wBoMn57r/nnQ2DhGmD9YxpJiqyu6mkdsAmCvRm1o
+ar4XKNXC/C6gUHUto9cOG0alWYZiZ/VMe/nhPTChr2Dhd+bavO1yx7/CxB+VQMfQ
+l6ihbv//3KgPJAElbaI7jfOGzX6KlwXSGf70REmZQnPGN4/n46/dLFFuA1HzcA5Z
+37NU1zgN2nIrXld8rsR1mSy6EwU46sW3AkEwv6SUajCjz7PCmmWxRaQErGJjZrUq
+sujNj5RBAoGBAPgdiY+6B7WvmLlCBCwI4PXjgRQ/6A1Ycgvi1LdSQzccSHddogNI
+tWKa0pIrYyH7y7jB/UzstFSnsOXAf4H6Xt70VUrFPq1/dRRw1CtSLA1sFspBAD8v
+aGl9R0XqWOk1t60mfgES9b4LJu46cTm7UMfyC7EbWkqHYWqf15umRgwrAoGBAOz4
+nZGqBVBW/ERDs+Imf9NcwDeuwllQ0S9ZBPHF///SQ4Rscz2Bl8GhjhTHldLNJg9k
+HjP8W2BOPas66K3WM+WC3AiGrdJfs6Ju3r27X4wA0hnNc6bcoRaoSNRaqThSkgCH
+l34l7yrB1gwpa5HlIfYXjHfJ7coX7WRMQK7wmVsVAoGBAJ/Y97z/DuSAgpYn7+Qm
+vDfuIETZfzjJ2H/L3VtVxjQFJrKwQiZ3e1RRhoPhK/bC79bSM8yRWwSHHLKIOB2X
+HfPp2eFX/i9sxBMtNaPLRtJG5s/a3LvYYR5FNdvXRPzKPNFy0Q8EFgofyS8Fu9iD
+02FdkSbDBoKpgZtd61w93TcNAoGBAKtM4SKeRC8aYku6oTtW10pkHvNhmk5UVJMk
+h6V6mx9D0NjWSMvqdVhcv8eXq19yOxQfLJIp16gbhwrTj8WyNVuwp/xl1xtfYQyH
+lu6Sl3QuV7KdSQATN0OYrOUNEIyNa8uEOOfQ5j4DVwb9niwd9dnelgU17HYNq+a4
+FH4hoMotAoGBAJk/9+RPAdxqJsr/oVp9E4wU9ffpZ2Lr0faN7/WqBFPPhhFOMWu2
+zj8fcRaP/9Wv9g2xK/GfCKhrX8FMfq/NMkZsNx6V3W0M8Zbarp9ZvA4Sj0OvsZAO
+J1NQjkvFjMCE0A29jtjY1zRmLzoC+Ds7Ola8IOKvAN8SM1X/CC6bOgGz
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/plugin/exception/rubygems_plugin.rb b/jni/ruby/test/rubygems/plugin/exception/rubygems_plugin.rb
new file mode 100644
index 0000000..16c417e
--- /dev/null
+++ b/jni/ruby/test/rubygems/plugin/exception/rubygems_plugin.rb
@@ -0,0 +1,2 @@
+TestGem::TEST_PLUGIN_EXCEPTION = :loaded
+raise Exception.new('boom')
diff --git a/jni/ruby/test/rubygems/plugin/load/rubygems_plugin.rb b/jni/ruby/test/rubygems/plugin/load/rubygems_plugin.rb
new file mode 100644
index 0000000..c7c7277
--- /dev/null
+++ b/jni/ruby/test/rubygems/plugin/load/rubygems_plugin.rb
@@ -0,0 +1,3 @@
+class TestGem
+ TEST_PLUGIN_LOAD = :loaded
+end
diff --git a/jni/ruby/test/rubygems/plugin/standarderror/rubygems_plugin.rb b/jni/ruby/test/rubygems/plugin/standarderror/rubygems_plugin.rb
new file mode 100644
index 0000000..4b577a6
--- /dev/null
+++ b/jni/ruby/test/rubygems/plugin/standarderror/rubygems_plugin.rb
@@ -0,0 +1,2 @@
+TestGem::TEST_PLUGIN_STANDARDERROR = :loaded
+raise StandardError.new('boom')
diff --git a/jni/ruby/test/rubygems/private_key.pem b/jni/ruby/test/rubygems/private_key.pem
new file mode 100644
index 0000000..c6ed3fc
--- /dev/null
+++ b/jni/ruby/test/rubygems/private_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAp2U1hy8UHrGClPxByczJtV6UYtQrJAv+FA9Mr0nkXKoHyEQM
+u3au4zAqwdTp+7+aAb6wu8cXWepaFkAOGfqvAJ80/TfTbm+S05nqIl9TrS/K1j9/
+eCSIY2Q/bWXSHPT4yzXl/7naCT6wVerAYFsO14jTHntSweL4oA3rtC48Oe2FgO3C
+FcgDmlx3HbAf41bwXzYcRm+bWFykDvkENWTi8/GekN+884pJif11aCGZS1o+arJW
++zxeQPEcN9jnj8PfOI96E/7NDMSDwLTtKr/Pq8tI5De5pifScEO40Tpc/eKMnhm+
+zZ4kR04zJLUfcKyeRaJ48Ksu0p3Dx38X4PluhwIDAQABAoIBAAx09qfJtBiYoxwN
+LaQjzjrl/+re2RsEnXLGtLEysYDH0m5vyfbFXTxg4D2uZ38pgf9xPluq9CznyK5x
+M9txEUbdkibp2Z0VRnrisE7Ag0yXCuQos4awSUoEMsgkVJ99B2qv5x7BqN0ZQiwS
+nSBOhms5rmRNTxpIlrHqd0jgS/EPggnqVzNcM4/K8PJFthwEBKDmzOyiRByvz54Y
+shzOnTjGtV2oGNgwpzmCXce1yO7dh2IdKnSnmeFwyU88GxEYnGh5MIFuTiyErP72
+k6iEUfiXy0hxk/iXmKs8UyD1lVnwTNWcZcpV8yw4a06Z6nkSnwQm0SSOVIo/w35V
+jdVdUkECgYEA3GhZ70MD/Q47GFvz6BovwQvxhjFN+nIEbBfi7OTkuXprKdhVhjaR
+nERPZpZjHWrcfgbFcvPY7/GJLTPN/VF1nhOsOZpzfAmCgBujRXrzlAGpU877ZNJA
+QKPgzo+iv/RsQCIdrzF1gwHkqD2v1HRLaqb2+dVumiG4Qp3NXgasT2cCgYEAwm1U
+uRDXgQKGODeLK8eSVpfMjD5umBVu7m4D3ZmipbN6sMBxGMAlsU40eQ7DBFH0AFft
+s2D88JdjlwoOrbXYYpOc6iWD/QkygJfPpA9VQx92hv8KBd82gLHuXYMd0T0G3yZO
+gPPioeRgl2TvgVCfjn6AYr3Ubt3rB5aBlSplE+ECgYEAiXhcf6rg1fkGSs8vddi/
+aDy2y+f8pvRuZa0QUIkDT9xW8qaH0Uo/z6ObknTCJRr9o209wdDtwdp4oMTq+dDQ
+92N1zAfVd8vGpXiXgUKKognXPvqeOegZQzfzg2J7NBaTXfzpXtgOX0PTBkxTWsOe
+NkslR/YjIedeMc6SxM6MsokCgYA3mTYyGevWe5dQOin1IgPp+UzICg5sNSzcx98Z
+HpcRVWrPYqi00DW3J0sAF0WTVbA17O8PbbvHPTOAfKLH8Alp3xZvKr08vcWQWllJ
+bA0Qvc2SOxptpXAbi0ZDvXvoWtA9PeITJCr56qnogTewPhLyl6A1HF3EOne8WsDB
+nDb9YQKBgEyUDWhDBGXUfQN0fWy5ksqCCeHXQzvt6aEUstWvkkbnnarUfLAhBIqC
+2B6omokICmWzvAfDt3UsRbb3QJUBxbbVsZVM7Vr+kY2cQ1Ma093I/2mXDoq3bV+j
+LZM5+Uc7xSfiCi1hbVhGm96DXofudddo86W5mhXp3xhcQP1Fl4JZ
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/public_cert.pem b/jni/ruby/test/rubygems/public_cert.pem
new file mode 100644
index 0000000..5642a6f
--- /dev/null
+++ b/jni/ruby/test/rubygems/public_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7jCCAdagAwIBAgIBEDANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTEyMTIwODAwMDAwMFoYDzk5
+OTkxMjMxMjM1OTU5WjAqMQ8wDQYDVQQDDAZub2JvZHkxFzAVBgoJkiaJk/IsZAEZ
+FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2U1hy8U
+HrGClPxByczJtV6UYtQrJAv+FA9Mr0nkXKoHyEQMu3au4zAqwdTp+7+aAb6wu8cX
+WepaFkAOGfqvAJ80/TfTbm+S05nqIl9TrS/K1j9/eCSIY2Q/bWXSHPT4yzXl/7na
+CT6wVerAYFsO14jTHntSweL4oA3rtC48Oe2FgO3CFcgDmlx3HbAf41bwXzYcRm+b
+WFykDvkENWTi8/GekN+884pJif11aCGZS1o+arJW+zxeQPEcN9jnj8PfOI96E/7N
+DMSDwLTtKr/Pq8tI5De5pifScEO40Tpc/eKMnhm+zZ4kR04zJLUfcKyeRaJ48Ksu
+0p3Dx38X4PluhwIDAQABox0wGzAZBgNVHREEEjAQgQ5ub2JvZHlAZXhhbXBsZTAN
+BgkqhkiG9w0BAQUFAAOCAQEAVmEqsyWID85F39fRKe09sFfguIAUJ8H7/8N9lHP/
+dBLbHmESgPaqh0u57Ys3Jf73ecVtJ93CYJlescvytg16rzLpbEwrojvJwYtLLeLP
+Nx0pKopmJ5+/wxvymrmq149mc9esQXgS1SSAN0X3mcYLNcsAQj4p1vG/1Q8GxMO3
+F655Dqj/8RFXDQOM6yEjRTaOrm1bYGSENog3K7aVGcfYfQeFf97QBId84Ov8B1NY
+EmZsUGqvGkMzfj7y7sWBlFqeZl8HUtGp9dFW7ONg+Ui8Gtqdxv43YFdEg3cA7juj
+X9Abwpdj93pgmleN6zEyT0hYx5S7/dadxLgZat+NTpXnrA==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/public_cert_32.pem b/jni/ruby/test/rubygems/public_cert_32.pem
new file mode 100644
index 0000000..dcc8a62
--- /dev/null
+++ b/jni/ruby/test/rubygems/public_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCAdSgAwIBAgIBETANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTEyMTIwODAwMDAwMFoXDTM4
+MDExOTAzMTQwN1owKjEPMA0GA1UEAwwGbm9ib2R5MRcwFQYKCZImiZPyLGQBGRYH
+ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdlNYcvFB6x
+gpT8QcnMybVelGLUKyQL/hQPTK9J5FyqB8hEDLt2ruMwKsHU6fu/mgG+sLvHF1nq
+WhZADhn6rwCfNP03025vktOZ6iJfU60vytY/f3gkiGNkP21l0hz0+Ms15f+52gk+
+sFXqwGBbDteI0x57UsHi+KAN67QuPDnthYDtwhXIA5pcdx2wH+NW8F82HEZvm1hc
+pA75BDVk4vPxnpDfvPOKSYn9dWghmUtaPmqyVvs8XkDxHDfY54/D3ziPehP+zQzE
+g8C07Sq/z6vLSOQ3uaYn0nBDuNE6XP3ijJ4Zvs2eJEdOMyS1H3CsnkWiePCrLtKd
+w8d/F+D5bocCAwEAAaMdMBswGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwDQYJ
+KoZIhvcNAQEFBQADggEBABx3grWVpcJ2RRP/vhT9X7xxdAeEwNahEb4pqHcN57Mm
+S1vH1anaNk9BK4sZLf82Ers6DeGQC8PrBunwBvCdOhFvBMBgcUDGzjJgMyc4RXmi
+0WQPtWXjntXtNm3mlvJM7k4ezMLZwUIRL7+UQXewdM778phZdDVoqpuInAiz/A/x
++gJvTJF4lUVf7wEwNljrRIIOGZaX1S/qJb1rw+hugbB6kA2WLJehsZfzSNxaM+cT
+LlgUh2luXvX1ZSwMQJ1SrZQ9rZLwuaBV2kcVUfAi0peQ03ETZdhjrMRmIbpa+iw5
+vmviY+p7FvjhkwCrVNjiF0CEtPAL3dGi2YHBMDkWY3U=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/public_key.pem b/jni/ruby/test/rubygems/public_key.pem
new file mode 100644
index 0000000..7c29dcd
--- /dev/null
+++ b/jni/ruby/test/rubygems/public_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2U1hy8UHrGClPxByczJ
+tV6UYtQrJAv+FA9Mr0nkXKoHyEQMu3au4zAqwdTp+7+aAb6wu8cXWepaFkAOGfqv
+AJ80/TfTbm+S05nqIl9TrS/K1j9/eCSIY2Q/bWXSHPT4yzXl/7naCT6wVerAYFsO
+14jTHntSweL4oA3rtC48Oe2FgO3CFcgDmlx3HbAf41bwXzYcRm+bWFykDvkENWTi
+8/GekN+884pJif11aCGZS1o+arJW+zxeQPEcN9jnj8PfOI96E/7NDMSDwLTtKr/P
+q8tI5De5pifScEO40Tpc/eKMnhm+zZ4kR04zJLUfcKyeRaJ48Ksu0p3Dx38X4Plu
+hwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/jni/ruby/test/rubygems/rubygems/commands/crash_command.rb b/jni/ruby/test/rubygems/rubygems/commands/crash_command.rb
new file mode 100644
index 0000000..624be9f
--- /dev/null
+++ b/jni/ruby/test/rubygems/rubygems/commands/crash_command.rb
@@ -0,0 +1,5 @@
+class Gem::Commands::CrashCommand < Gem::Command
+
+ raise "crash"
+
+end
diff --git a/jni/ruby/test/rubygems/rubygems_plugin.rb b/jni/ruby/test/rubygems/rubygems_plugin.rb
new file mode 100644
index 0000000..6c08f97
--- /dev/null
+++ b/jni/ruby/test/rubygems/rubygems_plugin.rb
@@ -0,0 +1,21 @@
+require 'rubygems/command_manager'
+
+##
+# This is an example of exactly what NOT to do.
+#
+# DO NOT include code like this in your rubygems_plugin.rb
+
+class Gem::Commands::InterruptCommand < Gem::Command
+
+ def initialize
+ super('interrupt', 'Raises an Interrupt Exception', {})
+ end
+
+ def execute
+ raise Interrupt, "Interrupt exception"
+ end
+
+end
+
+Gem::CommandManager.instance.register_command :interrupt
+
diff --git a/jni/ruby/test/rubygems/sff/discover.rb b/jni/ruby/test/rubygems/sff/discover.rb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/jni/ruby/test/rubygems/sff/discover.rb
diff --git a/jni/ruby/test/rubygems/simple_gem.rb b/jni/ruby/test/rubygems/simple_gem.rb
new file mode 100644
index 0000000..d7c0356
--- /dev/null
+++ b/jni/ruby/test/rubygems/simple_gem.rb
@@ -0,0 +1,66 @@
+SIMPLE_GEM = <<-GEMDATA
+ MD5SUM = "989bf34a1cbecd52e0ea66b662b3a405"
+ if $0 == __FILE__
+ require 'optparse'
+
+ options = {}
+ ARGV.options do |opts|
+ opts.on_tail("--help", "show this message") {puts opts; exit}
+ opts.on('--dir=DIRNAME', "Installation directory for the Gem") {|options[:directory]|}
+ opts.on('--force', "Force Gem to install, bypassing dependency checks") {|options[:force]|}
+ opts.on('--gen-rdoc', "Generate RDoc documentation for the Gem") {|options[:gen_rdoc]|}
+ opts.parse!
+ end
+
+ require 'rubygems'
+ @directory = options[:directory] || Gem.dir
+ @force = options[:force]
+
+ gem = Gem::Installer.new(__FILE__).install(@force, @directory)
+ if options[:gen_rdoc]
+ Gem::DocManager.new(gem).generate_rdoc
+ end
+end
+
+__END__
+--- !ruby/object:Gem::Specification
+rubygems_version: "1.0"
+name: testing
+version: !ruby/object:Gem::Version
+ version: 1.2.3
+date: 2004-03-18 22:01:52.859121 -05:00
+platform:
+summary: This exercise the gem testing stuff.
+require_paths:
+ - lib
+files:
+ - lib/foo.rb
+ - lib/test
+ - lib/test.rb
+ - lib/test/wow.rb
+autorequire: test
+test_suite_file: foo
+requirements:
+ - a computer processor
+---
+-
+ size: 109
+ mode: 420
+ path: lib/foo.rb
+-
+ size: 0
+ mode: 420
+ path: lib/test.rb
+-
+ size: 15
+ mode: 420
+ path: lib/test/wow.rb
+---
+eJwVjDEKgDAUQ/eeIpsKguhY3ARPoHMp9quF0mL7e39/h5DwQpLpqz4TOqbC
+U42eO6WuYEvBntIhECuaaX1KqXXLmy2kAEc32szExK+PjyBAlpTZyK0N/Twu
+g1CKTjX9BGAj1w==
+---
+eJwDAAAAAAE=
+---
+eJwrKC0pVlAvzy9XyE3MU+cCACwiBP4=
+GEMDATA
diff --git a/jni/ruby/test/rubygems/specifications/bar-0.0.2.gemspec b/jni/ruby/test/rubygems/specifications/bar-0.0.2.gemspec
new file mode 100644
index 0000000..ceefa4e
--- /dev/null
+++ b/jni/ruby/test/rubygems/specifications/bar-0.0.2.gemspec
@@ -0,0 +1,9 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = "bar"
+ s.version = "0.0.2"
+ s.platform = "ruby"
+ s.require_paths = ["lib"]
+ s.summary = "A very bar gem"
+end
diff --git a/jni/ruby/test/rubygems/specifications/foo-0.0.1.gemspec b/jni/ruby/test/rubygems/specifications/foo-0.0.1.gemspec
new file mode 100644
index 0000000..7fbc564
--- /dev/null
+++ b/jni/ruby/test/rubygems/specifications/foo-0.0.1.gemspec
Binary files differ
diff --git a/jni/ruby/test/rubygems/ssl_cert.pem b/jni/ruby/test/rubygems/ssl_cert.pem
new file mode 100644
index 0000000..998ccc5
--- /dev/null
+++ b/jni/ruby/test/rubygems/ssl_cert.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/zCCAeegAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQGDAJKUDES
+MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxDjAMBgNVBAMMBVN1YkNB
+MB4XDTA0MDEzMTAzMTMxNloXDTMzMDEyMzAzMTMxNlowQzELMAkGA1UEBgwCSlAx
+EjAQBgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMRIwEAYDVQQDDAlsb2Nh
+bGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANFJTxWqup3nV9dsJAku
+p+WaXnPNIzcpAA3qMGZDJTJsfa8Du7ZxTP0XJK5mETttBrn711cJxAuP3KjqnW9S
+vtZ9lY2sXJ6Zj62sN5LwG3VVe25dI28yR1EsbHjJ5Zjf9tmggMC6am52dxuHbt5/
+vHo4ngJuKE/U+eeGRivMn6gFAgMBAAGjgYUwgYIwDAYDVR0TAQH/BAIwADAxBglg
+hkgBhvhCAQ0EJBYiUnVieS9PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAd
+BgNVHQ4EFgQUpZIyygD9JxFYHHOTEuWOLbCKfckwCwYDVR0PBAQDAgWgMBMGA1Ud
+JQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4IBAQBwAIj5SaBHaA5X31IP
+CFCJiep96awfp7RANO0cuUj+ZpGoFn9d6FXY0g+Eg5wAkCNIzZU5NHN9xsdOpnUo
+zIBbyTfQEPrge1CMWMvL6uGaoEXytq84VTitF/xBTky4KtTn6+es4/e7jrrzeUXQ
+RC46gkHObmDT91RkOEGjHLyld2328jo3DIN/VTHIryDeVHDWjY5dENwpwdkhhm60
+DR9IrNBbXWEe9emtguNXeN0iu1ux0lG1Hc6pWGQxMlRKNvGh0yZB9u5EVe38tOV0
+jQaoNyL7qzcQoXD3Dmbi1p0iRmg/+HngISsz8K7k7MBNVsSclztwgCzTZOBiVtkM
+rRlQ
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/ssl_key.pem b/jni/ruby/test/rubygems/ssl_key.pem
new file mode 100644
index 0000000..9ba2218
--- /dev/null
+++ b/jni/ruby/test/rubygems/ssl_key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDRSU8Vqrqd51fXbCQJLqflml5zzSM3KQAN6jBmQyUybH2vA7u2
+cUz9FySuZhE7bQa5+9dXCcQLj9yo6p1vUr7WfZWNrFyemY+trDeS8Bt1VXtuXSNv
+MkdRLGx4yeWY3/bZoIDAumpudncbh27ef7x6OJ4CbihP1PnnhkYrzJ+oBQIDAQAB
+AoGBAIf4CstW2ltQO7+XYGoex7Hh8s9lTSW/G2vu5Hbr1LTHy3fzAvdq8MvVR12O
+rk9fa+lU9vhzPc0NMB0GIDZ9GcHuhW5hD1Wg9OSCbTOkZDoH3CAFqonjh4Qfwv5W
+IPAFn9KHukdqGXkwEMdErsUaPTy9A1V/aROVEaAY+HJgq/eZAkEA/BP1QMV04WEZ
+Oynzz7/lLizJGGxp2AOvEVtqMoycA/Qk+zdKP8ufE0wbmCE3Qd6GoynavsHb6aGK
+gQobb8zDZwJBANSK6MrXlrZTtEaeZuyOB4mAmRzGzOUVkUyULUjEx2GDT93ujAma
+qm/2d3E+wXAkNSeRpjUmlQXy/2oSqnGvYbMCQQDRM+cYyEcGPUVpWpnj0shrF/QU
+9vSot/X1G775EMTyaw6+BtbyNxVgOIu2J+rqGbn3c+b85XqTXOPL0A2RLYkFAkAm
+syhSDtE9X55aoWsCNZY/vi+i4rvaFoQ/WleogVQAeGVpdo7/DK9t9YWoFBIqth0L
+mGSYFu9ZhvZkvQNV8eYrAkBJ+rOIaLDsmbrgkeDruH+B/9yrm4McDtQ/rgnOGYnH
+LjLpLLOrgUxqpzLWe++EwSLwK2//dHO+SPsQJ4xsyQJy
+-----END RSA PRIVATE KEY-----
diff --git a/jni/ruby/test/rubygems/test_bundled_ca.rb b/jni/ruby/test/rubygems/test_bundled_ca.rb
new file mode 100644
index 0000000..711cd1b
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_bundled_ca.rb
@@ -0,0 +1,60 @@
+require 'rubygems/test_case'
+require 'net/https'
+require 'rubygems/request'
+
+# = Testing Bundled CA
+#
+# The tested hosts are explained in detail here: https://github.com/rubygems/rubygems/commit/5e16a5428f973667cabfa07e94ff939e7a83ebd9
+#
+class TestBundledCA < Gem::TestCase
+
+ THIS_FILE = File.expand_path __FILE__
+
+ def bundled_certificate_store
+ store = OpenSSL::X509::Store.new
+
+ ssl_cert_glob =
+ File.expand_path '../../../lib/rubygems/ssl_certs/*.pem', THIS_FILE
+
+ Dir[ssl_cert_glob].each do |ssl_cert|
+ store.add_file ssl_cert
+ end
+
+ store
+ end
+
+ def assert_https(host)
+ if self.respond_to? :_assertions # minitest <= 4
+ self._assertions += 1
+ else # minitest >= 5
+ self.assertions += 1
+ end
+ http = Net::HTTP.new(host, 443)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ http.cert_store = bundled_certificate_store
+ http.get('/')
+ rescue Errno::ENOENT, Errno::ETIMEDOUT
+ skip "#{host} seems offline, I can't tell whether ssl would work."
+ rescue OpenSSL::SSL::SSLError => e
+ # Only fail for certificate verification errors
+ if e.message =~ /certificate verify failed/
+ flunk "#{host} is not verifiable using the included certificates. Error was: #{e.message}"
+ end
+ raise
+ end
+
+ def test_accessing_rubygems
+ assert_https('rubygems.org')
+ end
+
+ def test_accessing_cloudfront
+ assert_https('d2chzxaqi4y7f8.cloudfront.net')
+ end
+
+ def test_accessing_s3
+ assert_https('s3.amazonaws.com')
+ end
+
+end if ENV['TRAVIS']
+
diff --git a/jni/ruby/test/rubygems/test_config.rb b/jni/ruby/test/rubygems/test_config.rb
new file mode 100644
index 0000000..7829e90
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_config.rb
@@ -0,0 +1,14 @@
+require 'rubygems/test_case'
+require 'rubygems'
+
+class TestConfig < Gem::TestCase
+
+ def test_datadir
+ util_make_gems
+ spec = Gem::Specification.find_by_name("a")
+ spec.activate
+ assert_equal "#{spec.full_gem_path}/data/a", Gem.datadir('a')
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_deprecate.rb b/jni/ruby/test/rubygems/test_deprecate.rb
new file mode 100644
index 0000000..ed4e9aa
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_deprecate.rb
@@ -0,0 +1,76 @@
+require 'rubygems/test_case'
+# require 'rubygems/builder'
+# require 'rubygems/package'
+require 'rubygems/deprecate'
+
+class TestDeprecate < Gem::TestCase
+
+ def setup
+ super
+
+ # Gem::Deprecate.saved_warnings.clear
+ @original_skip = Gem::Deprecate.skip
+ Gem::Deprecate.skip = false
+ end
+
+ def teardown
+ super
+
+ # Gem::Deprecate.saved_warnings.clear
+ Gem::Deprecate.skip = @original_skip
+ end
+
+ def test_defaults
+ assert_equal false, @original_skip
+ end
+
+ def test_assignment
+ Gem::Deprecate.skip = false
+ assert_equal false, Gem::Deprecate.skip
+
+ Gem::Deprecate.skip = true
+ assert_equal true, Gem::Deprecate.skip
+
+ Gem::Deprecate.skip = nil
+ assert([true,false].include? Gem::Deprecate.skip)
+ end
+
+ def test_skip
+ Gem::Deprecate.skip_during do
+ assert_equal true, Gem::Deprecate.skip
+ end
+
+ Gem::Deprecate.skip = nil
+ end
+
+ class Thing
+ extend Gem::Deprecate
+ attr_accessor :message
+ def foo
+ @message = "foo"
+ end
+ def bar
+ @message = "bar"
+ end
+ deprecate :foo, :bar, 2099, 3
+ end
+
+ def test_deprecated_method_calls_the_old_method
+ capture_io do
+ thing = Thing.new
+ thing.foo
+ assert_equal "foo", thing.message
+ end
+ end
+
+ def test_deprecated_method_outputs_a_warning
+ out, err = capture_io do
+ thing = Thing.new
+ thing.foo
+ end
+
+ assert_equal "", out
+ assert_match(/Thing#foo is deprecated; use bar instead\./, err)
+ assert_match(/on or after 2099-03-01/, err)
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem.rb b/jni/ruby/test/rubygems/test_gem.rb
new file mode 100644
index 0000000..47f57ab
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem.rb
@@ -0,0 +1,1576 @@
+# coding: US-ASCII
+require 'rubygems/test_case'
+require 'rubygems'
+require 'rubygems/command'
+require 'rubygems/installer'
+require 'pathname'
+require 'tmpdir'
+
+# TODO: push this up to test_case.rb once battle tested
+$SAFE=1
+$LOAD_PATH.map! do |path|
+ path.dup.untaint
+end
+
+class TestGem < Gem::TestCase
+
+ PLUGINS_LOADED = []
+
+ def setup
+ super
+
+ PLUGINS_LOADED.clear
+
+ common_installer_setup
+
+ ENV.delete 'RUBYGEMS_GEMDEPS'
+ @additional = %w[a b].map { |d| File.join @tempdir, d }
+
+ util_remove_interrupt_command
+ end
+
+ def test_self_finish_resolve
+ save_loaded_features do
+ a1 = new_spec "a", "1", "b" => "> 0"
+ b1 = new_spec "b", "1", "c" => ">= 1"
+ b2 = new_spec "b", "2", "c" => ">= 2"
+ c1 = new_spec "c", "1"
+ c2 = new_spec "c", "2"
+
+ install_specs a1, b1, b2, c1, c2
+
+ a1.activate
+
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ Gem.finish_resolve
+
+ assert_equal %w(a-1 b-2 c-2), loaded_spec_names
+ assert_equal [], unresolved_names
+ end
+ end
+
+ def test_self_finish_resolve_wtf
+ save_loaded_features do
+ a1 = new_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
+ b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb" # this
+ b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
+ c1 = new_spec "c", "1" # this
+ c2 = new_spec "c", "2"
+ d1 = new_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
+ d2 = new_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
+
+ install_specs a1, b1, b2, c1, c2, d1, d2
+
+ a1.activate
+
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal ["b (> 0)", "d (> 0)"], unresolved_names
+
+ Gem.finish_resolve
+
+ assert_equal %w(a-1 b-1 c-1 d-2), loaded_spec_names
+ assert_equal [], unresolved_names
+ end
+ end
+
+ def test_self_install
+ spec_fetcher do |f|
+ f.gem 'a', 1
+ f.spec 'a', 2
+ end
+
+ gemhome2 = "#{@gemhome}2"
+
+ installed = Gem.install 'a', '= 1', :install_dir => gemhome2
+
+ assert_equal %w[a-1], installed.map { |spec| spec.full_name }
+
+ assert_path_exists File.join(gemhome2, 'gems', 'a-1')
+ end
+
+ def test_require_missing
+ save_loaded_features do
+ assert_raises ::LoadError do
+ require "q"
+ end
+ end
+ end
+
+ def test_require_does_not_glob
+ save_loaded_features do
+ a1 = new_spec "a", "1", nil, "lib/a1.rb"
+
+ install_specs a1
+
+ assert_raises ::LoadError do
+ require "a*"
+ end
+
+ assert_equal [], loaded_spec_names
+ end
+ end
+
+ def test_self_bin_path_active
+ a1 = util_spec 'a', '1' do |s|
+ s.executables = ['exec']
+ end
+
+ util_spec 'a', '2' do |s|
+ s.executables = ['exec']
+ end
+
+ a1.activate
+
+ assert_match 'a-1/bin/exec', Gem.bin_path('a', 'exec', '>= 0')
+ end
+
+ def test_self_bin_path_no_exec_name
+ e = assert_raises ArgumentError do
+ Gem.bin_path 'a'
+ end
+
+ assert_equal 'you must supply exec_name', e.message
+ end
+
+ def test_self_bin_path_bin_name
+ util_exec_gem
+ assert_equal @abin_path, Gem.bin_path('a', 'abin')
+ end
+
+ def test_self_bin_path_bin_name_version
+ util_exec_gem
+ assert_equal @abin_path, Gem.bin_path('a', 'abin', '4')
+ end
+
+ def test_self_bin_path_nonexistent_binfile
+ util_spec 'a', '2' do |s|
+ s.executables = ['exec']
+ end
+ assert_raises(Gem::GemNotFoundException) do
+ Gem.bin_path('a', 'other', '2')
+ end
+ end
+
+ def test_self_bin_path_no_bin_file
+ util_spec 'a', '1'
+ assert_raises(ArgumentError) do
+ Gem.bin_path('a', nil, '1')
+ end
+ end
+
+ def test_self_bin_path_not_found
+ assert_raises(Gem::GemNotFoundException) do
+ Gem.bin_path('non-existent', 'blah')
+ end
+ end
+
+ def test_self_bin_path_bin_file_gone_in_latest
+ util_exec_gem
+ util_spec 'a', '10' do |s|
+ s.executables = []
+ end
+ # Should not find a-10's non-abin (bug)
+ assert_equal @abin_path, Gem.bin_path('a', 'abin')
+ end
+
+ def test_self_bindir
+ assert_equal File.join(@gemhome, 'bin'), Gem.bindir
+ assert_equal File.join(@gemhome, 'bin'), Gem.bindir(Gem.dir)
+ assert_equal File.join(@gemhome, 'bin'), Gem.bindir(Pathname.new(Gem.dir))
+ end
+
+ def test_self_bindir_default_dir
+ default = Gem.default_dir
+
+ assert_equal Gem.default_bindir, Gem.bindir(default)
+ end
+
+ def test_self_clear_paths
+ assert_match(/gemhome$/, Gem.dir)
+ assert_match(/gemhome$/, Gem.path.first)
+
+ Gem.clear_paths
+
+ assert_nil Gem::Specification.send(:class_variable_get, :@@all)
+ end
+
+ def test_self_configuration
+ expected = Gem::ConfigFile.new []
+ Gem.configuration = nil
+
+ assert_equal expected, Gem.configuration
+ end
+
+ def test_self_datadir
+ foo = nil
+
+ Dir.chdir @tempdir do
+ FileUtils.mkdir_p 'data'
+ File.open File.join('data', 'foo.txt'), 'w' do |fp|
+ fp.puts 'blah'
+ end
+
+ foo = util_spec 'foo' do |s| s.files = %w[data/foo.txt] end
+ install_gem foo
+ end
+
+ gem 'foo'
+
+ expected = File.join @gemhome, 'gems', foo.full_name, 'data', 'foo'
+
+ assert_equal expected, Gem.datadir('foo')
+ end
+
+ def test_self_datadir_nonexistent_package
+ assert_nil Gem.datadir('xyzzy')
+ end
+
+ def test_self_default_exec_format
+ ruby_install_name 'ruby' do
+ assert_equal '%s', Gem.default_exec_format
+ end
+ end
+
+ def test_self_default_exec_format_18
+ ruby_install_name 'ruby18' do
+ assert_equal '%s18', Gem.default_exec_format
+ end
+ end
+
+ def test_self_default_exec_format_jruby
+ ruby_install_name 'jruby' do
+ assert_equal 'j%s', Gem.default_exec_format
+ end
+ end
+
+ def test_default_path
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG['vendordir'] = File.join @tempdir, 'vendor'
+
+ FileUtils.rm_rf Gem.user_home
+
+ expected = [Gem.default_dir]
+
+ assert_equal expected, Gem.default_path
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+ def test_default_path_missing_vendor
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG.delete 'vendordir'
+
+ FileUtils.rm_rf Gem.user_home
+
+ expected = [Gem.default_dir]
+
+ assert_equal expected, Gem.default_path
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+ def test_default_path_user_home
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG['vendordir'] = File.join @tempdir, 'vendor'
+
+ expected = [Gem.user_dir, Gem.default_dir]
+
+ assert_equal expected, Gem.default_path
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+ def test_default_path_vendor_dir
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG['vendordir'] = File.join @tempdir, 'vendor'
+
+ FileUtils.mkdir_p Gem.vendor_dir
+
+ FileUtils.rm_rf Gem.user_home
+
+ expected = [Gem.default_dir, Gem.vendor_dir]
+
+ assert_equal expected, Gem.default_path
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+ def test_self_default_sources
+ assert_equal %w[https://rubygems.org/], Gem.default_sources
+ end
+
+ def test_self_detect_gemdeps
+ skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
+
+ FileUtils.mkdir_p 'detect/a/b'
+ FileUtils.mkdir_p 'detect/a/Isolate'
+
+ FileUtils.touch 'detect/Isolate'
+
+ begin
+ Dir.chdir 'detect/a/b'
+
+ assert_empty Gem.detect_gemdeps
+ ensure
+ Dir.chdir @tempdir
+ end
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ def test_self_dir
+ assert_equal @gemhome, Gem.dir
+ end
+
+ def test_self_ensure_gem_directories
+ FileUtils.rm_r @gemhome
+ Gem.use_paths @gemhome
+
+ Gem.ensure_gem_subdirectories @gemhome
+
+ assert_path_exists File.join @gemhome, 'build_info'
+ assert_path_exists File.join @gemhome, 'cache'
+ assert_path_exists File.join @gemhome, 'doc'
+ assert_path_exists File.join @gemhome, 'extensions'
+ assert_path_exists File.join @gemhome, 'gems'
+ assert_path_exists File.join @gemhome, 'specifications'
+ end
+
+ def test_self_ensure_gem_directories_permissions
+ FileUtils.rm_r @gemhome
+ Gem.use_paths @gemhome
+
+ Gem.ensure_gem_subdirectories @gemhome, 0750
+
+ assert File.directory? File.join(@gemhome, "cache")
+
+ assert_equal 0750, File::Stat.new(@gemhome).mode & 0777
+ assert_equal 0750, File::Stat.new(File.join(@gemhome, "cache")).mode & 0777
+ end unless win_platform?
+
+ def test_self_ensure_gem_directories_safe_permissions
+ FileUtils.rm_r @gemhome
+ Gem.use_paths @gemhome
+
+ old_umask = File.umask
+ File.umask 0
+ Gem.ensure_gem_subdirectories @gemhome
+
+ assert_equal 0, File::Stat.new(@gemhome).mode & 002
+ assert_equal 0, File::Stat.new(File.join(@gemhome, "cache")).mode & 002
+ ensure
+ File.umask old_umask
+ end unless win_platform?
+
+ def test_self_ensure_gem_directories_missing_parents
+ gemdir = File.join @tempdir, 'a/b/c/gemdir'
+ FileUtils.rm_rf File.join(@tempdir, 'a') rescue nil
+ refute File.exist?(File.join(@tempdir, 'a')),
+ "manually remove #{File.join @tempdir, 'a'}, tests are broken"
+ Gem.use_paths gemdir
+
+ Gem.ensure_gem_subdirectories gemdir
+
+ assert File.directory?(util_cache_dir)
+ end
+
+ unless win_platform? then # only for FS that support write protection
+ def test_self_ensure_gem_directories_write_protected
+ gemdir = File.join @tempdir, "egd"
+ FileUtils.rm_r gemdir rescue nil
+ refute File.exist?(gemdir), "manually remove #{gemdir}, tests are broken"
+ FileUtils.mkdir_p gemdir
+ FileUtils.chmod 0400, gemdir
+ Gem.use_paths gemdir
+
+ Gem.ensure_gem_subdirectories gemdir
+
+ refute File.exist?(util_cache_dir)
+ ensure
+ FileUtils.chmod 0600, gemdir
+ end
+
+ def test_self_ensure_gem_directories_write_protected_parents
+ parent = File.join(@tempdir, "egd")
+ gemdir = "#{parent}/a/b/c"
+
+ FileUtils.rm_r parent rescue nil
+ refute File.exist?(parent), "manually remove #{parent}, tests are broken"
+ FileUtils.mkdir_p parent
+ FileUtils.chmod 0400, parent
+ Gem.use_paths(gemdir)
+
+ Gem.ensure_gem_subdirectories gemdir
+
+ refute File.exist? File.join(gemdir, "gems")
+ ensure
+ FileUtils.chmod 0600, parent
+ end
+ end
+
+ def test_self_extension_dir_shared
+ enable_shared 'yes' do
+ assert_equal Gem.ruby_api_version, Gem.extension_api_version
+ end
+ end
+
+ def test_self_extension_dir_static
+ enable_shared 'no' do
+ assert_equal "#{Gem.ruby_api_version}-static", Gem.extension_api_version
+ end
+ end
+
+ def test_self_find_files
+ cwd = File.expand_path("test/rubygems", @@project_dir)
+ $LOAD_PATH.unshift cwd
+
+ discover_path = File.join 'lib', 'sff', 'discover.rb'
+
+ foo1, foo2 = %w(1 2).map { |version|
+ spec = quick_gem 'sff', version do |s|
+ s.files << discover_path
+ end
+
+ write_file(File.join 'gems', spec.full_name, discover_path) do |fp|
+ fp.puts "# #{spec.full_name}"
+ end
+
+ spec
+ }
+
+ Gem.refresh
+
+ expected = [
+ File.expand_path('test/rubygems/sff/discover.rb', @@project_dir),
+ File.join(foo2.full_gem_path, discover_path),
+ File.join(foo1.full_gem_path, discover_path),
+ ]
+
+ assert_equal expected, Gem.find_files('sff/discover')
+ assert_equal expected, Gem.find_files('sff/**.rb'), '[ruby-core:31730]'
+ ensure
+ assert_equal cwd, $LOAD_PATH.shift
+ end
+
+ def test_self_find_latest_files
+ cwd = File.expand_path("test/rubygems", @@project_dir)
+ $LOAD_PATH.unshift cwd
+
+ discover_path = File.join 'lib', 'sff', 'discover.rb'
+
+ _, foo2 = %w(1 2).map { |version|
+ spec = quick_gem 'sff', version do |s|
+ s.files << discover_path
+ end
+
+ write_file(File.join 'gems', spec.full_name, discover_path) do |fp|
+ fp.puts "# #{spec.full_name}"
+ end
+
+ spec
+ }
+
+ Gem.refresh
+
+ expected = [
+ File.expand_path('test/rubygems/sff/discover.rb', @@project_dir),
+ File.join(foo2.full_gem_path, discover_path),
+ ]
+
+ assert_equal expected, Gem.find_latest_files('sff/discover')
+ assert_equal expected, Gem.find_latest_files('sff/**.rb'), '[ruby-core:31730]'
+ ensure
+ assert_equal cwd, $LOAD_PATH.shift
+ end
+
+ def test_self_latest_spec_for
+ gems = spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '3.a'
+ fetcher.spec 'a', 2
+ end
+
+ spec = Gem.latest_spec_for 'a'
+
+ assert_equal gems['a-2'], spec
+ end
+
+ def test_self_latest_rubygems_version
+ spec_fetcher do |fetcher|
+ fetcher.spec 'rubygems-update', '1.8.23'
+ fetcher.spec 'rubygems-update', '1.8.24'
+ fetcher.spec 'rubygems-update', '2.0.0.preview3'
+ end
+
+ version = Gem.latest_rubygems_version
+
+ assert_equal Gem::Version.new('1.8.24'), version
+ end
+
+ def test_self_latest_version_for
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', '3.a'
+ end
+
+ version = Gem.latest_version_for 'a'
+
+ assert_equal Gem::Version.new(2), version
+ end
+
+ def test_self_loaded_specs
+ foo = util_spec 'foo'
+ install_gem foo
+
+ foo.activate
+
+ assert_equal true, Gem.loaded_specs.keys.include?('foo')
+ end
+
+ def util_path
+ ENV.delete "GEM_HOME"
+ ENV.delete "GEM_PATH"
+ end
+
+ def test_self_path
+ assert_equal [Gem.dir], Gem.path
+ end
+
+ def test_self_path_default
+ util_path
+
+ if defined?(APPLE_GEM_HOME)
+ orig_APPLE_GEM_HOME = APPLE_GEM_HOME
+ Object.send :remove_const, :APPLE_GEM_HOME
+ end
+
+ Gem.instance_variable_set :@paths, nil
+
+ assert_equal [Gem.default_path, Gem.dir].flatten.uniq, Gem.path
+ ensure
+ Object.const_set :APPLE_GEM_HOME, orig_APPLE_GEM_HOME if orig_APPLE_GEM_HOME
+ end
+
+ unless win_platform?
+ def test_self_path_APPLE_GEM_HOME
+ util_path
+
+ Gem.clear_paths
+ apple_gem_home = File.join @tempdir, 'apple_gem_home'
+
+ old, $-w = $-w, nil
+ Object.const_set :APPLE_GEM_HOME, apple_gem_home
+ $-w = old
+
+ assert_includes Gem.path, apple_gem_home
+ ensure
+ Object.send :remove_const, :APPLE_GEM_HOME
+ end
+
+ def test_self_path_APPLE_GEM_HOME_GEM_PATH
+ Gem.clear_paths
+ ENV['GEM_PATH'] = @gemhome
+ apple_gem_home = File.join @tempdir, 'apple_gem_home'
+ Gem.const_set :APPLE_GEM_HOME, apple_gem_home
+
+ refute Gem.path.include?(apple_gem_home)
+ ensure
+ Gem.send :remove_const, :APPLE_GEM_HOME
+ end
+ end
+
+ def test_self_path_ENV_PATH
+ path_count = Gem.path.size
+ Gem.clear_paths
+
+ ENV['GEM_PATH'] = @additional.join(File::PATH_SEPARATOR)
+
+ assert_equal @additional, Gem.path[0,2]
+
+ assert_equal path_count + @additional.size, Gem.path.size,
+ "extra path components: #{Gem.path[2..-1].inspect}"
+ assert_equal Gem.dir, Gem.path.last
+ end
+
+ def test_self_path_duplicate
+ Gem.clear_paths
+ util_ensure_gem_dirs
+ dirs = @additional + [@gemhome] + [File.join(@tempdir, 'a')]
+
+ ENV['GEM_HOME'] = @gemhome
+ ENV['GEM_PATH'] = dirs.join File::PATH_SEPARATOR
+
+ assert_equal @gemhome, Gem.dir
+
+ paths = [Gem.dir]
+ assert_equal @additional + paths, Gem.path
+ end
+
+ def test_self_path_overlap
+ Gem.clear_paths
+
+ util_ensure_gem_dirs
+ ENV['GEM_HOME'] = @gemhome
+ ENV['GEM_PATH'] = @additional.join(File::PATH_SEPARATOR)
+
+ assert_equal @gemhome, Gem.dir
+
+ paths = [Gem.dir]
+ assert_equal @additional + paths, Gem.path
+ end
+
+ def test_self_platforms
+ assert_equal [Gem::Platform::RUBY, Gem::Platform.local], Gem.platforms
+ end
+
+ def test_self_prefix
+ assert_equal @@project_dir, Gem.prefix
+ end
+
+ def test_self_prefix_libdir
+ orig_libdir = RbConfig::CONFIG['libdir']
+ RbConfig::CONFIG['libdir'] = @@project_dir
+
+ assert_nil Gem.prefix
+ ensure
+ RbConfig::CONFIG['libdir'] = orig_libdir
+ end
+
+ def test_self_prefix_sitelibdir
+ orig_sitelibdir = RbConfig::CONFIG['sitelibdir']
+ RbConfig::CONFIG['sitelibdir'] = @@project_dir
+
+ assert_nil Gem.prefix
+ ensure
+ RbConfig::CONFIG['sitelibdir'] = orig_sitelibdir
+ end
+
+ def test_self_read_binary
+ open 'test', 'w' do |io|
+ io.write "\xCF\x80"
+ end
+
+ assert_equal ["\xCF", "\x80"], Gem.read_binary('test').chars.to_a
+
+ skip 'chmod not supported' if Gem.win_platform?
+
+ begin
+ File.chmod 0444, 'test'
+
+ assert_equal ["\xCF", "\x80"], Gem.read_binary('test').chars.to_a
+ ensure
+ File.chmod 0644, 'test'
+ end
+ end
+
+ def test_self_refresh
+ skip 'Insecure operation - mkdir' if RUBY_VERSION <= "1.8.7"
+ util_make_gems
+
+ a1_spec = @a1.spec_file
+ moved_path = File.join @tempdir, File.basename(a1_spec)
+
+ FileUtils.mv a1_spec, moved_path
+
+ Gem.refresh
+
+ refute_includes Gem::Specification.all_names, @a1.full_name
+
+ FileUtils.mv moved_path, a1_spec
+
+ Gem.refresh
+
+ assert_includes Gem::Specification.all_names, @a1.full_name
+ end
+
+ def test_self_refresh_keeps_loaded_specs_activated
+ skip 'Insecure operation - mkdir' if RUBY_VERSION <= "1.8.7"
+ util_make_gems
+
+ a1_spec = @a1.spec_file
+ moved_path = File.join @tempdir, File.basename(a1_spec)
+
+ FileUtils.mv a1_spec, moved_path
+
+ Gem.refresh
+
+ s = Gem::Specification.first
+ s.activate
+
+ Gem.refresh
+
+ Gem::Specification.each{|spec| assert spec.activated? if spec == s}
+
+ Gem.loaded_specs.delete(s)
+ Gem.refresh
+ end
+
+ def test_self_ruby_escaping_spaces_in_path
+ orig_ruby = Gem.ruby
+ orig_bindir = RbConfig::CONFIG['bindir']
+ orig_ruby_install_name = RbConfig::CONFIG['ruby_install_name']
+ orig_exe_ext = RbConfig::CONFIG['EXEEXT']
+
+ RbConfig::CONFIG['bindir'] = "C:/Ruby 1.8/bin"
+ RbConfig::CONFIG['ruby_install_name'] = "ruby"
+ RbConfig::CONFIG['EXEEXT'] = ".exe"
+ Gem.instance_variable_set("@ruby", nil)
+
+ assert_equal "\"C:/Ruby 1.8/bin/ruby.exe\"", Gem.ruby
+ ensure
+ Gem.instance_variable_set("@ruby", orig_ruby)
+ RbConfig::CONFIG['bindir'] = orig_bindir
+ RbConfig::CONFIG['ruby_install_name'] = orig_ruby_install_name
+ RbConfig::CONFIG['EXEEXT'] = orig_exe_ext
+ end
+
+ def test_self_ruby_path_without_spaces
+ orig_ruby = Gem.ruby
+ orig_bindir = RbConfig::CONFIG['bindir']
+ orig_ruby_install_name = RbConfig::CONFIG['ruby_install_name']
+ orig_exe_ext = RbConfig::CONFIG['EXEEXT']
+
+ RbConfig::CONFIG['bindir'] = "C:/Ruby18/bin"
+ RbConfig::CONFIG['ruby_install_name'] = "ruby"
+ RbConfig::CONFIG['EXEEXT'] = ".exe"
+ Gem.instance_variable_set("@ruby", nil)
+
+ assert_equal "C:/Ruby18/bin/ruby.exe", Gem.ruby
+ ensure
+ Gem.instance_variable_set("@ruby", orig_ruby)
+ RbConfig::CONFIG['bindir'] = orig_bindir
+ RbConfig::CONFIG['ruby_install_name'] = orig_ruby_install_name
+ RbConfig::CONFIG['EXEEXT'] = orig_exe_ext
+ end
+
+ def test_self_ruby_api_version
+ orig_ruby_version, RbConfig::CONFIG['ruby_version'] = RbConfig::CONFIG['ruby_version'], '1.2.3'
+
+ Gem.instance_variable_set :@ruby_api_version, nil
+
+ assert_equal '1.2.3', Gem.ruby_api_version
+ ensure
+ Gem.instance_variable_set :@ruby_api_version, nil
+
+ RbConfig::CONFIG['ruby_version'] = orig_ruby_version
+ end
+
+ def test_self_ruby_version_1_8_5
+ util_set_RUBY_VERSION '1.8.5'
+
+ assert_equal Gem::Version.new('1.8.5'), Gem.ruby_version
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
+ def test_self_ruby_version_1_8_6p287
+ util_set_RUBY_VERSION '1.8.6', 287
+
+ assert_equal Gem::Version.new('1.8.6.287'), Gem.ruby_version
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
+ def test_self_ruby_version_1_9_2dev_r23493
+ util_set_RUBY_VERSION '1.9.2', -1, 23493
+
+ assert_equal Gem::Version.new('1.9.2.dev.23493'), Gem.ruby_version
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
+ def test_self_rubygems_version
+ assert_equal Gem::Version.new(Gem::VERSION), Gem.rubygems_version
+ end
+
+ def test_self_paths_eq
+ other = File.join @tempdir, 'other'
+ path = [@userhome, other].join File::PATH_SEPARATOR
+
+ #
+ # FIXME remove after fixing test_case
+ #
+ ENV["GEM_HOME"] = @gemhome
+ Gem.paths = { "GEM_PATH" => path }
+
+ assert_equal [@userhome, other, @gemhome], Gem.path
+ end
+
+ def test_self_paths_eq_nonexistent_home
+ ENV['GEM_HOME'] = @gemhome
+ Gem.clear_paths
+
+ other = File.join @tempdir, 'other'
+
+ ENV['HOME'] = other
+
+ Gem.paths = { "GEM_PATH" => other }
+
+ assert_equal [other, @gemhome], Gem.path
+ end
+
+ def test_self_post_build
+ assert_equal 1, Gem.post_build_hooks.length
+
+ Gem.post_build do |installer| end
+
+ assert_equal 2, Gem.post_build_hooks.length
+ end
+
+ def test_self_post_install
+ assert_equal 1, Gem.post_install_hooks.length
+
+ Gem.post_install do |installer| end
+
+ assert_equal 2, Gem.post_install_hooks.length
+ end
+
+ def test_self_done_installing
+ assert_empty Gem.done_installing_hooks
+
+ Gem.done_installing do |gems| end
+
+ assert_equal 1, Gem.done_installing_hooks.length
+ end
+
+ def test_self_post_reset
+ assert_empty Gem.post_reset_hooks
+
+ Gem.post_reset { }
+
+ assert_equal 1, Gem.post_reset_hooks.length
+ end
+
+ def test_self_post_uninstall
+ assert_equal 1, Gem.post_uninstall_hooks.length
+
+ Gem.post_uninstall do |installer| end
+
+ assert_equal 2, Gem.post_uninstall_hooks.length
+ end
+
+ def test_self_pre_install
+ assert_equal 1, Gem.pre_install_hooks.length
+
+ Gem.pre_install do |installer| end
+
+ assert_equal 2, Gem.pre_install_hooks.length
+ end
+
+ def test_self_pre_reset
+ assert_empty Gem.pre_reset_hooks
+
+ Gem.pre_reset { }
+
+ assert_equal 1, Gem.pre_reset_hooks.length
+ end
+
+ def test_self_pre_uninstall
+ assert_equal 1, Gem.pre_uninstall_hooks.length
+
+ Gem.pre_uninstall do |installer| end
+
+ assert_equal 2, Gem.pre_uninstall_hooks.length
+ end
+
+ def test_self_sources
+ assert_equal %w[http://gems.example.com/], Gem.sources
+ end
+
+ def test_self_try_activate_missing_dep
+ a = util_spec 'a', '1.0', 'b' => '>= 1.0'
+
+ a_file = File.join a.gem_dir, 'lib', 'a_file.rb'
+
+ write_file a_file do |io|
+ io.puts '# a_file.rb'
+ end
+
+ e = assert_raises Gem::LoadError do
+ Gem.try_activate 'a_file'
+ end
+
+ assert_match %r%Could not find 'b' %, e.message
+ end
+
+ def test_self_try_activate_missing_extensions
+ util_spec 'ext', '1' do |s|
+ s.extensions = %w[ext/extconf.rb]
+ s.mark_version
+ s.installed_by_version = v('2.2')
+ end
+
+ _, err = capture_io do
+ refute Gem.try_activate 'nonexistent'
+ end
+
+ expected = "Ignoring ext-1 because its extensions are not built. " +
+ "Try: gem pristine ext --version 1\n"
+
+ assert_equal expected, err
+ end
+
+ def test_self_use_paths
+ util_ensure_gem_dirs
+
+ Gem.use_paths @gemhome, @additional
+
+ assert_equal @gemhome, Gem.dir
+ assert_equal @additional + [Gem.dir], Gem.path
+ end
+
+ def test_self_user_dir
+ parts = [@userhome, '.gem', Gem.ruby_engine]
+ parts << RbConfig::CONFIG['ruby_version'] unless RbConfig::CONFIG['ruby_version'].empty?
+
+ assert_equal File.join(parts), Gem.user_dir
+ end
+
+ def test_self_user_home
+ if ENV['HOME'] then
+ assert_equal ENV['HOME'], Gem.user_home
+ else
+ assert true, 'count this test'
+ end
+ end
+
+ def test_self_needs
+ util_clear_gems
+ a = util_spec "a", "1"
+ b = util_spec "b", "1", "c" => nil
+ c = util_spec "c", "2"
+
+ install_specs a, b, c
+
+ Gem.needs do |r|
+ r.gem "a"
+ r.gem "b", "= 1"
+ end
+
+ activated = Gem::Specification.map { |x| x.full_name }
+
+ assert_equal %w!a-1 b-1 c-2!, activated.sort
+ end
+
+ def test_self_needs_picks_up_unresolved_deps
+ skip 'loading from unsafe file' if RUBY_VERSION <= "1.8.7"
+ save_loaded_features do
+ util_clear_gems
+ a = util_spec "a", "1"
+ b = util_spec "b", "1", "c" => nil
+ c = util_spec "c", "2"
+ d = new_spec "d", "1", {'e' => '= 1'}, "lib/d.rb"
+ e = util_spec "e", "1"
+
+ install_specs a, b, c, d, e
+
+ Gem.needs do |r|
+ r.gem "a"
+ r.gem "b", "= 1"
+
+ require 'd'
+ end
+
+ assert_equal %w!a-1 b-1 c-2 d-1 e-1!, loaded_spec_names
+ end
+ end
+
+ def test_self_gunzip
+ input = "\x1F\x8B\b\0\xED\xA3\x1AQ\0\x03\xCBH" +
+ "\xCD\xC9\xC9\a\0\x86\xA6\x106\x05\0\0\0"
+
+ output = Gem.gunzip input
+
+ assert_equal 'hello', output
+
+ return unless Object.const_defined? :Encoding
+
+ assert_equal Encoding::BINARY, output.encoding
+ end
+
+ def test_self_gzip
+ input = 'hello'
+
+ output = Gem.gzip input
+
+ zipped = StringIO.new output
+
+ assert_equal 'hello', Zlib::GzipReader.new(zipped).read
+
+ return unless Object.const_defined? :Encoding
+
+ assert_equal Encoding::BINARY, output.encoding
+ end
+
+ if Gem.win_platform? && '1.9' > RUBY_VERSION
+ # Ruby 1.9 properly handles ~ path expansion, so no need to run such tests.
+ def test_self_user_home_userprofile
+
+ Gem.clear_paths
+
+ # safe-keep env variables
+ orig_home, orig_user_profile = ENV['HOME'], ENV['USERPROFILE']
+
+ # prepare for the test
+ ENV.delete('HOME')
+ ENV['USERPROFILE'] = "W:\\Users\\RubyUser"
+
+ assert_equal 'W:/Users/RubyUser', Gem.user_home
+
+ ensure
+ ENV['HOME'] = orig_home
+ ENV['USERPROFILE'] = orig_user_profile
+ end
+
+ def test_self_user_home_user_drive_and_path
+ Gem.clear_paths
+
+ # safe-keep env variables
+ orig_home, orig_user_profile = ENV['HOME'], ENV['USERPROFILE']
+ orig_home_drive, orig_home_path = ENV['HOMEDRIVE'], ENV['HOMEPATH']
+
+ # prepare the environment
+ ENV.delete('HOME')
+ ENV.delete('USERPROFILE')
+ ENV['HOMEDRIVE'] = 'Z:'
+ ENV['HOMEPATH'] = "\\Users\\RubyUser"
+
+ assert_equal 'Z:/Users/RubyUser', Gem.user_home
+
+ ensure
+ ENV['HOME'] = orig_home
+ ENV['USERPROFILE'] = orig_user_profile
+ ENV['HOMEDRIVE'] = orig_home_drive
+ ENV['HOMEPATH'] = orig_home_path
+ end
+ end
+
+ def test_self_vendor_dir
+ expected =
+ File.join RbConfig::CONFIG['vendordir'], 'gems',
+ RbConfig::CONFIG['ruby_version']
+
+ assert_equal expected, Gem.vendor_dir
+ end
+
+ def test_self_vendor_dir_ENV_GEM_VENDOR
+ ENV['GEM_VENDOR'] = File.join @tempdir, 'vendor', 'gems'
+
+ assert_equal ENV['GEM_VENDOR'], Gem.vendor_dir
+ refute Gem.vendor_dir.frozen?
+ end
+
+ def test_self_vendor_dir_missing
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG.delete 'vendordir'
+
+ assert_nil Gem.vendor_dir
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+ def test_load_plugins
+ skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
+ plugin_path = File.join "lib", "rubygems_plugin.rb"
+
+ Dir.chdir @tempdir do
+ FileUtils.mkdir_p 'lib'
+ File.open plugin_path, "w" do |fp|
+ fp.puts "class TestGem; PLUGINS_LOADED << 'plugin'; end"
+ end
+
+ foo1 = util_spec 'foo', '1' do |s|
+ s.files << plugin_path
+ end
+
+ install_gem foo1
+
+ foo2 = util_spec 'foo', '2' do |s|
+ s.files << plugin_path
+ end
+
+ install_gem foo2
+ end
+
+ Gem.searcher = nil
+ Gem::Specification.reset
+
+ gem 'foo'
+
+ Gem.load_plugins
+
+ assert_equal %w[plugin], PLUGINS_LOADED
+ end
+
+ def test_load_env_plugins
+ with_plugin('load') { Gem.load_env_plugins }
+ assert_equal :loaded, TEST_PLUGIN_LOAD rescue nil
+
+ util_remove_interrupt_command
+
+ # Should attempt to cause a StandardError
+ with_plugin('standarderror') { Gem.load_env_plugins }
+ assert_equal :loaded, TEST_PLUGIN_STANDARDERROR rescue nil
+
+ util_remove_interrupt_command
+
+ # Should attempt to cause an Exception
+ with_plugin('exception') { Gem.load_env_plugins }
+ assert_equal :loaded, TEST_PLUGIN_EXCEPTION rescue nil
+ end
+
+ def test_gem_path_ordering
+ refute_equal Gem.dir, Gem.user_dir
+
+ write_file File.join(@tempdir, 'lib', "g.rb") { |fp| fp.puts "" }
+ write_file File.join(@tempdir, 'lib', 'm.rb') { |fp| fp.puts "" }
+
+ g = new_spec 'g', '1', nil, "lib/g.rb"
+ m = new_spec 'm', '1', nil, "lib/m.rb"
+
+ install_gem g, :install_dir => Gem.dir
+ m0 = install_gem m, :install_dir => Gem.dir
+ m1 = install_gem m, :install_dir => Gem.user_dir
+
+ assert_equal m0.gem_dir, File.join(Gem.dir, "gems", "m-1")
+ assert_equal m1.gem_dir, File.join(Gem.user_dir, "gems", "m-1")
+
+ tests = [
+ [:dir0, [ Gem.dir, Gem.user_dir], m0],
+ [:dir1, [ Gem.user_dir, Gem.dir], m1]
+ ]
+
+ tests.each do |_name, _paths, expected|
+ Gem.paths = { 'GEM_HOME' => _paths.first, 'GEM_PATH' => _paths }
+ Gem::Specification.reset
+ Gem.searcher = nil
+
+ assert_equal Gem::Dependency.new('m','1').to_specs,
+ Gem::Dependency.new('m','1').to_specs.sort
+
+ assert_equal \
+ [expected.gem_dir],
+ Gem::Dependency.new('m','1').to_specs.map(&:gem_dir).sort,
+ "Wrong specs for #{_name}"
+
+ spec = Gem::Dependency.new('m','1').to_spec
+
+ assert_equal \
+ File.join(_paths.first, "gems", "m-1"),
+ spec.gem_dir,
+ "Wrong spec before require for #{_name}"
+ refute spec.activated?, "dependency already activated for #{_name}"
+
+ gem "m"
+
+ spec = Gem::Dependency.new('m','1').to_spec
+ assert spec.activated?, "dependency not activated for #{_name}"
+
+ assert_equal \
+ File.join(_paths.first, "gems", "m-1"),
+ spec.gem_dir,
+ "Wrong spec after require for #{_name}"
+
+ spec.instance_variable_set :@activated, false
+ Gem.loaded_specs.delete(spec.name)
+ $:.delete(File.join(spec.gem_dir, "lib"))
+ end
+ end
+
+ def test_gem_path_ordering_short
+ write_file File.join(@tempdir, 'lib', "g.rb") { |fp| fp.puts "" }
+ write_file File.join(@tempdir, 'lib', 'm.rb') { |fp| fp.puts "" }
+
+ g = new_spec 'g', '1', nil, "lib/g.rb"
+ m = new_spec 'm', '1', nil, "lib/m.rb"
+
+ install_gem g, :install_dir => Gem.dir
+ install_gem m, :install_dir => Gem.dir
+ install_gem m, :install_dir => Gem.user_dir
+
+ Gem.paths = {
+ 'GEM_HOME' => Gem.dir,
+ 'GEM_PATH' => [ Gem.dir, Gem.user_dir]
+ }
+
+ assert_equal \
+ File.join(Gem.dir, "gems", "m-1"),
+ Gem::Dependency.new('m','1').to_spec.gem_dir,
+ "Wrong spec selected"
+ end
+
+ def test_auto_activation_of_specific_gemdeps_file
+ util_clear_gems
+
+ a = new_spec "a", "1", nil, "lib/a.rb"
+ b = new_spec "b", "1", nil, "lib/b.rb"
+ c = new_spec "c", "1", nil, "lib/c.rb"
+
+ install_specs a, b, c
+
+ path = File.join @tempdir, "gem.deps.rb"
+
+ File.open path, "w" do |f|
+ f.puts "gem 'a'"
+ f.puts "gem 'b'"
+ f.puts "gem 'c'"
+ end
+
+ ENV['RUBYGEMS_GEMDEPS'] = path
+
+ Gem.detect_gemdeps
+
+ assert_equal %w!a-1 b-1 c-1!, loaded_spec_names
+ end
+
+ def test_auto_activation_of_detected_gemdeps_file
+ skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
+ util_clear_gems
+
+ a = new_spec "a", "1", nil, "lib/a.rb"
+ b = new_spec "b", "1", nil, "lib/b.rb"
+ c = new_spec "c", "1", nil, "lib/c.rb"
+
+ install_specs a, b, c
+
+ path = File.join @tempdir, "gem.deps.rb"
+
+ File.open path, "w" do |f|
+ f.puts "gem 'a'"
+ f.puts "gem 'b'"
+ f.puts "gem 'c'"
+ end
+
+ ENV['RUBYGEMS_GEMDEPS'] = "-"
+
+ assert_equal [a,b,c], Gem.detect_gemdeps.sort_by { |s| s.name }
+ end
+
+ LIB_PATH = File.expand_path "../../../lib".untaint, __FILE__.untaint
+
+ def test_looks_for_gemdeps_files_automatically_on_start
+ util_clear_gems
+
+ a = new_spec "a", "1", nil, "lib/a.rb"
+ b = new_spec "b", "1", nil, "lib/b.rb"
+ c = new_spec "c", "1", nil, "lib/c.rb"
+
+ install_specs a, b, c
+
+ path = File.join @tempdir, "gem.deps.rb"
+
+ File.open path, "w" do |f|
+ f.puts "gem 'a'"
+ f.puts "gem 'b'"
+ f.puts "gem 'c'"
+ end
+
+ path = File.join(@tempdir, "gd-tmp")
+ install_gem a, :install_dir => path
+ install_gem b, :install_dir => path
+ install_gem c, :install_dir => path
+
+ ENV['GEM_PATH'] = path
+ ENV['RUBYGEMS_GEMDEPS'] = "-"
+
+ out = `#{Gem.ruby.dup.untaint} -I #{LIB_PATH.untaint} -rubygems -e "p Gem.loaded_specs.values.map(&:full_name).sort"`
+
+ assert_equal '["a-1", "b-1", "c-1"]', out.strip
+ end
+
+ def test_looks_for_gemdeps_files_automatically_on_start_in_parent_dir
+ util_clear_gems
+
+ a = new_spec "a", "1", nil, "lib/a.rb"
+ b = new_spec "b", "1", nil, "lib/b.rb"
+ c = new_spec "c", "1", nil, "lib/c.rb"
+
+ install_specs a, b, c
+
+ path = File.join @tempdir, "gem.deps.rb"
+
+ File.open path, "w" do |f|
+ f.puts "gem 'a'"
+ f.puts "gem 'b'"
+ f.puts "gem 'c'"
+ end
+
+ path = File.join(@tempdir, "gd-tmp")
+ install_gem a, :install_dir => path
+ install_gem b, :install_dir => path
+ install_gem c, :install_dir => path
+
+ ENV['GEM_PATH'] = path
+ ENV['RUBYGEMS_GEMDEPS'] = "-"
+
+ Dir.mkdir "sub1"
+ out = Dir.chdir "sub1" do
+ `#{Gem.ruby.dup.untaint} -I #{LIB_PATH.untaint} -rubygems -e "p Gem.loaded_specs.values.map(&:full_name).sort"`
+ end
+
+ Dir.rmdir "sub1"
+
+ assert_equal '["a-1", "b-1", "c-1"]', out.strip
+ end
+
+ def test_register_default_spec
+ Gem.clear_default_specs
+
+ old_style = Gem::Specification.new do |spec|
+ spec.files = ["foo.rb", "bar.rb"]
+ end
+
+ Gem.register_default_spec old_style
+
+ assert_equal old_style, Gem.find_unresolved_default_spec("foo.rb")
+ assert_equal old_style, Gem.find_unresolved_default_spec("bar.rb")
+ assert_equal nil, Gem.find_unresolved_default_spec("baz.rb")
+
+ Gem.clear_default_specs
+
+ new_style = Gem::Specification.new do |spec|
+ spec.files = ["lib/foo.rb", "ext/bar.rb", "bin/exec", "README"]
+ spec.require_paths = ["lib", "ext"]
+ end
+
+ Gem.register_default_spec new_style
+
+ assert_equal new_style, Gem.find_unresolved_default_spec("foo.rb")
+ assert_equal new_style, Gem.find_unresolved_default_spec("bar.rb")
+ assert_equal nil, Gem.find_unresolved_default_spec("exec")
+ assert_equal nil, Gem.find_unresolved_default_spec("README")
+ end
+
+ def test_default_gems_use_full_paths
+ begin
+ if defined?(RUBY_ENGINE) then
+ engine = RUBY_ENGINE
+ Object.send :remove_const, :RUBY_ENGINE
+ end
+ Object.const_set :RUBY_ENGINE, 'ruby'
+
+ refute Gem.default_gems_use_full_paths?
+ ensure
+ Object.send :remove_const, :RUBY_ENGINE
+ Object.const_set :RUBY_ENGINE, engine if engine
+ end
+
+ begin
+ if defined?(RUBY_ENGINE) then
+ engine = RUBY_ENGINE
+ Object.send :remove_const, :RUBY_ENGINE
+ end
+ Object.const_set :RUBY_ENGINE, 'jruby'
+ assert Gem.default_gems_use_full_paths?
+ ensure
+ Object.send :remove_const, :RUBY_ENGINE
+ Object.const_set :RUBY_ENGINE, engine if engine
+ end
+ end
+
+ def test_use_gemdeps
+ gem_deps_file = 'gem.deps.rb'.untaint
+ spec = util_spec 'a', 1
+
+ refute spec.activated?
+
+ open gem_deps_file, 'w' do |io|
+ io.write 'gem "a"'
+ end
+
+ Gem.use_gemdeps gem_deps_file
+
+ assert spec.activated?
+ end
+
+ def test_use_gemdeps_ENV
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
+
+ spec = util_spec 'a', 1
+
+ refute spec.activated?
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.write 'gem "a"'
+ end
+
+ Gem.use_gemdeps
+
+ refute spec.activated?
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ def test_use_gemdeps_argument_missing
+ e = assert_raises ArgumentError do
+ Gem.use_gemdeps 'gem.deps.rb'
+ end
+
+ assert_equal 'Unable to find gem dependencies file at gem.deps.rb',
+ e.message
+ end
+
+ def test_use_gemdeps_argument_missing_match_ENV
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] =
+ ENV['RUBYGEMS_GEMDEPS'], 'gem.deps.rb'
+
+ e = assert_raises ArgumentError do
+ Gem.use_gemdeps 'gem.deps.rb'
+ end
+
+ assert_equal 'Unable to find gem dependencies file at gem.deps.rb',
+ e.message
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ def test_use_gemdeps_automatic
+ skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
+
+ spec = util_spec 'a', 1
+
+ refute spec.activated?
+
+ open 'Gemfile', 'w' do |io|
+ io.write 'gem "a"'
+ end
+
+ Gem.use_gemdeps
+
+ assert spec.activated?
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ def test_use_gemdeps_automatic_missing
+ skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
+
+ Gem.use_gemdeps
+
+ assert true # count
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ def test_use_gemdeps_disabled
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], ''
+
+ spec = util_spec 'a', 1
+
+ refute spec.activated?
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.write 'gem "a"'
+ end
+
+ Gem.use_gemdeps
+
+ refute spec.activated?
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ def test_use_gemdeps_missing_gem
+ skip 'Insecure operation - read' if RUBY_VERSION <= "1.8.7"
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'
+
+ open 'x', 'w' do |io|
+ io.write 'gem "a"'
+ end
+
+ expected = <<-EXPECTED
+Unable to resolve dependency: user requested 'a (>= 0)'
+You may need to `gem install -g` to install missing gems
+
+ EXPECTED
+
+ assert_output nil, expected do
+ Gem.use_gemdeps
+ end
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ def test_use_gemdeps_specific
+ skip 'Insecure operation - read' if RUBY_VERSION <= "1.8.7"
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'
+
+ spec = util_spec 'a', 1
+
+ refute spec.activated?
+
+ open 'x', 'w' do |io|
+ io.write 'gem "a"'
+ end
+
+ Gem.use_gemdeps
+
+ assert spec.activated?
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ def ruby_install_name name
+ orig_RUBY_INSTALL_NAME = RbConfig::CONFIG['ruby_install_name']
+ RbConfig::CONFIG['ruby_install_name'] = name
+
+ yield
+ ensure
+ if orig_RUBY_INSTALL_NAME then
+ RbConfig::CONFIG['ruby_install_name'] = orig_RUBY_INSTALL_NAME
+ else
+ RbConfig::CONFIG.delete 'ruby_install_name'
+ end
+ end
+
+ def with_plugin(path)
+ test_plugin_path = File.expand_path("test/rubygems/plugin/#{path}",
+ @@project_dir)
+
+ # A single test plugin should get loaded once only, in order to preserve
+ # sane test semantics.
+ refute_includes $LOAD_PATH, test_plugin_path
+ $LOAD_PATH.unshift test_plugin_path
+
+ capture_io do
+ yield
+ end
+ ensure
+ $LOAD_PATH.delete test_plugin_path
+ end
+
+ def util_ensure_gem_dirs
+ Gem.ensure_gem_subdirectories @gemhome
+
+ #
+ # FIXME what does this solve precisely? -ebh
+ #
+ @additional.each do |dir|
+ Gem.ensure_gem_subdirectories @gemhome
+ end
+ end
+
+ def util_exec_gem
+ spec, _ = util_spec 'a', '4' do |s|
+ s.executables = ['exec', 'abin']
+ end
+
+ @exec_path = File.join spec.full_gem_path, spec.bindir, 'exec'
+ @abin_path = File.join spec.full_gem_path, spec.bindir, 'abin'
+ end
+
+ def util_remove_interrupt_command
+ Gem::Commands.send :remove_const, :InterruptCommand if
+ Gem::Commands.const_defined? :InterruptCommand
+ end
+
+ def util_cache_dir
+ File.join Gem.dir, "cache"
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_available_set.rb b/jni/ruby/test/rubygems/test_gem_available_set.rb
new file mode 100644
index 0000000..2b51544
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_available_set.rb
@@ -0,0 +1,128 @@
+require 'rubygems/test_case'
+require 'rubygems/available_set'
+require 'rubygems/security'
+
+class TestGemAvailableSet < Gem::TestCase
+
+ def setup
+ super
+
+ @source = Gem::Source.new(@gem_repo)
+ end
+
+ def test_add_and_empty
+ a1, _ = util_gem 'a', '1'
+
+ set = Gem::AvailableSet.new
+ assert set.empty?
+
+ set.add a1, @source
+
+ refute set.empty?
+
+ assert_equal [a1], set.all_specs
+ end
+
+ def test_find_all
+ a1, a1_gem = util_gem 'a', 1
+ a1a, a1a_gem = util_gem 'a', '1.a'
+
+ a1_source = Gem::Source::SpecificFile.new a1_gem
+ a1a_source = Gem::Source::SpecificFile.new a1a_gem
+
+ set = Gem::AvailableSet.new
+ set.add a1, a1_source
+ set.add a1a, a1a_source
+
+ dep = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_equal %w[a-1], set.find_all(dep).map { |spec| spec.full_name }
+
+ dep = Gem::Resolver::DependencyRequest.new dep('a', '>= 0.a'), nil
+
+ assert_equal %w[a-1 a-1.a],
+ set.find_all(dep).map { |spec| spec.full_name }.sort
+ end
+
+ def test_match_platform
+ a1, _ = util_gem 'a', '1' do |g|
+ g.platform = "something-weird-yep"
+ end
+
+ a1c, _ = util_gem 'a', '2' do |g|
+ g.platform = Gem::Platform.local
+ end
+
+ a2, _ = util_gem 'a', '2'
+
+ set = Gem::AvailableSet.new
+ set.add a1, @source
+ set.add a1c, @source
+ set.add a2, @source
+
+ set.match_platform!
+
+ assert_equal [a1c, a2], set.all_specs
+ end
+
+ def test_best
+ a1, _ = util_gem 'a', '1'
+ a2, _ = util_gem 'a', '2'
+
+ set = Gem::AvailableSet.new
+ set.add a1, @source
+ set.add a2, @source
+
+ set.pick_best!
+
+ assert_equal [a2], set.all_specs
+ end
+
+ def test_remove_installed_bang
+ a1, _ = util_gem 'a', '1'
+
+ a1.activate
+
+ set = Gem::AvailableSet.new
+ set.add a1, @source
+
+ dep = Gem::Dependency.new "a", ">= 0"
+
+ set.remove_installed! dep
+
+ assert set.empty?
+ end
+
+ def test_sorted_normal_versions
+ a1, _ = util_gem 'a', '1'
+ a2, _ = util_gem 'a', '2'
+
+ set = Gem::AvailableSet.new
+ set.add a1, @source
+ set.add a2, @source
+
+ g = set.sorted
+
+ assert_equal a2, g[0].spec
+ assert_equal a1, g[1].spec
+ end
+
+ def test_sorted_respect_pre
+ a1a, _ = util_gem 'a', '1.a'
+ a1, _ = util_gem 'a', '1'
+ a2a, _ = util_gem 'a', '2.a'
+ a2, _ = util_gem 'a', '2'
+ a3a, _ = util_gem 'a', '3.a'
+
+ set = Gem::AvailableSet.new
+ set.add a1, @source
+ set.add a1a, @source
+ set.add a3a, @source
+ set.add a2a, @source
+ set.add a2, @source
+
+ g = set.sorted.map { |t| t.spec }
+
+ assert_equal [a3a, a2, a2a, a1, a1a], g
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_command.rb b/jni/ruby/test/rubygems/test_gem_command.rb
new file mode 100644
index 0000000..48cbc98
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_command.rb
@@ -0,0 +1,243 @@
+require 'rubygems/test_case'
+require 'rubygems/command'
+
+class Gem::Command
+ public :parser
+end
+
+class TestGemCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @xopt = nil
+
+ Gem::Command.common_options.clear
+ Gem::Command.common_options << [
+ ['-x', '--exe', 'Execute'], lambda do |*a|
+ @xopt = true
+ end
+ ]
+
+ @cmd_name = 'doit'
+ @cmd = Gem::Command.new @cmd_name, 'summary'
+ end
+
+ def test_self_add_specific_extra_args
+ added_args = %w[--all]
+ @cmd.add_option '--all' do |v,o| end
+
+ Gem::Command.add_specific_extra_args @cmd_name, added_args
+
+ assert_equal added_args, Gem::Command.specific_extra_args(@cmd_name)
+
+ h = @cmd.add_extra_args []
+
+ assert_equal added_args, h
+ end
+
+ def test_self_add_specific_extra_args_unknown
+ added_args = %w[--definitely_not_there]
+
+ Gem::Command.add_specific_extra_args @cmd_name, added_args
+
+ assert_equal added_args, Gem::Command.specific_extra_args(@cmd_name)
+
+ h = @cmd.add_extra_args []
+
+ assert_equal [], h
+ end
+
+ def test_basic_accessors
+ assert_equal "doit", @cmd.command
+ assert_equal "gem doit", @cmd.program_name
+ assert_equal "summary", @cmd.summary
+ end
+
+ def test_common_option_in_class
+ assert Array === Gem::Command.common_options
+ end
+
+ def test_defaults
+ @cmd.add_option('-h', '--help [COMMAND]', 'Get help on COMMAND') do |value, options|
+ options[:help] = value
+ end
+
+ @cmd.defaults = { :help => true }
+
+ @cmd.when_invoked do |options|
+ assert options[:help], "Help options should default true"
+ end
+
+ use_ui @ui do
+ @cmd.invoke
+ end
+
+ assert_match %r|Usage: gem doit|, @ui.output
+ end
+
+ def test_invoke
+ done = false
+ @cmd.when_invoked { done = true }
+
+ use_ui @ui do
+ @cmd.invoke
+ end
+
+ assert done
+ end
+
+ def test_invoke_with_bad_options
+ use_ui @ui do
+ @cmd.when_invoked do true end
+
+ ex = assert_raises OptionParser::InvalidOption do
+ @cmd.invoke('-zzz')
+ end
+
+ assert_match(/invalid option:/, ex.message)
+ end
+ end
+
+ def test_invoke_with_common_options
+ @cmd.when_invoked do true end
+
+ use_ui @ui do
+ @cmd.invoke "-x"
+ end
+
+ assert @xopt, "Should have done xopt"
+ end
+
+ def test_invoke_with_build_args
+ @cmd.when_invoked { true }
+
+ use_ui @ui do
+ @cmd.invoke_with_build_args ["-x"], ["--awesome=true"]
+ end
+
+ assert_equal ["--awesome=true"], @cmd.options[:build_args]
+ end
+
+ # Returning false from the command handler invokes the usage output.
+ def test_invoke_with_help
+ done = false
+
+ use_ui @ui do
+ @cmd.add_option('-h', '--help [COMMAND]', 'Get help on COMMAND') do |value, options|
+ options[:help] = true
+ done = true
+ end
+
+ @cmd.invoke('--help')
+
+ assert done
+ end
+
+ assert_match(/Usage/, @ui.output)
+ assert_match(/gem doit/, @ui.output)
+ assert_match(/\[options\]/, @ui.output)
+ assert_match(/-h/, @ui.output)
+ assert_match(/--help \[COMMAND\]/, @ui.output)
+ assert_match(/Get help on COMMAND/, @ui.output)
+ assert_match(/-x/, @ui.output)
+ assert_match(/--exe/, @ui.output)
+ assert_match(/Execute/, @ui.output)
+ assert_match(/Common Options:/, @ui.output)
+ end
+
+ def test_invoke_with_options
+ @cmd.add_option('-h', '--help [COMMAND]', 'Get help on COMMAND') do |value, options|
+ options[:help] = true
+ end
+
+ @cmd.when_invoked do |opts|
+ assert opts[:help]
+ end
+
+ use_ui @ui do
+ @cmd.invoke '-h'
+ end
+
+ assert_match %r|Usage: gem doit|, @ui.output
+ end
+
+ def test_option_recognition
+ @cmd.add_option('-h', '--help [COMMAND]', 'Get help on COMMAND') do |value, options|
+ options[:help] = true
+ end
+ @cmd.add_option('-f', '--file FILE', 'File option') do |value, options|
+ options[:help] = true
+ end
+ assert @cmd.handles?(['-x'])
+ assert @cmd.handles?(['-h'])
+ assert @cmd.handles?(['-h', 'command'])
+ assert @cmd.handles?(['--help', 'command'])
+ assert @cmd.handles?(['-f', 'filename'])
+ assert @cmd.handles?(['--file=filename'])
+ refute @cmd.handles?(['-z'])
+ refute @cmd.handles?(['-f'])
+ refute @cmd.handles?(['--toothpaste'])
+
+ args = ['-h', 'command']
+ @cmd.handles?(args)
+ assert_equal ['-h', 'command'], args
+ end
+
+ def test_show_lookup_failure_suggestions_local
+ correct = "non_existent_with_hint"
+ misspelled = "nonexistent_with_hint"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec correct, 2
+ end
+
+ use_ui @ui do
+ @cmd.show_lookup_failure misspelled, Gem::Requirement.default, [], :local
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'nonexistent_with_hint' (>= 0) in any repository
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_show_lookup_failure_suggestions_none
+ spec_fetcher do |fetcher|
+ fetcher.spec 'correct', 2
+ end
+
+ use_ui @ui do
+ @cmd.show_lookup_failure 'other', Gem::Requirement.default, [], :remote
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'other' (>= 0) in any repository
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_show_lookup_failure_suggestions_remote
+ correct = "non_existent_with_hint"
+ misspelled = "nonexistent_with_hint"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec correct, 2
+ end
+
+ use_ui @ui do
+ @cmd.show_lookup_failure misspelled, Gem::Requirement.default, [], :remote
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'nonexistent_with_hint' (>= 0) in any repository
+ERROR: Possible alternatives: non_existent_with_hint
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_command_manager.rb b/jni/ruby/test/rubygems/test_gem_command_manager.rb
new file mode 100644
index 0000000..f6433c5
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_command_manager.rb
@@ -0,0 +1,263 @@
+require 'rubygems/test_case'
+require 'rubygems/command_manager'
+
+class TestGemCommandManager < Gem::TestCase
+
+ def setup
+ super
+
+ @command_manager = Gem::CommandManager.new
+ end
+
+ def test_find_command
+ command = @command_manager.find_command 'install'
+
+ assert_kind_of Gem::Commands::InstallCommand, command
+
+ command = @command_manager.find_command 'ins'
+
+ assert_kind_of Gem::Commands::InstallCommand, command
+ end
+
+ def test_find_command_ambiguous
+ e = assert_raises Gem::CommandLineError do
+ @command_manager.find_command 'u'
+ end
+
+ assert_equal 'Ambiguous command u matches [uninstall, unpack, update]',
+ e.message
+ end
+
+ def test_find_command_ambiguous_exact
+ ins_command = Class.new
+ Gem::Commands.send :const_set, :InsCommand, ins_command
+
+ @command_manager.register_command :ins
+
+ command = @command_manager.find_command 'ins'
+
+ assert_kind_of ins_command, command
+ ensure
+ Gem::Commands.send :remove_const, :InsCommand
+ end
+
+ def test_find_command_unknown
+ e = assert_raises Gem::CommandLineError do
+ @command_manager.find_command 'xyz'
+ end
+
+ assert_equal 'Unknown command xyz', e.message
+ end
+
+ def test_run_interrupt
+ old_load_path = $:.dup
+ $: << File.expand_path("test/rubygems", @@project_dir)
+ Gem.load_env_plugins
+
+ @command_manager.register_command :interrupt
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @command_manager.run %w[interrupt]
+ end
+ assert_equal '', ui.output
+ assert_equal "ERROR: Interrupted\n", ui.error
+ end
+ ensure
+ $:.replace old_load_path
+ Gem::CommandManager.reset
+ end
+
+ def test_run_crash_command
+ old_load_path = $:.dup
+ $: << File.expand_path("test/rubygems", @@project_dir)
+
+ @command_manager.register_command :crash
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @command_manager.run %w[crash]
+ end
+ assert_equal '', ui.output
+ err = ui.error.split("\n").first
+ assert_equal "ERROR: Loading command: crash (RuntimeError)", err
+ end
+ ensure
+ $:.replace old_load_path
+ @command_manager.unregister_command :crash
+ end
+
+ def test_process_args_bad_arg
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @command_manager.process_args %w[--bad-arg]
+ end
+ end
+
+ assert_match(/invalid option: --bad-arg/i, @ui.error)
+ end
+
+ # HACK move to install command test
+ def test_process_args_install
+ #capture all install options
+ use_ui @ui do
+ check_options = nil
+ @command_manager['install'].when_invoked do |options|
+ check_options = options
+ true
+ end
+
+ #check defaults
+ @command_manager.process_args %w[install]
+ assert_equal %w[ri], check_options[:document].sort
+ assert_equal false, check_options[:force]
+ assert_equal :both, check_options[:domain]
+ assert_equal true, check_options[:wrappers]
+ assert_equal Gem::Requirement.default, check_options[:version]
+ assert_equal nil, check_options[:install_dir]
+ assert_equal nil, check_options[:bin_dir]
+
+ #check settings
+ check_options = nil
+ @command_manager.process_args %w[
+ install --force --local --rdoc --install-dir .
+ --version 3.0 --no-wrapper --bindir .
+ ]
+ assert_equal %w[rdoc ri], check_options[:document].sort
+ assert_equal true, check_options[:force]
+ assert_equal :local, check_options[:domain]
+ assert_equal false, check_options[:wrappers]
+ assert_equal Gem::Requirement.new('3.0'), check_options[:version]
+ assert_equal Dir.pwd, check_options[:install_dir]
+ assert_equal Dir.pwd, check_options[:bin_dir]
+
+ #check remote domain
+ check_options = nil
+ @command_manager.process_args %w[install --remote]
+ assert_equal :remote, check_options[:domain]
+
+ #check both domain
+ check_options = nil
+ @command_manager.process_args %w[install --both]
+ assert_equal :both, check_options[:domain]
+
+ #check both domain
+ check_options = nil
+ @command_manager.process_args %w[install --both]
+ assert_equal :both, check_options[:domain]
+ end
+ end
+
+ # HACK move to uninstall command test
+ def test_process_args_uninstall
+ #capture all uninstall options
+ check_options = nil
+ @command_manager['uninstall'].when_invoked do |options|
+ check_options = options
+ true
+ end
+
+ #check defaults
+ @command_manager.process_args %w[uninstall]
+ assert_equal Gem::Requirement.default, check_options[:version]
+
+ #check settings
+ check_options = nil
+ @command_manager.process_args %w[uninstall foobar --version 3.0]
+ assert_equal "foobar", check_options[:args].first
+ assert_equal Gem::Requirement.new('3.0'), check_options[:version]
+ end
+
+ # HACK move to check command test
+ def test_process_args_check
+ #capture all check options
+ check_options = nil
+ @command_manager['check'].when_invoked do |options|
+ check_options = options
+ true
+ end
+
+ #check defaults
+ @command_manager.process_args %w[check]
+ assert_equal true, check_options[:alien]
+
+ #check settings
+ check_options = nil
+ @command_manager.process_args %w[check foobar --alien]
+ assert_equal true, check_options[:alien]
+ end
+
+ # HACK move to build command test
+ def test_process_args_build
+ #capture all build options
+ check_options = nil
+ @command_manager['build'].when_invoked do |options|
+ check_options = options
+ true
+ end
+
+ #check defaults
+ @command_manager.process_args %w[build]
+ #NOTE: Currently no defaults
+
+ #check settings
+ check_options = nil
+ @command_manager.process_args %w[build foobar.rb]
+ assert_equal 'foobar.rb', check_options[:args].first
+ end
+
+ # HACK move to query command test
+ def test_process_args_query
+ #capture all query options
+ check_options = nil
+ @command_manager['query'].when_invoked do |options|
+ check_options = options
+ true
+ end
+
+ #check defaults
+ @command_manager.process_args %w[query]
+ assert_equal(//, check_options[:name])
+ assert_equal :local, check_options[:domain]
+ assert_equal false, check_options[:details]
+
+ #check settings
+ check_options = nil
+ @command_manager.process_args %w[query --name foobar --local --details]
+ assert_equal(/foobar/i, check_options[:name])
+ assert_equal :local, check_options[:domain]
+ assert_equal true, check_options[:details]
+
+ #remote domain
+ check_options = nil
+ @command_manager.process_args %w[query --remote]
+ assert_equal :remote, check_options[:domain]
+
+ #both (local/remote) domains
+ check_options = nil
+ @command_manager.process_args %w[query --both]
+ assert_equal :both, check_options[:domain]
+ end
+
+ # HACK move to update command test
+ def test_process_args_update
+ #capture all update options
+ check_options = nil
+ @command_manager['update'].when_invoked do |options|
+ check_options = options
+ true
+ end
+
+ #check defaults
+ @command_manager.process_args %w[update]
+ assert_includes check_options[:document], 'rdoc'
+
+ #check settings
+ check_options = nil
+ @command_manager.process_args %w[update --force --rdoc --install-dir .]
+ assert_includes check_options[:document], 'ri'
+ assert_equal true, check_options[:force]
+ assert_equal Dir.pwd, check_options[:install_dir]
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_build_command.rb b/jni/ruby/test/rubygems/test_gem_commands_build_command.rb
new file mode 100644
index 0000000..5f870c0
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_build_command.rb
@@ -0,0 +1,110 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/build_command'
+require 'rubygems/package'
+
+class TestGemCommandsBuildCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @gem = util_spec 'some_gem' do |s|
+ s.rubyforge_project = 'example'
+ end
+
+ @cmd = Gem::Commands::BuildCommand.new
+ end
+
+ def test_execute
+ gemspec_file = File.join(@tempdir, @gem.spec_name)
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ util_test_build_gem @gem, gemspec_file
+ end
+
+ def test_execute_bad_spec
+ @gem.date = "2010-11-08"
+
+ gemspec_file = File.join(@tempdir, @gem.spec_name)
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby.sub(/11-08/, "11-8")
+ end
+
+ @cmd.options[:args] = [gemspec_file]
+
+ out, err = use_ui @ui do
+ capture_io do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+ end
+
+ assert_equal "", out
+ assert_match(/invalid date format in specification/, err)
+
+ assert_equal '', @ui.output
+ assert_equal "ERROR: Error loading gemspec. Aborting.\n", @ui.error
+ end
+
+ def test_execute_missing_file
+ @cmd.options[:args] = %w[some_gem]
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_equal "ERROR: Gemspec file not found: some_gem\n", @ui.error
+ end
+
+ def util_test_build_gem(gem, gemspec_file, check_licenses=true)
+ @cmd.options[:args] = [gemspec_file]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ output = @ui.output.split "\n"
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: some_gem", output.shift
+ assert_equal " Version: 2", output.shift
+ assert_equal " File: some_gem-2.gem", output.shift
+ assert_equal [], output
+
+ if check_licenses
+ assert_match "WARNING: licenses is empty", @ui.error
+ end
+
+ gem_file = File.join @tempdir, File.basename(gem.cache_file)
+ assert File.exist?(gem_file)
+
+ spec = Gem::Package.new(gem_file).spec
+
+ assert_equal "some_gem", spec.name
+ assert_equal "this is a summary", spec.summary
+ end
+
+ def test_execute_force
+ gemspec_file = File.join(@tempdir, @gem.spec_name)
+
+ @gem.send :remove_instance_variable, :@rubygems_version
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ @cmd.options[:args] = [gemspec_file]
+ @cmd.options[:force] = true
+
+ util_test_build_gem @gem, gemspec_file, false
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_cert_command.rb b/jni/ruby/test/rubygems/test_gem_commands_cert_command.rb
new file mode 100644
index 0000000..4c1dcc2
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_cert_command.rb
@@ -0,0 +1,670 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/cert_command'
+require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
+
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping `gem cert` tests. openssl not found.'
+end
+
+class TestGemCommandsCertCommand < Gem::TestCase
+
+ ALTERNATE_CERT = load_cert 'alternate'
+
+ ALTERNATE_KEY_FILE = key_path 'alternate'
+ PRIVATE_KEY_FILE = key_path 'private'
+ PUBLIC_KEY_FILE = key_path 'public'
+
+ ALTERNATE_CERT_FILE = cert_path 'alternate'
+ CHILD_CERT_FILE = cert_path 'child'
+ PUBLIC_CERT_FILE = cert_path 'public'
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::CertCommand.new
+
+ @trust_dir = Gem::Security.trust_dir
+ end
+
+ def test_certificates_matching
+ @trust_dir.trust_cert PUBLIC_CERT
+ @trust_dir.trust_cert ALTERNATE_CERT
+
+ matches = @cmd.certificates_matching ''
+
+ # HACK OpenSSL::X509::Certificate#== is Object#==, so do this the hard way
+ match = matches.next
+ assert_equal ALTERNATE_CERT.to_pem, match.first.to_pem
+ assert_equal @trust_dir.cert_path(ALTERNATE_CERT), match.last
+
+ match = matches.next
+ assert_equal PUBLIC_CERT.to_pem, match.first.to_pem
+ assert_equal @trust_dir.cert_path(PUBLIC_CERT), match.last
+
+ assert_raises StopIteration do
+ matches.next
+ end
+ end
+
+ def test_certificates_matching_filter
+ @trust_dir.trust_cert PUBLIC_CERT
+ @trust_dir.trust_cert ALTERNATE_CERT
+
+ matches = @cmd.certificates_matching 'alternate'
+
+ match = matches.next
+ assert_equal ALTERNATE_CERT.to_pem, match.first.to_pem
+ assert_equal @trust_dir.cert_path(ALTERNATE_CERT), match.last
+
+ assert_raises StopIteration do
+ matches.next
+ end
+ end
+
+ def test_execute_add
+ @cmd.handle_options %W[--add #{PUBLIC_CERT_FILE}]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ cert_path = @trust_dir.cert_path PUBLIC_CERT
+
+ assert_path_exists cert_path
+
+ assert_equal "Added '/CN=nobody/DC=example'\n", @ui.output
+ assert_empty @ui.error
+ end
+
+ def test_execute_add_twice
+ self.class.cert_path 'alternate'
+
+ @cmd.handle_options %W[
+ --add #{PUBLIC_CERT_FILE}
+ --add #{ALTERNATE_CERT_FILE}
+ ]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EXPECTED
+Added '/CN=nobody/DC=example'
+Added '/CN=alternate/DC=example'
+ EXPECTED
+
+ assert_equal expected, @ui.output
+ assert_empty @ui.error
+ end
+
+ def test_execute_build
+ passphrase = 'Foo bar'
+
+ @cmd.handle_options %W[--build nobody@example.com]
+
+ @build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase}"
+
+ use_ui @build_ui do
+ @cmd.execute
+ end
+
+ output = @build_ui.output.squeeze("\n").split "\n"
+
+ assert_equal "Passphrase for your Private Key: ",
+ output.shift
+ assert_equal "Please repeat the passphrase for your Private Key: ",
+ output.shift
+ assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
+ output.shift
+ assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}",
+ output.shift
+
+ assert_equal "Don't forget to move the key file to somewhere private!",
+ output.shift
+
+ assert_empty output
+ assert_empty @build_ui.error
+
+ assert_path_exists File.join(@tempdir, 'gem-private_key.pem')
+ assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ end
+
+ def test_execute_build_bad_passphrase_confirmation
+ passphrase = 'Foo bar'
+ passphrase_confirmation = 'Fu bar'
+
+ @cmd.handle_options %W[--build nobody@example.com]
+
+ @build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase_confirmation}"
+
+ use_ui @build_ui do
+ e = assert_raises Gem::CommandLineError do
+ @cmd.execute
+ end
+
+ output = @build_ui.output.squeeze("\n").split "\n"
+
+ assert_equal "Passphrase for your Private Key: ",
+ output.shift
+ assert_equal "Please repeat the passphrase for your Private Key: ",
+ output.shift
+
+ assert_empty output
+
+ assert_equal "Passphrase and passphrase confirmation don't match",
+ e.message
+
+ end
+
+ refute_path_exists File.join(@tempdir, 'gem-private_key.pem')
+ refute_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ end
+
+ def test_execute_build_key
+ @cmd.handle_options %W[
+ --build nobody@example.com
+ --private-key #{PRIVATE_KEY_FILE}
+ ]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ output = @ui.output.split "\n"
+
+ assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
+ output.shift
+
+ assert_empty output
+ assert_empty @ui.error
+
+ assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ refute_path_exists File.join(@tempdir, 'gem-private_key.pem')
+ end
+
+ def test_execute_build_encrypted_key
+ @cmd.handle_options %W[
+ --build nobody@example.com
+ --private-key #{ENCRYPTED_PRIVATE_KEY_PATH}
+ ]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ output = @ui.output.split "\n"
+
+ assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
+ output.shift
+
+ assert_empty output
+ assert_empty @ui.error
+
+ assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ end
+
+ def test_execute_certificate
+ use_ui @ui do
+ @cmd.handle_options %W[--certificate #{PUBLIC_CERT_FILE}]
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem
+ end
+
+ def test_execute_list
+ @trust_dir.trust_cert PUBLIC_CERT
+ @trust_dir.trust_cert ALTERNATE_CERT
+
+ @cmd.handle_options %W[--list]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "/CN=alternate/DC=example\n/CN=nobody/DC=example\n",
+ @ui.output
+ assert_empty @ui.error
+ end
+
+ def test_execute_list_filter
+ @trust_dir.trust_cert PUBLIC_CERT
+ @trust_dir.trust_cert ALTERNATE_CERT
+
+ @cmd.handle_options %W[--list nobody]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "/CN=nobody/DC=example\n", @ui.output
+ assert_empty @ui.error
+ end
+
+ def test_execute_private_key
+ use_ui @ui do
+ @cmd.send :handle_options, %W[--private-key #{PRIVATE_KEY_FILE}]
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
+ end
+
+ def test_execute_encrypted_private_key
+ use_ui @ui do
+ @cmd.send :handle_options, %W[--private-key #{ENCRYPTED_PRIVATE_KEY_PATH}]
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal ENCRYPTED_PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
+ end
+
+ def test_execute_remove
+ @trust_dir.trust_cert PUBLIC_CERT
+
+ cert_path = @trust_dir.cert_path PUBLIC_CERT
+
+ assert_path_exists cert_path
+
+ @cmd.handle_options %W[--remove nobody]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "Removed '/CN=nobody/DC=example'\n", @ui.output
+ assert_equal '', @ui.error
+
+ refute_path_exists cert_path
+ end
+
+ def test_execute_remove_multiple
+ @trust_dir.trust_cert PUBLIC_CERT
+ @trust_dir.trust_cert ALTERNATE_CERT
+
+ public_path = @trust_dir.cert_path PUBLIC_CERT
+ alternate_path = @trust_dir.cert_path ALTERNATE_CERT
+
+ assert_path_exists public_path
+ assert_path_exists alternate_path
+
+ @cmd.handle_options %W[--remove example]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EXPECTED
+Removed '/CN=alternate/DC=example'
+Removed '/CN=nobody/DC=example'
+ EXPECTED
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+
+ refute_path_exists public_path
+ refute_path_exists alternate_path
+ end
+
+ def test_execute_remove_twice
+ @trust_dir.trust_cert PUBLIC_CERT
+ @trust_dir.trust_cert ALTERNATE_CERT
+
+ public_path = @trust_dir.cert_path PUBLIC_CERT
+ alternate_path = @trust_dir.cert_path ALTERNATE_CERT
+
+ assert_path_exists public_path
+ assert_path_exists alternate_path
+
+ @cmd.handle_options %W[--remove nobody --remove alternate]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EXPECTED
+Removed '/CN=nobody/DC=example'
+Removed '/CN=alternate/DC=example'
+ EXPECTED
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+
+ refute_path_exists public_path
+ refute_path_exists alternate_path
+ end
+
+ def test_execute_sign
+ path = File.join @tempdir, 'cert.pem'
+ Gem::Security.write ALTERNATE_CERT, path, 0600
+
+ assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s
+
+ @cmd.handle_options %W[
+ --private-key #{PRIVATE_KEY_FILE}
+ --certificate #{PUBLIC_CERT_FILE}
+
+ --sign #{path}
+ ]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+
+ cert = OpenSSL::X509::Certificate.new File.read path
+
+ assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
+
+ mask = 0100600 & (~File.umask)
+
+ assert_equal mask, File.stat(path).mode unless win_platform?
+ end
+
+ def test_execute_sign_encrypted_key
+ path = File.join @tempdir, 'cert.pem'
+ Gem::Security.write ALTERNATE_CERT, path, 0600
+
+ assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s
+
+ @cmd.handle_options %W[
+ --private-key #{ENCRYPTED_PRIVATE_KEY_PATH}
+ --certificate #{PUBLIC_CERT_FILE}
+
+ --sign #{path}
+ ]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+
+ cert = OpenSSL::X509::Certificate.new File.read path
+
+ assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
+
+ mask = 0100600 & (~File.umask)
+
+ assert_equal mask, File.stat(path).mode unless win_platform?
+ end
+
+ def test_execute_sign_default
+ FileUtils.mkdir_p File.join Gem.user_home, '.gem'
+
+ private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
+ Gem::Security.write PRIVATE_KEY, private_key_path
+
+ public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
+ Gem::Security.write PUBLIC_CERT, public_cert_path
+
+ path = File.join @tempdir, 'cert.pem'
+ Gem::Security.write ALTERNATE_CERT, path, 0600
+
+ assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s
+
+ @cmd.handle_options %W[--sign #{path}]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+
+ cert = OpenSSL::X509::Certificate.new File.read path
+
+ assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
+
+ mask = 0100600 & (~File.umask)
+
+ assert_equal mask, File.stat(path).mode unless win_platform?
+ end
+
+ def test_execute_sign_default_encrypted_key
+ FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
+
+ private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
+ Gem::Security.write ENCRYPTED_PRIVATE_KEY, private_key_path, 0600, PRIVATE_KEY_PASSPHRASE
+
+ public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
+ Gem::Security.write PUBLIC_CERT, public_cert_path
+
+ path = File.join @tempdir, 'cert.pem'
+ Gem::Security.write ALTERNATE_CERT, path, 0600
+
+ assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s
+
+ @cmd.handle_options %W[--sign #{path}]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+
+ cert = OpenSSL::X509::Certificate.new File.read path
+
+ assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
+
+ mask = 0100600 & (~File.umask)
+
+ assert_equal mask, File.stat(path).mode unless win_platform?
+ end
+
+ def test_execute_sign_no_cert
+ FileUtils.mkdir_p File.join Gem.user_home, '.gem'
+
+ private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
+ Gem::Security.write PRIVATE_KEY, private_key_path
+
+ path = File.join @tempdir, 'cert.pem'
+ Gem::Security.write ALTERNATE_CERT, path, 0600
+
+ assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s
+
+ @cmd.handle_options %W[--sign #{path}]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+
+ expected = <<-EXPECTED
+ERROR: --certificate not specified and ~/.gem/gem-public_cert.pem does not exist
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_execute_sign_no_key
+ FileUtils.mkdir_p File.join Gem.user_home, '.gem'
+
+ public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
+ Gem::Security.write PUBLIC_CERT, public_cert_path
+
+ path = File.join @tempdir, 'cert.pem'
+ Gem::Security.write ALTERNATE_CERT, path, 0600
+
+ assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s
+
+ @cmd.handle_options %W[--sign #{path}]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+
+ expected = <<-EXPECTED
+ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exist
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_handle_options
+ @cmd.handle_options %W[
+ --add #{PUBLIC_CERT_FILE}
+ --add #{ALTERNATE_CERT_FILE}
+
+ --remove nobody
+ --remove example
+
+ --list
+ --list example
+
+ --build nobody@example
+ --build other@example
+ ]
+
+ assert_equal [PUBLIC_CERT.to_pem, ALTERNATE_CERT.to_pem],
+ @cmd.options[:add].map { |cert| cert.to_pem }
+
+ assert_equal %w[nobody example], @cmd.options[:remove]
+
+ assert_equal %w[nobody@example other@example],
+ @cmd.options[:build].map { |name| name.to_s }
+
+ assert_equal ['', 'example'], @cmd.options[:list]
+ end
+
+ def test_handle_options_add_bad
+ nonexistent = File.join @tempdir, 'nonexistent'
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[--add #{nonexistent}]
+ end
+
+ assert_equal "invalid argument: --add #{nonexistent}: does not exist",
+ e.message
+
+ bad = File.join @tempdir, 'bad'
+ FileUtils.touch bad
+
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[--add #{bad}]
+ end
+
+ assert_equal "invalid argument: --add #{bad}: invalid X509 certificate",
+ e.message
+ end
+
+ def test_handle_options_certificate
+ nonexistent = File.join @tempdir, 'nonexistent'
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[--certificate #{nonexistent}]
+ end
+
+ assert_equal "invalid argument: --certificate #{nonexistent}: does not exist",
+ e.message
+
+ bad = File.join @tempdir, 'bad'
+ FileUtils.touch bad
+
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[--certificate #{bad}]
+ end
+
+ assert_equal "invalid argument: " +
+ "--certificate #{bad}: invalid X509 certificate",
+ e.message
+ end
+
+ def test_handle_options_key_bad
+ nonexistent = File.join @tempdir, 'nonexistent'
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[--private-key #{nonexistent}]
+ end
+
+ assert_equal "invalid argument: " +
+ "--private-key #{nonexistent}: does not exist",
+ e.message
+
+ bad = File.join @tempdir, 'bad'
+ FileUtils.touch bad
+
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[--private-key #{bad}]
+ end
+
+ assert_equal "invalid argument: --private-key #{bad}: invalid RSA key",
+ e.message
+
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[--private-key #{PUBLIC_KEY_FILE}]
+ end
+
+ assert_equal "invalid argument: " +
+ "--private-key #{PUBLIC_KEY_FILE}: private key not found",
+ e.message
+ end
+
+ def test_handle_options_sign
+ @cmd.handle_options %W[
+ --private-key #{ALTERNATE_KEY_FILE}
+ --private-key #{PRIVATE_KEY_FILE}
+
+ --certificate #{ALTERNATE_CERT_FILE}
+ --certificate #{PUBLIC_CERT_FILE}
+
+ --sign #{ALTERNATE_CERT_FILE}
+ --sign #{CHILD_CERT_FILE}
+ ]
+
+ assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
+ assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem
+
+ assert_equal [ALTERNATE_CERT_FILE, CHILD_CERT_FILE], @cmd.options[:sign]
+ end
+
+ def test_handle_options_sign_encrypted_key
+ @cmd.handle_options %W[
+ --private-key #{ALTERNATE_KEY_FILE}
+ --private-key #{ENCRYPTED_PRIVATE_KEY_PATH}
+
+ --certificate #{ALTERNATE_CERT_FILE}
+ --certificate #{PUBLIC_CERT_FILE}
+
+ --sign #{ALTERNATE_CERT_FILE}
+ --sign #{CHILD_CERT_FILE}
+ ]
+
+ assert_equal ENCRYPTED_PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
+ assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem
+
+ assert_equal [ALTERNATE_CERT_FILE, CHILD_CERT_FILE], @cmd.options[:sign]
+ end
+
+ def test_handle_options_sign_nonexistent
+ nonexistent = File.join @tempdir, 'nonexistent'
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[
+ --private-key #{ALTERNATE_KEY_FILE}
+
+ --certificate #{ALTERNATE_CERT_FILE}
+
+ --sign #{nonexistent}
+ ]
+ end
+
+ assert_equal "invalid argument: --sign #{nonexistent}: does not exist",
+ e.message
+ end
+
+end if defined?(OpenSSL::SSL)
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_check_command.rb b/jni/ruby/test/rubygems/test_gem_commands_check_command.rb
new file mode 100644
index 0000000..67db6a3
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_check_command.rb
@@ -0,0 +1,68 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/check_command'
+
+class TestGemCommandsCheckCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::CheckCommand.new
+ end
+
+ def gem name
+ spec = quick_gem name do |gem|
+ gem.files = %W[lib/#{name}.rb Rakefile]
+ end
+
+ write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
+ write_file File.join(*%W[gems #{spec.full_name} Rakefile])
+
+ spec
+ end
+
+ def test_initialize
+ assert_equal "check", @cmd.command
+ assert_equal "gem check", @cmd.program_name
+ assert_match(/Check/, @cmd.summary)
+ end
+
+ def test_handle_options
+ @cmd.handle_options %w[--no-alien --no-gems --doctor --dry-run]
+
+ assert @cmd.options[:doctor]
+ refute @cmd.options[:alien]
+ assert @cmd.options[:dry_run]
+ refute @cmd.options[:gems]
+ end
+
+ def test_handle_options_defaults
+ @cmd.handle_options []
+
+ assert @cmd.options[:alien]
+ assert @cmd.options[:gems]
+ refute @cmd.options[:doctor]
+ refute @cmd.options[:dry_run]
+ end
+
+ def test_doctor
+ gem 'a'
+ b = gem 'b'
+
+ FileUtils.rm b.spec_file
+
+ assert_path_exists b.gem_dir
+ refute_path_exists b.spec_file
+
+ Gem.use_paths @gemhome
+
+ capture_io do
+ use_ui @ui do
+ @cmd.doctor
+ end
+ end
+
+ refute_path_exists b.gem_dir
+ refute_path_exists b.spec_file
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_cleanup_command.rb b/jni/ruby/test/rubygems/test_gem_commands_cleanup_command.rb
new file mode 100644
index 0000000..fdaac54
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_cleanup_command.rb
@@ -0,0 +1,167 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/cleanup_command'
+
+class TestGemCommandsCleanupCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::CleanupCommand.new
+
+ @a_1 = util_spec 'a', 1
+ @a_2 = util_spec 'a', 2
+
+ install_gem @a_1
+ install_gem @a_2
+ end
+
+ def test_handle_options_d
+ @cmd.handle_options %w[-d]
+ assert @cmd.options[:dryrun]
+ end
+
+ def test_handle_options_dry_run
+ @cmd.handle_options %w[--dryrun]
+ assert @cmd.options[:dryrun]
+ end
+
+ def test_handle_options_n
+ @cmd.handle_options %w[-n]
+ assert @cmd.options[:dryrun]
+ end
+
+ def test_execute
+ @cmd.options[:args] = %w[a]
+
+ @cmd.execute
+
+ refute_path_exists @a_1.gem_dir
+ end
+
+ def test_execute_all_dependencies
+ @b_1 = util_spec 'b', 1 do |s| s.add_dependency 'a', '1' end
+ @b_2 = util_spec 'b', 2 do |s| s.add_dependency 'a', '2' end
+
+ install_gem @b_1
+ install_gem @b_2
+
+ @cmd.options[:args] = []
+
+ @cmd.execute
+
+ refute_path_exists @a_1.gem_dir
+ refute_path_exists @b_1.gem_dir
+ end
+
+ def test_execute_all
+ gemhome2 = File.join @tempdir, 'gemhome2'
+
+ Gem.ensure_gem_subdirectories gemhome2
+
+ Gem.use_paths @gemhome, gemhome2
+
+ @b_1 = util_spec 'b', 1
+ @b_2 = util_spec 'b', 2
+
+ install_gem @b_1
+ install_gem @b_2
+
+ @cmd.options[:args] = []
+
+ @cmd.execute
+
+ assert_equal @gemhome, Gem.dir, 'GEM_HOME'
+ assert_equal [@gemhome, gemhome2], Gem.path.sort, 'GEM_PATH'
+
+ refute_path_exists @a_1.gem_dir
+ refute_path_exists @b_1.gem_dir
+ end
+
+ def test_execute_all_user
+ @a_1_1 = util_spec 'a', '1.1'
+ @a_1_1 = install_gem_user @a_1_1 # pick up user install path
+
+ Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
+
+ assert_path_exists @a_1.gem_dir
+ assert_path_exists @a_1_1.gem_dir
+
+ @cmd.options[:args] = %w[a]
+
+ @cmd.execute
+
+ refute_path_exists @a_1.gem_dir
+ refute_path_exists @a_1_1.gem_dir
+ end
+
+ def test_execute_all_user_no_sudo
+ FileUtils.chmod 0555, @gemhome
+
+ @a_1_1 = util_spec 'a', '1.1'
+ @a_1_1 = install_gem_user @a_1_1 # pick up user install path
+
+ Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
+
+ assert_path_exists @a_1.gem_dir
+ assert_path_exists @a_1_1.gem_dir
+
+ @cmd.options[:args] = %w[a]
+
+ @cmd.execute
+
+ assert_path_exists @a_1.gem_dir
+ refute_path_exists @a_1_1.gem_dir
+ ensure
+ FileUtils.chmod 0755, @gemhome
+ end unless win_platform?
+
+ def test_execute_dry_run
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:dryrun] = true
+
+ @cmd.execute
+
+ assert_path_exists @a_1.gem_dir
+ end
+
+ def test_execute_keeps_older_versions_with_deps
+ @b_1 = util_spec 'b', 1
+ @b_2 = util_spec 'b', 2
+
+ @c = util_spec 'c', 1 do |s|
+ s.add_dependency 'b', '1'
+ end
+
+ install_gem @c
+ install_gem @b_1
+ install_gem @b_2
+
+ @cmd.options[:args] = []
+
+ @cmd.execute
+
+ assert_path_exists @b_1.gem_dir
+ end
+
+ def test_execute_ignore_default_gem_verbose
+ Gem.configuration.verbose = :really
+
+ @b_1 = util_spec 'b', 1
+ @b_default = new_default_spec "b", "2"
+ @b_2 = util_spec 'b', 3
+
+ install_gem @b_1
+ install_default_specs @b_default
+ install_gem @b_2
+
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r%^Skipped default gems: b-2%, @ui.output
+ assert_empty @ui.error
+ end
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_contents_command.rb b/jni/ruby/test/rubygems/test_gem_commands_contents_command.rb
new file mode 100644
index 0000000..ae8e49f
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_contents_command.rb
@@ -0,0 +1,239 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/contents_command'
+
+class TestGemCommandsContentsCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::ContentsCommand.new
+ end
+
+ def gem name, version = 2
+ spec = quick_gem name, version do |gem|
+ gem.files = %W[lib/#{name}.rb Rakefile]
+ end
+ write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
+ write_file File.join(*%W[gems #{spec.full_name} Rakefile])
+ end
+
+ def test_execute
+ @cmd.options[:args] = %w[foo]
+
+ gem 'foo'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|lib/foo\.rb|, @ui.output
+ assert_match %r|Rakefile|, @ui.output
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_all
+ @cmd.options[:all] = true
+
+ gem 'foo'
+ gem 'bar'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|lib/foo\.rb|, @ui.output
+ assert_match %r|lib/bar\.rb|, @ui.output
+ assert_match %r|Rakefile|, @ui.output
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_bad_gem
+ @cmd.options[:args] = %w[foo]
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_match %r|Unable to find gem 'foo' in default gem paths|, @ui.output
+ assert_match %r|Directories searched:|, @ui.output
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_exact_match
+ @cmd.options[:args] = %w[foo]
+ gem 'foo'
+ gem 'bar'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|lib/foo\.rb|, @ui.output
+ assert_match %r|Rakefile|, @ui.output
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_lib_only
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:lib_only] = true
+
+ gem 'foo'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|lib/foo\.rb|, @ui.output
+ refute_match %r|Rakefile|, @ui.output
+
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_missing_single
+ @cmd.options[:args] = %w[foo]
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_match "Unable to find gem 'foo'", @ui.output
+ assert_empty @ui.error
+ end
+
+ def test_execute_missing_multiple
+ @cmd.options[:args] = %w[foo bar]
+
+ gem 'foo'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match "lib/foo.rb", @ui.output
+ assert_match "Unable to find gem 'bar'", @ui.output
+
+ assert_empty @ui.error
+ end
+
+ def test_execute_multiple
+ @cmd.options[:args] = %w[foo bar]
+
+ gem 'foo'
+ gem 'bar'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|lib/foo\.rb|, @ui.output
+ assert_match %r|lib/bar\.rb|, @ui.output
+ assert_match %r|Rakefile|, @ui.output
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_show_install_dir
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:show_install_dir] = true
+
+ gem 'foo'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = File.join @gemhome, 'gems', 'foo-2'
+
+ assert_equal "#{expected}\n", @ui.output
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_show_install_dir_version
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:show_install_dir] = true
+ @cmd.options[:version] = Gem::Requirement.new '= 1'
+
+ gem 'foo', 1
+ gem 'foo', 2
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = File.join @gemhome, 'gems', 'foo-1'
+
+ assert_equal "#{expected}\n", @ui.output
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_no_prefix
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:prefix] = false
+
+ gem 'foo'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+Rakefile
+lib/foo.rb
+ EOF
+
+ assert_equal expected, @ui.output
+
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_default_gem
+ default_gem_spec = new_default_spec("default", "2.0.0.0",
+ nil, "default/gem.rb")
+ default_gem_spec.executables = ["default_command"]
+ default_gem_spec.files += ["default_gem.so"]
+ install_default_specs(default_gem_spec)
+
+ @cmd.options[:args] = %w[default]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = [
+ [RbConfig::CONFIG['bindir'], 'default_command'],
+ [RbConfig::CONFIG['rubylibdir'], 'default/gem.rb'],
+ [RbConfig::CONFIG['archdir'], 'default_gem.so']
+ ].sort.map{|a|File.join a}.join "\n"
+
+ assert_equal expected, @ui.output.chomp
+ assert_equal "", @ui.error
+ end
+
+ def test_handle_options
+ refute @cmd.options[:lib_only]
+ assert @cmd.options[:prefix]
+ assert_empty @cmd.options[:specdirs]
+ assert_nil @cmd.options[:version]
+ refute @cmd.options[:show_install_dir]
+
+ @cmd.send :handle_options, %w[
+ -l
+ -s
+ foo
+ --version 0.0.2
+ --no-prefix
+ --show-install-dir
+ ]
+
+ assert @cmd.options[:lib_only]
+ refute @cmd.options[:prefix]
+ assert_equal %w[foo], @cmd.options[:specdirs]
+ assert_equal Gem::Requirement.new('0.0.2'), @cmd.options[:version]
+ assert @cmd.options[:show_install_dir]
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_dependency_command.rb b/jni/ruby/test/rubygems/test_gem_commands_dependency_command.rb
new file mode 100644
index 0000000..e22b240
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_dependency_command.rb
@@ -0,0 +1,221 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/dependency_command'
+
+class TestGemCommandsDependencyCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::DependencyCommand.new
+ @cmd.options[:domain] = :local
+ end
+
+ def test_execute
+ quick_gem 'foo' do |gem|
+ gem.add_dependency 'bar', '> 1'
+ gem.add_dependency 'baz', '> 1'
+ end
+
+ @cmd.options[:args] = %w[foo]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "Gem foo-2\n bar (> 1)\n baz (> 1)\n\n",
+ @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_no_args
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '2.a'
+ fetcher.spec 'dep_x', 1, 'x' => '>= 1'
+ fetcher.legacy_platform
+ end
+
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+Gem a-1
+
+Gem a-2.a
+
+Gem dep_x-1
+ x (>= 1)
+
+Gem pl-1-x86-linux
+
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_no_match
+ @cmd.options[:args] = %w[foo]
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "No gems found matching foo (>= 0)\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_pipe_format
+ util_spec 'foo' do |gem|
+ gem.add_dependency 'bar', '> 1'
+ end
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:pipe_format] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "bar --version '> 1'\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_regexp
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '2.a'
+ fetcher.spec 'a_evil', 9
+ fetcher.spec 'b', 2
+ end
+
+ @cmd.options[:args] = %w[/[ab]/]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+Gem a-1
+
+Gem a-2.a
+
+Gem a_evil-9
+
+Gem b-2
+
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_reverse
+ # FIX: this shouldn't need to write out, but fails if you switch it
+ quick_gem 'foo' do |gem|
+ gem.add_dependency 'bar', '> 1'
+ end
+
+ quick_gem 'baz' do |gem|
+ gem.add_dependency 'foo'
+ end
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:reverse_dependencies] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+Gem foo-2
+ bar (> 1)
+ Used by
+ baz-2 (foo (>= 0))
+
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_reverse_remote
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:reverse_dependencies] = true
+ @cmd.options[:domain] = :remote
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ expected = <<-EOF
+ERROR: Only reverse dependencies for local gems are supported.
+ EOF
+
+ assert_equal '', @ui.output
+ assert_equal expected, @ui.error
+ end
+
+ def test_execute_remote
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', 2, 'bar' => '> 1'
+ end
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:domain] = :remote
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "Gem foo-2\n bar (> 1)\n\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_remote_version
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
+
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ end
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:domain] = :remote
+ @cmd.options[:version] = req '= 1'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "Gem a-1\n\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', '2.a'
+ end
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:domain] = :remote
+ @cmd.options[:prerelease] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "Gem a-2.a\n\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_environment_command.rb b/jni/ruby/test/rubygems/test_gem_commands_environment_command.rb
new file mode 100644
index 0000000..81ff55d
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_environment_command.rb
@@ -0,0 +1,153 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/environment_command'
+
+class TestGemCommandsEnvironmentCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::EnvironmentCommand.new
+ end
+
+ def test_execute
+ orig_sources = Gem.sources.dup
+ orig_path, ENV['PATH'] = ENV['PATH'], %w[/usr/local/bin /usr/bin /bin].join(File::PATH_SEPARATOR)
+ Gem.sources.replace %w[http://gems.example.com]
+ Gem.configuration['gemcutter_key'] = 'blah'
+
+ @cmd.send :handle_options, %w[]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|RUBYGEMS VERSION: (\d\.)+\d|, @ui.output
+ assert_match %r|RUBY VERSION: \d\.\d\.\d \(.*\) \[.*\]|, @ui.output
+ assert_match %r|INSTALLATION DIRECTORY: #{Regexp.escape @gemhome}|,
+ @ui.output
+ assert_match %r|RUBYGEMS PREFIX: |, @ui.output
+ assert_match %r|RUBY EXECUTABLE:.*#{RbConfig::CONFIG['ruby_install_name']}|,
+ @ui.output
+ assert_match %r|SYSTEM CONFIGURATION DIRECTORY:|, @ui.output
+ assert_match %r|EXECUTABLE DIRECTORY:|, @ui.output
+ assert_match %r|RUBYGEMS PLATFORMS:|, @ui.output
+ assert_match %r|- #{Gem::Platform.local}|, @ui.output
+ assert_match %r|GEM PATHS:|, @ui.output
+ assert_match %r|- #{Regexp.escape @gemhome}|, @ui.output
+ assert_match %r|GEM CONFIGURATION:|, @ui.output
+ assert_match %r|"gemcutter_key" => "\*\*\*\*"|, @ui.output
+ assert_match %r|:verbose => |, @ui.output
+ assert_match %r|REMOTE SOURCES:|, @ui.output
+
+ assert_match %r|- SHELL PATH:|, @ui.output
+ assert_match %r|- /usr/local/bin$|, @ui.output
+ assert_match %r|- /usr/bin$|, @ui.output
+ assert_match %r|- /bin$|, @ui.output
+
+ assert_empty @ui.error
+
+ ensure
+ Gem.sources.replace orig_sources
+ ENV['PATH'] = orig_path
+ end
+
+ def test_execute_gemdir
+ @cmd.send :handle_options, %w[gemdir]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#{@gemhome}\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_gempath
+ @cmd.send :handle_options, %w[gempath]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#{@gemhome}\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_gempath_multiple
+ Gem.clear_paths
+ path = [@gemhome, "#{@gemhome}2"].join File::PATH_SEPARATOR
+ ENV['GEM_PATH'] = path
+
+ @cmd.send :handle_options, %w[gempath]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#{Gem.path.join File::PATH_SEPARATOR}\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_packageversion
+ @cmd.send :handle_options, %w[packageversion]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#{Gem::RubyGemsPackageVersion}\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_remotesources
+ orig_sources = Gem.sources.dup
+ Gem.sources.replace %w[http://gems.example.com]
+
+ @cmd.send :handle_options, %w[remotesources]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "http://gems.example.com\n", @ui.output
+ assert_equal '', @ui.error
+
+ ensure
+ Gem.sources.replace orig_sources
+ end
+
+ def test_execute_unknown
+ @cmd.send :handle_options, %w[unknown]
+
+ assert_raises Gem::CommandLineError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_version
+ @cmd.send :handle_options, %w[version]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#{Gem::VERSION}\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_platform
+ @cmd.send :handle_options, %w[platform]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#{Gem.platforms.join File::PATH_SEPARATOR}\n", @ui.output
+ assert_equal '', @ui.error
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_fetch_command.rb b/jni/ruby/test/rubygems/test_gem_commands_fetch_command.rb
new file mode 100644
index 0000000..c452e79
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_fetch_command.rb
@@ -0,0 +1,126 @@
+require 'rubygems/test_case'
+require 'rubygems/package'
+require 'rubygems/security'
+require 'rubygems/commands/fetch_command'
+
+class TestGemCommandsFetchCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::FetchCommand.new
+ end
+
+ def test_execute
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ refute_path_exists File.join(@tempdir, 'cache'), 'sanity check'
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ a2 = specs['a-2']
+
+ assert_path_exists(File.join(@tempdir, a2.file_name),
+ "#{a2.full_name} not fetched")
+ refute_path_exists File.join(@tempdir, 'cache'),
+ 'gem repository directories must not be created'
+ end
+
+ def test_execute_latest
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ end
+
+ refute_path_exists File.join(@tempdir, 'cache'), 'sanity check'
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:version] = req('>= 0.1')
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ a2 = specs['a-2']
+ assert_path_exists(File.join(@tempdir, a2.file_name),
+ "#{a2.full_name} not fetched")
+ refute_path_exists File.join(@tempdir, 'cache'),
+ 'gem repository directories must not be created'
+ end
+
+ def test_execute_prerelease
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.gem 'a', '2.a'
+ end
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:prerelease] = true
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ a2 = specs['a-2']
+
+ assert_path_exists(File.join(@tempdir, a2.file_name),
+ "#{a2.full_name} not fetched")
+ end
+
+ def test_execute_specific_prerelease
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.gem 'a', '2.a'
+ end
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:prerelease] = true
+ @cmd.options[:version] = "2.a"
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ a2_pre = specs['a-2.a']
+
+ assert_path_exists(File.join(@tempdir, a2_pre.file_name),
+ "#{a2_pre.full_name} not fetched")
+ end
+
+ def test_execute_version
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:version] = Gem::Requirement.new '1'
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ a1 = specs['a-1']
+
+ assert_path_exists(File.join(@tempdir, a1.file_name),
+ "#{a1.full_name} not fetched")
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_generate_index_command.rb b/jni/ruby/test/rubygems/test_gem_commands_generate_index_command.rb
new file mode 100644
index 0000000..2e478d9
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_generate_index_command.rb
@@ -0,0 +1,50 @@
+require 'rubygems/test_case'
+require 'rubygems/indexer'
+require 'rubygems/commands/generate_index_command'
+
+class TestGemCommandsGenerateIndexCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::GenerateIndexCommand.new
+ @cmd.options[:directory] = @gemhome
+ end
+
+ def test_execute
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ specs = File.join @gemhome, "specs.4.8.gz"
+
+ assert File.exist?(specs), specs
+ end
+
+ def test_handle_options_directory
+ return if win_platform?
+ refute_equal '/nonexistent', @cmd.options[:directory]
+
+ @cmd.handle_options %w[--directory /nonexistent]
+
+ assert_equal '/nonexistent', @cmd.options[:directory]
+ end
+
+ def test_handle_options_directory_windows
+ return unless win_platform?
+
+ refute_equal '/nonexistent', @cmd.options[:directory]
+
+ @cmd.handle_options %w[--directory C:/nonexistent]
+
+ assert_equal 'C:/nonexistent', @cmd.options[:directory]
+ end
+
+ def test_handle_options_update
+ @cmd.handle_options %w[--update]
+
+ assert @cmd.options[:update]
+ end
+
+end if ''.respond_to? :to_xs
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_help_command.rb b/jni/ruby/test/rubygems/test_gem_commands_help_command.rb
new file mode 100644
index 0000000..bed6095
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_help_command.rb
@@ -0,0 +1,74 @@
+require "rubygems"
+require "rubygems/test_case"
+require "rubygems/commands/help_command"
+require "rubygems/package"
+require "rubygems/command_manager"
+require File.expand_path('../rubygems_plugin', __FILE__)
+
+class TestGemCommandsHelpCommand < Gem::TestCase
+ def setup
+ super
+
+ @cmd = Gem::Commands::HelpCommand.new
+
+ load File.expand_path('../rubygems_plugin.rb', __FILE__) unless
+ Gem::Commands.const_defined? :InterruptCommand
+ end
+
+ def test_gem_help_bad
+ util_gem 'bad' do |out, err|
+ assert_equal('', out)
+ assert_match "Unknown command bad", err
+ end
+ end
+
+ def test_gem_help_gem_dependencies
+ util_gem 'gem_dependencies' do |out, err|
+ assert_match 'gem.deps.rb', out
+ assert_equal '', err
+ end
+ end
+
+ def test_gem_help_platforms
+ util_gem 'platforms' do |out, err|
+ assert_match(/x86-freebsd/, out)
+ assert_equal '', err
+ end
+ end
+
+ def test_gem_help_commands
+ mgr = Gem::CommandManager.new
+
+ util_gem 'commands' do |out, err|
+ mgr.command_names.each do |cmd|
+ assert_match(/\s+#{cmd}\s+\S+/, out)
+ end
+
+ if defined?(OpenSSL::SSL) then
+ assert_empty err
+
+ refute_match 'No command found for ', out
+ end
+ end
+ end
+
+ def test_gem_no_args_shows_help
+ util_gem do |out, err|
+ assert_match(/Usage:/, out)
+ assert_match(/gem install/, out)
+ assert_equal '', err
+ end
+ end
+
+ def util_gem *args
+ @cmd.options[:args] = args
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ yield @ui.output, @ui.error
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_install_command.rb b/jni/ruby/test/rubygems/test_gem_commands_install_command.rb
new file mode 100644
index 0000000..f03285a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_install_command.rb
@@ -0,0 +1,997 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/install_command'
+require 'rubygems/rdoc'
+
+class TestGemCommandsInstallCommand < Gem::TestCase
+
+ def setup
+ super
+ common_installer_setup
+
+ @cmd = Gem::Commands::InstallCommand.new
+ @cmd.options[:document] = []
+
+ @gemdeps = "tmp_install_gemdeps"
+ @orig_args = Gem::Command.build_args
+
+ common_installer_setup
+ end
+
+ def teardown
+ super
+
+ common_installer_teardown
+
+ Gem::Command.build_args = @orig_args
+ File.unlink @gemdeps if File.file? @gemdeps
+ File.unlink "#{@gemdeps}.lock" if File.file? "#{@gemdeps}.lock"
+ end
+
+ def test_execute_exclude_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.gem 'a', '2.pre'
+ end
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |spec| spec.full_name }
+ end
+
+ def test_execute_explicit_version_includes_prerelease
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.gem 'a', '2.a'
+ end
+
+ a2_pre = specs['a-2.a']
+
+ @cmd.handle_options [a2_pre.name, '--version', a2_pre.version.to_s,
+ "--no-ri", "--no-rdoc"]
+ assert @cmd.options[:prerelease]
+ assert @cmd.options[:version].satisfied_by?(a2_pre.version)
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-2.a], @cmd.installed_specs.map { |spec| spec.full_name }
+ end
+
+ def test_execute_local
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:domain] = :local
+
+ FileUtils.mv specs['a-2'].cache_file, @tempdir
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ orig_dir = Dir.pwd
+ begin
+ Dir.chdir @tempdir
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir orig_dir
+ end
+ end
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "1 gem installed", @ui.output
+ end
+
+ def test_execute_no_user_install
+ skip 'skipped on MS Windows (chmod has no effect)' if win_platform?
+
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:user_install] = false
+
+ FileUtils.mv specs['a-2'].cache_file, @tempdir
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ orig_dir = Dir.pwd
+ begin
+ FileUtils.chmod 0755, @userhome
+ FileUtils.chmod 0555, @gemhome
+
+ Dir.chdir @tempdir
+ assert_raises Gem::FilePermissionError do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir orig_dir
+ FileUtils.chmod 0755, @gemhome
+ end
+ end
+ end
+
+ def test_execute_local_missing
+ spec_fetcher
+
+ @cmd.options[:domain] = :local
+
+ @cmd.options[:args] = %w[no_such_gem]
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ assert_equal 2, e.exit_code
+ end
+
+ # HACK no repository was checked
+ assert_match(/ould not find a valid gem 'no_such_gem'/, @ui.error)
+ end
+
+ def test_execute_no_gem
+ @cmd.options[:args] = %w[]
+
+ assert_raises Gem::CommandLineError do
+ @cmd.execute
+ end
+ end
+
+ def test_execute_nonexistent
+ spec_fetcher
+
+ @cmd.options[:args] = %w[nonexistent]
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ assert_equal 2, e.exit_code
+ end
+
+ assert_match(/ould not find a valid gem 'nonexistent'/, @ui.error)
+ end
+
+ def test_execute_bad_source
+ spec_fetcher
+
+ # This is needed because we need to exercise the cache path
+ # within SpecFetcher
+ path = File.join Gem.spec_cache_dir, "not-there.nothing%80", "latest_specs.4.8"
+
+ FileUtils.mkdir_p File.dirname(path)
+
+ File.open path, "w" do |f|
+ f.write Marshal.dump([])
+ end
+
+ Gem.sources.replace ["http://not-there.nothing"]
+
+ @cmd.options[:args] = %w[nonexistent]
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ assert_equal 2, e.exit_code
+ end
+
+ errs = @ui.error.split("\n")
+
+ assert_match(/ould not find a valid gem 'nonexistent'/, errs.shift)
+ assert_match(%r!Unable to download data from http://not-there.nothing!, errs.shift)
+ end
+
+ def test_execute_nonexistent_hint_disabled
+ misspelled = "nonexistent_with_hint"
+ correctly_spelled = "non_existent_with_hint"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec correctly_spelled, 2
+ end
+
+ @cmd.options[:args] = [misspelled]
+ @cmd.options[:suggest_alternate] = false
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+
+ assert_equal 2, e.exit_code
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'nonexistent_with_hint' (>= 0) in any repository
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_execute_nonexistent_with_hint
+ misspelled = "nonexistent_with_hint"
+ correctly_spelled = "non_existent_with_hint"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec correctly_spelled, 2
+ end
+
+ @cmd.options[:args] = [misspelled]
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+
+ assert_equal 2, e.exit_code
+ end
+
+ expected = "ERROR: Could not find a valid gem 'nonexistent_with_hint' (>= 0) in any repository
+ERROR: Possible alternatives: non_existent_with_hint
+"
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_execute_nonexistent_with_dashes
+ misspelled = "non-existent_with-hint"
+ correctly_spelled = "nonexistent-with_hint"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec correctly_spelled, 2
+ fetcher.clear
+ end
+
+ @cmd.options[:args] = [misspelled]
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+
+ assert_equal 2, e.exit_code
+ end
+
+ expected = [
+ "ERROR: Could not find a valid gem 'non-existent_with-hint' (>= 0) in any repository",
+ "ERROR: Possible alternatives: nonexistent-with_hint"
+ ]
+
+ output = @ui.error.split "\n"
+
+ assert_equal expected, output
+ end
+
+ def test_execute_conflicting_install_options
+ @cmd.options[:user_install] = true
+ @cmd.options[:install_dir] = "whatever"
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ expected = "ERROR: Use --install-dir or --user-install but not both\n"
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_execute_prerelease_skipped_when_no_flag_set
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', '3.a'
+ end
+
+ @cmd.options[:prerelease] = false
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-1], @cmd.installed_specs.map { |spec| spec.full_name }
+ end
+
+ def test_execute_prerelease_wins_over_previous_ver
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', '2.a'
+ fetcher.clear
+ end
+
+ @cmd.options[:prerelease] = true
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-2.a], @cmd.installed_specs.map { |spec| spec.full_name }
+ end
+
+ def test_execute_prerelease_skipped_when_non_pre_available
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '2.pre'
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:prerelease] = true
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |spec| spec.full_name }
+ end
+
+ def test_execute_rdoc
+ skip if RUBY_VERSION <= "1.8.7"
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ Gem.done_installing(&Gem::RDoc.method(:generation_hook))
+
+ @cmd.options[:document] = %w[rdoc ri]
+ @cmd.options[:domain] = :local
+
+ a2 = specs['a-2']
+ FileUtils.mv a2.cache_file, @tempdir
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ # Don't use Dir.chdir with a block, it warnings a lot because
+ # of a downstream Dir.chdir with a block
+ old = Dir.getwd
+
+ begin
+ Dir.chdir @tempdir
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir old
+ end
+ end
+
+ wait_for_child_process_to_exit
+
+ assert_path_exists File.join(a2.doc_dir, 'ri')
+ assert_path_exists File.join(a2.doc_dir, 'rdoc')
+ end
+
+ def test_execute_saves_build_args
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ args = %w!--with-awesome=true --more-awesome=yes!
+
+ Gem::Command.build_args = args
+
+ a2 = specs['a-2']
+ FileUtils.mv a2.cache_file, @tempdir
+
+ @cmd.options[:domain] = :local
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ # Don't use Dir.chdir with a block, it warnings a lot because
+ # of a downstream Dir.chdir with a block
+ old = Dir.getwd
+
+ begin
+ Dir.chdir @tempdir
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir old
+ end
+ end
+
+ path = a2.build_info_file
+ assert_path_exists path
+
+ assert_equal args, a2.build_args
+ end
+
+
+ def test_execute_remote
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "1 gem installed", @ui.output
+ end
+
+ def test_execute_remote_ignores_files
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:domain] = :remote
+
+ a1 = specs['a-1']
+ a2 = specs['a-2']
+
+ FileUtils.mv a2.cache_file, @tempdir
+
+ @fetcher.data["#{@gem_repo}gems/#{a2.file_name}"] =
+ read_binary(a1.cache_file)
+
+ @cmd.options[:args] = [a2.name]
+
+ gemdir = File.join @gemhome, 'specifications'
+
+ a2_gemspec = File.join(gemdir, "a-2.gemspec")
+ a1_gemspec = File.join(gemdir, "a-1.gemspec")
+
+ FileUtils.rm_rf a1_gemspec
+ FileUtils.rm_rf a2_gemspec
+
+ start = Dir["#{gemdir}/*"]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+ end
+
+ assert_equal %w[a-1], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "1 gem installed", @ui.output
+
+ fin = Dir["#{gemdir}/*"]
+
+ assert_equal [a1_gemspec], fin - start
+ end
+
+ def test_execute_two
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.gem 'b', 2
+ end
+
+ FileUtils.mv specs['a-2'].cache_file, @tempdir
+ FileUtils.mv specs['b-2'].cache_file, @tempdir
+
+ @cmd.options[:domain] = :local
+
+ @cmd.options[:args] = %w[a b]
+
+ use_ui @ui do
+ orig_dir = Dir.pwd
+ begin
+ Dir.chdir @tempdir
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir orig_dir
+ end
+ end
+
+ assert_equal %w[a-2 b-2], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "2 gems installed", @ui.output
+ end
+
+ def test_execute_two_version
+ @cmd.options[:args] = %w[a b]
+ @cmd.options[:version] = Gem::Requirement.new("> 1")
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+
+ assert_equal 1, e.exit_code
+ end
+
+ assert_empty @cmd.installed_specs
+
+ msg = "ERROR: Can't use --version w/ multiple gems. Use name:ver instead."
+
+ assert_empty @ui.output
+ assert_equal msg, @ui.error.chomp
+ end
+
+ def test_execute_conservative
+ spec_fetcher do |fetcher|
+ fetcher.gem 'b', 2
+
+ fetcher.clear
+
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:conservative] = true
+
+ @cmd.options[:args] = %w[a b]
+
+ use_ui @ui do
+ orig_dir = Dir.pwd
+ begin
+ Dir.chdir @tempdir
+ assert_raises Gem::MockGemUi::SystemExitException do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir orig_dir
+ end
+ end
+
+ assert_equal %w[b-2], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_equal "", @ui.error
+ assert_match "1 gem installed", @ui.output
+ end
+
+ def test_install_gem_ignore_dependencies_both
+ done_installing = false
+ Gem.done_installing do
+ done_installing = true
+ end
+
+ spec = quick_spec 'a', 2
+
+ util_build_gem spec
+
+ FileUtils.mv spec.cache_file, @tempdir
+
+ @cmd.options[:ignore_dependencies] = true
+
+ @cmd.install_gem 'a', '>= 0'
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |s| s.full_name }
+
+ assert done_installing, 'documentation was not generated'
+ end
+
+ def test_install_gem_ignore_dependencies_remote
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:ignore_dependencies] = true
+
+ @cmd.install_gem 'a', '>= 0'
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |spec| spec.full_name }
+ end
+
+ def test_install_gem_ignore_dependencies_specific_file
+ spec = quick_spec 'a', 2
+
+ util_build_gem spec
+
+ FileUtils.mv spec.cache_file, @tempdir
+
+ @cmd.options[:ignore_dependencies] = true
+
+ @cmd.install_gem File.join(@tempdir, spec.file_name), nil
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |s| s.full_name }
+ end
+
+ def test_parses_requirement_from_gemname
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.gem 'b', 2
+ end
+
+ @cmd.options[:domain] = :local
+
+ req = "a:10.0"
+
+ @cmd.options[:args] = [req]
+
+ e = nil
+ use_ui @ui do
+ orig_dir = Dir.pwd
+ begin
+ Dir.chdir @tempdir
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir orig_dir
+ end
+ end
+
+ assert_equal 2, e.exit_code
+ assert_match %r!Could not find a valid gem 'a' \(= 10.0\)!, @ui.error
+ end
+
+ def test_show_errors_on_failure
+ Gem.sources.replace ["http://not-there.nothing"]
+
+ @cmd.options[:args] = ["blah"]
+
+ e = nil
+ use_ui @ui do
+ orig_dir = Dir.pwd
+ begin
+ Dir.chdir @tempdir
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ ensure
+ Dir.chdir orig_dir
+ end
+ end
+
+ assert_equal 2, e.exit_code
+
+ assert_match 'Unable to download data', @ui.error
+ end
+
+ def test_show_source_problems_even_on_success
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.clear
+ end
+
+ Gem.sources << "http://nonexistent.example"
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "1 gem installed", @ui.output
+
+ e = @ui.error
+
+ x = "WARNING: Unable to pull data from 'http://nonexistent.example': no data for http://nonexistent.example/specs.4.8.gz (http://nonexistent.example/specs.4.8.gz)\n"
+ assert_equal x, e
+ end
+
+ def test_execute_uses_from_a_gemdeps
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'a'"
+ end
+
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "Using a (2)", @ui.output
+ assert File.exist?("#{@gemdeps}.lock")
+ end
+
+ def test_execute_uses_from_a_gemdeps_with_no_lock
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'a'"
+ end
+
+ @cmd.handle_options %w[--no-lock]
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "Using a (2)", @ui.output
+ assert !File.exist?("#{@gemdeps}.lock")
+ end
+
+ def test_execute_installs_from_a_gemdeps_with_conservative
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.clear
+ fetcher.gem 'a', 1
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'a'"
+ end
+
+ @cmd.handle_options %w[--conservative]
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "Using a (1)", @ui.output
+ end
+
+ def test_execute_installs_from_a_gemdeps
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.clear
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'a'"
+ end
+
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[a-2], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "Installing a (2)", @ui.output
+ end
+
+ def test_execute_installs_deps_a_gemdeps
+ spec_fetcher do |fetcher|
+ fetcher.gem 'q', '1.0'
+ fetcher.gem 'r', '2.0', 'q' => nil
+ fetcher.clear
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'r'"
+ end
+
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ names = @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_equal %w[q-1.0 r-2.0], names
+
+ assert_match "Installing q (1.0)", @ui.output
+ assert_match "Installing r (2.0)", @ui.output
+ end
+
+ def test_execute_uses_deps_a_gemdeps
+ spec_fetcher do |fetcher|
+ fetcher.gem 'r', '2.0', 'q' => nil
+
+ fetcher.clear
+
+ fetcher.spec 'q', '1.0'
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'r'"
+ end
+
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ names = @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_equal %w[r-2.0], names
+
+ assert_match "Using q (1.0)", @ui.output
+ assert_match "Installing r (2.0)", @ui.output
+ end
+
+ def test_execute_installs_deps_a_gemdeps_into_a_path
+ spec_fetcher do |fetcher|
+ fetcher.gem 'q', '1.0'
+ fetcher.gem 'r', '2.0', 'q' => nil
+ fetcher.clear
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'r'"
+ end
+
+ @cmd.options[:install_dir] = "gf-path"
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ names = @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_equal %w[q-1.0 r-2.0], names
+
+ assert_match "Installing q (1.0)", @ui.output
+ assert_match "Installing r (2.0)", @ui.output
+
+ assert File.file?("gf-path/specifications/q-1.0.gemspec"), "not installed"
+ assert File.file?("gf-path/specifications/r-2.0.gemspec"), "not installed"
+ end
+
+ def test_execute_with_gemdeps_path_ignores_system
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'q', '1.0'
+ fetcher.gem 'r', '2.0', 'q' => nil
+ fetcher.clear
+ end
+
+ Gem::Specification.add_specs specs['q-1.0']
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'r'"
+ end
+
+ @cmd.options[:install_dir] = "gf-path"
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ names = @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_equal %w[q-1.0 r-2.0], names
+
+ assert_match "Installing q (1.0)", @ui.output
+ assert_match "Installing r (2.0)", @ui.output
+
+ assert File.file?("gf-path/specifications/q-1.0.gemspec"), "not installed"
+ assert File.file?("gf-path/specifications/r-2.0.gemspec"), "not installed"
+ end
+
+ def test_execute_uses_deps_a_gemdeps_with_a_path
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'q', '1.0'
+ fetcher.gem 'r', '2.0', 'q' => nil
+ end
+
+ i = Gem::Installer.new specs['q-1.0'].cache_file, :install_dir => "gf-path"
+ i.install
+
+ assert File.file?("gf-path/specifications/q-1.0.gemspec"), "not installed"
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'r'"
+ end
+
+ @cmd.options[:install_dir] = "gf-path"
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ names = @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_equal %w[r-2.0], names
+
+ assert_match "Using q (1.0)", @ui.output
+ assert_match "Installing r (2.0)", @ui.output
+ end
+
+ def test_handle_options_file
+ FileUtils.touch 'Gemfile'
+
+ @cmd.handle_options %w[-g Gemfile]
+
+ assert_equal 'Gemfile', @cmd.options[:gemdeps]
+
+ FileUtils.rm 'Gemfile'
+
+ FileUtils.touch 'gem.deps.rb'
+
+ @cmd.handle_options %w[--file gem.deps.rb]
+
+ assert_equal 'gem.deps.rb', @cmd.options[:gemdeps]
+
+ FileUtils.rm 'gem.deps.rb'
+
+ FileUtils.touch 'Isolate'
+
+ @cmd.handle_options %w[-g]
+
+ assert_equal 'Isolate', @cmd.options[:gemdeps]
+
+ FileUtils.touch 'Gemfile'
+
+ @cmd.handle_options %w[-g]
+
+ assert_equal 'Gemfile', @cmd.options[:gemdeps]
+
+ FileUtils.touch 'gem.deps.rb'
+
+ @cmd.handle_options %w[-g]
+
+ assert_equal 'gem.deps.rb', @cmd.options[:gemdeps]
+ end
+
+ def test_handle_options_suggest
+ assert @cmd.options[:suggest_alternate]
+
+ @cmd.handle_options %w[--no-suggestions]
+
+ refute @cmd.options[:suggest_alternate]
+
+ @cmd.handle_options %w[--suggestions]
+
+ assert @cmd.options[:suggest_alternate]
+ end
+
+ def test_handle_options_without
+ @cmd.handle_options %w[--without test]
+
+ assert_equal [:test], @cmd.options[:without_groups]
+
+ @cmd.handle_options %w[--without test,development]
+
+ assert_equal [:test, :development], @cmd.options[:without_groups]
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_list_command.rb b/jni/ruby/test/rubygems/test_gem_commands_list_command.rb
new file mode 100644
index 0000000..b03f166
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_list_command.rb
@@ -0,0 +1,33 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/list_command'
+
+class TestGemCommandsListCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::ListCommand.new
+
+ spec_fetcher do |fetcher|
+ fetcher.spec 'c', 1
+ end
+
+ @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do
+ raise Gem::RemoteFetcher::FetchError
+ end
+ end
+
+ def test_execute_installed
+ @cmd.handle_options %w[c --installed]
+
+ assert_raises Gem::MockGemUi::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "true\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_lock_command.rb b/jni/ruby/test/rubygems/test_gem_commands_lock_command.rb
new file mode 100644
index 0000000..8f30b7f
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_lock_command.rb
@@ -0,0 +1,68 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/lock_command'
+
+class TestGemCommandsLockCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @a1 = quick_gem 'a', '1'
+ @b1 = quick_gem 'b', '1' do |s|
+ s.add_runtime_dependency 'a'
+ end
+
+ @d1 = quick_gem 'd', '1' do |s|
+ s.add_runtime_dependency 'z'
+ end
+
+ @cmd = Gem::Commands::LockCommand.new
+ end
+
+ def test_execute
+ @cmd.handle_options %w[b-1]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EXPECTED
+require 'rubygems'
+gem 'b', '= 1'
+gem 'a', '= 1'
+ EXPECTED
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_missing_dependency
+ @cmd.handle_options %w[d-1]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EXPECTED
+require 'rubygems'
+gem 'd', '= 1'
+# Unable to satisfy 'z (>= 0)' from currently installed gems
+ EXPECTED
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_strict
+ @cmd.handle_options %w[c-1 --strict]
+
+ e = assert_raises Gem::Exception do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal 'Could not find gem c-1, try using the full name', e.message
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_mirror.rb b/jni/ruby/test/rubygems/test_gem_commands_mirror.rb
new file mode 100644
index 0000000..2f6fe52
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_mirror.rb
@@ -0,0 +1,32 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/mirror_command'
+
+class TestGemCommandsMirrorCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::MirrorCommand.new
+
+ @mirror_specs = Gem::Specification.find_all_by_name('rubygems-mirror').each do |spec|
+ Gem::Specification.remove_spec spec
+ end
+ end
+
+ def teardown
+ @mirror_specs.each do |spec|
+ Gem::Specification.add_spec spec
+ end
+
+ super
+ end
+
+ def test_execute
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r%Install the rubygems-mirror%i, @ui.error
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_open_command.rb b/jni/ruby/test/rubygems/test_gem_commands_open_command.rb
new file mode 100644
index 0000000..25f22c8
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_open_command.rb
@@ -0,0 +1,46 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/open_command'
+
+class TestGemCommandsOpenCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::OpenCommand.new
+ end
+
+ def gem name
+ spec = quick_gem name do |gem|
+ gem.files = %W[lib/#{name}.rb Rakefile]
+ end
+ write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
+ write_file File.join(*%W[gems #{spec.full_name} Rakefile])
+ end
+
+ def test_execute
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:editor] = "#{Gem.ruby} -e0 --"
+
+ gem 'foo'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_bad_gem
+ @cmd.options[:args] = %w[foo]
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_match %r|Unable to find gem 'foo'|, @ui.output
+ assert_equal "", @ui.error
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_outdated_command.rb b/jni/ruby/test/rubygems/test_gem_commands_outdated_command.rb
new file mode 100644
index 0000000..d369c6b
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_outdated_command.rb
@@ -0,0 +1,33 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/outdated_command'
+
+class TestGemCommandsOutdatedCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::OutdatedCommand.new
+ end
+
+ def test_initialize
+ assert @cmd.handles?(%W[--platform #{Gem::Platform.local}])
+ end
+
+ def test_execute
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', '1.0'
+ fetcher.spec 'foo', '2.0'
+ fetcher.clear
+ fetcher.gem 'foo', '0.1'
+ fetcher.gem 'foo', '0.2'
+ end
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "foo (0.2 < 2.0)\n", @ui.output
+ assert_equal "", @ui.error
+ end
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_owner_command.rb b/jni/ruby/test/rubygems/test_gem_commands_owner_command.rb
new file mode 100644
index 0000000..5d7b661
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_owner_command.rb
@@ -0,0 +1,204 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/owner_command'
+
+class TestGemCommandsOwnerCommand < Gem::TestCase
+
+ def setup
+ super
+
+ ENV["RUBYGEMS_HOST"] = nil
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
+ Gem.configuration.rubygems_api_key = "ed244fbf2b1a52e012da8616c512fa47f9aa5250"
+
+ @cmd = Gem::Commands::OwnerCommand.new
+ end
+
+ def test_show_owners
+ response = <<EOF
+---
+- email: user1@example.com
+- email: user2@example.com
+EOF
+
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+
+ use_ui @ui do
+ @cmd.show_owners("freewill")
+ end
+
+ assert_equal Net::HTTP::Get, @fetcher.last_request.class
+ assert_equal Gem.configuration.rubygems_api_key, @fetcher.last_request["Authorization"]
+
+ assert_match %r{Owners for gem: freewill}, @ui.output
+ assert_match %r{- user1@example.com}, @ui.output
+ assert_match %r{- user2@example.com}, @ui.output
+ end
+
+ def test_show_owners_setting_up_host_through_env_var
+ response = "- email: user1@example.com\n"
+ host = "http://rubygems.example"
+ ENV["RUBYGEMS_HOST"] = host
+
+ @fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+
+ use_ui @ui do
+ @cmd.show_owners("freewill")
+ end
+
+ assert_match %r{Owners for gem: freewill}, @ui.output
+ assert_match %r{- user1@example.com}, @ui.output
+ end
+
+ def test_show_owners_setting_up_host
+ response = "- email: user1@example.com\n"
+ host = "http://rubygems.example"
+ @cmd.host = host
+
+ @fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+
+ use_ui @ui do
+ @cmd.show_owners("freewill")
+ end
+
+ assert_match %r{Owners for gem: freewill}, @ui.output
+ assert_match %r{- user1@example.com}, @ui.output
+ end
+
+ def test_show_owners_denied
+ response = "You don't have permission to push to this gem"
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 403, 'Forbidden']
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.show_owners("freewill")
+ end
+ end
+
+ assert_match response, @ui.output
+ end
+
+ def test_show_owners_key
+ response = "- email: user1@example.com\n"
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
+ File.open Gem.configuration.credentials_path, 'a' do |f|
+ f.write ':other: 701229f217cdf23b1344c7b4b54ca97'
+ end
+ Gem.configuration.load_api_keys
+
+ @cmd.handle_options %w(-k other)
+ @cmd.show_owners('freewill')
+
+ assert_equal '701229f217cdf23b1344c7b4b54ca97', @fetcher.last_request['Authorization']
+ end
+
+ def test_add_owners
+ response = "Owner added successfully."
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
+
+ use_ui @ui do
+ @cmd.add_owners("freewill", ["user-new1@example.com"])
+ end
+
+ assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem.configuration.rubygems_api_key, @fetcher.last_request["Authorization"]
+ assert_equal "email=user-new1%40example.com", @fetcher.last_request.body
+
+ assert_match response, @ui.output
+ end
+
+ def test_add_owners_denied
+ response = "You don't have permission to push to this gem"
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 403, 'Forbidden']
+
+ use_ui @ui do
+ @cmd.add_owners("freewill", ["user-new1@example.com"])
+ end
+
+ assert_match response, @ui.output
+ end
+
+ def test_add_owner_with_host_option_through_execute
+ host = "http://rubygems.example"
+ add_owner_response = "Owner added successfully."
+ show_owners_response = "- email: user1@example.com\n"
+ @fetcher.data["#{host}/api/v1/gems/freewill/owners"] = [add_owner_response, 200, 'OK']
+ @fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [show_owners_response, 200, 'OK']
+
+ @cmd.handle_options %W[--host #{host} --add user-new1@example.com freewill]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match add_owner_response, @ui.output
+ assert_match %r{Owners for gem: freewill}, @ui.output
+ assert_match %r{- user1@example.com}, @ui.output
+ end
+
+ def test_add_owners_key
+ response = "Owner added successfully."
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
+ File.open Gem.configuration.credentials_path, 'a' do |f|
+ f.write ':other: 701229f217cdf23b1344c7b4b54ca97'
+ end
+ Gem.configuration.load_api_keys
+
+ @cmd.handle_options %w(-k other)
+ @cmd.add_owners('freewill', ['user-new1@example.com'])
+
+ assert_equal '701229f217cdf23b1344c7b4b54ca97', @fetcher.last_request['Authorization']
+ end
+
+ def test_remove_owners
+ response = "Owner removed successfully."
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
+
+ use_ui @ui do
+ @cmd.remove_owners("freewill", ["user-remove1@example.com"])
+ end
+
+ assert_equal Net::HTTP::Delete, @fetcher.last_request.class
+ assert_equal Gem.configuration.rubygems_api_key, @fetcher.last_request["Authorization"]
+ assert_equal "email=user-remove1%40example.com", @fetcher.last_request.body
+
+ assert_match response, @ui.output
+ end
+
+ def test_remove_owners_denied
+ response = "You don't have permission to push to this gem"
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 403, 'Forbidden']
+
+ use_ui @ui do
+ @cmd.remove_owners("freewill", ["user-remove1@example.com"])
+ end
+
+ assert_match response, @ui.output
+ end
+
+ def test_remove_owners_key
+ response = "Owner removed successfully."
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
+ File.open Gem.configuration.credentials_path, 'a' do |f|
+ f.write ':other: 701229f217cdf23b1344c7b4b54ca97'
+ end
+ Gem.configuration.load_api_keys
+
+ @cmd.handle_options %w(-k other)
+ @cmd.remove_owners('freewill', ['user-remove1@example.com'])
+
+ assert_equal '701229f217cdf23b1344c7b4b54ca97', @fetcher.last_request['Authorization']
+ end
+
+ def test_remove_owners_missing
+ response = 'Owner could not be found.'
+ @fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 404, 'Not Found']
+
+ use_ui @ui do
+ @cmd.remove_owners("freewill", ["missing@example"])
+ end
+
+ assert_equal "Removing missing@example: #{response}\n", @ui.output
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_pristine_command.rb b/jni/ruby/test/rubygems/test_gem_commands_pristine_command.rb
new file mode 100644
index 0000000..554d503
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_pristine_command.rb
@@ -0,0 +1,462 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/pristine_command'
+
+class TestGemCommandsPristineCommand < Gem::TestCase
+
+ def setup
+ super
+ @cmd = Gem::Commands::PristineCommand.new
+ end
+
+ def test_execute
+ a = util_spec 'a' do |s|
+ s.executables = %w[foo]
+ s.files = %w[bin/foo lib/a.rb]
+ end
+
+ write_file File.join(@tempdir, 'lib', 'a.rb') do |fp|
+ fp.puts "puts __FILE__"
+ end
+ write_file File.join(@tempdir, 'bin', 'foo') do |fp|
+ fp.puts "#!/usr/bin/ruby"
+ end
+
+ install_gem a
+
+ foo_path = File.join @gemhome, 'gems', a.full_name, 'bin', 'foo'
+ a_rb_path = File.join @gemhome, 'gems', a.full_name, 'lib', 'a.rb'
+
+ write_file foo_path do |io|
+ io.puts 'I changed it!'
+ end
+
+ write_file a_rb_path do |io|
+ io.puts 'I changed it!'
+ end
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#!/usr/bin/ruby\n", File.read(foo_path), foo_path
+ assert_equal "puts __FILE__\n", File.read(a_rb_path), a_rb_path
+
+ out = @ui.output.split "\n"
+
+ assert_equal "Restoring gems to pristine condition...", out.shift
+ assert_equal "Restored #{a.full_name}", out.shift
+ assert_empty out, out.inspect
+ end
+
+ def test_execute_all
+ a = util_spec 'a' do |s| s.executables = %w[foo] end
+ write_file File.join(@tempdir, 'bin', 'foo') do |fp|
+ fp.puts "#!/usr/bin/ruby"
+ end
+
+ install_gem a
+
+ gem_bin = File.join @gemhome, 'gems', a.full_name, 'bin', 'foo'
+ gem_stub = File.join @gemhome, 'bin', 'foo'
+
+ FileUtils.rm gem_bin
+ FileUtils.rm gem_stub
+
+ @cmd.handle_options %w[--all]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert File.exist?(gem_bin)
+ assert File.exist?(gem_stub)
+
+ out = @ui.output.split "\n"
+
+ assert_equal "Restoring gems to pristine condition...", out.shift
+ assert_equal "Restored #{a.full_name}", out.shift
+ assert_empty out, out.inspect
+ end
+
+ def test_execute_env_shebang
+ a = util_spec 'a' do |s|
+ s.executables = %w[foo]
+ s.files = %w[bin/foo]
+ end
+ write_file File.join(@tempdir, 'bin', 'foo') do |fp|
+ fp.puts "#!/usr/bin/ruby"
+ end
+
+ install_gem a
+
+ gem_exec = File.join @gemhome, 'bin', 'foo'
+
+ FileUtils.rm gem_exec
+
+ @cmd.handle_options %w[--all --env-shebang]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_path_exists gem_exec
+
+ if win_platform?
+ assert_match %r%\A#!\s*ruby%, File.read(gem_exec)
+ else
+ assert_match %r%\A#!\s*/usr/bin/env ruby%, File.read(gem_exec)
+ end
+ end
+
+ def test_execute_extensions_explicit
+ a = util_spec 'a' do |s| s.extensions << 'ext/a/extconf.rb' end
+
+ ext_path = File.join @tempdir, 'ext', 'a', 'extconf.rb'
+ write_file ext_path do |io|
+ io.write <<-'RUBY'
+ File.open "Makefile", "w" do |f|
+ f.puts "clean:\n\techo cleaned\n"
+ f.puts "all:\n\techo built\n"
+ f.puts "install:\n\techo installed\n"
+ end
+ RUBY
+ end
+
+ b = util_spec 'b'
+
+ install_gem a
+ install_gem b
+
+ @cmd.options[:extensions] = true
+ @cmd.options[:extensions_set] = true
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ assert_equal 'Restoring gems to pristine condition...', out.shift
+ assert_equal 'Building native extensions. This could take a while...',
+ out.shift
+ assert_equal "Restored #{a.full_name}", out.shift
+ assert_empty out, out.inspect
+ end
+
+ def test_execute_no_extension
+ a = util_spec 'a' do |s| s.extensions << 'ext/a/extconf.rb' end
+
+ ext_path = File.join @tempdir, 'ext', 'a', 'extconf.rb'
+ write_file ext_path do |io|
+ io.write '# extconf.rb'
+ end
+
+ util_build_gem a
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:extensions] = false
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ assert_equal 'Restoring gems to pristine condition...', out.shift
+ assert_equal "Skipped #{a.full_name}, it needs to compile an extension",
+ out.shift
+ assert_empty out, out.inspect
+ end
+
+ def test_execute_with_extension_with_build_args
+ a = util_spec 'a' do |s| s.extensions << 'ext/a/extconf.rb' end
+
+ ext_path = File.join @tempdir, 'ext', 'a', 'extconf.rb'
+ write_file ext_path do |io|
+ io.write <<-'RUBY'
+ File.open "Makefile", "w" do |f|
+ f.puts "clean:\n\techo cleaned\n"
+ f.puts "all:\n\techo built\n"
+ f.puts "install:\n\techo installed\n"
+ end
+ RUBY
+ end
+
+ build_args = %w!--with-awesome=true --sweet!
+
+ install_gem a, :build_args => build_args
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ assert_equal 'Restoring gems to pristine condition...', out.shift
+ assert_equal "Building native extensions with: '--with-awesome=true --sweet'", out.shift
+ assert_equal "This could take a while...", out.shift
+ assert_equal "Restored #{a.full_name}", out.shift
+ assert_empty out, out.inspect
+ end
+
+ def test_execute_many
+ a = util_spec 'a'
+ b = util_spec 'b'
+
+ install_gem a
+ install_gem b
+
+ @cmd.options[:args] = %w[a b]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ assert_equal "Restoring gems to pristine condition...", out.shift
+ assert_equal "Restored #{a.full_name}", out.shift
+ assert_equal "Restored #{b.full_name}", out.shift
+ assert_empty out, out.inspect
+ end
+
+ def test_execute_many_multi_repo
+ a = util_spec 'a'
+ install_gem a
+
+ Gem.clear_paths
+ gemhome2 = File.join @tempdir, 'gemhome2'
+ Gem.paths = { "GEM_PATH" => [gemhome2, @gemhome], "GEM_HOME" => gemhome2 }
+
+ b = util_spec 'b'
+ install_gem b
+
+ @cmd.options[:args] = %w[a b]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ assert_equal "Restoring gems to pristine condition...", out.shift
+ assert_equal "Restored #{a.full_name}", out.shift
+ assert_equal "Restored #{b.full_name}", out.shift
+ assert_empty out, out.inspect
+
+ assert_path_exists File.join(@gemhome, "gems", 'a-2')
+ refute_path_exists File.join(gemhome2, "gems", 'a-2')
+ assert_path_exists File.join(gemhome2, "gems", 'b-2')
+ refute_path_exists File.join(@gemhome, "gems", 'b-2')
+ end
+
+ def test_execute_missing_cache_gem
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ fetcher.gem 'a', 3
+ fetcher.gem 'a', '3.a'
+ end
+
+ FileUtils.rm specs['a-2'].cache_file
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ [
+ "Restoring gems to pristine condition...",
+ "Restored a-1",
+ "Cached gem for a-2 not found, attempting to fetch...",
+ "Restored a-2",
+ "Restored a-3.a",
+ "Restored a-3",
+ ].each do |line|
+ assert_equal line, out.shift
+ end
+
+ assert_empty out, out.inspect
+ end
+
+ def test_execute_missing_cache_gem_when_multi_repo
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'b', 1
+ end
+
+ FileUtils.rm_rf File.join(@gemhome, 'gems', 'a-1')
+ FileUtils.rm_rf File.join(@gemhome, 'gems', 'b-1')
+
+ install_gem specs["a-1"]
+ FileUtils.rm File.join(@gemhome, 'cache', 'a-1.gem')
+
+ Gem.clear_paths
+ gemhome2 = File.join(@tempdir, 'gemhome2')
+ Gem.paths = { "GEM_PATH" => [gemhome2, @gemhome], "GEM_HOME" => gemhome2 }
+
+ install_gem specs["b-1"]
+ FileUtils.rm File.join(gemhome2, 'cache', 'b-1.gem')
+
+ @cmd.options[:args] = %w[a b]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+
+ [
+ "Restoring gems to pristine condition...",
+ "Cached gem for a-1 not found, attempting to fetch...",
+ "Restored a-1",
+ "Cached gem for b-1 not found, attempting to fetch...",
+ "Restored b-1",
+ ].each do |line|
+ assert_equal line, out.shift
+ end
+
+ assert_empty out, out.inspect
+ assert_empty @ui.error
+
+ assert_path_exists File.join(@gemhome, "cache", 'a-1.gem')
+ refute_path_exists File.join(gemhome2, "cache", 'a-2.gem')
+ assert_path_exists File.join(@gemhome, "gems", 'a-1')
+ refute_path_exists File.join(gemhome2, "gems", 'a-1')
+
+ assert_path_exists File.join(gemhome2, "cache", 'b-1.gem')
+ refute_path_exists File.join(@gemhome, "cache", 'b-2.gem')
+ assert_path_exists File.join(gemhome2, "gems", 'b-1')
+ refute_path_exists File.join(@gemhome, "gems", 'b-1')
+ end
+
+ def test_execute_no_gem
+ @cmd.options[:args] = %w[]
+
+ e = assert_raises Gem::CommandLineError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_match %r|at least one gem name|, e.message
+ end
+
+ def test_execute_only_executables
+ a = util_spec 'a' do |s|
+ s.executables = %w[foo]
+ s.files = %w[bin/foo lib/a.rb]
+ end
+ write_file File.join(@tempdir, 'lib', 'a.rb') do |fp|
+ fp.puts "puts __FILE__"
+ end
+ write_file File.join(@tempdir, 'bin', 'foo') do |fp|
+ fp.puts "#!/usr/bin/ruby"
+ end
+
+ install_gem a
+
+ gem_lib = File.join @gemhome, 'gems', a.full_name, 'lib', 'a.rb'
+ gem_exec = File.join @gemhome, 'bin', 'foo'
+
+ FileUtils.rm gem_exec
+ FileUtils.rm gem_lib
+
+ @cmd.handle_options %w[--all --only-executables]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert File.exist? gem_exec
+ refute File.exist? gem_lib
+ end
+
+ def test_execute_unknown_gem_at_remote_source
+ util_spec 'a'
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal([
+ "Restoring gems to pristine condition...",
+ "Cached gem for a-2 not found, attempting to fetch...",
+ "Skipped a-2, it was not found from cache and remote sources"
+ ], @ui.output.split("\n"))
+
+ assert_empty @ui.error
+ end
+
+ def test_execute_default_gem
+ default_gem_spec = new_default_spec("default", "2.0.0.0",
+ nil, "default/gem.rb")
+ install_default_specs(default_gem_spec)
+
+ @cmd.options[:args] = %w[default]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal([
+ "Restoring gems to pristine condition...",
+ "Skipped default-2.0.0.0, it is a default gem",
+ ],
+ @ui.output.split("\n"))
+ assert_empty(@ui.error)
+ end
+
+ def test_execute_bundled_gem_on_old_rubies
+ util_set_RUBY_VERSION '1.9.3', 551
+
+ util_spec 'bigdecimal', '1.1.0' do |s|
+ s.summary = "This bigdecimal is bundled with Ruby"
+ end
+
+ @cmd.options[:args] = %w[bigdecimal]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal([
+ "Restoring gems to pristine condition...",
+ "Skipped bigdecimal-1.1.0, it is bundled with old Ruby"
+ ], @ui.output.split("\n"))
+
+ assert_empty @ui.error
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
+ def test_handle_options
+ @cmd.handle_options %w[]
+
+ refute @cmd.options[:all]
+
+ assert @cmd.options[:extensions]
+ refute @cmd.options[:extensions_set]
+
+ assert_equal Gem::Requirement.default, @cmd.options[:version]
+ end
+
+ def test_handle_options_extensions
+ @cmd.handle_options %w[--extensions]
+
+ assert @cmd.options[:extensions]
+ assert @cmd.options[:extensions_set]
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_push_command.rb b/jni/ruby/test/rubygems/test_gem_commands_push_command.rb
new file mode 100644
index 0000000..7d3d2ef
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_push_command.rb
@@ -0,0 +1,262 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/push_command'
+
+class TestGemCommandsPushCommand < Gem::TestCase
+
+ def setup
+ super
+ ENV["RUBYGEMS_HOST"] = nil
+ Gem.host = Gem::DEFAULT_HOST
+ Gem.configuration.disable_default_gem_server = false
+
+ @gems_dir = File.join @tempdir, 'gems'
+ @cache_dir = File.join @gemhome, "cache"
+
+ FileUtils.mkdir @gems_dir
+
+ Gem.configuration.rubygems_api_key =
+ "ed244fbf2b1a52e012da8616c512fa47f9aa5250"
+
+ @spec, @path = util_gem "freewill", "1.0.0"
+ @host = 'https://rubygems.example'
+ @api_key = Gem.configuration.rubygems_api_key
+
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
+
+ @cmd = Gem::Commands::PushCommand.new
+
+ class << Gem
+ alias_method :orig_latest_rubygems_version, :latest_rubygems_version
+
+ def latest_rubygems_version
+ Gem.rubygems_version
+ end
+ end
+ end
+
+ def teardown
+ super
+
+ class << Gem
+ remove_method :latest_rubygems_version
+ alias_method :latest_rubygems_version, :orig_latest_rubygems_version
+ end
+ end
+
+ def send_battery
+ use_ui @ui do
+ @cmd.instance_variable_set :@host, @host
+ @cmd.send_gem(@path)
+ end
+
+ assert_match %r{Pushing gem to #{@host}...}, @ui.output
+
+ assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem.read_binary(@path), @fetcher.last_request.body
+ assert_equal File.size(@path), @fetcher.last_request["Content-Length"].to_i
+ assert_equal "application/octet-stream", @fetcher.last_request["Content-Type"]
+ assert_equal @api_key, @fetcher.last_request["Authorization"]
+
+ assert_match @response, @ui.output
+ end
+
+ def test_execute
+ @response = "Successfully registered gem: freewill (1.0.0)"
+ @fetcher.data["#{Gem.host}/api/v1/gems"] = [@response, 200, 'OK']
+
+ @cmd.options[:args] = [@path]
+
+ @cmd.execute
+
+ assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem.read_binary(@path), @fetcher.last_request.body
+ assert_equal "application/octet-stream",
+ @fetcher.last_request["Content-Type"]
+ end
+
+ def test_execute_host
+ host = 'https://other.example'
+
+ @response = "Successfully registered gem: freewill (1.0.0)"
+ @fetcher.data["#{host}/api/v1/gems"] = [@response, 200, 'OK']
+ @fetcher.data["#{Gem.host}/api/v1/gems"] =
+ ['fail', 500, 'Internal Server Error']
+
+ @cmd.options[:host] = host
+ @cmd.options[:args] = [@path]
+
+ @cmd.execute
+
+ assert_equal Net::HTTP::Post, @fetcher.last_request.class
+ assert_equal Gem.read_binary(@path), @fetcher.last_request.body
+ assert_equal "application/octet-stream",
+ @fetcher.last_request["Content-Type"]
+ end
+
+ def test_sending_when_default_host_disabled
+ Gem.configuration.disable_default_gem_server = true
+ response = "You must specify a gem server"
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.send_gem(@path)
+ end
+ end
+
+ assert_match response, @ui.error
+ end
+
+ def test_sending_when_default_host_disabled_with_override
+ ENV["RUBYGEMS_HOST"] = @host
+ Gem.configuration.disable_default_gem_server = true
+ @response = "Successfully registered gem: freewill (1.0.0)"
+ @fetcher.data["#{@host}/api/v1/gems"] = [@response, 200, 'OK']
+
+ send_battery
+ end
+
+ def test_sending_gem_to_metadata_host
+ @host = "http://rubygems.engineyard.com"
+
+ @spec, @path = util_gem "freebird", "1.0.1" do |spec|
+ spec.metadata['default_gem_server'] = @host
+ end
+
+ @api_key = "EYKEY"
+
+ keys = {
+ :rubygems_api_key => 'KEY',
+ @host => @api_key
+ }
+
+ FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
+ open Gem.configuration.credentials_path, 'w' do |f|
+ f.write keys.to_yaml
+ end
+ Gem.configuration.load_api_keys
+
+ FileUtils.rm Gem.configuration.credentials_path
+
+ @response = "Successfully registered gem: freebird (1.0.1)"
+ @fetcher.data["#{@host}/api/v1/gems"] = [@response, 200, 'OK']
+ send_battery
+ end
+
+ def test_sending_gem
+ @response = "Successfully registered gem: freewill (1.0.0)"
+ @fetcher.data["#{@host}/api/v1/gems"] = [@response, 200, 'OK']
+
+ send_battery
+ end
+
+ def test_sending_gem_to_allowed_push_host
+ @host = "http://privategemserver.com"
+
+ @spec, @path = util_gem "freebird", "1.0.1" do |spec|
+ spec.metadata['allowed_push_host'] = @host
+ end
+
+ @api_key = "PRIVKEY"
+
+ keys = {
+ :rubygems_api_key => 'KEY',
+ @host => @api_key
+ }
+
+ FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
+ open Gem.configuration.credentials_path, 'w' do |f|
+ f.write keys.to_yaml
+ end
+ Gem.configuration.load_api_keys
+
+ FileUtils.rm Gem.configuration.credentials_path
+
+ @response = "Successfully registered gem: freebird (1.0.1)"
+ @fetcher.data["#{@host}/api/v1/gems"] = [@response, 200, 'OK']
+ send_battery
+ end
+
+ def test_sending_gem_to_disallowed_default_host
+ @spec, @path = util_gem "freebird", "1.0.1" do |spec|
+ spec.metadata['allowed_push_host'] = "https://privategemserver.com"
+ end
+
+ response = %{ERROR: "#{@host}" is not allowed by the gemspec, which only allows "https://privategemserver.com"}
+
+ assert_raises Gem::MockGemUi::TermError do
+ send_battery
+ end
+
+ assert_match response, @ui.error
+ end
+
+ def test_sending_gem_to_disallowed_push_host
+ @host = "https://somebodyelse.com"
+
+ @spec, @path = util_gem "freebird", "1.0.1" do |spec|
+ spec.metadata['allowed_push_host'] = "https://privategemserver.com"
+ end
+
+ @api_key = "PRIVKEY"
+
+ keys = {
+ :rubygems_api_key => 'KEY',
+ @host => @api_key
+ }
+
+ FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
+ open Gem.configuration.credentials_path, 'w' do |f|
+ f.write keys.to_yaml
+ end
+ Gem.configuration.load_api_keys
+
+ FileUtils.rm Gem.configuration.credentials_path
+
+ response = 'ERROR: "https://somebodyelse.com" is not allowed by the gemspec, which only allows "https://privategemserver.com"'
+
+ assert_raises Gem::MockGemUi::TermError do
+ send_battery
+ end
+
+ assert_match response, @ui.error
+ end
+
+ def test_raises_error_with_no_arguments
+ def @cmd.sign_in(*); end
+ assert_raises Gem::CommandLineError do
+ @cmd.execute
+ end
+ end
+
+ def test_sending_gem_denied
+ response = "You don't have permission to push to this gem"
+ @fetcher.data["#{@host}/api/v1/gems"] = [response, 403, 'Forbidden']
+ @cmd.instance_variable_set :@host, @host
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.send_gem(@path)
+ end
+ end
+
+ assert_match response, @ui.output
+ end
+
+ def test_sending_gem_key
+ @response = "Successfully registered gem: freewill (1.0.0)"
+ @fetcher.data["#{@host}/api/v1/gems"] = [@response, 200, "OK"]
+ File.open Gem.configuration.credentials_path, 'a' do |f|
+ f.write ':other: 701229f217cdf23b1344c7b4b54ca97'
+ end
+ Gem.configuration.load_api_keys
+
+ @cmd.handle_options %w(-k other)
+ @cmd.instance_variable_set :@host, @host
+ @cmd.send_gem(@path)
+
+ assert_equal Gem.configuration.api_keys[:other],
+ @fetcher.last_request["Authorization"]
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_query_command.rb b/jni/ruby/test/rubygems/test_gem_commands_query_command.rb
new file mode 100644
index 0000000..43fa825
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_query_command.rb
@@ -0,0 +1,668 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/query_command'
+
+class TestGemCommandsQueryCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::QueryCommand.new
+
+ @specs = spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', '3.a'
+ end
+
+ @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do
+ raise Gem::RemoteFetcher::FetchError
+ end
+ end
+
+ def test_execute
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (2)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_platform
+ spec_fetcher do |fetcher|
+ fetcher.clear
+
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 1 do |s|
+ s.platform = 'x86-linux'
+ end
+
+ fetcher.spec 'a', 2 do |s|
+ s.platform = 'universal-darwin'
+ end
+ end
+
+ @cmd.handle_options %w[-r -a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (2 universal-darwin, 1 ruby x86-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_all
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r --all]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (2, 1)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_all_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r --all --prerelease]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (3.a, 2, 1)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_details
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.summary = 'This is a lot of text. ' * 4
+ s.authors = ['Abraham Lincoln', 'Hirohito']
+ s.homepage = 'http://a.example.com/'
+ end
+
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r -d]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (2)
+ Authors: Abraham Lincoln, Hirohito
+ Homepage: http://a.example.com/
+
+ This is a lot of text. This is a lot of text. This is a lot of text.
+ This is a lot of text.
+
+pl (1)
+ Platform: i386-linux
+ Author: A User
+ Homepage: http://example.com
+
+ this is a summary
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_details_platform
+ spec_fetcher do |fetcher|
+ fetcher.clear
+
+ fetcher.spec 'a', 1 do |s|
+ s.platform = 'x86-linux'
+ end
+
+ fetcher.spec 'a', 2 do |s|
+ s.summary = 'This is a lot of text. ' * 4
+ s.authors = ['Abraham Lincoln', 'Hirohito']
+ s.homepage = 'http://a.example.com/'
+ s.platform = 'universal-darwin'
+ end
+
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r -d]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (2, 1)
+ Platforms:
+ 1: x86-linux
+ 2: universal-darwin
+ Authors: Abraham Lincoln, Hirohito
+ Homepage: http://a.example.com/
+
+ This is a lot of text. This is a lot of text. This is a lot of text.
+ This is a lot of text.
+
+pl (1)
+ Platform: i386-linux
+ Author: A User
+ Homepage: http://example.com
+
+ this is a summary
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_installed
+ @cmd.handle_options %w[-n a --installed]
+
+ assert_raises Gem::MockGemUi::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "true\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_installed_inverse
+ @cmd.handle_options %w[-n a --no-installed]
+
+ e = assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "false\n", @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal 1, e.exit_code
+ end
+
+ def test_execute_installed_inverse_not_installed
+ @cmd.handle_options %w[-n not_installed --no-installed]
+
+ assert_raises Gem::MockGemUi::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "true\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_installed_no_name
+ @cmd.handle_options %w[--installed]
+
+ e = assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_equal "ERROR: You must specify a gem name\n", @ui.error
+
+ assert_equal 4, e.exit_code
+ end
+
+ def test_execute_installed_not_installed
+ @cmd.handle_options %w[-n not_installed --installed]
+
+ e = assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "false\n", @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal 1, e.exit_code
+ end
+
+ def test_execute_installed_version
+ @cmd.handle_options %w[-n a --installed --version 2]
+
+ assert_raises Gem::MockGemUi::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "true\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_installed_version_not_installed
+ @cmd.handle_options %w[-n c --installed --version 2]
+
+ e = assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "false\n", @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal 1, e.exit_code
+ end
+
+ def test_execute_local
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.options[:domain] = :local
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** LOCAL GEMS ***
+
+a (3.a, 2, 1)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_local_notty
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[]
+
+ @ui.outs.tty = false
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+a (3.a, 2, 1)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_local_quiet
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.options[:domain] = :local
+ Gem.configuration.verbose = false
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+a (3.a, 2, 1)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_no_versions
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r --no-versions]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a
+pl
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_notty
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-r]
+
+ @ui.outs.tty = false
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+a (2)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_prerelease
+ @cmd.handle_options %w[-r --prerelease]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (3.a)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_prerelease_local
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-l --prerelease]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** LOCAL GEMS ***
+
+a (3.a, 2, 1)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal "WARNING: prereleases are always shown locally\n", @ui.error
+ end
+
+ def test_execute_remote
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.options[:domain] = :remote
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** REMOTE GEMS ***
+
+a (2)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_remote_notty
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[]
+
+ @ui.outs.tty = false
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+a (3.a, 2, 1)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_remote_quiet
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.options[:domain] = :remote
+ Gem.configuration.verbose = false
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+a (2)
+pl (1 i386-linux)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_local_details
+ spec_fetcher do |fetcher|
+ fetcher.clear
+
+ fetcher.spec 'a', 1 do |s|
+ s.platform = 'x86-linux'
+ end
+
+ fetcher.spec 'a', 2 do |s|
+ s.summary = 'This is a lot of text. ' * 4
+ s.authors = ['Abraham Lincoln', 'Hirohito']
+ s.homepage = 'http://a.example.com/'
+ s.platform = 'universal-darwin'
+ end
+
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[-l -d]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ str = @ui.output
+
+ str.gsub!(/\(\d\): [^\n]*/, "-")
+ str.gsub!(/at: [^\n]*/, "at: -")
+
+ expected = <<-EOF
+
+*** LOCAL GEMS ***
+
+a (2, 1)
+ Platforms:
+ 1: x86-linux
+ 2: universal-darwin
+ Authors: Abraham Lincoln, Hirohito
+ Homepage: http://a.example.com/
+ Installed at -
+ -
+
+ This is a lot of text. This is a lot of text. This is a lot of text.
+ This is a lot of text.
+
+pl (1)
+ Platform: i386-linux
+ Author: A User
+ Homepage: http://example.com
+ Installed at: -
+
+ this is a summary
+ EOF
+
+ assert_equal expected, @ui.output
+ end
+
+ def test_execute_default_details
+ spec_fetcher do |fetcher|
+ fetcher.clear
+
+ fetcher.spec 'a', 2
+ end
+
+ a1 = new_default_spec 'a', 1
+ install_default_specs a1
+
+ @cmd.handle_options %w[-l -d]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+
+*** LOCAL GEMS ***
+
+a (2, 1)
+ Author: A User
+ Homepage: http://example.com
+ Installed at (2): #{@gemhome}
+ (1, default): #{a1.base_dir}
+
+ this is a summary
+ EOF
+
+ assert_equal expected, @ui.output
+ end
+
+ def test_make_entry
+ a_2_name = @specs['a-2'].original_name
+
+ @fetcher.data.delete \
+ "#{@gem_repo}quick/Marshal.#{Gem.marshal_version}/#{a_2_name}.gemspec.rz"
+
+ a2 = @specs['a-2']
+ entry_tuples = [
+ [Gem::NameTuple.new(a2.name, a2.version, a2.platform),
+ Gem.sources.first],
+ ]
+
+ platforms = { a2.version => [a2.platform] }
+
+ entry = @cmd.send :make_entry, entry_tuples, platforms
+
+ assert_equal 'a (2)', entry
+ end
+
+ # Test for multiple args handling!
+ def test_execute_multiple_args
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ @cmd.handle_options %w[a pl]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r%^a %, @ui.output
+ assert_match %r%^pl %, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_show_gems
+ @cmd.options[:name] = //
+ @cmd.options[:domain] = :remote
+
+ use_ui @ui do
+ @cmd.send :show_gems, /a/i, false
+ end
+
+ assert_match %r%^a %, @ui.output
+ refute_match %r%^pl %, @ui.output
+ assert_empty @ui.error
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_search_command.rb b/jni/ruby/test/rubygems/test_gem_commands_search_command.rb
new file mode 100644
index 0000000..fb8debc
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_search_command.rb
@@ -0,0 +1,17 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/search_command'
+
+class TestGemCommandsSearchCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::SearchCommand.new
+ end
+
+ def test_initialize
+ assert_equal :remote, @cmd.defaults[:domain]
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_server_command.rb b/jni/ruby/test/rubygems/test_gem_commands_server_command.rb
new file mode 100644
index 0000000..db27a4c
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_server_command.rb
@@ -0,0 +1,59 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/server_command'
+
+class TestGemCommandsServerCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::ServerCommand.new
+ end
+
+ def test_handle_options
+ @cmd.send :handle_options, %w[-p 8808 --no-daemon]
+
+ assert_equal false, @cmd.options[:daemon]
+ assert_equal [], @cmd.options[:gemdir]
+ assert_equal 8808, @cmd.options[:port]
+
+ @cmd.send :handle_options, %w[-p 9999 -d /nonexistent --daemon]
+
+ assert_equal true, @cmd.options[:daemon]
+ assert_equal [File.expand_path('/nonexistent')], @cmd.options[:gemdir]
+ assert_equal 9999, @cmd.options[:port]
+ end
+
+ def test_handle_options_gemdir
+ @cmd.send :handle_options, %w[--dir a --dir b]
+
+ assert_equal [File.expand_path('a'), File.expand_path('b')],
+ @cmd.options[:gemdir]
+ end
+
+ def test_handle_options_port
+ @cmd.send :handle_options, %w[-p 0]
+ assert_equal 0, @cmd.options[:port]
+
+ @cmd.send :handle_options, %w[-p 65535]
+ assert_equal 65535, @cmd.options[:port]
+
+ @cmd.send :handle_options, %w[-p http]
+ assert_equal 80, @cmd.options[:port]
+
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.send :handle_options, %w[-p nonexistent]
+ end
+
+ assert_equal 'invalid argument: -p nonexistent: no such named service',
+ e.message
+
+ e = assert_raises OptionParser::InvalidArgument do
+ @cmd.send :handle_options, %w[-p 65536]
+ end
+
+ assert_equal 'invalid argument: -p 65536: not a port number',
+ e.message
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_setup_command.rb b/jni/ruby/test/rubygems/test_gem_commands_setup_command.rb
new file mode 100644
index 0000000..974c157
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_setup_command.rb
@@ -0,0 +1,136 @@
+# coding: UTF-8
+
+require 'rubygems/test_case'
+require 'rubygems/commands/setup_command'
+
+class TestGemCommandsSetupCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @install_dir = File.join @tempdir, 'install'
+ @cmd = Gem::Commands::SetupCommand.new
+ @cmd.options[:prefix] = @install_dir
+
+ FileUtils.mkdir_p 'bin'
+ FileUtils.mkdir_p 'lib/rubygems/ssl_certs'
+
+ open 'bin/gem', 'w' do |io| io.puts '# gem' end
+ open 'lib/rubygems.rb', 'w' do |io| io.puts '# rubygems.rb' end
+ open 'lib/rubygems/test_case.rb', 'w' do |io| io.puts '# test_case.rb' end
+ open 'lib/rubygems/ssl_certs/foo.pem', 'w' do |io| io.puts 'PEM' end
+ end
+
+ def test_pem_files_in
+ assert_equal %w[rubygems/ssl_certs/foo.pem],
+ @cmd.pem_files_in('lib').sort
+ end
+
+ def test_rb_files_in
+ assert_equal %w[rubygems.rb rubygems/test_case.rb],
+ @cmd.rb_files_in('lib').sort
+ end
+
+ def test_install_lib
+ @cmd.extend FileUtils
+
+ Dir.mktmpdir 'lib' do |dir|
+ @cmd.install_lib dir
+
+ assert_path_exists File.join(dir, 'rubygems.rb')
+ assert_path_exists File.join(dir, 'rubygems/ssl_certs/foo.pem')
+ end
+ end
+
+ def test_remove_old_lib_files
+ lib = File.join @install_dir, 'lib'
+ lib_rubygems = File.join lib, 'rubygems'
+ lib_rubygems_defaults = File.join lib_rubygems, 'defaults'
+
+ securerandom_rb = File.join lib, 'securerandom.rb'
+
+ engine_defaults_rb = File.join lib_rubygems_defaults, 'jruby.rb'
+ os_defaults_rb = File.join lib_rubygems_defaults, 'operating_system.rb'
+
+ old_builder_rb = File.join lib_rubygems, 'builder.rb'
+ old_format_rb = File.join lib_rubygems, 'format.rb'
+
+ FileUtils.mkdir_p lib_rubygems_defaults
+
+ open securerandom_rb, 'w' do |io| io.puts '# securerandom.rb' end
+
+ open old_builder_rb, 'w' do |io| io.puts '# builder.rb' end
+ open old_format_rb, 'w' do |io| io.puts '# format.rb' end
+
+ open engine_defaults_rb, 'w' do |io| io.puts '# jruby.rb' end
+ open os_defaults_rb, 'w' do |io| io.puts '# operating_system.rb' end
+
+ @cmd.remove_old_lib_files lib
+
+ refute_path_exists old_builder_rb
+ refute_path_exists old_format_rb
+
+ assert_path_exists securerandom_rb
+ assert_path_exists engine_defaults_rb
+ assert_path_exists os_defaults_rb
+ end
+
+ def test_show_release_notes
+ @default_external = nil
+ if Object.const_defined? :Encoding
+ @default_external = @ui.outs.external_encoding
+ @ui.outs.set_encoding Encoding::US_ASCII
+ end
+
+ @cmd.options[:previous_version] = Gem::Version.new '2.0.2'
+
+ open 'History.txt', 'w' do |io|
+ io.puts <<-History_txt
+# coding: UTF-8
+
+=== #{Gem::VERSION} / 2013-03-26
+
+* Bug fixes:
+ * Fixed release note display for LANG=C when installing rubygems
+ * π is tasty
+
+=== 2.0.2 / 2013-03-06
+
+* Bug fixes:
+ * Other bugs fixed
+
+=== 2.0.1 / 2013-03-05
+
+* Bug fixes:
+ * Yet more bugs fixed
+ History_txt
+ end
+
+ use_ui @ui do
+ @cmd.show_release_notes
+ end
+
+ expected = <<-EXPECTED
+=== #{Gem::VERSION} / 2013-03-26
+
+* Bug fixes:
+ * Fixed release note display for LANG=C when installing rubygems
+ * π is tasty
+
+=== 2.0.2 / 2013-03-06
+
+* Bug fixes:
+ * Other bugs fixed
+
+ EXPECTED
+
+ output = @ui.output
+ output.force_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
+
+ assert_equal expected, output
+ ensure
+ @ui.outs.set_encoding @default_external if @default_external
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_sources_command.rb b/jni/ruby/test/rubygems/test_gem_commands_sources_command.rb
new file mode 100644
index 0000000..8ee0fd3
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_sources_command.rb
@@ -0,0 +1,248 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/sources_command'
+
+class TestGemCommandsSourcesCommand < Gem::TestCase
+
+ def setup
+ super
+
+ spec_fetcher
+
+ @cmd = Gem::Commands::SourcesCommand.new
+
+ @new_repo = "http://beta-gems.example.com"
+ end
+
+ def test_initialize_proxy
+ assert @cmd.handles?(['--http-proxy', 'http://proxy.example.com'])
+ end
+
+ def test_execute
+ @cmd.handle_options []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+*** CURRENT SOURCES ***
+
+#{@gem_repo}
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_add
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ specs = Gem::Specification.map { |spec|
+ [spec.name, spec.version, spec.original_platform]
+ }
+
+ specs_dump_gz = StringIO.new
+ Zlib::GzipWriter.wrap specs_dump_gz do |io|
+ Marshal.dump specs, io
+ end
+
+ @fetcher.data["#{@new_repo}/specs.#{@marshal_version}.gz"] =
+ specs_dump_gz.string
+
+ @cmd.handle_options %W[--add #{@new_repo}]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal [@gem_repo, @new_repo], Gem.sources
+
+ expected = <<-EOF
+#{@new_repo} added to sources
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_add_nonexistent_source
+ uri = "http://beta-gems.example.com/specs.#{@marshal_version}.gz"
+ @fetcher.data[uri] = proc do
+ raise Gem::RemoteFetcher::FetchError.new('it died', uri)
+ end
+
+ @cmd.handle_options %w[--add http://beta-gems.example.com]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ expected = <<-EOF
+Error fetching http://beta-gems.example.com:
+\tit died (#{uri})
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_add_redundant_source
+ @cmd.handle_options %W[--add #{@gem_repo}]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal [@gem_repo], Gem.sources
+
+ expected = <<-EOF
+source #{@gem_repo} already present in the cache
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_add_http_rubygems_org
+ http_rubygems_org = 'http://rubygems.org'
+
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ specs = Gem::Specification.map { |spec|
+ [spec.name, spec.version, spec.original_platform]
+ }
+
+ specs_dump_gz = StringIO.new
+ Zlib::GzipWriter.wrap specs_dump_gz do |io|
+ Marshal.dump specs, io
+ end
+
+ @fetcher.data["#{http_rubygems_org}/specs.#{@marshal_version}.gz"] =
+ specs_dump_gz.string
+
+ @cmd.handle_options %W[--add #{http_rubygems_org}]
+
+ ui = Gem::MockGemUi.new "n"
+
+ use_ui ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal [@gem_repo], Gem.sources
+
+ expected = <<-EXPECTED
+ EXPECTED
+
+ assert_equal expected, @ui.output
+ assert_empty @ui.error
+ end
+
+ def test_execute_add_bad_uri
+ @cmd.handle_options %w[--add beta-gems.example.com]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal [@gem_repo], Gem.sources
+
+ expected = <<-EOF
+beta-gems.example.com is not a URI
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_clear_all
+ @cmd.handle_options %w[--clear-all]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+*** Removed specs cache ***
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+
+ dir = Gem.spec_cache_dir
+ refute File.exist?(dir), 'cache dir removed'
+ end
+
+ def test_execute_list
+ @cmd.handle_options %w[--list]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+*** CURRENT SOURCES ***
+
+#{@gem_repo}
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_remove
+ @cmd.handle_options %W[--remove #{@gem_repo}]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = "#{@gem_repo} removed from sources\n"
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_remove_no_network
+ @cmd.handle_options %W[--remove #{@gem_repo}]
+
+ @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do
+ raise Gem::RemoteFetcher::FetchError
+ end
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = "#{@gem_repo} removed from sources\n"
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_update
+ @cmd.handle_options %w[--update]
+
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ end
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "source cache successfully updated\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_specification_command.rb b/jni/ruby/test/rubygems/test_gem_commands_specification_command.rb
new file mode 100644
index 0000000..80564f9
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_specification_command.rb
@@ -0,0 +1,250 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/specification_command'
+
+class TestGemCommandsSpecificationCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::SpecificationCommand.new
+ end
+
+ def test_execute
+ foo = util_spec 'foo'
+
+ install_specs foo
+
+ @cmd.options[:args] = %w[foo]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|Gem::Specification|, @ui.output
+ assert_match %r|name: foo|, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_all
+ util_spec 'foo', '0.0.1'
+ util_spec 'foo', '0.0.2'
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:all] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|Gem::Specification|, @ui.output
+ assert_match %r|name: foo|, @ui.output
+ assert_match %r|version: 0.0.1|, @ui.output
+ assert_match %r|version: 0.0.2|, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_all_conflicts_with_version
+ util_spec 'foo', '0.0.1'
+ util_spec 'foo', '0.0.2'
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:all] = true
+ @cmd.options[:version] = "1"
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_equal "ERROR: Specify --all or -v, not both\n", @ui.error
+ end
+
+ def test_execute_bad_name
+ @cmd.options[:args] = %w[foo]
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_equal "ERROR: No gem matching 'foo (>= 0)' found\n", @ui.error
+ end
+
+ def test_execute_bad_name_with_version
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:version] = "1.3.2"
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_equal "ERROR: No gem matching 'foo (= 1.3.2)' found\n", @ui.error
+ end
+
+ def test_execute_exact_match
+ util_spec 'foo'
+ util_spec 'foo_bar'
+
+ @cmd.options[:args] = %w[foo]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|Gem::Specification|, @ui.output
+ assert_match %r|name: foo|, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_field
+ foo = new_spec 'foo', '2'
+
+ install_specs foo
+
+ @cmd.options[:args] = %w[foo name]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "foo", YAML.load(@ui.output)
+ end
+
+ def test_execute_file
+ foo = util_spec 'foo' do |s|
+ s.files = %w[lib/code.rb]
+ end
+
+ util_build_gem foo
+
+ @cmd.options[:args] = [foo.cache_file]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|Gem::Specification|, @ui.output
+ assert_match %r|name: foo|, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_marshal
+ foo = new_spec 'foo', '2'
+
+ install_specs foo
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:format] = :marshal
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal foo, Marshal.load(@ui.output)
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_remote
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', 1
+ end
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:domain] = :remote
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|\A--- !ruby/object:Gem::Specification|, @ui.output
+ assert_match %r|name: foo|, @ui.output
+ end
+
+ def test_execute_remote_with_version
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', "1"
+ fetcher.spec 'foo', "2"
+ end
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:version] = "1"
+ @cmd.options[:domain] = :remote
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ spec = Gem::Specification.from_yaml @ui.output
+
+ assert_equal Gem::Version.new("1"), spec.version
+ end
+
+ def test_execute_remote_without_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', '2.0.0'
+ fetcher.spec 'foo', '2.0.1.pre'
+ end
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:domain] = :remote
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|\A--- !ruby/object:Gem::Specification|, @ui.output
+ assert_match %r|name: foo|, @ui.output
+
+ spec = YAML.load @ui.output
+
+ assert_equal Gem::Version.new("2.0.0"), spec.version
+ end
+
+ def test_execute_remote_with_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', '2.0.0'
+ fetcher.spec 'foo', '2.0.1.pre'
+ end
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:domain] = :remote
+ @cmd.options[:prerelease] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|\A--- !ruby/object:Gem::Specification|, @ui.output
+ assert_match %r|name: foo|, @ui.output
+
+ spec = YAML.load @ui.output
+
+ assert_equal Gem::Version.new("2.0.1.pre"), spec.version
+ end
+
+ def test_execute_ruby
+ foo = util_spec 'foo'
+
+ install_specs foo
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:format] = :ruby
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r|Gem::Specification.new|, @ui.output
+ assert_match %r|s.name = "foo"|, @ui.output
+ assert_equal '', @ui.error
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_stale_command.rb b/jni/ruby/test/rubygems/test_gem_commands_stale_command.rb
new file mode 100644
index 0000000..ca80784
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_stale_command.rb
@@ -0,0 +1,40 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/stale_command'
+
+class TestGemCommandsStaleCommand < Gem::TestCase
+
+ def setup
+ super
+ @cmd = Gem::Commands::StaleCommand.new
+ end
+
+ def test_execute_sorts
+ files = %w[lib/foo_bar.rb Rakefile]
+ foo_bar = util_spec 'foo_bar' do |gem|
+ gem.files = files
+ end
+
+ bar_baz = util_spec 'bar_baz' do |gem|
+ gem.files = files
+ end
+
+ files.each do |file|
+ filename = File.join(bar_baz.full_gem_path, file)
+ FileUtils.mkdir_p File.dirname filename
+ FileUtils.touch(filename, :mtime => Time.now)
+
+ filename = File.join(foo_bar.full_gem_path, file)
+ FileUtils.mkdir_p File.dirname filename
+ FileUtils.touch(filename, :mtime => Time.now - 86400)
+ end
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ lines = @ui.output.split("\n")
+ assert_equal("#{foo_bar.name}-#{foo_bar.version}", lines[0].split.first)
+ assert_equal("#{bar_baz.name}-#{bar_baz.version}", lines[1].split.first)
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_commands_uninstall_command.rb b/jni/ruby/test/rubygems/test_gem_commands_uninstall_command.rb
new file mode 100644
index 0000000..4f045c5
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_uninstall_command.rb
@@ -0,0 +1,281 @@
+require 'rubygems/installer_test_case'
+require 'rubygems/commands/uninstall_command'
+
+class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
+
+ def setup
+ super
+
+ build_rake_in do
+ use_ui @ui do
+ @installer.install
+ end
+ end
+
+ @cmd = Gem::Commands::UninstallCommand.new
+ @executable = File.join(@gemhome, 'bin', 'executable')
+ end
+
+ def test_execute_all_named
+ util_make_gems
+
+ default = new_default_spec 'default', '1'
+ install_default_gems default
+
+ gemhome2 = "#{@gemhome}2"
+
+ a_4 = util_spec 'a', 4
+ install_gem a_4, :install_dir => gemhome2
+
+ Gem::Specification.dirs = [@gemhome, gemhome2]
+
+ assert_includes Gem::Specification.all_names, 'a-1'
+ assert_includes Gem::Specification.all_names, 'a-4'
+ assert_includes Gem::Specification.all_names, 'b-2'
+ assert_includes Gem::Specification.all_names, 'default-1'
+
+ @cmd.options[:all] = true
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal %w[a-4 a_evil-9 b-2 c-1.2 default-1 dep_x-1 pl-1-x86-linux x-1],
+ Gem::Specification.all_names.sort
+ end
+
+ def test_execute_dependency_order
+ c = quick_gem 'c' do |spec|
+ spec.add_dependency 'a'
+ end
+
+ util_build_gem c
+ installer = util_installer c, @gemhome
+ use_ui @ui do installer.install end
+
+ ui = Gem::MockGemUi.new
+
+ @cmd.options[:args] = %w[a c]
+ @cmd.options[:executables] = true
+
+ use_ui ui do
+ @cmd.execute
+ end
+
+ output = ui.output.split "\n"
+
+ assert_equal 'Successfully uninstalled c-2', output.shift
+ assert_equal "Removing executable", output.shift
+ assert_equal 'Successfully uninstalled a-2', output.shift
+ end
+
+ def test_execute_removes_executable
+ ui = Gem::MockGemUi.new
+
+ util_setup_gem ui
+
+ build_rake_in do
+ use_ui ui do
+ @installer.install
+ end
+ end
+
+ if win_platform? then
+ assert File.exist?(@executable)
+ else
+ assert File.symlink?(@executable)
+ end
+
+ # Evil hack to prevent false removal success
+ FileUtils.rm_f @executable
+
+ open @executable, "wb+" do |f| f.puts "binary" end
+
+ @cmd.options[:executables] = true
+ @cmd.options[:args] = [@spec.name]
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ output = @ui.output.split "\n"
+ assert_match(/Removing executable/, output.shift)
+ assert_match(/Successfully uninstalled/, output.shift)
+ assert_equal false, File.exist?(@executable)
+ assert_nil output.shift, "UI output should have contained only two lines"
+ end
+
+ def test_execute_removes_formatted_executable
+ FileUtils.rm_f @executable # Wish this didn't happen in #setup
+
+ Gem::Installer.exec_format = 'foo-%s-bar'
+
+ @installer.format_executable = true
+ @installer.install
+
+ formatted_executable = File.join @gemhome, 'bin', 'foo-executable-bar'
+ assert_equal true, File.exist?(formatted_executable)
+
+ @cmd.options[:executables] = true
+ @cmd.options[:format_executable] = true
+ @cmd.execute
+
+ assert_equal false, File.exist?(formatted_executable)
+ rescue
+ Gem::Installer.exec_format = nil
+ end
+
+ def test_execute_prerelease
+ @spec = util_spec "pre", "2.b"
+ @gem = File.join @tempdir, @spec.file_name
+ FileUtils.touch @gem
+
+ util_setup_gem
+
+ build_rake_in do
+ use_ui @ui do
+ @installer.install
+ end
+ end
+
+ @cmd.options[:executables] = true
+ @cmd.options[:args] = ["pre"]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ output = @ui.output
+ assert_match(/Successfully uninstalled/, output)
+ end
+
+ def test_execute_with_force_leaves_executable
+ ui = Gem::MockGemUi.new
+
+ util_make_gems
+ util_setup_gem ui
+
+ @cmd.options[:version] = '1'
+ @cmd.options[:force] = true
+ @cmd.options[:args] = ['a']
+
+ use_ui ui do
+ @cmd.execute
+ end
+
+ assert !Gem::Specification.all_names.include?('a')
+ assert File.exist? File.join(@gemhome, 'bin', 'executable')
+ end
+
+ def test_execute_with_force_uninstalls_all_versions
+ ui = Gem::MockGemUi.new "y\n"
+
+ util_make_gems
+ util_setup_gem ui
+
+ assert Gem::Specification.find_all_by_name('a').length > 1
+
+ @cmd.options[:force] = true
+ @cmd.options[:args] = ['a']
+
+ use_ui ui do
+ @cmd.execute
+ end
+
+ refute_includes Gem::Specification.all_names, 'a'
+ end
+
+ def test_execute_with_force_ignores_dependencies
+ ui = Gem::MockGemUi.new
+
+ util_make_gems
+ util_setup_gem ui
+
+ assert Gem::Specification.find_all_by_name('dep_x').length > 0
+ assert Gem::Specification.find_all_by_name('x').length > 0
+
+ @cmd.options[:force] = true
+ @cmd.options[:args] = ['x']
+
+ use_ui ui do
+ @cmd.execute
+ end
+
+ assert Gem::Specification.find_all_by_name('dep_x').length > 0
+ assert Gem::Specification.find_all_by_name('x').length == 0
+ end
+
+ def test_execute_all
+ util_make_gems
+
+ default = new_default_spec 'default', '1'
+ install_default_gems default
+
+ gemhome2 = "#{@gemhome}2"
+
+ a_4 = util_spec 'a', 4
+ install_gem a_4, :install_dir => gemhome2
+
+ Gem::Specification.dirs = [@gemhome, gemhome2]
+
+ assert_includes Gem::Specification.all_names, 'a-1'
+ assert_includes Gem::Specification.all_names, 'a-4'
+ assert_includes Gem::Specification.all_names, 'default-1'
+
+ @cmd.options[:all] = true
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal %w[a-4 default-1], Gem::Specification.all_names.sort
+ end
+
+ def test_handle_options
+ @cmd.handle_options %w[]
+
+ assert_equal false, @cmd.options[:check_dev]
+ assert_equal nil, @cmd.options[:install_dir]
+ assert_equal true, @cmd.options[:user_install]
+ assert_equal Gem::Requirement.default, @cmd.options[:version]
+ assert_equal false, @cmd.options[:vendor]
+ end
+
+ def test_handle_options_vendor
+ use_ui @ui do
+ @cmd.handle_options %w[--vendor]
+ end
+
+ assert @cmd.options[:vendor]
+ assert_equal Gem.vendor_dir, @cmd.options[:install_dir]
+
+ assert_empty @ui.output
+
+ expected = <<-EXPECTED
+WARNING: Use your OS package manager to uninstall vendor gems
+ EXPECTED
+
+ assert_match expected, @ui.error
+ end
+
+ def test_handle_options_vendor_missing
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG.delete 'vendordir'
+
+ e = assert_raises OptionParser::InvalidOption do
+ @cmd.handle_options %w[--vendor]
+ end
+
+ assert_equal 'invalid option: --vendor your platform is not supported',
+ e.message
+
+ refute @cmd.options[:vendor]
+ refute @cmd.options[:install_dir]
+
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_unpack_command.rb b/jni/ruby/test/rubygems/test_gem_commands_unpack_command.rb
new file mode 100644
index 0000000..59f6cc6
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_unpack_command.rb
@@ -0,0 +1,210 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/unpack_command'
+
+class TestGemCommandsUnpackCommand < Gem::TestCase
+
+ def setup
+ super
+
+ Dir.chdir @tempdir do
+ @cmd = Gem::Commands::UnpackCommand.new
+ end
+ end
+
+ def test_find_in_cache
+ util_make_gems
+
+ assert_equal(
+ @cmd.find_in_cache(File.basename @a1.cache_file),
+ @a1.cache_file,
+ 'found a-1.gem in the cache'
+ )
+ end
+
+ def test_get_path
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ end
+
+ dep = Gem::Dependency.new 'a', 1
+ assert_equal(
+ @cmd.get_path(dep),
+ specs['a-1'].cache_file,
+ 'fetches a-1 and returns the cache path'
+ )
+
+ FileUtils.rm specs['a-1'].cache_file
+
+ assert_equal(
+ @cmd.get_path(dep),
+ specs['a-1'].cache_file,
+ 'when removed from cache, refetches a-1'
+ )
+ end
+
+ def test_execute
+ util_make_gems
+
+ @cmd.options[:args] = %w[a b]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert File.exist?(File.join(@tempdir, 'a-3.a')), 'a should be unpacked'
+ assert File.exist?(File.join(@tempdir, 'b-2')), 'b should be unpacked'
+ end
+
+ def test_execute_gem_path
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '3.a'
+ end
+
+ Gem.clear_paths
+
+ gemhome2 = File.join @tempdir, 'gemhome2'
+
+ Gem.paths = { "GEM_PATH" => [gemhome2, @gemhome], "GEM_HOME" => gemhome2 }
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert File.exist?(File.join(@tempdir, 'a-3.a'))
+ end
+
+ def test_execute_gem_path_missing
+ spec_fetcher
+
+ Gem.clear_paths
+
+ gemhome2 = File.join @tempdir, 'gemhome2'
+
+ Gem.paths = { "GEM_PATH" => [gemhome2, @gemhome], "GEM_HOME" => gemhome2 }
+
+ @cmd.options[:args] = %w[z]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ end
+
+ def test_execute_remote
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.gem 'a', 2
+
+ fetcher.clear
+ end
+
+ Gem.configuration.verbose = :really
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert File.exist?(File.join(@tempdir, 'a-2')), 'a should be unpacked'
+ end
+
+ def test_execute_spec
+ util_make_gems
+
+ @cmd.options[:args] = %w[a b]
+ @cmd.options[:spec] = true
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert File.exist?(File.join(@tempdir, 'a-3.a.gemspec'))
+ assert File.exist?(File.join(@tempdir, 'b-2.gemspec'))
+ end
+
+ def test_execute_sudo
+ skip 'Cannot perform this test on windows (chmod)' if win_platform?
+
+ util_make_gems
+
+ FileUtils.chmod 0555, @gemhome
+
+ @cmd.options[:args] = %w[b]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert File.exist?(File.join(@tempdir, 'b-2')), 'b should be unpacked'
+ ensure
+ FileUtils.chmod 0755, @gemhome
+ end
+
+ def test_execute_with_target_option
+ util_make_gems
+
+ target = 'with_target'
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:target] = target
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert File.exist?(File.join(@tempdir, target, 'a-3.a'))
+ end
+
+ def test_execute_exact_match
+ foo_spec = util_spec 'foo'
+ foo_bar_spec = util_spec 'foo_bar'
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ Gem::Package.build foo_spec
+ Gem::Package.build foo_bar_spec
+ end
+ end
+
+ foo_path = File.join(@tempdir, "#{foo_spec.full_name}.gem")
+ foo_bar_path = File.join(@tempdir, "#{foo_bar_spec.full_name}.gem")
+ Gem::Installer.new(foo_path).install
+ Gem::Installer.new(foo_bar_path).install
+
+ @cmd.options[:args] = %w[foo]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert_path_exists File.join(@tempdir, foo_spec.full_name)
+ end
+
+ def test_handle_options_metadata
+ refute @cmd.options[:spec]
+
+ @cmd.send :handle_options, %w[--spec a]
+
+ assert @cmd.options[:spec]
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_update_command.rb b/jni/ruby/test/rubygems/test_gem_commands_update_command.rb
new file mode 100644
index 0000000..6a32706
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_update_command.rb
@@ -0,0 +1,543 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/update_command'
+
+begin
+ gem "rdoc"
+rescue Gem::LoadError
+ # ignore
+end
+
+class TestGemCommandsUpdateCommand < Gem::TestCase
+
+ def setup
+ super
+ common_installer_setup
+
+ @cmd = Gem::Commands::UpdateCommand.new
+
+ @cmd.options[:document] = []
+
+ @specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ fetcher.gem 'a', '3.a'
+
+ fetcher.clear
+ end
+
+ @a1_path = @specs['a-1'].cache_file
+ @a2_path = @specs['a-1'].cache_file
+ @a3a_path = @specs['a-3.a'].cache_file
+ end
+
+ def test_execute
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+
+ fetcher.clear
+
+ fetcher.spec 'a', 1
+ end
+
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Updating a", out.shift
+ assert_equal "Gems updated: a", out.shift
+ assert_empty out
+ end
+
+ def test_execute_multiple
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.gem 'ab', 2
+
+ fetcher.clear
+
+ fetcher.spec 'a', 1
+ fetcher.spec 'ab', 1
+ end
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Updating a", out.shift
+ assert_equal "Gems updated: a", out.shift
+ assert_empty out
+ end
+
+ def test_execute_system
+ spec_fetcher do |fetcher|
+ fetcher.gem 'rubygems-update', 9 do |s| s.files = %w[setup.rb] end
+
+ fetcher.clear
+ end
+
+ @cmd.options[:args] = []
+ @cmd.options[:system] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating rubygems-update", out.shift
+ assert_equal "Installing RubyGems 9", out.shift
+ assert_equal "RubyGems system software updated", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_system_at_latest
+ spec_fetcher do |fetcher|
+ fetcher.gem 'rubygems-update', Gem::VERSION do |s|
+ s.files = %w[setup.rb]
+ end
+
+ fetcher.clear
+ end
+
+ @cmd.options[:args] = []
+ @cmd.options[:system] = true
+
+ assert_raises Gem::MockGemUi::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Latest version currently installed. Aborting.", out.shift
+ assert_empty out
+ end
+
+ def test_execute_system_multiple
+ spec_fetcher do |fetcher|
+ fetcher.gem 'rubygems-update', 8 do |s| s.files = %w[setup.rb] end
+ fetcher.gem 'rubygems-update', 9 do |s| s.files = %w[setup.rb] end
+
+ fetcher.clear
+ end
+
+ @cmd.options[:args] = []
+ @cmd.options[:system] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating rubygems-update", out.shift
+ assert_equal "Installing RubyGems 9", out.shift
+ assert_equal "RubyGems system software updated", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_system_specific
+ spec_fetcher do |fetcher|
+ fetcher.gem 'rubygems-update', 8 do |s| s.files = %w[setup.rb] end
+ fetcher.gem 'rubygems-update', 9 do |s| s.files = %w[setup.rb] end
+
+ fetcher.clear
+ end
+
+ @cmd.options[:args] = []
+ @cmd.options[:system] = "8"
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating rubygems-update", out.shift
+ assert_equal "Installing RubyGems 8", out.shift
+ assert_equal "RubyGems system software updated", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_system_specifically_to_latest_version
+ spec_fetcher do |fetcher|
+ fetcher.gem 'rubygems-update', 8 do |s| s.files = %w[setup.rb] end
+ fetcher.gem 'rubygems-update', 9 do |s| s.files = %w[setup.rb] end
+
+ fetcher.clear
+ end
+
+ @cmd.options[:args] = []
+ @cmd.options[:system] = "9"
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating rubygems-update", out.shift
+ assert_equal "Installing RubyGems 9", out.shift
+ assert_equal "RubyGems system software updated", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_system_with_gems
+ @cmd.options[:args] = %w[gem]
+ @cmd.options[:system] = true
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_empty @ui.output
+ assert_equal "ERROR: Gem names are not allowed with the --system option\n",
+ @ui.error
+ end
+
+ # before:
+ # a1 -> c1.2
+ # after:
+ # a2 -> b2 # new dependency
+ # a2 -> c2
+
+ def test_execute_dependencies
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2, 'b' => 2, 'c' => 2
+ fetcher.gem 'b', 2
+ fetcher.gem 'c', 2
+
+ fetcher.clear
+
+ fetcher.spec 'a', 1, 'c' => '1.2'
+ fetcher.spec 'c', '1.2'
+ end
+
+ Gem::Specification.reset
+
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Updating a", out.shift
+ assert_equal "Gems updated: a b c",
+ out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_rdoc
+ skip if RUBY_VERSION <= "1.8.7"
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+
+ fetcher.clear
+
+ fetcher.spec 'a', 1
+ end
+
+ Gem.done_installing(&Gem::RDoc.method(:generation_hook))
+
+ @cmd.options[:document] = %w[rdoc ri]
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ wait_for_child_process_to_exit
+
+ a2 = @specs['a-2']
+
+ assert_path_exists File.join(a2.doc_dir, 'rdoc')
+ end
+
+ def test_execute_named
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+
+ fetcher.clear
+
+ fetcher.spec 'a', 1
+ end
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Updating a", out.shift
+ assert_equal "Gems updated: a", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_named_some_up_to_date
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.clear
+ fetcher.spec 'a', 1
+
+ fetcher.spec 'b', 2
+ end
+
+ @cmd.options[:args] = %w[a b]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Updating a", out.shift
+ assert_equal "Gems updated: a", out.shift
+ assert_equal "Gems already up-to-date: b", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_named_up_to_date
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ end
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Nothing to update", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_named_up_to_date_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '3.a'
+
+ fetcher.clear
+
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:prerelease] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Updating a", out.shift
+ assert_equal "Gems updated: a", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_up_to_date
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Nothing to update", out.shift
+
+ assert_empty out
+ end
+
+ def test_execute_user_install
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+
+ fetcher.clear
+
+ fetcher.spec 'a', 1
+ end
+
+ @cmd.handle_options %w[--user-install]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ installer = @cmd.installer
+ user_install = installer.instance_variable_get :@user_install
+
+ assert user_install, 'user_install must be set on the installer'
+ end
+
+ def test_fetch_remote_gems
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ end
+
+ expected = [
+ [Gem::NameTuple.new('a', v(2), Gem::Platform::RUBY),
+ Gem::Source.new(@gem_repo)],
+ ]
+
+ assert_equal expected, @cmd.fetch_remote_gems(specs['a-1'])
+ end
+
+ def test_fetch_remote_gems_error
+ Gem.sources.replace %w[http://nonexistent.example]
+
+ assert_raises Gem::RemoteFetcher::FetchError do
+ @cmd.fetch_remote_gems @specs['a-1']
+ end
+ end
+
+ def test_fetch_remote_gems_mismatch
+ platform = Gem::Platform.new 'x86-freebsd9'
+
+ specs = spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', 2 do |s| s.platform = platform end
+ end
+
+ expected = [
+ [Gem::NameTuple.new('a', v(2), Gem::Platform::RUBY),
+ Gem::Source.new(@gem_repo)],
+ ]
+
+ assert_equal expected, @cmd.fetch_remote_gems(specs['a-1'])
+ end
+
+ def test_fetch_remote_gems_prerelease
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ fetcher.gem 'a', '3.a'
+ end
+
+ @cmd.options[:prerelease] = true
+
+ expected = [
+ [Gem::NameTuple.new('a', v(2), Gem::Platform::RUBY),
+ Gem::Source.new(@gem_repo)],
+ [Gem::NameTuple.new('a', v('3.a'), Gem::Platform::RUBY),
+ Gem::Source.new(@gem_repo)],
+ ]
+
+ assert_equal expected, @cmd.fetch_remote_gems(specs['a-1'])
+ end
+
+ def test_handle_options_system
+ @cmd.handle_options %w[--system]
+
+ expected = {
+ :args => [],
+ :document => %w[rdoc ri],
+ :force => false,
+ :system => true,
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+ def test_handle_options_system_non_version
+ assert_raises ArgumentError do
+ @cmd.handle_options %w[--system non-version]
+ end
+ end
+
+ def test_handle_options_system_specific
+ @cmd.handle_options %w[--system 1.3.7]
+
+ expected = {
+ :args => [],
+ :document => %w[rdoc ri],
+ :force => false,
+ :system => "1.3.7",
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+ def test_update_gem_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', '1.a'
+ fetcher.gem 'a', '1.b'
+ end
+
+ @cmd.update_gem 'a', Gem::Requirement.new('= 1.b')
+
+ refute_empty @cmd.updated
+
+ assert @cmd.installer.instance_variable_get :@prerelease
+ end
+
+ def test_update_gem_unresolved_dependency
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.gem 'a', 2 do |s|
+ s.add_dependency 'b', '>= 2'
+ end
+
+ fetcher.spec 'b', 1
+ end
+
+ @cmd.update_gem 'a'
+
+ assert_empty @cmd.updated
+ end
+
+ def test_update_rubygems_arguments
+ @cmd.options[:system] = true
+
+ arguments = @cmd.update_rubygems_arguments
+
+ assert_equal '--prefix', arguments.shift
+ assert_equal Gem.prefix, arguments.shift
+ assert_equal '--no-rdoc', arguments.shift
+ assert_equal '--no-ri', arguments.shift
+ assert_equal '--previous-version', arguments.shift
+ assert_equal Gem::VERSION, arguments.shift
+ assert_empty arguments
+ end
+
+ def test_update_rubygems_arguments_1_8_x
+ @cmd.options[:system] = '1.8.26'
+
+ arguments = @cmd.update_rubygems_arguments
+
+ assert_equal '--prefix', arguments.shift
+ assert_equal Gem.prefix, arguments.shift
+ assert_equal '--no-rdoc', arguments.shift
+ assert_equal '--no-ri', arguments.shift
+ assert_empty arguments
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_which_command.rb b/jni/ruby/test/rubygems/test_gem_commands_which_command.rb
new file mode 100644
index 0000000..7ce26c8
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_which_command.rb
@@ -0,0 +1,85 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/which_command'
+
+class TestGemCommandsWhichCommand < Gem::TestCase
+
+ def setup
+ super
+ Gem::Specification.reset
+ @cmd = Gem::Commands::WhichCommand.new
+ end
+
+ def test_execute
+ util_foo_bar
+
+ @cmd.handle_options %w[foo_bar]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#{@foo_bar.full_gem_path}/lib/foo_bar.rb\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_directory
+ @cmd.handle_options %w[directory]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_match %r%Can.t find ruby library file or shared library directory\n%,
+ @ui.error
+ end
+
+ def test_execute_one_missing
+ # TODO: this test fails in isolation
+
+ util_foo_bar
+
+ @cmd.handle_options %w[foo_bar missinglib]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "#{@foo_bar.full_gem_path}/lib/foo_bar.rb\n", @ui.output
+ assert_match %r%Can.t find ruby library file or shared library missinglib\n%,
+ @ui.error
+ end
+
+ def test_execute_missing
+ @cmd.handle_options %w[missinglib]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_match %r%Can.t find ruby library file or shared library missinglib\n%,
+ @ui.error
+ end
+
+ def util_foo_bar
+ files = %w[lib/foo_bar.rb lib/directory/baz.rb Rakefile]
+ @foo_bar = util_spec 'foo_bar' do |gem|
+ gem.files = files
+ end
+
+ files.each do |file|
+ filename = File.join(@foo_bar.full_gem_path, file)
+ FileUtils.mkdir_p File.dirname filename
+ FileUtils.touch filename
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_commands_yank_command.rb b/jni/ruby/test/rubygems/test_gem_commands_yank_command.rb
new file mode 100644
index 0000000..469fd15
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_commands_yank_command.rb
@@ -0,0 +1,97 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/yank_command'
+
+class TestGemCommandsYankCommand < Gem::TestCase
+ def setup
+ super
+
+ @cmd = Gem::Commands::YankCommand.new
+ @cmd.host = 'http://example'
+
+ @fetcher = Gem::RemoteFetcher.fetcher
+
+ Gem.configuration.rubygems_api_key = 'key'
+ Gem.configuration.api_keys[:KEY] = 'other'
+ end
+
+ def test_handle_options
+ @cmd.handle_options %w[a --version 1.0 --platform x86-darwin -k KEY]
+
+ assert_equal %w[a], @cmd.options[:args]
+ assert_equal :KEY, @cmd.options[:key]
+ assert_nil @cmd.options[:platform]
+ assert_equal req('= 1.0'), @cmd.options[:version]
+ end
+
+ def test_handle_options_missing_argument
+ %w[-v --version -p --platform].each do |option|
+ assert_raises OptionParser::MissingArgument do
+ @cmd.handle_options %W[a #{option}]
+ end
+ end
+ end
+
+ def test_execute
+ yank_uri = 'http://example/api/v1/gems/yank'
+ @fetcher.data[yank_uri] = ['Successfully yanked', 200, 'OK']
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:added_platform] = true
+ @cmd.options[:version] = req('= 1.0')
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r%Yanking gem from http://example%, @ui.output
+ assert_match %r%Successfully yanked%, @ui.output
+
+ platform = Gem.platforms[1]
+ body = @fetcher.last_request.body.split('&').sort
+ assert_equal %W[gem_name=a platform=#{platform} version=1.0], body
+
+ assert_equal 'key', @fetcher.last_request['Authorization']
+
+ assert_equal [yank_uri], @fetcher.paths
+ end
+
+ def test_execute_key
+ yank_uri = 'http://example/api/v1/gems/yank'
+ @fetcher.data[yank_uri] = ['Successfully yanked', 200, 'OK']
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:version] = req('= 1.0')
+ @cmd.options[:key] = :KEY
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ body = @fetcher.last_request.body.split('&').sort
+ assert_equal %w[gem_name=a version=1.0], body
+ assert_equal 'other', @fetcher.last_request['Authorization']
+ end
+
+ def test_execute_undo
+ unyank_uri = 'http://example/api/v1/gems/unyank'
+ @fetcher.data[unyank_uri] = ['Successfully unyanked', 200, 'OK']
+
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:version] = req('= 1.0')
+ @cmd.options[:undo] = true
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_match %r%Unyanking gem from http://example%, @ui.output
+ assert_match %r%Successfully unyanked%, @ui.output
+
+ body = @fetcher.last_request.body.split('&').sort
+ assert_equal %w[gem_name=a version=1.0], body
+
+ assert_equal [unyank_uri], @fetcher.paths
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_config_file.rb b/jni/ruby/test/rubygems/test_gem_config_file.rb
new file mode 100644
index 0000000..5077f37
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_config_file.rb
@@ -0,0 +1,467 @@
+require 'rubygems/test_case'
+require 'rubygems/config_file'
+
+class TestGemConfigFile < Gem::TestCase
+
+ def setup
+ super
+
+ @temp_conf = File.join @tempdir, '.gemrc'
+
+ @cfg_args = %W[--config-file #{@temp_conf}]
+
+ @orig_SYSTEM_WIDE_CONFIG_FILE = Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE
+ Gem::ConfigFile.send :remove_const, :SYSTEM_WIDE_CONFIG_FILE
+ Gem::ConfigFile.send :const_set, :SYSTEM_WIDE_CONFIG_FILE,
+ File.join(@tempdir, 'system-gemrc')
+ Gem::ConfigFile::OPERATING_SYSTEM_DEFAULTS.clear
+ Gem::ConfigFile::PLATFORM_DEFAULTS.clear
+
+ @env_gemrc = ENV['GEMRC']
+ ENV['GEMRC'] = ''
+
+ util_config_file
+ end
+
+ def teardown
+ Gem::ConfigFile::OPERATING_SYSTEM_DEFAULTS.clear
+ Gem::ConfigFile::PLATFORM_DEFAULTS.clear
+ Gem::ConfigFile.send :remove_const, :SYSTEM_WIDE_CONFIG_FILE
+ Gem::ConfigFile.send :const_set, :SYSTEM_WIDE_CONFIG_FILE,
+ @orig_SYSTEM_WIDE_CONFIG_FILE
+
+ ENV['GEMRC'] = @env_gemrc
+
+ super
+ end
+
+ def test_initialize
+ assert_equal @temp_conf, @cfg.config_file_name
+
+ assert_equal false, @cfg.backtrace
+ assert_equal true, @cfg.update_sources
+ assert_equal Gem::ConfigFile::DEFAULT_BULK_THRESHOLD, @cfg.bulk_threshold
+ assert_equal true, @cfg.verbose
+ assert_equal [@gem_repo], Gem.sources
+
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts ":backtrace: true"
+ fp.puts ":update_sources: false"
+ fp.puts ":bulk_threshold: 10"
+ fp.puts ":verbose: false"
+ fp.puts ":sources:"
+ fp.puts " - http://more-gems.example.com"
+ fp.puts "install: --wrappers"
+ fp.puts ":gempath:"
+ fp.puts "- /usr/ruby/1.8/lib/ruby/gems/1.8"
+ fp.puts "- /var/ruby/1.8/gem_home"
+ fp.puts ":ssl_verify_mode: 0"
+ fp.puts ":ssl_ca_cert: /etc/ssl/certs"
+ end
+
+ util_config_file
+
+ assert_equal true, @cfg.backtrace
+ assert_equal 10, @cfg.bulk_threshold
+ assert_equal false, @cfg.verbose
+ assert_equal false, @cfg.update_sources
+ assert_equal %w[http://more-gems.example.com], Gem.sources
+ assert_equal '--wrappers', @cfg[:install]
+ assert_equal(['/usr/ruby/1.8/lib/ruby/gems/1.8', '/var/ruby/1.8/gem_home'],
+ @cfg.path)
+ assert_equal 0, @cfg.ssl_verify_mode
+ assert_equal '/etc/ssl/certs', @cfg.ssl_ca_cert
+ end
+
+ def test_initialize_handle_arguments_config_file
+ util_config_file %W[--config-file #{@temp_conf}]
+
+ assert_equal @temp_conf, @cfg.config_file_name
+ end
+
+ def test_initialize_handle_arguments_config_file_with_other_params
+ util_config_file %W[--config-file #{@temp_conf} --backtrace]
+
+ assert_equal @temp_conf, @cfg.config_file_name
+ end
+
+ def test_initialize_handle_arguments_config_file_equals
+ util_config_file %W[--config-file=#{@temp_conf}]
+
+ assert_equal @temp_conf, @cfg.config_file_name
+ end
+
+ def test_initialize_operating_system_override
+ Gem::ConfigFile::OPERATING_SYSTEM_DEFAULTS[:bulk_threshold] = 1
+ Gem::ConfigFile::OPERATING_SYSTEM_DEFAULTS['install'] = '--no-env-shebang'
+
+ Gem::ConfigFile::PLATFORM_DEFAULTS[:bulk_threshold] = 2
+
+ util_config_file
+
+ assert_equal 2, @cfg.bulk_threshold
+ assert_equal '--no-env-shebang', @cfg[:install]
+ end
+
+ def test_initialize_platform_override
+ Gem::ConfigFile::PLATFORM_DEFAULTS[:bulk_threshold] = 2
+ Gem::ConfigFile::PLATFORM_DEFAULTS['install'] = '--no-env-shebang'
+
+ File.open Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE, 'w' do |fp|
+ fp.puts ":bulk_threshold: 3"
+ end
+
+ util_config_file
+
+ assert_equal 3, @cfg.bulk_threshold
+ assert_equal '--no-env-shebang', @cfg[:install]
+ end
+
+ def test_initialize_system_wide_override
+ File.open Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE, 'w' do |fp|
+ fp.puts ":backtrace: false"
+ fp.puts ":bulk_threshold: 2048"
+ end
+
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts ":backtrace: true"
+ end
+
+ util_config_file
+
+ assert_equal 2048, @cfg.bulk_threshold
+ assert_equal true, @cfg.backtrace
+ end
+
+ def test_initialize_environment_variable_override
+ File.open Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE, 'w' do |fp|
+ fp.puts ':backtrace: false'
+ fp.puts ':verbose: false'
+ fp.puts ':bulk_threshold: 2048'
+ end
+
+ conf1 = File.join @tempdir, 'gemrc1'
+ File.open conf1, 'w' do |fp|
+ fp.puts ':backtrace: true'
+ end
+
+ conf2 = File.join @tempdir, 'gemrc2'
+ File.open conf2, 'w' do |fp|
+ fp.puts ':verbose: true'
+ end
+
+ conf3 = File.join @tempdir, 'gemrc3'
+ File.open conf3, 'w' do |fp|
+ fp.puts ':verbose: :loud'
+ end
+
+ ENV['GEMRC'] = conf1 + ':' + conf2 + ';' + conf3
+
+ util_config_file
+
+ assert_equal true, @cfg.backtrace
+ assert_equal :loud, @cfg.verbose
+ assert_equal 2048, @cfg.bulk_threshold
+ end
+
+ def test_api_keys
+ assert_nil @cfg.instance_variable_get :@api_keys
+
+ temp_cred = File.join Gem.user_home, '.gem', 'credentials'
+ FileUtils.mkdir File.dirname(temp_cred)
+ File.open temp_cred, 'w', 0600 do |fp|
+ fp.puts ':rubygems_api_key: 701229f217cdf23b1344c7b4b54ca97'
+ end
+
+ util_config_file
+
+ assert_equal({:rubygems => '701229f217cdf23b1344c7b4b54ca97'},
+ @cfg.api_keys)
+ end
+
+ def test_check_credentials_permissions
+ skip 'chmod not supported' if win_platform?
+
+ @cfg.rubygems_api_key = 'x'
+
+ File.chmod 0644, @cfg.credentials_path
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cfg.load_api_keys
+ end
+ end
+
+ assert_empty @ui.output
+
+ expected = <<-EXPECTED
+ERROR: Your gem push credentials file located at:
+
+\t#{@cfg.credentials_path}
+
+has file permissions of 0644 but 0600 is required.
+
+To fix this error run:
+
+\tchmod 0600 #{@cfg.credentials_path}
+
+You should reset your credentials at:
+
+\thttps://rubygems.org/profile/edit
+
+if you believe they were disclosed to a third party.
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_handle_arguments
+ args = %w[--backtrace --bunch --of --args here]
+
+ @cfg.handle_arguments args
+
+ assert_equal %w[--bunch --of --args here], @cfg.args
+ end
+
+ def test_handle_arguments_backtrace
+ assert_equal false, @cfg.backtrace
+
+ args = %w[--backtrace]
+
+ @cfg.handle_arguments args
+
+ assert_equal true, @cfg.backtrace
+ end
+
+ def test_handle_arguments_debug
+ assert_equal false, $DEBUG
+
+ args = %w[--debug]
+
+ _, err = capture_io do
+ @cfg.handle_arguments args
+ end
+
+ assert_match 'NOTE', err
+
+ assert_equal true, $DEBUG
+ ensure
+ $DEBUG = false
+ end
+
+ def test_handle_arguments_override
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts ":backtrace: false"
+ end
+
+ util_config_file %W[--backtrace --config-file=#{@temp_conf}]
+
+ assert_equal true, @cfg.backtrace
+ end
+
+ def test_handle_arguments_traceback
+ assert_equal false, @cfg.backtrace
+
+ args = %w[--traceback]
+
+ @cfg.handle_arguments args
+
+ assert_equal true, @cfg.backtrace
+ end
+
+ def test_load_api_keys
+ temp_cred = File.join Gem.user_home, '.gem', 'credentials'
+ FileUtils.mkdir File.dirname(temp_cred)
+ File.open temp_cred, 'w', 0600 do |fp|
+ fp.puts ":rubygems_api_key: 701229f217cdf23b1344c7b4b54ca97"
+ fp.puts ":other: a5fdbb6ba150cbb83aad2bb2fede64c"
+ end
+
+ util_config_file
+
+ assert_equal({:rubygems => '701229f217cdf23b1344c7b4b54ca97',
+ :other => 'a5fdbb6ba150cbb83aad2bb2fede64c'}, @cfg.api_keys)
+ end
+
+ def test_load_api_keys_bad_permission
+ skip 'chmod not supported' if win_platform?
+
+ @cfg.rubygems_api_key = 'x'
+
+ File.chmod 0644, @cfg.credentials_path
+
+ assert_raises Gem::MockGemUi::TermError do
+ @cfg.load_api_keys
+ end
+ end
+
+ def test_really_verbose
+ assert_equal false, @cfg.really_verbose
+
+ @cfg.verbose = true
+
+ assert_equal false, @cfg.really_verbose
+
+ @cfg.verbose = 1
+
+ assert_equal true, @cfg.really_verbose
+ end
+
+ def test_rubygems_api_key_equals
+ @cfg.rubygems_api_key = 'x'
+
+ assert_equal 'x', @cfg.rubygems_api_key
+
+ expected = {
+ :rubygems_api_key => 'x',
+ }
+
+ assert_equal expected, YAML.load_file(@cfg.credentials_path)
+
+ unless win_platform? then
+ stat = File.stat @cfg.credentials_path
+
+ assert_equal 0600, stat.mode & 0600
+ end
+ end
+
+ def test_rubygems_api_key_equals_bad_permission
+ skip 'chmod not supported' if win_platform?
+
+ @cfg.rubygems_api_key = 'x'
+
+ File.chmod 0644, @cfg.credentials_path
+
+ assert_raises Gem::MockGemUi::TermError do
+ @cfg.rubygems_api_key = 'y'
+ end
+
+ expected = {
+ :rubygems_api_key => 'x',
+ }
+
+ assert_equal expected, YAML.load_file(@cfg.credentials_path)
+
+ stat = File.stat @cfg.credentials_path
+
+ assert_equal 0644, stat.mode & 0644
+ end
+
+ def test_write
+ @cfg.backtrace = true
+ @cfg.update_sources = false
+ @cfg.bulk_threshold = 10
+ @cfg.verbose = false
+ Gem.sources.replace %w[http://more-gems.example.com]
+ @cfg[:install] = '--wrappers'
+
+ @cfg.write
+
+ util_config_file
+
+ # These should not be written out to the config file.
+ assert_equal false, @cfg.backtrace, 'backtrace'
+ assert_equal Gem::ConfigFile::DEFAULT_BULK_THRESHOLD, @cfg.bulk_threshold,
+ 'bulk_threshold'
+ assert_equal true, @cfg.update_sources, 'update_sources'
+ assert_equal true, @cfg.verbose, 'verbose'
+
+ assert_equal '--wrappers', @cfg[:install], 'install'
+
+ # this should be written out to the config file.
+ assert_equal %w[http://more-gems.example.com], Gem.sources
+ end
+
+ def test_write_from_hash
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts ":backtrace: true"
+ fp.puts ":bulk_threshold: 10"
+ fp.puts ":update_sources: false"
+ fp.puts ":verbose: false"
+ fp.puts ":sources:"
+ fp.puts " - http://more-gems.example.com"
+ fp.puts ":ssl_verify_mode: 2"
+ fp.puts ":ssl_ca_cert: /nonexistent/ca_cert.pem"
+ fp.puts ":ssl_client_cert: /nonexistent/client_cert.pem"
+ fp.puts "install: --wrappers"
+ end
+
+ util_config_file
+
+ @cfg.backtrace = :junk
+ @cfg.update_sources = :junk
+ @cfg.bulk_threshold = 20
+ @cfg.verbose = :junk
+ Gem.sources.replace %w[http://even-more-gems.example.com]
+ @cfg[:install] = '--wrappers --no-rdoc'
+
+ @cfg.write
+
+ util_config_file
+
+ # These should not be written out to the config file
+ assert_equal true, @cfg.backtrace, 'backtrace'
+ assert_equal 10, @cfg.bulk_threshold, 'bulk_threshold'
+ assert_equal false, @cfg.update_sources, 'update_sources'
+ assert_equal false, @cfg.verbose, 'verbose'
+
+ assert_equal 2, @cfg.ssl_verify_mode
+ assert_equal '/nonexistent/ca_cert.pem', @cfg.ssl_ca_cert
+ assert_equal '/nonexistent/client_cert.pem', @cfg.ssl_client_cert
+
+ assert_equal '--wrappers --no-rdoc', @cfg[:install], 'install'
+
+ assert_equal %w[http://even-more-gems.example.com], Gem.sources
+ end
+
+ def test_ignore_invalid_config_file
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts "some-non-yaml-hash-string"
+ end
+
+ begin
+ verbose, $VERBOSE = $VERBOSE, nil
+
+ util_config_file
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ def test_load_ssl_verify_mode_from_config
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts ":ssl_verify_mode: 1"
+ end
+ util_config_file
+ assert_equal(1, @cfg.ssl_verify_mode)
+ end
+
+ def test_load_ssl_ca_cert_from_config
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts ":ssl_ca_cert: /home/me/certs"
+ end
+ util_config_file
+ assert_equal('/home/me/certs', @cfg.ssl_ca_cert)
+ end
+
+ def test_load_ssl_client_cert_from_config
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts ":ssl_client_cert: /home/me/mine.pem"
+ end
+ util_config_file
+ assert_equal('/home/me/mine.pem', @cfg.ssl_client_cert)
+ end
+
+ def util_config_file(args = @cfg_args)
+ @cfg = Gem::ConfigFile.new args
+ end
+
+ def test_disable_default_gem_server
+ File.open @temp_conf, 'w' do |fp|
+ fp.puts ":disable_default_gem_server: true"
+ end
+ util_config_file
+ assert_equal(true, @cfg.disable_default_gem_server)
+ end
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_dependency.rb b/jni/ruby/test/rubygems/test_gem_dependency.rb
new file mode 100644
index 0000000..c43ff0e
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_dependency.rb
@@ -0,0 +1,357 @@
+require 'rubygems/test_case'
+require 'rubygems/dependency'
+
+class TestGemDependency < Gem::TestCase
+
+ def test_initialize
+ d = dep "pkg", "> 1.0"
+
+ assert_equal "pkg", d.name
+ assert_equal req("> 1.0"), d.requirement
+ end
+
+ def test_initialize_type_bad
+ e = assert_raises ArgumentError do
+ Gem::Dependency.new 'monkey' => '1.0'
+ end
+
+ assert_equal 'dependency name must be a String, was {"monkey"=>"1.0"}',
+ e.message
+ end
+
+ def test_initialize_double
+ d = dep "pkg", "> 1.0", "< 2.0"
+ assert_equal req("> 1.0", "< 2.0"), d.requirement
+ end
+
+ def test_initialize_empty
+ d = dep "pkg"
+ assert_equal req(">= 0"), d.requirement
+ end
+
+ def test_initialize_prerelease
+ d = dep 'd', '1.a'
+
+ assert d.prerelease?
+
+ d = dep 'd', '= 1.a'
+
+ assert d.prerelease?
+ end
+
+ def test_initialize_type
+ assert_equal :runtime, dep("pkg").type
+ assert_equal :development, dep("pkg", [], :development).type
+
+ assert_raises ArgumentError do
+ dep "pkg", :sometimes
+ end
+ end
+
+ def test_initialize_version
+ d = dep "pkg", v("2")
+ assert_equal req("= 2"), d.requirement
+ end
+
+ def test_equals2
+ o = dep "other"
+ d = dep "pkg", "> 1.0"
+ d1 = dep "pkg", "> 1.1"
+
+ assert_equal d, d.dup
+ assert_equal d.dup, d
+
+ refute_equal d, d1
+ refute_equal d1, d
+
+ refute_equal d, o
+ refute_equal o, d
+
+ refute_equal d, Object.new
+ refute_equal Object.new, d
+ end
+
+ def test_equals2_type
+ refute_equal dep("pkg", :runtime), dep("pkg", :development)
+ end
+
+ def test_equals_tilde
+ d = dep "a", "0"
+
+ assert_match d, d, "match self"
+ assert_match dep("a", ">= 0"), d, "match version exact"
+ assert_match dep("a", ">= 0"), dep("a", "1"), "match version"
+ refute_match dep("a"), Object.new
+
+ Gem::Deprecate.skip_during do
+ assert_match dep(/a/, ">= 0"), d, "match simple regexp"
+ assert_match dep(/a|b/, ">= 0"), d, "match scary regexp"
+ refute_match dep(/a/), dep("b")
+ end
+ end
+
+ def test_equals_tilde_escape
+ refute_match dep("a|b"), dep("a", "1")
+ Gem::Deprecate.skip_during do
+ assert_match dep(/a|b/), dep("a", "1")
+ end
+ end
+
+ def test_equals_tilde_object
+ o = Object.new
+ def o.name ; 'a' end
+ def o.version ; '0' end
+
+ assert_match dep("a"), o
+ end
+
+ def test_equals_tilde_spec
+ assert_match dep("a", ">= 0"), spec("a", "0")
+ assert_match dep("a", "1"), spec("a", "1")
+ Gem::Deprecate.skip_during do
+ assert_match dep(/a/, ">= 0"), spec("a", "0")
+ assert_match dep(/a|b/, ">= 0"), spec("b", "0")
+ refute_match dep(/a/, ">= 0"), spec("b", "0")
+ end
+ end
+
+ def test_hash
+ d = dep "pkg", "1.0"
+
+ assert_equal d.hash, d.dup.hash
+ assert_equal d.dup.hash, d.hash
+
+ refute_equal dep("pkg", "1.0").hash, dep("pkg", "2.0").hash, "requirement"
+ refute_equal dep("pkg", "1.0").hash, dep("abc", "1.0").hash, "name"
+ refute_equal dep("pkg", :development), dep("pkg", :runtime), "type"
+ end
+
+ def test_match_eh_name_tuple
+ a_dep = dep 'a'
+
+ a_tup = Gem::NameTuple.new 'a', 1
+ b_tup = Gem::NameTuple.new 'b', 2
+ c_tup = Gem::NameTuple.new 'c', '2.a'
+
+ assert a_dep.match? a_tup
+ refute a_dep.match? b_tup
+
+ b_dep = dep 'b', '>= 3'
+
+ refute b_dep.match? b_tup
+
+ c_dep = dep 'c', '>= 1'
+
+ refute c_dep.match? c_tup
+
+ c_dep = dep 'c'
+
+ refute c_dep.match? c_tup
+
+ c_dep = dep 'c', '2.a'
+
+ assert c_dep.match? c_tup
+ end
+
+ def test_match_eh_allow_prerelease
+ a_dep = dep 'a'
+
+ a_tup = Gem::NameTuple.new 'a', 1
+ b_tup = Gem::NameTuple.new 'b', 2
+ c_tup = Gem::NameTuple.new 'c', '2.a'
+
+ assert a_dep.match? a_tup, nil, true
+ refute a_dep.match? b_tup, nil, true
+
+ b_dep = dep 'b', '>= 3'
+
+ refute b_dep.match? b_tup, nil, true
+
+ c_dep = dep 'c', '>= 1'
+
+ assert c_dep.match? c_tup, nil, true
+
+ c_dep = dep 'c'
+
+ assert c_dep.match? c_tup, nil, true
+
+ c_dep = dep 'c', '2.a'
+
+ assert c_dep.match? c_tup, nil, true
+ end
+
+ def test_match_eh_specification
+ a_dep = dep 'a'
+
+ a_spec = util_spec 'a', 1
+ b_spec = util_spec 'b', 2
+ c_spec = util_spec 'c', '2.a'
+
+ assert a_dep.match? a_spec
+ refute a_dep.match? b_spec
+
+ b_dep = dep 'b', '>= 3'
+
+ refute b_dep.match? b_spec
+
+ c_dep = dep 'c', '>= 1'
+
+ refute c_dep.match? c_spec
+
+ c_dep = dep 'c'
+
+ refute c_dep.match? c_spec
+
+ c_dep = dep 'c', '2.a'
+
+ assert c_dep.match? c_spec
+ end
+
+ def test_matches_spec_eh
+ spec = util_spec 'b', 2
+
+ refute dep('a') .matches_spec?(spec), 'name mismatch'
+ assert dep('b') .matches_spec?(spec), 'name match'
+ refute dep('b', '= 1') .matches_spec?(spec), 'requirement mismatch'
+ assert dep('b', '~> 2').matches_spec?(spec), 'requirement match'
+ end
+
+ def test_matches_spec_eh_prerelease
+ spec = util_spec 'b', '2.1.a'
+
+ refute dep('a') .matches_spec?(spec), 'name mismatch'
+ assert dep('b') .matches_spec?(spec), 'name match'
+ refute dep('b', '= 1') .matches_spec?(spec), 'requirement mismatch'
+ assert dep('b', '~> 2') .matches_spec?(spec), 'requirement match'
+ assert dep('b', '~> 2.a').matches_spec?(spec), 'prerelease requirement'
+ end
+
+ def test_merge
+ a1 = dep 'a', '~> 1.0'
+ a2 = dep 'a', '= 1.0'
+
+ a3 = a1.merge a2
+
+ assert_equal dep('a', '~> 1.0', '= 1.0'), a3
+ end
+
+ def test_merge_default
+ a1 = dep 'a'
+ a2 = dep 'a', '1'
+
+ a3 = a1.merge a2
+
+ assert_equal dep('a', '1'), a3
+ end
+
+ def test_merge_name_mismatch
+ a = dep 'a'
+ b = dep 'b'
+
+ e = assert_raises ArgumentError do
+ a.merge b
+ end
+
+ assert_equal 'a (>= 0) and b (>= 0) have different names',
+ e.message
+ end
+
+ def test_merge_other_default
+ a1 = dep 'a', '1'
+ a2 = dep 'a'
+
+ a3 = a1.merge a2
+
+ assert_equal dep('a', '1'), a3
+ end
+
+ def test_prerelease_eh
+ d = dep "pkg", "= 1"
+
+ refute d.prerelease?
+
+ d.prerelease = true
+
+ assert d.prerelease?
+
+ d = dep "pkg", "= 1.a"
+
+ assert d.prerelease?
+
+ d.prerelease = false
+
+ assert d.prerelease?
+
+ d = dep "pkg", "> 1.a", "> 2"
+
+ assert d.prerelease?
+ end
+
+ def test_specific
+ refute dep('a', '> 1').specific?
+
+ assert dep('a', '= 1').specific?
+ end
+
+ def test_to_spec
+ util_spec 'a', '1'
+ a_2 = util_spec 'a', '2'
+
+ a_dep = dep 'a', '>= 0'
+
+ assert_equal a_2, a_dep.to_spec
+ end
+
+ def test_to_spec_prerelease
+ a_1 = util_spec 'a', '1'
+ a_1_1_a = util_spec 'a', '1.1.a'
+
+ a_dep = dep 'a', '>= 0'
+
+ assert_equal a_1, a_dep.to_spec
+
+ a_pre_dep = dep 'a', '>= 0'
+ a_pre_dep.prerelease = true
+
+ assert_equal a_1_1_a, a_pre_dep.to_spec
+ end
+
+ def test_to_specs_suggests_other_versions
+ a = util_spec 'a', '1.0', 'b' => '>= 1.0'
+
+ a_file = File.join a.gem_dir, 'lib', 'a_file.rb'
+
+ write_file a_file do |io|
+ io.puts '# a_file.rb'
+ end
+
+ dep = Gem::Dependency.new "a", "= 2.0"
+
+ e = assert_raises Gem::LoadError do
+ dep.to_specs
+ end
+
+ assert_match "Could not find 'a' (= 2.0) - did find: [a-1.0]", e.message
+ end
+
+ def test_to_specs_indicates_total_gem_set_size
+ a = util_spec 'a', '1.0', 'b' => '>= 1.0'
+
+ a_file = File.join a.gem_dir, 'lib', 'a_file.rb'
+
+ write_file a_file do |io|
+ io.puts '# a_file.rb'
+ end
+
+ dep = Gem::Dependency.new "b", "= 2.0"
+
+ e = assert_raises Gem::LoadError do
+ dep.to_specs
+ end
+
+ assert_match "Could not find 'b' (= 2.0) among 1 total gem(s)", e.message
+ end
+
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_dependency_installer.rb b/jni/ruby/test/rubygems/test_gem_dependency_installer.rb
new file mode 100644
index 0000000..8a04707
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_dependency_installer.rb
@@ -0,0 +1,1208 @@
+require 'rubygems/test_case'
+require 'rubygems/dependency_installer'
+require 'rubygems/security'
+
+class TestGemDependencyInstaller < Gem::TestCase
+
+ def setup
+ super
+ common_installer_setup
+
+ @gems_dir = File.join @tempdir, 'gems'
+ @cache_dir = File.join @gemhome, 'cache'
+
+ FileUtils.mkdir @gems_dir
+
+ Gem::RemoteFetcher.fetcher = @fetcher = Gem::FakeFetcher.new
+ end
+
+ def util_setup_gems
+ @a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end
+ @a1_pre, @a1_pre_gem = util_gem 'a', '1.a'
+ @b1, @b1_gem = util_gem 'b', '1' do |s|
+ s.add_dependency 'a'
+ s.add_development_dependency 'aa'
+ end
+
+ @c1, @c1_gem = util_gem 'c', '1' do |s|
+ s.add_development_dependency 'b'
+ end
+
+ @d1, @d1_gem = util_gem 'd', '1' do |s|
+ s.add_development_dependency 'c'
+ end
+
+ util_clear_gems
+ util_reset_gems
+ end
+
+ def test_available_set_for_name
+ util_setup_gems
+ p1a, = util_gem 'a', '10.a'
+ util_setup_spec_fetcher p1a, @a1, @a1_pre
+
+ inst = Gem::DependencyInstaller.new
+
+ available = inst.available_set_for 'a', Gem::Requirement.default
+
+ assert_equal %w[a-1], available.set.map { |s| s.spec.full_name }
+ end
+
+ def test_available_set_for_name_prerelease
+ util_setup_gems
+ p1a, = util_gem 'a', '10.a'
+ util_setup_spec_fetcher p1a, @a1, @a1_pre
+
+ inst = Gem::DependencyInstaller.new :prerelease => true
+
+ available = inst.available_set_for 'a', Gem::Requirement.default
+
+ assert_equal %w[a-10.a],
+ available.sorted.map { |s| s.spec.full_name }
+ end
+
+ def test_available_set_for_dep
+ util_setup_gems
+ p1a, = util_gem 'a', '10.a'
+ util_setup_spec_fetcher p1a, @a1, @a1_pre
+
+ inst = Gem::DependencyInstaller.new
+
+ dep = Gem::Dependency.new 'a', Gem::Requirement.default
+
+ available = inst.available_set_for dep, Gem::Requirement.default
+
+ assert_equal %w[a-1], available.set.map { |s| s.spec.full_name }
+ end
+
+ def test_available_set_for_dep_prerelease
+ util_setup_gems
+ p1a, = util_gem 'a', '10.a'
+ util_setup_spec_fetcher p1a, @a1, @a1_pre
+
+ inst = Gem::DependencyInstaller.new :prerelease => true
+
+ dep = Gem::Dependency.new 'a', Gem::Requirement.default
+ dep.prerelease = true
+
+ available = inst.available_set_for dep, Gem::Requirement.default
+
+ assert_equal %w[a-10.a],
+ available.sorted.map { |s| s.spec.full_name }
+ end
+
+ def test_install
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'a'
+ end
+
+ assert_equal %w[a-1], Gem::Specification.map(&:full_name)
+ assert_equal [@a1], inst.installed_gems
+ end
+
+ def test_install_prerelease
+ util_setup_gems
+
+ p1a, gem = util_gem 'a', '10.a'
+
+ util_setup_spec_fetcher(p1a, @a1, @a1_pre)
+ util_clear_gems
+
+ p1a_data = Gem.read_binary(gem)
+
+ @fetcher.data['http://gems.example.com/gems/a-10.a.gem'] = p1a_data
+
+ dep = Gem::Dependency.new "a"
+ inst = Gem::DependencyInstaller.new :prerelease => true
+ inst.install dep
+
+ assert_equal %w[a-10.a], Gem::Specification.map(&:full_name)
+ assert_equal [p1a], inst.installed_gems
+ end
+
+ def test_install_prerelease_bug_990
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '1.b' do |s|
+ s.add_dependency 'b', '~> 1.a'
+ end
+
+ fetcher.gem 'b', '1.b' do |s|
+ s.add_dependency 'c', '>= 1'
+ end
+
+ fetcher.gem 'c', '1.1.b'
+ end
+
+ dep = Gem::Dependency.new 'a'
+
+ inst = Gem::DependencyInstaller.new :prerelease => true
+ inst.install dep
+
+ assert_equal %w[a-1.b b-1.b c-1.1.b], Gem::Specification.map(&:full_name)
+ end
+
+ def test_install_when_only_prerelease
+ p1a, gem = util_gem 'p', '1.a'
+
+ util_setup_spec_fetcher(p1a)
+ util_clear_gems
+
+ p1a_data = Gem.read_binary(gem)
+
+ @fetcher.data['http://gems.example.com/gems/p-1.a.gem'] = p1a_data
+
+ dep = Gem::Dependency.new "p"
+ inst = Gem::DependencyInstaller.new
+ assert_raises Gem::UnsatisfiableDependencyError do
+ inst.install dep
+ end
+
+ assert_equal %w[], Gem::Specification.map(&:full_name)
+ assert_equal [], inst.installed_gems
+ end
+
+ def test_install_prerelease_skipped_when_normal_ver
+ util_setup_gems
+
+ util_setup_spec_fetcher(@a1, @a1_pre)
+ util_clear_gems
+
+ p1a_data = Gem.read_binary(@a1_gem)
+
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = p1a_data
+
+ dep = Gem::Dependency.new "a"
+ inst = Gem::DependencyInstaller.new :prerelease => true
+ inst.install dep
+
+ assert_equal %w[a-1], Gem::Specification.map(&:full_name)
+ assert_equal [@a1], inst.installed_gems
+ end
+
+ def test_install_all_dependencies
+ util_setup_gems
+
+ _, e1_gem = util_gem 'e', '1' do |s|
+ s.add_dependency 'b'
+ end
+
+ util_clear_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ FileUtils.mv e1_gem, @tempdir
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst.install 'b'
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name },
+ 'sanity check'
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'e'
+ end
+
+ assert_equal %w[a-1 e-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_cache_dir
+ util_setup_gems
+
+ dir = "dir"
+ Dir.mkdir dir
+ FileUtils.mv @a1_gem, dir
+ FileUtils.mv @b1_gem, dir
+ inst = nil
+
+ Dir.chdir dir do
+ inst = Gem::DependencyInstaller.new :cache_dir => @tempdir
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
+
+ assert File.exist? File.join(@gemhome, "cache", @a1.file_name)
+ assert File.exist? File.join(@gemhome, "cache", @b1.file_name)
+ end
+
+ def test_install_dependencies_satisfied
+ util_setup_gems
+
+ a2, a2_gem = util_gem 'a', '2'
+
+ FileUtils.rm_rf File.join(@gemhome, 'gems')
+
+ Gem::Specification.reset
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv a2_gem, @tempdir # not in index
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'a', req("= 2")
+ end
+
+ assert_equal %w[a-2], inst.installed_gems.map { |s| s.full_name },
+ 'sanity check'
+
+ FileUtils.rm File.join(@tempdir, a2.file_name)
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-2 b-1], Gem::Specification.map(&:full_name)
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ # This asserts that if a gem's dependency is satisfied by an
+ # already installed gem, RubyGems doesn't installed a newer
+ # version
+ def test_install_doesnt_upgrade_installed_depedencies
+ util_setup_gems
+
+ a2, a2_gem = util_gem 'a', '2'
+ a3, a3_gem = util_gem 'a', '3'
+
+ util_setup_spec_fetcher @a1, a3, @b1
+
+ FileUtils.rm_rf File.join(@gemhome, 'gems')
+
+ Gem::Specification.reset
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv a2_gem, @tempdir # not in index
+ FileUtils.mv @b1_gem, @tempdir
+ FileUtils.mv a3_gem, @tempdir
+
+ Dir.chdir @tempdir do
+ Gem::DependencyInstaller.new.install 'a', req("= 2")
+ end
+
+ FileUtils.rm File.join(@tempdir, a2.file_name)
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-2 b-1], Gem::Specification.map(&:full_name)
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_dependency
+ util_setup_gems
+
+ done_installing_ran = false
+ inst = nil
+
+ Gem.done_installing do |installer, specs|
+ done_installing_ran = true
+ refute_nil installer
+ assert_equal [@a1, @b1], specs
+ end
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new(:build_docs_in_background => false)
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
+
+ assert done_installing_ran, 'post installs hook was not run'
+ end
+
+ def test_install_dependency_development
+ util_setup_gems
+
+ @aa1, @aa1_gem = util_gem 'aa', '1'
+
+ util_reset_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @aa1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new(:development => true)
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-1 aa-1 b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_dependency_development_deep
+ util_setup_gems
+
+ @aa1, @aa1_gem = util_gem 'aa', '1'
+
+ util_reset_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @aa1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ FileUtils.mv @c1_gem, @tempdir
+ FileUtils.mv @d1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new(:development => true)
+ inst.install 'd'
+ end
+
+ assert_equal %w[a-1 aa-1 b-1 c-1 d-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_dependency_development_shallow
+ util_setup_gems
+
+ @aa1, @aa1_gem = util_gem 'aa', '1'
+
+ util_reset_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @aa1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ FileUtils.mv @c1_gem, @tempdir
+ FileUtils.mv @d1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new(:development => true, :dev_shallow => true)
+ inst.install 'd'
+ end
+
+ assert_equal %w[c-1 d-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_dependency_existing
+ util_setup_gems
+
+ Gem::Installer.new(@a1_gem).install
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'b'
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_dependency_existing_extension
+ extconf_rb = File.join @gemhome, 'gems', 'e-1', 'extconf.rb'
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |io|
+ io.write <<-EXTCONF_RB
+ require 'mkmf'
+ create_makefile 'e'
+ EXTCONF_RB
+ end
+
+ e1 = new_spec 'e', '1', nil, 'extconf.rb' do |s|
+ s.extensions << 'extconf.rb'
+ end
+ e1_gem = File.join @tempdir, 'gems', "#{e1.full_name}.gem"
+
+ _, f1_gem = util_gem 'f', '1', 'e' => nil
+
+ Gem::Installer.new(e1_gem).install
+ FileUtils.rm_r e1.extension_dir
+
+ FileUtils.mv e1_gem, @tempdir
+ FileUtils.mv f1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'f'
+ end
+
+ assert_equal %w[f-1], inst.installed_gems.map { |s| s.full_name }
+
+ assert_path_exists e1.extension_dir
+ end
+
+ def test_install_dependency_old
+ _, e1_gem = util_gem 'e', '1'
+ _, f1_gem = util_gem 'f', '1', 'e' => nil
+ _, f2_gem = util_gem 'f', '2'
+
+ FileUtils.mv e1_gem, @tempdir
+ FileUtils.mv f1_gem, @tempdir
+ FileUtils.mv f2_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'f'
+ end
+
+ assert_equal %w[f-2], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_local
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'a-1.gem'
+ end
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_local_prerelease
+ util_setup_gems
+
+ FileUtils.mv @a1_pre_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'a-1.a.gem'
+ end
+
+ assert_equal %w[a-1.a], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_local_dependency
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'b-1.gem'
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_local_dependency_installed
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ Gem::Installer.new('a-1.gem').install
+
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'b-1.gem'
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_local_subdir
+ util_setup_gems
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'gems/a-1.gem'
+ end
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_minimal_deps
+ util_setup_gems
+
+ _, e1_gem = util_gem 'e', '1' do |s|
+ s.add_dependency 'b'
+ end
+
+ _, b2_gem = util_gem 'b', '2' do |s|
+ s.add_dependency 'a'
+ end
+
+ util_clear_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ FileUtils.mv b2_gem, @tempdir
+ FileUtils.mv e1_gem, @tempdir
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst.install 'b', req('= 1')
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name },
+ 'sanity check'
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :minimal_deps => true
+ inst.install 'e'
+ end
+
+ assert_equal %w[a-1 e-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_no_document
+ util_setup_gems
+
+ done_installing_called = false
+
+ Gem.done_installing do |dep_installer, specs|
+ done_installing_called = true
+ assert_empty dep_installer.document
+ end
+
+ inst = Gem::DependencyInstaller.new :domain => :local, :document => []
+
+ inst.install @a1_gem
+
+ assert done_installing_called
+ end
+
+ def test_install_env_shebang
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :env_shebang => true, :wrappers => true, :format_executable => false
+ inst.install 'a'
+ end
+
+ env = "/\\S+/env" unless Gem.win_platform?
+
+ assert_match %r|\A#!#{env} #{RbConfig::CONFIG['ruby_install_name']}\n|,
+ File.read(File.join(@gemhome, 'bin', 'a_bin'))
+ end
+
+ def test_install_force
+ util_setup_gems
+
+ FileUtils.mv @b1_gem, @tempdir
+ si = util_setup_spec_fetcher @b1
+ @fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :force => true
+ inst.install 'b'
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_build_args
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ inst = nil
+ build_args = %w[--a --b="c"]
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new(
+ :build_args => build_args)
+ inst.install 'a'
+ end
+
+ assert_equal build_args.join("\n"), File.read(inst.installed_gems.first.build_info_file).strip
+ end
+
+ def test_install_ignore_dependencies
+ util_setup_gems
+
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst.install 'b'
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_install_dir
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+
+ inst = Gem::Installer.new @a1.file_name
+ inst.install
+
+ gemhome2 = File.join @tempdir, 'gemhome2'
+ Dir.mkdir gemhome2
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :install_dir => gemhome2
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
+
+ assert File.exist?(File.join(gemhome2, 'specifications', @a1.spec_name))
+ assert File.exist?(File.join(gemhome2, 'cache', @a1.file_name))
+ end
+
+ def test_install_domain_both
+ util_setup_gems
+
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
+
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :both
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
+ a1, b1 = inst.installed_gems
+
+ assert_equal a1.spec_file, a1.loaded_from
+ assert_equal b1.spec_file, b1.loaded_from
+ end
+
+ def test_install_domain_both_no_network
+ util_setup_gems
+
+ @fetcher.data["http://gems.example.com/gems/Marshal.#{@marshal_version}"] =
+ proc do
+ raise Gem::RemoteFetcher::FetchError
+ end
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :both
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_domain_local
+ util_setup_gems
+
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ e = assert_raises Gem::UnsatisfiableDependencyError do
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'b'
+ end
+
+ expected = "Unable to resolve dependency: 'b (>= 0)' requires 'a (>= 0)'"
+ assert_equal expected, e.message
+ end
+
+ assert_equal [], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_domain_remote
+ util_setup_gems
+
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
+
+ inst = Gem::DependencyInstaller.new :domain => :remote
+ inst.install 'a'
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_dual_repository
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ gemhome2 = "#{@gemhome}2"
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :install_dir => gemhome2
+ inst.install 'a'
+ end
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name },
+ 'sanity check'
+
+ ENV['GEM_HOME'] = @gemhome
+ ENV['GEM_PATH'] = [@gemhome, gemhome2].join File::PATH_SEPARATOR
+ Gem.clear_paths
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'b'
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_reinstall
+ util_setup_gems
+
+ Gem::Installer.new(@a1_gem).install
+ FileUtils.mv @a1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new
+ inst.install 'a'
+ end
+
+ assert_equal %w[a-1], Gem::Specification.map(&:full_name)
+ assert_equal %w[a-1], inst.installed_gems.map(&:full_name)
+ end
+
+ def test_install_remote
+ util_setup_gems
+
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
+
+ inst = Gem::DependencyInstaller.new
+
+ Dir.chdir @tempdir do
+ inst.install 'a'
+ end
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_remote_dep
+ util_setup_gems
+
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
+
+ inst = Gem::DependencyInstaller.new
+
+ Dir.chdir @tempdir do
+ dep = Gem::Dependency.new @a1.name, @a1.version
+ inst.install dep
+ end
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_remote_platform_newer
+ util_setup_gems
+
+ a2_o, a2_o_gem = util_gem 'a', '2' do |s|
+ s.platform = Gem::Platform.new %w[cpu other_platform 1]
+ end
+
+ si = util_setup_spec_fetcher @a1, a2_o
+
+ util_clear_gems
+
+ @fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
+
+ a1_data = nil
+ a2_o_data = nil
+
+ File.open @a1_gem, 'rb' do |fp| a1_data = fp.read end
+ File.open a2_o_gem, 'rb' do |fp| a2_o_data = fp.read end
+
+ @fetcher.data["http://gems.example.com/gems/#{@a1.file_name}"] =
+ a1_data
+ @fetcher.data["http://gems.example.com/gems/#{a2_o.file_name}"] =
+ a2_o_data
+
+ inst = Gem::DependencyInstaller.new :domain => :remote
+ inst.install 'a'
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_platform_is_ignored_when_a_file_is_specified
+ _, a_gem = util_gem 'a', '1' do |s|
+ s.platform = Gem::Platform.new %w[cpu other_platform 1]
+ end
+
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install a_gem
+
+ assert_equal %w[a-1-cpu-other_platform-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ if defined? OpenSSL then
+ def test_install_security_policy
+ util_setup_gems
+
+ data = File.open(@a1_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = data
+
+ data = File.open(@b1_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/b-1.gem'] = data
+
+ policy = Gem::Security::HighSecurity
+ inst = Gem::DependencyInstaller.new :security_policy => policy
+
+ e = assert_raises Gem::Security::Exception do
+ inst.install 'b'
+ end
+
+ assert_equal 'unsigned gems are not allowed by the High Security policy',
+ e.message
+
+ assert_equal %w[], inst.installed_gems.map { |s| s.full_name }
+ end
+ end
+
+ # Wrappers don't work on mswin
+ unless win_platform? then
+ def test_install_no_wrappers
+ util_setup_gems
+
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = read_binary(@a1_gem)
+
+ inst = Gem::DependencyInstaller.new :wrappers => false, :format_executable => false
+ inst.install 'a'
+
+ refute_match(%r|This file was generated by RubyGems.|,
+ File.read(File.join(@gemhome, 'bin', 'a_bin')))
+ end
+ end
+
+ def test_install_version
+ util_setup_d
+
+ data = File.open(@d2_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/d-2.gem'] = data
+
+ data = File.open(@d1_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/d-1.gem'] = data
+
+ inst = Gem::DependencyInstaller.new
+
+ inst.install 'd', '= 1'
+
+ assert_equal %w[d-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_version_default
+ util_setup_d
+
+ data = File.open(@d2_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/d-2.gem'] = data
+
+ data = File.open(@d1_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/d-1.gem'] = data
+
+ inst = Gem::DependencyInstaller.new
+ inst.install 'd'
+
+ assert_equal %w[d-2], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_find_gems_gems_with_sources
+ util_setup_gems
+
+ inst = Gem::DependencyInstaller.new
+ dep = Gem::Dependency.new 'b', '>= 0'
+
+ Gem::Specification.reset
+
+ set = inst.find_gems_with_sources(dep)
+
+ assert_kind_of Gem::AvailableSet, set
+
+ s = set.set.first
+
+ assert_equal @b1, s.spec
+ assert_equal Gem::Source.new(@gem_repo), s.source
+ end
+
+ def test_find_spec_by_name_and_version_wildcard
+ util_gem 'a', 1
+ FileUtils.mv 'gems/a-1.gem', @tempdir
+
+ FileUtils.touch 'rdoc.gem'
+
+ inst = Gem::DependencyInstaller.new
+
+ available = inst.find_spec_by_name_and_version('*.gem')
+
+ assert_equal %w[a-1], available.each_spec.map { |spec| spec.full_name }
+ end
+
+ def test_find_spec_by_name_and_version_wildcard_bad_gem
+ FileUtils.touch 'rdoc.gem'
+
+ inst = Gem::DependencyInstaller.new
+
+ assert_raises Gem::Package::FormatError do
+ inst.find_spec_by_name_and_version '*.gem'
+ end
+ end
+
+ def test_find_spec_by_name_and_version_bad_gem
+ FileUtils.touch 'rdoc.gem'
+
+ inst = Gem::DependencyInstaller.new
+
+ e = assert_raises Gem::Package::FormatError do
+ inst.find_spec_by_name_and_version 'rdoc.gem'
+ end
+
+ full_path = File.join @tempdir, 'rdoc.gem'
+ assert_equal "package metadata is missing in #{full_path}", e.message
+ end
+
+ def test_find_spec_by_name_and_version_directory
+ Dir.mkdir 'rdoc'
+
+ inst = Gem::DependencyInstaller.new
+
+ e = assert_raises Gem::SpecificGemNotFoundException do
+ inst.find_spec_by_name_and_version 'rdoc'
+ end
+
+ assert_equal "Could not find a valid gem 'rdoc' (>= 0) " +
+ "locally or in a repository",
+ e.message
+ end
+
+ def test_find_spec_by_name_and_version_file
+ FileUtils.touch 'rdoc'
+
+ inst = Gem::DependencyInstaller.new
+
+ e = assert_raises Gem::SpecificGemNotFoundException do
+ inst.find_spec_by_name_and_version 'rdoc'
+ end
+
+ assert_equal "Could not find a valid gem 'rdoc' (>= 0) " +
+ "locally or in a repository",
+ e.message
+ end
+
+ def test_find_gems_with_sources_local
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ inst = Gem::DependencyInstaller.new
+ dep = Gem::Dependency.new 'a', '>= 0'
+ set = nil
+
+ Dir.chdir @tempdir do
+ set = inst.find_gems_with_sources dep
+ end
+
+ gems = set.sorted
+
+ assert_equal 2, gems.length
+
+ remote, local = gems
+
+ assert_equal 'a-1', local.spec.full_name, 'local spec'
+ assert_equal File.join(@tempdir, @a1.file_name),
+ local.source.download(local.spec), 'local path'
+
+ assert_equal 'a-1', remote.spec.full_name, 'remote spec'
+ assert_equal Gem::Source.new(@gem_repo), remote.source, 'remote path'
+
+ end
+
+ def test_find_gems_with_sources_prerelease
+ util_setup_gems
+
+ installer = Gem::DependencyInstaller.new
+
+ dependency = Gem::Dependency.new('a', Gem::Requirement.default)
+
+ releases =
+ installer.find_gems_with_sources(dependency).all_specs
+
+ assert releases.any? { |s| s.name == 'a' and s.version.to_s == '1' }
+ refute releases.any? { |s| s.name == 'a' and s.version.to_s == '1.a' }
+
+ dependency.prerelease = true
+
+ prereleases =
+ installer.find_gems_with_sources(dependency).all_specs
+
+ assert_equal [@a1_pre, @a1], prereleases
+ end
+
+ def test_find_gems_with_sources_with_bad_source
+ Gem.sources.replace ["http://not-there.nothing"]
+
+ installer = Gem::DependencyInstaller.new
+
+ dep = Gem::Dependency.new('a')
+
+ out = installer.find_gems_with_sources(dep)
+
+ assert out.empty?
+ assert_kind_of Gem::SourceFetchProblem, installer.errors.first
+ end
+
+ def test_resolve_dependencies
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+
+ inst = Gem::DependencyInstaller.new
+ request_set = inst.resolve_dependencies 'b', req('>= 0')
+
+ requests = request_set.sorted_requests.map { |req| req.full_name }
+
+ assert_equal %w[a-1 b-1], requests
+ end
+
+ def test_resolve_dependencies_ignore_dependencies
+ util_setup_gems
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+
+ inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ request_set = inst.resolve_dependencies 'b', req('>= 0')
+
+ requests = request_set.sorted_requests.map { |req| req.full_name }
+
+ assert request_set.ignore_dependencies
+
+ assert_equal %w[b-1], requests
+ end
+
+ def test_resolve_dependencies_local
+ util_setup_gems
+
+ @a2, @a2_gem = util_gem 'a', '2'
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @a2_gem, @tempdir
+
+ inst = Gem::DependencyInstaller.new
+ request_set = inst.resolve_dependencies 'a-1.gem', req('>= 0')
+
+ requests = request_set.sorted_requests.map { |req| req.full_name }
+
+ assert_equal %w[a-1], requests
+ end
+
+ def util_write_a1_bin
+ write_file File.join('gems', 'a-1', 'bin', 'a_bin') do |fp|
+ fp.puts "#!/usr/bin/ruby"
+ end
+ end
+
+ def util_setup_c1_pre
+ @c1_pre, @c1_pre_gem = util_spec 'c', '1.a' do |s|
+ s.add_dependency 'a', '1.a'
+ s.add_dependency 'b', '1'
+ end
+
+ util_reset_gems
+ end
+
+ def util_setup_d
+ @d1, @d1_gem = util_gem 'd', '1'
+ @d2, @d2_gem = util_gem 'd', '2'
+
+ util_reset_gems
+ end
+
+ def util_setup_wxyz
+ @x1_m, @x1_m_gem = util_spec 'x', '1' do |s|
+ s.platform = Gem::Platform.new %w[cpu my_platform 1]
+ end
+
+ @x1_o, @x1_o_gem = util_spec 'x', '1' do |s|
+ s.platform = Gem::Platform.new %w[cpu other_platform 1]
+ end
+
+ @w1, @w1_gem = util_spec 'w', '1', 'x' => nil
+
+ @y1, @y1_gem = util_spec 'y', '1'
+ @y1_1_p, @y1_1_p_gem = util_spec 'y', '1.1' do |s|
+ s.platform = Gem::Platform.new %w[cpu my_platform 1]
+ end
+
+ @z1, @z1_gem = util_spec 'z', '1', 'y' => nil
+
+ util_reset_gems
+ end
+
+ def util_reset_gems
+ @a1 ||= nil
+ @b1 ||= nil
+ @a1_pre ||= nil
+ @c1_pre ||= nil
+ @d1 ||= nil
+ @d2 ||= nil
+ @w1 ||= nil
+ @x1_m ||= nil
+ @x1_o ||= nil
+ @y1 ||= nil
+ @y1_1_p ||= nil
+ @z1 ||= nil
+
+ util_setup_spec_fetcher(*[@a1, @a1_pre, @b1, @c1_pre,
+ @d1, @d2, @x1_m, @x1_o, @w1, @y1,
+ @y1_1_p, @z1].compact)
+
+ util_clear_gems
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_dependency_list.rb b/jni/ruby/test/rubygems/test_gem_dependency_list.rb
new file mode 100644
index 0000000..d25359e
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_dependency_list.rb
@@ -0,0 +1,259 @@
+require 'rubygems/test_case'
+require 'rubygems/dependency_list'
+
+class TestGemDependencyList < Gem::TestCase
+
+ def setup
+ super
+
+ util_clear_gems
+
+ @deplist = Gem::DependencyList.new
+
+ # TODO: switch to new_spec
+ @a1 = util_spec 'a', '1'
+ @a2 = util_spec 'a', '2'
+ @a3 = util_spec 'a', '3'
+
+ @b1 = util_spec 'b', '1' do |s| s.add_dependency 'a', '>= 1' end
+ @b2 = util_spec 'b', '2' do |s| s.add_dependency 'a', '>= 1' end
+
+ @c1 = util_spec 'c', '1' do |s| s.add_dependency 'b', '>= 1' end
+ @c2 = util_spec 'c', '2'
+
+ @d1 = util_spec 'd', '1' do |s| s.add_dependency 'c', '>= 1' end
+ end
+
+ def test_active_count
+ assert_equal 0, @deplist.send(:active_count, [], {})
+ assert_equal 1, @deplist.send(:active_count, [@a1], {})
+ assert_equal 0, @deplist.send(:active_count, [@a1],
+ { @a1.full_name => true })
+ end
+
+ def test_add
+ assert_equal [], @deplist.dependency_order
+
+ @deplist.add @a1, @b2
+
+ assert_equal [@b2, @a1], @deplist.dependency_order
+ end
+
+ def test_dependency_order
+ @deplist.add @a1, @b1, @c1, @d1
+
+ order = @deplist.dependency_order
+
+ assert_equal %w[d-1 c-1 b-1 a-1], order.map { |s| s.full_name }
+ end
+
+ def test_dependency_order_circle
+ @a1.add_dependency 'c', '>= 1'
+ @deplist.add @a1, @b1, @c1
+
+ order = @deplist.dependency_order
+
+ assert_equal %w[b-1 c-1 a-1], order.map { |s| s.full_name }
+ end
+
+ def test_dependency_order_development
+ e1 = util_spec 'e', '1'
+ f1 = util_spec 'f', '1'
+ g1 = util_spec 'g', '1'
+
+ @a1.add_dependency 'e'
+ @a1.add_dependency 'f'
+ @a1.add_dependency 'g'
+ g1.add_development_dependency 'a'
+
+ deplist = Gem::DependencyList.new true
+ deplist.add @a1, e1, f1, g1
+
+ order = deplist.dependency_order
+
+ assert_equal %w[g-1 a-1 f-1 e-1], order.map { |s| s.full_name },
+ 'development on'
+
+ deplist2 = Gem::DependencyList.new
+ deplist2.add @a1, e1, f1, g1
+
+ order = deplist2.dependency_order
+
+ assert_equal %w[a-1 g-1 f-1 e-1], order.map { |s| s.full_name },
+ 'development off'
+ end
+
+ def test_dependency_order_diamond
+ util_diamond
+ e1 = util_spec 'e', '1'
+ @deplist.add e1
+ @a1.add_dependency 'e', '>= 1'
+
+ order = @deplist.dependency_order
+
+ assert_equal %w[d-1 c-2 b-1 a-2 e-1], order.map { |s| s.full_name },
+ 'deps of trimmed specs not included'
+ end
+
+ def test_dependency_order_no_dependencies
+ @deplist.add @a1, @c2
+
+ order = @deplist.dependency_order
+
+ assert_equal %w[c-2 a-1], order.map { |s| s.full_name }
+ end
+
+ def test_find_name
+ @deplist.add @a1, @b2
+
+ assert_equal "a-1", @deplist.find_name("a-1").full_name
+ assert_equal "b-2", @deplist.find_name("b-2").full_name
+
+ assert_nil @deplist.find_name("c-2")
+ end
+
+ def test_ok_eh
+ util_clear_gems
+
+ assert @deplist.ok?, 'no dependencies'
+
+ @deplist.add @b2
+
+ refute @deplist.ok?, 'unsatisfied dependency'
+
+ @deplist.add @a1
+
+ assert @deplist.ok?, 'satisfied dependency'
+ end
+
+ def test_why_not_ok_eh
+ util_clear_gems
+
+ assert_equal({}, @deplist.why_not_ok?)
+
+ @deplist.add @b2
+
+ exp = {
+ "b" => [
+ Gem::Dependency.new("a", ">= 1")
+ ]
+ }
+
+ assert_equal exp, @deplist.why_not_ok?
+ end
+
+ def test_why_not_ok_eh_old_dependency
+ a = new_spec 'a', '1',
+ 'b' => '~> 1.0'
+
+ b0 = new_spec 'b', '1.0',
+ 'd' => '>= 0'
+
+ b1 = new_spec 'b', '1.1'
+
+ util_clear_gems
+
+ @deplist.clear
+
+ @deplist.add a, b0, b1
+
+ assert_equal({}, @deplist.why_not_ok?)
+ end
+
+ def test_ok_eh_mismatch
+ a1 = util_spec 'a', '1'
+ a2 = util_spec 'a', '2'
+
+ b = util_spec 'b', '1' do |s| s.add_dependency 'a', '= 1' end
+ c = util_spec 'c', '1' do |s| s.add_dependency 'a', '= 2' end
+
+ d = util_spec 'd', '1' do |s|
+ s.add_dependency 'b'
+ s.add_dependency 'c'
+ end
+
+ @deplist.add a1, a2, b, c, d
+
+ assert @deplist.ok?, 'this will break on require'
+ end
+
+ def test_ok_eh_redundant
+ @deplist.add @a1, @a3, @b2
+
+ @deplist.remove_by_name("a-1")
+
+ assert @deplist.ok?
+ end
+
+ def test_ok_to_remove_eh
+ @deplist.add @a1
+
+ assert @deplist.ok_to_remove?("a-1")
+
+ @deplist.add @b2
+
+ refute @deplist.ok_to_remove?("a-1")
+
+ @deplist.add @a2
+
+ assert @deplist.ok_to_remove?("a-1")
+ assert @deplist.ok_to_remove?("a-2")
+ assert @deplist.ok_to_remove?("b-2")
+ end
+
+ def test_ok_to_remove_eh_after_sibling_removed
+ @deplist.add @a1, @a2, @b2
+
+ assert @deplist.ok_to_remove?("a-1")
+ assert @deplist.ok_to_remove?("a-2")
+
+ @deplist.remove_by_name("a-1")
+
+ refute @deplist.ok_to_remove?("a-2")
+ end
+
+ def test_remove_by_name
+ util_clear_gems
+
+ @deplist.add @a1, @b2
+
+ @deplist.remove_by_name "a-1"
+
+ refute @deplist.ok?
+ end
+
+ def test_tsort_each_node
+ util_diamond
+
+ order = %w[a-1 a-2 b-1 c-2 d-1]
+
+ @deplist.tsort_each_node do |node|
+ assert_equal order.shift, node.full_name
+ end
+
+ assert_empty order
+ end
+
+ def test_tsort_each_child
+ util_diamond
+
+ order = %w[a-2]
+
+ @deplist.tsort_each_child(@b1) do |node|
+ assert_equal order.shift, node.full_name
+ end
+
+ assert_empty order
+ end
+
+ # d1 -> b1 -> a1
+ # d1 -> c2 -> a2
+ def util_diamond
+ @c2.add_dependency 'a', '>= 2'
+ @d1.add_dependency 'b'
+
+ @deplist.add @a1, @a2, @b1, @c2, @d1
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_dependency_resolution_error.rb b/jni/ruby/test/rubygems/test_gem_dependency_resolution_error.rb
new file mode 100644
index 0000000..0442082
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_dependency_resolution_error.rb
@@ -0,0 +1,28 @@
+require 'rubygems/test_case'
+
+class TestGemDependencyResolutionError < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::Resolver
+
+ @spec = util_spec 'a', 2
+
+ @a1_req = @DR::DependencyRequest.new dep('a', '= 1'), nil
+ @a2_req = @DR::DependencyRequest.new dep('a', '= 2'), nil
+
+ @activated = @DR::ActivationRequest.new @spec, @a2_req
+
+ @conflict = @DR::Conflict.new @a1_req, @activated
+
+ @error = Gem::DependencyResolutionError.new @conflict
+ end
+
+ def test_message
+ assert_match %r%^conflicting dependencies a \(= 1\) and a \(= 2\)$%,
+ @error.message
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_doctor.rb b/jni/ruby/test/rubygems/test_gem_doctor.rb
new file mode 100644
index 0000000..f4d4659
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_doctor.rb
@@ -0,0 +1,168 @@
+require 'rubygems/test_case'
+require 'rubygems/doctor'
+
+class TestGemDoctor < Gem::TestCase
+
+ def gem name
+ spec = quick_gem name do |gem|
+ gem.files = %W[lib/#{name}.rb Rakefile]
+ end
+
+ write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
+ write_file File.join(*%W[gems #{spec.full_name} Rakefile])
+
+ spec
+ end
+
+ def test_doctor
+ a = gem 'a'
+ b = gem 'b'
+ c = gem 'c'
+
+ Gem.use_paths @userhome, @gemhome
+
+ FileUtils.rm b.spec_file
+
+ open c.spec_file, 'w' do |io|
+ io.write 'this will raise an exception when evaluated.'
+ end
+
+ assert_path_exists File.join(a.gem_dir, 'Rakefile')
+ assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb')
+
+ assert_path_exists b.gem_dir
+ refute_path_exists b.spec_file
+
+ assert_path_exists c.gem_dir
+ assert_path_exists c.spec_file
+
+ doctor = Gem::Doctor.new @gemhome
+
+ capture_io do
+ use_ui @ui do
+ doctor.doctor
+ end
+ end
+
+ assert_path_exists File.join(a.gem_dir, 'Rakefile')
+ assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb')
+
+ refute_path_exists b.gem_dir
+ refute_path_exists b.spec_file
+
+ refute_path_exists c.gem_dir
+ refute_path_exists c.spec_file
+
+ expected = <<-OUTPUT
+Checking #{@gemhome}
+Removed file specifications/c-2.gemspec
+Removed directory gems/b-2
+Removed directory gems/c-2
+
+ OUTPUT
+
+ assert_equal expected, @ui.output
+
+ assert_equal Gem.dir, @userhome
+ assert_equal Gem.path, [@gemhome, @userhome]
+ end
+
+ def test_doctor_dry_run
+ a = gem 'a'
+ b = gem 'b'
+ c = gem 'c'
+
+ Gem.use_paths @userhome, @gemhome
+
+ FileUtils.rm b.spec_file
+
+ open c.spec_file, 'w' do |io|
+ io.write 'this will raise an exception when evaluated.'
+ end
+
+ assert_path_exists File.join(a.gem_dir, 'Rakefile')
+ assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb')
+
+ assert_path_exists b.gem_dir
+ refute_path_exists b.spec_file
+
+ assert_path_exists c.gem_dir
+ assert_path_exists c.spec_file
+
+ doctor = Gem::Doctor.new @gemhome, true
+
+ capture_io do
+ use_ui @ui do
+ doctor.doctor
+ end
+ end
+
+ assert_path_exists File.join(a.gem_dir, 'Rakefile')
+ assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb')
+
+ assert_path_exists b.gem_dir
+ refute_path_exists b.spec_file
+
+ assert_path_exists c.gem_dir
+ assert_path_exists c.spec_file
+
+ expected = <<-OUTPUT
+Checking #{@gemhome}
+Extra file specifications/c-2.gemspec
+Extra directory gems/b-2
+Extra directory gems/c-2
+
+ OUTPUT
+
+ assert_equal expected, @ui.output
+
+ assert_equal Gem.dir, @userhome
+ assert_equal Gem.path, [@gemhome, @userhome]
+ end
+
+ def test_doctor_non_gem_home
+ other_dir = File.join @tempdir, 'other', 'dir'
+
+ FileUtils.mkdir_p other_dir
+
+ doctor = Gem::Doctor.new @tempdir
+
+ capture_io do
+ use_ui @ui do
+ doctor.doctor
+ end
+ end
+
+ assert_path_exists other_dir
+
+ expected = <<-OUTPUT
+Checking #{@tempdir}
+This directory does not appear to be a RubyGems repository, skipping
+
+ OUTPUT
+
+ assert_equal expected, @ui.output
+ end
+
+ def test_doctor_child_missing
+ doctor = Gem::Doctor.new @gemhome
+
+ doctor.doctor_child 'missing', ''
+
+ assert true # count
+ end
+
+ def test_gem_repository_eh
+ doctor = Gem::Doctor.new @gemhome
+
+ refute doctor.gem_repository?, 'no gems installed'
+
+ util_spec 'a'
+
+ doctor = Gem::Doctor.new @gemhome
+
+ assert doctor.gem_repository?, 'gems installed'
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_ext_builder.rb b/jni/ruby/test/rubygems/test_gem_ext_builder.rb
new file mode 100644
index 0000000..5607096
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_ext_builder.rb
@@ -0,0 +1,334 @@
+require 'rubygems/test_case'
+require 'rubygems/ext'
+require 'rubygems/installer'
+
+class TestGemExtBuilder < Gem::TestCase
+
+ def setup
+ super
+
+ @ext = File.join @tempdir, 'ext'
+ @dest_path = File.join @tempdir, 'prefix'
+
+ FileUtils.mkdir_p @ext
+ FileUtils.mkdir_p @dest_path
+
+ @orig_DESTDIR = ENV['DESTDIR']
+
+ @spec = util_spec 'a'
+
+ @builder = Gem::Ext::Builder.new @spec, ''
+ end
+
+ def teardown
+ ENV['DESTDIR'] = @orig_DESTDIR
+
+ super
+ end
+
+ def test_class_make
+ ENV['DESTDIR'] = 'destination'
+ results = []
+
+ Dir.chdir @ext do
+ open 'Makefile', 'w' do |io|
+ io.puts <<-MAKEFILE
+all:
+\t@#{Gem.ruby} -e "puts %Q{all: \#{ENV['DESTDIR']}}"
+
+clean:
+\t@#{Gem.ruby} -e "puts %Q{clean: \#{ENV['DESTDIR']}}"
+
+install:
+\t@#{Gem.ruby} -e "puts %Q{install: \#{ENV['DESTDIR']}}"
+ MAKEFILE
+ end
+
+ Gem::Ext::Builder.make @dest_path, results
+ end
+
+ results = results.join "\n"
+
+ if RUBY_VERSION > '2.0' then
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
+ else
+ refute_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
+ refute_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
+ refute_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
+ end
+
+ if /nmake/ !~ results
+ assert_match %r%^clean: destination$%, results
+ assert_match %r%^all: destination$%, results
+ assert_match %r%^install: destination$%, results
+ end
+ end
+
+ def test_class_make_no_clean
+ ENV['DESTDIR'] = 'destination'
+ results = []
+
+ Dir.chdir @ext do
+ open 'Makefile', 'w' do |io|
+ io.puts <<-MAKEFILE
+all:
+\t@#{Gem.ruby} -e "puts %Q{all: \#{ENV['DESTDIR']}}"
+
+install:
+\t@#{Gem.ruby} -e "puts %Q{install: \#{ENV['DESTDIR']}}"
+ MAKEFILE
+ end
+
+ Gem::Ext::Builder.make @dest_path, results
+ end
+
+ results = results.join "\n"
+
+ if RUBY_VERSION > '2.0' then
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
+ assert_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
+ else
+ refute_match %r%"DESTDIR=#{ENV['DESTDIR']}" clean$%, results
+ refute_match %r%"DESTDIR=#{ENV['DESTDIR']}"$%, results
+ refute_match %r%"DESTDIR=#{ENV['DESTDIR']}" install$%, results
+ end
+ end
+
+ def test_build_extensions
+ @spec.extensions << 'ext/extconf.rb'
+
+ ext_dir = File.join @spec.gem_dir, 'ext'
+
+ FileUtils.mkdir_p ext_dir
+
+ extconf_rb = File.join ext_dir, 'extconf.rb'
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ require 'mkmf'
+
+ create_makefile 'a'
+ RUBY
+ end
+
+ ext_lib_dir = File.join ext_dir, 'lib'
+ FileUtils.mkdir ext_lib_dir
+ FileUtils.touch File.join ext_lib_dir, 'a.rb'
+ FileUtils.mkdir File.join ext_lib_dir, 'a'
+ FileUtils.touch File.join ext_lib_dir, 'a', 'b.rb'
+
+ use_ui @ui do
+ @builder.build_extensions
+ end
+
+ assert_path_exists @spec.extension_dir
+ assert_path_exists @spec.gem_build_complete_path
+ assert_path_exists File.join @spec.extension_dir, 'gem_make.out'
+ assert_path_exists File.join @spec.extension_dir, 'a.rb'
+ assert_path_exists File.join @spec.gem_dir, 'lib', 'a.rb'
+ assert_path_exists File.join @spec.gem_dir, 'lib', 'a', 'b.rb'
+ end
+
+ def test_build_extensions_with_gemhome_with_space
+ new_gemhome = File.join @tempdir, 'gem home'
+ File.rename(@gemhome, new_gemhome)
+ @gemhome = new_gemhome
+ Gem.use_paths(@gemhome)
+ @spec = util_spec 'a'
+ @builder = Gem::Ext::Builder.new @spec, ''
+
+ test_build_extensions
+ end
+
+ def test_build_extensions_install_ext_only
+ class << Gem
+ alias orig_install_extension_in_lib install_extension_in_lib
+
+ def Gem.install_extension_in_lib
+ false
+ end
+ end
+
+ @spec.extensions << 'ext/extconf.rb'
+
+ ext_dir = File.join @spec.gem_dir, 'ext'
+
+ FileUtils.mkdir_p ext_dir
+
+ extconf_rb = File.join ext_dir, 'extconf.rb'
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ require 'mkmf'
+
+ create_makefile 'a'
+ RUBY
+ end
+
+ ext_lib_dir = File.join ext_dir, 'lib'
+ FileUtils.mkdir ext_lib_dir
+ FileUtils.touch File.join ext_lib_dir, 'a.rb'
+ FileUtils.mkdir File.join ext_lib_dir, 'a'
+ FileUtils.touch File.join ext_lib_dir, 'a', 'b.rb'
+
+ use_ui @ui do
+ @builder.build_extensions
+ end
+
+ assert_path_exists @spec.extension_dir
+ assert_path_exists @spec.gem_build_complete_path
+ assert_path_exists File.join @spec.extension_dir, 'gem_make.out'
+ assert_path_exists File.join @spec.extension_dir, 'a.rb'
+ refute_path_exists File.join @spec.gem_dir, 'lib', 'a.rb'
+ refute_path_exists File.join @spec.gem_dir, 'lib', 'a', 'b.rb'
+ ensure
+ class << Gem
+ remove_method :install_extension_in_lib
+
+ alias install_extension_in_lib orig_install_extension_in_lib
+ end
+ end
+
+ def test_build_extensions_none
+ use_ui @ui do
+ @builder.build_extensions
+ end
+
+ assert_equal '', @ui.output
+ assert_equal '', @ui.error
+
+ refute_path_exists File.join @spec.extension_dir, 'gem_make.out'
+ end
+
+ def test_build_extensions_rebuild_failure
+ FileUtils.mkdir_p @spec.extension_dir
+ FileUtils.touch @spec.gem_build_complete_path
+
+ @spec.extensions << nil
+
+ assert_raises Gem::Ext::BuildError do
+ use_ui @ui do
+ @builder.build_extensions
+ end
+ end
+
+ refute_path_exists @spec.gem_build_complete_path
+ end
+
+ def test_build_extensions_extconf_bad
+ @spec.extensions << 'extconf.rb'
+
+ FileUtils.mkdir_p @spec.gem_dir
+
+ e = assert_raises Gem::Ext::BuildError do
+ use_ui @ui do
+ @builder.build_extensions
+ end
+ end
+
+ assert_match(/\AERROR: Failed to build gem native extension.$/, e.message)
+
+ assert_equal "Building native extensions. This could take a while...\n",
+ @ui.output
+ assert_equal '', @ui.error
+
+ gem_make_out = File.join @spec.extension_dir, 'gem_make.out'
+
+ assert_match %r%#{Regexp.escape Gem.ruby}.* extconf\.rb%,
+ File.read(gem_make_out)
+ assert_match %r%: No such file%,
+ File.read(gem_make_out)
+
+ refute_path_exists @spec.gem_build_complete_path
+
+ skip "Gem.ruby is not the name of the binary being run in the end" \
+ unless File.read(gem_make_out).include? "#{Gem.ruby}:"
+
+ assert_match %r%#{Regexp.escape Gem.ruby}: No such file%,
+ File.read(gem_make_out)
+ end
+
+ def test_build_extensions_unsupported
+ FileUtils.mkdir_p @spec.gem_dir
+ gem_make_out = File.join @spec.extension_dir, 'gem_make.out'
+ @spec.extensions << nil
+
+ e = assert_raises Gem::Ext::BuildError do
+ use_ui @ui do
+ @builder.build_extensions
+ end
+ end
+
+ assert_match(/^\s*No builder for extension ''$/, e.message)
+
+ assert_equal "Building native extensions. This could take a while...\n",
+ @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal "No builder for extension ''\n", File.read(gem_make_out)
+
+ refute_path_exists @spec.gem_build_complete_path
+ ensure
+ FileUtils.rm_f gem_make_out
+ end
+
+ def test_build_extensions_with_build_args
+ args = ["--aa", "--bb"]
+ @builder.build_args = args
+ @spec.extensions << 'extconf.rb'
+
+ FileUtils.mkdir_p @spec.gem_dir
+
+ open File.join(@spec.gem_dir, "extconf.rb"), "w" do |f|
+ f.write <<-'RUBY'
+ puts "IN EXTCONF"
+ extconf_args = File.join File.dirname(__FILE__), 'extconf_args'
+ File.open extconf_args, 'w' do |f|
+ f.puts ARGV.inspect
+ end
+
+ File.open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo cleaned"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+
+ use_ui @ui do
+ @builder.build_extensions
+ end
+
+ path = File.join @spec.gem_dir, "extconf_args"
+
+ assert_equal args.inspect, File.read(path).strip
+ assert_path_exists @spec.extension_dir
+ end
+
+ def test_initialize
+ build_info_dir = File.join @gemhome, 'build_info'
+
+ FileUtils.mkdir_p build_info_dir
+
+ build_info_file = File.join build_info_dir, "#{@spec.full_name}.info"
+
+ open build_info_file, 'w' do |io|
+ io.puts '--with-foo-dir=/nonexistent'
+ end
+
+ builder = Gem::Ext::Builder.new @spec
+
+ assert_equal %w[--with-foo-dir=/nonexistent], builder.build_args
+ end
+
+ def test_initialize_build_args
+ builder = Gem::Ext::Builder.new @spec, %w[--with-foo-dir=/nonexistent]
+
+ assert_equal %w[--with-foo-dir=/nonexistent], builder.build_args
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_ext_cmake_builder.rb b/jni/ruby/test/rubygems/test_gem_ext_cmake_builder.rb
new file mode 100644
index 0000000..a36be47
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_ext_cmake_builder.rb
@@ -0,0 +1,84 @@
+require 'rubygems/test_case'
+require 'rubygems/ext'
+
+class TestGemExtCmakeBuilder < Gem::TestCase
+
+ def setup
+ super
+
+ `cmake #{Gem::Ext::Builder.redirector}`
+
+ skip 'cmake not present' unless $?.success?
+
+ @ext = File.join @tempdir, 'ext'
+ @dest_path = File.join @tempdir, 'prefix'
+
+ FileUtils.mkdir_p @ext
+ FileUtils.mkdir_p @dest_path
+ end
+
+ def test_self_build
+ File.open File.join(@ext, 'CMakeLists.txt'), 'w' do |cmakelists|
+ cmakelists.write <<-eo_cmake
+cmake_minimum_required(VERSION 2.6)
+install (FILES test.txt DESTINATION bin)
+ eo_cmake
+ end
+
+ FileUtils.touch File.join(@ext, 'test.txt')
+
+ output = []
+
+ Dir.chdir @ext do
+ Gem::Ext::CmakeBuilder.build nil, nil, @dest_path, output
+ end
+
+ output = output.join "\n"
+
+ assert_match \
+ %r%^cmake \. -DCMAKE_INSTALL_PREFIX=#{Regexp.escape @dest_path}%, output
+ assert_match %r%#{Regexp.escape @ext}%, output
+ assert_contains_make_command '', output
+ assert_contains_make_command 'install', output
+ assert_match %r%test\.txt%, output
+ end
+
+ def test_self_build_fail
+ output = []
+
+ error = assert_raises Gem::InstallError do
+ Dir.chdir @ext do
+ Gem::Ext::CmakeBuilder.build nil, nil, @dest_path, output
+ end
+ end
+
+ output = output.join "\n"
+
+ shell_error_msg = %r{(CMake Error: .*)}
+ sh_prefix_cmake = "cmake . -DCMAKE_INSTALL_PREFIX="
+
+ assert_match 'cmake failed', error.message
+
+ assert_match %r%^#{sh_prefix_cmake}#{Regexp.escape @dest_path}%, output
+ assert_match %r%#{shell_error_msg}%, output
+ end
+
+ def test_self_build_has_makefile
+ File.open File.join(@ext, 'Makefile'), 'w' do |makefile|
+ makefile.puts "all:\n\t@echo ok\ninstall:\n\t@echo ok"
+ end
+
+ output = []
+
+ Dir.chdir @ext do
+ Gem::Ext::CmakeBuilder.build nil, nil, @dest_path, output
+ end
+
+ output = output.join "\n"
+
+ assert_contains_make_command '', output
+ assert_contains_make_command 'install', output
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_ext_configure_builder.rb b/jni/ruby/test/rubygems/test_gem_ext_configure_builder.rb
new file mode 100644
index 0000000..34e9fcc
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_ext_configure_builder.rb
@@ -0,0 +1,82 @@
+require 'rubygems/test_case'
+require 'rubygems/ext'
+
+class TestGemExtConfigureBuilder < Gem::TestCase
+
+ def setup
+ super
+
+ @makefile_body =
+ "clean:\n\t@echo ok\nall:\n\t@echo ok\ninstall:\n\t@echo ok"
+
+ @ext = File.join @tempdir, 'ext'
+ @dest_path = File.join @tempdir, 'prefix'
+
+ FileUtils.mkdir_p @ext
+ FileUtils.mkdir_p @dest_path
+ end
+
+ def test_self_build
+ skip("test_self_build skipped on MS Windows (VC++)") if vc_windows?
+
+ File.open File.join(@ext, './configure'), 'w' do |configure|
+ configure.puts "#!/bin/sh\necho \"#{@makefile_body}\" > Makefile"
+ end
+
+ output = []
+
+ Dir.chdir @ext do
+ Gem::Ext::ConfigureBuilder.build nil, nil, @dest_path, output
+ end
+
+ assert_equal "sh ./configure --prefix=#{@dest_path}", output.shift
+ assert_equal "", output.shift
+ assert_contains_make_command 'clean', output.shift
+ assert_match(/^ok$/m, output.shift)
+ assert_contains_make_command '', output.shift
+ assert_match(/^ok$/m, output.shift)
+ assert_contains_make_command 'install', output.shift
+ assert_match(/^ok$/m, output.shift)
+ end
+
+ def test_self_build_fail
+ skip("test_self_build_fail skipped on MS Windows (VC++)") if vc_windows?
+ output = []
+
+ error = assert_raises Gem::InstallError do
+ Dir.chdir @ext do
+ Gem::Ext::ConfigureBuilder.build nil, nil, @dest_path, output
+ end
+ end
+
+ shell_error_msg = %r{(\./configure: .*)|((?:Can't|cannot) open \./configure(?:: No such file or directory)?)}
+ sh_prefix_configure = "sh ./configure --prefix="
+
+ assert_match 'configure failed', error.message
+
+ assert_equal "#{sh_prefix_configure}#{@dest_path}", output.shift
+ assert_match %r(#{shell_error_msg}), output.shift
+ assert_equal true, output.empty?
+ end
+
+ def test_self_build_has_makefile
+ if vc_windows? && !nmake_found?
+ skip("test_self_build_has_makefile skipped - nmake not found")
+ end
+
+ File.open File.join(@ext, 'Makefile'), 'w' do |makefile|
+ makefile.puts @makefile_body
+ end
+
+ output = []
+ Dir.chdir @ext do
+ Gem::Ext::ConfigureBuilder.build nil, nil, @dest_path, output
+ end
+
+ assert_contains_make_command 'clean', output[0]
+ assert_contains_make_command '', output[2]
+ assert_contains_make_command 'install', output[4]
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_ext_ext_conf_builder.rb b/jni/ruby/test/rubygems/test_gem_ext_ext_conf_builder.rb
new file mode 100644
index 0000000..f2f467e
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_ext_ext_conf_builder.rb
@@ -0,0 +1,206 @@
+# coding: UTF-8
+
+require 'rubygems/test_case'
+require 'rubygems/ext'
+
+class TestGemExtExtConfBuilder < Gem::TestCase
+
+ def setup
+ super
+
+ @ext = File.join @tempdir, 'ext'
+ @dest_path = File.join @tempdir, 'prefix'
+
+ FileUtils.mkdir_p @ext
+ FileUtils.mkdir_p @dest_path
+ end
+
+ def test_class_build
+ if vc_windows? && !nmake_found?
+ skip("test_class_build skipped - nmake not found")
+ end
+
+ File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
+ extconf.puts "require 'mkmf'\ncreate_makefile 'foo'"
+ end
+
+ output = []
+
+ Dir.chdir @ext do
+ result =
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+
+ assert_same result, output
+ end
+
+ assert_match(/^#{Gem.ruby}.* extconf.rb/, output[0])
+ assert_equal "creating Makefile\n", output[1]
+ assert_contains_make_command 'clean', output[2]
+ assert_contains_make_command '', output[4]
+ assert_contains_make_command 'install', output[6]
+ assert_empty Dir.glob(File.join(@ext, 'siteconf*.rb'))
+ end
+
+ def test_class_build_rbconfig_make_prog
+ configure_args do
+
+ File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
+ extconf.puts "require 'mkmf'\ncreate_makefile 'foo'"
+ end
+
+ output = []
+
+ Dir.chdir @ext do
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ end
+
+ assert_equal "creating Makefile\n", output[1]
+ assert_contains_make_command 'clean', output[2]
+ assert_contains_make_command '', output[4]
+ assert_contains_make_command 'install', output[6]
+ end
+ end
+
+ def test_class_build_env_make
+ env_make = ENV.delete 'make'
+ ENV['make'] = 'anothermake'
+
+ configure_args '' do
+ File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
+ extconf.puts "require 'mkmf'\ncreate_makefile 'foo'"
+ end
+
+ output = []
+
+ assert_raises Gem::InstallError do
+ Dir.chdir @ext do
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ end
+ end
+
+ assert_equal "creating Makefile\n", output[1]
+ assert_contains_make_command 'clean', output[2]
+ end
+ ensure
+ ENV['make'] = env_make
+ end
+
+ def test_class_build_extconf_fail
+ if vc_windows? && !nmake_found?
+ skip("test_class_build_extconf_fail skipped - nmake not found")
+ end
+
+ File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
+ extconf.puts "require 'mkmf'"
+ extconf.puts "have_library 'nonexistent' or abort 'need libnonexistent'"
+ extconf.puts "create_makefile 'foo'"
+ end
+
+ output = []
+
+ error = assert_raises Gem::InstallError do
+ Dir.chdir @ext do
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ end
+ end
+
+ assert_equal 'extconf failed, exit code 1', error.message
+
+ assert_match(/^#{Gem.ruby}.* extconf.rb/, output[0])
+ assert_path_exists File.join @dest_path, 'mkmf.log'
+ end
+
+ def test_class_build_unconventional
+ if vc_windows? && !nmake_found?
+ skip("test_class_build skipped - nmake not found")
+ end
+
+ File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
+ extconf.puts <<-'EXTCONF'
+include RbConfig
+
+ruby =
+ if ENV['RUBY'] then
+ ENV['RUBY']
+ else
+ ruby_exe = "#{CONFIG['RUBY_INSTALL_NAME']}#{CONFIG['EXEEXT']}"
+ File.join CONFIG['bindir'], ruby_exe
+ end
+
+open 'Makefile', 'w' do |io|
+ io.write <<-Makefile
+clean: ruby
+all: ruby
+install: ruby
+
+ruby:
+\t#{ruby} -e0
+
+ Makefile
+end
+ EXTCONF
+ end
+
+ output = []
+
+ Dir.chdir @ext do
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', nil, @dest_path, output
+ end
+
+ assert_contains_make_command 'clean', output[2]
+ assert_contains_make_command '', output[4]
+ assert_contains_make_command 'install', output[6]
+ assert_empty Dir.glob(File.join(@ext, 'siteconf*.rb'))
+ end
+
+ def test_class_make
+ if vc_windows? && !nmake_found?
+ skip("test_class_make skipped - nmake not found")
+ end
+
+ output = []
+ makefile_path = File.join(@ext, 'Makefile')
+ File.open makefile_path, 'w' do |makefile|
+ makefile.puts "# π"
+ makefile.puts "RUBYARCHDIR = $(foo)$(target_prefix)"
+ makefile.puts "RUBYLIBDIR = $(bar)$(target_prefix)"
+ makefile.puts "clean:"
+ makefile.puts "all:"
+ makefile.puts "install:"
+ end
+
+ Dir.chdir @ext do
+ Gem::Ext::ExtConfBuilder.make @ext, output
+ end
+
+ assert_contains_make_command 'clean', output[0]
+ assert_contains_make_command '', output[2]
+ assert_contains_make_command 'install', output[4]
+ end
+
+ def test_class_make_no_Makefile
+ error = assert_raises Gem::InstallError do
+ Dir.chdir @ext do
+ Gem::Ext::ExtConfBuilder.make @ext, ['output']
+ end
+ end
+
+ assert_equal 'Makefile not found', error.message
+ end
+
+ def configure_args args = nil
+ configure_args = RbConfig::CONFIG['configure_args']
+ RbConfig::CONFIG['configure_args'] = args if args
+
+ yield
+
+ ensure
+ if configure_args then
+ RbConfig::CONFIG['configure_args'] = configure_args
+ else
+ RbConfig::CONFIG.delete 'configure_args'
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_ext_rake_builder.rb b/jni/ruby/test/rubygems/test_gem_ext_rake_builder.rb
new file mode 100644
index 0000000..0f4789a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_ext_rake_builder.rb
@@ -0,0 +1,64 @@
+require 'rubygems/test_case'
+require 'rubygems/ext'
+
+class TestGemExtRakeBuilder < Gem::TestCase
+ def setup
+ super
+
+ @ext = File.join @tempdir, 'ext'
+ @dest_path = File.join @tempdir, 'prefix'
+
+ FileUtils.mkdir_p @ext
+ FileUtils.mkdir_p @dest_path
+ end
+
+ def test_class_build
+ File.open File.join(@ext, 'mkrf_conf.rb'), 'w' do |mkrf_conf|
+ mkrf_conf.puts <<-EO_MKRF
+ File.open("Rakefile","w") do |f|
+ f.puts "task :default"
+ end
+ EO_MKRF
+ end
+
+ output = []
+ realdir = nil # HACK /tmp vs. /private/tmp
+
+ build_rake_in do |rake|
+ Dir.chdir @ext do
+ realdir = Dir.pwd
+ Gem::Ext::RakeBuilder.build 'mkrf_conf.rb', nil, @dest_path, output
+ end
+
+ output = output.join "\n"
+
+ refute_match %r%^rake failed:%, output
+ assert_match %r%^#{Regexp.escape @@ruby} mkrf_conf\.rb%, output
+ assert_match %r%^#{Regexp.escape rake} RUBYARCHDIR=#{Regexp.escape @dest_path} RUBYLIBDIR=#{Regexp.escape @dest_path}%, output
+ end
+ end
+
+ def test_class_build_fail
+ File.open File.join(@ext, 'mkrf_conf.rb'), 'w' do |mkrf_conf|
+ mkrf_conf.puts <<-EO_MKRF
+ File.open("Rakefile","w") do |f|
+ f.puts "task :default do abort 'fail' end"
+ end
+ EO_MKRF
+ end
+
+ output = []
+
+ build_rake_in(false) do |rake|
+ error = assert_raises Gem::InstallError do
+ Dir.chdir @ext do
+ Gem::Ext::RakeBuilder.build "mkrf_conf.rb", nil, @dest_path, output
+ end
+ end
+
+ assert_match %r%^rake failed%, error.message
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_gem_runner.rb b/jni/ruby/test/rubygems/test_gem_gem_runner.rb
new file mode 100644
index 0000000..85ff725
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_gem_runner.rb
@@ -0,0 +1,68 @@
+require 'rubygems/test_case'
+require 'rubygems/gem_runner'
+
+class TestGemGemRunner < Gem::TestCase
+
+ def setup
+ super
+
+ @orig_args = Gem::Command.build_args
+ @runner = Gem::GemRunner.new
+ end
+
+ def teardown
+ super
+
+ Gem::Command.build_args = @orig_args
+ end
+
+ def test_do_configuration
+ Gem.clear_paths
+
+ temp_conf = File.join @tempdir, '.gemrc'
+
+ other_gem_path = File.join @tempdir, 'other_gem_path'
+ other_gem_home = File.join @tempdir, 'other_gem_home'
+
+ Gem.ensure_gem_subdirectories other_gem_path
+ Gem.ensure_gem_subdirectories other_gem_home
+
+ File.open temp_conf, 'w' do |fp|
+ fp.puts "gem: --commands"
+ fp.puts "gemhome: #{other_gem_home}"
+ fp.puts "gempath:"
+ fp.puts " - #{other_gem_path}"
+ fp.puts "rdoc: --all"
+ end
+
+ gr = Gem::GemRunner.new
+ gr.send :do_configuration, %W[--config-file #{temp_conf}]
+
+ assert_equal [other_gem_path, other_gem_home], Gem.path
+ assert_equal %w[--commands], Gem::Command.extra_args
+ end
+
+ def test_extract_build_args
+ args = %w[]
+ assert_equal [], @runner.extract_build_args(args)
+ assert_equal %w[], args
+
+ args = %w[foo]
+ assert_equal [], @runner.extract_build_args(args)
+ assert_equal %w[foo], args
+
+ args = %w[--foo]
+ assert_equal [], @runner.extract_build_args(args)
+ assert_equal %w[--foo], args
+
+ args = %w[--foo --]
+ assert_equal [], @runner.extract_build_args(args)
+ assert_equal %w[--foo], args
+
+ args = %w[--foo -- --bar]
+ assert_equal %w[--bar], @runner.extract_build_args(args)
+ assert_equal %w[--foo], args
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_gemcutter_utilities.rb b/jni/ruby/test/rubygems/test_gem_gemcutter_utilities.rb
new file mode 100644
index 0000000..c117c8f
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_gemcutter_utilities.rb
@@ -0,0 +1,234 @@
+require 'rubygems/test_case'
+require 'rubygems'
+require 'rubygems/command'
+require 'rubygems/gemcutter_utilities'
+
+class TestGemGemcutterUtilities < Gem::TestCase
+
+ def setup
+ super
+
+ ENV['RUBYGEMS_HOST'] = nil
+ Gem.configuration.rubygems_api_key = nil
+
+ @cmd = Gem::Command.new '', 'summary'
+ @cmd.extend Gem::GemcutterUtilities
+ end
+
+ def teardown
+ ENV['RUBYGEMS_HOST'] = nil
+ Gem.configuration.rubygems_api_key = nil
+
+ super
+ end
+
+ def test_alternate_key_alternate_host
+ keys = {
+ :rubygems_api_key => 'KEY',
+ "http://rubygems.engineyard.com" => "EYKEY"
+ }
+
+ FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
+
+ open Gem.configuration.credentials_path, 'w' do |f|
+ f.write keys.to_yaml
+ end
+
+ ENV["RUBYGEMS_HOST"] = "http://rubygems.engineyard.com"
+
+ Gem.configuration.load_api_keys
+
+ assert_equal 'EYKEY', @cmd.api_key
+ end
+
+ def test_api_key
+ keys = { :rubygems_api_key => 'KEY' }
+ FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
+
+ open Gem.configuration.credentials_path, 'w' do |f|
+ f.write keys.to_yaml
+ end
+
+ Gem.configuration.load_api_keys
+
+ assert_equal 'KEY', @cmd.api_key
+ end
+
+ def test_api_key_override
+ keys = { :rubygems_api_key => 'KEY', :other => 'OTHER' }
+ FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
+
+ open Gem.configuration.credentials_path, 'w' do |f|
+ f.write keys.to_yaml
+ end
+
+ Gem.configuration.load_api_keys
+
+ @cmd.add_key_option
+ @cmd.handle_options %w[--key other]
+
+ assert_equal 'OTHER', @cmd.api_key
+ end
+
+ def test_host
+ assert_equal 'https://rubygems.org', @cmd.host
+ end
+
+ def test_host_RUBYGEMS_HOST
+ ENV['RUBYGEMS_HOST'] = 'https://other.example'
+
+ assert_equal 'https://other.example', @cmd.host
+ end
+
+ def test_host_RUBYGEMS_HOST_empty
+ ENV['RUBYGEMS_HOST'] = ''
+
+ assert_equal 'https://rubygems.org', @cmd.host
+ end
+
+ def test_sign_in
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ util_sign_in [api_key, 200, 'OK']
+
+ assert_match %r{Enter your RubyGems.org credentials.}, @sign_in_ui.output
+ assert @fetcher.last_request["authorization"]
+ assert_match %r{Signed in.}, @sign_in_ui.output
+
+ credentials = YAML.load_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[:rubygems_api_key]
+ end
+
+ def test_sign_in_with_host
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+
+ util_sign_in [api_key, 200, 'OK'], 'http://example.com', ['http://example.com']
+
+ assert_match "Enter your http://example.com credentials.",
+ @sign_in_ui.output
+ assert @fetcher.last_request["authorization"]
+ assert_match %r{Signed in.}, @sign_in_ui.output
+
+ credentials = YAML.load_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[:rubygems_api_key]
+ end
+
+ def test_sign_in_with_host_nil
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+
+ util_sign_in [api_key, 200, 'OK'], nil, [nil]
+
+ assert_match "Enter your RubyGems.org credentials.",
+ @sign_in_ui.output
+ assert @fetcher.last_request["authorization"]
+ assert_match %r{Signed in.}, @sign_in_ui.output
+
+ credentials = YAML.load_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[:rubygems_api_key]
+ end
+
+ def test_sign_in_with_host_ENV
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ util_sign_in [api_key, 200, 'OK'], 'http://example.com'
+
+ assert_match "Enter your http://example.com credentials.",
+ @sign_in_ui.output
+ assert @fetcher.last_request["authorization"]
+ assert_match %r{Signed in.}, @sign_in_ui.output
+
+ credentials = YAML.load_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[:rubygems_api_key]
+ end
+
+ def test_sign_in_skips_with_existing_credentials
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ Gem.configuration.rubygems_api_key = api_key
+
+ util_sign_in [api_key, 200, 'OK']
+
+ assert_equal "", @sign_in_ui.output
+ end
+
+ def test_sign_in_skips_with_key_override
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ Gem.configuration.api_keys[:KEY] = 'other'
+ @cmd.options[:key] = :KEY
+ util_sign_in [api_key, 200, 'OK']
+
+ assert_equal "", @sign_in_ui.output
+ end
+
+ def test_sign_in_with_other_credentials_doesnt_overwrite_other_keys
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ other_api_key = 'f46dbb18bb6a9c97cdc61b5b85c186a17403cdcbf'
+
+ FileUtils.mkdir_p File.dirname(Gem.configuration.credentials_path)
+ open Gem.configuration.credentials_path, 'w' do |f|
+ f.write Hash[:other_api_key, other_api_key].to_yaml
+ end
+ util_sign_in [api_key, 200, 'OK']
+
+ assert_match %r{Enter your RubyGems.org credentials.}, @sign_in_ui.output
+ assert_match %r{Signed in.}, @sign_in_ui.output
+
+ credentials = YAML.load_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[:rubygems_api_key]
+ assert_equal other_api_key, credentials[:other_api_key]
+ end
+
+ def test_sign_in_with_bad_credentials
+ skip 'Always uses $stdin on windows' if Gem.win_platform?
+
+ assert_raises Gem::MockGemUi::TermError do
+ util_sign_in ['Access Denied.', 403, 'Forbidden']
+ end
+
+ assert_match %r{Enter your RubyGems.org credentials.}, @sign_in_ui.output
+ assert_match %r{Access Denied.}, @sign_in_ui.output
+ end
+
+ def util_sign_in response, host = nil, args = []
+ skip 'Always uses $stdin on windows' if Gem.win_platform?
+
+ email = 'you@example.com'
+ password = 'secret'
+
+ if host
+ ENV['RUBYGEMS_HOST'] = host
+ else
+ host = Gem.host
+ end
+
+ @fetcher = Gem::FakeFetcher.new
+ @fetcher.data["#{host}/api/v1/api_key"] = response
+ Gem::RemoteFetcher.fetcher = @fetcher
+
+ @sign_in_ui = Gem::MockGemUi.new "#{email}\n#{password}\n"
+
+ use_ui @sign_in_ui do
+ if args.length > 0 then
+ @cmd.sign_in(*args)
+ else
+ @cmd.sign_in
+ end
+ end
+ end
+
+ def test_verify_api_key
+ keys = {:other => 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'}
+ FileUtils.mkdir_p File.dirname(Gem.configuration.credentials_path)
+ File.open Gem.configuration.credentials_path, 'w' do |f|
+ f.write keys.to_yaml
+ end
+ Gem.configuration.load_api_keys
+
+ assert_equal 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903',
+ @cmd.verify_api_key(:other)
+ end
+
+ def test_verify_missing_api_key
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.verify_api_key :missing
+ end
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_impossible_dependencies_error.rb b/jni/ruby/test/rubygems/test_gem_impossible_dependencies_error.rb
new file mode 100644
index 0000000..9c9825c
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_impossible_dependencies_error.rb
@@ -0,0 +1,61 @@
+require 'rubygems/test_case'
+
+class TestGemImpossibleDependenciesError < Gem::TestCase
+
+ def test_message_conflict
+ request = dependency_request dep('net-ssh', '>= 2.0.13'), 'rye', '0.9.8'
+
+ conflicts = []
+
+ # These conflicts are lies as their dependencies does not have the correct
+ # requested-by entries, but they are suitable for testing the message.
+ # See #485 to construct a correct conflict.
+ net_ssh_2_2_2 =
+ dependency_request dep('net-ssh', '>= 2.6.5'), 'net-ssh', '2.2.2', request
+ net_ssh_2_6_5 =
+ dependency_request dep('net-ssh', '~> 2.2.2'), 'net-ssh', '2.6.5', request
+
+ conflict1 = Gem::Resolver::Conflict.new \
+ net_ssh_2_6_5, net_ssh_2_6_5.requester
+
+ conflict2 = Gem::Resolver::Conflict.new \
+ net_ssh_2_2_2, net_ssh_2_2_2.requester
+
+ conflicts << [net_ssh_2_6_5.requester.spec, conflict1]
+ conflicts << [net_ssh_2_2_2.requester.spec, conflict2]
+
+ error = Gem::ImpossibleDependenciesError.new request, conflicts
+
+ expected = <<-EXPECTED
+rye-0.9.8 requires net-ssh (>= 2.0.13) but it conflicted:
+ Activated net-ssh-2.6.5
+ which does not match conflicting dependency (~> 2.2.2)
+
+ Conflicting dependency chains:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.6.5 activated
+
+ versus:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.6.5 activated, depends on
+ net-ssh (~> 2.2.2)
+
+ Activated net-ssh-2.2.2
+ which does not match conflicting dependency (>= 2.6.5)
+
+ Conflicting dependency chains:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.2.2 activated
+
+ versus:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.2.2 activated, depends on
+ net-ssh (>= 2.6.5)
+
+ EXPECTED
+
+ assert_equal expected, error.message
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_indexer.rb b/jni/ruby/test/rubygems/test_gem_indexer.rb
new file mode 100644
index 0000000..5ce0788
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_indexer.rb
@@ -0,0 +1,366 @@
+require 'rubygems/test_case'
+require 'rubygems/indexer'
+
+unless defined?(Builder::XChar) then
+ warn "Gem::Indexer tests are being skipped. Install builder gem." if $VERBOSE
+end
+
+class TestGemIndexer < Gem::TestCase
+
+ def setup
+ super
+
+ util_clear_gems
+ util_make_gems
+
+ @d2_0 = util_spec 'd', '2.0' do |s|
+ s.date = Gem::Specification::TODAY - 86400 * 3
+ end
+ util_build_gem @d2_0
+
+ @d2_0_a = util_spec 'd', '2.0.a'
+ util_build_gem @d2_0_a
+
+ @d2_0_b = util_spec 'd', '2.0.b'
+ util_build_gem @d2_0_b
+
+ @default = new_default_spec 'default', 2
+ install_default_gems @default
+
+ @tempdir = File.join(@tempdir, 'indexer')
+
+ gems = File.join(@tempdir, 'gems')
+ FileUtils.mkdir_p gems
+ FileUtils.mv Dir[File.join(@gemhome, "cache", '*.gem')], gems
+
+ @indexer = Gem::Indexer.new(@tempdir)
+ end
+
+ def test_initialize
+ assert_equal @tempdir, @indexer.dest_directory
+ assert_equal File.join(Dir.tmpdir, "gem_generate_index_#{$$}"),
+ @indexer.directory
+
+ indexer = Gem::Indexer.new @tempdir
+ assert indexer.build_modern
+
+ indexer = Gem::Indexer.new @tempdir, :build_modern => true
+ assert indexer.build_modern
+ end
+
+ def test_build_indicies
+ @indexer.make_temp_directories
+
+ use_ui @ui do
+ @indexer.build_indicies
+ end
+
+ specs_path = File.join @indexer.directory, "specs.#{@marshal_version}"
+ specs_dump = Gem.read_binary specs_path
+ specs = Marshal.load specs_dump
+
+ expected = [["a", Gem::Version.new("1"), "ruby"],
+ ["a", Gem::Version.new("2"), "ruby"],
+ ["a_evil", Gem::Version.new("9"), "ruby"],
+ ["b", Gem::Version.new("2"), "ruby"],
+ ["c", Gem::Version.new("1.2"), "ruby"],
+ ["d", Gem::Version.new("2.0"), "ruby"],
+ ["dep_x", Gem::Version.new("1"), "ruby"],
+ ["pl", Gem::Version.new("1"), "i386-linux"],
+ ["x", Gem::Version.new("1"), "ruby"]]
+
+ assert_equal expected, specs
+
+ latest_specs_path = File.join(@indexer.directory,
+ "latest_specs.#{@marshal_version}")
+ latest_specs_dump = Gem.read_binary latest_specs_path
+ latest_specs = Marshal.load latest_specs_dump
+
+ expected = [["a", Gem::Version.new("2"), "ruby"],
+ ["a_evil", Gem::Version.new("9"), "ruby"],
+ ["b", Gem::Version.new("2"), "ruby"],
+ ["c", Gem::Version.new("1.2"), "ruby"],
+ ["d", Gem::Version.new("2.0"), "ruby"],
+ ["dep_x", Gem::Version.new("1"), "ruby"],
+ ["pl", Gem::Version.new("1"), "i386-linux"],
+ ["x", Gem::Version.new("1"), "ruby"]]
+
+ assert_equal expected, latest_specs, 'latest_specs'
+ end
+
+ def test_generate_index
+ use_ui @ui do
+ @indexer.generate_index
+ end
+
+ quickdir = File.join @tempdir, 'quick'
+ marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
+
+ assert File.directory?(quickdir)
+ assert File.directory?(marshal_quickdir)
+
+ assert_indexed marshal_quickdir, "#{File.basename(@a1.spec_file)}.rz"
+ assert_indexed marshal_quickdir, "#{File.basename(@a2.spec_file)}.rz"
+
+ refute_indexed marshal_quickdir, File.basename(@c1_2.spec_file)
+
+ assert_indexed @tempdir, "specs.#{@marshal_version}"
+ assert_indexed @tempdir, "specs.#{@marshal_version}.gz"
+
+ assert_indexed @tempdir, "latest_specs.#{@marshal_version}"
+ assert_indexed @tempdir, "latest_specs.#{@marshal_version}.gz"
+ end
+
+ def test_generate_index_modern
+ @indexer.build_modern = true
+
+ use_ui @ui do
+ @indexer.generate_index
+ end
+
+ refute_indexed @tempdir, 'yaml'
+ refute_indexed @tempdir, 'yaml.Z'
+ refute_indexed @tempdir, "Marshal.#{@marshal_version}"
+ refute_indexed @tempdir, "Marshal.#{@marshal_version}.Z"
+
+ quickdir = File.join @tempdir, 'quick'
+ marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
+
+ assert File.directory?(quickdir), 'quickdir should be directory'
+ assert File.directory?(marshal_quickdir)
+
+ refute_indexed quickdir, "index"
+ refute_indexed quickdir, "index.rz"
+
+ refute_indexed quickdir, "latest_index"
+ refute_indexed quickdir, "latest_index.rz"
+
+ refute_indexed quickdir, "#{File.basename(@a1.spec_file)}.rz"
+ refute_indexed quickdir, "#{File.basename(@a2.spec_file)}.rz"
+ refute_indexed quickdir, "#{File.basename(@b2.spec_file)}.rz"
+ refute_indexed quickdir, "#{File.basename(@c1_2.spec_file)}.rz"
+
+ refute_indexed quickdir, "#{@pl1.original_name}.gemspec.rz"
+ refute_indexed quickdir, "#{File.basename(@pl1.spec_file)}.rz"
+
+ assert_indexed marshal_quickdir, "#{File.basename(@a1.spec_file)}.rz"
+ assert_indexed marshal_quickdir, "#{File.basename(@a2.spec_file)}.rz"
+
+ refute_indexed quickdir, "#{File.basename(@c1_2.spec_file)}"
+ refute_indexed marshal_quickdir, "#{File.basename(@c1_2.spec_file)}"
+
+ assert_indexed @tempdir, "specs.#{@marshal_version}"
+ assert_indexed @tempdir, "specs.#{@marshal_version}.gz"
+
+ assert_indexed @tempdir, "latest_specs.#{@marshal_version}"
+ assert_indexed @tempdir, "latest_specs.#{@marshal_version}.gz"
+ end
+
+ def test_generate_index_modern_back_to_back
+ @indexer.build_modern = true
+
+ use_ui @ui do
+ @indexer.generate_index
+ end
+
+ @indexer = Gem::Indexer.new @tempdir
+ @indexer.build_modern = true
+
+ use_ui @ui do
+ @indexer.generate_index
+ end
+ quickdir = File.join @tempdir, 'quick'
+ marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
+
+ assert File.directory?(quickdir)
+ assert File.directory?(marshal_quickdir)
+
+ assert_indexed marshal_quickdir, "#{File.basename(@a1.spec_file)}.rz"
+ assert_indexed marshal_quickdir, "#{File.basename(@a2.spec_file)}.rz"
+
+ assert_indexed @tempdir, "specs.#{@marshal_version}"
+ assert_indexed @tempdir, "specs.#{@marshal_version}.gz"
+
+ assert_indexed @tempdir, "latest_specs.#{@marshal_version}"
+ assert_indexed @tempdir, "latest_specs.#{@marshal_version}.gz"
+ end
+
+ def test_generate_index_ui
+ use_ui @ui do
+ @indexer.generate_index
+ end
+
+ assert_match %r%^\.\.\.\.\.\.\.\.\.\.\.\.$%, @ui.output
+ assert_match %r%^Generating Marshal quick index gemspecs for 12 gems$%,
+ @ui.output
+ assert_match %r%^Complete$%, @ui.output
+ assert_match %r%^Generating specs index$%, @ui.output
+ assert_match %r%^Generating latest specs index$%, @ui.output
+ assert_match %r%^Generating prerelease specs index$%, @ui.output
+ assert_match %r%^Complete$%, @ui.output
+ assert_match %r%^Compressing indicies$%, @ui.output
+
+ assert_equal '', @ui.error
+ end
+
+ def test_generate_index_specs
+ use_ui @ui do
+ @indexer.generate_index
+ end
+
+ specs_path = File.join @tempdir, "specs.#{@marshal_version}"
+
+ specs_dump = Gem.read_binary specs_path
+ specs = Marshal.load specs_dump
+
+ expected = [
+ ['a', Gem::Version.new(1), 'ruby'],
+ ['a', Gem::Version.new(2), 'ruby'],
+ ['a_evil', Gem::Version.new(9), 'ruby'],
+ ['b', Gem::Version.new(2), 'ruby'],
+ ['c', Gem::Version.new('1.2'), 'ruby'],
+ ['d', Gem::Version.new('2.0'), 'ruby'],
+ ['dep_x', Gem::Version.new(1), 'ruby'],
+ ['pl', Gem::Version.new(1), 'i386-linux'],
+ ['x', Gem::Version.new(1), 'ruby'],
+ ]
+
+ assert_equal expected, specs
+
+ assert_same specs[0].first, specs[1].first,
+ 'identical names not identical'
+
+ assert_same specs[0][1], specs[-1][1],
+ 'identical versions not identical'
+
+ assert_same specs[0].last, specs[1].last,
+ 'identical platforms not identical'
+
+ refute_same specs[1][1], specs[5][1],
+ 'different versions not different'
+ end
+
+ def test_generate_index_latest_specs
+ use_ui @ui do
+ @indexer.generate_index
+ end
+
+ latest_specs_path = File.join @tempdir, "latest_specs.#{@marshal_version}"
+
+ latest_specs_dump = Gem.read_binary latest_specs_path
+ latest_specs = Marshal.load latest_specs_dump
+
+ expected = [
+ ['a', Gem::Version.new(2), 'ruby'],
+ ['a_evil', Gem::Version.new(9), 'ruby'],
+ ['b', Gem::Version.new(2), 'ruby'],
+ ['c', Gem::Version.new('1.2'), 'ruby'],
+ ['d', Gem::Version.new('2.0'), 'ruby'],
+ ['dep_x', Gem::Version.new(1), 'ruby'],
+ ['pl', Gem::Version.new(1), 'i386-linux'],
+ ['x', Gem::Version.new(1), 'ruby'],
+ ]
+
+ assert_equal expected, latest_specs
+
+ assert_same latest_specs[0][1], latest_specs[2][1],
+ 'identical versions not identical'
+
+ assert_same latest_specs[0].last, latest_specs[1].last,
+ 'identical platforms not identical'
+ end
+
+ def test_generate_index_prerelease_specs
+ use_ui @ui do
+ @indexer.generate_index
+ end
+
+ prerelease_specs_path = File.join @tempdir, "prerelease_specs.#{@marshal_version}"
+
+ prerelease_specs_dump = Gem.read_binary prerelease_specs_path
+ prerelease_specs = Marshal.load prerelease_specs_dump
+
+ assert_equal [['a', Gem::Version.new('3.a'), 'ruby'],
+ ['d', Gem::Version.new('2.0.a'), 'ruby'],
+ ['d', Gem::Version.new('2.0.b'), 'ruby']],
+ prerelease_specs
+ end
+
+ ##
+ # Emulate the starting state of Gem::Specification in a live environment,
+ # where it will carry the list of system gems
+ def with_system_gems
+ Gem::Specification.reset
+
+ sys_gem = util_spec 'systemgem', '1.0'
+ util_build_gem sys_gem
+ Gem::Specification.add_spec sys_gem
+ yield
+ util_remove_gem sys_gem
+ end
+
+
+ def test_update_index
+ use_ui @ui do
+ @indexer.generate_index
+ end
+
+ quickdir = File.join @tempdir, 'quick'
+ marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
+
+ assert File.directory?(quickdir)
+ assert File.directory?(marshal_quickdir)
+
+ @d2_1 = util_spec 'd', '2.1'
+ util_build_gem @d2_1
+ @d2_1_tuple = [@d2_1.name, @d2_1.version, @d2_1.original_platform]
+
+ @d2_1_a = util_spec 'd', '2.2.a'
+ util_build_gem @d2_1_a
+ @d2_1_a_tuple = [@d2_1_a.name, @d2_1_a.version, @d2_1_a.original_platform]
+
+ gems = File.join @tempdir, 'gems'
+
+ FileUtils.mv @d2_1.cache_file, gems
+ FileUtils.mv @d2_1_a.cache_file, gems
+
+ with_system_gems do
+ use_ui @ui do
+ @indexer.update_index
+ end
+
+ assert_indexed marshal_quickdir, "#{File.basename(@d2_1.spec_file)}.rz"
+
+ specs_index = Marshal.load Gem.read_binary(@indexer.dest_specs_index)
+
+ assert_includes specs_index, @d2_1_tuple
+ refute_includes specs_index, @d2_1_a_tuple
+
+ latest_specs_index = Marshal.load \
+ Gem.read_binary(@indexer.dest_latest_specs_index)
+
+ assert_includes latest_specs_index, @d2_1_tuple
+ assert_includes latest_specs_index,
+ [@d2_0.name, @d2_0.version, @d2_0.original_platform]
+ refute_includes latest_specs_index, @d2_1_a_tuple
+
+ pre_specs_index = Marshal.load \
+ Gem.read_binary(@indexer.dest_prerelease_specs_index)
+
+ assert_includes pre_specs_index, @d2_1_a_tuple
+ refute_includes pre_specs_index, @d2_1_tuple
+ end
+ end
+
+ def assert_indexed(dir, name)
+ file = File.join dir, name
+ assert File.exist?(file), "#{file} does not exist"
+ end
+
+ def refute_indexed(dir, name)
+ file = File.join dir, name
+ refute File.exist?(file), "#{file} exists"
+ end
+
+end if defined?(Builder::XChar)
+
diff --git a/jni/ruby/test/rubygems/test_gem_install_update_options.rb b/jni/ruby/test/rubygems/test_gem_install_update_options.rb
new file mode 100644
index 0000000..de09d7a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_install_update_options.rb
@@ -0,0 +1,184 @@
+require 'rubygems/installer_test_case'
+require 'rubygems/install_update_options'
+require 'rubygems/command'
+require 'rubygems/dependency_installer'
+
+class TestGemInstallUpdateOptions < Gem::InstallerTestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Command.new 'dummy', 'dummy',
+ Gem::DependencyInstaller::DEFAULT_OPTIONS
+ @cmd.extend Gem::InstallUpdateOptions
+ @cmd.add_install_update_options
+ end
+
+ def test_add_install_update_options
+ args = %w[
+ --document
+ --build-root build_root
+ --format-exec
+ --ignore-dependencies
+ --rdoc
+ --ri
+ -E
+ -f
+ -i /install_to
+ -w
+ --vendor
+ ]
+
+ args.concat %w[-P HighSecurity] if defined?(OpenSSL::SSL)
+
+ assert @cmd.handles?(args)
+ end
+
+ def test_build_root
+ @cmd.handle_options %w[--build-root build_root]
+
+ assert_equal File.expand_path('build_root'), @cmd.options[:build_root]
+ end
+
+ def test_doc
+ @cmd.handle_options %w[--doc]
+
+ assert_equal %w[ri], @cmd.options[:document].sort
+ end
+
+ def test_doc_rdoc
+ @cmd.handle_options %w[--doc=rdoc]
+
+ assert_equal %w[rdoc], @cmd.options[:document]
+
+ @cmd.handle_options %w[--doc ri]
+
+ assert_equal %w[ri], @cmd.options[:document]
+ end
+
+ def test_doc_rdoc_ri
+ @cmd.handle_options %w[--doc=rdoc,ri]
+
+ assert_equal %w[rdoc ri], @cmd.options[:document]
+ end
+
+ def test_doc_no
+ @cmd.handle_options %w[--no-doc]
+
+ assert_equal [], @cmd.options[:document]
+ end
+
+ def test_document
+ @cmd.handle_options %w[--document]
+
+ assert_equal %w[ri], @cmd.options[:document].sort
+ end
+
+ def test_document_no
+ @cmd.handle_options %w[--no-document]
+
+ assert_equal %w[], @cmd.options[:document]
+ end
+
+ def test_document_rdoc
+ @cmd.handle_options %w[--document=rdoc]
+
+ assert_equal %w[rdoc], @cmd.options[:document]
+
+ @cmd.handle_options %w[--document ri]
+
+ assert_equal %w[ri], @cmd.options[:document]
+ end
+
+ def test_rdoc
+ @cmd.handle_options %w[--rdoc]
+
+ assert_equal %w[rdoc ri], @cmd.options[:document].sort
+ end
+
+ def test_rdoc_no
+ @cmd.handle_options %w[--no-rdoc]
+
+ assert_equal %w[ri], @cmd.options[:document]
+ end
+
+ def test_ri
+ @cmd.handle_options %w[--no-ri]
+
+ assert_equal %w[], @cmd.options[:document]
+ end
+
+ def test_security_policy
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ @cmd.handle_options %w[-P HighSecurity]
+
+ assert_equal Gem::Security::HighSecurity, @cmd.options[:security_policy]
+ end
+
+ def test_security_policy_unknown
+ @cmd.add_install_update_options
+
+ assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %w[-P UnknownSecurity]
+ end
+ end
+
+ def test_user_install_enabled
+ @cmd.handle_options %w[--user-install]
+
+ assert @cmd.options[:user_install]
+
+ @installer = Gem::Installer.new @gem, @cmd.options
+ @installer.install
+ assert_path_exists File.join(Gem.user_dir, 'gems')
+ assert_path_exists File.join(Gem.user_dir, 'gems', @spec.full_name)
+ end
+
+ def test_user_install_disabled_read_only
+ if win_platform?
+ skip('test_user_install_disabled_read_only test skipped on MS Windows')
+ else
+ @cmd.handle_options %w[--no-user-install]
+
+ refute @cmd.options[:user_install]
+
+ FileUtils.chmod 0755, @userhome
+ FileUtils.chmod 0000, @gemhome
+
+ Gem.use_paths @gemhome, @userhome
+
+ assert_raises(Gem::FilePermissionError) do
+ Gem::Installer.new(@gem, @cmd.options).install
+ end
+ end
+ ensure
+ FileUtils.chmod 0755, @gemhome
+ end
+
+ def test_vendor
+ @cmd.handle_options %w[--vendor]
+
+ assert @cmd.options[:vendor]
+ assert_equal Gem.vendor_dir, @cmd.options[:install_dir]
+ end
+
+ def test_vendor_missing
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG.delete 'vendordir'
+
+ e = assert_raises OptionParser::InvalidOption do
+ @cmd.handle_options %w[--vendor]
+ end
+
+ assert_equal 'invalid option: --vendor your platform is not supported',
+ e.message
+
+ refute @cmd.options[:vendor]
+ refute @cmd.options[:install_dir]
+
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_installer.rb b/jni/ruby/test/rubygems/test_gem_installer.rb
new file mode 100644
index 0000000..6f8012f
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_installer.rb
@@ -0,0 +1,1533 @@
+require 'rubygems/installer_test_case'
+
+class TestGemInstaller < Gem::InstallerTestCase
+
+ def setup
+ super
+ common_installer_setup
+
+ if __name__ =~ /^test_install(_|$)/ then
+ FileUtils.rm_r @spec.gem_dir
+ FileUtils.rm_r @user_spec.gem_dir
+ end
+
+ @config = Gem.configuration
+ end
+
+ def teardown
+ common_installer_teardown
+
+ super
+
+ Gem.configuration = @config
+ end
+
+ def test_app_script_text
+ util_make_exec @spec, ''
+
+ expected = <<-EOF
+#!#{Gem.ruby}
+#
+# This file was generated by RubyGems.
+#
+# The application 'a' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = \">= 0\"
+
+if ARGV.first
+ str = ARGV.first
+ str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
+ if str =~ /\\A_(.*)_\\z/ and Gem::Version.correct?($1) then
+ version = $1
+ ARGV.shift
+ end
+end
+
+gem 'a', version
+load Gem.bin_path('a', 'executable', version)
+ EOF
+
+ wrapper = @installer.app_script_text 'executable'
+ assert_equal expected, wrapper
+ end
+
+ def test_check_executable_overwrite
+ @installer.generate_bin
+
+ @spec = Gem::Specification.new do |s|
+ s.files = ['lib/code.rb']
+ s.name = "a"
+ s.version = "3"
+ s.summary = "summary"
+ s.description = "desc"
+ s.require_path = 'lib'
+ end
+
+ util_make_exec
+ @installer.gem_dir = util_gem_dir @spec
+ @installer.wrappers = true
+ @installer.generate_bin
+
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_path_exists installed_exec
+
+ wrapper = File.read installed_exec
+ assert_match %r|generated by RubyGems|, wrapper
+ end
+
+ def test_check_executable_overwrite_default_bin_dir
+ if defined?(RUBY_FRAMEWORK_VERSION)
+ orig_RUBY_FRAMEWORK_VERSION = RUBY_FRAMEWORK_VERSION
+ Object.send :remove_const, :RUBY_FRAMEWORK_VERSION
+ end
+ orig_bindir = RbConfig::CONFIG['bindir']
+ RbConfig::CONFIG['bindir'] = Gem.bindir
+
+ util_conflict_executable false
+
+ ui = Gem::MockGemUi.new "n\n"
+ use_ui ui do
+ e = assert_raises Gem::InstallError do
+ @installer.generate_bin
+ end
+
+ conflicted = File.join @gemhome, 'bin', 'executable'
+ assert_match %r%\A"executable" from a conflicts with (?:#{Regexp.quote(conflicted)}|installed executable from conflict)\z%,
+ e.message
+ end
+ ensure
+ Object.const_set :RUBY_FRAMEWORK_VERSION, orig_RUBY_FRAMEWORK_VERSION if
+ orig_RUBY_FRAMEWORK_VERSION
+ if orig_bindir then
+ RbConfig::CONFIG['bindir'] = orig_bindir
+ else
+ RbConfig::CONFIG.delete 'bindir'
+ end
+ end
+
+ def test_check_executable_overwrite_format_executable
+ @installer.generate_bin
+
+ @spec = Gem::Specification.new do |s|
+ s.files = ['lib/code.rb']
+ s.name = "a"
+ s.version = "3"
+ s.summary = "summary"
+ s.description = "desc"
+ s.require_path = 'lib'
+ end
+
+ open File.join(util_inst_bindir, 'executable'), 'w' do |io|
+ io.write <<-EXEC
+#!/usr/local/bin/ruby
+#
+# This file was generated by RubyGems
+
+gem 'other', version
+ EXEC
+ end
+
+ util_make_exec
+ Gem::Installer.exec_format = 'foo-%s-bar'
+ @installer.gem_dir = @spec.gem_dir
+ @installer.wrappers = true
+ @installer.format_executable = true
+
+ @installer.generate_bin # should not raise
+
+ installed_exec = File.join util_inst_bindir, 'foo-executable-bar'
+ assert_path_exists installed_exec
+
+ wrapper = File.read installed_exec
+ assert_match %r|generated by RubyGems|, wrapper
+ end
+
+ def test_check_executable_overwrite_other_gem
+ util_conflict_executable true
+
+ ui = Gem::MockGemUi.new "n\n"
+
+ use_ui ui do
+ e = assert_raises Gem::InstallError do
+ @installer.generate_bin
+ end
+
+ assert_equal '"executable" from a conflicts with installed executable from conflict',
+ e.message
+ end
+ end
+
+ def test_check_executable_overwrite_other_gem_force
+ util_conflict_executable true
+ @installer.wrappers = true
+ @installer.force = true
+
+ @installer.generate_bin
+
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_path_exists installed_exec
+
+ wrapper = File.read installed_exec
+ assert_match %r|generated by RubyGems|, wrapper
+ end
+
+ def test_check_executable_overwrite_other_non_gem
+ util_conflict_executable false
+ @installer.wrappers = true
+
+ @installer.generate_bin
+
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_path_exists installed_exec
+
+ wrapper = File.read installed_exec
+ assert_match %r|generated by RubyGems|, wrapper
+ end unless Gem.win_platform?
+
+ def test_check_that_user_bin_dir_is_in_path
+ bin_dir = @installer.bin_dir
+
+ if Gem.win_platform?
+ bin_dir = bin_dir.downcase.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
+ end
+
+ orig_PATH, ENV['PATH'] =
+ ENV['PATH'], [ENV['PATH'], bin_dir].join(File::PATH_SEPARATOR)
+
+ use_ui @ui do
+ @installer.check_that_user_bin_dir_is_in_path
+ end
+
+ assert_empty @ui.error
+ ensure
+ ENV['PATH'] = orig_PATH
+ end
+
+ def test_check_that_user_bin_dir_is_in_path_tilde
+ skip "Tilde is PATH is not supported under MS Windows" if win_platform?
+
+ orig_PATH, ENV['PATH'] =
+ ENV['PATH'], [ENV['PATH'], '~/bin'].join(File::PATH_SEPARATOR)
+
+ @installer.bin_dir.replace File.join @userhome, 'bin'
+
+ use_ui @ui do
+ @installer.check_that_user_bin_dir_is_in_path
+ end
+
+ assert_empty @ui.error
+ ensure
+ ENV['PATH'] = orig_PATH unless win_platform?
+ end
+
+ def test_check_that_user_bin_dir_is_in_path_not_in_path
+ use_ui @ui do
+ @installer.check_that_user_bin_dir_is_in_path
+ end
+
+ expected = @installer.bin_dir
+
+ if Gem.win_platform? then
+ expected = expected.downcase.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
+ end
+
+ assert_match expected, @ui.error
+ end
+
+ def test_ensure_dependency
+ util_spec 'a'
+
+ dep = Gem::Dependency.new 'a', '>= 2'
+ assert @installer.ensure_dependency(@spec, dep)
+
+ dep = Gem::Dependency.new 'b', '> 2'
+ e = assert_raises Gem::InstallError do
+ @installer.ensure_dependency @spec, dep
+ end
+
+ assert_equal 'a requires b (> 2)', e.message
+ end
+
+ def test_ensure_loadable_spec
+ a, a_gem = util_gem 'a', 2 do |s|
+ s.add_dependency 'garbage ~> 5'
+ end
+
+ installer = Gem::Installer.new a_gem
+
+ e = assert_raises Gem::InstallError do
+ installer.ensure_loadable_spec
+ end
+
+ assert_equal "The specification for #{a.full_name} is corrupt " +
+ "(SyntaxError)", e.message
+ end
+
+ def test_ensure_loadable_spec_security_policy
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ _, a_gem = util_gem 'a', 2 do |s|
+ s.add_dependency 'garbage ~> 5'
+ end
+
+ policy = Gem::Security::HighSecurity
+ installer = Gem::Installer.new a_gem, :security_policy => policy
+
+ assert_raises Gem::Security::Exception do
+ installer.ensure_loadable_spec
+ end
+ end
+
+ def test_extract_files
+ @installer.extract_files
+
+ assert_path_exists File.join util_gem_dir, 'bin/executable'
+ end
+
+ def test_generate_bin_bindir
+ @installer.wrappers = true
+
+ @spec.executables = %w[executable]
+ @spec.bindir = '.'
+
+ exec_file = @installer.formatted_program_filename 'executable'
+ exec_path = File.join util_gem_dir(@spec), exec_file
+ File.open exec_path, 'w' do |f|
+ f.puts '#!/usr/bin/ruby'
+ end
+
+ @installer.gem_dir = util_gem_dir
+
+ @installer.generate_bin
+
+ assert_equal true, File.directory?(util_inst_bindir)
+ installed_exec = File.join(util_inst_bindir, 'executable')
+ assert_path_exists installed_exec
+ assert_equal mask, File.stat(installed_exec).mode unless win_platform?
+
+ wrapper = File.read installed_exec
+ assert_match %r|generated by RubyGems|, wrapper
+ end
+
+ def test_generate_bin_bindir_with_user_install_warning
+ bin_dir = Gem.win_platform? ? File.expand_path(ENV["WINDIR"]).upcase :
+ "/usr/bin"
+
+ options = {
+ :bin_dir => bin_dir,
+ :install_dir => "/non/existant"
+ }
+
+ inst = Gem::Installer.new '', options
+
+ Gem::Installer.path_warning = false
+
+ use_ui @ui do
+ inst.check_that_user_bin_dir_is_in_path
+ end
+
+ assert_equal "", @ui.error
+ end
+
+ def test_generate_bin_script
+ @installer.wrappers = true
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ @installer.generate_bin
+ assert File.directory? util_inst_bindir
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_path_exists installed_exec
+ assert_equal mask, File.stat(installed_exec).mode unless win_platform?
+
+ wrapper = File.read installed_exec
+ assert_match %r|generated by RubyGems|, wrapper
+ end
+
+ def test_generate_bin_script_format
+ @installer.format_executable = true
+ @installer.wrappers = true
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ Gem::Installer.exec_format = 'foo-%s-bar'
+ @installer.generate_bin
+ assert_equal true, File.directory?(util_inst_bindir)
+ installed_exec = File.join util_inst_bindir, 'foo-executable-bar'
+ assert_path_exists installed_exec
+ ensure
+ Gem::Installer.exec_format = nil
+ end
+
+ def test_generate_bin_script_format_disabled
+ @installer.wrappers = true
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ Gem::Installer.exec_format = 'foo-%s-bar'
+ @installer.generate_bin
+ assert_equal true, File.directory?(util_inst_bindir)
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_path_exists installed_exec
+ ensure
+ Gem::Installer.exec_format = nil
+ end
+
+ def test_generate_bin_script_install_dir
+ @installer.wrappers = true
+
+ gem_dir = File.join("#{@gemhome}2", "gems", @spec.full_name)
+ gem_bindir = File.join gem_dir, 'bin'
+ FileUtils.mkdir_p gem_bindir
+ File.open File.join(gem_bindir, 'executable'), 'w' do |f|
+ f.puts "#!/bin/ruby"
+ end
+
+ @installer.gem_home = "#{@gemhome}2"
+ @installer.gem_dir = gem_dir
+ @installer.bin_dir = File.join "#{@gemhome}2", 'bin'
+
+ @installer.generate_bin
+
+ installed_exec = File.join("#{@gemhome}2", "bin", 'executable')
+ assert_path_exists installed_exec
+ assert_equal mask, File.stat(installed_exec).mode unless win_platform?
+
+ wrapper = File.read installed_exec
+ assert_match %r|generated by RubyGems|, wrapper
+ end
+
+ def test_generate_bin_script_no_execs
+ util_execless
+
+ @installer.wrappers = true
+ @installer.generate_bin
+
+ refute_path_exists util_inst_bindir, 'bin dir was created when not needed'
+ end
+
+ def test_generate_bin_script_no_perms
+ @installer.wrappers = true
+ util_make_exec
+
+ Dir.mkdir util_inst_bindir
+
+ if win_platform?
+ skip('test_generate_bin_script_no_perms skipped on MS Windows')
+ else
+ FileUtils.chmod 0000, util_inst_bindir
+
+ assert_raises Gem::FilePermissionError do
+ @installer.generate_bin
+ end
+ end
+ ensure
+ FileUtils.chmod 0755, util_inst_bindir unless ($DEBUG or win_platform?)
+ end
+
+ def test_generate_bin_script_no_shebang
+ @installer.wrappers = true
+ @spec.executables = %w[executable]
+
+ gem_dir = File.join @gemhome, 'gems', @spec.full_name
+ gem_bindir = File.join gem_dir, 'bin'
+ FileUtils.mkdir_p gem_bindir
+ File.open File.join(gem_bindir, 'executable'), 'w' do |f|
+ f.puts "blah blah blah"
+ end
+
+ @installer.generate_bin
+
+ installed_exec = File.join @gemhome, 'bin', 'executable'
+ assert_path_exists installed_exec
+ assert_equal mask, File.stat(installed_exec).mode unless win_platform?
+
+ wrapper = File.read installed_exec
+ assert_match %r|generated by RubyGems|, wrapper
+ # HACK some gems don't have #! in their executables, restore 2008/06
+ #assert_no_match %r|generated by RubyGems|, wrapper
+ end
+
+ def test_generate_bin_script_wrappers
+ @installer.wrappers = true
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+ installed_exec = File.join(util_inst_bindir, 'executable')
+
+ real_exec = File.join util_gem_dir, 'bin', 'executable'
+
+ # fake --no-wrappers for previous install
+ unless Gem.win_platform? then
+ FileUtils.mkdir_p File.dirname(installed_exec)
+ FileUtils.ln_s real_exec, installed_exec
+ end
+
+ @installer.generate_bin
+ assert_equal true, File.directory?(util_inst_bindir)
+ assert_path_exists installed_exec
+ assert_equal mask, File.stat(installed_exec).mode unless win_platform?
+
+ assert_match %r|generated by RubyGems|, File.read(installed_exec)
+
+ refute_match %r|generated by RubyGems|, File.read(real_exec),
+ 'real executable overwritten'
+ end
+
+ def test_generate_bin_symlink
+ return if win_platform? #Windows FS do not support symlinks
+
+ @installer.wrappers = false
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ @installer.generate_bin
+ assert_equal true, File.directory?(util_inst_bindir)
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_equal true, File.symlink?(installed_exec)
+ assert_equal(File.join(util_gem_dir, 'bin', 'executable'),
+ File.readlink(installed_exec))
+ end
+
+ def test_generate_bin_symlink_no_execs
+ util_execless
+
+ @installer.wrappers = false
+ @installer.generate_bin
+
+ refute_path_exists util_inst_bindir
+ end
+
+ def test_generate_bin_symlink_no_perms
+ @installer.wrappers = false
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ Dir.mkdir util_inst_bindir
+
+ if win_platform?
+ skip('test_generate_bin_symlink_no_perms skipped on MS Windows')
+ else
+ FileUtils.chmod 0000, util_inst_bindir
+
+ assert_raises Gem::FilePermissionError do
+ @installer.generate_bin
+ end
+ end
+ ensure
+ FileUtils.chmod 0755, util_inst_bindir unless ($DEBUG or win_platform?)
+ end
+
+ def test_generate_bin_symlink_update_newer
+ return if win_platform? #Windows FS do not support symlinks
+
+ @installer.wrappers = false
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ @installer.generate_bin
+ installed_exec = File.join(util_inst_bindir, 'executable')
+ assert_equal(File.join(util_gem_dir, 'bin', 'executable'),
+ File.readlink(installed_exec))
+
+ @spec = Gem::Specification.new do |s|
+ s.files = ['lib/code.rb']
+ s.name = "a"
+ s.version = "3"
+ s.summary = "summary"
+ s.description = "desc"
+ s.require_path = 'lib'
+ end
+
+ util_make_exec
+ @installer.gem_dir = util_gem_dir @spec
+ @installer.generate_bin
+ installed_exec = File.join(util_inst_bindir, 'executable')
+ assert_equal(@spec.bin_file('executable'),
+ File.readlink(installed_exec),
+ "Ensure symlink moved to latest version")
+ end
+
+ def test_generate_bin_symlink_update_older
+ return if win_platform? #Windows FS do not support symlinks
+
+ @installer.wrappers = false
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ @installer.generate_bin
+ installed_exec = File.join(util_inst_bindir, 'executable')
+ assert_equal(File.join(util_gem_dir, 'bin', 'executable'),
+ File.readlink(installed_exec))
+
+ spec = Gem::Specification.new do |s|
+ s.files = ['lib/code.rb']
+ s.name = "a"
+ s.version = "1"
+ s.summary = "summary"
+ s.description = "desc"
+ s.require_path = 'lib'
+ end
+
+ util_make_exec
+ one = @spec.dup
+ one.version = 1
+ @installer.gem_dir = util_gem_dir one
+ @installer.spec = spec
+
+ @installer.generate_bin
+
+ installed_exec = File.join util_inst_bindir, 'executable'
+ expected = File.join util_gem_dir, 'bin', 'executable'
+ assert_equal(expected,
+ File.readlink(installed_exec),
+ "Ensure symlink not moved")
+ end
+
+ def test_generate_bin_symlink_update_remove_wrapper
+ return if win_platform? #Windows FS do not support symlinks
+
+ @installer.wrappers = true
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ @installer.generate_bin
+
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_path_exists installed_exec
+
+ @spec = Gem::Specification.new do |s|
+ s.files = ['lib/code.rb']
+ s.name = "a"
+ s.version = "3"
+ s.summary = "summary"
+ s.description = "desc"
+ s.require_path = 'lib'
+ end
+ util_make_exec
+
+ util_installer @spec, @gemhome
+ @installer.wrappers = false
+ @installer.gem_dir = util_gem_dir
+
+ @installer.generate_bin
+
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_equal(@spec.bin_file('executable'),
+ File.readlink(installed_exec),
+ "Ensure symlink moved to latest version")
+ end
+
+ def test_generate_bin_symlink_win32
+ old_win_platform = Gem.win_platform?
+ Gem.win_platform = true
+ old_alt_separator = File::ALT_SEPARATOR
+ File.__send__(:remove_const, :ALT_SEPARATOR)
+ File.const_set(:ALT_SEPARATOR, '\\')
+ @installer.wrappers = false
+ util_make_exec
+ @installer.gem_dir = util_gem_dir
+
+ use_ui @ui do
+ @installer.generate_bin
+ end
+
+ assert_equal true, File.directory?(util_inst_bindir)
+ installed_exec = File.join(util_inst_bindir, 'executable')
+ assert_path_exists installed_exec
+
+ assert_match(/Unable to use symlinks on Windows, installing wrapper/i,
+ @ui.error)
+
+ wrapper = File.read installed_exec
+ assert_match(/generated by RubyGems/, wrapper)
+ ensure
+ File.__send__(:remove_const, :ALT_SEPARATOR)
+ File.const_set(:ALT_SEPARATOR, old_alt_separator)
+ Gem.win_platform = old_win_platform
+ end
+
+ def test_generate_bin_uses_default_shebang
+ return if win_platform? #Windows FS do not support symlinks
+
+ @installer.wrappers = true
+ util_make_exec
+
+ @installer.generate_bin
+
+ default_shebang = Gem.ruby
+ shebang_line = open("#{@gemhome}/bin/executable") { |f| f.readlines.first }
+ assert_match(/\A#!/, shebang_line)
+ assert_match(/#{default_shebang}/, shebang_line)
+ end
+
+ def test_initialize
+ spec = util_spec 'a' do |s| s.platform = Gem::Platform.new 'mswin32' end
+ gem = File.join @tempdir, spec.file_name
+
+ Dir.mkdir util_inst_bindir
+ util_build_gem spec
+ FileUtils.mv spec.cache_file, @tempdir
+
+ installer = Gem::Installer.new gem
+
+ assert_equal File.join(@gemhome, 'gems', spec.full_name), installer.gem_dir
+ assert_equal File.join(@gemhome, 'bin'), installer.bin_dir
+ end
+
+ def test_initialize_user_install
+ installer = Gem::Installer.new @gem, :user_install => true
+
+ assert_equal File.join(Gem.user_dir, 'gems', @spec.full_name),
+ installer.gem_dir
+ assert_equal Gem.bindir(Gem.user_dir), installer.bin_dir
+ end
+
+ def test_initialize_user_install_bin_dir
+ installer =
+ Gem::Installer.new @gem, :user_install => true, :bin_dir => @tempdir
+
+ assert_equal File.join(Gem.user_dir, 'gems', @spec.full_name),
+ installer.gem_dir
+ assert_equal @tempdir, installer.bin_dir
+ end
+
+ def test_install
+ Dir.mkdir util_inst_bindir
+ util_setup_gem
+ util_clear_gems
+
+ gemdir = File.join @gemhome, 'gems', @spec.full_name
+ cache_file = File.join @gemhome, 'cache', @spec.file_name
+ stub_exe = File.join @gemhome, 'bin', 'executable'
+ rakefile = File.join gemdir, 'ext', 'a', 'Rakefile'
+ spec_file = File.join @gemhome, 'specifications', @spec.spec_name
+
+ Gem.pre_install do |installer|
+ refute_path_exists cache_file, 'cache file must not exist yet'
+ refute_path_exists spec_file, 'spec file must not exist yet'
+ true
+ end
+
+ Gem.post_build do |installer|
+ assert_path_exists gemdir, 'gem install dir must exist'
+ assert_path_exists rakefile, 'gem executable must exist'
+ refute_path_exists stub_exe, 'gem executable must not exist'
+ refute_path_exists spec_file, 'spec file must not exist yet'
+ true
+ end
+
+ Gem.post_install do |installer|
+ assert_path_exists cache_file, 'cache file must exist'
+ assert_path_exists spec_file, 'spec file must exist'
+ end
+
+ @newspec = nil
+ build_rake_in do
+ use_ui @ui do
+ @newspec = @installer.install
+ end
+ end
+
+ assert_equal @spec, @newspec
+ assert_path_exists gemdir
+ assert_path_exists stub_exe, 'gem executable must exist'
+
+ exe = File.join gemdir, 'bin', 'executable'
+ assert_path_exists exe
+
+ exe_mode = File.stat(exe).mode & 0111
+ assert_equal 0111, exe_mode, "0%o" % exe_mode unless win_platform?
+
+ assert_path_exists File.join gemdir, 'lib', 'code.rb'
+
+ assert_path_exists rakefile
+
+ spec_file = File.join(@gemhome, 'specifications', @spec.spec_name)
+
+ assert_equal spec_file, @newspec.loaded_from
+ assert_path_exists spec_file
+
+ assert_same @installer, @post_build_hook_arg
+ assert_same @installer, @post_install_hook_arg
+ assert_same @installer, @pre_install_hook_arg
+ end
+
+ def test_install_creates_working_binstub
+ Dir.mkdir util_inst_bindir
+ util_setup_gem
+ util_clear_gems
+
+ @installer.wrappers = true
+
+ gemdir = File.join @gemhome, 'gems', @spec.full_name
+
+ @newspec = nil
+ build_rake_in do
+ use_ui @ui do
+ @newspec = @installer.install
+ end
+ end
+
+ exe = File.join gemdir, 'bin', 'executable'
+
+ e = assert_raises RuntimeError do
+ instance_eval File.read(exe)
+ end
+
+ assert_match(/ran executable/, e.message)
+ end
+
+ def test_install_creates_binstub_that_understand_version
+ Dir.mkdir util_inst_bindir
+ util_setup_gem
+ util_clear_gems
+
+ @installer.wrappers = true
+
+ @newspec = nil
+ build_rake_in do
+ use_ui @ui do
+ @newspec = @installer.install
+ end
+ end
+
+ exe = File.join @gemhome, 'bin', 'executable'
+
+ ARGV.unshift "_3.0_"
+
+ begin
+ Gem::Specification.reset
+
+ e = assert_raises Gem::LoadError do
+ instance_eval File.read(exe)
+ end
+ ensure
+ ARGV.shift if ARGV.first == "_3.0_"
+ end
+
+ assert_match(/\(= 3\.0\)/, e.message)
+ end
+
+ def test_install_creates_binstub_that_dont_trust_encoding
+ skip unless "".respond_to?(:force_encoding)
+
+ Dir.mkdir util_inst_bindir
+ util_setup_gem
+ util_clear_gems
+
+ @installer.wrappers = true
+
+ @newspec = nil
+ build_rake_in do
+ use_ui @ui do
+ @newspec = @installer.install
+ end
+ end
+
+ exe = File.join @gemhome, 'bin', 'executable'
+
+ extra_arg = "\xE4pfel".force_encoding("UTF-8")
+ ARGV.unshift extra_arg
+
+ begin
+ Gem::Specification.reset
+
+ e = assert_raises RuntimeError do
+ instance_eval File.read(exe)
+ end
+ ensure
+ ARGV.shift if ARGV.first == extra_arg
+ end
+
+ assert_match(/ran executable/, e.message)
+ end
+
+ def test_install_with_no_prior_files
+ Dir.mkdir util_inst_bindir
+ util_clear_gems
+
+ util_setup_gem
+ build_rake_in do
+ use_ui @ui do
+ assert_equal @spec, @installer.install
+ end
+ end
+
+ gemdir = File.join(@gemhome, 'gems', @spec.full_name)
+ assert_path_exists File.join gemdir, 'lib', 'code.rb'
+
+ util_setup_gem
+ # Morph spec to have lib/other.rb instead of code.rb and recreate
+ @spec.files = File.join('lib', 'other.rb')
+ Dir.chdir @tempdir do
+ File.open File.join('lib', 'other.rb'), 'w' do |f| f.puts '1' end
+ use_ui ui do
+ FileUtils.rm @gem
+ Gem::Package.build @spec
+ end
+ end
+ @installer = Gem::Installer.new @gem
+ build_rake_in do
+ use_ui @ui do
+ assert_equal @spec, @installer.install
+ end
+ end
+
+ assert_path_exists File.join gemdir, 'lib', 'other.rb'
+ refute_path_exists File.join gemdir, 'lib', 'code.rb',
+ "code.rb from prior install of same gem shouldn't remain here"
+ end
+
+ def test_install_force
+ use_ui @ui do
+ installer = Gem::Installer.new old_ruby_required, :force => true
+ installer.install
+ end
+
+ gem_dir = File.join(@gemhome, 'gems', 'old_ruby_required-1')
+ assert_path_exists gem_dir
+ end
+
+ def test_install_missing_dirs
+ FileUtils.rm_f File.join(Gem.dir, 'cache')
+ FileUtils.rm_f File.join(Gem.dir, 'docs')
+ FileUtils.rm_f File.join(Gem.dir, 'specifications')
+
+ use_ui @ui do
+ @installer.install
+ end
+
+ File.directory? File.join(Gem.dir, 'cache')
+ File.directory? File.join(Gem.dir, 'docs')
+ File.directory? File.join(Gem.dir, 'specifications')
+
+ assert_path_exists File.join @gemhome, 'cache', @spec.file_name
+ assert_path_exists File.join @gemhome, 'specifications', @spec.spec_name
+ end
+
+ def test_install_post_build_false
+ util_clear_gems
+
+ Gem.post_build do
+ false
+ end
+
+ use_ui @ui do
+ e = assert_raises Gem::InstallError do
+ @installer.install
+ end
+
+ location = "#{__FILE__}:#{__LINE__ - 9}"
+
+ assert_equal "post-build hook at #{location} failed for a-2", e.message
+ end
+
+ spec_file = File.join @gemhome, 'specifications', @spec.spec_name
+ refute_path_exists spec_file
+
+ gem_dir = File.join @gemhome, 'gems', @spec.full_name
+ refute_path_exists gem_dir
+ end
+
+ def test_install_post_build_nil
+ util_clear_gems
+
+ Gem.post_build do
+ nil
+ end
+
+ use_ui @ui do
+ @installer.install
+ end
+
+ spec_file = File.join @gemhome, 'specifications', @spec.spec_name
+ assert_path_exists spec_file
+
+ gem_dir = File.join @gemhome, 'gems', @spec.full_name
+ assert_path_exists gem_dir
+ end
+
+ def test_install_pre_install_false
+ util_clear_gems
+
+ Gem.pre_install do
+ false
+ end
+
+ use_ui @ui do
+ e = assert_raises Gem::InstallError do
+ @installer.install
+ end
+
+ location = "#{__FILE__}:#{__LINE__ - 9}"
+
+ assert_equal "pre-install hook at #{location} failed for a-2", e.message
+ end
+
+ spec_file = File.join @gemhome, 'specifications', @spec.spec_name
+ refute_path_exists spec_file
+ end
+
+ def test_install_pre_install_nil
+ util_clear_gems
+
+ Gem.pre_install do
+ nil
+ end
+
+ use_ui @ui do
+ @installer.install
+ end
+
+ spec_file = File.join @gemhome, 'specifications', @spec.spec_name
+ assert_path_exists spec_file
+ end
+
+ def test_install_with_message
+ @spec.post_install_message = 'I am a shiny gem!'
+
+ use_ui @ui do
+ path = Gem::Package.build @spec
+
+ @installer = Gem::Installer.new path
+ @installer.install
+ end
+
+ assert_match %r|I am a shiny gem!|, @ui.output
+ end
+
+ def test_install_extension_dir
+ gemhome2 = "#{@gemhome}2"
+
+ @spec.extensions << "extconf.rb"
+ write_file File.join(@tempdir, "extconf.rb") do |io|
+ io.write <<-RUBY
+ require "mkmf"
+ create_makefile("#{@spec.name}")
+ RUBY
+ end
+
+ @spec.files += %w[extconf.rb]
+
+ use_ui @ui do
+ path = Gem::Package.build @spec
+
+ installer = Gem::Installer.new path, :install_dir => gemhome2
+ installer.install
+ end
+
+ expected_makefile = File.join gemhome2, 'gems', @spec.full_name, 'Makefile'
+
+ assert_path_exists expected_makefile
+ end
+
+ def test_install_extension_and_script
+ @spec.extensions << "extconf.rb"
+ write_file File.join(@tempdir, "extconf.rb") do |io|
+ io.write <<-RUBY
+ require "mkmf"
+ create_makefile("#{@spec.name}")
+ RUBY
+ end
+
+ rb = File.join("lib", "#{@spec.name}.rb")
+ @spec.files += [rb]
+ write_file File.join(@tempdir, rb) do |io|
+ io.write <<-RUBY
+ # #{@spec.name}.rb
+ RUBY
+ end
+
+ Dir.mkdir(File.join("lib", @spec.name))
+ rb2 = File.join("lib", @spec.name, "#{@spec.name}.rb")
+ @spec.files << rb2
+ write_file File.join(@tempdir, rb2) do |io|
+ io.write <<-RUBY
+ # #{@spec.name}/#{@spec.name}.rb
+ RUBY
+ end
+
+ refute_path_exists File.join @spec.gem_dir, rb
+ refute_path_exists File.join @spec.gem_dir, rb2
+ use_ui @ui do
+ path = Gem::Package.build @spec
+
+ @installer = Gem::Installer.new path
+ @installer.install
+ end
+ assert_path_exists File.join @spec.gem_dir, rb
+ assert_path_exists File.join @spec.gem_dir, rb2
+ end
+
+ def test_install_extension_flat
+ skip '1.9.2 and earlier mkmf.rb does not create TOUCH' if
+ RUBY_VERSION < '1.9.3'
+
+ if RUBY_VERSION == "1.9.3" and RUBY_PATCHLEVEL <= 194
+ skip "TOUCH was introduced into 1.9.3 after p194"
+ end
+
+ @spec.require_paths = ["."]
+
+ @spec.extensions << "extconf.rb"
+
+ write_file File.join(@tempdir, "extconf.rb") do |io|
+ io.write <<-RUBY
+ require "mkmf"
+
+ CONFIG['CC'] = '$(TOUCH) $@ ||'
+ CONFIG['LDSHARED'] = '$(TOUCH) $@ ||'
+ $ruby = '#{Gem.ruby}'
+
+ create_makefile("#{@spec.name}")
+ RUBY
+ end
+
+ # empty depend file for no auto dependencies
+ @spec.files += %W"depend #{@spec.name}.c".each {|file|
+ write_file File.join(@tempdir, file)
+ }
+
+ so = File.join(@spec.gem_dir, "#{@spec.name}.#{RbConfig::CONFIG["DLEXT"]}")
+ refute_path_exists so
+ use_ui @ui do
+ path = Gem::Package.build @spec
+
+ @installer = Gem::Installer.new path
+ @installer.install
+ end
+ assert_path_exists so
+ rescue
+ puts '-' * 78
+ puts File.read File.join(@gemhome, 'gems', 'a-2', 'Makefile')
+ puts '-' * 78
+
+ path = File.join(@gemhome, 'gems', 'a-2', 'gem_make.out')
+
+ if File.exist?(path)
+ puts File.read(path)
+ puts '-' * 78
+ end
+
+ raise
+ end
+
+ def test_installation_satisfies_dependency_eh
+ util_spec 'a'
+
+ dep = Gem::Dependency.new 'a', '>= 2'
+ assert @installer.installation_satisfies_dependency?(dep)
+
+ dep = Gem::Dependency.new 'a', '> 2'
+ refute @installer.installation_satisfies_dependency?(dep)
+ end
+
+ def test_installation_satisfies_dependency_eh_development
+ @installer.options[:development] = true
+ @installer.options[:dev_shallow] = true
+
+ util_spec 'a'
+
+ dep = Gem::Dependency.new 'a', :development
+ assert @installer.installation_satisfies_dependency?(dep)
+ end
+
+ def test_pre_install_checks_dependencies
+ @spec.add_dependency 'b', '> 5'
+ util_setup_gem
+
+ use_ui @ui do
+ assert_raises Gem::InstallError do
+ @installer.install
+ end
+ end
+ end
+
+ def test_pre_install_checks_dependencies_ignore
+ @spec.add_dependency 'b', '> 5'
+ @installer.ignore_dependencies = true
+
+ build_rake_in do
+ use_ui @ui do
+ assert @installer.pre_install_checks
+ end
+ end
+ end
+
+ def test_pre_install_checks_dependencies_install_dir
+ gemhome2 = "#{@gemhome}2"
+ @spec.add_dependency 'd'
+
+ quick_gem 'd', 2
+
+ gem = File.join @gemhome, @spec.file_name
+
+ FileUtils.mv @gemhome, gemhome2
+ FileUtils.mkdir @gemhome
+
+ FileUtils.mv File.join(gemhome2, 'cache', @spec.file_name), gem
+
+ # Don't leak any already activated gems into the installer, require
+ # that it work everything out on it's own.
+ Gem::Specification.reset
+
+ installer = Gem::Installer.new gem, :install_dir => gemhome2
+
+ build_rake_in do
+ use_ui @ui do
+ assert installer.pre_install_checks
+ end
+ end
+ end
+
+ def test_pre_install_checks_ruby_version
+ use_ui @ui do
+ installer = Gem::Installer.new old_ruby_required
+ e = assert_raises Gem::InstallError do
+ installer.pre_install_checks
+ end
+ assert_equal 'old_ruby_required requires Ruby version = 1.4.6.',
+ e.message
+ end
+ end
+
+ def test_pre_install_checks_wrong_rubygems_version
+ spec = util_spec 'old_rubygems_required', '1' do |s|
+ s.required_rubygems_version = '< 0'
+ end
+
+ util_build_gem spec
+
+ gem = File.join(@gemhome, 'cache', spec.file_name)
+
+ use_ui @ui do
+ @installer = Gem::Installer.new gem
+ e = assert_raises Gem::InstallError do
+ @installer.pre_install_checks
+ end
+ assert_equal 'old_rubygems_required requires RubyGems version < 0. ' +
+ "Try 'gem update --system' to update RubyGems itself.", e.message
+ end
+ end
+
+ def test_shebang
+ util_make_exec @spec, "#!/usr/bin/ruby"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby}", shebang
+ end
+
+ def test_process_options
+ assert_nil @installer.build_root
+ assert_equal File.join(@gemhome, 'bin'), @installer.bin_dir
+ assert_equal @gemhome, @installer.gem_home
+ end
+
+ def test_process_options_build_root
+ build_root = File.join @tempdir, 'build_root'
+
+ @installer = Gem::Installer.new @gem, :build_root => build_root
+
+ assert_equal Pathname(build_root), @installer.build_root
+ assert_equal File.join(build_root, @gemhome, 'bin'), @installer.bin_dir
+ assert_equal File.join(build_root, @gemhome), @installer.gem_home
+ end
+
+ def test_shebang_arguments
+ util_make_exec @spec, "#!/usr/bin/ruby -ws"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby} -ws", shebang
+ end
+
+ def test_shebang_empty
+ util_make_exec @spec, ''
+
+ shebang = @installer.shebang 'executable'
+ assert_equal "#!#{Gem.ruby}", shebang
+ end
+
+ def test_shebang_env
+ util_make_exec @spec, "#!/usr/bin/env ruby"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby}", shebang
+ end
+
+ def test_shebang_env_arguments
+ util_make_exec @spec, "#!/usr/bin/env ruby -ws"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby} -ws", shebang
+ end
+
+ def test_shebang_env_shebang
+ util_make_exec @spec, ''
+ @installer.env_shebang = true
+
+ shebang = @installer.shebang 'executable'
+
+ env_shebang = "/usr/bin/env" unless Gem.win_platform?
+
+ assert_equal("#!#{env_shebang} #{RbConfig::CONFIG['ruby_install_name']}",
+ shebang)
+ end
+
+ def test_shebang_nested
+ util_make_exec @spec, "#!/opt/local/ruby/bin/ruby"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby}", shebang
+ end
+
+ def test_shebang_nested_arguments
+ util_make_exec @spec, "#!/opt/local/ruby/bin/ruby -ws"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby} -ws", shebang
+ end
+
+ def test_shebang_version
+ util_make_exec @spec, "#!/usr/bin/ruby18"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby}", shebang
+ end
+
+ def test_shebang_version_arguments
+ util_make_exec @spec, "#!/usr/bin/ruby18 -ws"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby} -ws", shebang
+ end
+
+ def test_shebang_version_env
+ util_make_exec @spec, "#!/usr/bin/env ruby18"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby}", shebang
+ end
+
+ def test_shebang_version_env_arguments
+ util_make_exec @spec, "#!/usr/bin/env ruby18 -ws"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!#{Gem.ruby} -ws", shebang
+ end
+
+ def test_shebang_custom
+ conf = Gem::ConfigFile.new []
+ conf[:custom_shebang] = 'test'
+
+ Gem.configuration = conf
+
+ util_make_exec @spec, "#!/usr/bin/ruby"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!test", shebang
+ end
+
+ def test_shebang_custom_with_expands
+ bin_env = win_platform? ? '' : '/usr/bin/env'
+ conf = Gem::ConfigFile.new []
+ conf[:custom_shebang] = '1 $env 2 $ruby 3 $exec 4 $name'
+
+ Gem.configuration = conf
+
+ util_make_exec @spec, "#!/usr/bin/ruby"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!1 #{bin_env} 2 #{Gem.ruby} 3 executable 4 a", shebang
+ end
+
+ def test_shebang_custom_with_expands_and_arguments
+ bin_env = win_platform? ? '' : '/usr/bin/env'
+ conf = Gem::ConfigFile.new []
+ conf[:custom_shebang] = '1 $env 2 $ruby 3 $exec'
+
+ Gem.configuration = conf
+
+ util_make_exec @spec, "#!/usr/bin/ruby -ws"
+
+ shebang = @installer.shebang 'executable'
+
+ assert_equal "#!1 #{bin_env} 2 #{Gem.ruby} -ws 3 executable", shebang
+ end
+
+ def test_unpack
+ util_setup_gem
+
+ dest = File.join @gemhome, 'gems', @spec.full_name
+
+ @installer.unpack dest
+
+ assert_path_exists File.join dest, 'lib', 'code.rb'
+ assert_path_exists File.join dest, 'bin', 'executable'
+ end
+
+ def test_write_build_info_file
+ refute_path_exists @spec.build_info_file
+
+ @installer.build_args = %w[
+ --with-libyaml-dir /usr/local/Cellar/libyaml/0.1.4
+ ]
+
+ @installer.write_build_info_file
+
+ assert_path_exists @spec.build_info_file
+
+ expected = "--with-libyaml-dir\n/usr/local/Cellar/libyaml/0.1.4\n"
+
+ assert_equal expected, File.read(@spec.build_info_file)
+ end
+
+ def test_write_build_info_file_empty
+ refute_path_exists @spec.build_info_file
+
+ @installer.write_build_info_file
+
+ refute_path_exists @spec.build_info_file
+ end
+
+ def test_write_build_info_file_install_dir
+ installer = Gem::Installer.new @gem, :install_dir => "#{@gemhome}2"
+
+ installer.build_args = %w[
+ --with-libyaml-dir /usr/local/Cellar/libyaml/0.1.4
+ ]
+
+ installer.write_build_info_file
+
+ refute_path_exists @spec.build_info_file
+ assert_path_exists \
+ File.join("#{@gemhome}2", 'build_info', "#{@spec.full_name}.info")
+ end
+
+ def test_write_cache_file
+ cache_file = File.join @gemhome, 'cache', @spec.file_name
+ gem = File.join @gemhome, @spec.file_name
+
+ FileUtils.mv cache_file, gem
+ refute_path_exists cache_file
+
+ installer = Gem::Installer.new gem
+ installer.spec = @spec
+ installer.gem_home = @gemhome
+
+ installer.write_cache_file
+
+ assert_path_exists cache_file
+ end
+
+ def test_write_spec
+ FileUtils.rm @spec.spec_file
+ refute_path_exists @spec.spec_file
+
+ @installer.spec = @spec
+ @installer.gem_home = @gemhome
+
+ @installer.write_spec
+
+ assert_path_exists @spec.spec_file
+
+ loaded = Gem::Specification.load @spec.spec_file
+
+ assert_equal @spec, loaded
+
+ assert_equal Gem.rubygems_version, @spec.installed_by_version
+ end
+
+ def test_write_spec_writes_cached_spec
+ FileUtils.rm @spec.spec_file
+ refute_path_exists @spec.spec_file
+
+ @spec.files = %w[a.rb b.rb c.rb]
+
+ @installer.spec = @spec
+ @installer.gem_home = @gemhome
+
+ @installer.write_spec
+
+ # cached specs have no file manifest:
+ @spec.files = []
+
+ assert_equal @spec, eval(File.read(@spec.spec_file))
+ end
+
+ def test_dir
+ assert_match %r!/gemhome/gems/a-2$!, @installer.dir
+ end
+
+ def test_default_gem
+ FileUtils.rm_f File.join(Gem.dir, 'specifications')
+
+ @installer.wrappers = true
+ @installer.options[:install_as_default] = true
+ @installer.gem_dir = util_gem_dir @spec
+ @installer.generate_bin
+
+ use_ui @ui do
+ @installer.install
+ end
+
+ assert File.directory? util_inst_bindir
+ installed_exec = File.join util_inst_bindir, 'executable'
+ assert_path_exists installed_exec
+
+ assert File.directory? File.join(Gem.dir, 'specifications')
+ assert File.directory? File.join(Gem.dir, 'specifications', 'default')
+
+ default_spec = eval File.read File.join(Gem.dir, 'specifications', 'default', 'a-2.gemspec')
+ assert_equal Gem::Version.new("2"), default_spec.version
+ assert_equal ['bin/executable'], default_spec.files
+ end
+
+ def old_ruby_required
+ spec = util_spec 'old_ruby_required', '1' do |s|
+ s.required_ruby_version = '= 1.4.6'
+ end
+
+ util_build_gem spec
+
+ spec.cache_file
+ end
+
+ def util_execless
+ @spec = util_spec 'z'
+ util_build_gem @spec
+
+ @installer = util_installer @spec, @gemhome
+ end
+
+ def util_conflict_executable wrappers
+ conflict = quick_gem 'conflict' do |spec|
+ util_make_exec spec
+ end
+
+ util_build_gem conflict
+
+ installer = util_installer conflict, @gemhome
+ installer.wrappers = wrappers
+ installer.generate_bin
+ end
+
+ def mask
+ 0100755 & (~File.umask)
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_local_remote_options.rb b/jni/ruby/test/rubygems/test_gem_local_remote_options.rb
new file mode 100644
index 0000000..1a0338b
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_local_remote_options.rb
@@ -0,0 +1,133 @@
+require 'rubygems/test_case'
+require 'rubygems/local_remote_options'
+require 'rubygems/command'
+
+class TestGemLocalRemoteOptions < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Command.new 'dummy', 'dummy'
+ @cmd.extend Gem::LocalRemoteOptions
+ end
+
+ def test_add_local_remote_options
+ @cmd.add_local_remote_options
+
+ args = %w[-l -r -b -B 10 --source http://gems.example.com -p --update-sources]
+ assert @cmd.handles?(args)
+ end
+
+ def test_both_eh
+ assert_equal false, @cmd.both?
+
+ @cmd.options[:domain] = :local
+
+ assert_equal false, @cmd.both?
+
+ @cmd.options[:domain] = :both
+
+ assert_equal true, @cmd.both?
+ end
+
+ def test_clear_sources_option
+ @cmd.add_local_remote_options
+
+ s = URI.parse "http://only-gems.example.com/"
+
+ @cmd.handle_options %W[--clear-sources --source #{s}]
+ assert_equal [s.to_s], Gem.sources
+ end
+
+ def test_clear_sources_option_idiot_proof
+ spec_fetcher
+
+ @cmd.add_local_remote_options
+ @cmd.handle_options %W[--clear-sources]
+ assert_equal Gem.default_sources, Gem.sources
+ end
+
+ def test_local_eh
+ assert_equal false, @cmd.local?
+
+ @cmd.options[:domain] = :local
+
+ assert_equal true, @cmd.local?
+
+ @cmd.options[:domain] = :both
+
+ assert_equal true, @cmd.local?
+ end
+
+ def test_remote_eh
+ assert_equal false, @cmd.remote?
+
+ @cmd.options[:domain] = :remote
+
+ assert_equal true, @cmd.remote?
+
+ @cmd.options[:domain] = :both
+
+ assert_equal true, @cmd.remote?
+ end
+
+ def test_source_option
+ @cmd.add_source_option
+
+ s1 = URI.parse 'http://more-gems.example.com/'
+ s2 = URI.parse 'http://even-more-gems.example.com/'
+ s3 = URI.parse 'http://other-gems.example.com/some_subdir'
+ s4 = URI.parse 'http://more-gems.example.com/' # Intentional duplicate
+
+ original_sources = Gem.sources.dup
+
+ @cmd.handle_options %W[--source #{s1} --source #{s2} --source #{s3} --source #{s4}]
+
+ original_sources << s1.to_s
+ original_sources << s2.to_s
+ original_sources << "#{s3}/"
+
+ assert_equal original_sources, Gem.sources
+ end
+
+ def test_short_source_option
+ @cmd.add_source_option
+
+ original_sources = Gem.sources.dup
+
+ source = URI.parse 'http://more-gems.example.com/'
+ @cmd.handle_options %W[-s #{source}]
+
+ original_sources << source
+
+ assert_equal original_sources, Gem.sources
+ end
+
+ def test_update_sources_option
+ @cmd.add_update_sources_option
+
+ Gem.configuration.update_sources = false
+
+ @cmd.handle_options %W[--update-sources]
+
+ assert_equal true, Gem.configuration.update_sources
+
+ @cmd.handle_options %W[--no-update-sources]
+
+ assert_equal false, Gem.configuration.update_sources
+ end
+
+ def test_source_option_bad
+ @cmd.add_source_option
+
+ s1 = 'htp://more-gems.example.com'
+
+ assert_raises OptionParser::InvalidArgument do
+ @cmd.handle_options %W[--source #{s1}]
+ end
+
+ assert_equal [@gem_repo], Gem.sources
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_name_tuple.rb b/jni/ruby/test/rubygems/test_gem_name_tuple.rb
new file mode 100644
index 0000000..38320f7
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_name_tuple.rb
@@ -0,0 +1,44 @@
+require 'rubygems/test_case'
+require 'rubygems/name_tuple'
+
+class TestGemNameTuple < Gem::TestCase
+
+ def test_full_name
+ n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
+ assert_equal "a-0", n.full_name
+
+ n = Gem::NameTuple.new "a", Gem::Version.new(0), nil
+ assert_equal "a-0", n.full_name
+
+ n = Gem::NameTuple.new "a", Gem::Version.new(0), ""
+ assert_equal "a-0", n.full_name
+
+ n = Gem::NameTuple.new "a", Gem::Version.new(0), "other"
+ assert_equal "a-0-other", n.full_name
+ end
+
+ def test_platform_normalization
+ n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
+ assert_equal "ruby", n.platform
+
+ n = Gem::NameTuple.new "a", Gem::Version.new(0), nil
+ assert_equal "ruby", n.platform
+
+ n = Gem::NameTuple.new "a", Gem::Version.new(0), ""
+ assert_equal "ruby", n.platform
+ end
+
+ def test_spec_name
+ n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
+ assert_equal "a-0.gemspec", n.spec_name
+ end
+
+ def test_spaceship
+ a = Gem::NameTuple.new 'a', Gem::Version.new(0), Gem::Platform::RUBY
+ a_p = Gem::NameTuple.new 'a', Gem::Version.new(0), Gem::Platform.local
+
+ assert_equal 1, a_p.<=>(a)
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_package.rb b/jni/ruby/test/rubygems/test_gem_package.rb
new file mode 100644
index 0000000..128dcdb
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_package.rb
@@ -0,0 +1,824 @@
+# coding: UTF-8
+
+require 'rubygems/package/tar_test_case'
+require 'rubygems/simple_gem'
+
+class TestGemPackage < Gem::Package::TarTestCase
+
+ def setup
+ super
+
+ @spec = quick_gem 'a' do |s|
+ s.description = 'π'
+ s.files = %w[lib/code.rb]
+ end
+
+ util_build_gem @spec
+
+ @gem = @spec.cache_file
+
+ @destination = File.join @tempdir, 'extract'
+
+ FileUtils.mkdir_p @destination
+ end
+
+ def test_class_new_old_format
+ open 'old_format.gem', 'wb' do |io|
+ io.write SIMPLE_GEM
+ end
+
+ package = Gem::Package.new 'old_format.gem'
+
+ assert package.spec
+ end
+
+ def test_add_checksums
+ gem_io = StringIO.new
+
+ spec = Gem::Specification.new 'build', '1'
+ spec.summary = 'build'
+ spec.authors = 'build'
+ spec.files = ['lib/code.rb']
+ spec.date = Time.at 0
+ spec.rubygems_version = Gem::Version.new '0'
+
+ FileUtils.mkdir 'lib'
+
+ open 'lib/code.rb', 'w' do |io|
+ io.write '# lib/code.rb'
+ end
+
+ package = Gem::Package.new spec.file_name
+ package.spec = spec
+ package.build_time = 1 # 0 uses current time
+ package.setup_signer
+
+ Gem::Package::TarWriter.new gem_io do |gem|
+ package.add_metadata gem
+ package.add_contents gem
+ package.add_checksums gem
+ end
+
+ gem_io.rewind
+
+ reader = Gem::Package::TarReader.new gem_io
+
+ checksums = nil
+ tar = nil
+
+ reader.each_entry do |entry|
+ case entry.full_name
+ when 'checksums.yaml.gz' then
+ Zlib::GzipReader.wrap entry do |io|
+ checksums = io.read
+ end
+ when 'data.tar.gz' then
+ tar = entry.read
+ end
+ end
+
+ s = StringIO.new
+
+ package.gzip_to s do |io|
+ io.write spec.to_yaml
+ end
+
+ metadata_sha1 = Digest::SHA1.hexdigest s.string
+ metadata_sha512 = Digest::SHA512.hexdigest s.string
+
+ expected = {
+ 'SHA512' => {
+ 'metadata.gz' => metadata_sha512,
+ 'data.tar.gz' => Digest::SHA512.hexdigest(tar),
+ }
+ }
+
+ if defined?(OpenSSL::Digest) then
+ expected['SHA1'] = {
+ 'metadata.gz' => metadata_sha1,
+ 'data.tar.gz' => Digest::SHA1.hexdigest(tar),
+ }
+ end
+
+ assert_equal expected, YAML.load(checksums)
+ end
+
+ def test_add_files
+ spec = Gem::Specification.new
+ spec.files = %w[lib/code.rb lib/empty]
+
+ FileUtils.mkdir_p 'lib/empty'
+
+ open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
+ open 'lib/extra.rb', 'w' do |io| io.write '# lib/extra.rb' end
+
+ package = Gem::Package.new 'bogus.gem'
+ package.spec = spec
+
+ tar = util_tar do |tar_io|
+ package.add_files tar_io
+ end
+
+ tar.rewind
+
+ files = []
+
+ Gem::Package::TarReader.new tar do |tar_io|
+ tar_io.each_entry do |entry|
+ files << entry.full_name
+ end
+ end
+
+ assert_equal %w[lib/code.rb], files
+ end
+
+ def test_build
+ spec = Gem::Specification.new 'build', '1'
+ spec.summary = 'build'
+ spec.authors = 'build'
+ spec.files = ['lib/code.rb']
+ spec.rubygems_version = :junk
+
+ FileUtils.mkdir 'lib'
+
+ open 'lib/code.rb', 'w' do |io|
+ io.write '# lib/code.rb'
+ end
+
+ package = Gem::Package.new spec.file_name
+ package.spec = spec
+
+ package.build
+
+ assert_equal Gem::VERSION, spec.rubygems_version
+ assert_path_exists spec.file_name
+
+ reader = Gem::Package.new spec.file_name
+ assert_equal spec, reader.spec
+
+ assert_equal %w[metadata.gz data.tar.gz checksums.yaml.gz],
+ reader.files
+
+ assert_equal %w[lib/code.rb], reader.contents
+ end
+
+ def test_build_auto_signed
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
+
+ private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
+ Gem::Security.write PRIVATE_KEY, private_key_path
+
+ public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
+ FileUtils.cp PUBLIC_CERT_PATH, public_cert_path
+
+ spec = Gem::Specification.new 'build', '1'
+ spec.summary = 'build'
+ spec.authors = 'build'
+ spec.files = ['lib/code.rb']
+
+ FileUtils.mkdir 'lib'
+
+ open 'lib/code.rb', 'w' do |io|
+ io.write '# lib/code.rb'
+ end
+
+ package = Gem::Package.new spec.file_name
+ package.spec = spec
+
+ package.build
+
+ assert_equal Gem::VERSION, spec.rubygems_version
+ assert_path_exists spec.file_name
+
+ reader = Gem::Package.new spec.file_name
+ assert reader.verify
+
+ assert_equal [PUBLIC_CERT.to_pem], reader.spec.cert_chain
+
+ assert_equal %w[metadata.gz metadata.gz.sig
+ data.tar.gz data.tar.gz.sig
+ checksums.yaml.gz checksums.yaml.gz.sig],
+ reader.files
+
+ assert_equal %w[lib/code.rb], reader.contents
+ end
+
+ def test_build_auto_signed_encrypted_key
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
+
+ private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
+ FileUtils.cp ENCRYPTED_PRIVATE_KEY_PATH, private_key_path
+
+ public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
+ Gem::Security.write PUBLIC_CERT, public_cert_path
+
+ spec = Gem::Specification.new 'build', '1'
+ spec.summary = 'build'
+ spec.authors = 'build'
+ spec.files = ['lib/code.rb']
+
+ FileUtils.mkdir 'lib'
+
+ open 'lib/code.rb', 'w' do |io|
+ io.write '# lib/code.rb'
+ end
+
+ package = Gem::Package.new spec.file_name
+ package.spec = spec
+
+ package.build
+
+ assert_equal Gem::VERSION, spec.rubygems_version
+ assert_path_exists spec.file_name
+
+ reader = Gem::Package.new spec.file_name
+ assert reader.verify
+
+ assert_equal [PUBLIC_CERT.to_pem], reader.spec.cert_chain
+
+ assert_equal %w[metadata.gz metadata.gz.sig
+ data.tar.gz data.tar.gz.sig
+ checksums.yaml.gz checksums.yaml.gz.sig],
+ reader.files
+
+ assert_equal %w[lib/code.rb], reader.contents
+ end
+
+ def test_build_invalid
+ spec = Gem::Specification.new 'build', '1'
+
+ package = Gem::Package.new spec.file_name
+ package.spec = spec
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ package.build
+ end
+
+ assert_equal 'missing value for attribute summary', e.message
+ end
+
+ def test_build_signed
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ spec = Gem::Specification.new 'build', '1'
+ spec.summary = 'build'
+ spec.authors = 'build'
+ spec.files = ['lib/code.rb']
+ spec.cert_chain = [PUBLIC_CERT.to_pem]
+ spec.signing_key = PRIVATE_KEY
+
+ FileUtils.mkdir 'lib'
+
+ open 'lib/code.rb', 'w' do |io|
+ io.write '# lib/code.rb'
+ end
+
+ package = Gem::Package.new spec.file_name
+ package.spec = spec
+
+ package.build
+
+ assert_equal Gem::VERSION, spec.rubygems_version
+ assert_path_exists spec.file_name
+
+ reader = Gem::Package.new spec.file_name
+ assert reader.verify
+
+ assert_equal spec, reader.spec
+
+ assert_equal %w[metadata.gz metadata.gz.sig
+ data.tar.gz data.tar.gz.sig
+ checksums.yaml.gz checksums.yaml.gz.sig],
+ reader.files
+
+ assert_equal %w[lib/code.rb], reader.contents
+ end
+
+ def test_build_signed_encryped_key
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ spec = Gem::Specification.new 'build', '1'
+ spec.summary = 'build'
+ spec.authors = 'build'
+ spec.files = ['lib/code.rb']
+ spec.cert_chain = [PUBLIC_CERT.to_pem]
+ spec.signing_key = ENCRYPTED_PRIVATE_KEY
+
+ FileUtils.mkdir 'lib'
+
+ open 'lib/code.rb', 'w' do |io|
+ io.write '# lib/code.rb'
+ end
+
+ package = Gem::Package.new spec.file_name
+ package.spec = spec
+
+ package.build
+
+ assert_equal Gem::VERSION, spec.rubygems_version
+ assert_path_exists spec.file_name
+
+ reader = Gem::Package.new spec.file_name
+ assert reader.verify
+
+ assert_equal spec, reader.spec
+
+ assert_equal %w[metadata.gz metadata.gz.sig
+ data.tar.gz data.tar.gz.sig
+ checksums.yaml.gz checksums.yaml.gz.sig],
+ reader.files
+
+ assert_equal %w[lib/code.rb], reader.contents
+ end
+
+ def test_contents
+ package = Gem::Package.new @gem
+
+ assert_equal %w[lib/code.rb], package.contents
+ end
+
+ def test_extract_files
+ package = Gem::Package.new @gem
+
+ package.extract_files @destination
+
+ extracted = File.join @destination, 'lib/code.rb'
+ assert_path_exists extracted
+
+ mask = 0100666 & (~File.umask)
+
+ assert_equal mask.to_s(8), File.stat(extracted).mode.to_s(8) unless
+ win_platform?
+ end
+
+ def test_extract_files_empty
+ data_tgz = util_tar_gz do end
+
+ gem = util_tar do |tar|
+ tar.add_file 'data.tar.gz', 0644 do |io|
+ io.write data_tgz.string
+ end
+
+ tar.add_file 'metadata.gz', 0644 do |io|
+ Zlib::GzipWriter.wrap io do |gzio|
+ gzio.write @spec.to_yaml
+ end
+ end
+ end
+
+ open 'empty.gem', 'wb' do |io|
+ io.write gem.string
+ end
+
+ package = Gem::Package.new 'empty.gem'
+
+ package.extract_files @destination
+
+ assert_path_exists @destination
+ end
+
+ def test_extract_tar_gz_absolute
+ package = Gem::Package.new @gem
+
+ tgz_io = util_tar_gz do |tar|
+ tar.add_file '/absolute.rb', 0644 do |io| io.write 'hi' end
+ end
+
+ e = assert_raises Gem::Package::PathError do
+ package.extract_tar_gz tgz_io, @destination
+ end
+
+ assert_equal("installing into parent path /absolute.rb of " +
+ "#{@destination} is not allowed", e.message)
+ end
+
+ def test_extract_tar_gz_directory
+ package = Gem::Package.new @gem
+
+ tgz_io = util_tar_gz do |tar|
+ tar.mkdir 'lib', 0755
+ tar.add_file 'lib/foo.rb', 0644 do |io| io.write 'hi' end
+ tar.mkdir 'lib/foo', 0755
+ end
+
+ package.extract_tar_gz tgz_io, @destination
+
+ extracted = File.join @destination, 'lib/foo.rb'
+ assert_path_exists extracted
+
+ extracted = File.join @destination, 'lib/foo'
+ assert_path_exists extracted
+ end
+
+ def test_extract_tar_gz_dot_slash
+ package = Gem::Package.new @gem
+
+ tgz_io = util_tar_gz do |tar|
+ tar.add_file './dot_slash.rb', 0644 do |io| io.write 'hi' end
+ end
+
+ package.extract_tar_gz tgz_io, @destination
+
+ extracted = File.join @destination, 'dot_slash.rb'
+ assert_path_exists extracted
+ end
+
+ def test_extract_tar_gz_dot_file
+ package = Gem::Package.new @gem
+
+ tgz_io = util_tar_gz do |tar|
+ tar.add_file '.dot_file.rb', 0644 do |io| io.write 'hi' end
+ end
+
+ package.extract_tar_gz tgz_io, @destination
+
+ extracted = File.join @destination, '.dot_file.rb'
+ assert_path_exists extracted
+ end
+
+ def test_install_location
+ package = Gem::Package.new @gem
+
+ file = 'file.rb'
+ file.taint
+
+ destination = package.install_location file, @destination
+
+ assert_equal File.join(@destination, 'file.rb'), destination
+ refute destination.tainted?
+ end
+
+ def test_install_location_absolute
+ package = Gem::Package.new @gem
+
+ e = assert_raises Gem::Package::PathError do
+ package.install_location '/absolute.rb', @destination
+ end
+
+ assert_equal("installing into parent path /absolute.rb of " +
+ "#{@destination} is not allowed", e.message)
+ end
+
+ def test_install_location_dots
+ package = Gem::Package.new @gem
+
+ file = 'file.rb'
+
+ destination = File.join @destination, 'foo', '..', 'bar'
+
+ FileUtils.mkdir_p File.join @destination, 'foo'
+ FileUtils.mkdir_p File.expand_path destination
+
+ destination = package.install_location file, destination
+
+ # this test only fails on ruby missing File.realpath
+ assert_equal File.join(@destination, 'bar', 'file.rb'), destination
+ end
+
+ def test_install_location_extra_slash
+ skip 'no File.realpath on 1.8' if RUBY_VERSION < '1.9'
+ package = Gem::Package.new @gem
+
+ file = 'foo//file.rb'
+ file.taint
+
+ destination = @destination.sub '/', '//'
+
+ destination = package.install_location file, destination
+
+ assert_equal File.join(@destination, 'foo', 'file.rb'), destination
+ refute destination.tainted?
+ end
+
+ def test_install_location_relative
+ package = Gem::Package.new @gem
+
+ e = assert_raises Gem::Package::PathError do
+ package.install_location '../relative.rb', @destination
+ end
+
+ parent = File.expand_path File.join @destination, "../relative.rb"
+
+ assert_equal("installing into parent path #{parent} of " +
+ "#{@destination} is not allowed", e.message)
+ end
+
+ def test_load_spec
+ entry = StringIO.new Gem.gzip @spec.to_yaml
+ def entry.full_name() 'metadata.gz' end
+
+ package = Gem::Package.new 'nonexistent.gem'
+
+ spec = package.load_spec entry
+
+ assert_equal @spec, spec
+ end
+
+ def test_verify
+ package = Gem::Package.new @gem
+
+ package.verify
+
+ assert_equal @spec, package.spec
+ assert_equal %w[checksums.yaml.gz data.tar.gz metadata.gz],
+ package.files.sort
+ end
+
+ def test_verify_checksum_bad
+ data_tgz = util_tar_gz do |tar|
+ tar.add_file 'lib/code.rb', 0444 do |io|
+ io.write '# lib/code.rb'
+ end
+ end
+
+ data_tgz = data_tgz.string
+
+ gem = util_tar do |tar|
+ metadata_gz = Gem.gzip @spec.to_yaml
+
+ tar.add_file 'metadata.gz', 0444 do |io|
+ io.write metadata_gz
+ end
+
+ tar.add_file 'data.tar.gz', 0444 do |io|
+ io.write data_tgz
+ end
+
+ bogus_checksums = {
+ 'SHA1' => {
+ 'data.tar.gz' => 'bogus',
+ 'metadata.gz' => 'bogus',
+ },
+ }
+ tar.add_file 'checksums.yaml.gz', 0444 do |io|
+ Zlib::GzipWriter.wrap io do |gz_io|
+ gz_io.write YAML.dump bogus_checksums
+ end
+ end
+ end
+
+ open 'mismatch.gem', 'wb' do |io|
+ io.write gem.string
+ end
+
+ package = Gem::Package.new 'mismatch.gem'
+
+ e = assert_raises Gem::Package::FormatError do
+ package.verify
+ end
+
+ assert_equal 'SHA1 checksum mismatch for data.tar.gz in mismatch.gem',
+ e.message
+ end
+
+ def test_verify_checksum_missing
+ data_tgz = util_tar_gz do |tar|
+ tar.add_file 'lib/code.rb', 0444 do |io|
+ io.write '# lib/code.rb'
+ end
+ end
+
+ data_tgz = data_tgz.string
+
+ gem = util_tar do |tar|
+ metadata_gz = Gem.gzip @spec.to_yaml
+
+ tar.add_file 'metadata.gz', 0444 do |io|
+ io.write metadata_gz
+ end
+
+ digest = Digest::SHA1.new
+ digest << metadata_gz
+
+ checksums = {
+ 'SHA1' => {
+ 'metadata.gz' => digest.hexdigest,
+ },
+ }
+
+ tar.add_file 'checksums.yaml.gz', 0444 do |io|
+ Zlib::GzipWriter.wrap io do |gz_io|
+ gz_io.write YAML.dump checksums
+ end
+ end
+
+ tar.add_file 'data.tar.gz', 0444 do |io|
+ io.write data_tgz
+ end
+ end
+
+ open 'data_checksum_missing.gem', 'wb' do |io|
+ io.write gem.string
+ end
+
+ package = Gem::Package.new 'data_checksum_missing.gem'
+
+ assert package.verify
+ end
+
+ def test_verify_corrupt
+ tf = Tempfile.open 'corrupt' do |io|
+ data = Gem.gzip 'a' * 10
+ io.write \
+ tar_file_header('metadata.gz', "\000x", 0644, data.length, Time.now)
+ io.write data
+ io.rewind
+
+ package = Gem::Package.new io.path
+
+ e = assert_raises Gem::Package::FormatError do
+ package.verify
+ end
+
+ assert_equal "tar is corrupt, name contains null byte in #{io.path}",
+ e.message
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+ end
+
+ def test_verify_empty
+ FileUtils.touch 'empty.gem'
+
+ package = Gem::Package.new 'empty.gem'
+
+ e = assert_raises Gem::Package::FormatError do
+ package.verify
+ end
+
+ assert_equal 'package metadata is missing in empty.gem', e.message
+ end
+
+ def test_verify_nonexistent
+ package = Gem::Package.new 'nonexistent.gem'
+
+ e = assert_raises Gem::Package::FormatError do
+ package.verify
+ end
+
+ assert_match %r%^No such file or directory%, e.message
+ assert_match %r%nonexistent.gem$%, e.message
+ end
+
+ def test_verify_security_policy
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ package = Gem::Package.new @gem
+ package.security_policy = Gem::Security::HighSecurity
+
+ e = assert_raises Gem::Security::Exception do
+ package.verify
+ end
+
+ assert_equal 'unsigned gems are not allowed by the High Security policy',
+ e.message
+
+ refute package.instance_variable_get(:@spec), '@spec must not be loaded'
+ assert_empty package.instance_variable_get(:@files), '@files must empty'
+ end
+
+ def test_verify_security_policy_low_security
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ @spec.cert_chain = [PUBLIC_CERT.to_pem]
+ @spec.signing_key = PRIVATE_KEY
+
+ FileUtils.mkdir_p 'lib'
+ FileUtils.touch 'lib/code.rb'
+
+ build = Gem::Package.new @gem
+ build.spec = @spec
+
+ build.build
+
+ package = Gem::Package.new @gem
+ package.security_policy = Gem::Security::LowSecurity
+
+ assert package.verify
+ end
+
+ def test_verify_security_policy_checksum_missing
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ @spec.cert_chain = [PUBLIC_CERT.to_pem]
+ @spec.signing_key = PRIVATE_KEY
+
+ build = Gem::Package.new @gem
+ build.spec = @spec
+ build.setup_signer
+
+ FileUtils.mkdir 'lib'
+ FileUtils.touch 'lib/code.rb'
+
+ open @gem, 'wb' do |gem_io|
+ Gem::Package::TarWriter.new gem_io do |gem|
+ build.add_metadata gem
+ build.add_contents gem
+
+ # write bogus data.tar.gz to foil signature
+ bogus_data = Gem.gzip 'hello'
+ gem.add_file_simple 'data.tar.gz', 0444, bogus_data.length do |io|
+ io.write bogus_data
+ end
+
+ # pre rubygems 2.0 gems do not add checksums
+ end
+ end
+
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ package = Gem::Package.new @gem
+ package.security_policy = Gem::Security::HighSecurity
+
+ e = assert_raises Gem::Security::Exception do
+ package.verify
+ end
+
+ assert_equal 'invalid signature', e.message
+
+ refute package.instance_variable_get(:@spec), '@spec must not be loaded'
+ assert_empty package.instance_variable_get(:@files), '@files must empty'
+ end
+
+ def test_verify_truncate
+ open 'bad.gem', 'wb' do |io|
+ io.write File.read(@gem, 1024) # don't care about newlines
+ end
+
+ package = Gem::Package.new 'bad.gem'
+
+ e = assert_raises Gem::Package::FormatError do
+ package.verify
+ end
+
+ assert_equal 'package content (data.tar.gz) is missing in bad.gem',
+ e.message
+ end
+
+ # end #verify tests
+
+ def test_verify_entry
+ entry = Object.new
+ def entry.full_name() raise ArgumentError, 'whatever' end
+
+ package = Gem::Package.new @gem
+
+ e = assert_raises Gem::Package::FormatError do
+ package.verify_entry entry
+ end
+
+ assert_equal "package is corrupt, exception while verifying: whatever (ArgumentError) in #{@gem}", e.message
+ end
+
+ def test_spec
+ package = Gem::Package.new @gem
+
+ assert_equal @spec, package.spec
+ end
+
+ def test_spec_from_io
+ # This functionality is used by rubygems.org to extract spec data from an
+ # uploaded gem before it is written to storage.
+ io = StringIO.new Gem.read_binary @gem
+ package = Gem::Package.new io
+
+ assert_equal @spec, package.spec
+ end
+
+ def test_spec_from_io_raises_gem_error_for_io_not_at_start
+ io = StringIO.new Gem.read_binary @gem
+ io.read(1)
+ assert_raises(Gem::Package::Error) do
+ Gem::Package.new io
+ end
+ end
+
+ def util_tar
+ tar_io = StringIO.new
+
+ Gem::Package::TarWriter.new tar_io do |tar|
+ yield tar
+ end
+
+ tar_io.rewind
+
+ tar_io
+ end
+
+ def util_tar_gz(&block)
+ tar_io = util_tar(&block)
+
+ tgz_io = StringIO.new
+
+ # can't wrap TarWriter because it seeks
+ Zlib::GzipWriter.wrap tgz_io do |io| io.write tar_io.string end
+
+ StringIO.new tgz_io.string
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_package_old.rb b/jni/ruby/test/rubygems/test_gem_package_old.rb
new file mode 100644
index 0000000..6236dbb
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_package_old.rb
@@ -0,0 +1,89 @@
+require 'rubygems/test_case'
+require 'rubygems/simple_gem'
+
+class TestGemPackageOld < Gem::TestCase
+
+ def setup
+ super
+
+ open 'old_format.gem', 'wb' do |io|
+ io.write SIMPLE_GEM
+ end
+
+ @package = Gem::Package::Old.new 'old_format.gem'
+ @destination = File.join @tempdir, 'extract'
+
+ FileUtils.mkdir_p @destination
+ end
+
+ def test_contents
+ assert_equal %w[lib/foo.rb lib/test.rb lib/test/wow.rb], @package.contents
+ end
+
+ def test_contents_security_policy
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ @package.security_policy = Gem::Security::AlmostNoSecurity
+
+ assert_raises Gem::Security::Exception do
+ @package.contents
+ end
+ end
+
+ def test_extract_files
+ @package.extract_files @destination
+
+ extracted = File.join @destination, 'lib/foo.rb'
+ assert_path_exists extracted
+
+ mask = 0100644 & (~File.umask)
+
+ assert_equal mask, File.stat(extracted).mode unless win_platform?
+ end
+
+ def test_extract_files_security_policy
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ @package.security_policy = Gem::Security::AlmostNoSecurity
+
+ assert_raises Gem::Security::Exception do
+ @package.extract_files @destination
+ end
+ end
+
+ def test_spec
+ assert_equal 'testing', @package.spec.name
+ end
+
+ def test_spec_security_policy
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ @package.security_policy = Gem::Security::AlmostNoSecurity
+
+ assert_raises Gem::Security::Exception do
+ @package.spec
+ end
+ end
+
+ def test_verify
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ assert @package.verify
+
+ @package.security_policy = Gem::Security::NoSecurity
+
+ assert @package.verify
+
+ @package.security_policy = Gem::Security::AlmostNoSecurity
+
+ e = assert_raises Gem::Security::Exception do
+ @package.verify
+ end
+
+ assert_equal 'old format gems do not contain signatures ' +
+ 'and cannot be verified',
+ e.message
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_package_tar_header.rb b/jni/ruby/test/rubygems/test_gem_package_tar_header.rb
new file mode 100644
index 0000000..9977e28
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_package_tar_header.rb
@@ -0,0 +1,146 @@
+require 'rubygems/package/tar_test_case'
+require 'rubygems/package'
+
+class TestGemPackageTarHeader < Gem::Package::TarTestCase
+
+ def setup
+ super
+
+ header = {
+ :name => 'x',
+ :mode => 0644,
+ :uid => 1000,
+ :gid => 10000,
+ :size => 100,
+ :mtime => 12345,
+ :typeflag => '0',
+ :linkname => 'link',
+ :uname => 'user',
+ :gname => 'group',
+ :devmajor => 1,
+ :devminor => 2,
+ :prefix => 'y',
+ }
+
+ @tar_header = Gem::Package::TarHeader.new header
+ end
+
+ def test_self_from
+ io = TempIO.new @tar_header.to_s
+
+ new_header = Gem::Package::TarHeader.from io
+
+ assert_headers_equal @tar_header, new_header
+ ensure
+ io.close!
+ end
+
+ def test_initialize
+ assert_equal '', @tar_header.checksum, 'checksum'
+ assert_equal 1, @tar_header.devmajor, 'devmajor'
+ assert_equal 2, @tar_header.devminor, 'devminor'
+ assert_equal 10000, @tar_header.gid, 'gid'
+ assert_equal 'group', @tar_header.gname, 'gname'
+ assert_equal 'link', @tar_header.linkname, 'linkname'
+ assert_equal 'ustar', @tar_header.magic, 'magic'
+ assert_equal 0644, @tar_header.mode, 'mode'
+ assert_equal 12345, @tar_header.mtime, 'mtime'
+ assert_equal 'x', @tar_header.name, 'name'
+ assert_equal 'y', @tar_header.prefix, 'prefix'
+ assert_equal 100, @tar_header.size, 'size'
+ assert_equal '0', @tar_header.typeflag, 'typeflag'
+ assert_equal 1000, @tar_header.uid, 'uid'
+ assert_equal 'user', @tar_header.uname, 'uname'
+ assert_equal '00', @tar_header.version, 'version'
+
+ refute_empty @tar_header, 'empty'
+ end
+
+ def test_initialize_bad
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.new :name => '', :size => '', :mode => ''
+ end
+
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.new :name => '', :size => '', :prefix => ''
+ end
+
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.new :name => '', :prefix => '', :mode => ''
+ end
+
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.new :prefix => '', :size => '', :mode => ''
+ end
+ end
+
+ def test_initialize_typeflag
+ header = {
+ :mode => '',
+ :name => '',
+ :prefix => '',
+ :size => '',
+ :typeflag => '',
+ }
+
+ tar_header = Gem::Package::TarHeader.new header
+
+ assert_equal '0', tar_header.typeflag
+ end
+
+ def test_empty_eh
+ refute_empty @tar_header
+
+ @tar_header = Gem::Package::TarHeader.new :name => 'x', :prefix => '',
+ :mode => 0, :size => 0,
+ :empty => true
+
+ assert_empty @tar_header
+ end
+
+ def test_equals2
+ assert_equal @tar_header, @tar_header
+ assert_equal @tar_header, @tar_header.dup
+ end
+
+ def test_to_s
+ expected = <<-EOF.split("\n").join
+x\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\0000000644\0000001750\0000023420\00000000000144\00000000030071
+\000012467\000 0link\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000ustar\00000user\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+group\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\0000000001\0000000002\000y\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000
+ EOF
+
+ assert_headers_equal expected, @tar_header
+ end
+
+ def test_update_checksum
+ assert_equal '', @tar_header.checksum
+
+ @tar_header.update_checksum
+
+ assert_equal '012467', @tar_header.checksum
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_package_tar_reader.rb b/jni/ruby/test/rubygems/test_gem_package_tar_reader.rb
new file mode 100644
index 0000000..aa45417
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_package_tar_reader.rb
@@ -0,0 +1,89 @@
+require 'rubygems/package/tar_test_case'
+require 'rubygems/package'
+
+class TestGemPackageTarReader < Gem::Package::TarTestCase
+
+ def test_each_entry
+ tar = tar_dir_header "foo", "bar", 0, Time.now
+ tar << tar_file_header("bar", "baz", 0, 0, Time.now)
+
+ io = TempIO.new tar
+
+ entries = 0
+
+ Gem::Package::TarReader.new io do |tar_reader|
+ tar_reader.each_entry do |entry|
+ assert_kind_of Gem::Package::TarReader::Entry, entry
+
+ entries += 1
+ end
+ end
+
+ assert_equal 2, entries
+ ensure
+ io.close!
+ end
+
+ def test_rewind
+ content = ('a'..'z').to_a.join(" ")
+
+ str =
+ tar_file_header("lib/foo", "", 010644, content.size, Time.now) +
+ content + "\0" * (512 - content.size)
+ str << "\0" * 1024
+
+ io = TempIO.new(str)
+
+ Gem::Package::TarReader.new(io) do |tar_reader|
+ 3.times do
+ tar_reader.rewind
+ i = 0
+ tar_reader.each_entry do |entry|
+ assert_equal(content, entry.read)
+ i += 1
+ end
+ assert_equal(1, i)
+ end
+ end
+ ensure
+ io.close!
+ end
+
+ def test_seek
+ tar = tar_dir_header "foo", "bar", 0, Time.now
+ tar << tar_file_header("bar", "baz", 0, 0, Time.now)
+
+ io = TempIO.new tar
+
+ Gem::Package::TarReader.new io do |tar_reader|
+ tar_reader.seek 'baz/bar' do |entry|
+ assert_kind_of Gem::Package::TarReader::Entry, entry
+
+ assert_equal 'baz/bar', entry.full_name
+ end
+
+ assert_equal 0, io.pos
+ end
+ ensure
+ io.close!
+ end
+
+ def test_seek_missing
+ tar = tar_dir_header "foo", "bar", 0, Time.now
+ tar << tar_file_header("bar", "baz", 0, 0, Time.now)
+
+ io = TempIO.new tar
+
+ Gem::Package::TarReader.new io do |tar_reader|
+ tar_reader.seek 'nonexistent' do |entry|
+ flunk 'entry missing but entry-found block was run'
+ end
+
+ assert_equal 0, io.pos
+ end
+ ensure
+ io.close!
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_package_tar_reader_entry.rb b/jni/ruby/test/rubygems/test_gem_package_tar_reader_entry.rb
new file mode 100644
index 0000000..1143187
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_package_tar_reader_entry.rb
@@ -0,0 +1,134 @@
+require 'rubygems/package/tar_test_case'
+require 'rubygems/package'
+
+class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
+
+ def setup
+ super
+
+ @contents = ('a'..'z').to_a.join * 100
+
+ @tar = ''
+ @tar << tar_file_header("lib/foo", "", 0, @contents.size, Time.now)
+ @tar << @contents
+ @tar << "\0" * (512 - (@tar.size % 512))
+
+ @entry = util_entry @tar
+ end
+
+ def teardown
+ close_util_entry(@entry)
+ super
+ end
+
+ def close_util_entry(entry)
+ entry.instance_variable_get(:@io).close!
+ end
+
+ def test_bytes_read
+ assert_equal 0, @entry.bytes_read
+
+ @entry.getc
+
+ assert_equal 1, @entry.bytes_read
+ end
+
+ def test_close
+ @entry.close
+
+ assert @entry.bytes_read
+
+ e = assert_raises IOError do @entry.eof? end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+
+ e = assert_raises IOError do @entry.getc end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+
+ e = assert_raises IOError do @entry.pos end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+
+ e = assert_raises IOError do @entry.read end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+
+ e = assert_raises IOError do @entry.rewind end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+ end
+
+ def test_closed_eh
+ @entry.close
+
+ assert @entry.closed?
+ end
+
+ def test_eof_eh
+ @entry.read
+
+ assert @entry.eof?
+ end
+
+ def test_full_name
+ assert_equal 'lib/foo', @entry.full_name
+ end
+
+ def test_full_name_null
+ @entry.header.prefix << "\000"
+
+ e = assert_raises Gem::Package::TarInvalidError do
+ @entry.full_name
+ end
+
+ assert_equal 'tar is corrupt, name contains null byte', e.message
+ end
+
+ def test_getc
+ assert_equal ?a, @entry.getc
+ end
+
+ def test_directory_eh
+ assert_equal false, @entry.directory?
+ dir_ent = util_dir_entry
+ assert_equal true, dir_ent.directory?
+ ensure
+ close_util_entry(dir_ent) if dir_ent
+ end
+
+ def test_file_eh
+ assert_equal true, @entry.file?
+ dir_ent = util_dir_entry
+ assert_equal false, dir_ent.file?
+ ensure
+ close_util_entry(dir_ent) if dir_ent
+ end
+
+ def test_pos
+ assert_equal 0, @entry.pos
+
+ @entry.getc
+
+ assert_equal 1, @entry.pos
+ end
+
+ def test_read
+ assert_equal @contents, @entry.read
+ end
+
+ def test_read_big
+ assert_equal @contents, @entry.read(@contents.size * 2)
+ end
+
+ def test_read_small
+ assert_equal @contents[0...100], @entry.read(100)
+ end
+
+ def test_rewind
+ char = @entry.getc
+
+ @entry.rewind
+
+ assert_equal 0, @entry.pos
+
+ assert_equal char, @entry.getc
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_package_tar_writer.rb b/jni/ruby/test/rubygems/test_gem_package_tar_writer.rb
new file mode 100644
index 0000000..f087df3
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_package_tar_writer.rb
@@ -0,0 +1,254 @@
+require 'rubygems/package/tar_test_case'
+require 'rubygems/package/tar_writer'
+require 'minitest/mock'
+
+class TestGemPackageTarWriter < Gem::Package::TarTestCase
+
+ def setup
+ super
+
+ @data = 'abcde12345'
+ @io = TempIO.new
+ @tar_writer = Gem::Package::TarWriter.new @io
+ end
+
+ def teardown
+ @tar_writer.close unless @tar_writer.closed?
+ @io.close!
+
+ super
+ end
+
+ def test_add_file
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_file 'x', 0644 do |f| f.write 'a' * 10 end
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
+ @io.string[0, 512])
+ end
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+ assert_equal 1024, @io.pos
+ end
+
+ def test_add_file_digest
+ digest_algorithms = Digest::SHA1, Digest::SHA512
+
+ Time.stub :now, Time.at(1458518157) do
+ digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
+ io.write 'a' * 10
+ end
+
+ assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
+ digests['SHA1'].hexdigest
+ assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
+ '1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
+ '4993d6b40a4e4d8424196afc347e97640d68de61' \
+ 'e1cf14b0',
+ digests['SHA512'].hexdigest
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
+ @io.string[0, 512])
+ end
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+ assert_equal 1024, @io.pos
+ end
+
+ def test_add_file_digest_multiple
+ digest_algorithms = [Digest::SHA1, Digest::SHA512]
+
+ Time.stub :now, Time.at(1458518157) do
+ digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
+ io.write 'a' * 10
+ end
+
+ assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
+ digests['SHA1'].hexdigest
+ assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
+ '1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
+ '4993d6b40a4e4d8424196afc347e97640d68de61' \
+ 'e1cf14b0',
+ digests['SHA512'].hexdigest
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
+ @io.string[0, 512])
+ end
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+ assert_equal 1024, @io.pos
+ end
+
+ def test_add_file_signer
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ signer = Gem::Security::Signer.new PRIVATE_KEY, [PUBLIC_CERT]
+
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_file_signed 'x', 0644, signer do |io|
+ io.write 'a' * 10
+ end
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
+ @io.string[0, 512])
+
+
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+
+ digest = signer.digest_algorithm.new
+ digest.update 'a' * 10
+
+ signature = signer.sign digest.digest
+
+ assert_headers_equal(tar_file_header('x.sig', '', 0444, signature.length,
+ Time.now),
+ @io.string[1024, 512])
+ assert_equal "#{signature}#{"\0" * (512 - signature.length)}",
+ @io.string[1536, 512]
+
+ assert_equal 2048, @io.pos
+ end
+
+ end
+
+ def test_add_file_signer_empty
+ signer = Gem::Security::Signer.new nil, nil
+
+ Time.stub :now, Time.at(1458518157) do
+
+ @tar_writer.add_file_signed 'x', 0644, signer do |io|
+ io.write 'a' * 10
+ end
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
+ @io.string[0, 512])
+ end
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+
+ assert_equal 1024, @io.pos
+ end
+
+ def test_add_file_simple
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_file_simple 'x', 0644, 10 do |io| io.write "a" * 10 end
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
+ @io.string[0, 512])
+ end
+
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+ assert_equal 1024, @io.pos
+ end
+
+ def test_add_file_simple_padding
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_file_simple 'x', 0, 100
+
+ assert_headers_equal tar_file_header('x', '', 0, 100, Time.now),
+ @io.string[0, 512]
+ end
+
+ assert_equal "\0" * 512, @io.string[512, 512]
+ end
+
+ def test_add_file_simple_data
+ @tar_writer.add_file_simple("lib/foo/bar", 0, 10) { |f| f.write @data }
+ @tar_writer.flush
+
+ assert_equal @data + ("\0" * (512-@data.size)),
+ @io.string[512, 512]
+ end
+
+ def test_add_file_simple_size
+ assert_raises Gem::Package::TarWriter::FileOverflow do
+ @tar_writer.add_file_simple("lib/foo/bar", 0, 10) do |io|
+ io.write "1" * 11
+ end
+ end
+ end
+
+ def test_add_file_unseekable
+ assert_raises Gem::Package::NonSeekableIO do
+ Gem::Package::TarWriter.new(Object.new).add_file 'x', 0
+ end
+ end
+
+ def test_close
+ @tar_writer.close
+
+ assert_equal "\0" * 1024, @io.string
+
+ e = assert_raises IOError do
+ @tar_writer.close
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+
+ e = assert_raises IOError do
+ @tar_writer.flush
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+
+ e = assert_raises IOError do
+ @tar_writer.add_file 'x', 0
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+
+ e = assert_raises IOError do
+ @tar_writer.add_file_simple 'x', 0, 0
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+
+ e = assert_raises IOError do
+ @tar_writer.mkdir 'x', 0
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+ end
+
+ def test_mkdir
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.mkdir 'foo', 0644
+
+ assert_headers_equal tar_dir_header('foo', '', 0644, Time.now),
+ @io.string[0, 512]
+
+ assert_equal 512, @io.pos
+ end
+ end
+
+ def test_split_name
+ assert_equal ['b' * 100, 'a' * 155],
+ @tar_writer.split_name("#{'a' * 155}/#{'b' * 100}")
+
+ assert_equal ["#{'qwer/' * 19}bla", 'a' * 151],
+ @tar_writer.split_name("#{'a' * 151}/#{'qwer/' * 19}bla")
+ end
+
+ def test_split_name_too_long_name
+ name = File.join 'a', 'b' * 100
+ assert_equal ['b' * 100, 'a'], @tar_writer.split_name(name)
+
+ name = File.join 'a', 'b' * 101
+ exception = assert_raises Gem::Package::TooLongFileName do
+ @tar_writer.split_name name
+ end
+ assert_includes exception.message, name
+ end
+
+ def test_split_name_too_long_prefix
+ name = File.join 'a' * 155, 'b'
+ assert_equal ['b', 'a' * 155], @tar_writer.split_name(name)
+
+ name = File.join 'a' * 156, 'b'
+ exception = assert_raises Gem::Package::TooLongFileName do
+ @tar_writer.split_name name
+ end
+ assert_includes exception.message, name
+ end
+
+ def test_split_name_too_long_total
+ name = 'a' * 257
+ exception = assert_raises Gem::Package::TooLongFileName do
+ @tar_writer.split_name name
+ end
+ assert_includes exception.message, name
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_package_task.rb b/jni/ruby/test/rubygems/test_gem_package_task.rb
new file mode 100644
index 0000000..1526e82
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_package_task.rb
@@ -0,0 +1,80 @@
+require 'rubygems/test_case'
+require 'rubygems'
+require 'rubygems/package_task'
+
+class TestGemPackageTask < Gem::TestCase
+
+ def setup
+ super
+
+ Rake.application = Rake::Application.new
+ RakeFileUtils.verbose_flag = false
+ end
+
+ def test_gem_package
+ gem = Gem::Specification.new do |g|
+ g.name = "pkgr"
+ g.version = "1.2.3"
+
+ g.authors = %w[author]
+ g.files = %w[x]
+ g.summary = 'summary'
+ end
+
+ pkg = Gem::PackageTask.new(gem) do |p|
+ p.package_files << "y"
+ end
+
+ assert_equal %w[x y], pkg.package_files
+
+ Dir.chdir @tempdir do
+ FileUtils.touch 'x'
+ FileUtils.touch 'y'
+
+ Rake.application['package'].invoke
+
+ assert_path_exists 'pkg/pkgr-1.2.3.gem'
+ end
+ end
+
+ def test_gem_package_with_current_platform
+ gem = Gem::Specification.new do |g|
+ g.name = "pkgr"
+ g.version = "1.2.3"
+ g.files = Rake::FileList["x"].resolve
+ g.platform = Gem::Platform::CURRENT
+ end
+ pkg = Gem::PackageTask.new(gem) do |p|
+ p.package_files << "y"
+ end
+ assert_equal ["x", "y"], pkg.package_files
+ end
+
+ def test_gem_package_with_ruby_platform
+ gem = Gem::Specification.new do |g|
+ g.name = "pkgr"
+ g.version = "1.2.3"
+ g.files = Rake::FileList["x"].resolve
+ g.platform = Gem::Platform::RUBY
+ end
+ pkg = Gem::PackageTask.new(gem) do |p|
+ p.package_files << "y"
+ end
+ assert_equal ["x", "y"], pkg.package_files
+ end
+
+ def test_package_dir_path
+ gem = Gem::Specification.new do |g|
+ g.name = 'nokogiri'
+ g.version = '1.5.0'
+ g.platform = 'java'
+ end
+
+ pkg = Gem::PackageTask.new gem
+ pkg.define
+
+ assert_equal 'pkg/nokogiri-1.5.0-java', pkg.package_dir_path
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_path_support.rb b/jni/ruby/test/rubygems/test_gem_path_support.rb
new file mode 100644
index 0000000..879cc98
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_path_support.rb
@@ -0,0 +1,84 @@
+require 'rubygems/test_case'
+require 'rubygems'
+require 'fileutils'
+
+class TestGemPathSupport < Gem::TestCase
+ def setup
+ super
+
+ ENV["GEM_HOME"] = @tempdir
+ ENV["GEM_PATH"] = [@tempdir, "something"].join(File::PATH_SEPARATOR)
+ end
+
+ def test_initialize
+ ps = Gem::PathSupport.new
+
+ assert_equal ENV["GEM_HOME"], ps.home
+
+ expected = util_path
+ assert_equal expected, ps.path, "defaults to GEM_PATH"
+ end
+
+ def test_initialize_home
+ ps = Gem::PathSupport.new "GEM_HOME" => "#{@tempdir}/foo"
+
+ assert_equal File.join(@tempdir, "foo"), ps.home
+
+ expected = util_path + [File.join(@tempdir, 'foo')]
+ assert_equal expected, ps.path
+ end
+
+ if defined?(File::ALT_SEPARATOR) and File::ALT_SEPARATOR
+ def test_initialize_home_normalize
+ alternate = @tempdir.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
+ ps = Gem::PathSupport.new "GEM_HOME" => alternate
+
+ assert_equal @tempdir, ps.home, "normalize values"
+ end
+ end
+
+ def test_initialize_path
+ ps = Gem::PathSupport.new "GEM_PATH" => %W[#{@tempdir}/foo #{@tempdir}/bar]
+
+ assert_equal ENV["GEM_HOME"], ps.home
+
+ expected = [
+ File.join(@tempdir, 'foo'),
+ File.join(@tempdir, 'bar'),
+ ENV["GEM_HOME"],
+ ]
+
+ assert_equal expected, ps.path
+ end
+
+ def test_initialize_home_path
+ ps = Gem::PathSupport.new("GEM_HOME" => "#{@tempdir}/foo",
+ "GEM_PATH" => %W[#{@tempdir}/foo #{@tempdir}/bar])
+
+ assert_equal File.join(@tempdir, "foo"), ps.home
+
+ expected = [File.join(@tempdir, 'foo'), File.join(@tempdir, 'bar')]
+ assert_equal expected, ps.path
+ end
+
+ def util_path
+ ENV["GEM_PATH"].split(File::PATH_SEPARATOR)
+ end
+
+ def test_initialize_spec
+ ENV["GEM_SPEC_CACHE"] = nil
+
+ ps = Gem::PathSupport.new
+ assert_equal Gem.default_spec_cache_dir, ps.spec_cache_dir
+
+ ENV["GEM_SPEC_CACHE"] = 'bar'
+
+ ps = Gem::PathSupport.new
+ assert_equal ENV["GEM_SPEC_CACHE"], ps.spec_cache_dir
+
+ ENV["GEM_SPEC_CACHE"] = File.join @tempdir, 'spec_cache'
+
+ ps = Gem::PathSupport.new "GEM_SPEC_CACHE" => "foo"
+ assert_equal "foo", ps.spec_cache_dir
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_platform.rb b/jni/ruby/test/rubygems/test_gem_platform.rb
new file mode 100644
index 0000000..17577dc
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_platform.rb
@@ -0,0 +1,296 @@
+require 'rubygems/test_case'
+require 'rubygems/platform'
+require 'rbconfig'
+
+class TestGemPlatform < Gem::TestCase
+
+ def test_self_local
+ util_set_arch 'i686-darwin8.10.1'
+
+ assert_equal Gem::Platform.new(%w[x86 darwin 8]), Gem::Platform.local
+ end
+
+ def test_self_match
+ assert Gem::Platform.match(nil), 'nil == ruby'
+ assert Gem::Platform.match(Gem::Platform.local), 'exact match'
+ assert Gem::Platform.match(Gem::Platform.local.to_s), '=~ match'
+ assert Gem::Platform.match(Gem::Platform::RUBY), 'ruby'
+ end
+
+ def test_self_new
+ assert_equal Gem::Platform.local, Gem::Platform.new(Gem::Platform::CURRENT)
+ assert_equal Gem::Platform::RUBY, Gem::Platform.new(Gem::Platform::RUBY)
+ assert_equal Gem::Platform::RUBY, Gem::Platform.new(nil)
+ assert_equal Gem::Platform::RUBY, Gem::Platform.new('')
+ end
+
+ def test_initialize
+ test_cases = {
+ 'amd64-freebsd6' => ['amd64', 'freebsd', '6'],
+ 'hppa2.0w-hpux11.31' => ['hppa2.0w', 'hpux', '11'],
+ 'java' => [nil, 'java', nil],
+ 'jruby' => [nil, 'java', nil],
+ 'universal-dotnet' => ['universal', 'dotnet', nil],
+ 'universal-dotnet2.0' => ['universal', 'dotnet', '2.0'],
+ 'universal-dotnet4.0' => ['universal', 'dotnet', '4.0'],
+ 'powerpc-aix5.3.0.0' => ['powerpc', 'aix', '5'],
+ 'powerpc-darwin7' => ['powerpc', 'darwin', '7'],
+ 'powerpc-darwin8' => ['powerpc', 'darwin', '8'],
+ 'powerpc-linux' => ['powerpc', 'linux', nil],
+ 'powerpc64-linux' => ['powerpc64', 'linux', nil],
+ 'sparc-solaris2.10' => ['sparc', 'solaris', '2.10'],
+ 'sparc-solaris2.8' => ['sparc', 'solaris', '2.8'],
+ 'sparc-solaris2.9' => ['sparc', 'solaris', '2.9'],
+ 'universal-darwin8' => ['universal', 'darwin', '8'],
+ 'universal-darwin9' => ['universal', 'darwin', '9'],
+ 'universal-macruby' => ['universal', 'macruby', nil],
+ 'i386-cygwin' => ['x86', 'cygwin', nil],
+ 'i686-darwin' => ['x86', 'darwin', nil],
+ 'i686-darwin8.4.1' => ['x86', 'darwin', '8'],
+ 'i386-freebsd4.11' => ['x86', 'freebsd', '4'],
+ 'i386-freebsd5' => ['x86', 'freebsd', '5'],
+ 'i386-freebsd6' => ['x86', 'freebsd', '6'],
+ 'i386-freebsd7' => ['x86', 'freebsd', '7'],
+ 'i386-freebsd' => ['x86', 'freebsd', nil],
+ 'universal-freebsd' => ['universal', 'freebsd', nil],
+ 'i386-java1.5' => ['x86', 'java', '1.5'],
+ 'x86-java1.6' => ['x86', 'java', '1.6'],
+ 'i386-java1.6' => ['x86', 'java', '1.6'],
+ 'i686-linux' => ['x86', 'linux', nil],
+ 'i586-linux' => ['x86', 'linux', nil],
+ 'i486-linux' => ['x86', 'linux', nil],
+ 'i386-linux' => ['x86', 'linux', nil],
+ 'i586-linux-gnu' => ['x86', 'linux', nil],
+ 'i386-linux-gnu' => ['x86', 'linux', nil],
+ 'i386-mingw32' => ['x86', 'mingw32', nil],
+ 'i386-mswin32' => ['x86', 'mswin32', nil],
+ 'i386-mswin32_80' => ['x86', 'mswin32', '80'],
+ 'i386-mswin32-80' => ['x86', 'mswin32', '80'],
+ 'x86-mswin32' => ['x86', 'mswin32', nil],
+ 'x86-mswin32_60' => ['x86', 'mswin32', '60'],
+ 'x86-mswin32-60' => ['x86', 'mswin32', '60'],
+ 'i386-netbsdelf' => ['x86', 'netbsdelf', nil],
+ 'i386-openbsd4.0' => ['x86', 'openbsd', '4.0'],
+ 'i386-solaris2.10' => ['x86', 'solaris', '2.10'],
+ 'i386-solaris2.8' => ['x86', 'solaris', '2.8'],
+ 'mswin32' => ['x86', 'mswin32', nil],
+ 'x86_64-linux' => ['x86_64', 'linux', nil],
+ 'x86_64-openbsd3.9' => ['x86_64', 'openbsd', '3.9'],
+ 'x86_64-openbsd4.0' => ['x86_64', 'openbsd', '4.0'],
+ 'x86_64-openbsd' => ['x86_64', 'openbsd', nil],
+ }
+
+ test_cases.each do |arch, expected|
+ platform = Gem::Platform.new arch
+ assert_equal expected, platform.to_a, arch.inspect
+ end
+ end
+
+ def test_initialize_command_line
+ expected = ['x86', 'mswin32', nil]
+
+ platform = Gem::Platform.new 'i386-mswin32'
+
+ assert_equal expected, platform.to_a, 'i386-mswin32'
+
+ expected = ['x86', 'mswin32', '80']
+
+ platform = Gem::Platform.new 'i386-mswin32-80'
+
+ assert_equal expected, platform.to_a, 'i386-mswin32-80'
+
+ expected = ['x86', 'solaris', '2.10']
+
+ platform = Gem::Platform.new 'i386-solaris-2.10'
+
+ assert_equal expected, platform.to_a, 'i386-solaris-2.10'
+ end
+
+ def test_initialize_mswin32_vc6
+ orig_RUBY_SO_NAME = RbConfig::CONFIG['RUBY_SO_NAME']
+ RbConfig::CONFIG['RUBY_SO_NAME'] = 'msvcrt-ruby18'
+
+ expected = ['x86', 'mswin32', nil]
+
+ platform = Gem::Platform.new 'i386-mswin32'
+
+ assert_equal expected, platform.to_a, 'i386-mswin32 VC6'
+ ensure
+ if orig_RUBY_SO_NAME then
+ RbConfig::CONFIG['RUBY_SO_NAME'] = orig_RUBY_SO_NAME
+ else
+ RbConfig::CONFIG.delete 'RUBY_SO_NAME'
+ end
+ end
+
+ def test_initialize_platform
+ platform = Gem::Platform.new 'cpu-my_platform1'
+
+ assert_equal 'cpu', platform.cpu
+ assert_equal 'my_platform', platform.os
+ assert_equal '1', platform.version
+ end
+
+ def test_initialize_test
+ platform = Gem::Platform.new 'cpu-my_platform1'
+ assert_equal 'cpu', platform.cpu
+ assert_equal 'my_platform', platform.os
+ assert_equal '1', platform.version
+
+ platform = Gem::Platform.new 'cpu-other_platform1'
+ assert_equal 'cpu', platform.cpu
+ assert_equal 'other_platform', platform.os
+ assert_equal '1', platform.version
+ end
+
+ def test_to_s
+ if win_platform? then
+ assert_equal 'x86-mswin32-60', Gem::Platform.local.to_s
+ else
+ assert_equal 'x86-darwin-8', Gem::Platform.local.to_s
+ end
+ end
+
+ def test_equals2
+ my = Gem::Platform.new %w[cpu my_platform 1]
+ other = Gem::Platform.new %w[cpu other_platform 1]
+
+ assert_equal my, my
+ refute_equal my, other
+ refute_equal other, my
+ end
+
+ def test_equals3
+ my = Gem::Platform.new %w[cpu my_platform 1]
+ other = Gem::Platform.new %w[cpu other_platform 1]
+
+ assert(my === my)
+ refute(other === my)
+ refute(my === other)
+ end
+
+ def test_equals3_cpu
+ ppc_darwin8 = Gem::Platform.new 'powerpc-darwin8.0'
+ uni_darwin8 = Gem::Platform.new 'universal-darwin8.0'
+ x86_darwin8 = Gem::Platform.new 'i686-darwin8.0'
+
+ util_set_arch 'powerpc-darwin8'
+ assert((ppc_darwin8 === Gem::Platform.local), 'powerpc =~ universal')
+ assert((uni_darwin8 === Gem::Platform.local), 'powerpc =~ universal')
+ refute((x86_darwin8 === Gem::Platform.local), 'powerpc =~ universal')
+
+ util_set_arch 'i686-darwin8'
+ refute((ppc_darwin8 === Gem::Platform.local), 'powerpc =~ universal')
+ assert((uni_darwin8 === Gem::Platform.local), 'x86 =~ universal')
+ assert((x86_darwin8 === Gem::Platform.local), 'powerpc =~ universal')
+
+ util_set_arch 'universal-darwin8'
+ assert((ppc_darwin8 === Gem::Platform.local), 'universal =~ ppc')
+ assert((uni_darwin8 === Gem::Platform.local), 'universal =~ universal')
+ assert((x86_darwin8 === Gem::Platform.local), 'universal =~ x86')
+ end
+
+ def test_equals3_cpu_arm
+ arm = Gem::Platform.new 'arm-linux'
+ armv5 = Gem::Platform.new 'armv5-linux'
+ armv7 = Gem::Platform.new 'armv7-linux'
+
+ util_set_arch 'armv5-linux'
+ assert((arm === Gem::Platform.local), 'arm === armv5')
+ assert((armv5 === Gem::Platform.local), 'armv5 === armv5')
+ refute((armv7 === Gem::Platform.local), 'armv7 === armv5')
+ refute((Gem::Platform.local === arm), 'armv5 === arm')
+
+ util_set_arch 'armv7-linux'
+ assert((arm === Gem::Platform.local), 'arm === armv7')
+ refute((armv5 === Gem::Platform.local), 'armv5 === armv7')
+ assert((armv7 === Gem::Platform.local), 'armv7 === armv7')
+ refute((Gem::Platform.local === arm), 'armv7 === arm')
+ end
+
+ def test_equals3_version
+ util_set_arch 'i686-darwin8'
+
+ x86_darwin = Gem::Platform.new ['x86', 'darwin', nil]
+ x86_darwin7 = Gem::Platform.new ['x86', 'darwin', '7']
+ x86_darwin8 = Gem::Platform.new ['x86', 'darwin', '8']
+ x86_darwin9 = Gem::Platform.new ['x86', 'darwin', '9']
+
+ assert((x86_darwin === Gem::Platform.local), 'x86_darwin === x86_darwin8')
+ assert((x86_darwin8 === Gem::Platform.local), 'x86_darwin8 === x86_darwin8')
+
+ refute((x86_darwin7 === Gem::Platform.local), 'x86_darwin7 === x86_darwin8')
+ refute((x86_darwin9 === Gem::Platform.local), 'x86_darwin9 === x86_darwin8')
+ end
+
+ def test_equals_tilde
+ util_set_arch 'i386-mswin32'
+
+ assert_local_match 'mswin32'
+ assert_local_match 'i386-mswin32'
+
+ # oddballs
+ assert_local_match 'i386-mswin32-mq5.3'
+ assert_local_match 'i386-mswin32-mq6'
+ refute_local_match 'win32-1.8.2-VC7'
+ refute_local_match 'win32-1.8.4-VC6'
+ refute_local_match 'win32-source'
+ refute_local_match 'windows'
+
+ util_set_arch 'i686-linux'
+ assert_local_match 'i486-linux'
+ assert_local_match 'i586-linux'
+ assert_local_match 'i686-linux'
+
+ util_set_arch 'i686-darwin8'
+ assert_local_match 'i686-darwin8.4.1'
+ assert_local_match 'i686-darwin8.8.2'
+
+ util_set_arch 'java'
+ assert_local_match 'java'
+ assert_local_match 'jruby'
+
+ util_set_arch 'universal-dotnet2.0'
+ assert_local_match 'universal-dotnet'
+ assert_local_match 'universal-dotnet-2.0'
+ refute_local_match 'universal-dotnet-4.0'
+ assert_local_match 'dotnet'
+ assert_local_match 'dotnet-2.0'
+ refute_local_match 'dotnet-4.0'
+
+ util_set_arch 'universal-dotnet4.0'
+ assert_local_match 'universal-dotnet'
+ refute_local_match 'universal-dotnet-2.0'
+ assert_local_match 'universal-dotnet-4.0'
+ assert_local_match 'dotnet'
+ refute_local_match 'dotnet-2.0'
+ assert_local_match 'dotnet-4.0'
+
+ util_set_arch 'universal-macruby-1.0'
+ assert_local_match 'universal-macruby'
+ assert_local_match 'macruby'
+ refute_local_match 'universal-macruby-0.10'
+ assert_local_match 'universal-macruby-1.0'
+
+ util_set_arch 'powerpc-darwin'
+ assert_local_match 'powerpc-darwin'
+
+ util_set_arch 'powerpc-darwin7'
+ assert_local_match 'powerpc-darwin7.9.0'
+
+ util_set_arch 'powerpc-darwin8'
+ assert_local_match 'powerpc-darwin8.10.0'
+
+ util_set_arch 'sparc-solaris2.8'
+ assert_local_match 'sparc-solaris2.8-mq5.3'
+ end
+
+ def assert_local_match name
+ assert_match Gem::Platform.local, name
+ end
+
+ def refute_local_match name
+ refute_match Gem::Platform.local, name
+ end
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_rdoc.rb b/jni/ruby/test/rubygems/test_gem_rdoc.rb
new file mode 100644
index 0000000..3ff06fe
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_rdoc.rb
@@ -0,0 +1,269 @@
+require 'rubygems'
+require 'rubygems/test_case'
+require 'rubygems/rdoc'
+
+class TestGemRDoc < Gem::TestCase
+ Gem::RDoc.load_rdoc
+ rdoc_4 = Gem::Requirement.new('> 3').satisfied_by?(Gem::RDoc.rdoc_version)
+
+ def setup
+ super
+
+ @a = util_spec 'a' do |s|
+ s.rdoc_options = %w[--main MyTitle]
+ s.extra_rdoc_files = %w[README]
+ end
+
+ write_file File.join(@tempdir, 'lib', 'a.rb')
+ write_file File.join(@tempdir, 'README')
+
+ install_gem @a
+
+ @hook = Gem::RDoc.new @a
+
+ begin
+ Gem::RDoc.load_rdoc
+ rescue Gem::DocumentError => e
+ skip e.message
+ end
+
+ Gem.configuration[:rdoc] = nil
+ end
+
+ ##
+ # RDoc 4 ships with its own Gem::RDoc which overrides this one which is
+ # shipped for backwards compatibility.
+
+ def rdoc_4?
+ Gem::Requirement.new('>= 4.0.0.preview2').satisfied_by? \
+ @hook.class.rdoc_version
+ end
+
+ def rdoc_3?
+ Gem::Requirement.new('~> 3.0').satisfied_by? @hook.class.rdoc_version
+ end
+
+ def rdoc_3_8_or_better?
+ Gem::Requirement.new('>= 3.8').satisfied_by? @hook.class.rdoc_version
+ end
+
+ def test_initialize
+ if rdoc_4? then
+ refute @hook.generate_rdoc
+ else
+ assert @hook.generate_rdoc
+ end
+ assert @hook.generate_ri
+
+ rdoc = Gem::RDoc.new @a, false, false
+
+ refute rdoc.generate_rdoc
+ refute rdoc.generate_ri
+ end
+
+ def test_delete_legacy_args
+ args = %w[
+ --inline-source
+ --one-file
+ --promiscuous
+ -p
+ ]
+
+ @hook.delete_legacy_args args
+
+ assert_empty args
+ end
+
+ def test_document
+ skip 'RDoc 3 required' unless rdoc_3?
+
+ options = RDoc::Options.new
+ options.files = []
+
+ rdoc = @hook.new_rdoc
+ @hook.instance_variable_set :@rdoc, rdoc
+ @hook.instance_variable_set :@file_info, []
+
+ @hook.document 'darkfish', options, @a.doc_dir('rdoc')
+
+ assert @hook.rdoc_installed?
+ end unless rdoc_4
+
+ def test_generate
+ skip 'RDoc 3 required' unless rdoc_3?
+
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ assert @hook.rdoc_installed?
+ assert @hook.ri_installed?
+
+ rdoc = @hook.instance_variable_get :@rdoc
+
+ refute rdoc.options.hyperlink_all
+ end unless rdoc_4
+
+ def test_generate_configuration_rdoc_array
+ skip 'RDoc 3 required' unless rdoc_3?
+
+ Gem.configuration[:rdoc] = %w[-A]
+
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ rdoc = @hook.instance_variable_get :@rdoc
+
+ assert rdoc.options.hyperlink_all
+ end unless rdoc_4
+
+ def test_generate_configuration_rdoc_string
+ skip 'RDoc 3 required' unless rdoc_3?
+
+ Gem.configuration[:rdoc] = '-A'
+
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ rdoc = @hook.instance_variable_get :@rdoc
+
+ assert rdoc.options.hyperlink_all
+ end unless rdoc_4
+
+ def test_generate_disabled
+ @hook.generate_rdoc = false
+ @hook.generate_ri = false
+
+ @hook.generate
+
+ refute @hook.rdoc_installed?
+ refute @hook.ri_installed?
+ end
+
+ def test_generate_force
+ skip 'RDoc 3 required' unless rdoc_3?
+
+ FileUtils.mkdir_p @a.doc_dir 'ri'
+ FileUtils.mkdir_p @a.doc_dir 'rdoc'
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.force = true
+
+ @hook.generate
+
+ assert_path_exists File.join(@a.doc_dir('rdoc'), 'index.html')
+ assert_path_exists File.join(@a.doc_dir('ri'), 'cache.ri')
+ end unless rdoc_4
+
+ def test_generate_no_overwrite
+ skip 'RDoc 3 required' unless rdoc_3?
+
+ FileUtils.mkdir_p @a.doc_dir 'ri'
+ FileUtils.mkdir_p @a.doc_dir 'rdoc'
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate
+
+ refute_path_exists File.join(@a.doc_dir('rdoc'), 'index.html')
+ refute_path_exists File.join(@a.doc_dir('ri'), 'cache.ri')
+ end unless rdoc_4
+
+ def test_generate_legacy
+ skip 'RDoc < 3.8 required' if rdoc_3_8_or_better?
+
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.generate_legacy
+
+ assert @hook.rdoc_installed?
+ assert @hook.ri_installed?
+ end unless rdoc_4
+
+ def test_legacy_rdoc
+ skip 'RDoc < 3.8 required' if rdoc_3_8_or_better?
+
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.mkdir_p File.join(@a.gem_dir, 'lib')
+
+ @hook.legacy_rdoc '--op', @a.doc_dir('rdoc')
+
+ assert @hook.rdoc_installed?
+ end unless rdoc_4
+
+ def test_new_rdoc
+ assert_kind_of RDoc::RDoc, @hook.new_rdoc
+ end
+
+ def test_rdoc_installed?
+ refute @hook.rdoc_installed?
+
+ FileUtils.mkdir_p @a.doc_dir 'rdoc'
+
+ assert @hook.rdoc_installed?
+ end
+
+ def test_remove
+ FileUtils.mkdir_p @a.doc_dir 'rdoc'
+ FileUtils.mkdir_p @a.doc_dir 'ri'
+
+ @hook.remove
+
+ refute @hook.rdoc_installed?
+ refute @hook.ri_installed?
+
+ assert_path_exists @a.doc_dir
+ end
+
+ def test_remove_unwritable
+ skip 'chmod not supported' if Gem.win_platform?
+ FileUtils.mkdir_p @a.base_dir
+ FileUtils.chmod 0, @a.base_dir
+
+ e = assert_raises Gem::FilePermissionError do
+ @hook.remove
+ end
+
+ assert_equal @a.base_dir, e.directory
+ ensure
+ FileUtils.chmod(0755, @a.base_dir) if File.directory?(@a.base_dir)
+ end
+
+ def test_ri_installed?
+ refute @hook.ri_installed?
+
+ FileUtils.mkdir_p @a.doc_dir 'ri'
+
+ assert @hook.ri_installed?
+ end
+
+ def test_setup
+ @hook.setup
+
+ assert_path_exists @a.doc_dir
+ end
+
+ def test_setup_unwritable
+ skip 'chmod not supported' if Gem.win_platform?
+ FileUtils.mkdir_p @a.doc_dir
+ FileUtils.chmod 0, @a.doc_dir
+
+ e = assert_raises Gem::FilePermissionError do
+ @hook.setup
+ end
+
+ assert_equal @a.doc_dir, e.directory
+ ensure
+ if File.exist? @a.doc_dir
+ FileUtils.chmod 0755, @a.doc_dir
+ FileUtils.rm_r @a.doc_dir
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_remote_fetcher.rb b/jni/ruby/test/rubygems/test_gem_remote_fetcher.rb
new file mode 100644
index 0000000..63dd8fe
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_remote_fetcher.rb
@@ -0,0 +1,987 @@
+require 'rubygems/test_case'
+
+require 'webrick'
+begin
+ require 'webrick/https'
+rescue LoadError => e
+ raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
+ e.message =~ / -- openssl$/
+end
+
+require 'rubygems/remote_fetcher'
+require 'rubygems/package'
+require 'minitest/mock'
+
+# = Testing Proxy Settings
+#
+# These tests check the proper proxy server settings by running two
+# web servers. The web server at http://localhost:#{SERVER_PORT}
+# represents the normal gem server and returns a gemspec with a rake
+# version of 0.4.11. The web server at http://localhost:#{PROXY_PORT}
+# represents the proxy server and returns a different dataset where
+# rake has version 0.4.2. This allows us to detect which server is
+# returning the data.
+#
+# Note that the proxy server is not a *real* proxy server. But our
+# software doesn't really care, as long as we hit the proxy URL when a
+# proxy is configured.
+
+class TestGemRemoteFetcher < Gem::TestCase
+
+ include Gem::DefaultUserInteraction
+
+ SERVER_DATA = <<-EOY
+--- !ruby/object:Gem::Cache
+gems:
+ rake-0.4.11: !ruby/object:Gem::Specification
+ rubygems_version: "0.7"
+ specification_version: 1
+ name: rake
+ version: !ruby/object:Gem::Version
+ version: 0.4.11
+ date: 2004-11-12
+ summary: Ruby based make-like utility.
+ require_paths:
+ - lib
+ author: Jim Weirich
+ email: jim@weirichhouse.org
+ homepage: http://rake.rubyforge.org
+ rubyforge_project: rake
+ description: Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax.
+ autorequire:
+ default_executable: rake
+ bindir: bin
+ has_rdoc: true
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
+ requirements:
+ -
+ - ">"
+ - !ruby/object:Gem::Version
+ version: 0.0.0
+ version:
+ platform: ruby
+ files:
+ - README
+ test_files: []
+ library_stubs:
+ rdoc_options:
+ extra_rdoc_files:
+ executables:
+ - rake
+ extensions: []
+ requirements: []
+ dependencies: []
+ EOY
+
+ PROXY_DATA = SERVER_DATA.gsub(/0.4.11/, '0.4.2')
+
+ DIR = File.expand_path(File.dirname(__FILE__))
+
+ def setup
+ @proxies = %w[http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY]
+ @old_proxies = @proxies.map {|k| ENV[k] }
+ @proxies.each {|k| ENV[k] = nil }
+
+ super
+ self.class.start_servers
+ self.class.enable_yaml = true
+ self.class.enable_zip = false
+
+ base_server_uri = "http://localhost:#{self.class.normal_server_port}"
+ @proxy_uri = "http://localhost:#{self.class.proxy_server_port}"
+
+ @server_uri = base_server_uri + "/yaml"
+ @server_z_uri = base_server_uri + "/yaml.Z"
+
+ # REFACTOR: copied from test_gem_dependency_installer.rb
+ @gems_dir = File.join @tempdir, 'gems'
+ @cache_dir = File.join @gemhome, "cache"
+ FileUtils.mkdir @gems_dir
+
+ # TODO: why does the remote fetcher need it written to disk?
+ @a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end
+ @a1.loaded_from = File.join(@gemhome, 'specifications', @a1.full_name)
+
+ Gem::RemoteFetcher.fetcher = nil
+
+ @fetcher = Gem::RemoteFetcher.fetcher
+ end
+
+ def teardown
+ @fetcher.close_all
+ self.class.stop_servers
+ super
+ Gem.configuration[:http_proxy] = nil
+ @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] }
+ end
+
+ def test_self_fetcher
+ fetcher = Gem::RemoteFetcher.fetcher
+ refute_nil fetcher
+ assert_kind_of Gem::RemoteFetcher, fetcher
+ end
+
+ def test_self_fetcher_with_proxy
+ proxy_uri = 'http://proxy.example.com'
+ Gem.configuration[:http_proxy] = proxy_uri
+ Gem::RemoteFetcher.fetcher = nil
+
+ fetcher = Gem::RemoteFetcher.fetcher
+
+ refute_nil fetcher
+ assert_kind_of Gem::RemoteFetcher, fetcher
+ assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy).to_s
+ end
+
+ def test_fetch_size_bad_uri
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ e = assert_raises ArgumentError do
+ fetcher.fetch_size 'gems.example.com/yaml'
+ end
+
+ assert_equal 'uri scheme is invalid: nil', e.message
+ end
+
+ def test_fetch_size_socket_error
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ def fetcher.request(uri, request_class, last_modified = nil)
+ raise SocketError, "tarded"
+ end
+
+ uri = 'http://gems.example.com/yaml'
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_size uri
+ end
+
+ assert_equal "SocketError: tarded (#{uri})", e.message
+ end
+
+ def test_no_proxy
+ use_ui @ui do
+ assert_data_from_server @fetcher.fetch_path(@server_uri)
+ assert_equal SERVER_DATA.size, @fetcher.fetch_size(@server_uri)
+ end
+ end
+
+ def test_api_endpoint
+ uri = URI.parse "http://example.com/foo"
+ target = MiniTest::Mock.new
+ target.expect :target, "gems.example.com"
+
+ dns = MiniTest::Mock.new
+ dns.expect :getresource, target, [String, Object]
+
+ fetch = Gem::RemoteFetcher.new nil, dns
+ assert_equal URI.parse("http://gems.example.com/foo"), fetch.api_endpoint(uri)
+
+ target.verify
+ dns.verify
+ end
+
+ def test_api_endpoint_ignores_trans_domain_values
+ uri = URI.parse "http://gems.example.com/foo"
+ target = MiniTest::Mock.new
+ target.expect :target, "blah.com"
+
+ dns = MiniTest::Mock.new
+ dns.expect :getresource, target, [String, Object]
+
+ fetch = Gem::RemoteFetcher.new nil, dns
+ assert_equal URI.parse("http://gems.example.com/foo"), fetch.api_endpoint(uri)
+
+ target.verify
+ dns.verify
+ end
+
+ def test_api_endpoint_ignores_trans_domain_values_that_starts_with_original
+ uri = URI.parse "http://example.com/foo"
+ target = MiniTest::Mock.new
+ target.expect :target, "example.combadguy.com"
+
+ dns = MiniTest::Mock.new
+ dns.expect :getresource, target, [String, Object]
+
+ fetch = Gem::RemoteFetcher.new nil, dns
+ assert_equal URI.parse("http://example.com/foo"), fetch.api_endpoint(uri)
+
+ target.verify
+ dns.verify
+ end
+
+ def test_api_endpoint_ignores_trans_domain_values_that_end_with_original
+ uri = URI.parse "http://example.com/foo"
+ target = MiniTest::Mock.new
+ target.expect :target, "badexample.com"
+
+ dns = MiniTest::Mock.new
+ dns.expect :getresource, target, [String, Object]
+
+ fetch = Gem::RemoteFetcher.new nil, dns
+ assert_equal URI.parse("http://example.com/foo"), fetch.api_endpoint(uri)
+
+ target.verify
+ dns.verify
+ end
+
+ def test_cache_update_path
+ uri = URI 'http://example/file'
+ path = File.join @tempdir, 'file'
+
+ fetcher = util_fuck_with_fetcher 'hello'
+
+ data = fetcher.cache_update_path uri, path
+
+ assert_equal 'hello', data
+
+ assert_equal 'hello', File.read(path)
+ end
+
+ def test_cache_update_path_no_update
+ uri = URI 'http://example/file'
+ path = File.join @tempdir, 'file'
+
+ fetcher = util_fuck_with_fetcher 'hello'
+
+ data = fetcher.cache_update_path uri, path, false
+
+ assert_equal 'hello', data
+
+ refute_path_exists path
+ end
+
+ def util_fuck_with_fetcher data, blow = false
+ fetcher = Gem::RemoteFetcher.fetcher
+ fetcher.instance_variable_set :@test_data, data
+
+ unless blow then
+ def fetcher.fetch_path arg, *rest
+ @test_arg = arg
+ @test_data
+ end
+ else
+ def fetcher.fetch_path arg, *rest
+ # OMG I'm such an ass
+ class << self; remove_method :fetch_path; end
+ def self.fetch_path arg, *rest
+ @test_arg = arg
+ @test_data
+ end
+
+ raise Gem::RemoteFetcher::FetchError.new("haha!", nil)
+ end
+ end
+
+ fetcher
+ end
+
+ def test_download
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher a1_data
+
+ a1_cache_gem = @a1.cache_file
+ assert_equal a1_cache_gem, fetcher.download(@a1, 'http://gems.example.com')
+ assert_equal("http://gems.example.com/gems/a-1.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+ assert File.exist?(a1_cache_gem)
+ end
+
+ def test_download_with_auth
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher a1_data
+
+ a1_cache_gem = @a1.cache_file
+ assert_equal a1_cache_gem, fetcher.download(@a1, 'http://user:password@gems.example.com')
+ assert_equal("http://user:password@gems.example.com/gems/a-1.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+ assert File.exist?(a1_cache_gem)
+ end
+
+ def test_download_with_encoded_auth
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher a1_data
+
+ a1_cache_gem = @a1.cache_file
+ assert_equal a1_cache_gem, fetcher.download(@a1, 'http://user:%25pas%25sword@gems.example.com')
+ assert_equal("http://user:%25pas%25sword@gems.example.com/gems/a-1.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+ assert File.exist?(a1_cache_gem)
+ end
+
+ def test_download_cached
+ FileUtils.mv @a1_gem, @cache_dir
+
+ inst = Gem::RemoteFetcher.fetcher
+
+ assert_equal @a1.cache_file, inst.download(@a1, 'http://gems.example.com')
+ end
+
+ def test_download_local
+ FileUtils.mv @a1_gem, @tempdir
+ local_path = File.join @tempdir, @a1.file_name
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::RemoteFetcher.fetcher
+ end
+
+ assert_equal @a1.cache_file, inst.download(@a1, local_path)
+ end
+
+ def test_download_local_space
+ space_path = File.join @tempdir, 'space path'
+ FileUtils.mkdir space_path
+ FileUtils.mv @a1_gem, space_path
+ local_path = File.join space_path, @a1.file_name
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::RemoteFetcher.fetcher
+ end
+
+ assert_equal @a1.cache_file, inst.download(@a1, local_path)
+ end
+
+ def test_download_install_dir
+ a1_data = File.open @a1_gem, 'rb' do |fp|
+ fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher a1_data
+
+ install_dir = File.join @tempdir, 'more_gems'
+
+ a1_cache_gem = File.join install_dir, "cache", @a1.file_name
+ FileUtils.mkdir_p(File.dirname(a1_cache_gem))
+ actual = fetcher.download(@a1, 'http://gems.example.com', install_dir)
+
+ assert_equal a1_cache_gem, actual
+ assert_equal("http://gems.example.com/gems/a-1.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+
+ assert File.exist?(a1_cache_gem)
+ end
+
+ unless win_platform? # File.chmod doesn't work
+ def test_download_local_read_only
+ FileUtils.mv @a1_gem, @tempdir
+ local_path = File.join @tempdir, @a1.file_name
+ inst = nil
+ FileUtils.chmod 0555, @a1.cache_dir
+
+ Dir.chdir @tempdir do
+ inst = Gem::RemoteFetcher.fetcher
+ end
+
+ assert_equal(File.join(@tempdir, @a1.file_name),
+ inst.download(@a1, local_path))
+ ensure
+ FileUtils.chmod 0755, @a1.cache_dir
+ end
+
+ def test_download_read_only
+ FileUtils.chmod 0555, @a1.cache_dir
+ FileUtils.chmod 0555, @gemhome
+
+ fetcher = util_fuck_with_fetcher File.read(@a1_gem)
+ fetcher.download(@a1, 'http://gems.example.com')
+ a1_cache_gem = File.join Gem.user_dir, "cache", @a1.file_name
+ assert File.exist? a1_cache_gem
+ ensure
+ FileUtils.chmod 0755, @gemhome
+ FileUtils.chmod 0755, @a1.cache_dir
+ end
+ end
+
+ def test_download_platform_legacy
+ original_platform = 'old-platform'
+
+ e1, e1_gem = util_gem 'e', '1' do |s|
+ s.platform = Gem::Platform::CURRENT
+ s.instance_variable_set :@original_platform, original_platform
+ end
+ e1.loaded_from = File.join(@gemhome, 'specifications', e1.full_name)
+
+ e1_data = nil
+ File.open e1_gem, 'rb' do |fp|
+ e1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher e1_data, :blow_chunks
+
+ e1_cache_gem = e1.cache_file
+
+ assert_equal e1_cache_gem, fetcher.download(e1, 'http://gems.example.com')
+
+ assert_equal("http://gems.example.com/gems/#{e1.original_name}.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+ assert File.exist?(e1_cache_gem)
+ end
+
+ def test_download_same_file
+ FileUtils.mv @a1_gem, @tempdir
+ local_path = File.join @tempdir, @a1.file_name
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::RemoteFetcher.fetcher
+ end
+
+ cache_path = @a1.cache_file
+ FileUtils.mv local_path, cache_path
+
+ gem = Gem::Package.new cache_path
+
+ assert_equal cache_path, inst.download(gem.spec, cache_path)
+ end
+
+ def test_download_unsupported
+ inst = Gem::RemoteFetcher.fetcher
+
+ e = assert_raises ArgumentError do
+ inst.download @a1, 'ftp://gems.rubyforge.org'
+ end
+
+ assert_equal 'unsupported URI scheme ftp', e.message
+ end
+
+ def test_download_to_cache
+ @a2, @a2_gem = util_gem 'a', '2'
+
+ util_setup_spec_fetcher @a1, @a2
+ @fetcher.instance_variable_set :@a1, @a1
+ @fetcher.instance_variable_set :@a2, @a2
+ def @fetcher.fetch_path uri, mtime = nil, head = false
+ case uri.request_uri
+ when /#{@a1.spec_name}/ then
+ Gem.deflate Marshal.dump @a1
+ when /#{@a2.spec_name}/ then
+ Gem.deflate Marshal.dump @a2
+ else
+ uri.to_s
+ end
+ end
+
+ gem = Gem::RemoteFetcher.fetcher.download_to_cache dep 'a'
+
+ assert_equal @a2.file_name, File.basename(gem)
+ end
+
+ def test_fetch_path_gzip
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(uri, mtime, head = nil)
+ Gem.gzip 'foo'
+ end
+
+ assert_equal 'foo', fetcher.fetch_path(@uri + 'foo.gz')
+ end
+
+ def test_fetch_path_gzip_unmodified
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(uri, mtime, head = nil)
+ nil
+ end
+
+ assert_equal nil, fetcher.fetch_path(@uri + 'foo.gz', Time.at(0))
+ end
+
+ def test_fetch_path_io_error
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(*)
+ raise EOFError
+ end
+
+ url = 'http://example.com/uri'
+
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path url
+ end
+
+ assert_equal "EOFError: EOFError (#{url})", e.message
+ assert_equal url, e.uri
+ end
+
+ def test_fetch_path_socket_error
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(uri, mtime, head = nil)
+ raise SocketError
+ end
+
+ url = 'http://example.com/uri'
+
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path url
+ end
+
+ assert_equal "SocketError: SocketError (#{url})", e.message
+ assert_equal url, e.uri
+ end
+
+ def test_fetch_path_system_call_error
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(uri, mtime = nil, head = nil)
+ raise Errno::ECONNREFUSED, 'connect(2)'
+ end
+
+ url = 'http://example.com/uri'
+
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path url
+ end
+
+ assert_match %r|ECONNREFUSED:.*connect\(2\) \(#{Regexp.escape url}\)\z|,
+ e.message
+ assert_equal url, e.uri
+ end
+
+ def test_fetch_path_unmodified
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(uri, mtime, head = nil)
+ nil
+ end
+
+ assert_equal nil, fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0))
+ end
+
+ def test_implicit_no_proxy
+ use_ui @ui do
+ ENV['http_proxy'] = 'http://fakeurl:12345'
+ fetcher = Gem::RemoteFetcher.new :no_proxy
+ @fetcher = fetcher
+ assert_data_from_server fetcher.fetch_path(@server_uri)
+ end
+ end
+
+ def test_implicit_proxy
+ use_ui @ui do
+ ENV['http_proxy'] = @proxy_uri
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ assert_data_from_proxy fetcher.fetch_path(@server_uri)
+ end
+ end
+
+ def test_implicit_upper_case_proxy
+ use_ui @ui do
+ ENV['HTTP_PROXY'] = @proxy_uri
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ assert_data_from_proxy fetcher.fetch_path(@server_uri)
+ end
+ end
+
+ def test_implicit_proxy_no_env
+ use_ui @ui do
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ assert_data_from_server fetcher.fetch_path(@server_uri)
+ end
+ end
+
+ def test_fetch_http
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ url = 'http://gems.example.com/redirect'
+
+ def fetcher.request(uri, request_class, last_modified = nil)
+ url = 'http://gems.example.com/redirect'
+ unless defined? @requested then
+ @requested = true
+ res = Net::HTTPMovedPermanently.new nil, 301, nil
+ res.add_field 'Location', url
+ res
+ else
+ res = Net::HTTPOK.new nil, 200, nil
+ def res.body() 'real_path' end
+ res
+ end
+ end
+
+ data = fetcher.fetch_http URI.parse(url)
+
+ assert_equal 'real_path', data
+ end
+
+ def test_fetch_http_redirects
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ url = 'http://gems.example.com/redirect'
+
+ def fetcher.request(uri, request_class, last_modified = nil)
+ url = 'http://gems.example.com/redirect'
+ res = Net::HTTPMovedPermanently.new nil, 301, nil
+ res.add_field 'Location', url
+ res
+ end
+
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_http URI.parse(url)
+ end
+
+ assert_equal "too many redirects (#{url})", e.message
+ end
+
+ def test_fetch_s3
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ url = 's3://testuser:testpass@my-bucket/gems/specs.4.8.gz'
+ $fetched_uri = nil
+
+ def fetcher.request(uri, request_class, last_modified = nil)
+ $fetched_uri = uri
+ res = Net::HTTPOK.new nil, 200, nil
+ def res.body() 'success' end
+ res
+ end
+
+ def fetcher.s3_expiration
+ 1395098371
+ end
+
+ data = fetcher.fetch_s3 URI.parse(url)
+
+ assert_equal 'https://my-bucket.s3.amazonaws.com/gems/specs.4.8.gz?AWSAccessKeyId=testuser&Expires=1395098371&Signature=eUTr7NkpZEet%2BJySE%2BfH6qukroI%3D', $fetched_uri.to_s
+ assert_equal 'success', data
+ ensure
+ $fetched_uri = nil
+ end
+
+ def test_fetch_s3_no_creds
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ url = 's3://my-bucket/gems/specs.4.8.gz'
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_s3 URI.parse(url)
+ end
+
+ assert_match "credentials needed", e.message
+ end
+
+ def test_observe_no_proxy_env_single_host
+ use_ui @ui do
+ ENV["http_proxy"] = @proxy_uri
+ ENV["no_proxy"] = URI::parse(@server_uri).host
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ assert_data_from_server fetcher.fetch_path(@server_uri)
+ end
+ end
+
+ def test_observe_no_proxy_env_list
+ use_ui @ui do
+ ENV["http_proxy"] = @proxy_uri
+ ENV["no_proxy"] = "fakeurl.com, #{URI::parse(@server_uri).host}"
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ assert_data_from_server fetcher.fetch_path(@server_uri)
+ end
+ end
+
+ def test_request_block
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ assert_throws :block_called do
+ fetcher.request URI('http://example'), Net::HTTP::Get do |req|
+ assert_kind_of Net::HTTPGenericRequest, req
+ throw :block_called
+ end
+ end
+ end
+
+ def test_yaml_error_on_size
+ use_ui @ui do
+ self.class.enable_yaml = false
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+ assert_error { fetcher.size }
+ end
+ end
+
+ def test_ssl_connection
+ ssl_server = self.class.start_ssl_server
+ temp_ca_cert = File.join(DIR, 'ca_cert.pem')
+ with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher|
+ fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
+ end
+ end
+
+ def test_ssl_client_cert_auth_connection
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ ssl_server = self.class.start_ssl_server({
+ :SSLVerifyClient =>
+ OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT})
+
+ temp_ca_cert = File.join(DIR, 'ca_cert.pem')
+ temp_client_cert = File.join(DIR, 'client.pem')
+
+ with_configured_fetcher(
+ ":ssl_ca_cert: #{temp_ca_cert}\n" +
+ ":ssl_client_cert: #{temp_client_cert}\n") do |fetcher|
+ fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
+ end
+ end
+
+ def test_do_not_allow_invalid_client_cert_auth_connection
+ skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+
+ ssl_server = self.class.start_ssl_server({
+ :SSLVerifyClient =>
+ OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT})
+
+ temp_ca_cert = File.join(DIR, 'ca_cert.pem')
+ temp_client_cert = File.join(DIR, 'invalid_client.pem')
+
+ with_configured_fetcher(
+ ":ssl_ca_cert: #{temp_ca_cert}\n" +
+ ":ssl_client_cert: #{temp_client_cert}\n") do |fetcher|
+ assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
+ end
+ end
+ end
+
+ def test_do_not_allow_insecure_ssl_connection_by_default
+ ssl_server = self.class.start_ssl_server
+ with_configured_fetcher do |fetcher|
+ assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
+ end
+ end
+ end
+
+ def test_ssl_connection_allow_verify_none
+ ssl_server = self.class.start_ssl_server
+ with_configured_fetcher(":ssl_verify_mode: 0") do |fetcher|
+ fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
+ end
+ end
+
+ def test_do_not_follow_insecure_redirect
+ ssl_server = self.class.start_ssl_server
+ temp_ca_cert = File.join(DIR, 'ca_cert.pem'),
+ with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher|
+ assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri}")
+ end
+ end
+ end
+
+ def with_configured_fetcher(config_str = nil, &block)
+ if config_str
+ temp_conf = File.join @tempdir, '.gemrc'
+ File.open temp_conf, 'w' do |fp|
+ fp.puts config_str
+ end
+ Gem.configuration = Gem::ConfigFile.new %W[--config-file #{temp_conf}]
+ end
+ fetcher = Gem::RemoteFetcher.new
+ yield fetcher
+ ensure
+ fetcher.close_all
+ Gem.configuration = nil
+ end
+
+ def assert_error(exception_class=Exception)
+ got_exception = false
+
+ begin
+ yield
+ rescue exception_class
+ got_exception = true
+ end
+
+ assert got_exception, "Expected exception conforming to #{exception_class}"
+ end
+
+ def assert_data_from_server(data)
+ assert_match(/0\.4\.11/, data, "Data is not from server")
+ end
+
+ def assert_data_from_proxy(data)
+ assert_match(/0\.4\.2/, data, "Data is not from proxy")
+ end
+
+ class NilLog < WEBrick::Log
+ def log(level, data) #Do nothing
+ end
+ end
+
+ class << self
+ attr_reader :normal_server, :proxy_server
+ attr_accessor :enable_zip, :enable_yaml
+
+ def start_servers
+ @normal_server ||= start_server(SERVER_DATA)
+ @proxy_server ||= start_server(PROXY_DATA)
+ @enable_yaml = true
+ @enable_zip = false
+ @ssl_server = nil
+ @ssl_server_thread = nil
+ end
+
+ def stop_servers
+ if @normal_server
+ @normal_server.kill.join
+ @normal_server = nil
+ end
+ if @proxy_server
+ @proxy_server.kill.join
+ @proxy_server = nil
+ end
+ if @ssl_server
+ @ssl_server.stop
+ @ssl_server = nil
+ end
+ if @ssl_server_thread
+ @ssl_server_thread.kill.join
+ @ssl_server_thread = nil
+ end
+ end
+
+ def normal_server_port
+ @normal_server[:server].config[:Port]
+ end
+
+ def proxy_server_port
+ @proxy_server[:server].config[:Port]
+ end
+
+ DIR = File.expand_path(File.dirname(__FILE__))
+
+ def start_ssl_server(config = {})
+ raise MiniTest::Skip, 'openssl not installed' unless
+ defined?(OpenSSL::SSL)
+
+ null_logger = NilLog.new
+ server = WEBrick::HTTPServer.new({
+ :Port => 0,
+ :Logger => null_logger,
+ :AccessLog => [],
+ :SSLEnable => true,
+ :SSLCACertificateFile => File.join(DIR, 'ca_cert.pem'),
+ :SSLCertificate => cert('ssl_cert.pem'),
+ :SSLPrivateKey => key('ssl_key.pem'),
+ :SSLVerifyClient => nil,
+ :SSLCertName => nil
+ }.merge(config))
+ server.mount_proc("/yaml") { |req, res|
+ res.body = "--- true\n"
+ }
+ server.mount_proc("/insecure_redirect") { |req, res|
+ res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, req.query['to'])
+ }
+ server.ssl_context.tmp_dh_callback = proc {|_, _, k| OpenSSL::PKey::DH.new(k) }
+ t = Thread.new do
+ begin
+ server.start
+ rescue Exception => ex
+ abort ex.message
+ puts "ERROR during server thread: #{ex.message}"
+ ensure
+ server.shutdown
+ end
+ end
+ while server.status != :Running
+ sleep 0.1
+ unless t.alive?
+ t.join
+ raise
+ end
+ end
+ @ssl_server = server
+ @ssl_server_thread = t
+ server
+ end
+
+ private
+
+ def start_server(data)
+ null_logger = NilLog.new
+ s = WEBrick::HTTPServer.new(
+ :Port => 0,
+ :DocumentRoot => nil,
+ :Logger => null_logger,
+ :AccessLog => null_logger
+ )
+ s.mount_proc("/kill") { |req, res| s.shutdown }
+ s.mount_proc("/yaml") { |req, res|
+ if @enable_yaml
+ res.body = data
+ res['Content-Type'] = 'text/plain'
+ res['content-length'] = data.size
+ else
+ res.status = "404"
+ res.body = "<h1>NOT FOUND</h1>"
+ res['Content-Type'] = 'text/html'
+ end
+ }
+ s.mount_proc("/yaml.Z") { |req, res|
+ if @enable_zip
+ res.body = Zlib::Deflate.deflate(data)
+ res['Content-Type'] = 'text/plain'
+ else
+ res.status = "404"
+ res.body = "<h1>NOT FOUND</h1>"
+ res['Content-Type'] = 'text/html'
+ end
+ }
+ th = Thread.new do
+ begin
+ s.start
+ rescue Exception => ex
+ abort "ERROR during server thread: #{ex.message}"
+ ensure
+ s.shutdown
+ end
+ end
+ th[:server] = s
+ th
+ end
+
+ def cert(filename)
+ OpenSSL::X509::Certificate.new(File.read(File.join(DIR, filename)))
+ end
+
+ def key(filename)
+ OpenSSL::PKey::RSA.new(File.read(File.join(DIR, filename)))
+ end
+ end
+
+ def test_correct_for_windows_path
+ path = "/C:/WINDOWS/Temp/gems"
+ assert_equal "C:/WINDOWS/Temp/gems", @fetcher.correct_for_windows_path(path)
+
+ path = "/home/skillet"
+ assert_equal "/home/skillet", @fetcher.correct_for_windows_path(path)
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_request.rb b/jni/ruby/test/rubygems/test_gem_request.rb
new file mode 100644
index 0000000..c45d5d8
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_request.rb
@@ -0,0 +1,362 @@
+require 'rubygems/test_case'
+require 'rubygems/request'
+require 'ostruct'
+require 'base64'
+
+class TestGemRequest < Gem::TestCase
+
+ CA_CERT_FILE = cert_path 'ca'
+ CHILD_CERT = load_cert 'child'
+ PUBLIC_CERT = load_cert 'public'
+ PUBLIC_CERT_FILE = cert_path 'public'
+ SSL_CERT = load_cert 'ssl'
+
+ def make_request uri, request_class, last_modified, proxy
+ Gem::Request.create_with_proxy uri, request_class, last_modified, proxy
+ end
+
+ def setup
+ @proxies = %w[http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY]
+ @old_proxies = @proxies.map {|k| ENV[k] }
+ @proxies.each {|k| ENV[k] = nil }
+
+ super
+
+ @proxy_uri = "http://localhost:1234"
+ @uri = URI('http://example')
+
+ @request = make_request @uri, nil, nil, nil
+ end
+
+ def teardown
+ super
+ Gem.configuration[:http_proxy] = nil
+ @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] }
+ end
+
+ def test_initialize_proxy
+ proxy_uri = 'http://proxy.example.com'
+
+ request = make_request @uri, nil, nil, proxy_uri
+
+ assert_equal proxy_uri, request.proxy_uri.to_s
+ end
+
+ def test_initialize_proxy_URI
+ proxy_uri = 'http://proxy.example.com'
+
+ request = make_request @uri, nil, nil, URI(proxy_uri)
+
+ assert_equal proxy_uri, request.proxy_uri.to_s
+ end
+
+ def test_initialize_proxy_ENV
+ ENV['http_proxy'] = @proxy_uri
+ ENV['http_proxy_user'] = 'foo'
+ ENV['http_proxy_pass'] = 'bar'
+
+ request = make_request @uri, nil, nil, nil
+
+ proxy = request.proxy_uri
+
+ assert_equal 'foo', proxy.user
+ assert_equal 'bar', proxy.password
+ end
+
+ def test_initialize_proxy_ENV_https
+ ENV['https_proxy'] = @proxy_uri
+
+ request = make_request URI('https://example'), nil, nil, nil
+
+ proxy = request.proxy_uri
+
+ assert_equal URI(@proxy_uri), proxy
+ end
+
+ def test_configure_connection_for_https
+ connection = Net::HTTP.new 'localhost', 443
+
+ request = Class.new(Gem::Request) {
+ def self.get_cert_files
+ [TestGemRequest::PUBLIC_CERT_FILE]
+ end
+ }.create_with_proxy URI('https://example'), nil, nil, nil
+
+ Gem::Request.configure_connection_for_https connection, request.cert_files
+
+ cert_store = connection.cert_store
+
+ assert cert_store.verify CHILD_CERT
+ end
+
+ def test_configure_connection_for_https_ssl_ca_cert
+ ssl_ca_cert, Gem.configuration.ssl_ca_cert =
+ Gem.configuration.ssl_ca_cert, CA_CERT_FILE
+
+ connection = Net::HTTP.new 'localhost', 443
+
+ request = Class.new(Gem::Request) {
+ def self.get_cert_files
+ [TestGemRequest::PUBLIC_CERT_FILE]
+ end
+ }.create_with_proxy URI('https://example'), nil, nil, nil
+
+ Gem::Request.configure_connection_for_https connection, request.cert_files
+
+ cert_store = connection.cert_store
+
+ assert cert_store.verify CHILD_CERT
+ assert cert_store.verify SSL_CERT
+ ensure
+ Gem.configuration.ssl_ca_cert = ssl_ca_cert
+ end
+
+ def test_get_proxy_from_env_fallback
+ ENV['http_proxy'] = @proxy_uri
+ request = make_request @uri, nil, nil, nil
+ proxy = request.proxy_uri
+
+ assert_equal URI(@proxy_uri), proxy
+ end
+
+ def test_get_proxy_from_env_https
+ ENV['https_proxy'] = @proxy_uri
+ uri = URI('https://example')
+ request = make_request uri, nil, nil, nil
+
+ proxy = request.proxy_uri
+
+ assert_equal URI(@proxy_uri), proxy
+ end
+
+ def test_get_proxy_from_env_domain
+ ENV['http_proxy'] = @proxy_uri
+ ENV['http_proxy_user'] = 'foo\user'
+ ENV['http_proxy_pass'] = 'my bar'
+ request = make_request @uri, nil, nil, nil
+
+ proxy = request.proxy_uri
+
+ assert_equal 'foo\user', Gem::UriFormatter.new(proxy.user).unescape
+ assert_equal 'my bar', Gem::UriFormatter.new(proxy.password).unescape
+ end
+
+ def test_get_proxy_from_env_escape
+ ENV['http_proxy'] = @proxy_uri
+ ENV['http_proxy_user'] = 'foo@user'
+ ENV['http_proxy_pass'] = 'my@bar'
+ request = make_request @uri, nil, nil, nil
+
+ proxy = request.proxy_uri
+
+ assert_equal 'foo%40user', proxy.user
+ assert_equal 'my%40bar', proxy.password
+ end
+
+ def test_get_proxy_from_env_normalize
+ ENV['HTTP_PROXY'] = 'fakeurl:12345'
+ request = make_request @uri, nil, nil, nil
+
+ assert_equal 'http://fakeurl:12345', request.proxy_uri.to_s
+ end
+
+ def test_get_proxy_from_env_empty
+ ENV['HTTP_PROXY'] = ''
+ ENV.delete 'http_proxy'
+ request = make_request @uri, nil, nil, nil
+
+ assert_nil request.proxy_uri
+ end
+
+ def test_fetch
+ uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
+ response = util_stub_net_http(:body => :junk, :code => 200) do
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+
+ @request.fetch
+ end
+
+ assert_equal 200, response.code
+ assert_equal :junk, response.body
+ end
+
+ def test_fetch_basic_auth
+ uri = URI.parse "https://user:pass@example.rubygems/specs.#{Gem.marshal_version}"
+ conn = util_stub_net_http(:body => :junk, :code => 200) do |c|
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ c
+ end
+
+ auth_header = conn.payload['Authorization']
+ assert_equal "Basic #{Base64.encode64('user:pass')}".strip, auth_header
+ end
+
+ def test_fetch_basic_auth_encoded
+ uri = URI.parse "https://user:%7BDEScede%7Dpass@example.rubygems/specs.#{Gem.marshal_version}"
+ conn = util_stub_net_http(:body => :junk, :code => 200) do |c|
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ c
+ end
+
+ auth_header = conn.payload['Authorization']
+ assert_equal "Basic #{Base64.encode64('user:{DEScede}pass')}".strip, auth_header
+ end
+
+ def test_fetch_head
+ uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
+ response = util_stub_net_http(:body => '', :code => 200) do |conn|
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ end
+
+ assert_equal 200, response.code
+ assert_equal '', response.body
+ end
+
+ def test_fetch_unmodified
+ uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
+ t = Time.utc(2013, 1, 2, 3, 4, 5)
+ conn, response = util_stub_net_http(:body => '', :code => 304) do |c|
+ @request = make_request(uri, Net::HTTP::Get, t, nil)
+ [c, @request.fetch]
+ end
+
+ assert_equal 304, response.code
+ assert_equal '', response.body
+
+ modified_header = conn.payload['if-modified-since']
+
+ assert_equal 'Wed, 02 Jan 2013 03:04:05 GMT', modified_header
+ end
+
+ def test_user_agent
+ ua = make_request(@uri, nil, nil, nil).user_agent
+
+ assert_match %r%^RubyGems/\S+ \S+ Ruby/\S+ \(.*?\)%, ua
+ assert_match %r%RubyGems/#{Regexp.escape Gem::VERSION}%, ua
+ assert_match %r% #{Regexp.escape Gem::Platform.local.to_s} %, ua
+ assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}%, ua
+ assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE} %, ua
+ end
+
+ def test_user_agent_engine
+ util_save_version
+
+ Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
+ Object.send :const_set, :RUBY_ENGINE, 'vroom'
+
+ ua = make_request(@uri, nil, nil, nil).user_agent
+
+ assert_match %r%\) vroom%, ua
+ ensure
+ util_restore_version
+ end
+
+ def test_user_agent_engine_ruby
+ util_save_version
+
+ Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
+ Object.send :const_set, :RUBY_ENGINE, 'ruby'
+
+ ua = make_request(@uri, nil, nil, nil).user_agent
+
+ assert_match %r%\)%, ua
+ ensure
+ util_restore_version
+ end
+
+ def test_user_agent_patchlevel
+ util_save_version
+
+ Object.send :remove_const, :RUBY_PATCHLEVEL
+ Object.send :const_set, :RUBY_PATCHLEVEL, 5
+
+ ua = make_request(@uri, nil, nil, nil).user_agent
+
+ assert_match %r% patchlevel 5\)%, ua
+ ensure
+ util_restore_version
+ end
+
+ def test_user_agent_revision
+ util_save_version
+
+ Object.send :remove_const, :RUBY_PATCHLEVEL
+ Object.send :const_set, :RUBY_PATCHLEVEL, -1
+ Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
+ Object.send :const_set, :RUBY_REVISION, 6
+
+ ua = make_request(@uri, nil, nil, nil).user_agent
+
+ assert_match %r% revision 6\)%, ua
+ assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}dev%, ua
+ ensure
+ util_restore_version
+ end
+
+ def test_user_agent_revision_missing
+ util_save_version
+
+ Object.send :remove_const, :RUBY_PATCHLEVEL
+ Object.send :const_set, :RUBY_PATCHLEVEL, -1
+ Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
+
+ ua = make_request(@uri, nil, nil, nil).user_agent
+
+ assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE}\)%, ua
+ ensure
+ util_restore_version
+ end
+
+ def util_restore_version
+ Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
+ Object.send :const_set, :RUBY_ENGINE, @orig_RUBY_ENGINE if
+ defined?(@orig_RUBY_ENGINE)
+
+ Object.send :remove_const, :RUBY_PATCHLEVEL
+ Object.send :const_set, :RUBY_PATCHLEVEL, @orig_RUBY_PATCHLEVEL
+
+ Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
+ Object.send :const_set, :RUBY_REVISION, @orig_RUBY_REVISION if
+ defined?(@orig_RUBY_REVISION)
+ end
+
+ def util_save_version
+ @orig_RUBY_ENGINE = RUBY_ENGINE if defined? RUBY_ENGINE
+ @orig_RUBY_PATCHLEVEL = RUBY_PATCHLEVEL
+ @orig_RUBY_REVISION = RUBY_REVISION if defined? RUBY_REVISION
+ end
+
+ def util_stub_net_http hash
+ old_client = Gem::Request::ConnectionPools.client
+ conn = Conn.new OpenStruct.new(hash)
+ Gem::Request::ConnectionPools.client = conn
+ yield conn
+ ensure
+ Gem::Request::ConnectionPools.client = old_client
+ end
+
+ class Conn
+ attr_accessor :payload
+
+ def new *args; self; end
+ def use_ssl=(bool); end
+ def verify_mode=(setting); end
+ def cert_store=(setting); end
+ def start; end
+
+ def initialize(response)
+ @response = response
+ self.payload = nil
+ end
+
+ def request(req)
+ self.payload = req
+ @response
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_request_connection_pools.rb b/jni/ruby/test/rubygems/test_gem_request_connection_pools.rb
new file mode 100644
index 0000000..1cf6b27
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_request_connection_pools.rb
@@ -0,0 +1,120 @@
+require 'rubygems/test_case'
+require 'rubygems/request'
+require 'timeout'
+
+class TestGemRequestConnectionPool < Gem::TestCase
+ class FakeHttp
+ def initialize *args
+ end
+
+ def start
+ end
+ end
+
+ def setup
+ super
+ @old_client = Gem::Request::ConnectionPools.client
+ Gem::Request::ConnectionPools.client = FakeHttp
+
+ @proxy = URI 'http://proxy.example'
+ end
+
+ def teardown
+ Gem::Request::ConnectionPools.client = @old_client
+ super
+ end
+
+ def test_checkout_same_connection
+ uri = URI.parse('http://example/some_endpoint')
+
+ pools = Gem::Request::ConnectionPools.new nil, []
+ pool = pools.pool_for uri
+ conn = pool.checkout
+ pool.checkin conn
+
+ assert_equal conn, pool.checkout
+ end
+
+ def test_to_proxy_eh
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ env_no_proxy = %w[
+ 1.no-proxy.example
+ 2.no-proxy.example
+ ]
+
+ no_proxy = pools.send :no_proxy?, '2.no-proxy.example', env_no_proxy
+
+ assert no_proxy, 'match'
+
+ no_proxy = pools.send :no_proxy?, 'proxy.example', env_no_proxy
+
+ refute no_proxy, 'mismatch'
+ end
+
+ def test_to_proxy_eh_wildcard
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ env_no_proxy = %w[
+ .no-proxy.example
+ ]
+
+ no_proxy = pools.send :no_proxy?, '2.no-proxy.example', env_no_proxy
+
+ assert no_proxy, 'wildcard matching subdomain'
+
+ no_proxy = pools.send :no_proxy?, 'no-proxy.example', env_no_proxy
+
+ assert no_proxy, 'wildcard matching dotless domain'
+
+ no_proxy = pools.send :no_proxy?, 'proxy.example', env_no_proxy
+
+ refute no_proxy, 'wildcard mismatch'
+ end
+
+ def test_net_http_args
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ net_http_args = pools.send :net_http_args, URI('http://example'), nil
+
+ assert_equal ['example', 80], net_http_args
+ end
+
+ def test_net_http_args_proxy
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ net_http_args = pools.send :net_http_args, URI('http://example'), @proxy
+
+ assert_equal ['example', 80, 'proxy.example', 80, nil, nil], net_http_args
+ end
+
+ def test_net_http_args_no_proxy
+ orig_no_proxy, ENV['no_proxy'] = ENV['no_proxy'], 'example'
+
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ net_http_args = pools.send :net_http_args, URI('http://example'), @proxy
+
+ assert_equal ['example', 80, nil, nil], net_http_args
+
+ ensure
+ ENV['no_proxy'] = orig_no_proxy
+ end
+
+ def test_thread_waits_for_connection
+ uri = URI.parse('http://example/some_endpoint')
+ pools = Gem::Request::ConnectionPools.new nil, []
+ pool = pools.pool_for uri
+
+ pool.checkout
+
+ t1 = Thread.new {
+ timeout(1) do
+ pool.checkout
+ end
+ }
+ assert_raises(Timeout::Error) do
+ t1.join
+ end
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_request_set.rb b/jni/ruby/test/rubygems/test_gem_request_set.rb
new file mode 100644
index 0000000..3c1d5ac
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_request_set.rb
@@ -0,0 +1,597 @@
+require 'rubygems/test_case'
+require 'rubygems/request_set'
+
+class TestGemRequestSet < Gem::TestCase
+ def setup
+ super
+
+ Gem::RemoteFetcher.fetcher = @fetcher = Gem::FakeFetcher.new
+
+ @DR = Gem::Resolver
+ end
+
+ def test_gem
+ util_spec "a", "2"
+
+ rs = Gem::RequestSet.new
+ rs.gem "a", "= 2"
+
+ assert_equal [Gem::Dependency.new("a", "=2")], rs.dependencies
+ end
+
+ def test_gem_duplicate
+ rs = Gem::RequestSet.new
+
+ rs.gem 'a', '1'
+ rs.gem 'a', '2'
+
+ assert_equal [dep('a', '= 1', '= 2')], rs.dependencies
+ end
+
+ def test_import
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+
+ rs.import [dep('b')]
+
+ assert_equal [dep('a'), dep('b')], rs.dependencies
+ end
+
+ def test_install_from_gemdeps
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ done_installing_ran = false
+
+ Gem.done_installing do |installer, specs|
+ done_installing_ran = true
+ end
+
+ rs = Gem::RequestSet.new
+ installed = []
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.puts 'gem "a"'
+ io.flush
+
+ result = rs.install_from_gemdeps :gemdeps => io.path do |req, installer|
+ installed << req.full_name
+ end
+
+ assert_kind_of Array, result # what is supposed to be in here?
+ end
+
+ assert_includes installed, 'a-2'
+ assert_path_exists File.join @gemhome, 'gems', 'a-2'
+ assert_path_exists 'gem.deps.rb.lock'
+
+ assert rs.remote
+ refute done_installing_ran
+ end
+
+ def test_install_from_gemdeps_explain
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ rs = Gem::RequestSet.new
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.puts 'gem "a"'
+ io.flush
+
+ expected = <<-EXPECTED
+Gems to install:
+ a-2
+ EXPECTED
+
+ assert_output expected do
+ rs.install_from_gemdeps :gemdeps => io.path, :explain => true
+ end
+ end
+ end
+
+ def test_install_from_gemdeps_install_dir
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ util_clear_gems
+ refute_path_exists File.join Gem.dir, 'gems', 'a-2'
+
+ rs = Gem::RequestSet.new
+ installed = []
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.puts 'gem "a"'
+ end
+
+ options = {
+ :gemdeps => 'gem.deps.rb',
+ :install_dir => "#{@gemhome}2",
+ }
+
+ rs.install_from_gemdeps options do |req, installer|
+ installed << req.full_name
+ end
+
+ assert_includes installed, 'a-2'
+ refute_path_exists File.join Gem.dir, 'gems', 'a-2'
+ end
+
+ def test_install_from_gemdeps_local
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ rs = Gem::RequestSet.new
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.puts 'gem "a"'
+ io.flush
+
+ assert_raises Gem::UnsatisfiableDependencyError do
+ rs.install_from_gemdeps :gemdeps => io.path, :domain => :local
+ end
+ end
+
+ refute rs.remote
+ end
+
+ def test_install_from_gemdeps_lockfile
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', 2
+ fetcher.gem 'b', 1, 'a' => '>= 0'
+ fetcher.clear
+ end
+
+ rs = Gem::RequestSet.new
+ installed = []
+
+ open 'gem.deps.rb.lock', 'w' do |io|
+ io.puts <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (1)
+ b (1)
+ a (~> 1.0)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ b
+ LOCKFILE
+ end
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.puts 'gem "b"'
+ end
+
+ rs.install_from_gemdeps :gemdeps => 'gem.deps.rb' do |req, installer|
+ installed << req.full_name
+ end
+
+ assert_includes installed, 'b-1'
+ assert_includes installed, 'a-1'
+
+ assert_path_exists File.join @gemhome, 'specifications', 'a-1.gemspec'
+ assert_path_exists File.join @gemhome, 'specifications', 'b-1.gemspec'
+ end
+
+ def test_install_from_gemdeps_version_mismatch
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ rs = Gem::RequestSet.new
+ installed = []
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.puts <<-GEM_DEPS
+gem "a"
+ruby "0"
+ GEM_DEPS
+
+ io.flush
+
+ rs.install_from_gemdeps :gemdeps => io.path do |req, installer|
+ installed << req.full_name
+ end
+ end
+
+ assert_includes installed, 'a-2'
+ end
+
+ def test_load_gemdeps
+ rs = Gem::RequestSet.new
+
+ tf = Tempfile.open 'gem.deps.rb' do |io|
+ io.puts 'gem "a"'
+ io.flush
+
+ gem_deps = rs.load_gemdeps io.path
+
+ assert_kind_of Gem::RequestSet::GemDependencyAPI, gem_deps
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+
+ assert_equal [dep('a')], rs.dependencies
+
+ assert rs.git_set
+ assert rs.vendor_set
+ end
+
+ def test_load_gemdeps_installing
+ rs = Gem::RequestSet.new
+
+ tf = Tempfile.open 'gem.deps.rb' do |io|
+ io.puts 'ruby "0"'
+ io.puts 'gem "a"'
+ io.flush
+
+ gem_deps = rs.load_gemdeps io.path, [], true
+
+ assert_kind_of Gem::RequestSet::GemDependencyAPI, gem_deps
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+
+ assert_equal [dep('a')], rs.dependencies
+ end
+
+ def test_load_gemdeps_without_groups
+ rs = Gem::RequestSet.new
+
+ tf = Tempfile.open 'gem.deps.rb' do |io|
+ io.puts 'gem "a", :group => :test'
+ io.flush
+
+ rs.load_gemdeps io.path, [:test]
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+
+ assert_empty rs.dependencies
+ end
+
+ def test_resolve
+ a = util_spec "a", "2", "b" => ">= 2"
+ b = util_spec "b", "2"
+
+ rs = Gem::RequestSet.new
+ rs.gem "a"
+
+ orig_errors = rs.errors
+
+ res = rs.resolve StaticSet.new([a, b])
+ assert_equal 2, res.size
+
+ names = res.map { |s| s.full_name }.sort
+
+ assert_equal ["a-2", "b-2"], names
+
+ refute_same orig_errors, rs.errors
+ end
+
+ def test_bug_bug_990
+ a = util_spec 'a', '1.b', 'b' => '~> 1.a'
+ b = util_spec 'b', '1.b', 'c' => '>= 1'
+ c = util_spec 'c', '1.1.b'
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.prerelease = true
+
+ res = rs.resolve StaticSet.new([a, b, c])
+ assert_equal 3, res.size
+
+ names = res.map { |s| s.full_name }.sort
+
+ assert_equal %w[a-1.b b-1.b c-1.1.b], names
+ end
+
+ def test_resolve_development
+ a = util_spec 'a', 1
+ spec = Gem::Resolver::SpecSpecification.new nil, a
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+
+ res = rs.resolve StaticSet.new [spec]
+ assert_equal 1, res.size
+
+ assert rs.resolver.development
+ refute rs.resolver.development_shallow
+ end
+
+ def test_resolve_development_shallow
+ a = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c = util_spec 'c', 1
+
+ a_spec = Gem::Resolver::SpecSpecification.new nil, a
+ b_spec = Gem::Resolver::SpecSpecification.new nil, b
+ c_spec = Gem::Resolver::SpecSpecification.new nil, c
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+ rs.development_shallow = true
+
+ res = rs.resolve StaticSet.new [a_spec, b_spec, c_spec]
+ assert_equal 2, res.size
+
+ assert rs.resolver.development
+ assert rs.resolver.development_shallow
+ end
+
+ def test_resolve_git
+ name, _, repository, = git_gem
+
+ rs = Gem::RequestSet.new
+
+ tf = Tempfile.open 'gem.deps.rb' do |io|
+ io.puts <<-gems_deps_rb
+ gem "#{name}", :git => "#{repository}"
+ gems_deps_rb
+
+ io.flush
+
+ rs.load_gemdeps io.path
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+
+ res = rs.resolve
+ assert_equal 1, res.size
+
+ names = res.map { |s| s.full_name }.sort
+
+ assert_equal %w[a-1], names
+
+ assert_equal [@DR::BestSet, @DR::GitSet, @DR::VendorSet],
+ rs.sets.map { |set| set.class }
+ end
+
+ def test_resolve_ignore_dependencies
+ a = util_spec "a", "2", "b" => ">= 2"
+ b = util_spec "b", "2"
+
+ rs = Gem::RequestSet.new
+ rs.gem "a"
+ rs.ignore_dependencies = true
+
+ res = rs.resolve StaticSet.new([a, b])
+ assert_equal 1, res.size
+
+ names = res.map { |s| s.full_name }.sort
+
+ assert_equal %w[a-2], names
+ end
+
+ def test_resolve_incompatible
+ a1 = util_spec 'a', 1
+ a2 = util_spec 'a', 2
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a', '= 1'
+ rs.gem 'a', '= 2'
+
+ set = StaticSet.new [a1, a2]
+
+ assert_raises Gem::UnsatisfiableDependencyError do
+ rs.resolve set
+ end
+ end
+
+ def test_resolve_vendor
+ a_name, _, a_directory = vendor_gem 'a', 1 do |s|
+ s.add_dependency 'b', '~> 2.0'
+ end
+
+ b_name, _, b_directory = vendor_gem 'b', 2
+
+ rs = Gem::RequestSet.new
+
+ tf = Tempfile.open 'gem.deps.rb' do |io|
+ io.puts <<-gems_deps_rb
+ gem "#{a_name}", :path => "#{a_directory}"
+ gem "#{b_name}", :path => "#{b_directory}"
+ gems_deps_rb
+
+ io.flush
+
+ rs.load_gemdeps io.path
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+
+ res = rs.resolve
+ assert_equal 2, res.size
+
+ names = res.map { |s| s.full_name }.sort
+
+ assert_equal ["a-1", "b-2"], names
+
+ assert_equal [@DR::BestSet, @DR::GitSet, @DR::VendorSet],
+ rs.sets.map { |set| set.class }
+ end
+
+ def test_sorted_requests
+ a = util_spec "a", "2", "b" => ">= 2"
+ b = util_spec "b", "2", "c" => ">= 2"
+ c = util_spec "c", "2"
+
+ rs = Gem::RequestSet.new
+ rs.gem "a"
+
+ rs.resolve StaticSet.new([a, b, c])
+
+ names = rs.sorted_requests.map { |s| s.full_name }
+ assert_equal %w!c-2 b-2 a-2!, names
+ end
+
+ def test_install
+ done_installing_ran = false
+
+ Gem.done_installing do
+ done_installing_ran = true
+ end
+
+ spec_fetcher do |fetcher|
+ fetcher.gem "a", "1", "b" => "= 1"
+ fetcher.gem "b", "1"
+
+ fetcher.clear
+ end
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+
+ rs.resolve
+
+ reqs = []
+ installers = []
+
+ installed = rs.install({}) do |req, installer|
+ reqs << req
+ installers << installer
+ end
+
+ assert_equal %w[b-1 a-1], reqs.map { |req| req.full_name }
+ assert_equal %w[b-1 a-1],
+ installers.map { |installer| installer.spec.full_name }
+
+ assert_path_exists File.join @gemhome, 'specifications', 'a-1.gemspec'
+ assert_path_exists File.join @gemhome, 'specifications', 'b-1.gemspec'
+
+ assert_equal %w[b-1 a-1], installed.map { |s| s.full_name }
+
+ assert done_installing_ran
+ end
+
+ def test_install_into
+ spec_fetcher do |fetcher|
+ fetcher.gem "a", "1", "b" => "= 1"
+ fetcher.gem "b", "1"
+ end
+
+ rs = Gem::RequestSet.new
+ rs.gem "a"
+
+ rs.resolve
+
+ installed = rs.install_into @tempdir do
+ assert_equal @tempdir, ENV['GEM_HOME']
+ end
+
+ assert_path_exists File.join @tempdir, 'specifications', 'a-1.gemspec'
+ assert_path_exists File.join @tempdir, 'specifications', 'b-1.gemspec'
+
+ assert_equal %w!b-1 a-1!, installed.map { |s| s.full_name }
+ end
+
+ def test_install_into_development_shallow
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '1' do |s|
+ s.add_development_dependency 'b', '= 1'
+ end
+
+ fetcher.gem 'b', '1' do |s|
+ s.add_development_dependency 'c', '= 1'
+ end
+
+ fetcher.spec 'c', '1'
+ end
+
+ rs = Gem::RequestSet.new
+ rs.development = true
+ rs.development_shallow = true
+ rs.gem 'a'
+
+ rs.resolve
+
+ options = {
+ :development => true,
+ :development_shallow => true,
+ }
+
+ installed = rs.install_into @tempdir, true, options do
+ assert_equal @tempdir, ENV['GEM_HOME']
+ end
+
+ assert_equal %w[a-1 b-1], installed.map { |s| s.full_name }.sort
+ end
+
+ def test_sorted_requests_development_shallow
+ a = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c = util_spec 'c', 1
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+ rs.development_shallow = true
+
+ a_spec = Gem::Resolver::SpecSpecification.new nil, a
+ b_spec = Gem::Resolver::SpecSpecification.new nil, b
+ c_spec = Gem::Resolver::SpecSpecification.new nil, c
+
+ rs.resolve StaticSet.new [a_spec, b_spec, c_spec]
+
+ assert_equal %w[b-1 a-1], rs.sorted_requests.map { |req| req.full_name }
+ end
+
+ def test_tsort_each_child_development
+ a = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c = util_spec 'c', 1
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+ rs.development_shallow = true
+
+ a_spec = Gem::Resolver::SpecSpecification.new nil, a
+ b_spec = Gem::Resolver::SpecSpecification.new nil, b
+ c_spec = Gem::Resolver::SpecSpecification.new nil, c
+
+ rs.resolve StaticSet.new [a_spec, b_spec, c_spec]
+
+ a_req = Gem::Resolver::ActivationRequest.new a_spec, nil
+
+ deps = rs.enum_for(:tsort_each_child, a_req).to_a
+
+ assert_equal %w[b], deps.map { |dep| dep.name }
+ end
+
+ def test_tsort_each_child_development_shallow
+ a = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c = util_spec 'c', 1
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+ rs.development_shallow = true
+
+ a_spec = Gem::Resolver::SpecSpecification.new nil, a
+ b_spec = Gem::Resolver::SpecSpecification.new nil, b
+ c_spec = Gem::Resolver::SpecSpecification.new nil, c
+
+ rs.resolve StaticSet.new [a_spec, b_spec, c_spec]
+
+ b_req = Gem::Resolver::ActivationRequest.new b_spec, nil
+
+ deps = rs.enum_for(:tsort_each_child, b_req).to_a
+
+ assert_empty deps
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_request_set_gem_dependency_api.rb b/jni/ruby/test/rubygems/test_gem_request_set_gem_dependency_api.rb
new file mode 100644
index 0000000..65b30f8
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_request_set_gem_dependency_api.rb
@@ -0,0 +1,823 @@
+require 'rubygems/test_case'
+require 'rubygems/request_set'
+
+class TestGemRequestSetGemDependencyAPI < Gem::TestCase
+
+ def setup
+ super
+
+ @GDA = Gem::RequestSet::GemDependencyAPI
+
+ @set = Gem::RequestSet.new
+
+ @git_set = Gem::Resolver::GitSet.new
+ @vendor_set = Gem::Resolver::VendorSet.new
+
+ @gda = @GDA.new @set, 'gem.deps.rb'
+ @gda.instance_variable_set :@git_set, @git_set
+ @gda.instance_variable_set :@vendor_set, @vendor_set
+ end
+
+ def with_engine_version name, version
+ engine = RUBY_ENGINE if Object.const_defined? :RUBY_ENGINE
+ engine_version_const = "#{Gem.ruby_engine.upcase}_VERSION"
+ engine_version = Object.const_get engine_version_const
+
+ Object.send :remove_const, :RUBY_ENGINE if engine
+ Object.send :remove_const, engine_version_const if name == 'ruby' and
+ Object.const_defined? engine_version_const
+
+ new_engine_version_const = "#{name.upcase}_VERSION"
+ Object.const_set :RUBY_ENGINE, name if name
+ Object.const_set new_engine_version_const, version if version
+
+ Gem.instance_variable_set :@ruby_version, Gem::Version.new(version)
+
+ yield
+
+ ensure
+ Object.send :remove_const, :RUBY_ENGINE if name
+ Object.send :remove_const, new_engine_version_const if version
+
+ Object.send :remove_const, engine_version_const if name == 'ruby' and
+ Object.const_defined? engine_version_const
+
+ Object.const_set :RUBY_ENGINE, engine if engine
+ Object.const_set engine_version_const, engine_version unless
+ Object.const_defined? engine_version_const
+
+ Gem.send :remove_instance_variable, :@ruby_version if
+ Gem.instance_variables.include? :@ruby_version
+ end
+
+ def test_gemspec_without_group
+ @gda.send :add_dependencies, [:development], [dep('a', '= 1')]
+
+ assert_equal [dep('a', '= 1')], @set.dependencies
+
+ @gda.without_groups << :development
+
+ @gda.send :add_dependencies, [:development], [dep('b', '= 2')]
+
+ assert_equal [dep('a', '= 1')], @set.dependencies
+ end
+
+ def test_gem
+ @gda.gem 'a'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[a], @gda.requires['a']
+
+ expected = { 'a' => nil }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_duplicate
+ @gda.gem 'a'
+
+ _, err = capture_io do
+ @gda.gem 'a'
+ end
+
+ expected = "Gem dependencies file gem.deps.rb requires a more than once."
+
+ assert_match expected, err
+ end
+
+ def test_gem_git
+ @gda.gem 'a', :git => 'git/a'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[git/a master], @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_bitbucket
+ @gda.gem 'a', :bitbucket => 'example/repository'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[https://example@bitbucket.org/example/repository.git master],
+ @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_bitbucket_expand_path
+ @gda.gem 'a', :bitbucket => 'example'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[https://example@bitbucket.org/example/example.git master],
+ @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_git_branch
+ @gda.gem 'a', :git => 'git/a', :branch => 'other', :tag => 'v1'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[git/a other], @git_set.repositories['a']
+ end
+
+ def test_gem_git_gist
+ @gda.gem 'a', :gist => 'a'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[https://gist.github.com/a.git master],
+ @git_set.repositories['a']
+ end
+
+ def test_gem_git_ref
+ @gda.gem 'a', :git => 'git/a', :ref => 'abcd123', :branch => 'other'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[git/a abcd123], @git_set.repositories['a']
+ end
+
+ def test_gem_git_submodules
+ @gda.gem 'a', :git => 'git/a', :submodules => true
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[git/a master], @git_set.repositories['a']
+ assert_equal %w[git/a], @git_set.need_submodules.keys
+ end
+
+ def test_gem_git_tag
+ @gda.gem 'a', :git => 'git/a', :tag => 'v1'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[git/a v1], @git_set.repositories['a']
+ end
+
+ def test_gem_github
+ @gda.gem 'a', :github => 'example/repository'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[git://github.com/example/repository.git master],
+ @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_github_expand_path
+ @gda.gem 'a', :github => 'example'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[git://github.com/example/example.git master],
+ @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_group
+ @gda.gem 'a', :group => :test
+
+ assert_equal [dep('a')], @set.dependencies
+ end
+
+ def test_gem_group_without
+ @gda.without_groups << :test
+
+ @gda.gem 'a', :group => :test
+
+ assert_empty @set.dependencies
+
+ expected = { 'a' => nil }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_groups
+ @gda.gem 'a', :groups => [:test, :development]
+
+ assert_equal [dep('a')], @set.dependencies
+ end
+
+ def test_gem_path
+ name, version, directory = vendor_gem
+
+ @gda.gem name, :path => directory
+
+ assert_equal [dep(name)], @set.dependencies
+
+ loaded = @vendor_set.load_spec(name, version, Gem::Platform::RUBY, nil)
+
+ assert_equal "#{name}-#{version}", loaded.full_name
+
+ expected = { name => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_platforms
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ with_engine_version 'ruby', '2.0.0' do
+ @gda.gem 'a', :platforms => :ruby
+
+ refute_empty @set.dependencies
+ end
+ ensure
+ Gem.win_platform = win_platform
+ end
+
+ def test_gem_platforms_bundler_ruby
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ with_engine_version 'ruby', '2.0.0' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ refute_empty set.dependencies
+ end
+
+ with_engine_version 'rbx', '2.0.0' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ refute_empty set.dependencies
+ end
+
+ with_engine_version 'jruby', '1.7.6' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ assert_empty set.dependencies
+ end
+
+ Gem.win_platform = true
+
+ with_engine_version 'ruby', '2.0.0' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ assert_empty set.dependencies
+ end
+
+ Gem.win_platform = win_platform
+ end
+
+ def test_gem_platforms_engine
+ with_engine_version 'jruby', '1.7.6' do
+ @gda.gem 'a', :platforms => :mri
+
+ assert_empty @set.dependencies
+ end
+ end
+
+ def test_gem_platforms_maglev
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ with_engine_version 'maglev', '1.0.0' do
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :ruby
+
+ refute_empty set.dependencies
+
+ set = Gem::RequestSet.new
+ gda = @GDA.new set, 'gem.deps.rb'
+ gda.gem 'a', :platforms => :maglev
+
+ refute_empty set.dependencies
+ end
+ ensure
+ Gem.win_platform = win_platform
+ end
+
+ def test_gem_platforms_multiple
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ with_engine_version 'ruby', '2.0.0' do
+ @gda.gem 'a', :platforms => [:mswin, :jruby]
+
+ assert_empty @set.dependencies
+ end
+
+ ensure
+ Gem.win_platform = win_platform
+ end
+
+ def test_gem_platforms_platform
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ with_engine_version 'ruby', '2.0.0' do
+ @gda.gem 'a', :platforms => :jruby, :platform => :ruby
+
+ refute_empty @set.dependencies
+ end
+ ensure
+ Gem.win_platform = win_platform
+ end
+
+ def test_gem_platforms_version
+ with_engine_version 'ruby', '2.0.0' do
+ @gda.gem 'a', :platforms => :ruby_18
+
+ assert_empty @set.dependencies
+ end
+ end
+
+ def test_gem_platforms_unknown
+ e = assert_raises ArgumentError do
+ @gda.gem 'a', :platforms => :unknown
+ end
+
+ assert_equal 'unknown platform :unknown', e.message
+ end
+
+ def test_gem_requires
+ @gda.gem 'a', :require => %w[b c]
+ @gda.gem 'd', :require => 'e'
+
+ assert_equal [dep('a'), dep('d')], @set.dependencies
+
+ assert_equal %w[b c], @gda.requires['a']
+ assert_equal %w[e], @gda.requires['d']
+ end
+
+ def test_gem_requires_false
+ @gda.gem 'a', :require => false
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_empty @gda.requires
+ end
+
+ def test_gem_requires_without_group
+ @gda.without_groups << :test
+
+ @gda.gem 'a', :group => :test
+
+ assert_empty @set.dependencies
+
+ assert_empty @gda.requires['a']
+ end
+
+ def test_gem_requirement
+ @gda.gem 'a', '~> 1.0'
+
+ assert_equal [dep('a', '~> 1.0')], @set.dependencies
+
+ expected = { 'a' => ['~> 1.0'] }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_requirements
+ @gda.gem 'b', '~> 1.0', '>= 1.0.2'
+
+ assert_equal [dep('b', '~> 1.0', '>= 1.0.2')], @set.dependencies
+
+ expected = { 'b' => ['~> 1.0', '>= 1.0.2'] }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_requirements_options
+ @gda.gem 'c', :git => 'https://example/c.git'
+
+ assert_equal [dep('c')], @set.dependencies
+ end
+
+ def test_gem_source_mismatch
+ name, _, directory = vendor_gem
+
+ gda = @GDA.new @set, nil
+ gda.gem name
+
+ e = assert_raises ArgumentError do
+ gda.gem name, :path => directory
+ end
+
+ assert_equal "duplicate source path: #{directory} for gem #{name}",
+ e.message
+
+ gda = @GDA.new @set, nil
+ gda.instance_variable_set :@vendor_set, @vendor_set
+ gda.gem name, :path => directory
+
+ e = assert_raises ArgumentError do
+ gda.gem name
+ end
+
+ assert_equal "duplicate source (default) for gem #{name}",
+ e.message
+ end
+
+ def test_gem_deps_file
+ assert_equal 'gem.deps.rb', @gda.gem_deps_file
+
+ gda = @GDA.new @set, 'foo/Gemfile'
+
+ assert_equal 'Gemfile', gda.gem_deps_file
+ end
+
+ def test_gem_group_method
+ groups = []
+
+ @gda.group :a do
+ groups = @gda.send :gem_group, 'a', :group => :b, :groups => [:c, :d]
+ end
+
+ assert_equal [:a, :b, :c, :d], groups.sort_by { |group| group.to_s }
+ end
+
+ def test_gemspec
+ spec = util_spec 'a', 1, 'b' => 2
+ spec.add_development_dependency 'c', 3
+
+ open 'a.gemspec', 'w' do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ @gda.gemspec
+
+ assert_equal [dep('a', '= 1'), dep('b', '= 2'), dep('c', '=3')],
+ @set.dependencies
+
+ assert_equal %w[a], @gda.requires['a']
+
+ expected = {
+ 'a' => '!',
+ 'b' => req('= 2'),
+ 'c' => req('= 3'),
+ }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gemspec_bad
+ FileUtils.touch 'a.gemspec'
+
+ e = assert_raises ArgumentError do
+ capture_io do
+ @gda.gemspec
+ end
+ end
+
+ assert_equal 'invalid gemspec ./a.gemspec', e.message
+ end
+
+ def test_gemspec_development_group
+ spec = util_spec 'a', 1, 'b' => 2
+ spec.add_development_dependency 'c', 3
+
+ open 'a.gemspec', 'w' do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ @gda.without_groups << :other
+
+ @gda.gemspec :development_group => :other
+
+ assert_equal [dep('a', '= 1'), dep('b', '= 2')], @set.dependencies
+
+ assert_equal %w[a], @gda.requires['a']
+ end
+
+ def test_gemspec_multiple
+ open 'a.gemspec', 'w' do |io|
+ spec = util_spec 'a', 1, 'b' => 2
+ io.write spec.to_ruby_for_cache
+ end
+
+ open 'b.gemspec', 'w' do |io|
+ spec = util_spec 'b', 2, 'c' => 3
+ io.write spec.to_ruby_for_cache
+ end
+
+ e = assert_raises ArgumentError do
+ @gda.gemspec
+ end
+
+ assert_equal "found multiple gemspecs at #{@tempdir}, use the name: option to specify the one you want", e.message
+ end
+
+ def test_gemspec_name
+ open 'a.gemspec', 'w' do |io|
+ spec = util_spec 'a', 1, 'b' => 2
+ io.write spec.to_ruby_for_cache
+ end
+
+ open 'b.gemspec', 'w' do |io|
+ spec = util_spec 'b', 2, 'c' => 3
+ io.write spec.to_ruby_for_cache
+ end
+
+ @gda.gemspec :name => 'b'
+
+ assert_equal [dep('b', '= 2'), dep('c', '= 3')], @set.dependencies
+ end
+
+ def test_gemspec_named
+ spec = util_spec 'a', 1, 'b' => 2
+
+ open 'other.gemspec', 'w' do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ @gda.gemspec
+
+ assert_equal [dep('a', '= 1'), dep('b', '= 2')], @set.dependencies
+ end
+
+ def test_gemspec_none
+ e = assert_raises ArgumentError do
+ @gda.gemspec
+ end
+
+ assert_equal "no gemspecs found at #{@tempdir}", e.message
+ end
+
+ def test_gemspec_path
+ spec = util_spec 'a', 1, 'b' => 2
+
+ FileUtils.mkdir 'other'
+
+ open 'other/a.gemspec', 'w' do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ @gda.gemspec :path => 'other'
+
+ assert_equal [dep('a', '= 1'), dep('b', '= 2')], @set.dependencies
+ end
+
+ def test_git
+ @gda.git 'git://example/repo.git' do
+ @gda.gem 'a'
+ @gda.gem 'b'
+ end
+
+ assert_equal [dep('a'), dep('b')], @set.dependencies
+
+ assert_equal %w[git://example/repo.git master], @git_set.repositories['a']
+ assert_equal %w[git://example/repo.git master], @git_set.repositories['b']
+ end
+
+ def test_git_source
+ @gda.git_source :example do |repo_name|
+ "git://example/#{repo_name}.git"
+ end
+
+ @gda.gem 'a', :example => 'repo'
+
+ assert_equal %w[git://example/repo.git master], @git_set.repositories['a']
+ end
+
+ def test_group
+ @gda.group :test do
+ @gda.gem 'a'
+ end
+
+ assert_equal [dep('a')], @set.dependencies
+ end
+
+ def test_load
+ tf = Tempfile.open 'gem.deps.rb' do |io|
+ io.write <<-GEM_DEPS
+gem 'a'
+
+group :test do
+ gem 'b'
+end
+ GEM_DEPS
+ io.flush
+
+ gda = @GDA.new @set, io.path
+
+ assert_equal gda, gda.load
+
+ assert_equal [dep('a'), dep('b')], @set.dependencies
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+ end
+
+ def test_name_typo
+ assert_same @GDA, Gem::RequestSet::GemDepedencyAPI
+ end
+
+ def test_pin_gem_source
+ gda = @GDA.new @set, nil
+
+ gda.send :pin_gem_source, 'a'
+ gda.send :pin_gem_source, 'a'
+
+ e = assert_raises ArgumentError do
+ gda.send :pin_gem_source, 'a', :path, 'vendor/a'
+ end
+
+ assert_equal "duplicate source path: vendor/a for gem a",
+ e.message
+
+ e = assert_raises ArgumentError do
+ gda.send :pin_gem_source, 'a', :git, 'git://example/repo.git'
+ end
+
+ assert_equal "duplicate source git: git://example/repo.git for gem a",
+ e.message
+ end
+
+ def test_platform_mswin
+ util_set_arch 'i686-darwin8.10.1' do
+ @gda.platform :mswin do
+ @gda.gem 'a'
+ end
+
+ assert_empty @set.dependencies
+ end
+
+ util_set_arch 'x86-mswin32-60' do
+ @gda.platform :mswin do
+ @gda.gem 'a'
+ end
+
+ refute_empty @set.dependencies
+ end
+ end
+
+ def test_platform_multiple
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ gda = @GDA.new @set, nil
+
+ with_engine_version 'ruby', '1.8.7' do
+ gda.platform :mri_19, :mri_20 do
+ gda.gem 'a'
+ end
+ end
+
+ assert_empty @set.dependencies
+
+ gda = @GDA.new @set, nil
+
+ with_engine_version 'ruby', '2.0.0' do
+ gda.platform :mri_19, :mri_20 do
+ gda.gem 'a'
+ end
+ end
+
+ refute_empty @set.dependencies
+ ensure
+ Gem.win_platform = win_platform
+ end
+
+ def test_platform_ruby
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ @gda.platform :ruby do
+ @gda.gem 'a'
+ end
+
+ assert_equal [dep('a')], @set.dependencies
+ ensure
+ Gem.win_platform = win_platform
+ end
+
+ def test_platforms
+ util_set_arch 'i686-darwin8.10.1' do
+ @gda.platforms :ruby do
+ @gda.gem 'a'
+ end
+
+ assert_equal [dep('a')], @set.dependencies
+
+ @gda.platforms :mswin do
+ @gda.gem 'b'
+ end
+
+ assert_equal [dep('a')], @set.dependencies
+ end
+
+ util_set_arch 'x86-mswin32-60' do
+ @gda.platforms :mswin do
+ @gda.gem 'c'
+ end
+
+ assert_equal [dep('a'), dep('c')], @set.dependencies
+ end
+ end
+
+ def test_ruby
+ assert @gda.ruby RUBY_VERSION
+ end
+
+ def test_ruby_engine
+ with_engine_version 'jruby', '1.7.6' do
+ assert @gda.ruby RUBY_VERSION,
+ :engine => 'jruby', :engine_version => '1.7.6'
+
+ end
+ end
+
+ def test_ruby_engine_mismatch_engine
+ with_engine_version 'ruby', '2.0.0' do
+ e = assert_raises Gem::RubyVersionMismatch do
+ @gda.ruby RUBY_VERSION, :engine => 'jruby', :engine_version => '1.7.4'
+ end
+
+ assert_equal 'Your ruby engine is ruby, but your gem.deps.rb requires jruby',
+ e.message
+ end
+ end
+
+ def test_ruby_engine_mismatch_version
+ with_engine_version 'jruby', '1.7.6' do
+ e = assert_raises Gem::RubyVersionMismatch do
+ @gda.ruby RUBY_VERSION, :engine => 'jruby', :engine_version => '1.7.4'
+ end
+
+ assert_equal 'Your ruby engine version is jruby 1.7.6, but your gem.deps.rb requires jruby 1.7.4',
+ e.message
+ end
+ end
+
+ def test_ruby_engine_no_engine_version
+ e = assert_raises ArgumentError do
+ @gda.ruby RUBY_VERSION, :engine => 'jruby'
+ end
+
+ assert_equal 'you must specify engine_version along with the ruby engine',
+ e.message
+ end
+
+ def test_ruby_mismatch
+ e = assert_raises Gem::RubyVersionMismatch do
+ @gda.ruby '1.8.0'
+ end
+
+ assert_equal "Your Ruby version is #{RUBY_VERSION}, but your gem.deps.rb requires 1.8.0", e.message
+ end
+
+ def test_ruby_mismatch_installing
+ @gda.installing = true
+
+ assert @gda.ruby '1.8.0'
+ end
+
+ def test_source
+ sources = Gem.sources
+
+ @gda.source 'http://first.example'
+
+ assert_equal %w[http://first.example], Gem.sources
+
+ assert_same sources, Gem.sources
+
+ @gda.source 'http://second.example'
+
+ assert_equal %w[http://first.example http://second.example], Gem.sources
+ end
+
+ def test_with_engine_version
+ version = RUBY_VERSION
+ engine = Gem.ruby_engine
+
+ engine_version_const = "#{Gem.ruby_engine.upcase}_VERSION"
+ engine_version = Object.const_get engine_version_const
+
+ with_engine_version 'other', '1.2.3' do
+ assert_equal 'other', Gem.ruby_engine
+ assert_equal '1.2.3', OTHER_VERSION
+
+ assert_equal version, RUBY_VERSION if engine
+ end
+
+ assert_equal version, RUBY_VERSION
+ assert_equal engine, Gem.ruby_engine
+
+ assert_equal engine_version, Object.const_get(engine_version_const) if
+ engine
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_request_set_lockfile.rb b/jni/ruby/test/rubygems/test_gem_request_set_lockfile.rb
new file mode 100644
index 0000000..7c5cd5a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_request_set_lockfile.rb
@@ -0,0 +1,1276 @@
+require 'rubygems/test_case'
+require 'rubygems/request_set'
+require 'rubygems/request_set/lockfile'
+
+class TestGemRequestSetLockfile < Gem::TestCase
+
+ def setup
+ super
+
+ Gem::RemoteFetcher.fetcher = @fetcher = Gem::FakeFetcher.new
+
+ util_set_arch 'i686-darwin8.10.1'
+
+ @set = Gem::RequestSet.new
+
+ @git_set = Gem::Resolver::GitSet.new
+ @vendor_set = Gem::Resolver::VendorSet.new
+
+ @set.instance_variable_set :@git_set, @git_set
+ @set.instance_variable_set :@vendor_set, @vendor_set
+
+ @gem_deps_file = 'gem.deps.rb'
+
+ @lockfile = Gem::RequestSet::Lockfile.new @set, @gem_deps_file
+ end
+
+ def write_gem_deps gem_deps
+ open @gem_deps_file, 'w' do |io|
+ io.write gem_deps
+ end
+ end
+
+ def write_lockfile lockfile
+ @lock_file = File.expand_path "#{@gem_deps_file}.lock"
+
+ open @lock_file, 'w' do |io|
+ io.write lockfile
+ end
+ end
+
+ def test_add_DEPENDENCIES
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.add_development_dependency 'b'
+ end
+ end
+
+ @set.gem 'a'
+ @set.resolve
+ @lockfile.instance_variable_set :@requests, @set.sorted_requests
+
+ out = []
+
+ @lockfile.add_DEPENDENCIES out
+
+ expected = [
+ 'DEPENDENCIES',
+ ' a',
+ nil
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_add_DEPENDENCIES_from_gem_deps
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.add_development_dependency 'b'
+ end
+ end
+
+ dependencies = { 'a' => '~> 2.0' }
+
+ @set.gem 'a'
+ @set.resolve
+ @lockfile =
+ Gem::RequestSet::Lockfile.new @set, @gem_deps_file, dependencies
+ @lockfile.instance_variable_set :@requests, @set.sorted_requests
+
+ out = []
+
+ @lockfile.add_DEPENDENCIES out
+
+ expected = [
+ 'DEPENDENCIES',
+ ' a (~> 2.0)',
+ nil
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_add_GEM
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.add_dependency 'b'
+ s.add_development_dependency 'c'
+ end
+
+ fetcher.spec 'b', 2
+
+ fetcher.spec 'bundler', 1
+ end
+
+ @set.gem 'a'
+ @set.gem 'bundler'
+ @set.resolve
+ @lockfile.instance_variable_set :@requests, @set.sorted_requests
+
+ spec_groups = @set.sorted_requests.group_by do |request|
+ request.spec.class
+ end
+ @lockfile.instance_variable_set :@spec_groups, spec_groups
+
+
+ out = []
+
+ @lockfile.add_GEM out
+
+ expected = [
+ 'GEM',
+ ' remote: http://gems.example.com/',
+ ' specs:',
+ ' a (2)',
+ ' b',
+ ' b (2)',
+ nil
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_add_PLATFORMS
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.add_dependency 'b'
+ end
+
+ fetcher.spec 'b', 2 do |s|
+ s.platform = Gem::Platform::CURRENT
+ end
+ end
+
+ @set.gem 'a'
+ @set.resolve
+ @lockfile.instance_variable_set :@requests, @set.sorted_requests
+
+ out = []
+
+ @lockfile.add_PLATFORMS out
+
+ expected = [
+ 'PLATFORMS',
+ ' ruby',
+ ' x86-darwin-8',
+ nil
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_get
+ @lockfile.instance_variable_set :@tokens, [:token]
+
+ assert_equal :token, @lockfile.get
+ end
+
+ def test_get_type_mismatch
+ @lockfile.instance_variable_set :@tokens, [[:section, 'x', 5, 1]]
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.get :text
+ end
+
+ expected =
+ 'unexpected token [:section, "x"], expected :text (at line 1 column 5)'
+
+ assert_equal expected, e.message
+
+ assert_equal 1, e.line
+ assert_equal 5, e.column
+ assert_equal File.expand_path("#{@gem_deps_file}.lock"), e.path
+ end
+
+ def test_get_type_multiple
+ @lockfile.instance_variable_set :@tokens, [[:section, 'x', 5, 1]]
+
+ assert @lockfile.get [:text, :section]
+ end
+
+ def test_get_type_value_mismatch
+ @lockfile.instance_variable_set :@tokens, [[:section, 'x', 5, 1]]
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.get :section, 'y'
+ end
+
+ expected =
+ 'unexpected token [:section, "x"], expected [:section, "y"] (at line 1 column 5)'
+
+ assert_equal expected, e.message
+
+ assert_equal 1, e.line
+ assert_equal 5, e.column
+ assert_equal File.expand_path("#{@gem_deps_file}.lock"), e.path
+ end
+
+ def test_parse
+ write_lockfile <<-LOCKFILE.strip
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal [Gem::Platform::RUBY], @lockfile.platforms
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ assert lockfile_set, 'could not find a LockSet'
+
+ assert_equal %w[a-2], lockfile_set.specs.map { |tuple| tuple.full_name }
+ end
+
+ def test_parse_dependencies
+ write_lockfile <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a (>= 1, <= 2)
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '>= 1', '<= 2')], @set.dependencies
+
+ assert_equal [Gem::Platform::RUBY], @lockfile.platforms
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ assert lockfile_set, 'could not find a LockSet'
+
+ assert_equal %w[a-2], lockfile_set.specs.map { |tuple| tuple.full_name }
+ end
+
+ def test_parse_DEPENDENCIES_git
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://git.example/josevalim/rails-footnotes.git
+ revision: 3a6ac1971e91d822f057650cc5916ebfcbd6ee37
+ specs:
+ rails-footnotes (3.7.9)
+ rails (>= 3.0.0)
+
+GIT
+ remote: git://git.example/svenfuchs/i18n-active_record.git
+ revision: 55507cf59f8f2173d38e07e18df0e90d25b1f0f6
+ specs:
+ i18n-active_record (0.0.2)
+ i18n (>= 0.5.0)
+
+GEM
+ remote: http://gems.example/
+ specs:
+ i18n (0.6.9)
+ rails (4.0.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ i18n-active_record!
+ rails-footnotes!
+ LOCKFILE
+
+ @lockfile.parse
+
+ expected = [
+ dep('i18n-active_record', '= 0.0.2'),
+ dep('rails-footnotes', '= 3.7.9'),
+ ]
+
+ assert_equal expected, @set.dependencies
+ end
+
+ def test_parse_DEPENDENCIES_git_version
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://github.com/progrium/ruby-jwt.git
+ revision: 8d74770c6cd92ea234b428b5d0c1f18306a4f41c
+ specs:
+ jwt (1.1)
+
+GEM
+ remote: http://gems.example/
+ specs:
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ jwt (= 1.1)!
+ LOCKFILE
+
+ @lockfile.parse
+
+ expected = [
+ dep('jwt', '= 1.1'),
+ ]
+
+ assert_equal expected, @set.dependencies
+ end
+
+ def test_parse_GEM
+ write_lockfile <<-LOCKFILE
+GEM
+ specs:
+ a (2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '>= 0')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ assert lockfile_set, 'found a LockSet'
+
+ assert_equal %w[a-2], lockfile_set.specs.map { |s| s.full_name }
+ end
+
+ def test_parse_GEM_remote_multiple
+ write_lockfile <<-LOCKFILE
+GEM
+ remote: https://gems.example/
+ remote: https://other.example/
+ specs:
+ a (2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '>= 0')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ assert lockfile_set, 'found a LockSet'
+
+ assert_equal %w[a-2 a-2], lockfile_set.specs.map { |s| s.full_name }
+
+ assert_equal %w[https://gems.example/ https://other.example/],
+ lockfile_set.specs.map { |s| s.source.uri.to_s }
+ end
+
+ def test_parse_GIT
+ @set.instance_variable_set :@install_dir, 'install_dir'
+
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://example/a.git
+ revision: master
+ specs:
+ a (2)
+ b (>= 3)
+ c
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '= 2')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set, 'fount a LockSet'
+
+ git_set = @set.sets.find do |set|
+ Gem::Resolver::GitSet === set
+ end
+
+ assert git_set, 'could not find a GitSet'
+
+ assert_equal %w[a-2], git_set.specs.values.map { |s| s.full_name }
+
+ assert_equal [dep('b', '>= 3'), dep('c')],
+ git_set.specs.values.first.dependencies
+
+ expected = {
+ 'a' => %w[git://example/a.git master],
+ }
+
+ assert_equal expected, git_set.repositories
+ assert_equal 'install_dir', git_set.root_dir
+ end
+
+ def test_parse_GIT_branch
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://example/a.git
+ revision: 1234abc
+ branch: 0-9-12-stable
+ specs:
+ a (2)
+ b (>= 3)
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '= 2')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set, 'fount a LockSet'
+
+ git_set = @set.sets.find do |set|
+ Gem::Resolver::GitSet === set
+ end
+
+ assert git_set, 'could not find a GitSet'
+
+ expected = {
+ 'a' => %w[git://example/a.git 1234abc],
+ }
+
+ assert_equal expected, git_set.repositories
+ end
+
+ def test_parse_GIT_ref
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://example/a.git
+ revision: 1234abc
+ ref: 1234abc
+ specs:
+ a (2)
+ b (>= 3)
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '= 2')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set, 'fount a LockSet'
+
+ git_set = @set.sets.find do |set|
+ Gem::Resolver::GitSet === set
+ end
+
+ assert git_set, 'could not find a GitSet'
+
+ expected = {
+ 'a' => %w[git://example/a.git 1234abc],
+ }
+
+ assert_equal expected, git_set.repositories
+ end
+
+ def test_parse_GIT_tag
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://example/a.git
+ revision: 1234abc
+ tag: v0.9.12
+ specs:
+ a (2)
+ b (>= 3)
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '= 2')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set, 'fount a LockSet'
+
+ git_set = @set.sets.find do |set|
+ Gem::Resolver::GitSet === set
+ end
+
+ assert git_set, 'could not find a GitSet'
+
+ expected = {
+ 'a' => %w[git://example/a.git 1234abc],
+ }
+
+ assert_equal expected, git_set.repositories
+ end
+
+ def test_parse_PATH
+ _, _, directory = vendor_gem
+
+ write_lockfile <<-LOCKFILE
+PATH
+ remote: #{directory}
+ specs:
+ a (1)
+ b (2)
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '= 1')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set, 'found a LockSet'
+
+ vendor_set = @set.sets.find do |set|
+ Gem::Resolver::VendorSet === set
+ end
+
+ assert vendor_set, 'could not find a VendorSet'
+
+ assert_equal %w[a-1], vendor_set.specs.values.map { |s| s.full_name }
+
+ spec = vendor_set.load_spec 'a', nil, nil, nil
+
+ assert_equal [dep('b', '= 2')], spec.dependencies
+ end
+
+ def test_parse_dependency
+ write_lockfile ' 1)'
+
+ @lockfile.tokenize
+
+ parsed = @lockfile.parse_dependency 'a', '='
+
+ assert_equal dep('a', '= 1'), parsed
+
+ write_lockfile ')'
+
+ @lockfile.tokenize
+
+ parsed = @lockfile.parse_dependency 'a', '2'
+
+ assert_equal dep('a', '= 2'), parsed
+ end
+
+ def test_parse_gem_specs_dependency
+ write_lockfile <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b (= 3)
+ c (~> 4)
+ d
+ e (~> 5.0, >= 5.0.1)
+ b (3-x86_64-linux)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal [Gem::Platform::RUBY], @lockfile.platforms
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ assert lockfile_set, 'could not find a LockSet'
+
+ assert_equal %w[a-2 b-3], lockfile_set.specs.map { |tuple| tuple.full_name }
+
+ expected = [
+ Gem::Platform::RUBY,
+ Gem::Platform.new('x86_64-linux'),
+ ]
+
+ assert_equal expected, lockfile_set.specs.map { |tuple| tuple.platform }
+
+ spec = lockfile_set.specs.first
+
+ expected = [
+ dep('b', '= 3'),
+ dep('c', '~> 4'),
+ dep('d'),
+ dep('e', '~> 5.0', '>= 5.0.1'),
+ ]
+
+ assert_equal expected, spec.dependencies
+ end
+
+ def test_parse_missing
+ @lockfile.parse
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set
+ end
+
+ def test_peek
+ @lockfile.instance_variable_set :@tokens, [:token]
+
+ assert_equal :token, @lockfile.peek
+
+ assert_equal :token, @lockfile.get
+
+ assert_equal [:EOF], @lockfile.peek
+ end
+
+ def test_relative_path_from
+ path = @lockfile.relative_path_from '/foo', '/foo/bar'
+
+ assert_equal File.expand_path('/foo'), path
+
+ path = @lockfile.relative_path_from '/foo', '/foo'
+
+ assert_equal '.', path
+ end
+
+ def test_skip
+ tokens = [[:token]]
+
+ @lockfile.instance_variable_set :@tokens, tokens
+
+ @lockfile.skip :token
+
+ assert_empty tokens
+ end
+
+ def test_token_pos
+ assert_equal [5, 0], @lockfile.token_pos(5)
+
+ @lockfile.instance_variable_set :@line_pos, 2
+ @lockfile.instance_variable_set :@line, 1
+
+ assert_equal [3, 1], @lockfile.token_pos(5)
+ end
+
+ def test_tokenize
+ write_lockfile <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b (= 2)
+ c (!= 3)
+ d (> 4)
+ e (< 5)
+ f (>= 6)
+ g (<= 7)
+ h (~> 8)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ expected = [
+ [:section, 'GEM', 0, 0],
+ [:newline, nil, 3, 0],
+
+ [:entry, 'remote', 2, 1],
+ [:text, @gem_repo, 10, 1],
+ [:newline, nil, 34, 1],
+
+ [:entry, 'specs', 2, 2],
+ [:newline, nil, 8, 2],
+
+ [:text, 'a', 4, 3],
+ [:l_paren, nil, 6, 3],
+ [:text, '2', 7, 3],
+ [:r_paren, nil, 8, 3],
+ [:newline, nil, 9, 3],
+
+ [:text, 'b', 6, 4],
+ [:l_paren, nil, 8, 4],
+ [:requirement, '=', 9, 4],
+ [:text, '2', 11, 4],
+ [:r_paren, nil, 12, 4],
+ [:newline, nil, 13, 4],
+
+ [:text, 'c', 6, 5],
+ [:l_paren, nil, 8, 5],
+ [:requirement, '!=', 9, 5],
+ [:text, '3', 12, 5],
+ [:r_paren, nil, 13, 5],
+ [:newline, nil, 14, 5],
+
+ [:text, 'd', 6, 6],
+ [:l_paren, nil, 8, 6],
+ [:requirement, '>', 9, 6],
+ [:text, '4', 11, 6],
+ [:r_paren, nil, 12, 6],
+ [:newline, nil, 13, 6],
+
+ [:text, 'e', 6, 7],
+ [:l_paren, nil, 8, 7],
+ [:requirement, '<', 9, 7],
+ [:text, '5', 11, 7],
+ [:r_paren, nil, 12, 7],
+ [:newline, nil, 13, 7],
+
+ [:text, 'f', 6, 8],
+ [:l_paren, nil, 8, 8],
+ [:requirement, '>=', 9, 8],
+ [:text, '6', 12, 8],
+ [:r_paren, nil, 13, 8],
+ [:newline, nil, 14, 8],
+
+ [:text, 'g', 6, 9],
+ [:l_paren, nil, 8, 9],
+ [:requirement, '<=', 9, 9],
+ [:text, '7', 12, 9],
+ [:r_paren, nil, 13, 9],
+ [:newline, nil, 14, 9],
+
+ [:text, 'h', 6, 10],
+ [:l_paren, nil, 8, 10],
+ [:requirement, '~>', 9, 10],
+ [:text, '8', 12, 10],
+ [:r_paren, nil, 13, 10],
+ [:newline, nil, 14, 10],
+
+ [:newline, nil, 0, 11],
+
+ [:section, 'PLATFORMS', 0, 12],
+ [:newline, nil, 9, 12],
+
+ [:text, Gem::Platform::RUBY, 2, 13],
+ [:newline, nil, 6, 13],
+
+ [:newline, nil, 0, 14],
+
+ [:section, 'DEPENDENCIES', 0, 15],
+ [:newline, nil, 12, 15],
+
+ [:text, 'a', 2, 16],
+ [:newline, nil, 3, 16],
+ ]
+
+ assert_equal expected, @lockfile.tokenize
+ end
+
+ def test_tokenize_capitals
+ write_lockfile <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ Ab (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ Ab
+ LOCKFILE
+
+ expected = [
+ [:section, 'GEM', 0, 0],
+ [:newline, nil, 3, 0],
+ [:entry, 'remote', 2, 1],
+ [:text, @gem_repo, 10, 1],
+ [:newline, nil, 34, 1],
+ [:entry, 'specs', 2, 2],
+ [:newline, nil, 8, 2],
+ [:text, 'Ab', 4, 3],
+ [:l_paren, nil, 7, 3],
+ [:text, '2', 8, 3],
+ [:r_paren, nil, 9, 3],
+ [:newline, nil, 10, 3],
+ [:newline, nil, 0, 4],
+ [:section, 'PLATFORMS', 0, 5],
+ [:newline, nil, 9, 5],
+ [:text, Gem::Platform::RUBY, 2, 6],
+ [:newline, nil, 6, 6],
+ [:newline, nil, 0, 7],
+ [:section, 'DEPENDENCIES', 0, 8],
+ [:newline, nil, 12, 8],
+ [:text, 'Ab', 2, 9],
+ [:newline, nil, 4, 9],
+ ]
+
+ assert_equal expected, @lockfile.tokenize
+ end
+
+ def test_tokenize_conflict_markers
+ write_lockfile '<<<<<<<'
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.tokenize
+ end
+
+ assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)",
+ e.message
+
+ write_lockfile '|||||||'
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.tokenize
+ end
+
+ assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)",
+ e.message
+
+ write_lockfile '======='
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.tokenize
+ end
+
+ assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)",
+ e.message
+
+ write_lockfile '>>>>>>>'
+
+ e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ @lockfile.tokenize
+ end
+
+ assert_equal "your #{@lock_file} contains merge conflict markers (at line 0 column 0)",
+ e.message
+ end
+
+ def test_tokenize_git
+ write_lockfile <<-LOCKFILE
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ expected = [
+ [:section, 'DEPENDENCIES', 0, 0],
+ [:newline, nil, 12, 0],
+
+ [:text, 'a', 2, 1],
+ [:bang, nil, 3, 1],
+ [:newline, nil, 4, 1],
+ ]
+
+ assert_equal expected, @lockfile.tokenize
+ end
+
+ def test_tokenize_missing
+ tokens = @lockfile.tokenize
+
+ assert_empty tokens
+ end
+
+ def test_tokenize_multiple
+ write_lockfile <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b (~> 3.0, >= 3.0.1)
+ LOCKFILE
+
+ expected = [
+ [:section, 'GEM', 0, 0],
+ [:newline, nil, 3, 0],
+
+ [:entry, 'remote', 2, 1],
+ [:text, @gem_repo, 10, 1],
+ [:newline, nil, 34, 1],
+
+ [:entry, 'specs', 2, 2],
+ [:newline, nil, 8, 2],
+
+ [:text, 'a', 4, 3],
+ [:l_paren, nil, 6, 3],
+ [:text, '2', 7, 3],
+ [:r_paren, nil, 8, 3],
+ [:newline, nil, 9, 3],
+
+ [:text, 'b', 6, 4],
+ [:l_paren, nil, 8, 4],
+ [:requirement, '~>', 9, 4],
+ [:text, '3.0', 12, 4],
+ [:comma, nil, 15, 4],
+ [:requirement, '>=', 17, 4],
+ [:text, '3.0.1', 20, 4],
+ [:r_paren, nil, 25, 4],
+ [:newline, nil, 26, 4],
+ ]
+
+ assert_equal expected, @lockfile.tokenize
+ end
+
+ def test_to_s_gem
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ end
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_dependency
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2, 'c' => '>= 0', 'b' => '>= 0'
+ fetcher.spec 'b', 2
+ fetcher.spec 'c', 2
+ end
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b
+ c
+ b (2)
+ c (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ b
+ c
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_dependency_non_default
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2, 'b' => '>= 1'
+ fetcher.spec 'b', 2
+ end
+
+ @set.gem 'b'
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b (>= 1)
+ b (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ b
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_dependency_requirement
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2, 'b' => '>= 0'
+ fetcher.spec 'b', 2
+ end
+
+ @set.gem 'a', '>= 1'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+ b
+ b (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a (>= 1)
+ b
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_path
+ name, version, directory = vendor_gem
+
+ @vendor_set.add_vendor_gem name, directory
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+PATH
+ remote: #{directory}
+ specs:
+ #{name} (#{version})
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_path_absolute
+ name, version, directory = vendor_gem
+
+ @vendor_set.add_vendor_gem name, File.expand_path(directory)
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+PATH
+ remote: #{directory}
+ specs:
+ #{name} (#{version})
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_platform
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |spec|
+ spec.platform = Gem::Platform.local
+ end
+ end
+
+ @set.gem 'a'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2-#{Gem::Platform.local})
+
+PLATFORMS
+ #{Gem::Platform.local}
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_gem_source
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ fetcher.clear
+ end
+
+ spec_fetcher 'http://other.example/' do |fetcher|
+ fetcher.spec 'b', 2
+ fetcher.clear
+ end
+
+ Gem.sources << 'http://other.example/'
+
+ @set.gem 'a'
+ @set.gem 'b'
+
+ expected = <<-LOCKFILE
+GEM
+ remote: #{@gem_repo}
+ specs:
+ a (2)
+
+GEM
+ remote: http://other.example/
+ specs:
+ b (2)
+
+PLATFORMS
+ #{Gem::Platform::RUBY}
+
+DEPENDENCIES
+ a
+ b
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_to_s_git
+ _, _, repository, = git_gem
+
+ head = nil
+
+ Dir.chdir repository do
+ FileUtils.mkdir 'b'
+
+ Dir.chdir 'b' do
+ b = Gem::Specification.new 'b', 1 do |s|
+ s.add_dependency 'a', '~> 1.0'
+ s.add_dependency 'c', '~> 1.0'
+ end
+
+ open 'b.gemspec', 'w' do |io|
+ io.write b.to_ruby
+ end
+
+ system @git, 'add', 'b.gemspec'
+ system @git, 'commit', '--quiet', '-m', 'add b/b.gemspec'
+ end
+
+ FileUtils.mkdir 'c'
+
+ Dir.chdir 'c' do
+ c = Gem::Specification.new 'c', 1
+
+ open 'c.gemspec', 'w' do |io|
+ io.write c.to_ruby
+ end
+
+ system @git, 'add', 'c.gemspec'
+ system @git, 'commit', '--quiet', '-m', 'add c/c.gemspec'
+ end
+
+ head = `#{@git} rev-parse HEAD`.strip
+ end
+
+ @git_set.add_git_gem 'a', repository, 'HEAD', true
+ @git_set.add_git_gem 'b', repository, 'HEAD', true
+ @git_set.add_git_gem 'c', repository, 'HEAD', true
+
+ @set.gem 'b'
+
+ expected = <<-LOCKFILE
+GIT
+ remote: #{repository}
+ revision: #{head}
+ specs:
+ a (1)
+ b (1)
+ a (~> 1.0)
+ c (~> 1.0)
+ c (1)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ a!
+ b!
+ c!
+ LOCKFILE
+
+ assert_equal expected, @lockfile.to_s
+ end
+
+ def test_unget
+ @lockfile.instance_variable_set :@current_token, :token
+
+ @lockfile.unget
+
+ assert_equal :token, @lockfile.get
+ end
+
+ def test_write
+ @lockfile.write
+
+ gem_deps_lock_file = "#{@gem_deps_file}.lock"
+
+ assert_path_exists gem_deps_lock_file
+
+ refute_empty File.read gem_deps_lock_file
+ end
+
+ def test_write_error
+ @set.gem 'nonexistent'
+
+ gem_deps_lock_file = "#{@gem_deps_file}.lock"
+
+ open gem_deps_lock_file, 'w' do |io|
+ io.write 'hello'
+ end
+
+ assert_raises Gem::UnsatisfiableDependencyError do
+ @lockfile.write
+ end
+
+ assert_path_exists gem_deps_lock_file
+
+ assert_equal 'hello', File.read(gem_deps_lock_file)
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_requirement.rb b/jni/ruby/test/rubygems/test_gem_requirement.rb
new file mode 100644
index 0000000..6974ff0
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_requirement.rb
@@ -0,0 +1,376 @@
+require 'rubygems/test_case'
+require "rubygems/requirement"
+
+class TestGemRequirement < Gem::TestCase
+
+ def test_concat
+ r = req '>= 1'
+
+ r.concat ['< 2']
+
+ assert_equal [['>=', v(1)], ['<', v(2)]], r.requirements
+ end
+
+ def test_equals2
+ r = req "= 1.2"
+ assert_equal r, r.dup
+ assert_equal r.dup, r
+
+ refute_requirement_equal "= 1.2", "= 1.3"
+ refute_requirement_equal "= 1.3", "= 1.2"
+
+ refute_equal Object.new, req("= 1.2")
+ refute_equal req("= 1.2"), Object.new
+ end
+
+ def test_initialize
+ assert_requirement_equal "= 2", "2"
+ assert_requirement_equal "= 2", ["2"]
+ assert_requirement_equal "= 2", v(2)
+ end
+
+ def test_empty_requirements_is_none
+ r = Gem::Requirement.new
+ assert_equal true, r.none?
+ end
+
+ def test_explicit_default_is_none
+ r = Gem::Requirement.new ">= 0"
+ assert_equal true, r.none?
+ end
+
+ def test_basic_non_none
+ r = Gem::Requirement.new "= 1"
+ assert_equal false, r.none?
+ end
+
+ def test_for_lockfile
+ assert_equal ' (~> 1.0)', req('~> 1.0').for_lockfile
+
+ assert_equal ' (~> 1.0, >= 1.0.1)', req('>= 1.0.1', '~> 1.0').for_lockfile
+
+ duped = req '= 1.0'
+ duped.requirements << ['=', v('1.0')]
+
+ assert_equal ' (= 1.0)', duped.for_lockfile
+
+ assert_nil Gem::Requirement.default.for_lockfile
+ end
+
+ def test_parse
+ assert_equal ['=', Gem::Version.new(1)], Gem::Requirement.parse(' 1')
+ assert_equal ['=', Gem::Version.new(1)], Gem::Requirement.parse('= 1')
+ assert_equal ['>', Gem::Version.new(1)], Gem::Requirement.parse('> 1')
+ assert_equal ['=', Gem::Version.new(1)], Gem::Requirement.parse("=\n1")
+
+ assert_equal ['=', Gem::Version.new(2)],
+ Gem::Requirement.parse(Gem::Version.new('2'))
+ end
+
+ def test_parse_bad
+ [
+ nil,
+ '',
+ '! 1',
+ '= junk',
+ '1..2',
+ ].each do |bad|
+ e = assert_raises Gem::Requirement::BadRequirementError do
+ Gem::Requirement.parse bad
+ end
+
+ assert_equal "Illformed requirement [#{bad.inspect}]", e.message
+ end
+
+ assert_equal Gem::Requirement::BadRequirementError.superclass, ArgumentError
+ end
+
+ def test_prerelease_eh
+ r = req '= 1'
+
+ refute r.prerelease?
+
+ r = req '= 1.a'
+
+ assert r.prerelease?
+
+ r = req '> 1.a', '< 2'
+
+ assert r.prerelease?
+ end
+
+ def test_satisfied_by_eh_bang_equal
+ r = req '!= 1.2'
+
+ assert_satisfied_by "1.1", r
+ refute_satisfied_by "1.2", r
+ assert_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ assert_satisfied_by nil, r
+ end
+ end
+
+ def test_satisfied_by_eh_blank
+ r = req "1.2"
+
+ refute_satisfied_by "1.1", r
+ assert_satisfied_by "1.2", r
+ refute_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ assert_satisfied_by nil, r
+ end
+ end
+
+ def test_satisfied_by_eh_equal
+ r = req "= 1.2"
+
+ refute_satisfied_by "1.1", r
+ assert_satisfied_by "1.2", r
+ refute_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ assert_satisfied_by nil, r
+ end
+ end
+
+ def test_satisfied_by_eh_gt
+ r = req "> 1.2"
+
+ refute_satisfied_by "1.1", r
+ refute_satisfied_by "1.2", r
+ assert_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ r.satisfied_by? nil
+ end
+ end
+
+ def test_satisfied_by_eh_gte
+ r = req ">= 1.2"
+
+ refute_satisfied_by "1.1", r
+ assert_satisfied_by "1.2", r
+ assert_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ r.satisfied_by? nil
+ end
+ end
+
+ def test_satisfied_by_eh_list
+ r = req "> 1.1", "< 1.3"
+
+ refute_satisfied_by "1.1", r
+ assert_satisfied_by "1.2", r
+ refute_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ r.satisfied_by? nil
+ end
+ end
+
+ def test_satisfied_by_eh_lt
+ r = req "< 1.2"
+
+ assert_satisfied_by "1.1", r
+ refute_satisfied_by "1.2", r
+ refute_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ r.satisfied_by? nil
+ end
+ end
+
+ def test_satisfied_by_eh_lte
+ r = req "<= 1.2"
+
+ assert_satisfied_by "1.1", r
+ assert_satisfied_by "1.2", r
+ refute_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ r.satisfied_by? nil
+ end
+ end
+
+ def test_satisfied_by_eh_tilde_gt
+ r = req "~> 1.2"
+
+ refute_satisfied_by "1.1", r
+ assert_satisfied_by "1.2", r
+ assert_satisfied_by "1.3", r
+
+ assert_raises ArgumentError do
+ r.satisfied_by? nil
+ end
+ end
+
+ def test_satisfied_by_eh_tilde_gt_v0
+ r = req "~> 0.0.1"
+
+ refute_satisfied_by "0.1.1", r
+ assert_satisfied_by "0.0.2", r
+ assert_satisfied_by "0.0.1", r
+ end
+
+ def test_satisfied_by_eh_good
+ assert_satisfied_by "0.2.33", "= 0.2.33"
+ assert_satisfied_by "0.2.34", "> 0.2.33"
+ assert_satisfied_by "1.0", "= 1.0"
+ assert_satisfied_by "1.0", "1.0"
+ assert_satisfied_by "1.8.2", "> 1.8.0"
+ assert_satisfied_by "1.112", "> 1.111"
+ assert_satisfied_by "0.2", "> 0.0.0"
+ assert_satisfied_by "0.0.0.0.0.2", "> 0.0.0"
+ assert_satisfied_by "0.0.1.0", "> 0.0.0.1"
+ assert_satisfied_by "10.3.2", "> 9.3.2"
+ assert_satisfied_by "1.0.0.0", "= 1.0"
+ assert_satisfied_by "10.3.2", "!= 9.3.4"
+ assert_satisfied_by "10.3.2", "> 9.3.2"
+ assert_satisfied_by "10.3.2", "> 9.3.2"
+ assert_satisfied_by " 9.3.2", ">= 9.3.2"
+ assert_satisfied_by "9.3.2 ", ">= 9.3.2"
+ assert_satisfied_by "", "= 0"
+ assert_satisfied_by "", "< 0.1"
+ assert_satisfied_by " ", "< 0.1 "
+ assert_satisfied_by "", " < 0.1"
+ assert_satisfied_by " ", "> 0.a "
+ assert_satisfied_by "", " > 0.a"
+ assert_satisfied_by "3.1", "< 3.2.rc1"
+
+ assert_satisfied_by "3.2.0", "> 3.2.0.rc1"
+ assert_satisfied_by "3.2.0.rc2", "> 3.2.0.rc1"
+
+ assert_satisfied_by "3.0.rc2", "< 3.0"
+ assert_satisfied_by "3.0.rc2", "< 3.0.0"
+ assert_satisfied_by "3.0.rc2", "< 3.0.1"
+
+ assert_satisfied_by "3.0.rc2", "> 0"
+ end
+
+ def test_illformed_requirements
+ [ ">>> 1.3.5", "> blah" ].each do |rq|
+ assert_raises Gem::Requirement::BadRequirementError, "req [#{rq}] should fail" do
+ Gem::Requirement.new rq
+ end
+ end
+ end
+
+ def test_satisfied_by_eh_non_versions
+ assert_raises ArgumentError do
+ req(">= 0").satisfied_by? Object.new
+ end
+
+ assert_raises ArgumentError do
+ req(">= 0").satisfied_by? Gem::Requirement.default
+ end
+ end
+
+ def test_satisfied_by_eh_boxed
+ refute_satisfied_by "1.3", "~> 1.4"
+ assert_satisfied_by "1.4", "~> 1.4"
+ assert_satisfied_by "1.5", "~> 1.4"
+ refute_satisfied_by "2.0", "~> 1.4"
+
+ refute_satisfied_by "1.3", "~> 1.4.4"
+ refute_satisfied_by "1.4", "~> 1.4.4"
+ assert_satisfied_by "1.4.4", "~> 1.4.4"
+ assert_satisfied_by "1.4.5", "~> 1.4.4"
+ refute_satisfied_by "1.5", "~> 1.4.4"
+ refute_satisfied_by "2.0", "~> 1.4.4"
+
+ refute_satisfied_by "1.1.pre", "~> 1.0.0"
+ refute_satisfied_by "1.1.pre", "~> 1.1"
+ refute_satisfied_by "2.0.a", "~> 1.0"
+ refute_satisfied_by "2.0.a", "~> 2.0"
+
+ refute_satisfied_by "0.9", "~> 1"
+ assert_satisfied_by "1.0", "~> 1"
+ assert_satisfied_by "1.1", "~> 1"
+ refute_satisfied_by "2.0", "~> 1"
+ end
+
+ def test_satisfied_by_eh_multiple
+ req = [">= 1.4", "<= 1.6", "!= 1.5"]
+
+ refute_satisfied_by "1.3", req
+ assert_satisfied_by "1.4", req
+ refute_satisfied_by "1.5", req
+ assert_satisfied_by "1.6", req
+ refute_satisfied_by "1.7", req
+ refute_satisfied_by "2.0", req
+ end
+
+ def test_satisfied_by_boxed
+ refute_satisfied_by "1.3", "~> 1.4"
+ assert_satisfied_by "1.4", "~> 1.4"
+ assert_satisfied_by "1.5", "~> 1.4"
+ refute_satisfied_by "2.0", "~> 1.4"
+
+ refute_satisfied_by "1.3", "~> 1.4.4"
+ refute_satisfied_by "1.4", "~> 1.4.4"
+ assert_satisfied_by "1.4.4", "~> 1.4.4"
+ assert_satisfied_by "1.4.5", "~> 1.4.4"
+ refute_satisfied_by "1.5", "~> 1.4.4"
+ refute_satisfied_by "2.0", "~> 1.4.4"
+ end
+
+ def test_specific
+ refute req('> 1') .specific?
+ refute req('>= 1').specific?
+
+ assert req('!= 1').specific?
+ assert req('< 1') .specific?
+ assert req('<= 1').specific?
+ assert req('= 1') .specific?
+ assert req('~> 1').specific?
+
+ assert req('> 1', '> 2').specific? # GIGO
+ end
+
+ def test_bad
+ refute_satisfied_by "", "> 0.1"
+ refute_satisfied_by "1.2.3", "!= 1.2.3"
+ refute_satisfied_by "1.2.003.0.0", "!= 1.02.3"
+ refute_satisfied_by "4.5.6", "< 1.2.3"
+ refute_satisfied_by "1.0", "> 1.1"
+ refute_satisfied_by "", "= 0.1"
+ refute_satisfied_by "1.1.1", "> 1.1.1"
+ refute_satisfied_by "1.2", "= 1.1"
+ refute_satisfied_by "1.40", "= 1.1"
+ refute_satisfied_by "1.3", "= 1.40"
+ refute_satisfied_by "9.3.3", "<= 9.3.2"
+ refute_satisfied_by "9.3.1", ">= 9.3.2"
+ refute_satisfied_by "9.3.03", "<= 9.3.2"
+ refute_satisfied_by "1.0.0.1", "= 1.0"
+ end
+
+ # Assert that two requirements are equal. Handles Gem::Requirements,
+ # strings, arrays, numbers, and versions.
+
+ def assert_requirement_equal expected, actual
+ assert_equal req(expected), req(actual)
+ end
+
+ # Assert that +version+ satisfies +requirement+.
+
+ def assert_satisfied_by version, requirement
+ assert req(requirement).satisfied_by?(v(version)),
+ "#{requirement} is satisfied by #{version}"
+ end
+
+ # Refute the assumption that two requirements are equal.
+
+ def refute_requirement_equal unexpected, actual
+ refute_equal req(unexpected), req(actual)
+ end
+
+ # Refute the assumption that +version+ satisfies +requirement+.
+
+ def refute_satisfied_by version, requirement
+ refute req(requirement).satisfied_by?(v(version)),
+ "#{requirement} is not satisfied by #{version}"
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_resolver.rb b/jni/ruby/test/rubygems/test_gem_resolver.rb
new file mode 100644
index 0000000..2b9e9fe
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver.rb
@@ -0,0 +1,733 @@
+require 'rubygems/test_case'
+
+class TestGemResolver < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::Resolver
+ end
+
+ def make_dep(name, *req)
+ Gem::Dependency.new(name, *req)
+ end
+
+ def set(*specs)
+ source = Gem::Source.new URI @gem_repo
+
+ specs = specs.map do |spec|
+ Gem::Resolver::SpecSpecification.new nil, spec, source
+ end
+
+ StaticSet.new(specs)
+ end
+
+ def assert_resolves_to expected, resolver
+ actual = resolver.resolve
+
+ exp = expected.sort_by { |s| s.full_name }
+ act = actual.map { |a| a.spec.spec }.sort_by { |s| s.full_name }
+
+ msg = "Set of gems was not the same: #{exp.map { |x| x.full_name}.inspect} != #{act.map { |x| x.full_name}.inspect}"
+
+ assert_equal exp, act, msg
+ rescue Gem::DependencyResolutionError => e
+ flunk e.message
+ end
+
+ def test_self_compatibility
+ assert_same Gem::Resolver, Gem::DependencyResolver
+ end
+
+ def test_self_compose_sets_best_set
+ best_set = @DR::BestSet.new
+
+ composed = @DR.compose_sets best_set
+
+ assert_equal best_set, composed
+ end
+
+ def test_self_compose_sets_multiple
+ index_set = @DR::IndexSet.new
+ vendor_set = @DR::VendorSet.new
+
+ composed = @DR.compose_sets index_set, vendor_set
+
+ assert_kind_of Gem::Resolver::ComposedSet, composed
+
+ assert_equal [index_set, vendor_set], composed.sets
+ end
+
+ def test_self_compose_sets_nest
+ index_set = @DR::IndexSet.new
+ vendor_set = @DR::VendorSet.new
+
+ inner = @DR.compose_sets index_set, vendor_set
+
+ current_set = @DR::CurrentSet.new
+
+ composed = @DR.compose_sets inner, current_set
+
+ assert_kind_of Gem::Resolver::ComposedSet, composed
+
+ assert_equal [index_set, vendor_set, current_set], composed.sets
+ end
+
+ def test_self_compose_sets_nil
+ index_set = @DR::IndexSet.new
+
+ composed = @DR.compose_sets index_set, nil
+
+ assert_same index_set, composed
+
+ e = assert_raises ArgumentError do
+ @DR.compose_sets nil
+ end
+
+ assert_equal 'one set in the composition must be non-nil', e.message
+ end
+
+ def test_self_compose_sets_single
+ index_set = @DR::IndexSet.new
+
+ composed = @DR.compose_sets index_set
+
+ assert_same index_set, composed
+ end
+
+ def test_handle_conflict
+ a1 = util_spec 'a', 1
+
+ r1 = Gem::Resolver::DependencyRequest.new dep('a', '= 1'), nil
+ r2 = Gem::Resolver::DependencyRequest.new dep('a', '= 2'), nil
+ r3 = Gem::Resolver::DependencyRequest.new dep('a', '= 3'), nil
+
+ existing = Gem::Resolver::ActivationRequest.new a1, r1, false
+
+ res = Gem::Resolver.new [a1]
+
+ res.handle_conflict r2, existing
+ res.handle_conflict r2, existing
+ res.handle_conflict r3, existing
+
+ assert_equal 2, res.conflicts.length
+ end
+
+ def test_requests
+ a1 = util_spec 'a', 1, 'b' => 2
+
+ r1 = Gem::Resolver::DependencyRequest.new dep('a', '= 1'), nil
+
+ act = Gem::Resolver::ActivationRequest.new a1, r1, false
+
+ res = Gem::Resolver.new [a1]
+
+ reqs = Gem::Resolver::RequirementList.new
+
+ res.requests a1, act, reqs
+
+ assert_equal ['b (= 2)'], reqs.to_a.map { |req| req.to_s }
+ end
+
+ def test_requests_development
+ a1 = util_spec 'a', 1, 'b' => 2
+
+ spec = Gem::Resolver::SpecSpecification.new nil, a1
+ def spec.fetch_development_dependencies
+ @called = true
+ end
+
+ r1 = Gem::Resolver::DependencyRequest.new dep('a', '= 1'), nil
+
+ act = Gem::Resolver::ActivationRequest.new spec, r1, false
+
+ res = Gem::Resolver.new [act]
+ res.development = true
+
+ reqs = Gem::Resolver::RequirementList.new
+
+ res.requests spec, act, reqs
+
+ assert_equal ['b (= 2)'], reqs.to_a.map { |req| req.to_s }
+
+ assert spec.instance_variable_defined? :@called
+ end
+
+ def test_requests_ignore_dependencies
+ a1 = util_spec 'a', 1, 'b' => 2
+
+ r1 = Gem::Resolver::DependencyRequest.new dep('a', '= 1'), nil
+
+ act = Gem::Resolver::ActivationRequest.new a1, r1, false
+
+ res = Gem::Resolver.new [a1]
+ res.ignore_dependencies = true
+
+ reqs = Gem::Resolver::RequirementList.new
+
+ res.requests a1, act, reqs
+
+ assert_empty reqs
+ end
+
+ def test_resolve_conservative
+ a1_spec = util_spec 'a', 1
+ a2_spec = util_spec 'a', 2 do |s|
+ s.add_dependency 'b', 2
+ s.add_dependency 'c'
+ end
+ b1_spec = util_spec 'b', 1
+ b2_spec = util_spec 'b', 2
+ c1_spec = util_spec 'c', 1 do |s| s.add_dependency 'd', 2 end
+ c2_spec = util_spec 'c', 2 do |s| s.add_dependency 'd', 2 end
+ d1_spec = util_spec 'd', 1 do |s| s.add_dependency 'e' end
+ d2_spec = util_spec 'd', 2 do |s| s.add_dependency 'e' end
+ e1_spec = util_spec 'e', 1
+ e2_spec = util_spec 'e', 2
+
+ a_dep = make_dep 'a', '= 2'
+ e_dep = make_dep 'e'
+
+ # When requesting to install:
+ # a-2, e
+ deps = [a_dep, e_dep]
+
+ s = set a1_spec, a2_spec, b1_spec, b2_spec, c1_spec, c2_spec, d1_spec, d2_spec, e1_spec, e2_spec
+
+ res = Gem::Resolver.new deps, s
+
+ # With the following gems already installed:
+ # a-1, b-1, c-1, e-1
+ res.skip_gems = {'a'=>[a1_spec], 'b'=>[b1_spec], 'c'=>[c1_spec], 'e'=>[e1_spec]}
+
+ # Make sure the following gems end up getting used/installed/upgraded:
+ # a-2 (upgraded)
+ # b-2 (upgraded), specific dependency from a-2
+ # c-1 (used, not upgraded), open dependency from a-2
+ # d-2 (installed), specific dependency from c-2
+ # e-1 (used, not upgraded), open dependency from request
+ assert_resolves_to [a2_spec, b2_spec, c1_spec, d2_spec, e1_spec], res
+ end
+
+ def test_resolve_development
+ a_spec = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b_spec = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c_spec = util_spec 'c', 1
+
+ a_dep = make_dep 'a', '= 1'
+
+ deps = [a_dep]
+
+ s = set a_spec, b_spec, c_spec
+
+ res = Gem::Resolver.new deps, s
+
+ res.development = true
+
+ assert_resolves_to [a_spec, b_spec, c_spec], res
+ end
+
+ def test_resolve_development_shallow
+ a_spec = util_spec 'a', 1 do |s|
+ s.add_development_dependency 'b'
+ s.add_runtime_dependency 'd'
+ end
+
+ b_spec = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c_spec = util_spec 'c', 1
+
+ d_spec = util_spec 'd', 1 do |s| s.add_development_dependency 'e' end
+ e_spec = util_spec 'e', 1
+
+ a_dep = make_dep 'a', '= 1'
+
+ deps = [a_dep]
+
+ s = set a_spec, b_spec, c_spec, d_spec, e_spec
+
+ res = Gem::Resolver.new deps, s
+
+ res.development = true
+ res.development_shallow = true
+
+ assert_resolves_to [a_spec, b_spec, d_spec], res
+ end
+
+ def test_resolve_remote_missing_dependency
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
+
+ a_dep = make_dep 'a', '= 1'
+
+ res = Gem::Resolver.new [a_dep], Gem::Resolver::IndexSet.new
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ res.resolve
+ end
+
+ refute_empty e.errors
+ end
+
+ def test_no_overlap_specificly
+ a = util_spec "a", '1'
+ b = util_spec "b", "1"
+
+ ad = make_dep "a", "= 1"
+ bd = make_dep "b", "= 1"
+
+ deps = [ad, bd]
+
+ s = set(a, b)
+
+ res = Gem::Resolver.new(deps, s)
+
+ assert_resolves_to [a, b], res
+ end
+
+ def test_pulls_in_dependencies
+ a = util_spec "a", '1'
+ b = util_spec "b", "1", "c" => "= 1"
+ c = util_spec "c", "1"
+
+ ad = make_dep "a", "= 1"
+ bd = make_dep "b", "= 1"
+
+ deps = [ad, bd]
+
+ s = set(a, b, c)
+
+ res = Gem::Resolver.new(deps, s)
+
+ assert_resolves_to [a, b, c], res
+ end
+
+ def test_picks_highest_version
+ a1 = util_spec "a", '1'
+ a2 = util_spec "a", '2'
+
+ s = set(a1, a2)
+
+ ad = make_dep "a"
+
+ res = Gem::Resolver.new([ad], s)
+
+ assert_resolves_to [a2], res
+ end
+
+ def test_picks_best_platform
+ is = Gem::Resolver::IndexSpecification
+ unknown = Gem::Platform.new 'unknown'
+ a2_p1 = a3_p2 = nil
+
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ a2_p1 = fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end
+ a3_p2 = fetcher.spec 'a', 3 do |s| s.platform = unknown end
+ end
+
+ v2 = v(2)
+ v3 = v(3)
+ source = Gem::Source.new @gem_repo
+
+ s = set
+
+ a2 = is.new s, 'a', v2, source, Gem::Platform::RUBY
+ a2_p1 = is.new s, 'a', v2, source, Gem::Platform.local.to_s
+ a3_p2 = is.new s, 'a', v3, source, unknown
+
+ s.add a3_p2
+ s.add a2_p1
+ s.add a2
+
+ ad = make_dep "a"
+
+ res = Gem::Resolver.new([ad], s)
+
+ assert_resolves_to [a2_p1.spec], res
+ end
+
+ def test_only_returns_spec_once
+ a1 = util_spec "a", "1", "c" => "= 1"
+ b1 = util_spec "b", "1", "c" => "= 1"
+
+ c1 = util_spec "c", "1"
+
+ ad = make_dep "a"
+ bd = make_dep "b"
+
+ s = set(a1, b1, c1)
+
+ res = Gem::Resolver.new([ad, bd], s)
+
+ assert_resolves_to [a1, b1, c1], res
+ end
+
+ def test_picks_lower_version_when_needed
+ a1 = util_spec "a", "1", "c" => ">= 1"
+ b1 = util_spec "b", "1", "c" => "= 1"
+
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
+
+ ad = make_dep "a"
+ bd = make_dep "b"
+
+ s = set(a1, b1, c1, c2)
+
+ res = Gem::Resolver.new([ad, bd], s)
+
+ assert_resolves_to [a1, b1, c1], res
+ end
+
+ def test_conflict_resolution_only_effects_correct_spec
+ a1 = util_spec "a", "1", "c" => ">= 1"
+ b1 = util_spec "b", "1", "d" => ">= 1"
+
+ d3 = util_spec "d", "3", "c" => "= 1"
+ d4 = util_spec "d", "4", "c" => "= 1"
+
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
+
+ ad = make_dep "a"
+ bd = make_dep "b"
+
+ s = set(a1, b1, d3, d4, c1, c2)
+
+ res = Gem::Resolver.new([ad, bd], s)
+
+ assert_resolves_to [a1, b1, c1, d4], res
+ end
+
+ def test_backoff_higher_version_to_satisfy_dep
+ t3 = util_spec "railties", "3.2"
+ t4 = util_spec "railties", "4.0"
+
+ r3 = util_spec "rails", "3.2", "railties" => "= 3.2"
+ r4 = util_spec "rails", "4.0", "railties" => "= 4.0"
+
+ rd = make_dep "rails", "3.2"
+
+ c3 = util_spec "coffee", "3.0", "railties" => "~> 3.0"
+ c4 = util_spec "coffee", "4.0", "railties" => "~> 4.0"
+
+ cd = make_dep "coffee"
+
+ s = set(t3, t4, r3, r4, c3, c4)
+
+ res = Gem::Resolver.new([rd, cd], s)
+
+ assert_resolves_to [r3, t3, c3], res
+ end
+
+ def test_raises_dependency_error
+ a1 = util_spec "a", "1", "c" => "= 1"
+ b1 = util_spec "b", "1", "c" => "= 2"
+
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
+
+ ad = make_dep "a"
+ bd = make_dep "b"
+
+ s = set(a1, b1, c1, c2)
+
+ r = Gem::Resolver.new([ad, bd], s)
+
+ e = assert_raises Gem::DependencyResolutionError do
+ r.resolve
+ end
+
+ deps = [make_dep("c", "= 1"), make_dep("c", "= 2")]
+ assert_equal deps, e.conflicting_dependencies
+
+ con = e.conflict
+
+ act = con.activated
+ assert_equal "c-2", act.spec.full_name
+
+ parent = act.parent
+ assert_equal "b-1", parent.spec.full_name
+
+ act = con.requester
+ assert_equal "a-1", act.spec.full_name
+ end
+
+ def test_raises_when_a_gem_is_missing
+ ad = make_dep "a"
+
+ r = Gem::Resolver.new([ad], set)
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ r.resolve
+ end
+
+ assert_equal "Unable to resolve dependency: user requested 'a (>= 0)'",
+ e.message
+
+ assert_equal "a (>= 0)", e.dependency.to_s
+ end
+
+ def test_raises_when_a_gem_version_is_missing
+ a1 = util_spec "a", "1"
+
+ ad = make_dep "a", "= 3"
+
+ r = Gem::Resolver.new([ad], set(a1))
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ r.resolve
+ end
+
+ assert_equal "a (= 3)", e.dependency.to_s
+ end
+
+ def test_raises_and_reports_a_toplevel_request_properly
+ a1 = util_spec "a", "1"
+ ad = make_dep "a", "= 3"
+
+ r = Gem::Resolver.new([ad], set(a1))
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ r.resolve
+ end
+
+ assert_equal "Unable to resolve dependency: user requested 'a (= 3)'",
+ e.message
+ end
+
+ def test_raises_and_reports_an_implicit_request_properly
+ a1 = util_spec "a", "1" do |s|
+ s.add_runtime_dependency 'b', '= 2'
+ end
+
+ ad = make_dep "a", "= 1"
+
+ r = Gem::Resolver.new([ad], set(a1))
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ r.resolve
+ end
+
+ assert_equal "Unable to resolve dependency: 'a (= 1)' requires 'b (= 2)'",
+ e.message
+ end
+
+ def test_raises_when_possibles_are_exhausted
+ a1 = util_spec "a", "1", "c" => ">= 2"
+ b1 = util_spec "b", "1", "c" => "= 1"
+
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
+ c3 = util_spec "c", "3"
+
+ s = set(a1, b1, c1, c2, c3)
+
+ ad = make_dep "a"
+ bd = make_dep "b"
+
+ r = Gem::Resolver.new([ad, bd], s)
+
+ e = assert_raises Gem::DependencyResolutionError do
+ r.resolve
+ end
+
+ dependency = e.conflict.dependency
+
+ assert_equal 'a', dependency.name
+ assert_equal req('>= 0'), dependency.requirement
+
+ activated = e.conflict.activated
+ assert_equal 'c-1', activated.full_name
+
+ assert_equal dep('c', '= 1'), activated.request.dependency
+
+ assert_equal [dep('c', '>= 2'), dep('c', '= 1')],
+ e.conflict.conflicting_dependencies
+ end
+
+ def test_keeps_resolving_after_seeing_satisfied_dep
+ a1 = util_spec "a", "1", "b" => "= 1", "c" => "= 1"
+ b1 = util_spec "b", "1"
+ c1 = util_spec "c", "1"
+
+ ad = make_dep "a"
+ bd = make_dep "b"
+
+ s = set(a1, b1, c1)
+
+ r = Gem::Resolver.new([ad, bd], s)
+
+ assert_resolves_to [a1, b1, c1], r
+ end
+
+ def test_common_rack_activation_scenario
+ rack100 = util_spec "rack", "1.0.0"
+ rack101 = util_spec "rack", "1.0.1"
+
+ lib1 = util_spec "lib", "1", "rack" => ">= 1.0.1"
+
+ rails = util_spec "rails", "3", "actionpack" => "= 3"
+ ap = util_spec "actionpack", "3", "rack" => ">= 1.0.0"
+
+ d1 = make_dep "rails"
+ d2 = make_dep "lib"
+
+ s = set(lib1, rails, ap, rack100, rack101)
+
+ r = Gem::Resolver.new([d1, d2], s)
+
+ assert_resolves_to [rails, ap, rack101, lib1], r
+
+ # check it with the deps reverse too
+
+ r = Gem::Resolver.new([d2, d1], s)
+
+ assert_resolves_to [lib1, rack101, rails, ap], r
+ end
+
+ def test_backtracks_to_the_first_conflict
+ a1 = util_spec "a", "1"
+ a2 = util_spec "a", "2"
+ a3 = util_spec "a", "3"
+ a4 = util_spec "a", "4"
+
+ d1 = make_dep "a"
+ d2 = make_dep "a", ">= 2"
+ d3 = make_dep "a", "= 1"
+
+ s = set(a1, a2, a3, a4)
+
+ r = Gem::Resolver.new([d1, d2, d3], s)
+
+ assert_raises Gem::DependencyResolutionError do
+ r.resolve
+ end
+ end
+
+ def test_resolve_conflict
+ a1 = util_spec 'a', 1
+ a2 = util_spec 'a', 2
+
+ b2 = util_spec 'b', 2, 'a' => '~> 2.0'
+
+ s = set a1, a2, b2
+
+ a_dep = dep 'a', '~> 1.0'
+ b_dep = dep 'b'
+
+ r = Gem::Resolver.new [a_dep, b_dep], s
+
+ assert_raises Gem::DependencyResolutionError do
+ r.resolve
+ end
+ end
+
+ def test_resolve_bug_699
+ a1 = util_spec 'a', '1', 'b' => '= 2',
+ 'c' => '~> 1.0.3'
+
+ b1 = util_spec 'b', '2', 'c' => '~> 1.0'
+
+ c1 = util_spec 'c', '1.0.9'
+ c2 = util_spec 'c', '1.1.0'
+ c3 = util_spec 'c', '1.2.0'
+
+ s = set a1, b1, c1, c2, c3
+
+ a_dep = dep 'a', '= 1'
+
+ r = Gem::Resolver.new [a_dep], s
+
+ assert_resolves_to [a1, b1, c1], r
+ end
+
+ def test_resolve_rollback
+ a1 = util_spec 'a', 1
+ a2 = util_spec 'a', 2
+
+ b1 = util_spec 'b', 1, 'a' => '~> 1.0'
+ b2 = util_spec 'b', 2, 'a' => '~> 2.0'
+
+ s = set a1, a2, b1, b2
+
+ a_dep = dep 'a', '~> 1.0'
+ b_dep = dep 'b'
+
+ r = Gem::Resolver.new [a_dep, b_dep], s
+
+ assert_resolves_to [a1, b1], r
+ end
+
+ # actionmailer 2.3.4
+ # activemerchant 1.5.0
+ # activesupport 2.3.5, 2.3.4
+ # Activemerchant needs activesupport >= 2.3.2. When you require activemerchant, it will activate the latest version that meets that requirement which is 2.3.5. Actionmailer on the other hand needs activesupport = 2.3.4. When rubygems tries to activate activesupport 2.3.4, it will raise an error.
+
+
+ def test_simple_activesupport_problem
+ sup1 = util_spec "activesupport", "2.3.4"
+ sup2 = util_spec "activesupport", "2.3.5"
+
+ merch = util_spec "activemerchant", "1.5.0", "activesupport" => ">= 2.3.2"
+ mail = util_spec "actionmailer", "2.3.4", "activesupport" => "= 2.3.4"
+
+ s = set(mail, merch, sup1, sup2)
+
+ d1 = make_dep "activemerchant"
+ d2 = make_dep "actionmailer"
+
+ r = Gem::Resolver.new([d1, d2], s)
+
+ assert_resolves_to [merch, mail, sup1], r
+ end
+
+ def test_second_level_backout
+ b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb"
+ b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
+ c1 = new_spec "c", "1"
+ c2 = new_spec "c", "2"
+ d1 = new_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
+ d2 = new_spec "d", "2", { "c" => "< 2" }, "lib/d.rb"
+
+ s = set(b1, b2, c1, c2, d1, d2)
+
+ p1 = make_dep "b", "> 0"
+ p2 = make_dep "d", "> 0"
+
+ r = Gem::Resolver.new([p1, p2], s)
+
+ assert_resolves_to [b1, c1, d2], r
+ end
+
+ def test_select_local_platforms
+ r = Gem::Resolver.new nil, nil
+
+ a1 = util_spec 'a', 1
+ a1_p1 = util_spec 'a', 1 do |s| s.platform = Gem::Platform.local end
+ a1_p2 = util_spec 'a', 1 do |s| s.platform = 'unknown' end
+
+ selected = r.select_local_platforms [a1, a1_p1, a1_p2]
+
+ assert_equal [a1, a1_p1], selected
+ end
+
+ def test_raises_and_explains_when_platform_prevents_install
+ a1 = util_spec "a", "1" do |s|
+ s.platform = Gem::Platform.new %w[c p 1]
+ end
+
+ ad = make_dep "a", "= 1"
+
+ r = Gem::Resolver.new([ad], set(a1))
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ r.resolve
+ end
+
+ assert_match "No match for 'a (= 1)' on this platform. Found: c-p-1",
+ e.message
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_activation_request.rb b/jni/ruby/test/rubygems/test_gem_resolver_activation_request.rb
new file mode 100644
index 0000000..c9163e2
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_activation_request.rb
@@ -0,0 +1,73 @@
+require 'rubygems/test_case'
+
+class TestGemResolverActivationRequest < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::Resolver
+
+ @dep = @DR::DependencyRequest.new dep('a', '>= 0'), nil
+
+ source = Gem::Source::Local.new
+ platform = Gem::Platform::RUBY
+
+ @a1 = @DR::IndexSpecification.new nil, 'a', v(1), source, platform
+ @a2 = @DR::IndexSpecification.new nil, 'a', v(2), source, platform
+ @a3 = @DR::IndexSpecification.new nil, 'a', v(3), source, platform
+
+ @req = @DR::ActivationRequest.new @a3, @dep, [@a1, @a2]
+ end
+
+ def test_development_eh
+ refute @req.development?
+
+ dep_req = @DR::DependencyRequest.new dep('a', '>= 0', :development), nil
+
+ act_req = @DR::ActivationRequest.new @a3, dep_req, [@a1, @a2]
+
+ assert act_req.development?
+ end
+
+ def test_inspect
+ assert_match 'a-3', @req.inspect
+ assert_match 'from a (>= 0)', @req.inspect
+ assert_match '(others possible: a-1, a-2)', @req.inspect
+ end
+
+ def test_inspect_legacy
+ req = @DR::ActivationRequest.new @a3, @dep, true
+
+ assert_match '(others possible)', req.inspect
+
+ req = @DR::ActivationRequest.new @a3, @dep, false
+
+ refute_match '(others possible)', req.inspect
+ end
+
+ def test_installed_eh
+ v_spec = Gem::Resolver::VendorSpecification.new nil, @a3
+
+ @req = @DR::ActivationRequest.new v_spec, @dep, [@a1, @a2]
+
+ assert @req.installed?
+ end
+
+ def test_others_possible_eh
+ assert @req.others_possible?
+
+ req = @DR::ActivationRequest.new @a3, @dep, []
+
+ refute req.others_possible?
+
+ req = @DR::ActivationRequest.new @a3, @dep, true
+
+ assert req.others_possible?
+
+ req = @DR::ActivationRequest.new @a3, @dep, false
+
+ refute req.others_possible?
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_api_set.rb b/jni/ruby/test/rubygems/test_gem_resolver_api_set.rb
new file mode 100644
index 0000000..4ae54d7
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_api_set.rb
@@ -0,0 +1,208 @@
+require 'rubygems/test_case'
+
+class TestGemResolverAPISet < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::Resolver
+ @dep_uri = URI "#{@gem_repo}api/v1/dependencies"
+ end
+
+ def test_initialize
+ set = @DR::APISet.new
+
+ assert_equal URI('https://rubygems.org/api/v1/dependencies'), set.dep_uri
+ assert_equal URI('https://rubygems.org'), set.uri
+ assert_equal Gem::Source.new(URI('https://rubygems.org')), set.source
+ end
+
+ def test_initialize_deeper_uri
+ set = @DR::APISet.new 'https://rubygemsserver.com/mygems/api/v1/dependencies'
+
+ assert_equal URI('https://rubygemsserver.com/mygems/api/v1/dependencies'), set.dep_uri
+ assert_equal URI('https://rubygemsserver.com/mygems/'), set.uri
+ assert_equal Gem::Source.new(URI('https://rubygemsserver.com/mygems/')), set.source
+ end
+
+ def test_initialize_uri
+ set = @DR::APISet.new @dep_uri
+
+ assert_equal URI("#{@gem_repo}api/v1/dependencies"), set.dep_uri
+ assert_equal URI("#{@gem_repo}"), set.uri
+ end
+
+ def test_find_all
+ spec_fetcher
+
+ data = [
+ { :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [], },
+ ]
+
+ @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data
+
+ set = @DR::APISet.new @dep_uri
+
+ a_dep = @DR::DependencyRequest.new dep('a'), nil
+
+ expected = [
+ @DR::APISpecification.new(set, data.first)
+ ]
+
+ assert_equal expected, set.find_all(a_dep)
+ end
+
+ def test_find_all_cache
+ spec_fetcher
+
+ data = [
+ { :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [], },
+ ]
+
+ @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data
+
+ set = @DR::APISet.new @dep_uri
+
+ a_dep = @DR::DependencyRequest.new dep('a'), nil
+
+ set.prefetch [a_dep]
+
+ expected = [
+ @DR::APISpecification.new(set, data.first)
+ ]
+
+ assert_equal expected, set.find_all(a_dep)
+
+ @fetcher.data.delete "#{@dep_uri}?gems=a"
+ end
+
+ def test_find_all_local
+ set = @DR::APISet.new @dep_uri
+ set.remote = false
+
+ a_dep = @DR::DependencyRequest.new dep('a'), nil
+
+ assert_empty set.find_all(a_dep)
+ end
+
+ def test_find_all_missing
+ spec_fetcher
+
+ @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump []
+
+ set = @DR::APISet.new @dep_uri
+
+ a_dep = @DR::DependencyRequest.new dep('a'), nil
+
+ assert_empty set.find_all(a_dep)
+
+ @fetcher.data.delete "#{@dep_uri}?gems=a"
+
+ assert_empty set.find_all(a_dep)
+ end
+
+ def test_prefetch
+ spec_fetcher
+
+ data = [
+ { :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [], },
+ ]
+
+ @fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data
+ @fetcher.data["#{@dep_uri}?gems=b"] = Marshal.dump []
+
+ set = @DR::APISet.new @dep_uri
+
+ a_dep = @DR::DependencyRequest.new dep('a'), nil
+ b_dep = @DR::DependencyRequest.new dep('b'), nil
+
+ set.prefetch [a_dep, b_dep]
+
+ assert_equal %w[a-1], set.find_all(a_dep).map { |s| s.full_name }
+ assert_empty set.find_all(b_dep)
+ end
+
+ def test_prefetch_cache
+ spec_fetcher
+
+ data = [
+ { :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [], },
+ ]
+
+ @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data
+
+ set = @DR::APISet.new @dep_uri
+
+ a_dep = @DR::DependencyRequest.new dep('a'), nil
+ b_dep = @DR::DependencyRequest.new dep('b'), nil
+
+ set.prefetch [a_dep]
+
+ @fetcher.data.delete "#{@dep_uri}?gems=a"
+ @fetcher.data["#{@dep_uri}?gems=b"] = Marshal.dump []
+
+ set.prefetch [a_dep, b_dep]
+ end
+
+ def test_prefetch_cache_missing
+ spec_fetcher
+
+ data = [
+ { :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [], },
+ ]
+
+ @fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data
+
+ set = @DR::APISet.new @dep_uri
+
+ a_dep = @DR::DependencyRequest.new dep('a'), nil
+ b_dep = @DR::DependencyRequest.new dep('b'), nil
+
+ set.prefetch [a_dep, b_dep]
+
+ @fetcher.data.delete "#{@dep_uri}?gems=a,b"
+
+ set.prefetch [a_dep, b_dep]
+ end
+
+ def test_prefetch_local
+ spec_fetcher
+
+ data = [
+ { :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [], },
+ ]
+
+ @fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data
+ @fetcher.data["#{@dep_uri}?gems=b"] = Marshal.dump []
+
+ set = @DR::APISet.new @dep_uri
+ set.remote = false
+
+ a_dep = @DR::DependencyRequest.new dep('a'), nil
+ b_dep = @DR::DependencyRequest.new dep('b'), nil
+
+ set.prefetch [a_dep, b_dep]
+
+ assert_empty set.instance_variable_get :@data
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_api_specification.rb b/jni/ruby/test/rubygems/test_gem_resolver_api_specification.rb
new file mode 100644
index 0000000..dfa61e9
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_api_specification.rb
@@ -0,0 +1,144 @@
+require 'rubygems/test_case'
+
+class TestGemResolverAPISpecification < Gem::TestCase
+
+ def test_initialize
+ set = Gem::Resolver::APISet.new
+ data = {
+ :name => 'rails',
+ :number => '3.0.3',
+ :platform => Gem::Platform.local.to_s,
+ :dependencies => [
+ ['bundler', '~> 1.0'],
+ ['railties', '= 3.0.3'],
+ ],
+ }
+
+ spec = Gem::Resolver::APISpecification.new set, data
+
+ assert_equal 'rails', spec.name
+ assert_equal Gem::Version.new('3.0.3'), spec.version
+ assert_equal Gem::Platform.local, spec.platform
+
+ expected = [
+ Gem::Dependency.new('bundler', '~> 1.0'),
+ Gem::Dependency.new('railties', '= 3.0.3'),
+ ]
+
+ assert_equal expected, spec.dependencies
+ end
+
+ def test_fetch_development_dependencies
+ specs = spec_fetcher do |fetcher|
+ fetcher.spec 'rails', '3.0.3' do |s|
+ s.add_runtime_dependency 'bundler', '~> 1.0'
+ s.add_runtime_dependency 'railties', '= 3.0.3'
+ s.add_development_dependency 'a', '= 1'
+ end
+ end
+
+ rails = specs['rails-3.0.3']
+
+ repo = @gem_repo + 'api/v1/dependencies'
+
+ set = Gem::Resolver::APISet.new repo
+
+ data = {
+ :name => 'rails',
+ :number => '3.0.3',
+ :platform => 'ruby',
+ :dependencies => [
+ ['bundler', '~> 1.0'],
+ ['railties', '= 3.0.3'],
+ ],
+ }
+
+ util_setup_spec_fetcher rails
+
+ spec = Gem::Resolver::APISpecification.new set, data
+
+ spec.fetch_development_dependencies
+
+ expected = [
+ Gem::Dependency.new('bundler', '~> 1.0'),
+ Gem::Dependency.new('railties', '= 3.0.3'),
+ Gem::Dependency.new('a', '= 1', :development),
+ ]
+
+ assert_equal expected, spec.dependencies
+ end
+
+ def test_installable_platform_eh
+ set = Gem::Resolver::APISet.new
+ data = {
+ :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [],
+ }
+
+ a_spec = Gem::Resolver::APISpecification.new set, data
+
+ assert a_spec.installable_platform?
+
+ data = {
+ :name => 'b',
+ :number => '1',
+ :platform => 'cpu-other_platform-1',
+ :dependencies => [],
+ }
+
+ b_spec = Gem::Resolver::APISpecification.new set, data
+
+ refute b_spec.installable_platform?
+
+ data = {
+ :name => 'c',
+ :number => '1',
+ :platform => Gem::Platform.local.to_s,
+ :dependencies => [],
+ }
+
+ c_spec = Gem::Resolver::APISpecification.new set, data
+
+ assert c_spec.installable_platform?
+ end
+
+ def test_source
+ set = Gem::Resolver::APISet.new
+ data = {
+ :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [],
+ }
+
+ api_spec = Gem::Resolver::APISpecification.new set, data
+
+ assert_equal set.source, api_spec.source
+ end
+
+ def test_spec
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ dep_uri = URI(@gem_repo) + 'api/v1/dependencies'
+ set = Gem::Resolver::APISet.new dep_uri
+ data = {
+ :name => 'a',
+ :number => '1',
+ :platform => 'ruby',
+ :dependencies => [],
+ }
+
+ api_spec = Gem::Resolver::APISpecification.new set, data
+
+ spec = api_spec.spec
+
+ assert_kind_of Gem::Specification, spec
+ assert_equal 'a-1', spec.full_name
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_best_set.rb b/jni/ruby/test/rubygems/test_gem_resolver_best_set.rb
new file mode 100644
index 0000000..055438c
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_best_set.rb
@@ -0,0 +1,137 @@
+require 'rubygems/test_case'
+
+class TestGemResolverBestSet < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::Resolver
+ end
+
+ def test_initialize
+ set = @DR::BestSet.new
+
+ assert_empty set.sets
+ end
+
+ def test_find_all_index
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'b', 1
+ end
+
+ set = @DR::BestSet.new
+
+ dependency = dep 'a', '~> 1'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_equal %w[a-1], found.map { |s| s.full_name }
+ end
+
+ def test_find_all_fallback
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ set = @DR::BestSet.new
+
+ api_uri = URI(@gem_repo) + './api/v1/dependencies'
+
+ set.sets << Gem::Resolver::APISet.new(api_uri)
+
+ dependency = dep 'a', '~> 1'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_equal %w[a-1], found.map { |s| s.full_name }
+ end
+
+ def test_find_all_local
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'b', 1
+ end
+
+ set = @DR::BestSet.new
+ set.remote = false
+
+ dependency = dep 'a', '~> 1'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_empty found
+ end
+
+ def test_prefetch
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ set = @DR::BestSet.new
+
+ set.prefetch []
+
+ refute_empty set.sets
+ end
+
+ def test_prefetch_local
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ set = @DR::BestSet.new
+ set.remote = false
+
+ set.prefetch []
+
+ assert_empty set.sets
+ end
+
+ def test_replace_failed_api_set
+ set = @DR::BestSet.new
+
+ api_uri = URI(@gem_repo) + './api/v1/dependencies'
+ api_set = Gem::Resolver::APISet.new api_uri
+
+ set.sets << api_set
+
+ error_uri = api_uri + '?gems=a'
+
+ error = Gem::RemoteFetcher::FetchError.new 'bogus', error_uri
+
+ set.replace_failed_api_set error
+
+ assert_equal 1, set.sets.size
+
+ refute_includes set.sets, api_set
+
+ assert_kind_of Gem::Resolver::IndexSet, set.sets.first
+ end
+
+ def test_replace_failed_api_set_no_api_set
+ set = @DR::BestSet.new
+
+ index_set = Gem::Resolver::IndexSet.new Gem::Source.new @gem_repo
+
+ set.sets << index_set
+
+ error = Gem::RemoteFetcher::FetchError.new 'bogus', @gem_repo
+
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ set.replace_failed_api_set error
+ end
+
+ assert_equal error, e
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_composed_set.rb b/jni/ruby/test/rubygems/test_gem_resolver_composed_set.rb
new file mode 100644
index 0000000..f8455e1
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_composed_set.rb
@@ -0,0 +1,45 @@
+require 'rubygems/test_case'
+
+class TestGemResolverComposedSet < Gem::TestCase
+
+ def test_errors
+ index_set = Gem::Resolver::IndexSet.new
+ current_set = Gem::Resolver::CurrentSet.new
+
+ set = Gem::Resolver::ComposedSet.new index_set, current_set
+
+ set.instance_variable_get(:@errors) << :a
+ current_set.errors << :b
+
+ assert_includes set.errors, :a
+ assert_includes set.errors, :b
+ assert_includes set.errors, index_set.errors.first
+ end
+
+ def test_prerelease_equals
+ best_set = Gem::Resolver::BestSet.new
+ current_set = Gem::Resolver::CurrentSet.new
+
+ set = Gem::Resolver::ComposedSet.new best_set, current_set
+
+ set.prerelease = true
+
+ assert set.prerelease
+ assert best_set.prerelease
+ assert current_set.prerelease
+ end
+
+ def test_remote_equals
+ best_set = Gem::Resolver::BestSet.new
+ current_set = Gem::Resolver::CurrentSet.new
+
+ set = Gem::Resolver::ComposedSet.new best_set, current_set
+
+ set.remote = false
+
+ refute best_set.remote?
+ refute current_set.remote?
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_conflict.rb b/jni/ruby/test/rubygems/test_gem_resolver_conflict.rb
new file mode 100644
index 0000000..0cef0ca
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_conflict.rb
@@ -0,0 +1,87 @@
+require 'rubygems/test_case'
+
+class TestGemResolverConflict < Gem::TestCase
+
+ def test_self_compatibility
+ assert_same Gem::Resolver::Conflict, Gem::Resolver::DependencyConflict
+ end
+
+ def test_explanation
+ root =
+ dependency_request dep('net-ssh', '>= 2.0.13'), 'rye', '0.9.8'
+ child =
+ dependency_request dep('net-ssh', '>= 2.6.5'), 'net-ssh', '2.2.2', root
+
+ dep = Gem::Resolver::DependencyRequest.new dep('net-ssh', '>= 2.0.13'), nil
+
+ spec = quick_spec 'net-ssh', '2.2.2'
+ active =
+ Gem::Resolver::ActivationRequest.new spec, dep
+
+ conflict =
+ Gem::Resolver::Conflict.new child, active
+
+ expected = <<-EXPECTED
+ Activated net-ssh-2.2.2
+ which does not match conflicting dependency (>= 2.6.5)
+
+ Conflicting dependency chains:
+ net-ssh (>= 2.0.13), 2.2.2 activated
+
+ versus:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.2.2 activated, depends on
+ net-ssh (>= 2.6.5)
+
+ EXPECTED
+
+ assert_equal expected, conflict.explanation
+ end
+
+ def test_explanation_user_request
+ @DR = Gem::Resolver
+
+ spec = util_spec 'a', 2
+
+ a1_req = @DR::DependencyRequest.new dep('a', '= 1'), nil
+ a2_req = @DR::DependencyRequest.new dep('a', '= 2'), nil
+
+ activated = @DR::ActivationRequest.new spec, a2_req
+
+ conflict = @DR::Conflict.new a1_req, activated
+
+ expected = <<-EXPECTED
+ Activated a-2
+ which does not match conflicting dependency (= 1)
+
+ Conflicting dependency chains:
+ a (= 2), 2 activated
+
+ versus:
+ a (= 1)
+
+ EXPECTED
+
+ assert_equal expected, conflict.explanation
+ end
+
+ def test_request_path
+ root =
+ dependency_request dep('net-ssh', '>= 2.0.13'), 'rye', '0.9.8'
+
+ child =
+ dependency_request dep('other', '>= 1.0'), 'net-ssh', '2.2.2', root
+
+ conflict =
+ Gem::Resolver::Conflict.new nil, nil
+
+ expected = [
+ 'net-ssh (>= 2.0.13), 2.2.2 activated',
+ 'rye (= 0.9.8), 0.9.8 activated'
+ ]
+
+ assert_equal expected, conflict.request_path(child.requester)
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_dependency_request.rb b/jni/ruby/test/rubygems/test_gem_resolver_dependency_request.rb
new file mode 100644
index 0000000..0665883
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_dependency_request.rb
@@ -0,0 +1,84 @@
+require 'rubygems/test_case'
+
+class TestGemResolverDependencyRequest < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::Resolver::DependencyRequest
+ end
+
+ def test_development_eh
+ a_dep = dep 'a', '>= 1'
+
+ a_dep_req = @DR.new a_dep, nil
+
+ refute a_dep_req.development?
+
+ b_dep = dep 'b', '>= 1', :development
+
+ b_dep_req = @DR.new b_dep, nil
+
+ assert b_dep_req.development?
+ end
+
+ def test_match_eh
+ spec = util_spec 'a', 1
+ dependency = dep 'a', '>= 1'
+
+ dr = @DR.new dependency, nil
+
+ assert dr.match? spec
+ end
+
+ def test_match_eh_prerelease
+ spec = util_spec 'a', '1.a'
+
+ a_dep = dep 'a', '>= 1'
+ a_dr = @DR.new a_dep, nil
+
+ refute a_dr.match? spec
+
+ a_pre_dep = dep 'a', '>= 1.a'
+ a_pre_dr = @DR.new a_pre_dep, nil
+
+ assert a_pre_dr.match? spec
+ end
+
+ def test_match_eh_prerelease_allow_prerelease
+ spec = util_spec 'a', '2.a'
+
+ a_dep = dep 'a', '>= 1'
+ a_dr = @DR.new a_dep, nil
+
+ assert a_dr.match? spec, true
+ end
+
+ def test_matches_spec_eh
+ spec = util_spec 'a', 1
+ dependency = dep 'a', '>= 1'
+
+ dr = @DR.new dependency, nil
+
+ assert dr.matches_spec? spec
+ end
+
+ def test_matches_spec_eh_prerelease
+ spec = util_spec 'a', '1.a'
+
+ dependency = dep 'a', '>= 0'
+ dr = @DR.new dependency, nil
+
+ assert dr.matches_spec? spec
+ end
+
+ def test_requirement
+ dependency = dep 'a', '>= 1'
+
+ dr = @DR.new dependency, nil
+
+ assert_equal dependency, dr.dependency
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_git_set.rb b/jni/ruby/test/rubygems/test_gem_resolver_git_set.rb
new file mode 100644
index 0000000..3659193
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_git_set.rb
@@ -0,0 +1,189 @@
+require 'rubygems/test_case'
+
+class TestGemResolverGitSet < Gem::TestCase
+
+ def setup
+ super
+
+ @set = Gem::Resolver::GitSet.new
+
+ @reqs = Gem::Resolver::RequirementList.new
+ end
+
+ def test_add_git_gem
+ name, version, repository, = git_gem
+
+ @set.add_git_gem name, repository, 'master', false
+
+ dependency = dep 'a'
+
+ specs = @set.find_all dependency
+
+ assert_equal "#{name}-#{version}", specs.first.full_name
+
+ refute @set.need_submodules[repository]
+ end
+
+ def test_add_git_gem_submodules
+ name, _, repository, = git_gem
+
+ @set.add_git_gem name, repository, 'master', true
+
+ dependency = dep 'a'
+
+ refute_empty @set.find_all dependency
+
+ assert @set.need_submodules[repository]
+ end
+
+ def test_add_git_spec
+ name, version, repository, revision = git_gem
+
+ @set.add_git_spec name, version, repository, revision, true
+
+ dependency = dep 'a'
+
+ specs = @set.find_all dependency
+
+ spec = specs.first
+
+ assert_equal "#{name}-#{version}", spec.full_name
+
+ assert @set.need_submodules[repository]
+
+ refute_path_exists spec.source.repo_cache_dir
+ end
+
+ def test_find_all
+ name, _, repository, = git_gem
+
+ @set.add_git_gem name, repository, 'master', false
+
+ dependency = dep 'a', '~> 1.0'
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ found = @set.find_all dependency
+
+ assert_equal [@set.specs['a']], found
+ end
+
+ def test_find_all_local
+ name, _, repository, = git_gem
+
+ @set.add_git_gem name, repository, 'master', false
+ @set.remote = false
+
+ dependency = dep 'a', '~> 1.0'
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ assert_empty @set.find_all dependency
+ end
+
+ def test_find_all_prerelease
+ name, _, repository, = git_gem 'a', '1.a'
+
+ @set.add_git_gem name, repository, 'master', false
+
+ dependency = dep 'a', '>= 0'
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ found = @set.find_all dependency
+
+ assert_empty found
+
+ dependency = dep 'a', '>= 0.a'
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ found = @set.find_all dependency
+
+ refute_empty found
+ end
+
+ def test_root_dir
+ assert_equal Gem.dir, @set.root_dir
+
+ @set.root_dir = "#{@gemhome}2"
+
+ assert_equal "#{@gemhome}2", @set.root_dir
+ end
+
+ def test_prefetch
+ name, _, repository, = git_gem
+
+ @set.add_git_gem name, repository, 'master', false
+
+ dependency = dep name
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ refute_empty @set.specs
+ end
+
+ def test_prefetch_cache
+ name, _, repository, = git_gem
+
+ @set.add_git_gem name, repository, 'master', false
+
+ dependency = dep name
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ spec = @set.specs[name]
+
+ @set.prefetch @reqs
+
+ assert_same spec, @set.specs[name]
+ end
+
+ def test_prefetch_filter
+ name, _, repository, = git_gem
+
+ @set.add_git_gem name, repository, 'master', false
+
+ dependency = dep 'b'
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ refute_empty @set.specs, 'the git source does not filter'
+ end
+
+ def test_prefetch_root_dir
+ name, _, repository, = git_gem
+
+ @set.add_git_gem name, repository, 'master', false
+
+ dependency = dep name
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.root_dir = "#{@gemhome}2"
+
+ @set.prefetch @reqs
+
+ refute_empty @set.specs
+
+ spec = @set.specs.values.first
+
+ assert_equal "#{@gemhome}2", spec.source.root_dir
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_git_specification.rb b/jni/ruby/test/rubygems/test_gem_resolver_git_specification.rb
new file mode 100644
index 0000000..a31674a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_git_specification.rb
@@ -0,0 +1,112 @@
+require 'rubygems/test_case'
+
+class TestGemResolverGitSpecification < Gem::TestCase
+
+ def setup
+ super
+
+ @set = Gem::Resolver::GitSet.new
+ @spec = Gem::Specification.new 'a', 1
+ end
+
+ def test_equals2
+ g_spec_a = Gem::Resolver::GitSpecification.new @set, @spec
+
+ assert_equal g_spec_a, g_spec_a
+
+ spec_b = Gem::Specification.new 'b', 1
+ g_spec_b = Gem::Resolver::GitSpecification.new @set, spec_b
+
+ refute_equal g_spec_a, g_spec_b
+
+ g_set = Gem::Resolver::GitSet.new
+ g_spec_s = Gem::Resolver::GitSpecification.new g_set, @spec
+
+ refute_equal g_spec_a, g_spec_s
+
+ i_set = Gem::Resolver::IndexSet.new
+ source = Gem::Source.new @gem_repo
+ i_spec = Gem::Resolver::IndexSpecification.new(
+ i_set, 'a', v(1), source, Gem::Platform::RUBY)
+
+ refute_equal g_spec_a, i_spec
+ end
+
+ def test_add_dependency
+ git_gem 'a', 1
+
+ git_spec = Gem::Resolver::GitSpecification.new @set, @spec
+
+ b_dep = dep 'b'
+
+ git_spec.add_dependency b_dep
+
+ assert_equal [b_dep], git_spec.dependencies
+ end
+
+ def test_install
+ git_gem 'a', 1
+
+ git_spec = Gem::Resolver::GitSpecification.new @set, @spec
+
+ called = false
+
+ git_spec.install({}) do |installer|
+ called = installer
+ end
+
+ assert called
+ end
+
+ # functional test for Gem::Ext::Builder
+
+ def test_install_extension
+ name, _, repository, = git_gem 'a', 1 do |s|
+ s.extensions << 'ext/extconf.rb'
+ end
+
+ Dir.chdir 'git/a' do
+ FileUtils.mkdir_p 'ext/lib'
+
+ open 'ext/extconf.rb', 'w' do |io|
+ io.puts 'require "mkmf"'
+ io.puts 'create_makefile "a"'
+ end
+
+ FileUtils.touch 'ext/lib/b.rb'
+
+ system @git, 'add', 'ext/extconf.rb'
+ system @git, 'add', 'ext/lib/b.rb'
+
+ system @git, 'commit', '--quiet', '-m', 'Add extension files'
+ end
+
+ source = Gem::Source::Git.new name, repository, 'master', true
+
+ spec = source.specs.first
+
+ git_spec = Gem::Resolver::GitSpecification.new @set, spec, source
+
+ git_spec.install({})
+
+ assert_path_exists File.join git_spec.spec.extension_dir, 'b.rb'
+ end
+
+ def test_install_installed
+ git_gem 'a', 1
+
+ git_spec = Gem::Resolver::GitSpecification.new @set, @spec
+
+ git_spec.install({})
+
+ called = false
+
+ git_spec.install({}) do |installer|
+ called = installer
+ end
+
+ assert called
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_index_set.rb b/jni/ruby/test/rubygems/test_gem_resolver_index_set.rb
new file mode 100644
index 0000000..04ef884
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_index_set.rb
@@ -0,0 +1,89 @@
+require 'rubygems/test_case'
+
+class TestGemResolverIndexSet < Gem::TestCase
+
+ def setup
+ super
+
+ @DR = Gem::Resolver
+ end
+
+ def test_initialize
+ set = @DR::IndexSet.new
+
+ fetcher = set.instance_variable_get :@f
+
+ assert_same Gem::SpecFetcher.fetcher, fetcher
+ end
+
+ def test_initialize_source
+ set = @DR::IndexSet.new 'http://alternate.example'
+
+ fetcher = set.instance_variable_get :@f
+
+ refute_same Gem::SpecFetcher.fetcher, fetcher
+
+ refute_empty set.errors
+ end
+
+ def test_find_all
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'b', 1
+ end
+
+ set = @DR::IndexSet.new
+
+ dependency = dep 'a', '~> 1'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_equal %w[a-1], found.map { |s| s.full_name }
+ end
+
+ def test_find_all_local
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'b', 1
+ end
+
+ set = @DR::IndexSet.new
+ set.remote = false
+
+ dependency = dep 'a', '~> 1'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ assert_empty set.find_all req
+ end
+
+ def test_find_all_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', '1.a'
+ end
+
+ set = @DR::IndexSet.new
+
+ dependency = dep 'a'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_empty found
+
+ dependency.prerelease = true
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_equal %w[a-1.a], found.map { |s| s.full_name }
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_index_specification.rb b/jni/ruby/test/rubygems/test_gem_resolver_index_specification.rb
new file mode 100644
index 0000000..e52f9c7
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_index_specification.rb
@@ -0,0 +1,89 @@
+require 'rubygems/test_case'
+require 'rubygems/available_set'
+
+class TestGemResolverIndexSpecification < Gem::TestCase
+
+ def test_initialize
+ set = Gem::Resolver::IndexSet.new
+ source = Gem::Source.new @gem_repo
+ version = Gem::Version.new '3.0.3'
+
+ spec = Gem::Resolver::IndexSpecification.new(
+ set, 'rails', version, source, Gem::Platform::RUBY)
+
+ assert_equal 'rails', spec.name
+ assert_equal version, spec.version
+ assert_equal Gem::Platform::RUBY, spec.platform
+
+ assert_equal source, spec.source
+ end
+
+ def test_initialize_platform
+ set = Gem::Resolver::IndexSet.new
+ source = Gem::Source::Local.new
+ version = Gem::Version.new '3.0.3'
+
+ spec = Gem::Resolver::IndexSpecification.new(
+ set, 'rails', version, source, Gem::Platform.local)
+
+ assert_equal Gem::Platform.local.to_s, spec.platform
+ end
+
+ def test_install
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ set = Gem::Resolver::IndexSet.new
+ source = Gem::Source.new @gem_repo
+
+ spec = Gem::Resolver::IndexSpecification.new(
+ set, 'a', v(2), source, Gem::Platform::RUBY)
+
+ called = false
+
+ spec.install({}) do |installer|
+ called = installer
+ end
+
+ assert_path_exists File.join @gemhome, 'specifications', 'a-2.gemspec'
+
+ assert_kind_of Gem::Installer, called
+ end
+
+ def test_spec
+ specs = spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end
+ end
+
+ source = Gem::Source.new @gem_repo
+ version = v 2
+
+ set = Gem::Resolver::IndexSet.new
+ i_spec = Gem::Resolver::IndexSpecification.new \
+ set, 'a', version, source, Gem::Platform.local
+
+ spec = i_spec.spec
+
+ assert_equal specs["a-2-#{Gem::Platform.local}"].full_name, spec.full_name
+ end
+
+ def test_spec_local
+ a_2_p = util_spec 'a', 2 do |s| s.platform = Gem::Platform.local end
+ Gem::Package.build a_2_p
+
+ source = Gem::Source::Local.new
+ set = Gem::Resolver::InstallerSet.new :local
+ set.always_install << a_2_p
+
+ i_spec = Gem::Resolver::IndexSpecification.new \
+ set, 'a', v(2), source, Gem::Platform.local
+
+ spec = i_spec.spec
+
+ assert_equal a_2_p.full_name, spec.full_name
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_installed_specification.rb b/jni/ruby/test/rubygems/test_gem_resolver_installed_specification.rb
new file mode 100644
index 0000000..d52876f
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_installed_specification.rb
@@ -0,0 +1,49 @@
+require 'rubygems/test_case'
+
+class TestGemResolverInstalledSpecification < Gem::TestCase
+
+ def setup
+ super
+
+ @set = Gem::Resolver::CurrentSet.new
+ end
+
+ def test_initialize
+ source_spec = util_spec 'a'
+
+ spec = Gem::Resolver::InstalledSpecification.new @set, source_spec
+
+ assert_equal 'a', spec.name
+ assert_equal Gem::Version.new(2), spec.version
+ assert_equal Gem::Platform::RUBY, spec.platform
+ end
+
+ def test_install
+ a = util_spec 'a'
+
+ spec = Gem::Resolver::InstalledSpecification.new @set, a
+
+ called = :junk
+
+ spec.install({}) do |installer|
+ called = installer
+ end
+
+ assert_nil called
+ end
+
+ def test_installable_platform_eh
+ b, b_gem = util_gem 'a', 1 do |s|
+ s.platform = Gem::Platform.new %w[cpu other_platform 1]
+ end
+
+ source = Gem::Source::SpecificFile.new b_gem
+
+ b_spec = Gem::Resolver::InstalledSpecification.new @set, b, source
+
+ assert b_spec.installable_platform?
+ end
+
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_installer_set.rb b/jni/ruby/test/rubygems/test_gem_resolver_installer_set.rb
new file mode 100644
index 0000000..d7b917b
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_installer_set.rb
@@ -0,0 +1,261 @@
+require 'rubygems/test_case'
+
+class TestGemResolverInstallerSet < Gem::TestCase
+
+ def test_add_always_install
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.clear
+ end
+
+ util_gem 'a', 1
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_always_install dep('a')
+
+ assert_equal %w[a-2], set.always_install.map { |s| s.full_name }
+
+ e = assert_raises Gem::UnsatisfiableDependencyError do
+ set.add_always_install dep('b')
+ end
+
+ assert_equal dep('b'), e.dependency.dependency
+ end
+
+ def test_add_always_install_errors
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ e = assert_raises Gem::UnsatisfiableDependencyError do
+ set.add_always_install dep 'a'
+ end
+
+ refute_empty e.errors
+ end
+
+ def test_add_always_install_platform
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2 do |s|
+ s.platform = Gem::Platform.new 'x86-freebsd-9'
+ end
+ fetcher.clear
+ end
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_always_install dep('a')
+
+ assert_equal %w[a-1], set.always_install.map { |s| s.full_name }
+ end
+
+ def test_add_always_install_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', '3.a'
+ end
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_always_install dep('a')
+
+ assert_equal %w[a-1], set.always_install.map { |s| s.full_name }
+ end
+
+ def test_add_always_install_prerelease_only
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '3.a'
+ end
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ assert_raises Gem::UnsatisfiableDependencyError do
+ set.add_always_install dep('a')
+ end
+ end
+
+ def test_add_local
+ a_1, a_1_gem = util_gem 'a', 1
+
+ a_1_source = Gem::Source::SpecificFile.new a_1_gem
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_local File.basename(a_1_gem), a_1, a_1_source
+
+ assert set.local? File.basename(a_1_gem)
+
+ FileUtils.rm a_1_gem
+ util_clear_gems
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_equal %w[a-1], set.find_all(req).map { |spec| spec.full_name }
+ end
+
+ def test_consider_local_eh
+ set = Gem::Resolver::InstallerSet.new :remote
+
+ refute set.consider_local?
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ assert set.consider_local?
+
+ set = Gem::Resolver::InstallerSet.new :local
+
+ assert set.consider_local?
+ end
+
+ def test_consider_remote_eh
+ set = Gem::Resolver::InstallerSet.new :remote
+
+ assert set.consider_remote?
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ assert set.consider_remote?
+
+ set = Gem::Resolver::InstallerSet.new :local
+
+ refute set.consider_remote?
+ end
+
+ def test_errors
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.instance_variable_get(:@errors) << :a
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ set.find_all req
+
+ assert_equal [:a, set.remote_set.errors.first], set.errors
+ end
+
+ def test_find_all_always_install
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ fetcher.clear
+ end
+
+ util_gem 'a', 1
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_always_install dep 'a'
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_equal %w[a-2], set.find_all(req).map { |spec| spec.full_name }
+ end
+
+ def test_find_all_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', '1'
+ fetcher.spec 'a', '1.a'
+ fetcher.clear
+ end
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_equal %w[a-1], set.find_all(req).map { |spec| spec.full_name }
+
+ req = Gem::Resolver::DependencyRequest.new dep('a', '>= 0.a'), nil
+
+ assert_equal %w[a-1 a-1.a],
+ set.find_all(req).map { |spec| spec.full_name }.sort
+ end
+
+ def test_load_spec
+ specs = spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end
+ end
+
+ source = Gem::Source.new @gem_repo
+ version = v 2
+
+ set = Gem::Resolver::InstallerSet.new :remote
+
+ spec = set.load_spec 'a', version, Gem::Platform.local, source
+
+ assert_equal specs["a-2-#{Gem::Platform.local}"].full_name, spec.full_name
+ end
+
+ def test_prefetch
+ set = Gem::Resolver::InstallerSet.new :remote
+ def (set.remote_set).prefetch(_)
+ raise "called"
+ end
+ assert_raises(RuntimeError){ set.prefetch(nil) }
+
+ set = Gem::Resolver::InstallerSet.new :local
+ def (set.remote_set).prefetch(_)
+ raise "called"
+ end
+ assert_equal nil, set.prefetch(nil)
+ end
+
+ def test_prerelease_equals
+ set = Gem::Resolver::InstallerSet.new :remote
+
+ refute set.prerelease
+ refute set.remote_set.prerelease
+
+ set.prerelease = true
+
+ assert set.prerelease
+ assert set.remote_set.prerelease
+ end
+
+ def test_remote_equals_both
+ set = Gem::Resolver::InstallerSet.new :both
+ set.remote = true
+
+ assert set.consider_local?
+ assert set.consider_remote?
+
+ set = Gem::Resolver::InstallerSet.new :both
+ set.remote = false
+
+ assert set.consider_local?
+ refute set.consider_remote?
+ end
+
+ def test_remote_equals_local
+ set = Gem::Resolver::InstallerSet.new :local
+ set.remote = true
+
+ assert set.consider_local?
+ assert set.consider_remote?
+
+ set = Gem::Resolver::InstallerSet.new :local
+ set.remote = false
+
+ assert set.consider_local?
+ refute set.consider_remote?
+ end
+
+ def test_remote_equals_remote
+ set = Gem::Resolver::InstallerSet.new :remote
+ set.remote = true
+
+ refute set.consider_local?
+ assert set.consider_remote?
+
+ set = Gem::Resolver::InstallerSet.new :remote
+ set.remote = false
+
+ refute set.consider_local?
+ refute set.consider_remote?
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_local_specification.rb b/jni/ruby/test/rubygems/test_gem_resolver_local_specification.rb
new file mode 100644
index 0000000..fc3175a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_local_specification.rb
@@ -0,0 +1,45 @@
+require 'rubygems/test_case'
+require 'rubygems/available_set'
+
+class TestGemResolverLocalSpecification < Gem::TestCase
+
+ def setup
+ super
+
+ @set = Gem::AvailableSet.new
+ end
+
+ def test_install
+ specs = spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ source = Gem::Source::SpecificFile.new 'gems/a-2.gem'
+
+ spec = Gem::Resolver::LocalSpecification.new @set, specs['a-2'], source
+
+ called = false
+
+ spec.install({}) do |installer|
+ called = installer
+ end
+
+ assert_path_exists File.join @gemhome, 'specifications', 'a-2.gemspec'
+
+ assert_kind_of Gem::Installer, called
+ end
+
+ def test_installable_platform_eh
+ b, b_gem = util_gem 'a', 1 do |s|
+ s.platform = Gem::Platform.new %w[cpu other_platform 1]
+ end
+
+ source = Gem::Source::SpecificFile.new b_gem
+
+ b_spec = Gem::Resolver::InstalledSpecification.new @set, b, source
+
+ assert b_spec.installable_platform?
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_lock_set.rb b/jni/ruby/test/rubygems/test_gem_resolver_lock_set.rb
new file mode 100644
index 0000000..fdcb8ff
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_lock_set.rb
@@ -0,0 +1,63 @@
+require 'rubygems/test_case'
+
+class TestGemResolverLockSet < Gem::TestCase
+
+ def setup
+ super
+
+ @sources = [Gem::Source.new(@gem_repo)]
+ @lock_source = Gem::Source::Lock.new @sources.first
+
+ @set = Gem::Resolver::LockSet.new @sources
+ end
+
+ def test_add
+ specs = @set.add 'a', '2', Gem::Platform::RUBY
+ spec = specs.first
+
+ assert_equal %w[a-2], @set.specs.map { |t| t.full_name }
+
+ assert_kind_of Gem::Resolver::LockSpecification, spec
+
+ assert_equal @set, spec.set
+ assert_equal 'a', spec.name
+ assert_equal v(2), spec.version
+ assert_equal Gem::Platform::RUBY, spec.platform
+ assert_equal @lock_source, spec.source
+ end
+
+ def test_find_all
+ @set.add 'a', '1.a', Gem::Platform::RUBY
+ @set.add 'a', '2', Gem::Platform::RUBY
+ @set.add 'b', '2', Gem::Platform::RUBY
+
+ found = @set.find_all dep 'a'
+
+ assert_equal %w[a-2], found.map { |s| s.full_name }
+
+ found = @set.find_all dep 'a', '>= 0.a'
+
+ assert_equal %w[a-1.a a-2], found.map { |s| s.full_name }
+ end
+
+ def test_load_spec
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ end
+
+ version = v(2)
+ @set.add 'a', version, Gem::Platform::RUBY
+
+ loaded = @set.load_spec 'a', version, Gem::Platform::RUBY, nil
+
+ assert_kind_of Gem::Specification, loaded
+
+ assert_equal 'a-2', loaded.full_name
+ end
+
+ def test_prefetch
+ assert_respond_to @set, :prefetch
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_lock_specification.rb b/jni/ruby/test/rubygems/test_gem_resolver_lock_specification.rb
new file mode 100644
index 0000000..f8a336e
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_lock_specification.rb
@@ -0,0 +1,98 @@
+require 'rubygems/test_case'
+require 'rubygems/resolver'
+
+class TestGemResolverLockSpecification < Gem::TestCase
+
+ def setup
+ super
+
+ @LS = Gem::Resolver::LockSpecification
+
+ @source = Gem::Source.new @gem_repo
+ @set = Gem::Resolver::LockSet.new [@source]
+ end
+
+ def test_initialize
+ spec = @LS.new @set, 'a', v(2), @source, Gem::Platform::RUBY
+
+ assert_equal 'a', spec.name
+ assert_equal v(2), spec.version
+ assert_equal Gem::Platform::RUBY, spec.platform
+
+ assert_equal @source, spec.source
+ end
+
+ def test_add_dependency
+ l_spec = @LS.new @set, 'a', v(2), @source, Gem::Platform::RUBY
+
+ b_dep = dep('b', '>= 0')
+
+ l_spec.add_dependency b_dep
+
+ assert_equal [b_dep], l_spec.dependencies
+ end
+
+ def test_install
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.clear
+ end
+
+ spec = @LS.new @set, 'a', v(2), @source, Gem::Platform::RUBY
+
+ called = false
+
+ spec.install({}) do |installer|
+ called = installer
+ end
+
+ refute_nil called
+ end
+
+ def test_install_installed
+ spec = @LS.new @set, 'a', v(2), @source, Gem::Platform::RUBY
+
+ FileUtils.touch File.join(@gemhome, 'specifications', spec.spec.spec_name)
+
+ called = false
+
+ spec.install({}) do |installer|
+ called = installer
+ end
+
+ assert_nil called
+ end
+
+ def test_spec
+ version = v(2)
+
+ l_spec = @LS.new @set, 'a', version, @source, Gem::Platform::RUBY
+
+ b_dep = dep 'b', '>= 0'
+ c_dep = dep 'c', '~> 1'
+
+ l_spec.add_dependency b_dep
+ l_spec.add_dependency c_dep
+
+ spec = l_spec.spec
+
+ assert_equal 'a', spec.name
+ assert_equal version, spec.version
+ assert_equal Gem::Platform::RUBY, spec.platform
+
+ assert_equal [b_dep, c_dep], l_spec.spec.dependencies
+ end
+
+ def test_spec_loaded
+ real_spec = util_spec 'a', 2
+ real_spec.activate
+
+ version = v(2)
+
+ l_spec = @LS.new @set, 'a', version, @source, Gem::Platform::RUBY
+
+ assert_same real_spec, l_spec.spec
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_requirement_list.rb b/jni/ruby/test/rubygems/test_gem_resolver_requirement_list.rb
new file mode 100644
index 0000000..fd9dccb
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_requirement_list.rb
@@ -0,0 +1,20 @@
+require 'rubygems/test_case'
+
+class TestGemResolverRequirementList < Gem::TestCase
+
+ def setup
+ super
+
+ @list = Gem::Resolver::RequirementList.new
+ end
+
+ def test_each
+ dep = Gem::Dependency.new "a", "= 1"
+ req = Gem::Resolver::DependencyRequest.new(dep, nil)
+ @list.add req
+
+ assert_equal [req], @list.each.to_a
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_specification.rb b/jni/ruby/test/rubygems/test_gem_resolver_specification.rb
new file mode 100644
index 0000000..e1ec68a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_specification.rb
@@ -0,0 +1,64 @@
+require 'rubygems/test_case'
+
+class TestGemResolverSpecification < Gem::TestCase
+
+ class TestSpec < Gem::Resolver::Specification
+ attr_writer :source
+ attr_reader :spec
+
+ def initialize spec
+ super()
+
+ @spec = spec
+ end
+ end
+
+ def test_install
+ gemhome = "#{@gemhome}2"
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ end
+
+ a = util_spec 'a', 1
+
+ a_spec = TestSpec.new a
+ a_spec.source = Gem::Source.new @gem_repo
+
+ a_spec.install :install_dir => gemhome
+
+ assert_path_exists File.join gemhome, 'gems', a.full_name
+
+ expected = File.join gemhome, 'specifications', a.spec_name
+
+ assert_equal expected, a_spec.spec.loaded_from
+ end
+
+ def test_installable_platform_eh
+ a = util_spec 'a', 1
+
+ a_spec = TestSpec.new a
+
+ assert a_spec.installable_platform?
+
+ b = util_spec 'a', 1 do |s|
+ s.platform = Gem::Platform.new %w[cpu other_platform 1]
+ end
+
+ b_spec = TestSpec.new b
+
+ refute b_spec.installable_platform?
+ end
+
+ def test_source
+ a = util_spec 'a', 1
+
+ source = Gem::Source.new @gem_repo
+
+ a_spec = TestSpec.new a
+ a_spec.source = source
+
+ assert_equal source, a_spec.source
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_vendor_set.rb b/jni/ruby/test/rubygems/test_gem_resolver_vendor_set.rb
new file mode 100644
index 0000000..618e251
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_vendor_set.rb
@@ -0,0 +1,83 @@
+require 'rubygems/test_case'
+
+class TestGemResolverVendorSet < Gem::TestCase
+
+ def setup
+ super
+
+ @set = Gem::Resolver::VendorSet.new
+ end
+
+ def test_add_vendor_gem
+ name, version, directory = vendor_gem
+
+ added = @set.add_vendor_gem name, directory
+
+ spec = @set.load_spec name, version, Gem::Platform::RUBY, nil
+
+ assert_equal spec, added
+
+ assert_equal "#{name}-#{version}", spec.full_name
+
+ assert_equal File.expand_path(directory), spec.full_gem_path
+ end
+
+ def test_add_vendor_gem_missing
+ name, _, directory = vendor_gem
+
+ FileUtils.rm_r directory
+
+ e = assert_raises Gem::GemNotFoundException do
+ @set.add_vendor_gem name, directory
+ end
+
+ assert_equal "unable to find #{directory}/#{name}.gemspec for gem #{name}",
+ e.message
+ end
+
+ def test_find_all
+ name, version, directory = vendor_gem
+
+ @set.add_vendor_gem name, directory
+
+ dependency = dep 'a', '~> 1'
+
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+
+ found = @set.find_all req
+
+ spec = @set.load_spec name, version, Gem::Platform::RUBY, nil
+
+ source = Gem::Source::Vendor.new directory
+
+ expected = [
+ Gem::Resolver::VendorSpecification.new(@set, spec, source)
+ ]
+
+ assert_equal expected, found
+ end
+
+ def test_find_all_prerelease
+ name, _, directory = vendor_gem 'a', '1.a'
+
+ @set.add_vendor_gem name, directory
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_empty @set.find_all req
+
+ req = Gem::Resolver::DependencyRequest.new dep('a', '>= 0.a'), nil
+
+ refute_empty @set.find_all req
+ end
+
+ def test_load_spec
+ error = Object.const_defined?(:KeyError) ? KeyError : IndexError
+
+ assert_raises error do
+ @set.load_spec 'b', v(1), Gem::Platform::RUBY, nil
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_resolver_vendor_specification.rb b/jni/ruby/test/rubygems/test_gem_resolver_vendor_specification.rb
new file mode 100644
index 0000000..3d94cfe
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_resolver_vendor_specification.rb
@@ -0,0 +1,83 @@
+require 'rubygems/test_case'
+
+class TestGemResolverVendorSpecification < Gem::TestCase
+
+ def setup
+ super
+
+ @set = Gem::Resolver::VendorSet.new
+ @spec = Gem::Specification.new 'a', 1
+ end
+
+ def test_equals2
+ v_spec_a = Gem::Resolver::VendorSpecification.new @set, @spec
+
+ assert_equal v_spec_a, v_spec_a
+
+ spec_b = Gem::Specification.new 'b', 1
+ v_spec_b = Gem::Resolver::VendorSpecification.new @set, spec_b
+
+ refute_equal v_spec_a, v_spec_b
+
+ v_set = Gem::Resolver::VendorSet.new
+ v_spec_s = Gem::Resolver::VendorSpecification.new v_set, @spec
+
+ refute_equal v_spec_a, v_spec_s
+
+ i_set = Gem::Resolver::IndexSet.new
+ source = Gem::Source.new @gem_repo
+ i_spec = Gem::Resolver::IndexSpecification.new(
+ i_set, 'a', v(1), source, Gem::Platform::RUBY)
+
+ refute_equal v_spec_a, i_spec
+ end
+
+ def test_dependencies
+ @spec.add_dependency 'b'
+ @spec.add_dependency 'c'
+
+ v_spec = Gem::Resolver::VendorSpecification.new @set, @spec
+
+ assert_equal [dep('b'), dep('c')], v_spec.dependencies
+ end
+
+ def test_full_name
+ v_spec = Gem::Resolver::VendorSpecification.new @set, @spec
+
+ assert_equal 'a-1', v_spec.full_name
+ end
+
+ def test_install
+ spec = Gem::Resolver::VendorSpecification.new @set, @spec
+
+ called = :junk
+
+ spec.install({}) do |installer|
+ called = installer
+ end
+
+ assert_nil called
+ end
+
+ def test_name
+ v_spec = Gem::Resolver::VendorSpecification.new @set, @spec
+
+ assert_equal 'a', v_spec.name
+ end
+
+ def test_platform
+ v_spec = Gem::Resolver::VendorSpecification.new @set, @spec
+
+ assert_equal Gem::Platform::RUBY, v_spec.platform
+ end
+
+ def test_version
+ spec = Gem::Specification.new 'a', 1
+
+ v_spec = Gem::Resolver::VendorSpecification.new @set, spec
+
+ assert_equal v(1), v_spec.version
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_security.rb b/jni/ruby/test/rubygems/test_gem_security.rb
new file mode 100644
index 0000000..b8747b7
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_security.rb
@@ -0,0 +1,306 @@
+require 'rubygems/test_case'
+require 'rubygems/security'
+require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
+
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping Gem::Security tests. openssl not found.'
+end
+
+class TestGemSecurity < Gem::TestCase
+
+ CHILD_KEY = load_key 'child'
+
+ ALTERNATE_CERT = load_cert 'child'
+ CHILD_CERT = load_cert 'child'
+ EXPIRED_CERT = load_cert 'expired'
+
+ def setup
+ super
+
+ @SEC = Gem::Security
+ end
+
+ def test_class_create_cert
+ name = PUBLIC_CERT.subject
+ key = PRIVATE_KEY
+
+ cert = @SEC.create_cert name, key, 60, Gem::Security::EXTENSIONS, 5
+
+ assert_kind_of OpenSSL::X509::Certificate, cert
+
+ assert_equal 2, cert.version
+ assert_equal 5, cert.serial
+ assert_equal key.public_key.to_pem, cert.public_key.to_pem
+ assert_in_delta Time.now, cert.not_before, 10
+ assert_in_delta Time.now + 60, cert.not_after, 10
+ assert_equal name.to_s, cert.subject.to_s
+
+ assert_equal 3, cert.extensions.length,
+ cert.extensions.map { |e| e.to_a.first }
+
+ constraints = cert.extensions.find { |ext| ext.oid == 'basicConstraints' }
+ assert_equal 'CA:FALSE', constraints.value
+
+ key_usage = cert.extensions.find { |ext| ext.oid == 'keyUsage' }
+ assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
+ key_usage.value
+
+ key_ident = cert.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
+ assert_equal 59, key_ident.value.length
+ assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
+ key_ident.value
+
+ assert_equal '', cert.issuer.to_s
+ assert_equal name.to_s, cert.subject.to_s
+ end
+
+ def test_class_create_cert_self_signed
+ subject = PUBLIC_CERT.subject
+
+ cert = @SEC.create_cert_self_signed subject, PRIVATE_KEY, 60
+
+ assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
+ end
+
+ def test_class_create_cert_email
+ email = 'nobody@example'
+ name = PUBLIC_CERT.subject
+ key = PRIVATE_KEY
+
+ cert = @SEC.create_cert_email email, key, 60
+
+ assert_kind_of OpenSSL::X509::Certificate, cert
+
+ assert_equal 2, cert.version
+ assert_equal 1, cert.serial
+ assert_equal key.public_key.to_pem, cert.public_key.to_pem
+ assert_in_delta Time.now, cert.not_before, 10
+ assert_in_delta Time.now + 60, cert.not_after, 10
+ assert_equal name.to_s, cert.subject.to_s
+ assert_equal name.to_s, cert.issuer.to_s
+
+ assert_equal 5, cert.extensions.length,
+ cert.extensions.map { |e| e.to_a.first }
+
+ constraints = cert.extensions.find { |ext| ext.oid == 'subjectAltName' }
+ assert_equal 'email:nobody@example', constraints.value
+
+ constraints = cert.extensions.find { |ext| ext.oid == 'basicConstraints' }
+ assert_equal 'CA:FALSE', constraints.value
+
+ key_usage = cert.extensions.find { |ext| ext.oid == 'keyUsage' }
+ assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
+ key_usage.value
+
+ key_ident = cert.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
+ assert_equal 59, key_ident.value.length
+ assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
+ key_ident.value
+ end
+
+ def test_class_create_key
+ key = @SEC.create_key 1024
+
+ assert_kind_of OpenSSL::PKey::RSA, key
+ end
+
+ def test_class_email_to_name
+ assert_equal '/CN=nobody/DC=example',
+ @SEC.email_to_name('nobody@example').to_s
+
+ assert_equal '/CN=nobody/DC=example/DC=com',
+ @SEC.email_to_name('nobody@example.com').to_s
+
+ assert_equal '/CN=no.body/DC=example',
+ @SEC.email_to_name('no.body@example').to_s
+
+ assert_equal '/CN=no_body/DC=example',
+ @SEC.email_to_name('no+body@example').to_s
+ end
+
+ def test_class_re_sign
+ re_signed = Gem::Security.re_sign EXPIRED_CERT, PRIVATE_KEY, 60
+
+ assert_in_delta Time.now, re_signed.not_before, 10
+ assert_in_delta Time.now + 60, re_signed.not_after, 10
+ assert_equal EXPIRED_CERT.serial + 1, re_signed.serial
+
+ assert re_signed.verify PUBLIC_KEY
+ end
+
+ def test_class_re_sign_not_self_signed
+ e = assert_raises Gem::Security::Exception do
+ Gem::Security.re_sign CHILD_CERT, CHILD_KEY
+ end
+
+ child_alt_name = CHILD_CERT.extensions.find do |extension|
+ extension.oid == 'subjectAltName'
+ end
+
+ assert_equal "#{child_alt_name.value} is not self-signed, contact " +
+ "#{ALTERNATE_CERT.issuer} to obtain a valid certificate",
+ e.message
+ end
+
+ def test_class_re_sign_wrong_key
+ e = assert_raises Gem::Security::Exception do
+ Gem::Security.re_sign ALTERNATE_CERT, PRIVATE_KEY
+ end
+
+ assert_equal "incorrect signing key for re-signing " +
+ "#{ALTERNATE_CERT.subject}",
+ e.message
+ end
+
+ def test_class_reset
+ trust_dir = @SEC.trust_dir
+
+ @SEC.reset
+
+ refute_equal trust_dir, @SEC.trust_dir
+ end
+
+ def test_class_sign
+ issuer = PUBLIC_CERT.subject
+ signee = OpenSSL::X509::Name.parse "/CN=signee/DC=example"
+
+ key = PRIVATE_KEY
+ cert = OpenSSL::X509::Certificate.new
+ cert.subject = signee
+
+ cert.subject = signee
+ cert.public_key = key.public_key
+
+ signed = @SEC.sign cert, key, PUBLIC_CERT, 60
+
+ assert_equal key.public_key.to_pem, signed.public_key.to_pem
+ assert_equal signee.to_s, signed.subject.to_s
+ assert_equal issuer.to_s, signed.issuer.to_s
+
+ assert_in_delta Time.now, signed.not_before, 10
+ assert_in_delta Time.now + 60, signed.not_after, 10
+
+ assert_equal 4, signed.extensions.length,
+ signed.extensions.map { |e| e.to_a.first }
+
+ constraints = signed.extensions.find { |ext| ext.oid == 'issuerAltName' }
+ assert_equal 'email:nobody@example', constraints.value, 'issuerAltName'
+
+ constraints = signed.extensions.find { |ext| ext.oid == 'basicConstraints' }
+ assert_equal 'CA:FALSE', constraints.value
+
+ key_usage = signed.extensions.find { |ext| ext.oid == 'keyUsage' }
+ assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
+ key_usage.value
+
+ key_ident =
+ signed.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
+ assert_equal 59, key_ident.value.length
+ assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
+ key_ident.value
+
+ assert signed.verify key
+ end
+
+ def test_class_sign_AltName
+ issuer = PUBLIC_CERT.subject
+ signee = OpenSSL::X509::Name.parse "/CN=signee/DC=example"
+
+ cert = @SEC.create_cert_email 'signee@example', PRIVATE_KEY
+
+ signed = @SEC.sign cert, PRIVATE_KEY, PUBLIC_CERT, 60
+
+ assert_equal PUBLIC_KEY.to_pem, signed.public_key.to_pem
+ assert_equal signee.to_s, signed.subject.to_s
+ assert_equal issuer.to_s, signed.issuer.to_s
+
+ assert_in_delta Time.now, signed.not_before, 10
+ assert_in_delta Time.now + 60, signed.not_after, 10
+
+ assert_equal 5, signed.extensions.length,
+ signed.extensions.map { |e| e.to_a.first }
+
+ constraints = signed.extensions.find { |ext| ext.oid == 'issuerAltName' }
+ assert_equal 'email:nobody@example', constraints.value, 'issuerAltName'
+
+ constraints = signed.extensions.find { |ext| ext.oid == 'subjectAltName' }
+ assert_equal 'email:signee@example', constraints.value, 'subjectAltName'
+
+ constraints = signed.extensions.find { |ext| ext.oid == 'basicConstraints' }
+ assert_equal 'CA:FALSE', constraints.value
+
+ key_usage = signed.extensions.find { |ext| ext.oid == 'keyUsage' }
+ assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
+ key_usage.value
+
+ key_ident =
+ signed.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
+ assert_equal 59, key_ident.value.length
+ assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
+ key_ident.value
+
+ assert signed.verify PUBLIC_KEY
+ end
+
+ def test_class_trust_dir
+ trust_dir = @SEC.trust_dir
+
+ expected = File.join Gem.user_home, '.gem/trust'
+
+ assert_equal expected, trust_dir.dir
+ end
+
+ def test_class_write
+ key = @SEC.create_key 1024
+
+ path = File.join @tempdir, 'test-private_key.pem'
+
+ @SEC.write key, path
+
+ assert_path_exists path
+
+ key_from_file = File.read path
+
+ assert_equal key.to_pem, key_from_file
+ end
+
+ def test_class_write_encrypted
+ key = @SEC.create_key 1024
+
+ path = File.join @tempdir, 'test-private_encrypted_key.pem'
+
+ passphrase = 'It should be long.'
+
+ @SEC.write key, path, 0600, passphrase
+
+ assert_path_exists path
+
+ key_from_file = OpenSSL::PKey::RSA.new File.read(path), passphrase
+
+ assert_equal key.to_pem, key_from_file.to_pem
+ end
+
+ def test_class_write_encrypted_cipher
+ key = @SEC.create_key 1024
+
+ path = File.join @tempdir, 'test-private_encrypted__with_non_default_cipher_key.pem'
+
+ passphrase = 'It should be long.'
+
+ cipher = OpenSSL::Cipher.new 'AES-192-CBC'
+
+ @SEC.write key, path, 0600, passphrase, cipher
+
+ assert_path_exists path
+
+ key_file_contents = File.read(path)
+
+ assert key_file_contents.split("\n")[2].match(cipher.name)
+
+ key_from_file = OpenSSL::PKey::RSA.new key_file_contents, passphrase
+
+ assert_equal key.to_pem, key_from_file.to_pem
+ end
+
+end if defined?(OpenSSL::SSL)
+
diff --git a/jni/ruby/test/rubygems/test_gem_security_policy.rb b/jni/ruby/test/rubygems/test_gem_security_policy.rb
new file mode 100644
index 0000000..d708306
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_security_policy.rb
@@ -0,0 +1,540 @@
+# coding: UTF-8
+
+require 'rubygems/test_case'
+
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping Gem::Security::Policy tests. openssl not found.'
+end
+
+class TestGemSecurityPolicy < Gem::TestCase
+
+ ALTERNATE_KEY = load_key 'alternate'
+ INVALID_KEY = load_key 'invalid'
+ CHILD_KEY = load_key 'child'
+ GRANDCHILD_KEY = load_key 'grandchild'
+ INVALIDCHILD_KEY = load_key 'invalidchild'
+
+ ALTERNATE_CERT = load_cert 'alternate'
+ CA_CERT = load_cert 'ca'
+ CHILD_CERT = load_cert 'child'
+ EXPIRED_CERT = load_cert 'expired'
+ FUTURE_CERT = load_cert 'future'
+ GRANDCHILD_CERT = load_cert 'grandchild'
+ INVALIDCHILD_CERT = load_cert 'invalidchild'
+ INVALID_ISSUER_CERT = load_cert 'invalid_issuer'
+ INVALID_SIGNER_CERT = load_cert 'invalid_signer'
+ WRONG_KEY_CERT = load_cert 'wrong_key'
+
+ def setup
+ super
+
+ @spec = quick_gem 'a' do |s|
+ s.description = 'π'
+ s.files = %w[lib/code.rb]
+ end
+
+ @sha1 = OpenSSL::Digest::SHA1
+ @trust_dir = Gem::Security.trust_dir.dir # HACK use the object
+
+ @no = Gem::Security::NoSecurity
+ @almost_no = Gem::Security::AlmostNoSecurity
+ @low = Gem::Security::LowSecurity
+ @medium = Gem::Security::MediumSecurity
+ @high = Gem::Security::HighSecurity
+
+ @chain = Gem::Security::Policy.new(
+ 'Chain',
+ :verify_data => true,
+ :verify_signer => true,
+ :verify_chain => true,
+ :verify_root => false,
+ :only_trusted => false,
+ :only_signed => false
+ )
+
+ @root = Gem::Security::Policy.new(
+ 'Root',
+ :verify_data => true,
+ :verify_signer => true,
+ :verify_chain => true,
+ :verify_root => true,
+ :only_trusted => false,
+ :only_signed => false
+ )
+ end
+
+ def test_check_data
+ data = digest 'hello'
+
+ signature = sign data
+
+ assert @almost_no.check_data(PUBLIC_KEY, @sha1, signature, data)
+ end
+
+ def test_check_data_invalid
+ data = digest 'hello'
+
+ signature = sign data
+
+ invalid = digest 'hello!'
+
+ e = assert_raises Gem::Security::Exception do
+ @almost_no.check_data PUBLIC_KEY, @sha1, signature, invalid
+ end
+
+ assert_equal 'invalid signature', e.message
+ end
+
+ def test_check_chain
+ chain = [PUBLIC_CERT, CHILD_CERT, GRANDCHILD_CERT]
+
+ assert @chain.check_chain chain, Time.now
+ end
+
+ def test_check_chain_empty_chain
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_chain [], Time.now
+ end
+
+ assert_equal 'empty signing chain', e.message
+ end
+
+ def test_check_chain_invalid
+ chain = [PUBLIC_CERT, CHILD_CERT, INVALIDCHILD_CERT]
+
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_chain chain, Time.now
+ end
+
+ assert_equal "invalid signing chain: " +
+ "certificate #{INVALIDCHILD_CERT.subject} " +
+ "was not issued by #{CHILD_CERT.subject}", e.message
+ end
+
+ def test_check_chain_no_chain
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_chain nil, Time.now
+ end
+
+ assert_equal 'missing signing chain', e.message
+ end
+
+ def test_check_cert
+ assert @low.check_cert(PUBLIC_CERT, nil, Time.now)
+ end
+
+ def test_check_cert_expired
+ e = assert_raises Gem::Security::Exception do
+ @low.check_cert EXPIRED_CERT, nil, Time.now
+ end
+
+ assert_equal "certificate #{EXPIRED_CERT.subject} " +
+ "not valid after #{EXPIRED_CERT.not_after}",
+ e.message
+ end
+
+ def test_check_cert_future
+ e = assert_raises Gem::Security::Exception do
+ @low.check_cert FUTURE_CERT, nil, Time.now
+ end
+
+ assert_equal "certificate #{FUTURE_CERT.subject} " +
+ "not valid before #{FUTURE_CERT.not_before}",
+ e.message
+ end
+
+ def test_check_cert_invalid_issuer
+ e = assert_raises Gem::Security::Exception do
+ @low.check_cert INVALID_ISSUER_CERT, PUBLIC_CERT, Time.now
+ end
+
+ assert_equal "certificate #{INVALID_ISSUER_CERT.subject} " +
+ "was not issued by #{PUBLIC_CERT.subject}",
+ e.message
+ end
+
+ def test_check_cert_issuer
+ assert @low.check_cert(CHILD_CERT, PUBLIC_CERT, Time.now)
+ end
+
+ def test_check_cert_no_signer
+ e = assert_raises Gem::Security::Exception do
+ @high.check_cert(nil, nil, Time.now)
+ end
+
+ assert_equal 'missing signing certificate', e.message
+ end
+
+ def test_check_key
+ assert @almost_no.check_key(PUBLIC_CERT, PRIVATE_KEY)
+ end
+
+ def test_check_key_no_signer
+ assert @almost_no.check_key(nil, nil)
+
+ e = assert_raises Gem::Security::Exception do
+ @high.check_key(nil, nil)
+ end
+
+ assert_equal 'missing key or signature', e.message
+ end
+
+ def test_check_key_wrong_key
+ e = assert_raises Gem::Security::Exception do
+ @almost_no.check_key(PUBLIC_CERT, ALTERNATE_KEY)
+ end
+
+ assert_equal "certificate #{PUBLIC_CERT.subject} " +
+ "does not match the signing key", e.message
+ end
+
+ def test_check_root
+ chain = [PUBLIC_CERT, CHILD_CERT, INVALIDCHILD_CERT]
+
+ assert @chain.check_root chain, Time.now
+ end
+
+ def test_check_root_empty_chain
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_root [], Time.now
+ end
+
+ assert_equal 'missing root certificate', e.message
+ end
+
+ def test_check_root_invalid_signer
+ chain = [INVALID_SIGNER_CERT]
+
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_root chain, Time.now
+ end
+
+ assert_equal "certificate #{INVALID_SIGNER_CERT.subject} " +
+ "was not issued by #{INVALID_SIGNER_CERT.issuer}",
+ e.message
+ end
+
+ def test_check_root_not_self_signed
+ chain = [INVALID_ISSUER_CERT]
+
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_root chain, Time.now
+ end
+
+ assert_equal "root certificate #{INVALID_ISSUER_CERT.subject} " +
+ "is not self-signed (issuer #{INVALID_ISSUER_CERT.issuer})",
+ e.message
+ end
+
+ def test_check_root_no_chain
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_root nil, Time.now
+ end
+
+ assert_equal 'missing signing chain', e.message
+ end
+
+ def test_check_trust
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ assert @high.check_trust [PUBLIC_CERT], @sha1, @trust_dir
+ end
+
+ def test_check_trust_child
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ assert @high.check_trust [PUBLIC_CERT, CHILD_CERT], @sha1, @trust_dir
+ end
+
+ def test_check_trust_empty_chain
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_trust [], @sha1, @trust_dir
+ end
+
+ assert_equal 'missing root certificate', e.message
+ end
+
+ def test_check_trust_mismatch
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ e = assert_raises Gem::Security::Exception do
+ @high.check_trust [WRONG_KEY_CERT], @sha1, @trust_dir
+ end
+
+ assert_equal "trusted root certificate #{PUBLIC_CERT.subject} checksum " +
+ "does not match signing root certificate checksum", e.message
+ end
+
+ def test_check_trust_no_chain
+ e = assert_raises Gem::Security::Exception do
+ @chain.check_trust nil, @sha1, @trust_dir
+ end
+
+ assert_equal 'missing signing chain', e.message
+ end
+
+ def test_check_trust_no_trust
+ e = assert_raises Gem::Security::Exception do
+ @high.check_trust [PUBLIC_CERT], @sha1, @trust_dir
+ end
+
+ assert_equal "root cert #{PUBLIC_CERT.subject} is not trusted", e.message
+ end
+
+ def test_check_trust_no_trust_child
+ e = assert_raises Gem::Security::Exception do
+ @high.check_trust [PUBLIC_CERT, CHILD_CERT], @sha1, @trust_dir
+ end
+
+ assert_equal "root cert #{PUBLIC_CERT.subject} is not trusted " +
+ "(root of signing cert #{CHILD_CERT.subject})", e.message
+ end
+
+ def test_subject
+ assert_equal 'email:nobody@example', @no.subject(PUBLIC_CERT)
+ assert_equal '/C=JP/O=JIN.GR.JP/OU=RRR/CN=CA', @no.subject(CA_CERT)
+ end
+
+ def test_verify
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ assert @almost_no.verify [PUBLIC_CERT], nil, *dummy_signatures
+ end
+
+ def test_verify_chain_signatures
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ assert @high.verify [PUBLIC_CERT], nil, *dummy_signatures
+ end
+
+ def test_verify_chain_key
+ @almost_no.verify [PUBLIC_CERT], PRIVATE_KEY, *dummy_signatures
+ end
+
+ def test_verify_no_digests
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ _, signatures = dummy_signatures
+
+ e = assert_raises Gem::Security::Exception do
+ @almost_no.verify [PUBLIC_CERT], nil, {}, signatures
+ end
+
+ assert_equal 'no digests provided (probable bug)', e.message
+ end
+
+ def test_verify_no_digests_no_security
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ _, signatures = dummy_signatures
+
+ e = assert_raises Gem::Security::Exception do
+ @no.verify [PUBLIC_CERT], nil, {}, signatures
+ end
+
+ assert_equal 'missing digest for 0', e.message
+ end
+
+ def test_verify_no_signatures
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ digests, = dummy_signatures
+
+ use_ui @ui do
+ @no.verify [PUBLIC_CERT], nil, digests, {}, 'some_gem'
+ end
+
+ assert_match "WARNING: some_gem is not signed\n", @ui.error
+
+ assert_raises Gem::Security::Exception do
+ @high.verify [PUBLIC_CERT], nil, digests, {}
+ end
+ end
+
+ def test_verify_no_signatures_no_digests
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ use_ui @ui do
+ @no.verify [PUBLIC_CERT], nil, {}, {}, 'some_gem'
+ end
+
+ assert_empty @ui.output
+ assert_empty @ui.error
+ end
+
+ def test_verify_not_enough_signatures
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ digests, signatures = dummy_signatures
+
+ data = digest 'goodbye'
+
+ signatures[1] = PRIVATE_KEY.sign @sha1.new, data.digest
+
+ e = assert_raises Gem::Security::Exception do
+ @almost_no.verify [PUBLIC_CERT], nil, digests, signatures
+ end
+
+ assert_equal 'missing digest for 1', e.message
+ end
+
+ def test_verify_no_trust
+ digests, signatures = dummy_signatures
+
+ use_ui @ui do
+ @low.verify [PUBLIC_CERT], nil, digests, signatures, 'some_gem'
+ end
+
+ assert_equal "WARNING: email:nobody@example is not trusted for some_gem\n",
+ @ui.error
+
+ assert_raises Gem::Security::Exception do
+ @medium.verify [PUBLIC_CERT], nil, digests, signatures
+ end
+ end
+
+ def test_verify_wrong_digest_type
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ sha512 = OpenSSL::Digest::SHA512
+
+ data = sha512.new
+ data << 'hello'
+
+ digests = { 'SHA512' => { 0 => data } }
+ signature = PRIVATE_KEY.sign sha512.new, data.digest
+ signatures = { 0 => signature }
+
+ e = assert_raises Gem::Security::Exception do
+ @almost_no.verify [PUBLIC_CERT], nil, digests, signatures
+ end
+
+ assert_equal 'no digests provided (probable bug)', e.message
+ end
+
+ def test_verify_signatures_chain
+ @spec.cert_chain = [PUBLIC_CERT, CHILD_CERT]
+
+ assert @chain.verify_signatures @spec, *dummy_signatures(CHILD_KEY)
+ end
+
+ def test_verify_signatures_data
+ @spec.cert_chain = [PUBLIC_CERT]
+
+ @almost_no.verify_signatures @spec, *dummy_signatures
+ end
+
+ def test_verify_signatures_root
+ @spec.cert_chain = [PUBLIC_CERT, CHILD_CERT]
+
+ assert @root.verify_signatures @spec, *dummy_signatures(CHILD_KEY)
+ end
+
+ def test_verify_signatures_signer
+ @spec.cert_chain = [PUBLIC_CERT]
+
+ assert @low.verify_signatures @spec, *dummy_signatures
+ end
+
+ def test_verify_signatures_trust
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ @spec.cert_chain = [PUBLIC_CERT]
+
+ assert @high.verify_signatures @spec, *dummy_signatures
+ end
+
+ def test_verify_signatures
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ @spec.cert_chain = [PUBLIC_CERT.to_s]
+
+ metadata_gz = Gem.gzip @spec.to_yaml
+
+ package = Gem::Package.new 'nonexistent.gem'
+ package.checksums['SHA1'] = {}
+
+ s = StringIO.new metadata_gz
+ def s.full_name() 'metadata.gz' end
+
+ digests = package.digest s
+ metadata_gz_digest = digests['SHA1']['metadata.gz']
+
+ signatures = {}
+ signatures['metadata.gz'] =
+ PRIVATE_KEY.sign @sha1.new, metadata_gz_digest.digest
+
+ assert @high.verify_signatures @spec, digests, signatures
+ end
+
+ def test_verify_signatures_missing
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ @spec.cert_chain = [PUBLIC_CERT.to_s]
+
+ metadata_gz = Gem.gzip @spec.to_yaml
+
+ package = Gem::Package.new 'nonexistent.gem'
+ package.checksums['SHA1'] = {}
+
+ s = StringIO.new metadata_gz
+ def s.full_name() 'metadata.gz' end
+
+ digests = package.digest s
+ digests['SHA1']['data.tar.gz'] = OpenSSL::Digest.new 'SHA1', 'hello'
+
+ metadata_gz_digest = digests['SHA1']['metadata.gz']
+
+ signatures = {}
+ signatures['metadata.gz'] =
+ PRIVATE_KEY.sign @sha1.new, metadata_gz_digest.digest
+
+ e = assert_raises Gem::Security::Exception do
+ @high.verify_signatures @spec, digests, signatures
+ end
+
+ assert_equal 'missing signature for data.tar.gz', e.message
+ end
+
+ def test_verify_signatures_none
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ @spec.cert_chain = [PUBLIC_CERT.to_s]
+
+ metadata_gz = Gem.gzip @spec.to_yaml
+
+ package = Gem::Package.new 'nonexistent.gem'
+ package.checksums['SHA1'] = {}
+
+ s = StringIO.new metadata_gz
+ def s.full_name() 'metadata.gz' end
+
+ digests = package.digest s
+ digests['SHA1']['data.tar.gz'] = OpenSSL::Digest.new 'SHA1', 'hello'
+
+ assert_raises Gem::Security::Exception do
+ @high.verify_signatures @spec, digests, {}
+ end
+ end
+
+ def digest data
+ digester = @sha1.new
+ digester << data
+ digester
+ end
+
+ def sign data, key = PRIVATE_KEY
+ key.sign @sha1.new, data.digest
+ end
+
+ def dummy_signatures key = PRIVATE_KEY
+ data = digest 'hello'
+
+ digests = { 'SHA1' => { 0 => data } }
+ signatures = { 0 => sign(data, key) }
+
+ return digests, signatures
+ end
+
+end if defined?(OpenSSL::SSL)
+
diff --git a/jni/ruby/test/rubygems/test_gem_security_signer.rb b/jni/ruby/test/rubygems/test_gem_security_signer.rb
new file mode 100644
index 0000000..f077a46
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_security_signer.rb
@@ -0,0 +1,208 @@
+require 'rubygems/test_case'
+
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping Gem::Security::Signer tests. openssl not found.'
+end
+
+class TestGemSecuritySigner < Gem::TestCase
+
+ ALTERNATE_KEY = load_key 'alternate'
+ CHILD_KEY = load_key 'child'
+ GRANDCHILD_KEY = load_key 'grandchild'
+
+ CHILD_CERT = load_cert 'child'
+ GRANDCHILD_CERT = load_cert 'grandchild'
+ EXPIRED_CERT = load_cert 'expired'
+
+ def setup
+ super
+
+ @cert_file = PUBLIC_CERT
+ end
+
+ def test_initialize
+ signer = Gem::Security::Signer.new nil, nil
+
+ assert_nil signer.key
+ assert_nil signer.cert_chain
+ end
+
+ def test_initialize_cert_chain_empty
+ signer = Gem::Security::Signer.new PUBLIC_KEY, []
+
+ assert_empty signer.cert_chain
+ end
+
+ def test_initialize_cert_chain_mixed
+ signer = Gem::Security::Signer.new nil, [@cert_file, CHILD_CERT]
+
+ assert_equal [PUBLIC_CERT, CHILD_CERT].map { |c| c.to_pem },
+ signer.cert_chain.map { |c| c.to_pem }
+ end
+
+ def test_initialize_cert_chain_invalid
+ assert_raises OpenSSL::X509::CertificateError do
+ Gem::Security::Signer.new nil, ['garbage']
+ end
+ end
+
+ def test_initialize_cert_chain_path
+ signer = Gem::Security::Signer.new nil, [@cert_file]
+
+ assert_equal [PUBLIC_CERT].map { |c| c.to_pem },
+ signer.cert_chain.map { |c| c.to_pem }
+ end
+
+ def test_initialize_default
+ FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
+
+ private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
+ Gem::Security.write PRIVATE_KEY, private_key_path
+
+ public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
+ Gem::Security.write PUBLIC_CERT, public_cert_path
+
+ signer = Gem::Security::Signer.new nil, nil
+
+ assert_equal PRIVATE_KEY.to_pem, signer.key.to_pem
+ assert_equal [PUBLIC_CERT.to_pem], signer.cert_chain.map { |c| c.to_pem }
+ end
+
+ def test_initialize_key_path
+ key_file = PRIVATE_KEY_PATH
+
+ signer = Gem::Security::Signer.new key_file, nil
+
+ assert_equal PRIVATE_KEY.to_s, signer.key.to_s
+ end
+
+ def test_initialize_encrypted_key_path
+ key_file = ENCRYPTED_PRIVATE_KEY_PATH
+
+ signer = Gem::Security::Signer.new key_file, nil, PRIVATE_KEY_PASSPHRASE
+
+ assert_equal ENCRYPTED_PRIVATE_KEY.to_s, signer.key.to_s
+ end
+
+ def test_extract_name
+ signer = Gem::Security::Signer.new nil, nil
+
+ assert_equal 'child@example', signer.extract_name(CHILD_CERT)
+ end
+
+ def test_load_cert_chain
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ signer = Gem::Security::Signer.new nil, []
+ signer.cert_chain.replace [CHILD_CERT]
+
+ signer.load_cert_chain
+
+ assert_equal [PUBLIC_CERT.to_pem, CHILD_CERT.to_pem],
+ signer.cert_chain.map { |c| c.to_pem }
+ end
+
+ def test_load_cert_chain_broken
+ Gem::Security.trust_dir.trust_cert CHILD_CERT
+
+ signer = Gem::Security::Signer.new nil, []
+ signer.cert_chain.replace [GRANDCHILD_CERT]
+
+ signer.load_cert_chain
+
+ assert_equal [CHILD_CERT.to_pem, GRANDCHILD_CERT.to_pem],
+ signer.cert_chain.map { |c| c.to_pem }
+ end
+
+ def test_sign
+ signer = Gem::Security::Signer.new PRIVATE_KEY, [PUBLIC_CERT]
+
+ signature = signer.sign 'hello'
+
+ expected = <<-EXPECTED
+pxSf9ScaghbMNmNp8fqSJj7BiIGpbuoOVYCOM3TJNH9STLILA5z3xKp3gM6w
+VJ7aGsh9KCP485ftS3J9Kb/lKJsyoSkkRSQ5QG+LnyZwMuWlThPDR5o7q6al
+0oxE7vvbbqxFqcT4ojWIkwxJxOluFWmt2D8I6QTX2vLAn09y+Kl66AOrT7R5
+UinbXkz04VwcNvkBqJyko3yWxFKiGNpntZQg4jIw4L+h97EOaZp8H96udzQH
+Da3K0YZ6FsqLDFNnWAFhve3kmpE3CludpvDqH0piq0zKqnOiqAcvICIpPaJP
+c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg==
+ EXPECTED
+
+ assert_equal expected, [signature].pack('m')
+ end
+
+ def test_sign_expired
+ signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
+
+ assert_raises Gem::Security::Exception do
+ signer.sign 'hello'
+ end
+ end
+
+ def test_sign_expired_auto_update
+ FileUtils.mkdir_p File.join(Gem.user_home, '.gem'), :mode => 0700
+
+ private_key_path = File.join(Gem.user_home, '.gem', 'gem-private_key.pem')
+ Gem::Security.write PRIVATE_KEY, private_key_path
+
+ cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
+ Gem::Security.write EXPIRED_CERT, cert_path
+
+ signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
+
+ signer.sign 'hello'
+
+ cert = OpenSSL::X509::Certificate.new File.read cert_path
+
+ refute_equal EXPIRED_CERT.to_pem, cert.to_pem
+ assert_in_delta Time.now, cert.not_before, 10
+
+ expiry = EXPIRED_CERT.not_after.strftime "%Y%m%d%H%M%S"
+
+ expired_path =
+ File.join Gem.user_home, '.gem', "gem-public_cert.pem.expired.#{expiry}"
+
+ assert_path_exists expired_path
+ assert_equal EXPIRED_CERT.to_pem, File.read(expired_path)
+ end
+
+ def test_sign_expired_auto_update_exists
+ FileUtils.mkdir_p File.join(Gem.user_home, '.gem'), :mode => 0700
+
+ expiry = EXPIRED_CERT.not_after.strftime "%Y%m%d%H%M%S"
+ expired_path =
+ File.join Gem.user_home, "gem-public_cert.pem.expired.#{expiry}"
+
+ Gem::Security.write EXPIRED_CERT, expired_path
+
+ private_key_path = File.join(Gem.user_home, 'gem-private_key.pem')
+ Gem::Security.write PRIVATE_KEY, private_key_path
+
+ cert_path = File.join Gem.user_home, 'gem-public_cert.pem'
+ Gem::Security.write EXPIRED_CERT, cert_path
+
+ signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
+
+ e = assert_raises Gem::Security::Exception do
+ signer.sign 'hello'
+ end
+
+ assert_match %r%certificate /CN=nobody/DC=example not valid%, e.message
+ end
+
+ def test_sign_no_key
+ signer = Gem::Security::Signer.new nil, nil
+
+ assert_nil signer.sign 'stuff'
+ end
+
+ def test_sign_wrong_key
+ signer = Gem::Security::Signer.new ALTERNATE_KEY, [PUBLIC_CERT]
+
+ assert_raises Gem::Security::Exception do
+ signer.sign 'hello'
+ end
+ end
+
+end if defined?(OpenSSL::SSL)
+
diff --git a/jni/ruby/test/rubygems/test_gem_security_trust_dir.rb b/jni/ruby/test/rubygems/test_gem_security_trust_dir.rb
new file mode 100644
index 0000000..7b0d450
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_security_trust_dir.rb
@@ -0,0 +1,98 @@
+require 'rubygems/test_case'
+
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping Gem::Security::TrustDir tests. openssl not found.'
+end
+
+class TestGemSecurityTrustDir < Gem::TestCase
+
+ CHILD_CERT = load_cert 'child'
+
+ def setup
+ super
+
+ @dest_dir = File.join @tempdir, 'trust'
+
+ @trust_dir = Gem::Security::TrustDir.new @dest_dir
+ end
+
+ def test_cert_path
+ digest = OpenSSL::Digest::SHA1.hexdigest PUBLIC_CERT.subject.to_s
+
+ expected = File.join @dest_dir, "cert-#{digest}.pem"
+
+ assert_equal expected, @trust_dir.cert_path(PUBLIC_CERT)
+ end
+
+ def test_issuer_of
+ assert_nil @trust_dir.issuer_of(CHILD_CERT)
+
+ @trust_dir.trust_cert PUBLIC_CERT
+
+ assert_equal PUBLIC_CERT.to_pem, @trust_dir.issuer_of(CHILD_CERT).to_pem
+ end
+
+ def test_load_certificate
+ @trust_dir.trust_cert PUBLIC_CERT
+
+ path = @trust_dir.cert_path PUBLIC_CERT
+
+ assert_equal PUBLIC_CERT.to_pem, @trust_dir.load_certificate(path).to_pem
+ end
+
+ def test_name_path
+ digest = OpenSSL::Digest::SHA1.hexdigest PUBLIC_CERT.subject.to_s
+
+ expected = File.join @dest_dir, "cert-#{digest}.pem"
+
+ assert_equal expected, @trust_dir.name_path(PUBLIC_CERT.subject)
+ end
+
+ def test_trust_cert
+ @trust_dir.trust_cert PUBLIC_CERT
+
+ trusted = @trust_dir.cert_path PUBLIC_CERT
+
+ assert_path_exists trusted
+
+ mask = 0100600 & (~File.umask)
+
+ assert_equal mask, File.stat(trusted).mode unless win_platform?
+
+ assert_equal PUBLIC_CERT.to_pem, File.read(trusted)
+ end
+
+ def test_verify
+ refute_path_exists @dest_dir
+
+ @trust_dir.verify
+
+ assert_path_exists @dest_dir
+
+ mask = 040700 & (~File.umask)
+
+ assert_equal mask, File.stat(@dest_dir).mode unless win_platform?
+ end
+
+ def test_verify_file
+ FileUtils.touch @dest_dir
+
+ e = assert_raises Gem::Security::Exception do
+ @trust_dir.verify
+ end
+
+ assert_equal "trust directory #{@dest_dir} is not a directory", e.message
+ end
+
+ def test_verify_wrong_permissions
+ FileUtils.mkdir_p @dest_dir, :mode => 0777
+
+ @trust_dir.verify
+
+ mask = 040700 & (~File.umask)
+
+ assert_equal mask, File.stat(@dest_dir).mode unless win_platform?
+ end
+
+end if defined?(OpenSSL::SSL)
+
diff --git a/jni/ruby/test/rubygems/test_gem_server.rb b/jni/ruby/test/rubygems/test_gem_server.rb
new file mode 100644
index 0000000..9640486
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_server.rb
@@ -0,0 +1,404 @@
+require 'rubygems/test_case'
+require 'rubygems/server'
+require 'stringio'
+
+class Gem::Server
+ attr_reader :server
+end
+
+class TestGemServer < Gem::TestCase
+ def setup
+ super
+
+ @a1 = quick_gem 'a', '1'
+ @a2 = quick_gem 'a', '2'
+ @a3_p = quick_gem 'a', '3.a'
+
+ @server = Gem::Server.new Gem.dir, process_based_port, false
+ @req = WEBrick::HTTPRequest.new :Logger => nil
+ @res = WEBrick::HTTPResponse.new :HTTPVersion => '1.0'
+ end
+
+ def test_doc_root_3
+ orig_rdoc_version = Gem::RDoc.rdoc_version
+ Gem::RDoc.instance_variable_set :@rdoc_version, Gem::Version.new('3.12')
+
+ assert_equal '/doc_root/X-1/rdoc/index.html', @server.doc_root('X-1')
+
+ ensure
+ Gem::RDoc.instance_variable_set :@rdoc_version, orig_rdoc_version
+ end
+
+ def test_doc_root_4
+ orig_rdoc_version = Gem::RDoc.rdoc_version
+ Gem::RDoc.instance_variable_set :@rdoc_version, Gem::Version.new('4.0')
+
+ assert_equal '/doc_root/X-1/', @server.doc_root('X-1')
+
+ ensure
+ Gem::RDoc.instance_variable_set :@rdoc_version, orig_rdoc_version
+ end
+
+ def test_have_rdoc_4_plus_eh
+ orig_rdoc_version = Gem::RDoc.rdoc_version
+ Gem::RDoc.instance_variable_set(:@rdoc_version, Gem::Version.new('4.0'))
+
+ server = Gem::Server.new Gem.dir, 0, false
+ assert server.have_rdoc_4_plus?
+
+ Gem::RDoc.instance_variable_set :@rdoc_version, Gem::Version.new('3.12')
+
+ server = Gem::Server.new Gem.dir, 0, false
+ refute server.have_rdoc_4_plus?
+
+ Gem::RDoc.instance_variable_set(:@rdoc_version,
+ Gem::Version.new('4.0.0.preview2'))
+
+ server = Gem::Server.new Gem.dir, 0, false
+ assert server.have_rdoc_4_plus?
+ ensure
+ Gem::RDoc.instance_variable_set :@rdoc_version, orig_rdoc_version
+ end
+
+ def test_spec_dirs
+ s = Gem::Server.new Gem.dir, process_based_port, false
+
+ assert_equal [File.join(Gem.dir, 'specifications')], s.spec_dirs
+
+ s = Gem::Server.new [Gem.dir, Gem.dir], process_based_port, false
+
+ assert_equal [File.join(Gem.dir, 'specifications'),
+ File.join(Gem.dir, 'specifications')], s.spec_dirs
+ end
+
+ def test_latest_specs
+ data = StringIO.new "GET /latest_specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ Gem::Deprecate.skip_during do
+ @server.latest_specs @req, @res
+ end
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'application/octet-stream', @res['content-type']
+ assert_equal [['a', Gem::Version.new(2), Gem::Platform::RUBY]],
+ Marshal.load(@res.body)
+ end
+
+ def test_latest_specs_gemdirs
+ data = StringIO.new "GET /latest_specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = util_spec 'z', 9
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.latest_specs @req, @res
+
+ assert_equal 200, @res.status
+
+ assert_equal [['z', v(9), Gem::Platform::RUBY]], Marshal.load(@res.body)
+ end
+
+ def test_latest_specs_gz
+ data = StringIO.new "GET /latest_specs.#{Gem.marshal_version}.gz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ Gem::Deprecate.skip_during do
+ @server.latest_specs @req, @res
+ end
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'application/x-gzip', @res['content-type']
+ assert_equal [['a', Gem::Version.new(2), Gem::Platform::RUBY]],
+ Marshal.load(Gem.gunzip(@res.body))
+ end
+
+ def test_listen
+ util_listen
+
+ capture_io do
+ @server.listen
+ end
+
+ assert_equal 1, @server.server.listeners.length
+ end
+
+ def test_listen_addresses
+ util_listen
+
+ capture_io do
+ @server.listen %w[a b]
+ end
+
+ assert_equal 2, @server.server.listeners.length
+ end
+
+ def test_prerelease_specs
+ data = StringIO.new "GET /prerelease_specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ Gem::Deprecate.skip_during do
+ @server.prerelease_specs @req, @res
+ end
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'application/octet-stream', @res['content-type']
+ assert_equal [['a', v('3.a'), Gem::Platform::RUBY]],
+ Marshal.load(@res.body)
+ end
+
+ def test_prerelease_specs_gz
+ data = StringIO.new "GET /prerelease_specs.#{Gem.marshal_version}.gz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ Gem::Deprecate.skip_during do
+ @server.prerelease_specs @req, @res
+ end
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'application/x-gzip', @res['content-type']
+ assert_equal [['a', v('3.a'), Gem::Platform::RUBY]],
+ Marshal.load(Gem.gunzip(@res.body))
+ end
+
+ def test_quick_gemdirs
+ data = StringIO.new "GET /quick/Marshal.4.8/z-9.gemspec.rz HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.quick @req, @res
+
+ assert_equal 404, @res.status
+
+ spec = util_spec 'z', 9
+
+ specs_dir = File.join dir, 'specifications'
+
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ data.rewind
+
+ req = WEBrick::HTTPRequest.new :Logger => nil
+ res = WEBrick::HTTPResponse.new :HTTPVersion => '1.0'
+ req.parse data
+
+ server.quick req, res
+
+ assert_equal 200, res.status
+ end
+
+ def test_quick_missing
+ data = StringIO.new "GET /quick/Marshal.4.8/z-9.gemspec.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @req, @res
+
+ assert_equal 404, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'text/plain', @res['content-type']
+ assert_equal 'No gems found matching "z" "9" nil', @res.body
+ assert_equal 404, @res.status
+ end
+
+ def test_quick_marshal_a_1_gemspec_rz
+ data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-1.gemspec.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert @res['date']
+ assert_equal 'application/x-deflate', @res['content-type']
+
+ spec = Marshal.load Gem.inflate(@res.body)
+ assert_equal 'a', spec.name
+ assert_equal Gem::Version.new(1), spec.version
+ end
+
+ def test_quick_marshal_a_1_mswin32_gemspec_rz
+ quick_gem 'a', '1' do |s| s.platform = Gem::Platform.local end
+
+ data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-1-#{Gem::Platform.local}.gemspec.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert @res['date']
+ assert_equal 'application/x-deflate', @res['content-type']
+
+ spec = Marshal.load Gem.inflate(@res.body)
+ assert_equal 'a', spec.name
+ assert_equal Gem::Version.new(1), spec.version
+ assert_equal Gem::Platform.local, spec.platform
+ end
+
+ def test_quick_marshal_a_3_a_gemspec_rz
+ data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-3.a.gemspec.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert @res['date']
+ assert_equal 'application/x-deflate', @res['content-type']
+
+ spec = Marshal.load Gem.inflate(@res.body)
+ assert_equal 'a', spec.name
+ assert_equal v('3.a'), spec.version
+ end
+
+ def test_quick_marshal_a_b_3_a_gemspec_rz
+ quick_gem 'a-b', '3.a'
+
+ data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-b-3.a.gemspec.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert @res['date']
+ assert_equal 'application/x-deflate', @res['content-type']
+
+ spec = Marshal.load Gem.inflate(@res.body)
+ assert_equal 'a-b', spec.name
+ assert_equal v('3.a'), spec.version
+ end
+
+ def test_rdoc
+ data = StringIO.new "GET /rdoc?q=a HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.rdoc @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r|No documentation found|, @res.body
+ assert_equal 'text/html', @res['content-type']
+ end
+
+ def test_root
+ data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.root @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'text/html', @res['content-type']
+ end
+
+ def test_root_gemdirs
+ data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = util_spec 'z', 9
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.root @req, @res
+
+ assert_equal 200, @res.status
+ assert_match 'z 9', @res.body
+ end
+
+ def test_specs
+ data = StringIO.new "GET /specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.specs @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'application/octet-stream', @res['content-type']
+
+ assert_equal [['a', Gem::Version.new(1), Gem::Platform::RUBY],
+ ['a', Gem::Version.new(2), Gem::Platform::RUBY],
+ ['a', v('3.a'), Gem::Platform::RUBY]],
+ Marshal.load(@res.body)
+ end
+
+ def test_specs_gemdirs
+ data = StringIO.new "GET /specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
+ dir = "#{@gemhome}2"
+
+ spec = util_spec 'z', 9
+
+ specs_dir = File.join dir, 'specifications'
+ FileUtils.mkdir_p specs_dir
+
+ open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ io.write spec.to_ruby
+ end
+
+ server = Gem::Server.new dir, process_based_port, false
+
+ @req.parse data
+
+ server.specs @req, @res
+
+ assert_equal 200, @res.status
+
+ assert_equal [['z', v(9), Gem::Platform::RUBY]], Marshal.load(@res.body)
+ end
+
+ def test_specs_gz
+ data = StringIO.new "GET /specs.#{Gem.marshal_version}.gz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.specs @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'application/x-gzip', @res['content-type']
+
+ assert_equal [['a', Gem::Version.new(1), Gem::Platform::RUBY],
+ ['a', Gem::Version.new(2), Gem::Platform::RUBY],
+ ['a', v('3.a'), Gem::Platform::RUBY]],
+ Marshal.load(Gem.gunzip(@res.body))
+ end
+
+ def util_listen
+ webrick = Object.new
+ webrick.instance_variable_set :@listeners, []
+ def webrick.listeners() @listeners end
+ def webrick.listen(host, port)
+ socket = Object.new
+ socket.instance_variable_set :@host, host
+ socket.instance_variable_set :@port, port
+ def socket.addr() [nil, @port, @host] end
+ @listeners << socket
+ end
+
+ @server.instance_variable_set :@server, webrick
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_silent_ui.rb b/jni/ruby/test/rubygems/test_gem_silent_ui.rb
new file mode 100644
index 0000000..01ff79d
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_silent_ui.rb
@@ -0,0 +1,116 @@
+require 'rubygems/test_case'
+require 'rubygems/user_interaction'
+require 'timeout'
+
+class TestGemSilentUI < Gem::TestCase
+
+ def setup
+ super
+ @sui = Gem::SilentUI.new
+ end
+
+ def teardown
+ @sui.close
+ super
+ end
+
+ def test_ask
+ value = nil
+ out, err = capture_io do
+ use_ui @sui do
+ value = @sui.ask 'Problem?'
+ end
+ end
+
+ assert_empty out, 'No output'
+ assert_empty err, 'No output'
+
+ assert_nil value, 'No value'
+ end
+
+ def test_ask_for_password
+ value = nil
+ out, err = capture_io do
+ use_ui @sui do
+ value = @sui.ask_for_password 'Problem?'
+ end
+ end
+
+ assert_empty out, 'No output'
+ assert_empty err, 'No output'
+
+ assert_nil value, 'No value'
+ end
+
+ def test_ask_yes_no
+ value = nil
+ out, err = capture_io do
+ use_ui @sui do
+ assert_raises(Gem::OperationNotSupportedError) do
+ @sui.ask_yes_no 'Problem?'
+ end
+ end
+ end
+
+ assert_empty out, 'No output'
+ assert_empty err, 'No output'
+
+ out, err = capture_io do
+ use_ui @sui do
+ value = @sui.ask_yes_no 'Problem?', true
+ end
+ end
+
+ assert_empty out, 'No output'
+ assert_empty err, 'No output'
+
+ assert value, 'Value is true'
+
+ out, err = capture_io do
+ use_ui @sui do
+ value = @sui.ask_yes_no 'Problem?', false
+ end
+ end
+
+ assert_empty out, 'No output'
+ assert_empty err, 'No output'
+
+ assert_equal value, false, 'Value is false'
+ end
+
+ def test_choose_from_list
+ value = nil
+ out, err = capture_io do
+ use_ui @sui do
+ value = @sui.choose_from_list 'Problem?', %w[yes no]
+ end
+ end
+
+ assert_empty out, 'No output'
+ assert_empty err, 'No output'
+
+ assert_equal [nil, nil], value, 'Value is nil!'
+ end
+
+ def test_progress_reporter
+ out, err = capture_io do
+ use_ui @sui do
+ @sui.progress_reporter 10, 'hi'
+ end
+ end
+
+ assert_empty out, 'No output'
+ assert_empty err, 'No output'
+ end
+
+ def test_download_reporter
+ out, err = capture_io do
+ use_ui @sui do
+ @sui.download_reporter.fetch 'a.gem', 1024
+ end
+ end
+
+ assert_empty out, 'No output'
+ assert_empty err, 'No output'
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_source.rb b/jni/ruby/test/rubygems/test_gem_source.rb
new file mode 100644
index 0000000..7d23eee
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source.rb
@@ -0,0 +1,241 @@
+require 'rubygems/test_case'
+require 'rubygems/source'
+require 'rubygems/indexer'
+
+class TestGemSource < Gem::TestCase
+
+ def tuple(*args)
+ Gem::NameTuple.new(*args)
+ end
+
+ def setup
+ super
+
+ @specs = spec_fetcher do |fetcher|
+ fetcher.spec 'a', '1.a'
+ fetcher.gem 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'b', 2
+ end
+
+ @source = Gem::Source.new(@gem_repo)
+ end
+
+ def test_initialize_invalid_uri
+ assert_raises URI::InvalidURIError do
+ Gem::Source.new 'git@example:a.git'
+ end
+ end
+
+ def test_initialize_git
+ repository = 'git@example:a.git'
+
+ source = Gem::Source::Git.new 'a', repository, 'master', false
+
+ assert_equal repository, source.uri
+ end
+
+ def test_api_uri
+ assert_equal @source.api_uri, @source.uri
+ end
+
+ def test_api_uri_resolved_from_remote_fetcher
+ uri = URI.parse "http://gem.example/foo"
+ @fetcher.api_endpoints[uri] = URI.parse "http://api.blah"
+
+ src = Gem::Source.new uri
+ assert_equal URI.parse("http://api.blah"), src.api_uri
+ end
+
+ def test_cache_dir_escapes_windows_paths
+ uri = URI.parse("file:///C:/WINDOWS/Temp/gem_repo")
+ root = Gem.spec_cache_dir
+ cache_dir = @source.cache_dir(uri).gsub(root, '')
+ assert cache_dir !~ /:/, "#{cache_dir} should not contain a :"
+ end
+
+ def test_dependency_resolver_set_bundler_api
+ response = Net::HTTPResponse.new '1.1', 200, 'OK'
+ response.uri = URI('http://example') if response.respond_to? :uri
+
+ @fetcher.data["#{@gem_repo}api/v1/dependencies"] = response
+
+ set = @source.dependency_resolver_set
+
+ assert_kind_of Gem::Resolver::APISet, set
+ end
+
+ def test_dependency_resolver_set_file_uri
+ skip 'install builder gem' unless defined? Builder::XChar
+
+ Gem::Indexer.new(@tempdir).generate_index
+
+ source = Gem::Source.new "file://#{@tempdir}/"
+
+ set = source.dependency_resolver_set
+
+ assert_kind_of Gem::Resolver::IndexSet, set
+ end
+
+ def test_dependency_resolver_set_marshal_api
+ set = @source.dependency_resolver_set
+
+ assert_kind_of Gem::Resolver::IndexSet, set
+ end
+
+ def test_fetch_spec
+ a1 = @specs['a-1']
+
+ spec_uri = "#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{a1.spec_name}"
+
+ spec = @source.fetch_spec tuple('a', Gem::Version.new(1), 'ruby')
+ assert_equal a1.full_name, spec.full_name
+
+ cache_dir = @source.cache_dir URI.parse(spec_uri)
+
+ cache_file = File.join cache_dir, a1.spec_name
+
+ assert File.exist?(cache_file)
+ end
+
+ def test_fetch_spec_cached
+ a1 = @specs['a-1']
+
+ spec_uri = "#{@gem_repo}/#{Gem::MARSHAL_SPEC_DIR}#{a1.spec_name}"
+ @fetcher.data["#{spec_uri}.rz"] = nil
+
+ cache_dir = @source.cache_dir URI.parse(spec_uri)
+ FileUtils.mkdir_p cache_dir
+
+ cache_file = File.join cache_dir, a1.spec_name
+
+ open cache_file, 'wb' do |io|
+ Marshal.dump a1, io
+ end
+
+ spec = @source.fetch_spec tuple('a', Gem::Version.new(1), 'ruby')
+ assert_equal a1.full_name, spec.full_name
+ end
+
+ def test_fetch_spec_platform
+ specs = spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ spec = @source.fetch_spec tuple('pl', Gem::Version.new(1), 'i386-linux')
+
+ assert_equal specs['pl-1-x86-linux'].full_name, spec.full_name
+ end
+
+ def test_fetch_spec_platform_ruby
+ spec = @source.fetch_spec tuple('a', Gem::Version.new(1), nil)
+ assert_equal @specs['a-1'].full_name, spec.full_name
+
+ spec = @source.fetch_spec tuple('a', Gem::Version.new(1), '')
+ assert_equal @specs['a-1'].full_name, spec.full_name
+ end
+
+ def test_load_specs
+ released = @source.load_specs(:released).map { |spec| spec.full_name }
+ assert_equal %W[a-2 a-1 b-2], released
+
+ cache_dir = File.join Gem.spec_cache_dir, 'gems.example.com%80'
+ assert File.exist?(cache_dir), "#{cache_dir} does not exist"
+
+ cache_file = File.join cache_dir, "specs.#{Gem.marshal_version}"
+ assert File.exist?(cache_file)
+ end
+
+ def test_load_specs_cached
+ latest_specs = @source.load_specs :latest
+
+ # Make sure the cached version is actually different:
+ latest_specs << Gem::NameTuple.new('cached', Gem::Version.new('1.0.0'), 'ruby')
+
+ @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = nil
+ @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}"] =
+ ' ' * Marshal.dump(latest_specs).length
+
+ cache_dir = File.join Gem.spec_cache_dir, 'gems.example.com%80'
+
+ FileUtils.mkdir_p cache_dir
+
+ cache_file = File.join cache_dir, "latest_specs.#{Gem.marshal_version}"
+
+ open cache_file, 'wb' do |io|
+ Marshal.dump latest_specs, io
+ end
+
+ cached_specs = @source.load_specs :latest
+
+ assert_equal latest_specs, cached_specs
+ end
+
+ def test_load_specs_cached_empty
+ latest_specs = @source.load_specs :latest
+
+ # Make sure the cached version is actually different:
+ latest_specs << Gem::NameTuple.new('fixed', Gem::Version.new('1.0.0'), 'ruby')
+ # Setup valid data on the 'remote'
+ @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] =
+ util_gzip(Marshal.dump(latest_specs))
+
+ cache_dir = File.join Gem.spec_cache_dir, 'gems.example.com%80'
+
+ FileUtils.mkdir_p cache_dir
+
+ cache_file = File.join cache_dir, "latest_specs.#{Gem.marshal_version}"
+
+ open cache_file, 'wb' do |io|
+ # Setup invalid data in the cache:
+ io.write Marshal.dump(latest_specs)[0, 10]
+ end
+
+ fixed_specs = @source.load_specs :latest
+
+ assert_equal latest_specs, fixed_specs
+ end
+
+ def test_load_specs_from_unavailable_uri
+ src = Gem::Source.new("http://not-there.nothing")
+
+ assert_raises Gem::RemoteFetcher::FetchError do
+ src.load_specs :latest
+ end
+ end
+
+ def test_spaceship
+ remote = @source
+ specific = Gem::Source::SpecificFile.new @specs['a-1'].cache_file
+ installed = Gem::Source::Installed.new
+ local = Gem::Source::Local.new
+
+ assert_equal( 0, remote. <=>(remote), 'remote <=> remote')
+
+ assert_equal(-1, remote. <=>(specific), 'remote <=> specific')
+ assert_equal( 1, specific. <=>(remote), 'specific <=> remote')
+
+ assert_equal(-1, remote. <=>(local), 'remote <=> local')
+ assert_equal( 1, local. <=>(remote), 'local <=> remote')
+
+ assert_equal(-1, remote. <=>(installed), 'remote <=> installed')
+ assert_equal( 1, installed.<=>(remote), 'installed <=> remote')
+
+ no_uri = @source.dup
+ no_uri.instance_variable_set :@uri, nil
+
+ assert_equal(-1, remote. <=>(no_uri), 'remote <=> no_uri')
+ end
+
+ def test_update_cache_eh
+ assert @source.update_cache?
+ end
+
+ def test_update_cache_eh_home_nonexistent
+ FileUtils.rmdir Gem.user_home
+
+ refute @source.update_cache?
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_source_fetch_problem.rb b/jni/ruby/test/rubygems/test_gem_source_fetch_problem.rb
new file mode 100644
index 0000000..e8d4785
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source_fetch_problem.rb
@@ -0,0 +1,19 @@
+require 'rubygems/test_case'
+
+class TestGemSourceFetchProblem < Gem::TestCase
+
+ def test_exception
+ source = Gem::Source.new @gem_repo
+ error = RuntimeError.new 'test'
+
+ sf = Gem::SourceFetchProblem.new source, error
+
+ e = assert_raises RuntimeError do
+ raise sf
+ end
+
+ assert_equal 'test', e.message
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_source_git.rb b/jni/ruby/test/rubygems/test_gem_source_git.rb
new file mode 100644
index 0000000..7e5bcca
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source_git.rb
@@ -0,0 +1,308 @@
+require 'rubygems/test_case'
+require 'rubygems/source'
+
+class TestGemSourceGit < Gem::TestCase
+
+ def setup
+ super
+
+ @name, @version, @repository, @head = git_gem
+
+ @hash = Digest::SHA1.hexdigest @repository
+
+ @source = Gem::Source::Git.new @name, @repository, 'master', false
+ end
+
+ def test_base_dir
+ assert_equal File.join(Gem.dir, 'bundler'), @source.base_dir
+
+ @source.root_dir = "#{@gemhome}2"
+
+ assert_equal File.join("#{@gemhome}2", 'bundler'), @source.base_dir
+ end
+
+ def test_checkout
+ @source.checkout
+
+ assert_path_exists File.join @source.install_dir, 'a.gemspec'
+ end
+
+ def test_checkout_master
+ Dir.chdir @repository do
+ system @git, 'checkout', '-q', '-b', 'other'
+ system @git, 'mv', 'a.gemspec', 'b.gemspec'
+ system @git, 'commit', '-q', '-a', '-m', 'rename gemspec'
+ system @git, 'checkout', '-q', 'master'
+ end
+
+ @source = Gem::Source::Git.new @name, @repository, 'other', false
+
+ @source.checkout
+
+ assert_path_exists File.join @source.install_dir, 'b.gemspec'
+ end
+
+ def test_checkout_local
+ @source.remote = false
+
+ @source.checkout
+
+ install_dir = File.join Gem.dir, 'bundler', 'gems', "a-#{@head[0..11]}"
+
+ refute_path_exists File.join install_dir, 'a.gemspec'
+ end
+
+ def test_checkout_local_cached
+ @source.cache
+
+ @source.remote = false
+
+ @source.checkout
+
+ assert_path_exists File.join @source.install_dir, 'a.gemspec'
+ end
+
+ def test_checkout_submodules
+ source = Gem::Source::Git.new @name, @repository, 'master', true
+
+ git_gem 'b'
+
+ Dir.chdir 'git/a' do
+ Gem::Util.silent_system @git, 'submodule', '--quiet',
+ 'add', File.expand_path('../b'), 'b'
+ system @git, 'commit', '--quiet', '-m', 'add submodule b'
+ end
+
+ source.checkout
+
+ assert_path_exists File.join source.install_dir, 'a.gemspec'
+ assert_path_exists File.join source.install_dir, 'b/b.gemspec'
+ end
+
+ def test_cache
+ assert @source.cache
+
+ assert_path_exists @source.repo_cache_dir
+
+ Dir.chdir @source.repo_cache_dir do
+ assert_equal @head, Gem::Util.popen(@git, 'rev-parse', 'master').strip
+ end
+ end
+
+ def test_cache_local
+ @source.remote = false
+
+ @source.cache
+
+ refute_path_exists @source.repo_cache_dir
+ end
+
+ def test_dir_shortref
+ @source.cache
+
+ assert_equal @head[0..11], @source.dir_shortref
+ end
+
+ def test_download
+ refute @source.download nil, nil
+ end
+
+ def test_equals2
+ assert_equal @source, @source
+
+ assert_equal @source, @source.dup
+
+ source =
+ Gem::Source::Git.new @source.name, @source.repository, 'other', false
+
+ refute_equal @source, source
+
+ source =
+ Gem::Source::Git.new @source.name, 'repo/other', @source.reference, false
+
+ refute_equal @source, source
+
+ source =
+ Gem::Source::Git.new 'b', @source.repository, @source.reference, false
+
+ refute_equal @source, source
+
+ source =
+ Gem::Source::Git.new @source.name, @source.repository, @source.reference,
+ true
+
+ refute_equal @source, source
+ end
+
+ def test_install_dir
+ @source.cache
+
+ expected = File.join Gem.dir, 'bundler', 'gems', "a-#{@head[0..11]}"
+
+ assert_equal expected, @source.install_dir
+ end
+
+ def test_install_dir_local
+ @source.remote = false
+
+ assert_nil @source.install_dir
+ end
+
+ def test_repo_cache_dir
+ expected =
+ File.join Gem.dir, 'cache', 'bundler', 'git', "a-#{@hash}"
+
+ assert_equal expected, @source.repo_cache_dir
+
+ @source.root_dir = "#{@gemhome}2"
+
+ expected =
+ File.join "#{@gemhome}2", 'cache', 'bundler', 'git', "a-#{@hash}"
+
+ assert_equal expected, @source.repo_cache_dir
+ end
+
+ def test_rev_parse
+ @source.cache
+
+ assert_equal @head, @source.rev_parse
+
+ Dir.chdir @repository do
+ system @git, 'checkout', '--quiet', '-b', 'other'
+ end
+
+ master_head = @head
+
+ git_gem 'a', 2
+
+ source = Gem::Source::Git.new @name, @repository, 'other', false
+
+ source.cache
+
+ refute_equal master_head, source.rev_parse
+
+ source = Gem::Source::Git.new @name, @repository, 'nonexistent', false
+
+ source.cache
+
+ e = assert_raises Gem::Exception do
+ capture_subprocess_io {source.rev_parse}
+ end
+
+ assert_equal "unable to find reference nonexistent in #{@repository}",
+ e.message
+ end
+
+ def test_root_dir
+ assert_equal Gem.dir, @source.root_dir
+
+ @source.root_dir = "#{@gemhome}2"
+
+ assert_equal "#{@gemhome}2", @source.root_dir
+ end
+
+ def test_spaceship
+ git = Gem::Source::Git.new 'a', 'git/a', 'master', false
+ remote = Gem::Source.new @gem_repo
+ installed = Gem::Source::Installed.new
+ vendor = Gem::Source::Vendor.new 'vendor/foo'
+
+ assert_equal( 0, git. <=>(git), 'git <=> git')
+
+ assert_equal( 1, git. <=>(remote), 'git <=> remote')
+ assert_equal(-1, remote. <=>(git), 'remote <=> git')
+
+ assert_equal( 1, git. <=>(installed), 'git <=> installed')
+ assert_equal(-1, installed.<=>(git), 'installed <=> git')
+
+ assert_equal(-1, git. <=>(vendor), 'git <=> vendor')
+ assert_equal( 1, vendor. <=>(git), 'vendor <=> git')
+ end
+
+ def test_specs
+ source = Gem::Source::Git.new @name, @repository, 'master', true
+
+ Dir.chdir 'git/a' do
+ FileUtils.mkdir 'b'
+
+ Dir.chdir 'b' do
+ b = Gem::Specification.new 'b', 1
+
+ open 'b.gemspec', 'w' do |io|
+ io.write b.to_ruby
+ end
+
+ system @git, 'add', 'b.gemspec'
+ system @git, 'commit', '--quiet', '-m', 'add b/b.gemspec'
+ end
+
+ FileUtils.touch 'c.gemspec'
+
+ system @git, 'add', 'c.gemspec'
+ system @git, 'commit', '--quiet', '-m', 'add c.gemspec'
+ end
+
+ specs = nil
+
+ capture_io do
+ specs = source.specs
+ end
+
+ assert_equal %w[a-1 b-1], specs.map { |spec| spec.full_name }
+
+ a_spec = specs.shift
+
+ base_dir = File.dirname File.dirname source.install_dir
+
+ assert_equal source.install_dir, a_spec.full_gem_path
+ assert_equal File.join(source.install_dir, 'a.gemspec'), a_spec.loaded_from
+ assert_equal base_dir, a_spec.base_dir
+
+ extension_dir =
+ File.join Gem.dir, 'bundler', 'extensions',
+ Gem::Platform.local.to_s, Gem.extension_api_version,
+ "a-#{source.dir_shortref}"
+
+ assert_equal extension_dir, a_spec.extension_dir
+
+ b_spec = specs.shift
+
+ assert_equal File.join(source.install_dir, 'b'), b_spec.full_gem_path
+ assert_equal File.join(source.install_dir, 'b', 'b.gemspec'),
+ b_spec.loaded_from
+ assert_equal base_dir, b_spec.base_dir
+
+ assert_equal extension_dir, b_spec.extension_dir
+ end
+
+ def test_specs_local
+ source = Gem::Source::Git.new @name, @repository, 'master', true
+ source.remote = false
+
+ capture_io do
+ assert_empty source.specs
+ end
+ end
+
+ def test_uri
+ assert_equal URI(@repository), @source.uri
+ end
+
+ def test_uri_hash
+ assert_equal @hash, @source.uri_hash
+
+ source =
+ Gem::Source::Git.new 'a', 'http://git@example/repo.git', 'master', false
+
+ assert_equal '291c4caac7feba8bb64c297987028acb3dde6cfe',
+ source.uri_hash
+
+ source =
+ Gem::Source::Git.new 'a', 'HTTP://git@EXAMPLE/repo.git', 'master', false
+
+ assert_equal '291c4caac7feba8bb64c297987028acb3dde6cfe',
+ source.uri_hash
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_source_installed.rb b/jni/ruby/test/rubygems/test_gem_source_installed.rb
new file mode 100644
index 0000000..9eaddf7
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source_installed.rb
@@ -0,0 +1,36 @@
+require 'rubygems/test_case'
+require 'rubygems/source'
+
+class TestGemSourceInstalled < Gem::TestCase
+
+ def test_spaceship
+ a1 = quick_gem 'a', '1'
+ util_build_gem a1
+
+ remote = Gem::Source.new @gem_repo
+ specific = Gem::Source::SpecificFile.new a1.cache_file
+ installed = Gem::Source::Installed.new
+ local = Gem::Source::Local.new
+ git = Gem::Source::Git.new 'a', 'a', 'master'
+ vendor = Gem::Source::Vendor.new 'a'
+
+ assert_equal( 0, installed.<=>(installed), 'installed <=> installed')
+
+ assert_equal(-1, remote. <=>(installed), 'remote <=> installed')
+ assert_equal( 1, installed.<=>(remote), 'installed <=> remote')
+
+ assert_equal( 1, installed.<=>(local), 'installed <=> local')
+ assert_equal(-1, local. <=>(installed), 'local <=> installed')
+
+ assert_equal(-1, specific. <=>(installed), 'specific <=> installed')
+ assert_equal( 1, installed.<=>(specific), 'installed <=> specific')
+
+ assert_equal( 1, git. <=>(installed), 'git <=> installed')
+ assert_equal(-1, installed.<=>(git), 'installed <=> git')
+
+ assert_equal( 1, vendor. <=>(installed), 'vendor <=> installed')
+ assert_equal(-1, installed.<=>(vendor), 'installed <=> vendor')
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_source_list.rb b/jni/ruby/test/rubygems/test_gem_source_list.rb
new file mode 100644
index 0000000..43db204
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source_list.rb
@@ -0,0 +1,111 @@
+require 'rubygems/source_list'
+require 'rubygems/test_case'
+
+class TestGemSourceList < Gem::TestCase
+ def setup
+ super
+
+ @uri = "http://example"
+ @source = Gem::Source.new(@uri)
+
+ @sl = Gem::SourceList.new
+ @sl << @source
+ end
+
+ def test_self_from
+ sl = Gem::SourceList.from [@uri]
+
+ assert_equal [Gem::Source.new(@uri)], sl.sources
+ end
+
+ def test_Enumerable
+ assert_includes Gem::SourceList.ancestors, Enumerable
+ end
+
+ def test_append
+ sl = Gem::SourceList.new
+ source = (sl << @uri)
+
+ assert_kind_of Gem::Source, source
+
+ assert_kind_of URI, source.uri
+ assert_equal source.uri.to_s, @uri
+
+ assert_equal [source], sl.sources
+ end
+
+ def test_clear
+ sl = Gem::SourceList.new
+
+ sl << 'http://source.example'
+
+ sl.clear
+
+ assert_empty sl
+ end
+
+ def test_replace
+ sl = Gem::SourceList.new
+ sl.replace [@uri]
+
+ assert_equal [@source], sl.sources
+ end
+
+ def test_each
+ @sl.each do |x|
+ assert_equal @uri, x
+ end
+ end
+
+ def test_each_source
+ @sl.each_source do |x|
+ assert_equal @source, x
+ end
+ end
+
+ def test_empty?
+ sl = Gem::SourceList.new
+
+ assert_empty sl
+
+ sl << 'http://source.example'
+
+ refute_empty sl
+ end
+
+ def test_equal_to_another_list
+ sl2 = Gem::SourceList.new
+ sl2 << Gem::Source.new(@uri)
+
+ assert @sl == sl2, "lists not equal"
+ end
+
+ def test_equal_to_array
+ assert @sl == [@uri], "lists not equal"
+ end
+
+ def test_to_a
+ assert_equal @sl.to_a, [@uri]
+ end
+
+ def test_include_eh
+ assert @sl.include?(@uri), "string comparison not working"
+ assert @sl.include?(URI.parse(@uri)), "uri comparison not working"
+ end
+
+ def test_include_matches_a_source
+ assert @sl.include?(@source), "source comparison not working"
+ assert @sl.include?(Gem::Source.new(@uri)), "source comparison not working"
+ end
+
+ def test_delete
+ @sl.delete @uri
+ assert_equal @sl.sources, []
+ end
+
+ def test_delete_a_source
+ @sl.delete Gem::Source.new(@uri)
+ assert_equal @sl.sources, []
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_source_local.rb b/jni/ruby/test/rubygems/test_gem_source_local.rb
new file mode 100644
index 0000000..19e1c4b
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source_local.rb
@@ -0,0 +1,106 @@
+require 'rubygems/test_case'
+require 'rubygems/source'
+
+require 'fileutils'
+
+class TestGemSourceLocal < Gem::TestCase
+ def setup
+ super
+
+ @sl = Gem::Source::Local.new
+
+ @a, @a_gem = util_gem "a", '1'
+ @ap, @ap_gem = util_gem "a", '2.a'
+ @b, @b_gem = util_gem "b", '1'
+
+ FileUtils.mv @a_gem, @tempdir
+ FileUtils.mv @ap_gem, @tempdir
+ FileUtils.mv @b_gem, @tempdir
+ end
+
+ def test_load_specs_released
+ assert_equal [@a.name_tuple, @b.name_tuple].sort,
+ @sl.load_specs(:released).sort
+ end
+
+ def test_load_specs_prerelease
+ assert_equal [@ap.name_tuple], @sl.load_specs(:prerelease)
+ end
+
+ def test_load_specs_latest
+ a2, a2_gem = util_gem "a", "2"
+
+ FileUtils.mv a2_gem, @tempdir
+
+ assert_equal [a2.name_tuple, @b.name_tuple].sort,
+ @sl.load_specs(:latest).sort
+ end
+
+ def test_find_gem
+ assert_equal "a-1", @sl.find_gem("a").full_name
+ end
+
+ def test_find_gem_highest_version
+ _, a2_gem = util_gem "a", "2"
+ FileUtils.mv a2_gem, @tempdir
+
+ assert_equal "a-2", @sl.find_gem("a").full_name
+ end
+
+ def test_find_gem_specific_version
+ _, a2_gem = util_gem "a", "2"
+ FileUtils.mv a2_gem, @tempdir
+
+ req = Gem::Requirement.create("= 1")
+
+ assert_equal "a-1", @sl.find_gem("a", req).full_name
+ end
+
+ def test_find_gem_prerelease
+ req = Gem::Requirement.create(">= 0")
+ assert_equal "a-2.a", @sl.find_gem("a", req, true).full_name
+ end
+
+ def test_fetch_spec
+ s = @sl.fetch_spec @a.name_tuple
+ assert_equal s, @a
+ end
+
+ def test_inspect
+ assert_equal '#<Gem::Source::Local specs: "NOT LOADED">', @sl.inspect
+
+ @sl.load_specs :released
+
+ inner = [@a, @ap, @b].map { |t| t.name_tuple }.inspect
+
+ assert_equal "#<Gem::Source::Local specs: #{inner}>", @sl.inspect
+ end
+
+ def test_download
+ path = @sl.download @a
+
+ assert_equal File.expand_path(@a.file_name), path
+ end
+
+ def test_spaceship
+ a1 = quick_gem 'a', '1'
+ util_build_gem a1
+
+ remote = Gem::Source.new @gem_repo
+ specific = Gem::Source::SpecificFile.new a1.cache_file
+ installed = Gem::Source::Installed.new
+ local = Gem::Source::Local.new
+
+ assert_equal( 0, local. <=>(local), 'local <=> local')
+
+ assert_equal(-1, remote. <=>(local), 'remote <=> local')
+ assert_equal( 1, local. <=>(remote), 'local <=> remote')
+
+ assert_equal( 1, installed.<=>(local), 'installed <=> local')
+ assert_equal(-1, local. <=>(installed), 'local <=> installed')
+
+ assert_equal(-1, specific. <=>(local), 'specific <=> local')
+ assert_equal( 1, local. <=>(specific), 'local <=> specific')
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_source_lock.rb b/jni/ruby/test/rubygems/test_gem_source_lock.rb
new file mode 100644
index 0000000..23f063d
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source_lock.rb
@@ -0,0 +1,114 @@
+require 'rubygems/test_case'
+
+class TestGemSourceLock < Gem::TestCase
+
+ def test_fetch_spec
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ name_tuple = Gem::NameTuple.new 'a', v(1), 'ruby'
+
+ remote = Gem::Source.new @gem_repo
+ lock = Gem::Source::Lock.new remote
+
+ spec = lock.fetch_spec name_tuple
+
+ assert_equal 'a-1', spec.full_name
+ end
+
+ def test_equals2
+ git = Gem::Source::Git.new 'a', 'git/a', 'master', false
+ g_lock = Gem::Source::Lock.new git
+
+ installed = Gem::Source::Installed.new
+ i_lock = Gem::Source::Lock.new installed
+
+ assert_equal g_lock, g_lock
+ refute_equal g_lock, i_lock
+ refute_equal g_lock, Object.new
+ end
+
+ def test_spaceship
+ git = Gem::Source::Git.new 'a', 'git/a', 'master', false
+ g_lock = Gem::Source::Lock.new git
+
+ installed = Gem::Source::Installed.new
+ i_lock = Gem::Source::Lock.new installed
+
+ vendor = Gem::Source::Vendor.new 'vendor/a'
+ v_lock = Gem::Source::Lock.new vendor
+
+ assert_equal( 0, g_lock.<=>(g_lock), 'g_lock <=> g_lock')
+ assert_equal( 0, i_lock.<=>(i_lock), 'i_lock <=> i_lock')
+ assert_equal( 0, v_lock.<=>(v_lock), 'v_lock <=> v_lock')
+
+ assert_equal( 1, g_lock.<=>(i_lock), 'g_lock <=> i_lock')
+ assert_equal(-1, i_lock.<=>(g_lock), 'i_lock <=> g_lock')
+
+ assert_equal(-1, g_lock.<=>(v_lock), 'g_lock <=> v_lock')
+ assert_equal( 1, v_lock.<=>(g_lock), 'v_lock <=> g_lock')
+
+ assert_equal(-1, i_lock.<=>(v_lock), 'i_lock <=> v_lock')
+ assert_equal( 1, v_lock.<=>(i_lock), 'i_lock <=> v_lock')
+ end
+
+ def test_spaceship_git
+ git = Gem::Source::Git.new 'a', 'git/a', 'master', false
+ lock = Gem::Source::Lock.new git
+
+ assert_equal( 1, lock.<=>(git), 'lock <=> git')
+ assert_equal(-1, git .<=>(lock), 'git <=> lock')
+ end
+
+ def test_spaceship_installed
+ installed = Gem::Source::Installed.new
+ lock = Gem::Source::Lock.new installed
+
+ assert_equal( 1, lock. <=>(installed), 'lock <=> installed')
+ assert_equal(-1, installed.<=>(lock), 'installed <=> lock')
+ end
+
+ def test_spaceship_local
+ local = Gem::Source::Local.new
+ lock = Gem::Source::Lock.new local # nonsense
+
+ assert_equal( 1, lock. <=>(local), 'lock <=> local')
+ assert_equal(-1, local.<=>(lock), 'local <=> lock')
+ end
+
+ def test_spaceship_remote
+ remote = Gem::Source.new @gem_repo
+ lock = Gem::Source::Lock.new remote
+
+ assert_equal( 1, lock. <=>(remote), 'lock <=> remote')
+ assert_equal(-1, remote.<=>(lock), 'remote <=> lock')
+ end
+
+ def test_spaceship_specific_file
+ _, gem = util_gem 'a', 1
+
+ specific = Gem::Source::SpecificFile.new gem
+ lock = Gem::Source::Lock.new specific # nonsense
+
+ assert_equal( 1, lock .<=>(specific), 'lock <=> specific')
+ assert_equal(-1, specific.<=>(lock), 'specific <=> lock')
+ end
+
+ def test_spaceship_vendor
+ vendor = Gem::Source::Vendor.new 'vendor/a'
+ lock = Gem::Source::Lock.new vendor
+
+ assert_equal( 1, lock. <=>(vendor), 'lock <=> vendor')
+ assert_equal(-1, vendor.<=>(lock), 'vendor <=> lock')
+ end
+
+ def test_uri
+ remote = Gem::Source.new @gem_repo
+ lock = Gem::Source::Lock.new remote
+
+ assert_equal URI(@gem_repo), lock.uri
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_source_specific_file.rb b/jni/ruby/test/rubygems/test_gem_source_specific_file.rb
new file mode 100644
index 0000000..12ef7f5
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source_specific_file.rb
@@ -0,0 +1,75 @@
+require 'rubygems/test_case'
+require 'rubygems/source'
+
+class TestGemSourceSpecificFile < Gem::TestCase
+ def setup
+ super
+
+ @a, @a_gem = util_gem "a", '1'
+ @sf = Gem::Source::SpecificFile.new(@a_gem)
+ end
+
+ def test_path
+ assert_equal @a_gem, @sf.path
+ end
+
+ def test_spec
+ assert_equal @a, @sf.spec
+ end
+
+ def test_load_specs
+ assert_equal [@a.name_tuple], @sf.load_specs
+ end
+
+ def test_fetch_spec
+ assert_equal @a, @sf.fetch_spec(@a.name_tuple)
+ end
+
+ def test_fetch_spec_fails_on_unknown_name
+ assert_raises Gem::Exception do
+ @sf.fetch_spec(nil)
+ end
+ end
+
+ def test_download
+ assert_equal @a_gem, @sf.download(@a)
+ end
+
+ def test_spaceship
+ a1 = quick_gem 'a', '1'
+ util_build_gem a1
+
+ remote = Gem::Source.new @gem_repo
+ specific = Gem::Source::SpecificFile.new a1.cache_file
+ installed = Gem::Source::Installed.new
+ local = Gem::Source::Local.new
+
+ assert_equal( 0, specific. <=>(specific), 'specific <=> specific')
+
+ assert_equal(-1, remote. <=>(specific), 'remote <=> specific')
+ assert_equal( 1, specific. <=>(remote), 'specific <=> remote')
+
+ assert_equal(-1, specific. <=>(local), 'specific <=> local')
+ assert_equal( 1, local. <=>(specific), 'local <=> specific')
+
+ assert_equal(-1, specific. <=>(installed), 'specific <=> installed')
+ assert_equal( 1, installed.<=>(specific), 'installed <=> specific')
+
+ a2 = quick_gem 'a', '2'
+ util_build_gem a2
+
+ b1 = quick_gem 'b', '1'
+ util_build_gem b1
+
+ a1_source = specific
+ a2_source = Gem::Source::SpecificFile.new a2.cache_file
+ b1_source = Gem::Source::SpecificFile.new b1.cache_file
+
+ assert_nil a1_source.<=>(b1_source), 'a1_source <=> b1_source'
+
+ assert_equal(-1, a1_source.<=>(a2_source), 'a1_source <=> a2_source')
+ assert_equal( 0, a1_source.<=>(a1_source), 'a1_source <=> a1_source')
+ assert_equal( 1, a2_source.<=>(a1_source), 'a2_source <=> a1_source')
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/test_gem_source_vendor.rb b/jni/ruby/test/rubygems/test_gem_source_vendor.rb
new file mode 100644
index 0000000..1d9ae35
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_source_vendor.rb
@@ -0,0 +1,31 @@
+require 'rubygems/test_case'
+require 'rubygems/source'
+
+class TestGemSourceVendor < Gem::TestCase
+
+ def test_initialize
+ source = Gem::Source::Vendor.new 'vendor/foo'
+
+ assert_equal 'vendor/foo', source.uri
+ end
+
+ def test_spaceship
+ vendor = Gem::Source::Vendor.new 'vendor/foo'
+ remote = Gem::Source.new @gem_repo
+ git = Gem::Source::Git.new 'a', 'a', 'master'
+ installed = Gem::Source::Installed.new
+
+ assert_equal( 0, vendor. <=>(vendor), 'vendor <=> vendor')
+
+ assert_equal( 1, vendor. <=>(remote), 'vendor <=> remote')
+ assert_equal(-1, remote. <=>(vendor), 'remote <=> vendor')
+
+ assert_equal( 1, vendor. <=>(git), 'vendor <=> git')
+ assert_equal(-1, git. <=>(vendor), 'git <=> vendor')
+
+ assert_equal( 1, vendor. <=>(installed), 'vendor <=> installed')
+ assert_equal(-1, installed.<=>(vendor), 'installed <=> vendor')
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_spec_fetcher.rb b/jni/ruby/test/rubygems/test_gem_spec_fetcher.rb
new file mode 100644
index 0000000..6ee1a52
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_spec_fetcher.rb
@@ -0,0 +1,310 @@
+require 'rubygems/test_case'
+require 'rubygems/spec_fetcher'
+
+class TestGemSpecFetcher < Gem::TestCase
+
+ def tuple(*args)
+ Gem::NameTuple.new(*args)
+ end
+
+ def setup
+ super
+
+ @uri = URI.parse @gem_repo
+ @source = Gem::Source.new(@uri)
+
+ @sf = Gem::SpecFetcher.new
+ end
+
+ def test_initialize
+ fetcher = Gem::SpecFetcher.new
+
+ assert_same Gem.sources, fetcher.sources
+ end
+
+ def test_initialize_source
+ alternate = 'http://alternate.example'
+ fetcher = Gem::SpecFetcher.new alternate
+
+ refute_same Gem.sources, fetcher.sources
+
+ assert_equal alternate, fetcher.sources
+ end
+
+ def test_initialize_nonexistent_home_dir
+ FileUtils.rmdir Gem.user_home
+
+ assert Gem::SpecFetcher.new
+ end
+
+ def test_initialize_unwritable_home_dir
+ skip 'chmod not supported' if Gem.win_platform?
+
+ FileUtils.chmod 0000, Gem.user_home
+
+ begin
+ assert Gem::SpecFetcher.new
+ ensure
+ FileUtils.chmod 0755, Gem.user_home
+ end
+ end
+
+ def test_spec_for_dependency_all
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '2.a'
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', '3.a'
+ end
+
+ dep = Gem::Dependency.new 'a', ">= 1"
+
+ specs_and_sources, _ = @sf.spec_for_dependency dep
+
+ spec_names = specs_and_sources.map do |spec, source_uri|
+ [spec.full_name, source_uri]
+ end
+
+ expected = [['a-1', @source], ['a-2', @source]]
+
+ assert_equal expected, spec_names
+
+ assert_same specs_and_sources.first.last, specs_and_sources.last.last
+ end
+
+ def test_spec_for_dependency_latest
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', '3.a'
+ end
+
+ dep = Gem::Dependency.new 'a'
+ specs_and_sources, _ = @sf.spec_for_dependency dep
+
+ spec_names = specs_and_sources.map do |spec, source_uri|
+ [spec.full_name, source_uri]
+ end
+
+ assert_equal [['a-2', Gem::Source.new(@gem_repo)]],
+ spec_names
+ end
+
+ def test_spec_for_dependency_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', '1.a'
+ fetcher.spec 'a', 1
+ end
+
+ specs_and_sources, _ = @sf.spec_for_dependency dep('a', '1.a')
+
+ spec_names = specs_and_sources.map do |spec, source_uri|
+ [spec.full_name, source_uri]
+ end
+
+ assert_equal [['a-1.a', Gem::Source.new(@gem_repo)]], spec_names
+ end
+
+ def test_spec_for_dependency_platform
+ util_set_arch 'i386-linux'
+
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ dep = Gem::Dependency.new 'pl', 1
+ specs_and_sources, _ = @sf.spec_for_dependency dep
+
+ spec_names = specs_and_sources.map do |spec, source_uri|
+ [spec.full_name, source_uri]
+ end
+
+ assert_equal [['pl-1-x86-linux', Gem::Source.new(@gem_repo)]],
+ spec_names
+ end
+
+ def test_spec_for_dependency_mismatched_platform
+ util_set_arch 'hrpa-989'
+
+ spec_fetcher do |fetcher|
+ fetcher.legacy_platform
+ end
+
+ dep = Gem::Dependency.new 'pl', 1
+ specs_and_sources, errors = @sf.spec_for_dependency dep
+
+ assert_equal 0, specs_and_sources.size
+ assert_equal 1, errors.size
+ pmm = errors.first
+
+ assert_equal "i386-linux", pmm.platforms.first
+ assert_equal "Found pl (1), but was for platform i386-linux", pmm.wordy
+ end
+
+ def test_spec_for_dependency_bad_fetch_spec
+ src = Gem::Source.new(@gem_repo)
+ def src.fetch_spec(name)
+ raise Gem::RemoteFetcher::FetchError.new("bad news from the internet", @uri)
+ end
+
+ Gem.sources.replace [src]
+
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '2.a'
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', '3.a'
+ end
+
+ dep = Gem::Dependency.new 'a', ">= 1"
+
+ specs_and_sources, errors = @sf.spec_for_dependency dep
+
+ assert_equal [], specs_and_sources
+ sfp = errors.first
+
+ assert_kind_of Gem::SourceFetchProblem, sfp
+ assert_equal src, sfp.source
+ assert_equal "bad news from the internet (#{@gem_repo})", sfp.error.message
+ end
+
+ def test_available_specs_latest
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.spec 'a', '3.a'
+ fetcher.legacy_platform
+ end
+
+ specs, _ = @sf.available_specs(:latest)
+
+ assert_equal [@source], specs.keys
+
+ expected = Gem::NameTuple.from_list \
+ [['a', v(2), Gem::Platform::RUBY],
+ ['pl', v(1), 'i386-linux']]
+
+ assert_equal expected, specs[@source]
+ end
+
+ def test_available_specs_released
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.legacy_platform
+ end
+
+ specs, _ = @sf.available_specs(:released)
+
+ assert_equal [@source], specs.keys
+
+ expected = Gem::NameTuple.from_list \
+ [['a', v(1), Gem::Platform::RUBY],
+ ['pl', v(1), 'i386-linux']]
+
+ assert_equal expected, specs[@source]
+ end
+
+ def test_available_specs_complete
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '2.a'
+ fetcher.spec 'b', 2
+ fetcher.legacy_platform
+ end
+
+ specs, _ = @sf.available_specs(:complete)
+
+ assert_equal [@source], specs.keys
+
+ expected = Gem::NameTuple.from_list \
+ [['a', v(1), Gem::Platform::RUBY],
+ ['a', v('2.a'), Gem::Platform::RUBY],
+ ['b', v(2), Gem::Platform::RUBY],
+ ['pl', v(1), 'i386-linux']]
+
+ assert_equal expected, specs[@source]
+ end
+
+ def test_available_specs_complete_handles_no_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '2.a'
+ fetcher.spec 'b', 2
+ fetcher.legacy_platform
+ end
+
+ v = Gem.marshal_version
+ @fetcher.data.delete "#{@gem_repo}prerelease_specs.#{v}.gz"
+
+ specs, _ = @sf.available_specs(:complete)
+
+ assert_equal [@source], specs.keys
+
+ expected = Gem::NameTuple.from_list \
+ [['a', v(1), Gem::Platform::RUBY],
+ ['b', v(2), Gem::Platform::RUBY],
+ ['pl', v(1), 'i386-linux']]
+
+ assert_equal expected, specs[@source]
+ end
+
+ def test_available_specs_cache
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ specs, _ = @sf.available_specs(:latest)
+
+ refute specs[@source].empty?
+
+ @fetcher.data["#{@gem_repo}/latest_specs.#{Gem.marshal_version}.gz"] = nil
+
+ cached_specs, _ = @sf.available_specs(:latest)
+
+ assert_equal specs, cached_specs
+ end
+
+ def test_available_specs_cache_released
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '2.a'
+ fetcher.spec 'b', 2
+ fetcher.legacy_platform
+ end
+
+ specs, _ = @sf.available_specs(:released)
+
+ refute specs[@source].empty?
+
+ @fetcher.data["#{@gem_repo}/specs.#{Gem.marshal_version}.gz"] = nil
+
+ cached_specs, _ = @sf.available_specs(:released)
+
+ assert_equal specs, cached_specs
+ end
+
+ def test_available_specs_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', '2.a'
+ end
+
+ specs, _ = @sf.available_specs(:prerelease)
+
+ expected = Gem::NameTuple.from_list \
+ [['a', v('2.a'), Gem::Platform::RUBY]]
+
+ assert_equal expected, specs[@source]
+ end
+
+ def test_available_specs_with_bad_source
+ Gem.sources.replace ["http://not-there.nothing"]
+
+ specs, errors = @sf.available_specs(:latest)
+
+ assert_equal({}, specs)
+ assert_kind_of Gem::SourceFetchProblem, errors.first
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_specification.rb b/jni/ruby/test/rubygems/test_gem_specification.rb
new file mode 100644
index 0000000..3cadc55
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_specification.rb
@@ -0,0 +1,3138 @@
+require 'rubygems/test_case'
+require 'pathname'
+require 'stringio'
+require 'rubygems/ext'
+require 'rubygems/specification'
+
+class TestGemSpecification < Gem::TestCase
+
+ LEGACY_YAML_SPEC = <<-EOF
+--- !ruby/object:Gem::Specification
+rubygems_version: "1.0"
+name: keyedlist
+version: !ruby/object:Gem::Version
+ version: 0.4.0
+date: 2004-03-28 15:37:49.828000 +02:00
+platform:
+summary: A Hash which automatically computes keys.
+require_paths:
+ - lib
+files:
+ - lib/keyedlist.rb
+autorequire: keyedlist
+author: Florian Gross
+email: flgr@ccan.de
+has_rdoc: true
+ EOF
+
+ LEGACY_RUBY_SPEC = <<-EOF
+Gem::Specification.new do |s|
+ s.name = %q{keyedlist}
+ s.version = %q{0.4.0}
+ s.has_rdoc = true
+ s.summary = %q{A Hash which automatically computes keys.}
+ s.files = [%q{lib/keyedlist.rb}]
+ s.require_paths = [%q{lib}]
+ s.autorequire = %q{keyedlist}
+ s.author = %q{Florian Gross}
+ s.email = %q{flgr@ccan.de}
+end
+ EOF
+
+ def make_spec_c1
+ @c1 = util_spec 'a', '1' do |s|
+ s.executable = 'exec'
+ s.extensions << 'ext/a/extconf.rb'
+ s.test_file = 'test/suite.rb'
+ s.requirements << 'A working computer'
+ s.rubyforge_project = 'example'
+ s.license = 'MIT'
+
+ s.add_dependency 'rake', '> 0.4'
+ s.add_dependency 'jabber4r', '> 0.0.0'
+ s.add_dependency 'pqa', ['> 0.4', '<= 0.6']
+
+ s.mark_version
+ s.files = %w[lib/code.rb]
+ end
+ end
+
+ def ext_spec
+ @ext = util_spec 'ext', '1' do |s|
+ s.executable = 'exec'
+ s.test_file = 'test/suite.rb'
+ s.extensions = %w[ext/extconf.rb]
+ s.license = 'MIT'
+
+ s.mark_version
+ s.files = %w[lib/code.rb]
+ s.installed_by_version = v('2.2')
+ end
+ end
+
+ def setup
+ super
+
+ @a1 = util_spec 'a', '1' do |s|
+ s.executable = 'exec'
+ s.test_file = 'test/suite.rb'
+ s.requirements << 'A working computer'
+ s.rubyforge_project = 'example'
+ s.license = 'MIT'
+
+ s.mark_version
+ s.files = %w[lib/code.rb]
+ end
+
+ @a2 = util_spec 'a', '2' do |s|
+ s.files = %w[lib/code.rb]
+ end
+
+ @a3 = util_spec 'a', '3' do |s|
+ s.metadata['allowed_push_host'] = "https://privategemserver.com"
+ end
+
+ @current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+
+ load 'rubygems/syck_hack.rb'
+ end
+
+ def test_self_activate
+ foo = util_spec 'foo', '1'
+
+ assert_activate %w[foo-1], foo
+ end
+
+ def test_self_activate_ambiguous_direct
+ save_loaded_features do
+ a1 = new_spec "a", "1", "b" => "> 0"
+ b1 = new_spec("b", "1", { "c" => ">= 1" }, "lib/d.rb")
+ b2 = new_spec("b", "2", { "c" => ">= 2" }, "lib/d.rb")
+ c1 = new_spec "c", "1"
+ c2 = new_spec "c", "2"
+
+ Gem::Specification.reset
+ install_specs a1, b1, b2, c1, c2
+
+ a1.activate
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ require "d"
+
+ assert_equal %w(a-1 b-2 c-2), loaded_spec_names
+ assert_equal [], unresolved_names
+ end
+ end
+
+ def test_self_activate_ambiguous_indirect
+ save_loaded_features do
+ a1 = new_spec "a", "1", "b" => "> 0"
+ b1 = new_spec "b", "1", "c" => ">= 1"
+ b2 = new_spec "b", "2", "c" => ">= 2"
+ c1 = new_spec "c", "1", nil, "lib/d.rb"
+ c2 = new_spec "c", "2", nil, "lib/d.rb"
+
+ install_specs a1, b1, b2, c1, c2
+
+ a1.activate
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ require "d"
+
+ assert_equal %w(a-1 b-2 c-2), loaded_spec_names
+ assert_equal [], unresolved_names
+ end
+ end
+
+ def test_self_activate_ambiguous_indirect_conflict
+ save_loaded_features do
+ a1 = new_spec "a", "1", "b" => "> 0"
+ a2 = new_spec "a", "2", "b" => "> 0"
+ b1 = new_spec "b", "1", "c" => ">= 1"
+ b2 = new_spec "b", "2", "c" => ">= 2"
+ c1 = new_spec "c", "1", nil, "lib/d.rb"
+ c2 = new_spec("c", "2", { "a" => "1" }, "lib/d.rb") # conflicts with a-2
+
+ install_specs a1, a2, b1, b2, c1, c2
+
+ a2.activate
+ assert_equal %w(a-2), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ require "d"
+
+ assert_equal %w(a-2 b-1 c-1), loaded_spec_names
+ assert_equal [], unresolved_names
+ end
+ end
+
+ def test_self_activate_ambiguous_unrelated
+ save_loaded_features do
+ a1 = new_spec "a", "1", "b" => "> 0"
+ b1 = new_spec "b", "1", "c" => ">= 1"
+ b2 = new_spec "b", "2", "c" => ">= 2"
+ c1 = new_spec "c", "1"
+ c2 = new_spec "c", "2"
+ d1 = new_spec "d", "1", nil, "lib/d.rb"
+
+ install_specs a1, b1, b2, c1, c2, d1
+
+ a1.activate
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ require "d"
+
+ assert_equal %w(a-1 d-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+ end
+ end
+
+ ##
+ # [A] depends on
+ # [C] = 1.0 depends on
+ # [B] = 2.0
+ # [B] ~> 1.0 (satisfied by 1.0)
+
+ def test_self_activate_checks_dependencies
+ a, _ = util_spec 'a', '1.0'
+ a.add_dependency 'c', '= 1.0'
+ a.add_dependency 'b', '~> 1.0'
+
+ util_spec 'b', '1.0'
+ util_spec 'b', '2.0'
+ c, _ = util_spec 'c', '1.0', 'b' => '= 2.0'
+
+ e = assert_raises Gem::LoadError do
+ assert_activate nil, a, c, "b"
+ end
+
+ expected = "can't satisfy 'b (~> 1.0)', already activated 'b-2.0'"
+ assert_equal expected, e.message
+ end
+
+ ##
+ # [A] depends on
+ # [B] ~> 1.0 (satisfied by 1.0)
+ # [C] = 1.0 depends on
+ # [B] = 2.0
+
+ def test_self_activate_divergent
+ a, _ = util_spec 'a', '1.0', 'b' => '~> 1.0', 'c' => '= 1.0'
+ util_spec 'b', '1.0'
+ util_spec 'b', '2.0'
+ c, _ = util_spec 'c', '1.0', 'b' => '= 2.0'
+
+ e = assert_raises Gem::ConflictError do
+ assert_activate nil, a, c, "b"
+ end
+
+ assert_match(/Unable to activate c-1.0,/, e.message)
+ assert_match(/because b-1.0 conflicts with b .= 2.0/, e.message)
+ end
+
+ ##
+ # DOC
+
+ def test_self_activate_old_required
+ e1, = util_spec 'e', '1', 'd' => '= 1'
+ @d1 = util_spec 'd', '1'
+ @d2 = util_spec 'd', '2'
+
+ assert_activate %w[d-1 e-1], e1, "d"
+ end
+
+ ##
+ # DOC
+
+ def test_self_activate_platform_alternate
+ @x1_m = util_spec 'x', '1' do |s|
+ s.platform = Gem::Platform.new %w[cpu my_platform 1]
+ end
+
+ @x1_o = util_spec 'x', '1' do |s|
+ s.platform = Gem::Platform.new %w[cpu other_platform 1]
+ end
+
+ @w1 = util_spec 'w', '1', 'x' => nil
+
+ util_set_arch 'cpu-my_platform1'
+
+ assert_activate %w[x-1-cpu-my_platform-1 w-1], @w1, @x1_m
+ end
+
+ ##
+ # DOC
+
+ def test_self_activate_platform_bump
+ @y1 = util_spec 'y', '1'
+
+ @y1_1_p = util_spec 'y', '1.1' do |s|
+ s.platform = Gem::Platform.new %w[cpu my_platform 1]
+ end
+
+ @z1 = util_spec 'z', '1', 'y' => nil
+
+ assert_activate %w[y-1 z-1], @z1, @y1
+ end
+
+ ##
+ # [C] depends on
+ # [A] = 1.a
+ # [B] = 1.0 depends on
+ # [A] >= 0 (satisfied by 1.a)
+
+ def test_self_activate_prerelease
+ @c1_pre = util_spec 'c', '1.a', "a" => "1.a", "b" => "1"
+ @a1_pre = util_spec 'a', '1.a'
+ @b1 = util_spec 'b', '1' do |s|
+ s.add_dependency 'a'
+ s.add_development_dependency 'aa'
+ end
+
+ assert_activate %w[a-1.a b-1 c-1.a], @c1_pre, @a1_pre, @b1
+ end
+
+ def test_self_activate_via_require
+ a1 = new_spec "a", "1", "b" => "= 1"
+ b1 = new_spec "b", "1", nil, "lib/b/c.rb"
+ b2 = new_spec "b", "2", nil, "lib/b/c.rb"
+
+ install_specs a1, b1, b2
+
+ a1.activate
+ save_loaded_features do
+ require "b/c"
+ end
+
+ assert_equal %w(a-1 b-1), loaded_spec_names
+ end
+
+ def test_self_activate_via_require_wtf
+ save_loaded_features do
+ a1 = new_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
+ b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb"
+ b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb" # this
+ c1 = new_spec "c", "1"
+ c2 = new_spec "c", "2" # this
+ d1 = new_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
+ d2 = new_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
+
+ install_specs a1, b1, b2, c1, c2, d1, d2
+
+ a1.activate
+
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal ["b (> 0)", "d (> 0)"], unresolved_names
+
+ require "b"
+
+ e = assert_raises Gem::LoadError do
+ require "d"
+ end
+
+ assert_equal "unable to find a version of 'd' to activate", e.message
+
+ assert_equal %w(a-1 b-2 c-2), loaded_spec_names
+ assert_equal ["d (> 0)"], unresolved_names
+ end
+ end
+
+ def test_self_activate_deep_unambiguous
+ a1 = new_spec "a", "1", "b" => "= 1"
+ b1 = new_spec "b", "1", "c" => "= 1"
+ b2 = new_spec "b", "2", "c" => "= 2"
+ c1 = new_spec "c", "1"
+ c2 = new_spec "c", "2"
+
+ install_specs a1, b1, b2, c1, c2
+
+ a1.activate
+ assert_equal %w(a-1 b-1 c-1), loaded_spec_names
+ end
+
+ def test_self_activate_loaded
+ foo = util_spec 'foo', '1'
+
+ assert foo.activate
+ refute foo.activate
+ end
+
+ ##
+ # [A] depends on
+ # [B] >= 1.0 (satisfied by 2.0)
+ # [C] depends on nothing
+
+ def test_self_activate_unrelated
+ a = util_spec 'a', '1.0', 'b' => '>= 1.0'
+ util_spec 'b', '1.0'
+ c = util_spec 'c', '1.0'
+
+ assert_activate %w[b-1.0 c-1.0 a-1.0], a, c, "b"
+ end
+
+ ##
+ # [A] depends on
+ # [B] >= 1.0 (satisfied by 2.0)
+ # [C] = 1.0 depends on
+ # [B] ~> 1.0
+ #
+ # and should resolve using b-1.0
+ # TODO: move these to specification
+
+ def test_self_activate_over
+ a = util_spec 'a', '1.0', 'b' => '>= 1.0', 'c' => '= 1.0'
+ util_spec 'b', '1.0'
+ util_spec 'b', '1.1'
+ util_spec 'b', '2.0'
+ util_spec 'c', '1.0', 'b' => '~> 1.0'
+
+ a.activate
+
+ assert_equal %w[a-1.0 c-1.0], loaded_spec_names
+ assert_equal ["b (>= 1.0, ~> 1.0)"], unresolved_names
+ end
+
+ ##
+ # [A] depends on
+ # [B] ~> 1.0 (satisfied by 1.1)
+ # [C] = 1.0 depends on
+ # [B] = 1.0
+ #
+ # and should resolve using b-1.0
+ #
+ # TODO: this is not under, but over... under would require depth
+ # first resolve through a dependency that is later pruned.
+
+ def test_self_activate_under
+ a, _ = util_spec 'a', '1.0', 'b' => '~> 1.0', 'c' => '= 1.0'
+ util_spec 'b', '1.0'
+ util_spec 'b', '1.1'
+ c, _ = util_spec 'c', '1.0', 'b' => '= 1.0'
+
+ assert_activate %w[b-1.0 c-1.0 a-1.0], a, c, "b"
+ end
+
+ ##
+ # [A1] depends on
+ # [B] > 0 (satisfied by 2.0)
+ # [B1] depends on
+ # [C] > 0 (satisfied by 1.0)
+ # [B2] depends on nothing!
+ # [C1] depends on nothing
+
+ def test_self_activate_dropped
+ a1, = util_spec 'a', '1', 'b' => nil
+ util_spec 'b', '1', 'c' => nil
+ util_spec 'b', '2'
+ util_spec 'c', '1'
+
+ assert_activate %w[b-2 a-1], a1, "b"
+ end
+
+ ##
+ # [A] depends on
+ # [B] >= 1.0 (satisfied by 1.1) depends on
+ # [Z]
+ # [C] >= 1.0 depends on
+ # [B] = 1.0
+ #
+ # and should backtrack to resolve using b-1.0, pruning Z from the
+ # resolve.
+
+ def test_self_activate_raggi_the_edgecase_generator
+ a, _ = util_spec 'a', '1.0', 'b' => '>= 1.0', 'c' => '>= 1.0'
+ util_spec 'b', '1.0'
+ util_spec 'b', '1.1', 'z' => '>= 1.0'
+ c, _ = util_spec 'c', '1.0', 'b' => '= 1.0'
+
+ assert_activate %w[b-1.0 c-1.0 a-1.0], a, c, "b"
+ end
+
+ def test_self_activate_conflict
+ util_spec 'b', '1.0'
+ util_spec 'b', '2.0'
+
+ gem "b", "= 1.0"
+
+ assert_raises Gem::LoadError do
+ gem "b", "= 2.0"
+ end
+ end
+
+ def test_self_all_equals
+ a = new_spec "foo", "1", nil, "lib/foo.rb"
+
+ Gem::Specification.all = [a]
+
+ assert_equal a, Gem::Specification.find_inactive_by_path('foo')
+ end
+
+ def test_self_attribute_names
+ expected_value = %w[
+ authors
+ autorequire
+ bindir
+ cert_chain
+ date
+ dependencies
+ description
+ email
+ executables
+ extensions
+ extra_rdoc_files
+ files
+ homepage
+ licenses
+ metadata
+ name
+ platform
+ post_install_message
+ rdoc_options
+ require_paths
+ required_ruby_version
+ required_rubygems_version
+ requirements
+ rubyforge_project
+ rubygems_version
+ signing_key
+ specification_version
+ summary
+ test_files
+ version
+ ]
+
+ actual_value = Gem::Specification.attribute_names.map { |a| a.to_s }.sort
+
+ assert_equal expected_value, actual_value
+ end
+
+ def test_self__load_future
+ spec = Gem::Specification.new
+ spec.name = 'a'
+ spec.version = '1'
+ spec.specification_version = @current_version + 1
+
+ new_spec = Marshal.load Marshal.dump(spec)
+
+ assert_equal 'a', new_spec.name
+ assert_equal Gem::Version.new(1), new_spec.version
+ assert_equal @current_version, new_spec.specification_version
+ end
+
+ def test_self_from_yaml
+ @a1.instance_variable_set :@specification_version, nil
+
+ spec = Gem::Specification.from_yaml @a1.to_yaml
+
+ assert_equal Gem::Specification::NONEXISTENT_SPECIFICATION_VERSION,
+ spec.specification_version
+ end
+
+ def test_self_from_yaml_syck_date_bug
+ # This is equivalent to (and totally valid) psych 1.0 output and
+ # causes parse errors on syck.
+ yaml = @a1.to_yaml
+ yaml.sub!(/^date:.*/, "date: 2011-04-26 00:00:00.000000000Z")
+
+ new_spec = with_syck do
+ Gem::Specification.from_yaml yaml
+ end
+
+ assert_kind_of Time, @a1.date
+ assert_kind_of Time, new_spec.date
+ end
+
+ def test_self_from_yaml_syck_default_key_bug
+ # This is equivalent to (and totally valid) psych 1.0 output and
+ # causes parse errors on syck.
+ yaml = <<-YAML
+--- !ruby/object:Gem::Specification
+name: posix-spawn
+version: !ruby/object:Gem::Version
+ version: 0.3.6
+ prerelease:
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: rake-compiler
+ requirement: &70243867725240 !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - =
+ - !ruby/object:Gem::Version
+ version: 0.7.6
+ type: :development
+ prerelease: false
+ version_requirements: *70243867725240
+platform: ruby
+files: []
+test_files: []
+bindir:
+ YAML
+
+ new_spec = with_syck do
+ Gem::Specification.from_yaml yaml
+ end
+
+ op = new_spec.dependencies.first.requirement.requirements.first.first
+ refute_kind_of YAML::Syck::DefaultKey, op
+
+ refute_match %r%DefaultKey%, new_spec.to_ruby
+ end
+
+ def test_self_from_yaml_cleans_up_defaultkey
+ yaml = <<-YAML
+--- !ruby/object:Gem::Specification
+name: posix-spawn
+version: !ruby/object:Gem::Version
+ version: 0.3.6
+ prerelease:
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: rake-compiler
+ requirement: &70243867725240 !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - !ruby/object:YAML::Syck::DefaultKey {}
+
+ - !ruby/object:Gem::Version
+ version: 0.7.6
+ type: :development
+ prerelease: false
+ version_requirements: *70243867725240
+platform: ruby
+files: []
+test_files: []
+bindir:
+ YAML
+
+ new_spec = Gem::Specification.from_yaml yaml
+
+ op = new_spec.dependencies.first.requirement.requirements.first.first
+ refute_kind_of YAML::Syck::DefaultKey, op
+
+ refute_match %r%DefaultKey%, new_spec.to_ruby
+ end
+
+ def test_self_from_yaml_cleans_up_defaultkey_from_newer_192
+ yaml = <<-YAML
+--- !ruby/object:Gem::Specification
+name: posix-spawn
+version: !ruby/object:Gem::Version
+ version: 0.3.6
+ prerelease:
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: rake-compiler
+ requirement: &70243867725240 !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - !ruby/object:Syck::DefaultKey {}
+
+ - !ruby/object:Gem::Version
+ version: 0.7.6
+ type: :development
+ prerelease: false
+ version_requirements: *70243867725240
+platform: ruby
+files: []
+test_files: []
+bindir:
+ YAML
+
+ new_spec = Gem::Specification.from_yaml yaml
+
+ op = new_spec.dependencies.first.requirement.requirements.first.first
+ refute_kind_of YAML::Syck::DefaultKey, op
+
+ refute_match %r%DefaultKey%, new_spec.to_ruby
+ end
+
+ def test_self_from_yaml_cleans_up_Date_objects
+ yaml = <<-YAML
+--- !ruby/object:Gem::Specification
+rubygems_version: 0.8.1
+specification_version: 1
+name: diff-lcs
+version: !ruby/object:Gem::Version
+ version: 1.1.2
+date: 2004-10-20
+summary: Provides a list of changes that represent the difference between two sequenced collections.
+require_paths:
+ - lib
+author: Austin Ziegler
+email: diff-lcs@halostatue.ca
+homepage: http://rubyforge.org/projects/ruwiki/
+rubyforge_project: ruwiki
+description: "Test"
+bindir: bin
+has_rdoc: true
+required_ruby_version: !ruby/object:Gem::Version::Requirement
+ requirements:
+ -
+ - ">="
+ - !ruby/object:Gem::Version
+ version: 1.8.1
+ version:
+platform: ruby
+files:
+ - tests/00test.rb
+rdoc_options:
+ - "--title"
+ - "Diff::LCS -- A Diff Algorithm"
+ - "--main"
+ - README
+ - "--line-numbers"
+extra_rdoc_files:
+ - README
+ - ChangeLog
+ - Install
+executables:
+ - ldiff
+ - htmldiff
+extensions: []
+requirements: []
+dependencies: []
+ YAML
+
+ new_spec = Gem::Specification.from_yaml yaml
+
+ assert_kind_of Time, new_spec.date
+ end
+
+ def test_self_load
+ full_path = @a2.spec_file
+ write_file full_path do |io|
+ io.write @a2.to_ruby_for_cache
+ end
+
+ spec = Gem::Specification.load full_path
+
+ @a2.files.clear
+
+ assert_equal @a2, spec
+ end
+
+ def test_self_load_relative
+ open 'a-2.gemspec', 'w' do |io|
+ io.write @a2.to_ruby_for_cache
+ end
+
+ spec = Gem::Specification.load 'a-2.gemspec'
+
+ @a2.files.clear
+
+ assert_equal @a2, spec
+
+ assert_equal File.join(@tempdir, 'a-2.gemspec'), spec.loaded_from
+ end
+
+ def test_self_load_tainted
+ full_path = @a2.spec_file
+ write_file full_path do |io|
+ io.write @a2.to_ruby_for_cache
+ end
+
+ full_path.taint
+ loader = Thread.new { $SAFE = 1; Gem::Specification.load full_path }
+ spec = loader.value
+
+ @a2.files.clear
+
+ assert_equal @a2, spec
+ end
+
+ def test_self_load_escape_curly
+ @a2.name = 'a};raise "improper escaping";%q{'
+
+ full_path = @a2.spec_file
+ write_file full_path do |io|
+ io.write @a2.to_ruby_for_cache
+ end
+
+ spec = Gem::Specification.load full_path
+
+ @a2.files.clear
+
+ assert_equal @a2, spec
+ end
+
+ def test_self_load_escape_interpolation
+ @a2.name = 'a#{raise %<improper escaping>}'
+
+ full_path = @a2.spec_file
+ write_file full_path do |io|
+ io.write @a2.to_ruby_for_cache
+ end
+
+ spec = Gem::Specification.load full_path
+
+ @a2.files.clear
+
+ assert_equal @a2, spec
+ end
+
+ def test_self_load_escape_quote
+ @a2.name = 'a";raise "improper escaping";"'
+
+ full_path = @a2.spec_file
+ write_file full_path do |io|
+ io.write @a2.to_ruby_for_cache
+ end
+
+ spec = Gem::Specification.load full_path
+
+ @a2.files.clear
+
+ assert_equal @a2, spec
+ end
+
+ if defined?(Encoding)
+ def test_self_load_utf8_with_ascii_encoding
+ int_enc = Encoding.default_internal
+ silence_warnings { Encoding.default_internal = 'US-ASCII' }
+
+ spec2 = @a2.dup
+ bin = "\u5678"
+ spec2.authors = [bin]
+ full_path = spec2.spec_file
+ write_file full_path do |io|
+ io.write spec2.to_ruby_for_cache.force_encoding('BINARY').sub("\\u{5678}", bin.force_encoding('BINARY'))
+ end
+
+ spec = Gem::Specification.load full_path
+
+ spec2.files.clear
+
+ assert_equal spec2, spec
+ ensure
+ silence_warnings { Encoding.default_internal = int_enc }
+ end
+ end
+
+ def test_self_load_legacy_ruby
+ spec = Gem::Deprecate.skip_during do
+ eval LEGACY_RUBY_SPEC
+ end
+ assert_equal 'keyedlist', spec.name
+ assert_equal '0.4.0', spec.version.to_s
+ assert_equal Gem::Specification::TODAY, spec.date
+ assert spec.required_ruby_version.satisfied_by?(Gem::Version.new('1'))
+ assert_equal false, spec.has_unit_tests?
+ end
+
+ def test_self_normalize_yaml_input_with_183_yaml
+ input = "!ruby/object:Gem::Specification "
+ assert_equal "--- #{input}", Gem::Specification.normalize_yaml_input(input)
+ end
+
+ def test_self_normalize_yaml_input_with_non_183_yaml
+ input = "--- !ruby/object:Gem::Specification "
+ assert_equal input, Gem::Specification.normalize_yaml_input(input)
+ end
+
+ def test_self_normalize_yaml_input_with_183_io
+ input = "!ruby/object:Gem::Specification "
+ assert_equal "--- #{input}",
+ Gem::Specification.normalize_yaml_input(StringIO.new(input))
+ end
+
+ def test_self_normalize_yaml_input_with_non_183_io
+ input = "--- !ruby/object:Gem::Specification "
+ assert_equal input,
+ Gem::Specification.normalize_yaml_input(StringIO.new(input))
+ end
+
+ def test_self_normalize_yaml_input_with_192_yaml
+ input = "--- !ruby/object:Gem::Specification \nblah: !!null \n"
+ expected = "--- !ruby/object:Gem::Specification \nblah: \n"
+
+ assert_equal expected, Gem::Specification.normalize_yaml_input(input)
+ end
+
+ def test_self_outdated
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 4
+
+ fetcher.clear
+
+ fetcher.spec 'a', 3
+ end
+
+ assert_equal %w[a], Gem::Specification.outdated
+ end
+
+ def test_self_outdated_and_latest_remotes
+ specs = spec_fetcher do |fetcher|
+ fetcher.spec 'a', 4
+ fetcher.spec 'b', 3
+
+ fetcher.clear
+
+ fetcher.spec 'a', '3.a'
+ fetcher.spec 'b', 2
+ end
+
+ expected = [
+ [specs['a-3.a'], v(4)],
+ [specs['b-2'], v(3)],
+ ]
+
+ assert_equal expected, Gem::Specification.outdated_and_latest_version.to_a
+ end
+
+ def test_self_remove_spec
+ assert_includes Gem::Specification.all_names, 'a-1'
+ assert_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
+
+ Gem::Specification.remove_spec @a1
+
+ refute_includes Gem::Specification.all_names, 'a-1'
+ refute_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
+ end
+
+ def test_self_remove_spec_removed
+ open @a1.spec_file, 'w' do |io|
+ io.write @a1.to_ruby
+ end
+
+ Gem::Specification.reset
+
+ FileUtils.rm @a1.spec_file # bug #698
+
+ Gem::Specification.remove_spec @a1
+
+ refute_includes Gem::Specification.all_names, 'a-1'
+ refute_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
+ end
+
+ DATA_PATH = File.expand_path "../data", __FILE__
+
+ def test_handles_private_null_type
+ path = File.join DATA_PATH, "null-type.gemspec.rz"
+
+ data = Marshal.load Gem.inflate(Gem.read_binary(path))
+
+ assert_equal nil, data.rubyforge_project
+ end
+
+ def test_emits_zulu_timestamps_properly
+ t = Time.utc(2012, 3, 12)
+ @a2.date = t
+
+ yaml = with_psych { @a2.to_yaml }
+
+ assert_match %r!date: 2012-03-12 00:00:00\.000000000 Z!, yaml
+ end if RUBY_VERSION =~ /1\.9\.2/
+
+ def test_initialize
+ spec = Gem::Specification.new do |s|
+ s.name = "blah"
+ s.version = "1.3.5"
+ end
+
+ assert_equal "blah", spec.name
+ assert_equal "1.3.5", spec.version.to_s
+ assert_equal Gem::Platform::RUBY, spec.platform
+ assert_equal nil, spec.summary
+ assert_equal [], spec.files
+
+ assert_equal [], spec.test_files
+ assert_equal [], spec.rdoc_options
+ assert_equal [], spec.extra_rdoc_files
+ assert_equal [], spec.executables
+ assert_equal [], spec.extensions
+ assert_equal [], spec.requirements
+ assert_equal [], spec.dependencies
+ assert_equal 'bin', spec.bindir
+ assert_equal '>= 0', spec.required_ruby_version.to_s
+ assert_equal '>= 0', spec.required_rubygems_version.to_s
+ end
+
+ def test_initialize_future
+ version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + 1
+ spec = Gem::Specification.new do |s|
+ s.name = "blah"
+ s.version = "1.3.5"
+
+ s.specification_version = version
+
+ s.new_unknown_attribute = "a value"
+ end
+
+ assert_equal "blah", spec.name
+ assert_equal "1.3.5", spec.version.to_s
+ end
+
+ def test_initialize_copy
+ spec = Gem::Specification.new do |s|
+ s.name = "blah"
+ s.version = "1.3.5"
+ s.summary = 'summary'
+ s.description = 'description'
+ s.authors = 'author a', 'author b'
+ s.licenses = 'BSD'
+ s.files = 'lib/file.rb'
+ s.test_files = 'test/file.rb'
+ s.rdoc_options = '--foo'
+ s.extra_rdoc_files = 'README.txt'
+ s.executables = 'exec'
+ s.extensions = 'ext/extconf.rb'
+ s.requirements = 'requirement'
+ s.add_dependency 'some_gem'
+ end
+
+ new_spec = spec.dup
+
+ assert_equal "blah", spec.name
+ assert_same spec.name, new_spec.name
+
+ assert_equal "1.3.5", spec.version.to_s
+ assert_same spec.version, new_spec.version
+
+ assert_equal Gem::Platform::RUBY, spec.platform
+ assert_same spec.platform, new_spec.platform
+
+ assert_equal 'summary', spec.summary
+ assert_same spec.summary, new_spec.summary
+
+ assert_equal %w[README.txt bin/exec ext/extconf.rb lib/file.rb
+ test/file.rb].sort,
+ spec.files
+ refute_same spec.files, new_spec.files, 'files'
+
+ assert_equal %w[test/file.rb], spec.test_files
+ refute_same spec.test_files, new_spec.test_files, 'test_files'
+
+ assert_equal %w[--foo], spec.rdoc_options
+ refute_same spec.rdoc_options, new_spec.rdoc_options, 'rdoc_options'
+
+ assert_equal %w[README.txt], spec.extra_rdoc_files
+ refute_same spec.extra_rdoc_files, new_spec.extra_rdoc_files,
+ 'extra_rdoc_files'
+
+ assert_equal %w[exec], spec.executables
+ refute_same spec.executables, new_spec.executables, 'executables'
+
+ assert_equal %w[ext/extconf.rb], spec.extensions
+ refute_same spec.extensions, new_spec.extensions, 'extensions'
+
+ assert_equal %w[requirement], spec.requirements
+ refute_same spec.requirements, new_spec.requirements, 'requirements'
+
+ assert_equal [Gem::Dependency.new('some_gem', Gem::Requirement.default)],
+ spec.dependencies
+ refute_same spec.dependencies, new_spec.dependencies, 'dependencies'
+
+ assert_equal 'bin', spec.bindir
+ assert_same spec.bindir, new_spec.bindir
+
+ assert_equal '>= 0', spec.required_ruby_version.to_s
+ assert_same spec.required_ruby_version, new_spec.required_ruby_version
+
+ assert_equal '>= 0', spec.required_rubygems_version.to_s
+ assert_same spec.required_rubygems_version,
+ new_spec.required_rubygems_version
+ end
+
+ def test_initialize_copy_broken
+ spec = Gem::Specification.new do |s|
+ s.name = 'a'
+ s.version = '1'
+ end
+
+ spec.instance_variable_set :@licenses, :blah
+ spec.loaded_from = '/path/to/file'
+
+ e = assert_raises Gem::FormatException do
+ spec.dup
+ end
+
+ assert_equal 'a-1 has an invalid value for @licenses', e.message
+ assert_equal '/path/to/file', e.file_path
+ end
+
+ def test__dump
+ @a2.platform = Gem::Platform.local
+ @a2.instance_variable_set :@original_platform, 'old_platform'
+
+ data = Marshal.dump @a2
+
+ same_spec = Marshal.load data
+
+ assert_equal 'old_platform', same_spec.original_platform
+ end
+
+ def test_activate
+ @a2.activate
+
+ assert @a2.activated?
+ end
+
+ def test_add_dependency_with_type
+ gem = util_spec "awesome", "1.0" do |awesome|
+ awesome.add_dependency true
+ awesome.add_dependency :gem_name
+ end
+
+ assert_equal %w[true gem_name], gem.dependencies.map { |dep| dep.name }
+ end
+
+ def test_add_dependency_with_type_explicit
+ gem = util_spec "awesome", "1.0" do |awesome|
+ awesome.add_development_dependency "monkey"
+ end
+
+ monkey = gem.dependencies.detect { |d| d.name == "monkey" }
+ assert_equal(:development, monkey.type)
+ end
+
+ def test_author
+ assert_equal 'A User', @a1.author
+ end
+
+ def test_authors
+ assert_equal ['A User'], @a1.authors
+ end
+
+ def test_bindir_equals
+ @a1.bindir = 'apps'
+
+ assert_equal 'apps', @a1.bindir
+ end
+
+ def test_bindir_equals_nil
+ @a2.bindir = nil
+ @a2.executable = 'app'
+
+ assert_equal nil, @a2.bindir
+ assert_equal %w[app lib/code.rb].sort, @a2.files
+ end
+
+ def test_extensions_equals_nil
+ @a2.instance_variable_set(:@extensions, nil)
+ assert_equal nil, @a2.instance_variable_get(:@extensions)
+ assert_equal %w[lib/code.rb], @a2.files
+ end
+
+ def test_test_files_equals_nil
+ @a2.instance_variable_set(:@test_files, nil)
+ assert_equal nil, @a2.instance_variable_get(:@test_files)
+ assert_equal %w[lib/code.rb], @a2.files
+ end
+
+ def test_executables_equals_nil
+ @a2.instance_variable_set(:@executables, nil)
+ assert_equal nil, @a2.instance_variable_get(:@executables)
+ assert_equal %w[lib/code.rb], @a2.files
+ end
+
+ def test_extra_rdoc_files_equals_nil
+ @a2.instance_variable_set(:@extra_rdoc_files, nil)
+ assert_equal nil, @a2.instance_variable_get(:@extra_rdoc_files)
+ assert_equal %w[lib/code.rb], @a2.files
+ end
+
+ def test_build_args
+ ext_spec
+
+ assert_empty @ext.build_args
+
+ open @ext.build_info_file, 'w' do |io|
+ io.puts
+ end
+
+ assert_empty @ext.build_args
+
+ open @ext.build_info_file, 'w' do |io|
+ io.puts '--with-foo-dir=wherever'
+ end
+
+ assert_equal %w[--with-foo-dir=wherever], @ext.build_args
+ end
+
+ def test_build_extensions
+ ext_spec
+
+ refute_path_exists @ext.extension_dir, 'sanity check'
+ refute_empty @ext.extensions, 'sanity check'
+
+ extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo clean"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+
+ @ext.build_extensions
+
+ assert_path_exists @ext.extension_dir
+ end
+
+ def test_build_extensions_built
+ ext_spec
+
+ refute_empty @ext.extensions, 'sanity check'
+
+ gem_build_complete =
+ File.join @ext.extension_dir, 'gem.build_complete'
+
+ FileUtils.mkdir_p @ext.extension_dir
+ FileUtils.touch gem_build_complete
+
+ @ext.build_extensions
+
+ gem_make_out = File.join @ext.extension_dir, 'gem_make.out'
+ refute_path_exists gem_make_out
+ end
+
+ def test_build_extensions_default_gem
+ spec = new_default_spec 'default', 1
+ spec.extensions << 'extconf.rb'
+
+ extconf_rb = File.join spec.gem_dir, spec.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+
+ spec.build_extensions
+
+ refute_path_exists spec.extension_dir
+ end
+
+ def test_build_extensions_error
+ ext_spec
+
+ refute_empty @ext.extensions, 'sanity check'
+
+ assert_raises Gem::Ext::BuildError do
+ @ext.build_extensions
+ end
+ end
+
+ def test_build_extensions_extensions_dir_unwritable
+ skip 'chmod not supported' if Gem.win_platform?
+
+ ext_spec
+
+ refute_empty @ext.extensions, 'sanity check'
+
+ extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo clean"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+
+ FileUtils.mkdir_p File.join @ext.base_dir, 'extensions'
+ FileUtils.chmod 0555, @ext.base_dir
+ FileUtils.chmod 0555, File.join(@ext.base_dir, 'extensions')
+
+ @ext.build_extensions
+ refute_path_exists @ext.extension_dir
+ ensure
+ unless ($DEBUG or win_platform?) then
+ FileUtils.chmod 0755, File.join(@ext.base_dir, 'extensions')
+ FileUtils.chmod 0755, @ext.base_dir
+ end
+ end
+
+ def test_build_extensions_no_extensions_dir_unwritable
+ skip 'chmod not supported' if Gem.win_platform?
+
+ ext_spec
+
+ refute_empty @ext.extensions, 'sanity check'
+
+ extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo clean"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+
+ FileUtils.rm_r File.join @gemhome, 'extensions'
+ FileUtils.chmod 0555, @gemhome
+
+ @ext.build_extensions
+
+ gem_make_out = File.join @ext.extension_dir, 'gem_make.out'
+ refute_path_exists gem_make_out
+ ensure
+ FileUtils.chmod 0755, @gemhome
+ end
+
+ def test_build_extensions_none
+ refute_path_exists @a1.extension_dir, 'sanity check'
+ assert_empty @a1.extensions, 'sanity check'
+
+ @a1.build_extensions
+
+ refute_path_exists @a1.extension_dir
+ end
+
+ def test_build_extensions_old
+ ext_spec
+
+ refute_empty @ext.extensions, 'sanity check'
+
+ @ext.installed_by_version = v(0)
+
+ @ext.build_extensions
+
+ gem_make_out = File.join @ext.extension_dir, 'gem_make.out'
+ refute_path_exists gem_make_out
+ end
+
+ def test_build_extensions_preview
+ ext_spec
+
+ extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo clean"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+
+ refute_empty @ext.extensions, 'sanity check'
+
+ @ext.installed_by_version = v('2.2.0.preview.2')
+
+ @ext.build_extensions
+
+ gem_make_out = File.join @ext.extension_dir, 'gem_make.out'
+ assert_path_exists gem_make_out
+ end
+
+ def test_contains_requirable_file_eh
+ code_rb = File.join @a1.gem_dir, 'lib', 'code.rb'
+ FileUtils.mkdir_p File.dirname code_rb
+ FileUtils.touch code_rb
+
+ assert @a1.contains_requirable_file? 'code'
+ end
+
+ def test_contains_requirable_file_eh_extension
+ ext_spec
+
+ _, err = capture_io do
+ refute @ext.contains_requirable_file? 'nonexistent'
+ end
+
+ expected = "Ignoring ext-1 because its extensions are not built. " +
+ "Try: gem pristine ext --version 1\n"
+
+ assert_equal expected, err
+ end
+
+ def test_date
+ assert_equal Gem::Specification::TODAY, @a1.date
+ end
+
+ def test_date_equals_date
+ @a1.date = Date.new(2003, 9, 17)
+ assert_equal Time.utc(2003, 9, 17, 0,0,0), @a1.date
+ end
+
+ def test_date_equals_string
+ @a1.date = '2003-09-17'
+ assert_equal Time.utc(2003, 9, 17, 0,0,0), @a1.date
+ end
+
+ def test_date_equals_string_bad
+ assert_raises Gem::InvalidSpecificationException do
+ @a1.date = '9/11/2003'
+ end
+ end
+
+ def test_date_equals_time
+ @a1.date = Time.local(2003, 9, 17, 0,0,0)
+ assert_equal Time.utc(2003, 9, 17, 0,0,0), @a1.date
+ end
+
+ def test_date_equals_time_local
+ @a1.date = Time.local(2003, 9, 17, 19,50,0) # may not pass in utc >= +4
+ assert_equal Time.utc(2003, 9, 17, 0,0,0), @a1.date
+ end
+
+ def test_date_equals_time_utc
+ @a1.date = Time.utc(2003, 9, 17, 19,50,0)
+ assert_equal Time.utc(2003, 9, 17, 0,0,0), @a1.date
+ end
+
+ def test_date_tolerates_hour_sec_zulu
+ @a1.date = "2012-01-12 11:22:33.4444444 Z"
+ assert_equal Time.utc(2012,01,12,0,0,0), @a1.date
+ end
+
+ def test_date_tolerates_hour_sec_and_timezone
+ @a1.date = "2012-01-12 11:22:33.4444444 +02:33"
+ assert_equal Time.utc(2012,01,12,0,0,0), @a1.date
+ end
+
+ def test_dependencies
+ util_setup_deps
+ assert_equal [@bonobo, @monkey], @gem.dependencies
+ end
+
+ def test_dependent_gems
+ util_setup_deps
+
+ assert_empty @gem.dependent_gems
+
+ bonobo = util_spec 'bonobo'
+
+ expected = [
+ [@gem, @bonobo, [bonobo]],
+ ]
+
+ assert_equal expected, bonobo.dependent_gems
+ end
+
+ def test_doc_dir
+ assert_equal File.join(@gemhome, 'doc', 'a-1'), @a1.doc_dir
+ end
+
+ def test_doc_dir_type
+ assert_equal File.join(@gemhome, 'doc', 'a-1', 'ri'), @a1.doc_dir('ri')
+ end
+
+ def test_runtime_dependencies
+ util_setup_deps
+ assert_equal [@bonobo], @gem.runtime_dependencies
+ end
+
+ def test_development_dependencies
+ util_setup_deps
+ assert_equal [@monkey], @gem.development_dependencies
+ end
+
+ def test_description
+ assert_equal 'This is a test description', @a1.description
+ end
+
+ def test_eql_eh
+ g1 = new_spec 'gem', 1
+ g2 = new_spec 'gem', 1
+
+ assert_equal g1, g2
+ assert_equal g1.hash, g2.hash
+ assert_equal true, g1.eql?(g2)
+ end
+
+ def test_eql_eh_extensions
+ spec = @a1.dup
+ spec.extensions = 'xx'
+
+ refute_operator @a1, :eql?, spec
+ refute_operator spec, :eql?, @a1
+ end
+
+ def test_executables
+ @a1.executable = 'app'
+ assert_equal %w[app], @a1.executables
+ end
+
+ def test_executable_equals
+ @a2.executable = 'app'
+ assert_equal 'app', @a2.executable
+ assert_equal %w[bin/app lib/code.rb].sort, @a2.files
+ end
+
+ def test_extensions
+ assert_equal ['ext/extconf.rb'], ext_spec.extensions
+ end
+
+ def test_extension_dir
+ enable_shared, RbConfig::CONFIG['ENABLE_SHARED'] =
+ RbConfig::CONFIG['ENABLE_SHARED'], 'no'
+
+ ext_spec
+
+ refute_empty @ext.extensions
+
+ expected =
+ File.join(@ext.base_dir, 'extensions', Gem::Platform.local.to_s,
+ "#{Gem.ruby_api_version}-static", @ext.full_name)
+
+ assert_equal expected, @ext.extension_dir
+ ensure
+ RbConfig::CONFIG['ENABLE_SHARED'] = enable_shared
+ end
+
+ def test_extension_dir_override
+ enable_shared, RbConfig::CONFIG['ENABLE_SHARED'] =
+ RbConfig::CONFIG['ENABLE_SHARED'], 'no'
+
+ class << Gem
+ alias orig_default_ext_dir_for default_ext_dir_for
+
+ def Gem.default_ext_dir_for(base_dir)
+ 'elsewhere'
+ end
+ end
+
+ ext_spec
+
+ refute_empty @ext.extensions
+
+ expected = File.join @tempdir, 'elsewhere', @ext.full_name
+
+ assert_equal expected, @ext.extension_dir
+ ensure
+ RbConfig::CONFIG['ENABLE_SHARED'] = enable_shared
+
+ class << Gem
+ remove_method :default_ext_dir_for
+
+ alias default_ext_dir_for orig_default_ext_dir_for
+ end
+ end
+
+ def test_files
+ @a1.files = %w(files bin/common)
+ @a1.test_files = %w(test_files bin/common)
+ @a1.executables = %w(executables common)
+ @a1.extra_rdoc_files = %w(extra_rdoc_files bin/common)
+ @a1.extensions = %w(extensions bin/common)
+
+ expected = %w[
+ bin/common
+ bin/executables
+ extensions
+ extra_rdoc_files
+ files
+ test_files
+ ]
+ assert_equal expected, @a1.files
+ end
+
+ def test_files_append
+ @a1.files = %w(files bin/common)
+ @a1.test_files = %w(test_files bin/common)
+ @a1.executables = %w(executables common)
+ @a1.extra_rdoc_files = %w(extra_rdoc_files bin/common)
+ @a1.extensions = %w(extensions bin/common)
+
+ expected = %w[
+ bin/common
+ bin/executables
+ extensions
+ extra_rdoc_files
+ files
+ test_files
+ ]
+ assert_equal expected, @a1.files
+
+ @a1.files << "generated_file.c"
+
+ expected << "generated_file.c"
+ expected.sort!
+
+ assert_equal expected, @a1.files
+ end
+
+ def test_files_duplicate
+ @a2.files = %w[a b c d b]
+ @a2.extra_rdoc_files = %w[x y z x]
+ @a2.normalize
+
+ assert_equal %w[a b c d x y z], @a2.files
+ assert_equal %w[x y z], @a2.extra_rdoc_files
+ end
+
+ def test_files_extra_rdoc_files
+ @a2.files = %w[a b c d]
+ @a2.extra_rdoc_files = %w[x y z]
+ @a2.normalize
+ assert_equal %w[a b c d x y z], @a2.files
+ end
+
+ def test_files_non_array
+ @a1.files = "F"
+ @a1.test_files = "TF"
+ @a1.executables = "X"
+ @a1.extra_rdoc_files = "ERF"
+ @a1.extensions = "E"
+
+ assert_equal %w[E ERF F TF bin/X], @a1.files
+ end
+
+ def test_files_non_array_pathological
+ @a1.instance_variable_set :@files, "F"
+ @a1.instance_variable_set :@test_files, "TF"
+ @a1.instance_variable_set :@extra_rdoc_files, "ERF"
+ @a1.instance_variable_set :@extensions, "E"
+ @a1.instance_variable_set :@executables, "X"
+
+ assert_equal %w[E ERF F TF bin/X], @a1.files
+ assert_kind_of Integer, @a1.hash
+ end
+
+ def test_for_cache
+ @a2.add_runtime_dependency 'b', '1'
+ @a2.dependencies.first.instance_variable_set :@type, nil
+ @a2.required_rubygems_version = Gem::Requirement.new '> 0'
+ @a2.test_files = %w[test/test_b.rb]
+
+ refute_empty @a2.files
+ refute_empty @a2.test_files
+
+ spec = @a2.for_cache
+
+ assert_empty spec.files
+ assert_empty spec.test_files
+
+ refute_empty @a2.files
+ refute_empty @a2.test_files
+ end
+
+ def test_full_gem_path
+ assert_equal File.join(@gemhome, 'gems', @a1.full_name), @a1.full_gem_path
+
+ @a1.original_platform = 'mswin32'
+
+ assert_equal File.join(@gemhome, 'gems', @a1.original_name),
+ @a1.full_gem_path
+ end
+
+ def test_full_gem_path_double_slash
+ gemhome = @gemhome.to_s.sub(/\w\//, '\&/')
+ @a1.loaded_from = File.join gemhome, "specifications", @a1.spec_name
+
+ expected = File.join @gemhome, "gems", @a1.full_name
+ assert_equal expected, @a1.full_gem_path
+ end
+
+ def test_full_name
+ assert_equal 'a-1', @a1.full_name
+
+ @a1 = Gem::Specification.new "a", 1
+ @a1.platform = Gem::Platform.new ['universal', 'darwin', nil]
+ assert_equal 'a-1-universal-darwin', @a1.full_name
+
+ @a1 = Gem::Specification.new "a", 1
+ @a1.instance_variable_set :@new_platform, 'mswin32'
+ assert_equal 'a-1-mswin32', @a1.full_name, 'legacy'
+
+ return if win_platform?
+
+ @a1 = Gem::Specification.new "a", 1
+ @a1.platform = 'current'
+ assert_equal 'a-1-x86-darwin-8', @a1.full_name
+ end
+
+ def test_full_name_windows
+ test_cases = {
+ 'i386-mswin32' => 'a-1-x86-mswin32-60',
+ 'i386-mswin32_80' => 'a-1-x86-mswin32-80',
+ 'i386-mingw32' => 'a-1-x86-mingw32'
+ }
+
+ test_cases.each do |arch, expected|
+ @a1 = Gem::Specification.new "a", 1
+ util_set_arch arch
+ @a1.platform = 'current'
+ assert_equal expected, @a1.full_name
+ end
+ end
+
+ def test_gem_build_complete_path
+ expected = File.join @a1.extension_dir, 'gem.build_complete'
+ assert_equal expected, @a1.gem_build_complete_path
+ end
+
+ def test_hash
+ assert_equal @a1.hash, @a1.hash
+ assert_equal @a1.hash, @a1.dup.hash
+ refute_equal @a1.hash, @a2.hash
+ end
+
+ def test_installed_by_version
+ assert_equal v(0), @a1.installed_by_version
+
+ @a1.installed_by_version = Gem.rubygems_version
+
+ assert_equal Gem.rubygems_version, @a1.installed_by_version
+ end
+
+ def test_base_dir
+ assert_equal @gemhome, @a1.base_dir
+ end
+
+ def test_base_dir_not_loaded
+ @a1.instance_variable_set :@loaded_from, nil
+
+ assert_equal Gem.dir, @a1.base_dir
+ end
+
+ def test_base_dir_default
+ default_dir =
+ File.join Gem::Specification.default_specifications_dir, @a1.spec_name
+
+ @a1.instance_variable_set :@loaded_from, default_dir
+
+ assert_equal Gem.default_dir, @a1.base_dir
+ end
+
+ def test_lib_files
+ @a1.files = %w[lib/foo.rb Rakefile]
+
+ assert_equal %w[lib/foo.rb], @a1.lib_files
+ end
+
+ def test_license
+ assert_equal 'MIT', @a1.license
+ end
+
+ def test_licenses
+ assert_equal ['MIT'], @a1.licenses
+ end
+
+ def test_name
+ assert_equal 'a', @a1.name
+ end
+
+ def test_original_name
+ assert_equal 'a-1', @a1.full_name
+
+ @a1.platform = 'i386-linux'
+ @a1.instance_variable_set :@original_platform, 'i386-linux'
+ assert_equal 'a-1-i386-linux', @a1.original_name
+ end
+
+ def test_platform
+ assert_equal Gem::Platform::RUBY, @a1.platform
+ end
+
+ def test_platform_change_reset_full_name
+ orig_full_name = @a1.full_name
+
+ @a1.platform = "universal-unknown"
+ refute_equal orig_full_name, @a1.full_name
+ end
+
+ def test_platform_change_reset_cache_file
+ orig_cache_file = @a1.cache_file
+
+ @a1.platform = "universal-unknown"
+ refute_equal orig_cache_file, @a1.cache_file
+ end
+
+ def test_platform_equals
+ @a1.platform = nil
+ assert_equal Gem::Platform::RUBY, @a1.platform
+
+ @a1.platform = Gem::Platform::RUBY
+ assert_equal Gem::Platform::RUBY, @a1.platform
+
+ test_cases = {
+ 'i386-mswin32' => ['x86', 'mswin32', '60'],
+ 'i386-mswin32_80' => ['x86', 'mswin32', '80'],
+ 'i386-mingw32' => ['x86', 'mingw32', nil ],
+ 'x86-darwin8' => ['x86', 'darwin', '8' ],
+ }
+
+ test_cases.each do |arch, expected|
+ util_set_arch arch
+ @a1.platform = Gem::Platform::CURRENT
+ assert_equal Gem::Platform.new(expected), @a1.platform
+ end
+ end
+
+ def test_platform_equals_current
+ @a1.platform = Gem::Platform::CURRENT
+ assert_equal Gem::Platform.local, @a1.platform
+ assert_equal Gem::Platform.local.to_s, @a1.original_platform
+ end
+
+ def test_platform_equals_legacy
+ @a1.platform = 'mswin32'
+ assert_equal Gem::Platform.new('x86-mswin32'), @a1.platform
+
+ @a1.platform = 'i586-linux'
+ assert_equal Gem::Platform.new('x86-linux'), @a1.platform
+
+ @a1.platform = 'powerpc-darwin'
+ assert_equal Gem::Platform.new('ppc-darwin'), @a1.platform
+ end
+
+ def test_prerelease_spec_adds_required_rubygems_version
+ @prerelease = util_spec('tardis', '2.2.0.a')
+ refute @prerelease.required_rubygems_version.satisfied_by?(Gem::Version.new('1.3.1'))
+ assert @prerelease.required_rubygems_version.satisfied_by?(Gem::Version.new('1.4.0'))
+ end
+
+ def test_require_paths
+ enable_shared 'no' do
+ ext_spec
+
+ @ext.require_paths = 'lib'
+
+ assert_equal [@ext.extension_dir, 'lib'], @ext.require_paths
+ end
+ end
+
+ def test_require_paths_default_ext_dir_for
+ class << Gem
+ send :alias_method, :orig_default_ext_dir_for, :default_ext_dir_for
+ end
+
+ def Gem.default_ext_dir_for base_dir
+ '/foo'
+ end
+
+ enable_shared 'no' do
+ ext_spec
+
+ @ext.require_paths = 'lib'
+
+ assert_equal [File.expand_path('/foo/ext-1'), 'lib'], @ext.require_paths
+ end
+ ensure
+ class << Gem
+ send :remove_method, :default_ext_dir_for
+ send :alias_method, :default_ext_dir_for, :orig_default_ext_dir_for
+ send :remove_method, :orig_default_ext_dir_for
+ end
+ end
+
+ def test_source
+ assert_kind_of Gem::Source::Installed, @a1.source
+ end
+
+ def test_source_paths
+ ext_spec
+
+ @ext.require_paths = %w[lib ext foo]
+ @ext.extensions << 'bar/baz'
+
+ expected = %w[
+ lib
+ ext
+ foo
+ bar
+ ]
+
+ assert_equal expected, @ext.source_paths
+ end
+
+ def test_full_require_paths
+ ext_spec
+
+ @ext.require_paths = 'lib'
+
+ expected = [
+ @ext.extension_dir,
+ File.join(@gemhome, 'gems', @ext.original_name, 'lib'),
+ ]
+
+ assert_equal expected, @ext.full_require_paths
+ end
+
+ def test_to_fullpath
+ ext_spec
+
+ @ext.require_paths = 'lib'
+
+ dir = File.join(@gemhome, 'gems', @ext.original_name, 'lib')
+ expected_rb = File.join(dir, 'code.rb')
+ FileUtils.mkdir_p dir
+ FileUtils.touch expected_rb
+
+ dir = @ext.extension_dir
+ ext = RbConfig::CONFIG["DLEXT"]
+ expected_so = File.join(dir, "ext.#{ext}")
+ FileUtils.mkdir_p dir
+ FileUtils.touch expected_so
+
+ assert_nil @ext.to_fullpath("code")
+ assert_nil @ext.to_fullpath("code.rb")
+ assert_nil @ext.to_fullpath("code.#{ext}")
+
+ assert_nil @ext.to_fullpath("ext")
+ assert_nil @ext.to_fullpath("ext.rb")
+ assert_nil @ext.to_fullpath("ext.#{ext}")
+
+ @ext.activate
+
+ assert_equal expected_rb, @ext.to_fullpath("code")
+ assert_equal expected_rb, @ext.to_fullpath("code.rb")
+ assert_nil @ext.to_fullpath("code.#{ext}")
+
+ assert_equal expected_so, @ext.to_fullpath("ext")
+ assert_nil @ext.to_fullpath("ext.rb")
+ assert_equal expected_so, @ext.to_fullpath("ext.#{ext}")
+
+ assert_nil @ext.to_fullpath("notexist")
+ end
+
+ def test_require_already_activated
+ save_loaded_features do
+ a1 = new_spec "a", "1", nil, "lib/d.rb"
+
+ install_specs a1 # , a2, b1, b2, c1, c2
+
+ a1.activate
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal [], unresolved_names
+
+ assert require "d"
+
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal [], unresolved_names
+ end
+ end
+
+ def test_require_already_activated_indirect_conflict
+ save_loaded_features do
+ a1 = new_spec "a", "1", "b" => "> 0"
+ a2 = new_spec "a", "2", "b" => "> 0"
+ b1 = new_spec "b", "1", "c" => ">= 1"
+ b2 = new_spec "b", "2", "c" => ">= 2"
+ c1 = new_spec "c", "1", nil, "lib/d.rb"
+ c2 = new_spec("c", "2", { "a" => "1" }, "lib/d.rb") # conflicts with a-2
+
+ install_specs a1, a2, b1, b2, c1, c2
+
+ a1.activate
+ c1.activate
+ assert_equal %w(a-1 c-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ assert require "d"
+
+ assert_equal %w(a-1 c-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+ end
+ end
+
+ def test_requirements
+ assert_equal ['A working computer'], @a1.requirements
+ end
+
+ def test_allowed_push_host
+ assert_equal nil, @a1.metadata['allowed_push_host']
+ assert_equal 'https://privategemserver.com', @a3.metadata['allowed_push_host']
+ end
+
+ def test_runtime_dependencies_legacy
+ make_spec_c1
+ # legacy gems don't have a type
+ @c1.runtime_dependencies.each do |dep|
+ dep.instance_variable_set :@type, nil
+ end
+
+ expected = %w[rake jabber4r pqa]
+
+ assert_equal expected, @c1.runtime_dependencies.map { |d| d.name }
+ end
+
+ def test_spaceship_name
+ s1 = new_spec 'a', '1'
+ s2 = new_spec 'b', '1'
+
+ assert_equal(-1, (s1 <=> s2))
+ assert_equal( 0, (s1 <=> s1))
+ assert_equal( 1, (s2 <=> s1))
+ end
+
+ def test_spaceship_platform
+ s1 = new_spec 'a', '1'
+ s2 = new_spec 'a', '1' do |s|
+ s.platform = Gem::Platform.new 'x86-my_platform1'
+ end
+
+ assert_equal( -1, (s1 <=> s2))
+ assert_equal( 0, (s1 <=> s1))
+ assert_equal( 1, (s2 <=> s1))
+ end
+
+ def test_spaceship_version
+ s1 = new_spec 'a', '1'
+ s2 = new_spec 'a', '2'
+
+ assert_equal( -1, (s1 <=> s2))
+ assert_equal( 0, (s1 <=> s1))
+ assert_equal( 1, (s2 <=> s1))
+ end
+
+ def test_spec_file
+ assert_equal File.join(@gemhome, 'specifications', 'a-1.gemspec'),
+ @a1.spec_file
+ end
+
+ def test_spec_name
+ assert_equal 'a-1.gemspec', @a1.spec_name
+ end
+
+ def test_summary
+ assert_equal 'this is a summary', @a1.summary
+ end
+
+ def test_test_files
+ @a1.test_file = 'test/suite.rb'
+ assert_equal ['test/suite.rb'], @a1.test_files
+ end
+
+ def test_to_ruby
+ @a2.add_runtime_dependency 'b', '1'
+ @a2.dependencies.first.instance_variable_set :@type, nil
+ @a2.required_rubygems_version = Gem::Requirement.new '> 0'
+ @a2.require_paths << 'other'
+
+ ruby_code = @a2.to_ruby
+
+ expected = <<-SPEC
+# -*- encoding: utf-8 -*-
+# stub: a 2 ruby lib\0other
+
+Gem::Specification.new do |s|
+ s.name = "a"
+ s.version = "2"
+
+ s.required_rubygems_version = Gem::Requirement.new(\"> 0\") if s.respond_to? :required_rubygems_version=
+ s.require_paths = ["lib", "other"]
+ s.authors = ["A User"]
+ s.date = "#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}"
+ s.description = "This is a test description"
+ s.email = "example@example.com"
+ s.files = ["lib/code.rb"]
+ s.homepage = "http://example.com"
+ s.rubygems_version = "#{Gem::VERSION}"
+ s.summary = "this is a summary"
+
+ if s.respond_to? :specification_version then
+ s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION}
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<b>, [\"= 1\"])
+ else
+ s.add_dependency(%q<b>, [\"= 1\"])
+ end
+ else
+ s.add_dependency(%q<b>, [\"= 1\"])
+ end
+end
+ SPEC
+
+ assert_equal expected, ruby_code
+
+ same_spec = eval ruby_code
+
+ assert_equal @a2, same_spec
+ end
+
+ def test_to_ruby_for_cache
+ @a2.add_runtime_dependency 'b', '1'
+ @a2.dependencies.first.instance_variable_set :@type, nil
+ @a2.required_rubygems_version = Gem::Requirement.new '> 0'
+ @a2.installed_by_version = Gem.rubygems_version
+
+ # cached specs do not have spec.files populated:
+ ruby_code = @a2.to_ruby_for_cache
+
+ expected = <<-SPEC
+# -*- encoding: utf-8 -*-
+# stub: a 2 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = "a"
+ s.version = "2"
+
+ s.required_rubygems_version = Gem::Requirement.new(\"> 0\") if s.respond_to? :required_rubygems_version=
+ s.require_paths = ["lib"]
+ s.authors = ["A User"]
+ s.date = "#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}"
+ s.description = "This is a test description"
+ s.email = "example@example.com"
+ s.homepage = "http://example.com"
+ s.rubygems_version = "#{Gem::VERSION}"
+ s.summary = "this is a summary"
+
+ s.installed_by_version = "#{Gem::VERSION}" if s.respond_to? :installed_by_version
+
+ if s.respond_to? :specification_version then
+ s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION}
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<b>, [\"= 1\"])
+ else
+ s.add_dependency(%q<b>, [\"= 1\"])
+ end
+ else
+ s.add_dependency(%q<b>, [\"= 1\"])
+ end
+end
+ SPEC
+
+ assert_equal expected, ruby_code
+
+ same_spec = eval ruby_code
+
+ # cached specs do not have spec.files populated:
+ @a2.files = []
+ assert_equal @a2, same_spec
+ end
+
+ def test_to_ruby_fancy
+ make_spec_c1
+
+ @c1.platform = Gem::Platform.local
+ ruby_code = @c1.to_ruby
+
+ local = Gem::Platform.local
+ expected_platform = "[#{local.cpu.inspect}, #{local.os.inspect}, #{local.version.inspect}]"
+ stub_require_paths =
+ @c1.instance_variable_get(:@require_paths).join "\u0000"
+ extensions = @c1.extensions.join "\u0000"
+
+ expected = <<-SPEC
+# -*- encoding: utf-8 -*-
+# stub: a 1 #{win_platform? ? "x86-mswin32-60" : "x86-darwin-8"} #{stub_require_paths}
+# stub: #{extensions}
+
+Gem::Specification.new do |s|
+ s.name = "a"
+ s.version = "1"
+ s.platform = Gem::Platform.new(#{expected_platform})
+
+ s.required_rubygems_version = Gem::Requirement.new(\">= 0\") if s.respond_to? :required_rubygems_version=
+ s.require_paths = ["lib"]
+ s.authors = ["A User"]
+ s.date = "#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}"
+ s.description = "This is a test description"
+ s.email = "example@example.com"
+ s.executables = ["exec"]
+ s.extensions = ["ext/a/extconf.rb"]
+ s.files = ["bin/exec", "ext/a/extconf.rb", "lib/code.rb", "test/suite.rb"]
+ s.homepage = "http://example.com"
+ s.licenses = ["MIT"]
+ s.requirements = ["A working computer"]
+ s.rubyforge_project = "example"
+ s.rubygems_version = "#{Gem::VERSION}"
+ s.summary = "this is a summary"
+ s.test_files = ["test/suite.rb"]
+
+ if s.respond_to? :specification_version then
+ s.specification_version = 4
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<rake>, [\"> 0.4\"])
+ s.add_runtime_dependency(%q<jabber4r>, [\"> 0.0.0\"])
+ s.add_runtime_dependency(%q<pqa>, [\"<= 0.6\", \"> 0.4\"])
+ else
+ s.add_dependency(%q<rake>, [\"> 0.4\"])
+ s.add_dependency(%q<jabber4r>, [\"> 0.0.0\"])
+ s.add_dependency(%q<pqa>, [\"<= 0.6\", \"> 0.4\"])
+ end
+ else
+ s.add_dependency(%q<rake>, [\"> 0.4\"])
+ s.add_dependency(%q<jabber4r>, [\"> 0.0.0\"])
+ s.add_dependency(%q<pqa>, [\"<= 0.6\", \"> 0.4\"])
+ end
+end
+ SPEC
+
+ assert_equal expected, ruby_code
+
+ same_spec = eval ruby_code
+
+ assert_equal @c1, same_spec
+ end
+
+ def test_to_ruby_legacy
+ gemspec1 = Gem::Deprecate.skip_during do
+ eval LEGACY_RUBY_SPEC
+ end
+ ruby_code = gemspec1.to_ruby
+ gemspec2 = eval ruby_code
+
+ assert_equal gemspec1, gemspec2
+ end
+
+ def test_to_ruby_nested_hash
+ metadata = {}
+ metadata[metadata] = metadata
+
+ @a2.metadata = metadata
+
+ ruby = @a2.to_ruby
+
+ assert_match %r%^ s\.metadata = \{ "%, ruby
+ end
+
+ def test_to_ruby_platform
+ @a2.platform = Gem::Platform.local
+ @a2.instance_variable_set :@original_platform, 'old_platform'
+
+ ruby_code = @a2.to_ruby
+
+ same_spec = eval ruby_code
+
+ assert_equal 'old_platform', same_spec.original_platform
+ end
+
+ def test_to_yaml
+ yaml_str = @a1.to_yaml
+
+ refute_match '!!null', yaml_str
+
+ same_spec = Gem::Specification.from_yaml(yaml_str)
+
+ assert_equal @a1, same_spec
+ end
+
+ def test_to_yaml_fancy
+ @a1.platform = Gem::Platform.local
+ yaml_str = @a1.to_yaml
+
+ same_spec = Gem::Specification.from_yaml(yaml_str)
+
+ assert_equal Gem::Platform.local, same_spec.platform
+
+ assert_equal @a1, same_spec
+ end
+
+ def test_to_yaml_platform_empty_string
+ @a1.instance_variable_set :@original_platform, ''
+
+ assert_match %r|^platform: ruby$|, @a1.to_yaml
+ end
+
+ def test_to_yaml_platform_legacy
+ @a1.platform = 'powerpc-darwin7.9.0'
+ @a1.instance_variable_set :@original_platform, 'powerpc-darwin7.9.0'
+
+ yaml_str = @a1.to_yaml
+
+ same_spec = YAML.load yaml_str
+
+ assert_equal Gem::Platform.new('powerpc-darwin7'), same_spec.platform
+ assert_equal 'powerpc-darwin7.9.0', same_spec.original_platform
+ end
+
+ def test_to_yaml_platform_nil
+ @a1.instance_variable_set :@original_platform, nil
+
+ assert_match %r|^platform: ruby$|, @a1.to_yaml
+ end
+
+ def test_validate
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ assert @a1.validate
+ end
+ end
+
+ def x s; s.gsub(/xxx/, ''); end
+ def w; x "WARxxxNING"; end
+ def t; x "TOxxxDO"; end
+ def f; x "FxxxIXME"; end
+
+ def test_validate_authors
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.authors = [""]
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: no author specified\n", @ui.error, 'error'
+
+ @a1.authors = [Object.new]
+
+ assert_equal [], @a1.authors
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal "authors may not be empty", e.message
+
+ @a1.authors = ["#{f} (who is writing this software)"]
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal %{"#{f}" or "#{t}" is not an author}, e.message
+
+ @a1.authors = ["#{t} (who is writing this software)"]
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal %{"#{f}" or "#{t}" is not an author}, e.message
+ end
+ end
+
+ def test_validate_autorequire
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.autorequire = 'code'
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: deprecated autorequire specified\n",
+ @ui.error, 'error'
+ end
+ end
+
+ def test_validate_dependencies
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.add_runtime_dependency 'b', '>= 1.0.rc1'
+ @a1.add_development_dependency 'c', '>= 2.0.rc2'
+ @a1.add_runtime_dependency 'd', '~> 1.2.3'
+ @a1.add_runtime_dependency 'e', '~> 1.2.3.4'
+ @a1.add_runtime_dependency 'g', '~> 1.2.3', '>= 1.2.3.4'
+ @a1.add_runtime_dependency 'h', '>= 1.2.3', '<= 2'
+ @a1.add_runtime_dependency 'i', '>= 1.2'
+ @a1.add_runtime_dependency 'j', '>= 1.2.3'
+ @a1.add_runtime_dependency 'k', '> 1.2'
+ @a1.add_runtime_dependency 'l', '> 1.2.3'
+ @a1.add_runtime_dependency 'm', '~> 2.1.0'
+ @a1.add_runtime_dependency 'n', '~> 0.1.0'
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ expected = <<-EXPECTED
+#{w}: prerelease dependency on b (>= 1.0.rc1) is not recommended
+#{w}: prerelease dependency on c (>= 2.0.rc2, development) is not recommended
+#{w}: pessimistic dependency on d (~> 1.2.3) may be overly strict
+ if d is semantically versioned, use:
+ add_runtime_dependency 'd', '~> 1.2', '>= 1.2.3'
+#{w}: pessimistic dependency on e (~> 1.2.3.4) may be overly strict
+ if e is semantically versioned, use:
+ add_runtime_dependency 'e', '~> 1.2', '>= 1.2.3.4'
+#{w}: open-ended dependency on i (>= 1.2) is not recommended
+ if i is semantically versioned, use:
+ add_runtime_dependency 'i', '~> 1.2'
+#{w}: open-ended dependency on j (>= 1.2.3) is not recommended
+ if j is semantically versioned, use:
+ add_runtime_dependency 'j', '~> 1.2', '>= 1.2.3'
+#{w}: open-ended dependency on k (> 1.2) is not recommended
+ if k is semantically versioned, use:
+ add_runtime_dependency 'k', '~> 1.2', '> 1.2'
+#{w}: open-ended dependency on l (> 1.2.3) is not recommended
+ if l is semantically versioned, use:
+ add_runtime_dependency 'l', '~> 1.2', '> 1.2.3'
+#{w}: pessimistic dependency on m (~> 2.1.0) may be overly strict
+ if m is semantically versioned, use:
+ add_runtime_dependency 'm', '~> 2.1', '>= 2.1.0'
+#{w}: See http://guides.rubygems.org/specification-reference/ for help
+ EXPECTED
+
+ assert_equal expected, @ui.error, 'warning'
+ end
+ end
+
+ def test_validate_dependencies_open_ended
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.add_runtime_dependency 'b', '~> 1.2'
+ @a1.add_runtime_dependency 'b', '>= 1.2.3'
+
+ use_ui @ui do
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ expected = <<-EXPECTED
+duplicate dependency on b (>= 1.2.3), (~> 1.2) use:
+ add_runtime_dependency 'b', '>= 1.2.3', '~> 1.2'
+ EXPECTED
+
+ assert_equal expected, e.message
+ end
+
+ assert_equal <<-EXPECTED, @ui.error
+#{w}: See http://guides.rubygems.org/specification-reference/ for help
+ EXPECTED
+ end
+ end
+
+ def test_validate_description
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.description = ''
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: no description specified\n", @ui.error, "error"
+
+ @ui = Gem::MockGemUi.new
+ @a1.summary = "this is my summary"
+ @a1.description = @a1.summary
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: description and summary are identical\n",
+ @ui.error, "error"
+
+ @a1.description = "#{f} (describe your package)"
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal %{"#{f}" or "#{t}" is not a description}, e.message
+
+ @a1.description = "#{t} (describe your package)"
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal %{"#{f}" or "#{t}" is not a description}, e.message
+ end
+ end
+
+ def test_validate_email
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.email = ""
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: no email specified\n", @ui.error, "error"
+
+ @a1.email = "FIxxxXME (your e-mail)".sub(/xxx/, "")
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal %{"#{f}" or "#{t}" is not an email}, e.message
+
+ @a1.email = "#{t} (your e-mail)"
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal %{"#{f}" or "#{t}" is not an email}, e.message
+ end
+ end
+
+ def test_validate_empty
+ e = assert_raises Gem::InvalidSpecificationException do
+ Gem::Specification.new.validate
+ end
+
+ assert_equal 'missing value for attribute name', e.message
+ end
+
+ def test_validate_error
+ assert_raises Gem::InvalidSpecificationException do
+ use_ui @ui do
+ Gem::Specification.new.validate
+ end
+ end
+
+ assert_match 'See http://guides.rubygems.org/specification-reference/ for help', @ui.error
+ end
+
+ def test_validate_executables
+ util_setup_validate
+
+ FileUtils.mkdir_p File.join(@tempdir, 'bin')
+ File.open File.join(@tempdir, 'bin', 'exec'), 'w' do end
+ FileUtils.mkdir_p File.join(@tempdir, 'exec')
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ assert @a1.validate
+ end
+ end
+
+ assert_equal %w[exec], @a1.executables
+
+ assert_equal '', @ui.output, 'output'
+ assert_match "#{w}: bin/exec is missing #! line\n", @ui.error, 'error'
+ end
+
+ def test_validate_empty_require_paths
+ if win_platform? then
+ skip 'test_validate_empty_require_paths skipped on MS Windows (symlink)'
+ else
+ util_setup_validate
+
+ @a1.require_paths = []
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal 'specification must have at least one require_path',
+ e.message
+ end
+ end
+
+ def test_validate_files
+ skip 'test_validate_files skipped on MS Windows (symlink)' if win_platform?
+ util_setup_validate
+
+ @a1.files += ['lib', 'lib2']
+ @a1.extensions << 'ext/a/extconf.rb'
+
+ Dir.chdir @tempdir do
+ FileUtils.ln_s '/root/path', 'lib2' unless vc_windows?
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal '["lib2"] are not files', e.message
+ end
+
+ assert_equal %w[bin/exec ext/a/extconf.rb lib/code.rb lib2 test/suite.rb].sort,
+ @a1.files
+ end
+
+ def test_validate_files_recursive
+ util_setup_validate
+ FileUtils.touch @a1.file_name
+
+ @a1.files = [@a1.file_name]
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal "#{@a1.full_name} contains itself (#{@a1.file_name}), check your files list",
+ e.message
+ end
+
+ def test_validate_homepage
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.homepage = nil
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: no homepage specified\n", @ui.error, 'error'
+
+ @ui = Gem::MockGemUi.new
+
+ @a1.homepage = ''
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: no homepage specified\n", @ui.error, 'error'
+
+ @a1.homepage = 'over at my cool site'
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal '"over at my cool site" is not a URI', e.message
+ end
+ end
+
+ def test_validate_license
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.licenses.clear
+ @a1.validate
+ end
+
+ assert_match <<-warning, @ui.error
+WARNING: licenses is empty, but is recommended. Use a license abbreviation from:
+http://opensource.org/licenses/alphabetical
+ warning
+ end
+
+ def test_validate_name
+ util_setup_validate
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.name = :json
+ @a1.validate
+ end
+
+ assert_equal 'invalid value for attribute name: ":json"', e.message
+ end
+
+ def test_validate_non_nil
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ assert @a1.validate
+
+ Gem::Specification.non_nil_attributes.each do |name|
+ next if name == :files # set by #normalize
+ spec = @a1.dup
+ spec.instance_variable_set "@#{name}", nil
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ spec.validate
+ end
+
+ assert_match %r%^#{name}%, e.message
+ end
+ end
+ end
+
+ def test_validate_permissions
+ skip 'chmod not supported' if Gem.win_platform?
+
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ File.chmod 0640, File.join('lib', 'code.rb')
+ File.chmod 0640, File.join('bin', 'exec')
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: lib/code.rb is not world-readable\n", @ui.error
+ assert_match "#{w}: bin/exec is not world-readable\n", @ui.error
+ assert_match "#{w}: bin/exec is not executable\n", @ui.error
+ end
+ end
+
+ def test_validate_platform_legacy
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.platform = 'mswin32'
+ assert @a1.validate
+
+ @a1.platform = 'i586-linux'
+ assert @a1.validate
+
+ @a1.platform = 'powerpc-darwin'
+ assert @a1.validate
+ end
+ end
+
+ def test_validate_rubygems_version
+ util_setup_validate
+
+ @a1.rubygems_version = "3"
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal "expected RubyGems version #{Gem::VERSION}, was 3",
+ e.message
+ end
+
+ def test_validate_specification_version
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.specification_version = '1.0'
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ use_ui @ui do
+ @a1.validate
+ end
+ end
+
+ err = 'specification_version must be a Fixnum (did you mean version?)'
+ assert_equal err, e.message
+ end
+ end
+
+ def test_validate_summary
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @a1.summary = ''
+
+ use_ui @ui do
+ @a1.validate
+ end
+
+ assert_match "#{w}: no summary specified\n", @ui.error, 'error'
+
+ @a1.summary = "#{f} (describe your package)"
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal %{"#{f}" or "#{t}" is not a summary}, e.message
+
+ @a1.summary = "#{t} (describe your package)"
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @a1.validate
+ end
+
+ assert_equal %{"#{f}" or "#{t}" is not a summary}, e.message
+ end
+ end
+
+ def test_validate_warning
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.licenses.clear
+ @a1.validate
+ end
+
+ assert_match 'See http://guides.rubygems.org/specification-reference/ for help', @ui.error
+ end
+
+ def test_version
+ assert_equal Gem::Version.new('1'), @a1.version
+ end
+
+ def test_version_change_reset_full_name
+ orig_full_name = @a1.full_name
+
+ @a1.version = "2"
+
+ refute_equal orig_full_name, @a1.full_name
+ end
+
+ def test_version_change_reset_cache_file
+ orig_cache_file = @a1.cache_file
+
+ @a1.version = "2"
+
+ refute_equal orig_cache_file, @a1.cache_file
+ end
+
+ def test__load_fixes_Date_objects
+ spec = new_spec "a", 1
+ spec.instance_variable_set :@date, Date.today
+
+ spec = Marshal.load Marshal.dump(spec)
+
+ assert_kind_of Time, spec.date
+ end
+
+ def test_load_errors_contain_filename
+ specfile = Tempfile.new(self.class.name.downcase)
+ specfile.write "raise 'boom'"
+ specfile.close
+ begin
+ capture_io do
+ Gem::Specification.load(specfile.path)
+ end
+ rescue => e
+ name_rexp = Regexp.new(Regexp.escape(specfile.path))
+ assert e.backtrace.grep(name_rexp).any?
+ end
+ ensure
+ specfile.delete
+ end
+
+ ##
+ # KEEP p-1-x86-darwin-8
+ # KEEP p-1
+ # KEEP c-1.2
+ # KEEP a_evil-9
+ # a-1
+ # a-1-x86-my_platform-1
+ # KEEP a-2
+ # a-2-x86-other_platform-1
+ # KEEP a-2-x86-my_platform-1
+ # a-3.a
+ # KEEP a-3-x86-other_platform-1
+
+ def test_latest_specs
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1 do |s|
+ s.platform = Gem::Platform.new 'x86-my_platform1'
+ end
+
+ fetcher.spec 'a', 2
+
+ fetcher.spec 'a', 2 do |s|
+ s.platform = Gem::Platform.new 'x86-my_platform1'
+ end
+
+ fetcher.spec 'a', 2 do |s|
+ s.platform = Gem::Platform.new 'x86-other_platform1'
+ end
+
+ fetcher.spec 'a', 3 do |s|
+ s.platform = Gem::Platform.new 'x86-other_platform1'
+ end
+ end
+
+ expected = %W[
+ a-2
+ a-2-x86-my_platform-1
+ a-3-x86-other_platform-1
+ ]
+
+ latest_specs = Gem::Specification.latest_specs.map(&:full_name).sort
+
+ assert_equal expected, latest_specs
+ end
+
+ def test_metadata_validates_ok
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @m1 = quick_gem 'm', '1' do |s|
+ s.files = %w[lib/code.rb]
+ s.metadata = { 'one' => "two", 'two' => "three" }
+ end
+
+ use_ui @ui do
+ @m1.validate
+ end
+ end
+ end
+
+ def test_metadata_key_type_validation_fails
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @m2 = quick_gem 'm', '2' do |s|
+ s.files = %w[lib/code.rb]
+ s.metadata = { 1 => "fail" }
+ end
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @m2.validate
+ end
+
+ assert_equal "metadata keys must be a String", e.message
+ end
+ end
+
+ def test_metadata_key_size_validation_fails
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @m2 = quick_gem 'm', '2' do |s|
+ s.files = %w[lib/code.rb]
+ s.metadata = { ("x" * 129) => "fail" }
+ end
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @m2.validate
+ end
+
+ assert_equal "metadata key too large (129 > 128)", e.message
+ end
+ end
+
+ def test_metadata_value_type_validation_fails
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @m2 = quick_gem 'm', '2' do |s|
+ s.files = %w[lib/code.rb]
+ s.metadata = { 'fail' => [] }
+ end
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @m2.validate
+ end
+
+ assert_equal "metadata values must be a String", e.message
+ end
+ end
+
+ def test_metadata_value_size_validation_fails
+ util_setup_validate
+
+ Dir.chdir @tempdir do
+ @m2 = quick_gem 'm', '2' do |s|
+ s.files = %w[lib/code.rb]
+ s.metadata = { 'fail' => ("x" * 1025) }
+ end
+
+ e = assert_raises Gem::InvalidSpecificationException do
+ @m2.validate
+ end
+
+ assert_equal "metadata value too large (1025 > 1024)", e.message
+ end
+ end
+
+ def test_metadata_specs
+ valid_ruby_spec = <<-EOF
+# -*- encoding: utf-8 -*-
+# stub: m 1 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = "m"
+ s.version = "1"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.metadata = { "one" => "two", "two" => "three" } if s.respond_to? :metadata=
+ s.require_paths = ["lib"]
+ s.authors = ["A User"]
+ s.date = "#{Gem::Specification::TODAY.strftime("%Y-%m-%d")}"
+ s.description = "This is a test description"
+ s.email = "example@example.com"
+ s.files = ["lib/code.rb"]
+ s.homepage = "http://example.com"
+ s.rubygems_version = "#{Gem::VERSION}"
+ s.summary = "this is a summary"
+end
+ EOF
+
+ @m1 = quick_gem 'm', '1' do |s|
+ s.files = %w[lib/code.rb]
+ s.metadata = { 'one' => "two", 'two' => "three" }
+ end
+
+ assert_equal @m1.to_ruby, valid_ruby_spec
+ end
+
+ def test_missing_extensions_eh
+ ext_spec
+
+ assert @ext.missing_extensions?
+
+ extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo clean"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+
+ @ext.build_extensions
+
+ refute @ext.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_default_gem
+ spec = new_default_spec 'default', 1
+ spec.extensions << 'extconf.rb'
+
+ refute spec.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_legacy
+ ext_spec
+
+ @ext.installed_by_version = v '2.2.0.preview.2'
+
+ assert @ext.missing_extensions?
+
+ @ext.installed_by_version = v '2.2.0.preview.1'
+
+ refute @ext.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_none
+ refute @a1.missing_extensions?
+ end
+
+ def test_find_by_name
+ util_spec "a"
+
+ assert Gem::Specification.find_by_name "a"
+ assert Gem::Specification.find_by_name "a", "1"
+ assert Gem::Specification.find_by_name "a", ">1"
+
+ assert_raises Gem::LoadError do
+ Gem::Specification.find_by_name "monkeys"
+ end
+ end
+
+ def test_find_by_name_prerelease
+ b = util_spec "b", "2.a"
+
+ b.activate
+
+ assert Gem::Specification.find_by_name "b"
+
+ assert_raises Gem::LoadError do
+ Gem::Specification.find_by_name "b", "1"
+ end
+
+ assert Gem::Specification.find_by_name "b", ">1"
+ end
+
+ def test_find_by_path
+ a = new_spec "foo", "1", nil, "lib/foo.rb"
+
+ install_specs a
+
+ assert_equal a, Gem::Specification.find_by_path('foo')
+ a.activate
+ assert_equal a, Gem::Specification.find_by_path('foo')
+ end
+
+ def test_find_inactive_by_path
+ a = new_spec "foo", "1", nil, "lib/foo.rb"
+
+ install_specs a
+
+ assert_equal a, Gem::Specification.find_inactive_by_path('foo')
+ a.activate
+ assert_equal nil, Gem::Specification.find_inactive_by_path('foo')
+ end
+
+ def test_load_default_gem
+ Gem::Specification.reset
+ assert_equal [], Gem::Specification.map(&:full_name)
+
+ default_gem_spec = new_default_spec("default", "2.0.0.0",
+ nil, "default/gem.rb")
+ spec_path = File.join(@default_spec_dir, default_gem_spec.spec_name)
+ write_file(spec_path) do |file|
+ file.print(default_gem_spec.to_ruby)
+ end
+ Gem::Specification.reset
+ assert_equal ["default-2.0.0.0"], Gem::Specification.map(&:full_name)
+ end
+
+ def test_detect_bundled_gem_in_old_ruby
+ util_set_RUBY_VERSION '1.9.3', 551
+
+ spec = new_spec 'bigdecimal', '1.1.0' do |s|
+ s.summary = "This bigdecimal is bundled with Ruby"
+ end
+
+ assert spec.bundled_gem_in_old_ruby?
+ ensure
+ util_restore_RUBY_VERSION
+ end
+
+ def util_setup_deps
+ @gem = util_spec "awesome", "1.0" do |awesome|
+ awesome.add_runtime_dependency "bonobo", []
+ awesome.add_development_dependency "monkey", []
+ end
+
+ @bonobo = Gem::Dependency.new("bonobo", [])
+ @monkey = Gem::Dependency.new("monkey", [], :development)
+ end
+
+ def util_setup_validate
+ Dir.chdir @tempdir do
+ FileUtils.mkdir_p File.join("ext", "a")
+ FileUtils.mkdir_p "lib"
+ FileUtils.mkdir_p "test"
+ FileUtils.mkdir_p "bin"
+
+ FileUtils.touch File.join("ext", "a", "extconf.rb")
+ FileUtils.touch File.join("lib", "code.rb")
+ FileUtils.touch File.join("test", "suite.rb")
+
+ File.open "bin/exec", "w", 0755 do |fp|
+ fp.puts "#!#{Gem.ruby}"
+ end
+ end
+ end
+
+ def with_syck
+ begin
+ verbose, $VERBOSE = $VERBOSE, nil
+ require "yaml"
+ old_engine = YAML::ENGINE.yamler
+ YAML::ENGINE.yamler = 'syck'
+ load 'rubygems/syck_hack.rb'
+ rescue NameError
+ # probably on 1.8, ignore
+ ensure
+ $VERBOSE = verbose
+ end
+
+ yield
+ ensure
+ begin
+ YAML::ENGINE.yamler = old_engine
+ load 'rubygems/syck_hack.rb'
+ rescue NameError
+ # ignore
+ end
+ end
+
+ def with_psych
+ begin
+ require "yaml"
+ old_engine = YAML::ENGINE.yamler
+ YAML::ENGINE.yamler = 'psych'
+ load 'rubygems/syck_hack.rb'
+ rescue NameError
+ # probably on 1.8, ignore
+ end
+
+ yield
+ ensure
+ begin
+ YAML::ENGINE.yamler = old_engine
+ load 'rubygems/syck_hack.rb'
+ rescue NameError
+ # ignore
+ end
+ end
+
+ def silence_warnings
+ old_verbose, $VERBOSE = $VERBOSE, false
+ yield
+ ensure
+ $VERBOSE = old_verbose
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_stream_ui.rb b/jni/ruby/test/rubygems/test_gem_stream_ui.rb
new file mode 100644
index 0000000..f483568
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_stream_ui.rb
@@ -0,0 +1,238 @@
+require 'rubygems/test_case'
+require 'rubygems/user_interaction'
+require 'timeout'
+
+class TestGemStreamUI < Gem::TestCase
+
+ module IsTty
+ attr_accessor :tty
+
+ def tty?
+ @tty = true unless defined? @tty
+ return @tty
+ end
+
+ alias_method :isatty, :tty?
+
+ def noecho
+ yield self
+ end
+ end
+
+ def setup
+ super
+
+ @cfg = Gem.configuration
+
+ @in = StringIO.new
+ @out = StringIO.new
+ @err = StringIO.new
+
+ @in.extend IsTty
+ @out.extend IsTty
+
+ @sui = Gem::StreamUI.new @in, @out, @err, true
+ end
+
+ def test_ask
+ skip 'TTY detection broken on windows' if
+ Gem.win_platform? unless RUBY_VERSION > '1.9.2'
+
+ timeout(1) do
+ expected_answer = "Arthur, King of the Britons"
+ @in.string = "#{expected_answer}\n"
+ actual_answer = @sui.ask("What is your name?")
+ assert_equal expected_answer, actual_answer
+ end
+ end
+
+ def test_ask_no_tty
+ skip 'TTY detection broken on windows' if
+ Gem.win_platform? unless RUBY_VERSION > '1.9.2'
+
+ @in.tty = false
+
+ timeout(0.1) do
+ answer = @sui.ask("what is your favorite color?")
+ assert_equal nil, answer
+ end
+ end
+
+ def test_ask_for_password
+ skip 'Always uses $stdin on windows' if
+ Gem.win_platform? unless RUBY_VERSION > '1.9.2'
+
+ timeout(1) do
+ expected_answer = "Arthur, King of the Britons"
+ @in.string = "#{expected_answer}\n"
+ actual_answer = @sui.ask_for_password("What is your name?")
+ assert_equal expected_answer, actual_answer
+ end
+ end
+
+ def test_ask_for_password_no_tty
+ skip 'TTY handling is broken on windows' if
+ Gem.win_platform? unless RUBY_VERSION > '1.9.2'
+
+ @in.tty = false
+
+ timeout(0.1) do
+ answer = @sui.ask_for_password("what is the airspeed velocity of an unladen swallow?")
+ assert_equal nil, answer
+ end
+ end
+
+ def test_ask_yes_no_no_tty_with_default
+ skip 'TTY handling is broken on windows' if
+ Gem.win_platform? unless RUBY_VERSION > '1.9.2'
+
+ @in.tty = false
+
+ timeout(0.1) do
+ answer = @sui.ask_yes_no("do coconuts migrate?", false)
+ assert_equal false, answer
+
+ answer = @sui.ask_yes_no("do coconuts migrate?", true)
+ assert_equal true, answer
+ end
+ end
+
+ def test_ask_yes_no_no_tty_without_default
+ skip 'TTY handling is broken on windows' if
+ Gem.win_platform? unless RUBY_VERSION > '1.9.2'
+
+ @in.tty = false
+
+ timeout(0.1) do
+ assert_raises(Gem::OperationNotSupportedError) do
+ @sui.ask_yes_no("do coconuts migrate?")
+ end
+ end
+ end
+
+ def test_choose_from_list
+ @in.puts "1"
+ @in.rewind
+
+ result = @sui.choose_from_list 'which one?', %w[foo bar]
+
+ assert_equal ['foo', 0], result
+ assert_equal "which one?\n 1. foo\n 2. bar\n> ", @out.string
+ end
+
+ def test_choose_from_list_EOF
+ result = @sui.choose_from_list 'which one?', %w[foo bar]
+
+ assert_equal [nil, nil], result
+ assert_equal "which one?\n 1. foo\n 2. bar\n> ", @out.string
+ end
+
+ def test_progress_reporter_silent_nil
+ @cfg.verbose = nil
+ reporter = @sui.progress_reporter 10, 'hi'
+ assert_kind_of Gem::StreamUI::SilentProgressReporter, reporter
+ end
+
+ def test_progress_reporter_silent_false
+ @cfg.verbose = false
+ reporter = @sui.progress_reporter 10, 'hi'
+ assert_kind_of Gem::StreamUI::SilentProgressReporter, reporter
+ assert_equal "", @out.string
+ end
+
+ def test_progress_reporter_simple
+ @cfg.verbose = true
+ reporter = @sui.progress_reporter 10, 'hi'
+ assert_kind_of Gem::StreamUI::SimpleProgressReporter, reporter
+ assert_equal "hi\n", @out.string
+ end
+
+ def test_progress_reporter_verbose
+ @cfg.verbose = 0
+ reporter = @sui.progress_reporter 10, 'hi'
+ assert_kind_of Gem::StreamUI::VerboseProgressReporter, reporter
+ assert_equal "hi\n", @out.string
+ end
+
+ def test_download_reporter_silent_nil
+ @cfg.verbose = nil
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', 1024
+ assert_kind_of Gem::StreamUI::SilentDownloadReporter, reporter
+ assert_equal "", @out.string
+ end
+
+ def test_download_reporter_silent_false
+ @cfg.verbose = false
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', 1024
+ assert_kind_of Gem::StreamUI::SilentDownloadReporter, reporter
+ assert_equal "", @out.string
+ end
+
+ def test_download_reporter_anything
+ @cfg.verbose = 0
+ reporter = @sui.download_reporter
+ assert_kind_of Gem::StreamUI::VerboseDownloadReporter, reporter
+ end
+
+ def test_verbose_download_reporter
+ @cfg.verbose = true
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', 1024
+ assert_equal "Fetching: a.gem", @out.string
+ end
+
+ def test_verbose_download_reporter_progress
+ @cfg.verbose = true
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', 1024
+ reporter.update 512
+ assert_equal "Fetching: a.gem\rFetching: a.gem ( 50%)", @out.string
+ end
+
+ def test_verbose_download_reporter_progress_once
+ @cfg.verbose = true
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', 1024
+ reporter.update 510
+ reporter.update 512
+ assert_equal "Fetching: a.gem\rFetching: a.gem ( 50%)", @out.string
+ end
+
+ def test_verbose_download_reporter_progress_complete
+ @cfg.verbose = true
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', 1024
+ reporter.update 510
+ reporter.done
+ assert_equal "Fetching: a.gem\rFetching: a.gem ( 50%)\rFetching: a.gem (100%)\n", @out.string
+ end
+
+ def test_verbose_download_reporter_progress_nil_length
+ @cfg.verbose = true
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', nil
+ reporter.update 1024
+ reporter.done
+ assert_equal "Fetching: a.gem\rFetching: a.gem (1024B)\rFetching: a.gem (1024B)\n", @out.string
+ end
+
+ def test_verbose_download_reporter_progress_zero_length
+ @cfg.verbose = true
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', 0
+ reporter.update 1024
+ reporter.done
+ assert_equal "Fetching: a.gem\rFetching: a.gem (1024B)\rFetching: a.gem (1024B)\n", @out.string
+ end
+
+ def test_verbose_download_reporter_no_tty
+ @out.tty = false
+
+ @cfg.verbose = true
+ reporter = @sui.download_reporter
+ reporter.fetch 'a.gem', 1024
+ assert_equal "", @out.string
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_stub_specification.rb b/jni/ruby/test/rubygems/test_gem_stub_specification.rb
new file mode 100644
index 0000000..914b06a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_stub_specification.rb
@@ -0,0 +1,199 @@
+require "rubygems/test_case"
+require "rubygems/stub_specification"
+
+class TestStubSpecification < Gem::TestCase
+ SPECIFICATIONS = File.expand_path(File.join("..", "specifications"), __FILE__)
+ FOO = File.join SPECIFICATIONS, "foo-0.0.1.gemspec"
+ BAR = File.join SPECIFICATIONS, "bar-0.0.2.gemspec"
+
+ def setup
+ super
+
+ @foo = Gem::StubSpecification.new FOO
+ end
+
+ def test_initialize
+ assert_equal "foo", @foo.name
+ assert_equal Gem::Version.new("0.0.1"), @foo.version
+ assert_equal Gem::Platform.new("mswin32"), @foo.platform
+ assert_equal ["lib", "lib/f oo/ext"], @foo.require_paths
+ assert @foo.stubbed?
+ end
+
+ def test_initialize_extension
+ stub = stub_with_extension
+
+ assert_equal 'stub_e', stub.name
+ assert_equal v(2), stub.version
+ assert_equal Gem::Platform::RUBY, stub.platform
+ assert_equal [stub.extension_dir, 'lib'], stub.require_paths
+ assert_equal %w[ext/stub_e/extconf.rb], stub.extensions
+ end
+
+ def test_initialize_missing_stubline
+ stub = Gem::StubSpecification.new(BAR)
+ assert_equal "bar", stub.name
+ assert_equal Gem::Version.new("0.0.2"), stub.version
+ assert_equal Gem::Platform.new("ruby"), stub.platform
+ assert_equal ["lib"], stub.require_paths
+ assert !stub.stubbed?
+ end
+
+ def test_contains_requirable_file_eh
+ stub = stub_without_extension
+ code_rb = File.join stub.gem_dir, 'lib', 'code.rb'
+ FileUtils.mkdir_p File.dirname code_rb
+ FileUtils.touch code_rb
+
+ assert stub.contains_requirable_file? 'code'
+ end
+
+ def test_contains_requirable_file_eh_extension
+ stub_with_extension do |stub|
+ _, err = capture_io do
+ refute stub.contains_requirable_file? 'nonexistent'
+ end
+
+ expected = "Ignoring stub_e-2 because its extensions are not built. " +
+ "Try: gem pristine stub_e --version 2\n"
+
+ assert_equal expected, err
+ end
+ end
+
+ def test_full_require_paths
+ stub = stub_with_extension
+
+ expected = [
+ stub.extension_dir,
+ File.join(stub.full_gem_path, 'lib'),
+ ]
+
+ assert_equal expected, stub.full_require_paths
+ end
+
+ def test_missing_extensions_eh
+ stub = stub_with_extension do |s|
+ extconf_rb = File.join s.gem_dir, s.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo clean"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+ end
+
+ assert stub.missing_extensions?
+
+ stub.build_extensions
+
+ refute stub.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_default_gem
+ spec = new_default_spec 'default', 1
+ spec.extensions << 'extconf.rb'
+
+ open spec.loaded_from, 'w' do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ default_spec = Gem::StubSpecification.new spec.loaded_from
+
+ refute default_spec.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_none
+ refute @foo.missing_extensions?
+ end
+
+ def test_to_spec
+ real_foo = util_spec @foo.name, @foo.version
+ real_foo.activate
+
+ assert_equal @foo.version, Gem.loaded_specs[@foo.name].version,
+ 'sanity check'
+
+ assert_same real_foo, @foo.to_spec
+ end
+
+ def test_to_spec_with_other_specs_loaded_does_not_warn
+ real_foo = util_spec @foo.name, @foo.version
+ real_foo.activate
+ bar = Gem::StubSpecification.new BAR
+ refute_predicate Gem.loaded_specs, :empty?
+ assert bar.to_spec
+ end
+
+ def test_to_spec_activated
+ assert @foo.to_spec.is_a?(Gem::Specification)
+ assert_equal "foo", @foo.to_spec.name
+ refute @foo.to_spec.instance_variable_defined? :@ignored
+ end
+
+ def test_to_spec_missing_extensions
+ stub = stub_with_extension
+
+ capture_io do
+ stub.contains_requirable_file? 'nonexistent'
+ end
+
+ assert stub.to_spec.instance_variable_get :@ignored
+ end
+
+ def stub_with_extension
+ spec = File.join @gemhome, 'specifications', 'stub_e-2.gemspec'
+ open spec, 'w' do |io|
+ io.write <<-STUB
+# -*- encoding: utf-8 -*-
+# stub: stub_e 2 ruby lib
+# stub: ext/stub_e/extconf.rb
+
+Gem::Specification.new do |s|
+ s.name = 'stub_e'
+ s.version = Gem::Version.new '2'
+ s.extensions = ['ext/stub_e/extconf.rb']
+ s.installed_by_version = '2.2'
+end
+ STUB
+
+ io.flush
+
+ stub = Gem::StubSpecification.new io.path
+
+ yield stub if block_given?
+
+ return stub
+ end
+ end
+
+ def stub_without_extension
+ spec = File.join @gemhome, 'specifications', 'stub-2.gemspec'
+ open spec, 'w' do |io|
+ io.write <<-STUB
+# -*- encoding: utf-8 -*-
+# stub: stub 2 ruby lib
+
+Gem::Specification.new do |s|
+ s.name = 'stub'
+ s.version = Gem::Version.new '2'
+end
+ STUB
+
+ io.flush
+
+ stub = Gem::StubSpecification.new io.path
+
+ yield stub if block_given?
+
+ return stub
+ end
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_text.rb b/jni/ruby/test/rubygems/test_gem_text.rb
new file mode 100644
index 0000000..e5cfc41
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_text.rb
@@ -0,0 +1,74 @@
+require 'rubygems/test_case'
+require "rubygems/text"
+
+class TestGemText < Gem::TestCase
+ include Gem::Text
+
+ def test_format_text
+ assert_equal "text to\nwrap", format_text("text to wrap", 8)
+ end
+
+ def test_format_text_indent
+ assert_equal " text to\n wrap", format_text("text to wrap", 8, 2)
+ end
+
+ def test_format_text_none
+ assert_equal "text to wrap", format_text("text to wrap", 40)
+ end
+
+ def test_format_text_none_indent
+ assert_equal " text to wrap", format_text("text to wrap", 40, 2)
+ end
+
+ def test_format_text_trailing # for two spaces after .
+ text = <<-TEXT
+This line is really, really long. So long, in fact, that it is more than eighty characters long! The purpose of this line is for testing wrapping behavior because sometimes people don't wrap their text to eighty characters. Without the wrapping, the text might not look good in the RSS feed.
+ TEXT
+
+ expected = <<-EXPECTED
+This line is really, really long. So long, in fact, that it is more than
+eighty characters long! The purpose of this line is for testing wrapping
+behavior because sometimes people don't wrap their text to eighty characters.
+Without the wrapping, the text might not look good in the RSS feed.
+ EXPECTED
+
+ assert_equal expected, format_text(text, 78)
+ end
+
+ def test_min3
+ assert_equal 1, min3(1, 1, 1)
+ assert_equal 1, min3(1, 1, 2)
+ assert_equal 1, min3(1, 2, 1)
+ assert_equal 1, min3(2, 1, 1)
+ assert_equal 1, min3(1, 2, 2)
+ assert_equal 1, min3(2, 1, 2)
+ assert_equal 1, min3(2, 2, 1)
+ assert_equal 1, min3(1, 2, 3)
+ assert_equal 1, min3(1, 3, 2)
+ assert_equal 1, min3(2, 1, 3)
+ assert_equal 1, min3(2, 3, 1)
+ assert_equal 1, min3(3, 1, 2)
+ assert_equal 1, min3(3, 2, 1)
+ end
+
+ def test_levenshtein_distance_add
+ assert_equal 2, levenshtein_distance("zentest", "zntst")
+ assert_equal 2, levenshtein_distance("zntst", "zentest")
+ end
+
+ def test_levenshtein_distance_empty
+ assert_equal 5, levenshtein_distance("abcde", "")
+ assert_equal 5, levenshtein_distance("", "abcde")
+ end
+
+ def test_levenshtein_distance_remove
+ assert_equal 3, levenshtein_distance("zentest", "zentestxxx")
+ assert_equal 3, levenshtein_distance("zentestxxx", "zentest")
+ end
+
+ def test_levenshtein_distance_replace
+ assert_equal 2, levenshtein_distance("zentest", "ZenTest")
+ assert_equal 7, levenshtein_distance("xxxxxxx", "ZenTest")
+ assert_equal 7, levenshtein_distance("zentest", "xxxxxxx")
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_uninstaller.rb b/jni/ruby/test/rubygems/test_gem_uninstaller.rb
new file mode 100644
index 0000000..2a9a15d
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_uninstaller.rb
@@ -0,0 +1,483 @@
+require 'rubygems/installer_test_case'
+require 'rubygems/uninstaller'
+
+class TestGemUninstaller < Gem::InstallerTestCase
+
+ def setup
+ super
+ common_installer_setup
+
+ build_rake_in do
+ use_ui ui do
+ @installer.install
+ @spec = @installer.spec
+
+ @user_installer.install
+ @user_spec = @user_installer.spec
+ end
+ end
+
+ Gem::Specification.reset
+ end
+
+ def test_initialize_expand_path
+ uninstaller = Gem::Uninstaller.new nil, :install_dir => '/foo//bar'
+
+ assert_match %r|/foo/bar$|, uninstaller.instance_variable_get(:@gem_home)
+ end
+
+ def test_ask_if_ok
+ c = util_spec 'c'
+
+ uninstaller = Gem::Uninstaller.new nil
+
+ ok = :junk
+
+ ui = Gem::MockGemUi.new "\n"
+
+ use_ui ui do
+ ok = uninstaller.ask_if_ok c
+ end
+
+ refute ok
+ end
+
+ def test_remove_all
+ uninstaller = Gem::Uninstaller.new nil
+
+ ui = Gem::MockGemUi.new "y\n"
+
+ use_ui ui do
+ uninstaller.remove_all [@spec]
+ end
+
+ refute_path_exists @spec.gem_dir
+ end
+
+ def test_remove_executables_force_keep
+ uninstaller = Gem::Uninstaller.new nil, :executables => false
+
+ executable = File.join Gem.bindir(@user_spec.base_dir), 'executable'
+ assert File.exist?(executable), 'executable not written'
+
+ use_ui @ui do
+ uninstaller.remove_executables @user_spec
+ end
+
+ assert File.exist? executable
+
+ assert_equal "Executables and scripts will remain installed.\n", @ui.output
+ end
+
+ def test_remove_executables_force_remove
+ uninstaller = Gem::Uninstaller.new nil, :executables => true
+
+ executable = File.join Gem.bindir(@user_spec.base_dir), 'executable'
+ assert File.exist?(executable), 'executable not written'
+
+ use_ui @ui do
+ uninstaller.remove_executables @user_spec
+ end
+
+ assert_equal "Removing executable\n", @ui.output
+
+ refute File.exist? executable
+ end
+
+ def test_remove_executables_user
+ uninstaller = Gem::Uninstaller.new nil, :executables => true
+
+ use_ui @ui do
+ uninstaller.remove_executables @user_spec
+ end
+
+ exec_path = File.join Gem.user_dir, 'bin', 'executable'
+ refute File.exist?(exec_path), 'exec still exists in user bin dir'
+
+ assert_equal "Removing executable\n", @ui.output
+ end
+
+ def test_remove_executables_user_format
+ Gem::Installer.exec_format = 'foo-%s-bar'
+
+ uninstaller = Gem::Uninstaller.new nil, :executables => true, :format_executable => true
+
+ use_ui @ui do
+ uninstaller.remove_executables @user_spec
+ end
+
+ exec_path = File.join Gem.user_dir, 'bin', 'foo-executable-bar'
+ assert_equal false, File.exist?(exec_path), 'removed exec from bin dir'
+
+ assert_equal "Removing foo-executable-bar\n", @ui.output
+ ensure
+ Gem::Installer.exec_format = nil
+ end
+
+ def test_remove_executables_user_format_disabled
+ Gem::Installer.exec_format = 'foo-%s-bar'
+
+ uninstaller = Gem::Uninstaller.new nil, :executables => true
+
+ use_ui @ui do
+ uninstaller.remove_executables @user_spec
+ end
+
+ exec_path = File.join Gem.user_dir, 'bin', 'executable'
+ refute File.exist?(exec_path), 'removed exec from bin dir'
+
+ assert_equal "Removing executable\n", @ui.output
+ ensure
+ Gem::Installer.exec_format = nil
+ end
+
+ def test_remove_not_in_home
+ uninstaller = Gem::Uninstaller.new nil, :install_dir => "#{@gemhome}2"
+
+ e = assert_raises Gem::GemNotInHomeException do
+ use_ui ui do
+ uninstaller.remove @spec
+ end
+ end
+
+ expected =
+ "Gem '#{@spec.full_name}' is not installed in directory #{@gemhome}2"
+
+ assert_equal expected, e.message
+
+ assert_path_exists @spec.gem_dir
+ end
+
+ def test_path_ok_eh
+ uninstaller = Gem::Uninstaller.new nil
+
+ assert_equal true, uninstaller.path_ok?(@gemhome, @spec)
+ end
+
+ def test_path_ok_eh_legacy
+ uninstaller = Gem::Uninstaller.new nil
+
+ @spec.loaded_from = @spec.loaded_from.gsub @spec.full_name, '\&-legacy'
+ @spec.platform = 'legacy'
+
+ assert_equal true, uninstaller.path_ok?(@gemhome, @spec)
+ end
+
+ def test_path_ok_eh_user
+ uninstaller = Gem::Uninstaller.new nil
+
+ assert_equal true, uninstaller.path_ok?(Gem.user_dir, @user_spec)
+ end
+
+ def test_uninstall
+ uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
+
+ gem_dir = File.join @gemhome, 'gems', @spec.full_name
+
+ Gem.pre_uninstall do
+ assert File.exist?(gem_dir), 'gem_dir should exist'
+ end
+
+ Gem.post_uninstall do
+ refute File.exist?(gem_dir), 'gem_dir should not exist'
+ end
+
+ uninstaller.uninstall
+
+ refute File.exist?(gem_dir)
+
+ assert_same uninstaller, @pre_uninstall_hook_arg
+ assert_same uninstaller, @post_uninstall_hook_arg
+ end
+
+ def test_uninstall_default_gem
+ spec = new_default_spec 'default', '2'
+
+ install_default_gems spec
+
+ uninstaller = Gem::Uninstaller.new spec.name, :executables => true
+
+ e = assert_raises Gem::InstallError do
+ uninstaller.uninstall
+ end
+
+ assert_equal 'gem "default" cannot be uninstalled ' +
+ 'because it is a default gem',
+ e.message
+ end
+
+ def test_uninstall_default_gem_with_same_version
+ default_spec = new_default_spec 'default', '2'
+ install_default_gems default_spec
+
+ spec = new_spec 'default', '2'
+ install_gem spec
+
+ Gem::Specification.reset
+
+ uninstaller = Gem::Uninstaller.new spec.name, :executables => true
+
+ uninstaller.uninstall
+
+ refute_path_exists spec.gem_dir
+ end
+
+ def test_uninstall_extension
+ @spec.extensions << 'extconf.rb'
+ write_file File.join(@tempdir, 'extconf.rb') do |io|
+ io.write <<-RUBY
+require 'mkmf'
+create_makefile '#{@spec.name}'
+ RUBY
+ end
+
+ @spec.files += %w[extconf.rb]
+
+ use_ui @ui do
+ path = Gem::Package.build @spec
+
+ installer = Gem::Installer.new path
+ installer.install
+ end
+
+ assert_path_exists @spec.extension_dir, 'sanity check'
+
+ uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
+ uninstaller.uninstall
+
+ refute_path_exists @spec.extension_dir
+ end
+
+ def test_uninstall_nonexistent
+ uninstaller = Gem::Uninstaller.new 'bogus', :executables => true
+
+ e = assert_raises Gem::InstallError do
+ uninstaller.uninstall
+ end
+
+ assert_equal 'gem "bogus" is not installed', e.message
+ end
+
+ def test_uninstall_not_ok
+ quick_gem 'z' do |s|
+ s.add_runtime_dependency @spec.name
+ end
+
+ uninstaller = Gem::Uninstaller.new @spec.name
+
+ gem_dir = File.join @gemhome, 'gems', @spec.full_name
+ executable = File.join @gemhome, 'bin', 'executable'
+
+ assert File.exist?(gem_dir), 'gem_dir must exist'
+ assert File.exist?(executable), 'executable must exist'
+
+ ui = Gem::MockGemUi.new "n\n"
+
+ assert_raises Gem::DependencyRemovalException do
+ use_ui ui do
+ uninstaller.uninstall
+ end
+ end
+
+ assert File.exist?(gem_dir), 'gem_dir must still exist'
+ assert File.exist?(executable), 'executable must still exist'
+ end
+
+ def test_uninstall_user_install
+ @user_spec = Gem::Specification.find_by_name 'b'
+
+ uninstaller = Gem::Uninstaller.new(@user_spec.name,
+ :executables => true,
+ :user_install => true)
+
+ gem_dir = File.join @user_spec.gem_dir
+
+ Gem.pre_uninstall do
+ assert_path_exists gem_dir
+ end
+
+ Gem.post_uninstall do
+ refute_path_exists gem_dir
+ end
+
+ uninstaller.uninstall
+
+ refute_path_exists gem_dir
+
+ assert_same uninstaller, @pre_uninstall_hook_arg
+ assert_same uninstaller, @post_uninstall_hook_arg
+ end
+
+ def test_uninstall_wrong_repo
+ Gem.use_paths "#{@gemhome}2", [@gemhome]
+
+ uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
+
+ e = assert_raises Gem::InstallError do
+ uninstaller.uninstall
+ end
+
+ expected = <<-MESSAGE.strip
+#{@spec.name} is not installed in GEM_HOME, try:
+\tgem uninstall -i #{@gemhome} a
+ MESSAGE
+
+ assert_equal expected, e.message
+ end
+
+ def test_uninstall_selection
+ util_make_gems
+
+ list = Gem::Specification.find_all_by_name 'a'
+
+ uninstaller = Gem::Uninstaller.new 'a'
+
+ ui = Gem::MockGemUi.new "1\ny\n"
+
+ use_ui ui do
+ uninstaller.uninstall
+ end
+
+ updated_list = Gem::Specification.find_all_by_name('a')
+ assert_equal list.length - 1, updated_list.length
+
+ assert_match ' 1. a-1', ui.output
+ assert_match ' 2. a-2', ui.output
+ assert_match ' 3. a-3.a', ui.output
+ assert_match ' 4. All versions', ui.output
+ assert_match 'uninstalled a-1', ui.output
+ end
+
+ def test_uninstall_selection_greater_than_one
+ util_make_gems
+
+ list = Gem::Specification.find_all_by_name('a')
+
+ uninstaller = Gem::Uninstaller.new('a')
+
+ use_ui Gem::MockGemUi.new("2\ny\n") do
+ uninstaller.uninstall
+ end
+
+ updated_list = Gem::Specification.find_all_by_name('a')
+ assert_equal list.length - 1, updated_list.length
+ end
+
+ def test_uninstall_prompts_about_broken_deps
+ quick_gem 'r', '1' do |s| s.add_dependency 'q', '= 1' end
+ quick_gem 'q', '1'
+
+ un = Gem::Uninstaller.new('q')
+ ui = Gem::MockGemUi.new("y\n")
+
+ use_ui ui do
+ un.uninstall
+ end
+
+ lines = ui.output.split("\n")
+ lines.shift
+
+ assert_match %r!You have requested to uninstall the gem:!, lines.shift
+ lines.shift
+ lines.shift
+
+ assert_match %r!r-1 depends on q \(= 1\)!, lines.shift
+ assert_match %r!Successfully uninstalled q-1!, lines.last
+ end
+
+ def test_uninstall_only_lists_unsatified_deps
+ quick_gem 'r', '1' do |s| s.add_dependency 'q', '~> 1.0' end
+ quick_gem 'x', '1' do |s| s.add_dependency 'q', '= 1.0' end
+ quick_gem 'q', '1.0'
+ quick_gem 'q', '1.1'
+
+ un = Gem::Uninstaller.new('q', :version => "1.0")
+ ui = Gem::MockGemUi.new("y\n")
+
+ use_ui ui do
+ un.uninstall
+ end
+
+ lines = ui.output.split("\n")
+ lines.shift
+
+ assert_match %r!You have requested to uninstall the gem:!, lines.shift
+ lines.shift
+ lines.shift
+
+ assert_match %r!x-1 depends on q \(= 1.0\)!, lines.shift
+ assert_match %r!Successfully uninstalled q-1.0!, lines.last
+ end
+
+ def test_uninstall_doesnt_prompt_when_other_gem_satifies_requirement
+ quick_gem 'r', '1' do |s| s.add_dependency 'q', '~> 1.0' end
+ quick_gem 'q', '1.0'
+ quick_gem 'q', '1.1'
+
+ un = Gem::Uninstaller.new('q', :version => "1.0")
+ ui = Gem::MockGemUi.new("y\n")
+
+ use_ui ui do
+ un.uninstall
+ end
+
+ lines = ui.output.split("\n")
+
+ assert_equal "Successfully uninstalled q-1.0", lines.shift
+ end
+
+ def test_uninstall_doesnt_prompt_when_removing_a_dev_dep
+ quick_gem 'r', '1' do |s| s.add_development_dependency 'q', '= 1.0' end
+ quick_gem 'q', '1.0'
+
+ un = Gem::Uninstaller.new('q', :version => "1.0")
+ ui = Gem::MockGemUi.new("y\n")
+
+ use_ui ui do
+ un.uninstall
+ end
+
+ lines = ui.output.split("\n")
+
+ assert_equal "Successfully uninstalled q-1.0", lines.shift
+ end
+
+ def test_uninstall_doesnt_prompt_and_raises_when_abort_on_dependent_set
+ quick_gem 'r', '1' do |s| s.add_dependency 'q', '= 1' end
+ quick_gem 'q', '1'
+
+ un = Gem::Uninstaller.new('q', :abort_on_dependent => true)
+ ui = Gem::MockGemUi.new("y\n")
+
+ assert_raises Gem::DependencyRemovalException do
+ use_ui ui do
+ un.uninstall
+ end
+ end
+ end
+
+ def test_uninstall_prompt_includes_dep_type
+ quick_gem 'r', '1' do |s|
+ s.add_development_dependency 'q', '= 1'
+ end
+
+ quick_gem 'q', '1'
+
+ un = Gem::Uninstaller.new('q', :check_dev => true)
+ ui = Gem::MockGemUi.new("y\n")
+
+ use_ui ui do
+ un.uninstall
+ end
+
+ lines = ui.output.split("\n")
+ lines.shift
+
+ assert_match %r!You have requested to uninstall the gem:!, lines.shift
+ lines.shift
+ lines.shift
+
+ assert_match %r!r-1 depends on q \(= 1, development\)!, lines.shift
+ assert_match %r!Successfully uninstalled q-1!, lines.last
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_unsatisfiable_dependency_error.rb b/jni/ruby/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
new file mode 100644
index 0000000..6418c5d
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
@@ -0,0 +1,32 @@
+require 'rubygems/test_case'
+
+class TestGemUnsatisfiableDependencyError < Gem::TestCase
+
+ def setup
+ super
+
+ @a_dep = dep 'a', '~> 1'
+
+ @req = Gem::Resolver::DependencyRequest.new @a_dep, nil
+
+ @e = Gem::UnsatisfiableDependencyError.new @req
+ end
+
+ def test_errors
+ assert_equal [], @e.errors
+
+ @e.errors << :a
+
+ assert_equal [:a], @e.errors
+ end
+
+ def test_name
+ assert_equal 'a', @e.name
+ end
+
+ def test_version
+ assert_equal @a_dep.requirement, @e.version
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_uri_formatter.rb b/jni/ruby/test/rubygems/test_gem_uri_formatter.rb
new file mode 100644
index 0000000..628b7c5
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_uri_formatter.rb
@@ -0,0 +1,28 @@
+require 'rubygems/test_case'
+require 'rubygems/uri_formatter'
+
+class TestGemUriFormatter < Gem::TestCase
+
+ def test_normalize_uri
+ assert_equal 'FILE://example/',
+ Gem::UriFormatter.new('FILE://example/').normalize
+ assert_equal 'FTP://example/',
+ Gem::UriFormatter.new('FTP://example/').normalize
+ assert_equal 'HTTP://example/',
+ Gem::UriFormatter.new('HTTP://example/').normalize
+ assert_equal 'HTTPS://example/',
+ Gem::UriFormatter.new('HTTPS://example/').normalize
+ assert_equal 'http://example/',
+ Gem::UriFormatter.new('example/').normalize
+ end
+
+ def test_escape
+ assert_equal 'a%40b%5Cc', Gem::UriFormatter.new('a@b\c').escape
+ end
+
+ def test_unescape
+ assert_equal 'a@b\c', Gem::UriFormatter.new('a%40b%5Cc').unescape
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_util.rb b/jni/ruby/test/rubygems/test_gem_util.rb
new file mode 100644
index 0000000..414487a
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_util.rb
@@ -0,0 +1,31 @@
+require 'rubygems/test_case'
+require 'rubygems/util'
+
+class TestGemUtil < Gem::TestCase
+
+ def test_class_popen
+ assert_equal "0\n", Gem::Util.popen(Gem.ruby, '-e', 'p 0')
+
+ assert_raises Errno::ECHILD do
+ Process.wait(-1)
+ end
+ end
+
+ def test_silent_system
+ assert_silent do
+ Gem::Util.silent_system Gem.ruby, '-e', 'puts "hello"; warn "hello"'
+ end
+ end
+
+ def test_traverse_parents
+ FileUtils.mkdir_p 'a/b/c'
+
+ enum = Gem::Util.traverse_parents 'a/b/c'
+
+ assert_equal File.join(@tempdir, 'a/b/c'), enum.next
+ assert_equal File.join(@tempdir, 'a/b'), enum.next
+ assert_equal File.join(@tempdir, 'a'), enum.next
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_validator.rb b/jni/ruby/test/rubygems/test_gem_validator.rb
new file mode 100644
index 0000000..4af8b52
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_validator.rb
@@ -0,0 +1,45 @@
+require 'rubygems/test_case'
+require "rubygems/simple_gem"
+require 'rubygems/validator'
+
+class TestGemValidator < Gem::TestCase
+
+ def setup
+ super
+
+ @simple_gem = SIMPLE_GEM
+ @validator = Gem::Validator.new
+ end
+
+ def test_alien
+ @spec = quick_gem 'a' do |s|
+ s.files = %w[lib/a.rb lib/b.rb]
+ end
+
+ util_build_gem @spec
+
+ FileUtils.rm File.join(@spec.gem_dir, 'lib/b.rb')
+ FileUtils.touch File.join(@spec.gem_dir, 'lib/c.rb')
+
+ alien = @validator.alien 'a'
+
+ expected = {
+ @spec.file_name => [
+ Gem::Validator::ErrorData.new('lib/b.rb', 'Missing file'),
+ Gem::Validator::ErrorData.new('lib/c.rb', 'Extra file'),
+ ]
+ }
+
+ assert_equal expected, alien
+ end
+
+ def test_alien_default
+ new_default_spec 'c', 1, nil, 'lib/c.rb'
+
+ alien = @validator.alien 'c'
+
+ assert_empty alien
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_gem_version.rb b/jni/ruby/test/rubygems/test_gem_version.rb
new file mode 100644
index 0000000..5a65b5c
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_version.rb
@@ -0,0 +1,213 @@
+require 'rubygems/test_case'
+require "rubygems/version"
+
+class TestGemVersion < Gem::TestCase
+
+ class V < ::Gem::Version
+ end
+
+ def test_bump
+ assert_bumped_version_equal "5.3", "5.2.4"
+ end
+
+ def test_bump_alpha
+ assert_bumped_version_equal "5.3", "5.2.4.a"
+ end
+
+ def test_bump_alphanumeric
+ assert_bumped_version_equal "5.3", "5.2.4.a10"
+ end
+
+ def test_bump_trailing_zeros
+ assert_bumped_version_equal "5.1", "5.0.0"
+ end
+
+ def test_bump_one_level
+ assert_bumped_version_equal "6", "5"
+ end
+
+ # A Gem::Version is already a Gem::Version and therefore not transformed by
+ # Gem::Version.create
+
+ def test_class_create
+ real = Gem::Version.new(1.0)
+
+ assert_same real, Gem::Version.create(real)
+ assert_nil Gem::Version.create(nil)
+ assert_equal v("5.1"), Gem::Version.create("5.1")
+
+ ver = '1.1'.freeze
+ assert_equal v('1.1'), Gem::Version.create(ver)
+ end
+
+ def test_class_new_subclass
+ v1 = Gem::Version.new '1'
+ v2 = V.new '1'
+
+ refute_same v1, v2
+ end
+
+ def test_eql_eh
+ assert_version_eql "1.2", "1.2"
+ refute_version_eql "1.2", "1.2.0"
+ refute_version_eql "1.2", "1.3"
+ refute_version_eql "1.2.b1", "1.2.b.1"
+ end
+
+ def test_equals2
+ assert_version_equal "1.2", "1.2"
+ refute_version_equal "1.2", "1.3"
+ assert_version_equal "1.2.b1", "1.2.b.1"
+ end
+
+ # REVISIT: consider removing as too impl-bound
+ def test_hash
+ assert_equal v("1.2").hash, v("1.2").hash
+ refute_equal v("1.2").hash, v("1.3").hash
+ refute_equal v("1.2").hash, v("1.2.0").hash
+ end
+
+ def test_initialize
+ ["1.0", "1.0 ", " 1.0 ", "1.0\n", "\n1.0\n", "1.0".freeze].each do |good|
+ assert_version_equal "1.0", good
+ end
+
+ assert_version_equal "1", 1
+ end
+
+ def test_initialize_bad
+ %W[
+ junk
+ 1.0\n2.0
+ 1..2
+ 1.2\ 3.4
+ ].each do |bad|
+ e = assert_raises ArgumentError, bad do
+ Gem::Version.new bad
+ end
+
+ assert_equal "Malformed version number string #{bad}", e.message, bad
+ end
+ end
+
+ def test_prerelease
+ assert_prerelease "1.2.0.a"
+ assert_prerelease "2.9.b"
+ assert_prerelease "22.1.50.0.d"
+ assert_prerelease "1.2.d.42"
+
+ assert_prerelease '1.A'
+
+ refute_prerelease "1.2.0"
+ refute_prerelease "2.9"
+ refute_prerelease "22.1.50.0"
+ end
+
+ def test_release
+ assert_release_equal "1.2.0", "1.2.0.a"
+ assert_release_equal "1.1", "1.1.rc10"
+ assert_release_equal "1.9.3", "1.9.3.alpha.5"
+ assert_release_equal "1.9.3", "1.9.3"
+ end
+
+ def test_spaceship
+ assert_equal( 0, v("1.0") <=> v("1.0.0"))
+ assert_equal( 1, v("1.0") <=> v("1.0.a"))
+ assert_equal( 1, v("1.8.2") <=> v("0.0.0"))
+ assert_equal( 1, v("1.8.2") <=> v("1.8.2.a"))
+ assert_equal( 1, v("1.8.2.b") <=> v("1.8.2.a"))
+ assert_equal(-1, v("1.8.2.a") <=> v("1.8.2"))
+ assert_equal( 1, v("1.8.2.a10") <=> v("1.8.2.a9"))
+ assert_equal( 0, v("") <=> v("0"))
+
+ assert_nil v("1.0") <=> "whatever"
+ end
+
+ def test_approximate_recommendation
+ assert_approximate_equal "~> 1.0", "1"
+ assert_approximate_equal "~> 1.0", "1.0"
+ assert_approximate_equal "~> 1.2", "1.2"
+ assert_approximate_equal "~> 1.2", "1.2.0"
+ assert_approximate_equal "~> 1.2", "1.2.3"
+ assert_approximate_equal "~> 1.2", "1.2.3.a.4"
+ end
+
+ def test_to_s
+ assert_equal "5.2.4", v("5.2.4").to_s
+ end
+
+ def test_semver
+ assert_less_than "1.0.0-alpha", "1.0.0-alpha.1"
+ assert_less_than "1.0.0-alpha.1", "1.0.0-beta.2"
+ assert_less_than "1.0.0-beta.2", "1.0.0-beta.11"
+ assert_less_than "1.0.0-beta.11", "1.0.0-rc.1"
+ assert_less_than "1.0.0-rc1", "1.0.0"
+ assert_less_than "1.0.0-1", "1"
+ end
+
+ # Asserts that +version+ is a prerelease.
+
+ def assert_prerelease version
+ assert v(version).prerelease?, "#{version} is a prerelease"
+ end
+
+ # Assert that +expected+ is the "approximate" recommendation for +version".
+
+ def assert_approximate_equal expected, version
+ assert_equal expected, v(version).approximate_recommendation
+ end
+
+ # Assert that bumping the +unbumped+ version yields the +expected+.
+
+ def assert_bumped_version_equal expected, unbumped
+ assert_version_equal expected, v(unbumped).bump
+ end
+
+ # Assert that +release+ is the correct non-prerelease +version+.
+
+ def assert_release_equal release, version
+ assert_version_equal release, v(version).release
+ end
+
+ # Assert that two versions are equal. Handles strings or
+ # Gem::Version instances.
+
+ def assert_version_equal expected, actual
+ assert_equal v(expected), v(actual)
+ end
+
+ # Assert that two versions are eql?. Checks both directions.
+
+ def assert_version_eql first, second
+ first, second = v(first), v(second)
+ assert first.eql?(second), "#{first} is eql? #{second}"
+ assert second.eql?(first), "#{second} is eql? #{first}"
+ end
+
+ def assert_less_than left, right
+ l = v(left)
+ r = v(right)
+ assert l < r, "#{left} not less than #{right}"
+ end
+
+ # Refute the assumption that +version+ is a prerelease.
+
+ def refute_prerelease version
+ refute v(version).prerelease?, "#{version} is NOT a prerelease"
+ end
+
+ # Refute the assumption that two versions are eql?. Checks both
+ # directions.
+
+ def refute_version_eql first, second
+ first, second = v(first), v(second)
+ refute first.eql?(second), "#{first} is NOT eql? #{second}"
+ refute second.eql?(first), "#{second} is NOT eql? #{first}"
+ end
+
+ # Refute the assumption that the two versions are equal?.
+
+ def refute_version_equal unexpected, actual
+ refute_equal v(unexpected), v(actual)
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_gem_version_option.rb b/jni/ruby/test/rubygems/test_gem_version_option.rb
new file mode 100644
index 0000000..d6035ab
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_gem_version_option.rb
@@ -0,0 +1,151 @@
+require 'rubygems/test_case'
+require 'rubygems/command'
+require 'rubygems/version_option'
+
+class TestGemVersionOption < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Command.new 'dummy', 'dummy'
+ @cmd.extend Gem::VersionOption
+ end
+
+ def test_add_platform_option
+ @cmd.add_platform_option
+
+ assert @cmd.handles?(%w[--platform x86-darwin])
+ end
+
+ def test_add_version_option
+ @cmd.add_version_option
+
+ assert @cmd.handles?(%w[--version >1])
+ end
+
+ def test_enables_prerelease
+ @cmd.add_version_option
+
+ @cmd.handle_options %w[mygem -v 0.2.0.a]
+ assert @cmd.options[:prerelease]
+
+ @cmd.handle_options %w[mygem -v 0.2.0]
+ refute @cmd.options[:prerelease]
+
+ @cmd.handle_options %w[mygem]
+ refute @cmd.options[:prerelease]
+ end
+
+ def test_platform_option
+ @cmd.add_platform_option
+
+ @cmd.handle_options %w[--platform x86-freebsd6 --platform x86-freebsd7]
+
+ expected = [
+ Gem::Platform::RUBY,
+ Gem::Platform.new('x86-freebsd6'),
+ Gem::Platform.new('x86-freebsd7'),
+ ]
+
+ assert_equal expected, Gem.platforms
+ end
+
+ def test_platform_option_ruby
+ @cmd.add_platform_option
+
+ @cmd.handle_options %w[--platform ruby]
+
+ expected = [
+ Gem::Platform::RUBY
+ ]
+
+ assert_equal expected, Gem.platforms
+ end
+
+ def test_platform_option_twice
+ @cmd.add_platform_option
+
+ @cmd.handle_options %w[--platform x86-freebsd6 --platform x86-freebsd-6]
+
+ expected = [
+ Gem::Platform::RUBY,
+ Gem::Platform.new('x86-freebsd6'),
+ ]
+
+ assert_equal expected, Gem.platforms
+ end
+
+ def test_version_option
+ @cmd.add_version_option
+
+ @cmd.handle_options %w[--version >1]
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => false,
+ :version => Gem::Requirement.new('> 1'),
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+ def test_version_option_compound
+ @cmd.add_version_option
+
+ @cmd.handle_options ['--version', '< 1, > 0.9']
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => false,
+ :version => Gem::Requirement.new('< 1', '> 0.9'),
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+ def test_version_option_explicit_prerelease
+ @cmd.add_prerelease_option
+ @cmd.add_version_option
+
+ @cmd.handle_options %w[--pre --version >1]
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => true,
+ :prerelease => true,
+ :version => Gem::Requirement.new('> 1'),
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+ def test_version_option_twice
+ @cmd.add_version_option
+
+ @cmd.handle_options %w[--version >1.a]
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => true,
+ :version => Gem::Requirement.new('> 1.a'),
+ }
+
+ assert_equal expected, @cmd.options
+
+ @cmd.handle_options %w[--version >1]
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => false,
+ :version => Gem::Requirement.new('> 1'),
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+end
+
diff --git a/jni/ruby/test/rubygems/test_kernel.rb b/jni/ruby/test/rubygems/test_kernel.rb
new file mode 100644
index 0000000..ee8b248
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_kernel.rb
@@ -0,0 +1,85 @@
+require 'rubygems/test_case'
+
+class TestKernel < Gem::TestCase
+
+ def setup
+ super
+
+ @old_path = $:.dup
+
+ util_make_gems
+ end
+
+ def teardown
+ super
+
+ $:.replace @old_path
+ end
+
+ def test_gem
+ assert gem('a', '= 1'), "Should load"
+ assert $:.any? { |p| %r{a-1/lib} =~ p }
+ end
+
+ def test_gem_default
+ assert gem('a', '>= 0')
+
+ assert_equal @a2, Gem.loaded_specs['a']
+ end
+
+ def test_gem_default_re_gem
+ assert gem('a', '=1')
+
+ refute gem('a', '>= 0')
+
+ assert_equal @a1, Gem.loaded_specs['a']
+ end
+
+ def test_gem_re_gem_mismatch
+ assert gem('a', '=1')
+
+ assert_raises Gem::LoadError do
+ gem('a', '= 2')
+ end
+
+ assert_equal @a1, Gem.loaded_specs['a']
+ end
+
+ def test_gem_redundant
+ assert gem('a', '= 1'), "Should load"
+ refute gem('a', '= 1'), "Should not load"
+ assert_equal 1, $:.select { |p| %r{a-1/lib} =~ p }.size
+ end
+
+ def test_gem_overlapping
+ assert gem('a', '= 1'), "Should load"
+ refute gem('a', '>= 1'), "Should not load"
+ assert_equal 1, $:.select { |p| %r{a-1/lib} =~ p }.size
+ end
+
+ def test_gem_prerelease
+ quick_gem 'd', '1.1.a'
+ refute gem('d', '>= 1'), 'release requirement must not load prerelease'
+ assert gem('d', '>= 1.a'), 'prerelease requirement may load prerelease'
+ end
+
+ def test_gem_conflicting
+ assert gem('a', '= 1'), "Should load"
+
+ ex = assert_raises Gem::LoadError do
+ gem 'a', '= 2'
+ end
+
+ assert_equal "can't activate a-2, already activated a-1", ex.message
+ assert_match(/activated a-1/, ex.message)
+ assert_equal 'a', ex.name
+
+ assert $:.any? { |p| %r{a-1/lib} =~ p }
+ refute $:.any? { |p| %r{a-2/lib} =~ p }
+ end
+
+ def test_gem_not_adding_bin
+ assert gem('a', '= 1'), "Should load"
+ refute $:.any? { |p| %r{a-1/bin} =~ p }
+ end
+end
diff --git a/jni/ruby/test/rubygems/test_require.rb b/jni/ruby/test/rubygems/test_require.rb
new file mode 100644
index 0000000..dec5285
--- /dev/null
+++ b/jni/ruby/test/rubygems/test_require.rb
@@ -0,0 +1,275 @@
+require 'rubygems/test_case'
+require 'rubygems'
+
+class TestGemRequire < Gem::TestCase
+ class Latch
+ def initialize count = 1
+ @count = count
+ @lock = Monitor.new
+ @cv = @lock.new_cond
+ end
+
+ def release
+ @lock.synchronize do
+ @count -= 1 if @count > 0
+ @cv.broadcast if @count.zero?
+ end
+ end
+
+ def await
+ @lock.synchronize do
+ @cv.wait_while { @count > 0 }
+ end
+ end
+ end
+
+ def setup
+ super
+
+ assert_raises LoadError do
+ save_loaded_features do
+ require 'test_gem_require_a'
+ end
+ end
+ end
+
+ def assert_require(path)
+ assert require(path), "'#{path}' was already required"
+ end
+
+ def append_latch spec
+ dir = spec.gem_dir
+ Dir.chdir dir do
+ spec.files.each do |file|
+ File.open file, 'a' do |fp|
+ fp.puts "FILE_ENTERED_LATCH.release"
+ fp.puts "FILE_EXIT_LATCH.await"
+ end
+ end
+ end
+ end
+
+ def test_concurrent_require
+ Object.const_set :FILE_ENTERED_LATCH, Latch.new(2)
+ Object.const_set :FILE_EXIT_LATCH, Latch.new(1)
+
+ a1 = new_spec "a", "1", nil, "lib/a.rb"
+ b1 = new_spec "b", "1", nil, "lib/b.rb"
+
+ install_specs a1, b1
+
+ append_latch a1
+ append_latch b1
+
+ t1 = Thread.new { assert_require 'a' }
+ t2 = Thread.new { assert_require 'b' }
+
+ # wait until both files are waiting on the exit latch
+ FILE_ENTERED_LATCH.await
+
+ # now let them finish
+ FILE_EXIT_LATCH.release
+
+ assert t1.join, "thread 1 should exit"
+ assert t2.join, "thread 2 should exit"
+ ensure
+ Object.send :remove_const, :FILE_ENTERED_LATCH
+ Object.send :remove_const, :FILE_EXIT_LATCH
+ end
+
+ def test_require_is_not_lazy_with_exact_req
+ a1 = new_spec "a", "1", {"b" => "= 1"}, "lib/test_gem_require_a.rb"
+ b1 = new_spec "b", "1", nil, "lib/b/c.rb"
+ b2 = new_spec "b", "2", nil, "lib/b/c.rb"
+
+ install_specs a1, b1, b2
+
+ save_loaded_features do
+ assert_require 'test_gem_require_a'
+ assert_equal %w(a-1 b-1), loaded_spec_names
+ assert_equal unresolved_names, []
+
+ assert_require "b/c"
+ assert_equal %w(a-1 b-1), loaded_spec_names
+ end
+ end
+
+ def test_require_is_lazy_with_inexact_req
+ a1 = new_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
+ b1 = new_spec "b", "1", nil, "lib/b/c.rb"
+ b2 = new_spec "b", "2", nil, "lib/b/c.rb"
+
+ install_specs a1, b1, b2
+
+ save_loaded_features do
+ assert_require 'test_gem_require_a'
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal unresolved_names, ["b (>= 1)"]
+
+ assert_require "b/c"
+ assert_equal %w(a-1 b-2), loaded_spec_names
+ end
+ end
+
+ def test_require_is_not_lazy_with_one_possible
+ a1 = new_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
+ b1 = new_spec "b", "1", nil, "lib/b/c.rb"
+
+ install_specs a1, b1
+
+ save_loaded_features do
+ assert_require 'test_gem_require_a'
+ assert_equal %w(a-1 b-1), loaded_spec_names
+ assert_equal unresolved_names, []
+
+ assert_require "b/c"
+ assert_equal %w(a-1 b-1), loaded_spec_names
+ end
+ end
+
+ def test_require_can_use_a_pathname_object
+ a1 = new_spec "a", "1", nil, "lib/test_gem_require_a.rb"
+
+ install_specs a1
+
+ save_loaded_features do
+ assert_require Pathname.new 'test_gem_require_a'
+ assert_equal %w(a-1), loaded_spec_names
+ assert_equal unresolved_names, []
+ end
+ end
+
+ def test_activate_via_require_respects_loaded_files
+ require 'benchmark' # stdlib
+ save_loaded_features do
+ a1 = new_spec "a", "1", {"b" => ">= 1"}, "lib/test_gem_require_a.rb"
+ b1 = new_spec "b", "1", nil, "lib/benchmark.rb"
+ b2 = new_spec "b", "2", nil, "lib/benchmark.rb"
+
+ install_specs a1, b1, b2
+
+ require 'test_gem_require_a'
+ assert_equal unresolved_names, ["b (>= 1)"]
+
+ refute require('benchmark'), "benchmark should have already been loaded"
+
+ # We detected that we should activate b-2, so we did so, but
+ # then original_require decided "I've already got benchmark.rb" loaded.
+ # This case is fine because our lazy loading is provided exactly
+ # the same behavior as eager loading would have.
+
+ assert_equal %w(a-1 b-2), loaded_spec_names
+ end
+ end
+
+ def test_already_activated_direct_conflict
+ save_loaded_features do
+ a1 = new_spec "a", "1", { "b" => "> 0" }
+ b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/ib.rb"
+ b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/ib.rb"
+ c1 = new_spec "c", "1", nil, "lib/d.rb"
+ c2 = new_spec("c", "2", nil, "lib/d.rb")
+
+ install_specs a1, b1, b2, c1, c2
+
+ a1.activate
+ c1.activate
+ assert_equal %w(a-1 c-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ assert require("ib")
+
+ assert_equal %w(a-1 b-1 c-1), loaded_spec_names
+ assert_equal [], unresolved_names
+ end
+ end
+
+ def test_multiple_gems_with_the_same_path
+ save_loaded_features do
+ a1 = new_spec "a", "1", { "b" => "> 0", "x" => "> 0" }
+ b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/ib.rb"
+ b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/ib.rb"
+ x1 = new_spec "x", "1", nil, "lib/ib.rb"
+ x2 = new_spec "x", "2", nil, "lib/ib.rb"
+ c1 = new_spec "c", "1", nil, "lib/d.rb"
+ c2 = new_spec("c", "2", nil, "lib/d.rb")
+
+ install_specs a1, b1, b2, c1, c2, x1, x2
+
+ a1.activate
+ c1.activate
+ assert_equal %w(a-1 c-1), loaded_spec_names
+ assert_equal ["b (> 0)", "x (> 0)"], unresolved_names
+
+ e = assert_raises(Gem::LoadError) do
+ require("ib")
+ end
+
+ assert_equal "ib found in multiple gems: b, x", e.message
+ end
+ end
+
+ def test_unable_to_find_good_unresolved_version
+ save_loaded_features do
+ a1 = new_spec "a", "1", { "b" => "> 0" }
+ b1 = new_spec "b", "1", { "c" => ">= 2" }, "lib/ib.rb"
+ b2 = new_spec "b", "2", { "c" => ">= 3" }, "lib/ib.rb"
+
+ c1 = new_spec "c", "1", nil, "lib/d.rb"
+ c2 = new_spec "c", "2", nil, "lib/d.rb"
+ c3 = new_spec "c", "3", nil, "lib/d.rb"
+
+ install_specs a1, b1, b2, c1, c2, c3
+
+ a1.activate
+ c1.activate
+ assert_equal %w(a-1 c-1), loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
+
+ e = assert_raises(Gem::LoadError) do
+ require("ib")
+ end
+
+ assert_equal "unable to find a version of 'b' to activate", e.message
+ end
+ end
+
+ def test_default_gem_only
+ save_loaded_features do
+ default_gem_spec = new_default_spec("default", "2.0.0.0",
+ nil, "default/gem.rb")
+ install_default_specs(default_gem_spec)
+ assert_require "default/gem"
+ assert_equal %w(default-2.0.0.0), loaded_spec_names
+ end
+ end
+
+ def test_default_gem_and_normal_gem
+ save_loaded_features do
+ default_gem_spec = new_default_spec("default", "2.0.0.0",
+ nil, "default/gem.rb")
+ install_default_specs(default_gem_spec)
+ normal_gem_spec = new_spec("default", "3.0", nil,
+ "lib/default/gem.rb")
+ install_specs(normal_gem_spec)
+ assert_require "default/gem"
+ assert_equal %w(default-3.0), loaded_spec_names
+ end
+ end
+
+ def loaded_spec_names
+ Gem.loaded_specs.values.map(&:full_name).sort
+ end
+
+ def unresolved_names
+ Gem::Specification.unresolved_deps.values.map(&:to_s).sort
+ end
+
+ def save_loaded_features
+ old_loaded_features = $LOADED_FEATURES.dup
+ yield
+ ensure
+ $LOADED_FEATURES.replace old_loaded_features
+ end
+
+end
diff --git a/jni/ruby/test/rubygems/wrong_key_cert.pem b/jni/ruby/test/rubygems/wrong_key_cert.pem
new file mode 100644
index 0000000..821eba7
--- /dev/null
+++ b/jni/ruby/test/rubygems/wrong_key_cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7jCCAdagAwIBAgIBEjANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMCAXDTEyMTIwODAwMDAwMFoYDzk5
+OTkxMjMxMjM1OTU5WjAqMQ8wDQYDVQQDDAZub2JvZHkxFzAVBgoJkiaJk/IsZAEZ
+FgdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvZQipBa1
+xH3M9OonkrUYhGZeX9UHAcJhe6jJbUr/uHXkh1Tu2ERWNnblm85upqBfjyZEnKer
+7uBcwwkkvmisVgC8uBECymsBxuEIw0rfiKYEnLu0B6SiWFYz3dYPS92bBK7Vks2/
+kNyXUmLLGoZ3id2K0eK5C/AJ0j+p84OqPnVhylsjrZmXfIZrh7lkHhgCIrzPefjE
+3pOloi/tz6fh2ktb0FYKQMfweT3Ba2TMeflG13PEOW80AD5w0THxDutGG0zPNCDy
+DEoT7UU1a3B3RMHYuUxEk1GUEYWq9L6a6SMpZISWTSpCp0Ww1QB55PONiCCn+o6v
+cIy46jI71dATAQIDAQABox0wGzAZBgNVHREEEjAQgQ5ub2JvZHlAZXhhbXBsZTAN
+BgkqhkiG9w0BAQUFAAOCAQEATOGl3DvDpWn6pOAjNkZz67UYNlTANSzMaXklJmxW
+pQGxy1g+CWRwExfbH7lYAOag3BB7X7h3RolLGhIpxmWriby+SFd/VJAOpCq4nz5P
+6spZ4hqkXchBGzBgbK6TSyNxjITC3iquBBJWTXZMAuvRTuSlZsCe2k0uWJ8ycmge
+wKhcdDLwVwCz2aMAGbwr/nZyzuIx/pkI3PWMw5Xk9PLUryaYHRSSFGm7mYQ6G7HE
+7yn0ZIy7nQxrsVYOV3MMyEJNRBld+MgnGfDMf5gZIH6k4NbRNKABtiCKmvD3nk4h
+3A863HaHH6wa8yKHXfyN71m/QwzaH3AbaORqKhfHl2uysQ==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/rubygems/wrong_key_cert_32.pem b/jni/ruby/test/rubygems/wrong_key_cert_32.pem
new file mode 100644
index 0000000..49ecb15
--- /dev/null
+++ b/jni/ruby/test/rubygems/wrong_key_cert_32.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCAdSgAwIBAgIBEzANBgkqhkiG9w0BAQUFADAqMQ8wDQYDVQQDDAZub2Jv
+ZHkxFzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTEyMTIwODAwMDAwMFoXDTM4
+MDExOTAzMTQwN1owKjEPMA0GA1UEAwwGbm9ib2R5MRcwFQYKCZImiZPyLGQBGRYH
+ZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2UIqQWtcR9
+zPTqJ5K1GIRmXl/VBwHCYXuoyW1K/7h15IdU7thEVjZ25ZvObqagX48mRJynq+7g
+XMMJJL5orFYAvLgRAsprAcbhCMNK34imBJy7tAekolhWM93WD0vdmwSu1ZLNv5Dc
+l1JiyxqGd4nditHiuQvwCdI/qfODqj51YcpbI62Zl3yGa4e5ZB4YAiK8z3n4xN6T
+paIv7c+n4dpLW9BWCkDH8Hk9wWtkzHn5RtdzxDlvNAA+cNEx8Q7rRhtMzzQg8gxK
+E+1FNWtwd0TB2LlMRJNRlBGFqvS+mukjKWSElk0qQqdFsNUAeeTzjYggp/qOr3CM
+uOoyO9XQEwECAwEAAaMdMBswGQYDVR0RBBIwEIEObm9ib2R5QGV4YW1wbGUwDQYJ
+KoZIhvcNAQEFBQADggEBAArsJU2zZfkHI90XNz69wSIrPMytM2Le0uFTXU4GUBix
+8G4451leIwRD8fIMBlc82iE39PjsF8w4svCMqyVHzv10xEjIZq5917hbvxupKKM/
+UX6u3TWEpF9j5s+JtXT+yfyG1TyWXp+Dqlx455I9oRyUJkRe7UeqUqqiResJBTV/
+aImNyeG33MFKilQEyxz0UVZBjSy/JZlTUwAV3dqvh1tRSXfMmk2hxbkiewYIJ48f
+Vu5uEnW1m866WLUIo0dz/KnGZ9aIq2XM+OhRl94MOpZ5tgA8e2R9RFDfzthbCnfU
+G+cnGdtRxnyo0/+SmC7WhSpfSZ7wju9Z04zGa6VsMjI=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/test/runner.rb b/jni/ruby/test/runner.rb
new file mode 100644
index 0000000..11db537
--- /dev/null
+++ b/jni/ruby/test/runner.rb
@@ -0,0 +1,45 @@
+require 'rbconfig'
+
+src_testdir = File.dirname(File.realpath(__FILE__))
+$LOAD_PATH << src_testdir
+$LOAD_PATH.unshift "#{src_testdir}/lib"
+
+require 'test/unit'
+
+module Gem
+end
+class Gem::TestCase < MiniTest::Unit::TestCase
+ @@project_dir = File.dirname($LOAD_PATH.last)
+end
+
+ENV["GEM_SKIP"] = ENV["GEM_HOME"] = ENV["GEM_PATH"] = "".freeze
+
+require_relative 'lib/profile_test_all' if ENV.has_key?('RUBY_TEST_ALL_PROFILE')
+require_relative 'lib/tracepointchecker'
+
+module Test::Unit
+ module ZombieHunter
+ def after_teardown
+ super
+ assert_empty(Process.waitall)
+ end
+ end
+
+ class TestCase
+ include ZombieHunter
+ end
+end
+
+if ENV['COVERAGE']
+ $LOAD_PATH.unshift "#{src_testdir}/../coverage/simplecov/lib"
+ require 'simplecov'
+ SimpleCov.start
+end
+
+begin
+ exit Test::Unit::AutoRunner.run(true, src_testdir)
+rescue NoMemoryError
+ system("cat /proc/meminfo") if File.exist?("/proc/meminfo")
+ system("ps x -opid,args,%cpu,%mem,nlwp,rss,vsz,wchan,stat,start,time,etime,blocked,caught,ignored,pending,f") if File.exist?("/bin/ps")
+ raise
+end
diff --git a/jni/ruby/test/scanf/data.txt b/jni/ruby/test/scanf/data.txt
new file mode 100644
index 0000000..302cfd0
--- /dev/null
+++ b/jni/ruby/test/scanf/data.txt
@@ -0,0 +1,6 @@
+this is 33 a fun
+little input file
+
+with
+
+characters
diff --git a/jni/ruby/test/scanf/test_scanf.rb b/jni/ruby/test/scanf/test_scanf.rb
new file mode 100644
index 0000000..2092626
--- /dev/null
+++ b/jni/ruby/test/scanf/test_scanf.rb
@@ -0,0 +1,303 @@
+# $Id: test_scanf.rb 45194 2014-02-27 08:19:48Z nobu $
+#
+# scanf for Ruby
+#
+# Unit tests
+#
+
+require 'scanf.rb'
+require 'test/unit'
+require 'tempfile'
+
+# Comment out either of these lines to skip those tests.
+
+class TestStringScanf < Test::Unit::TestCase;end
+class TestIOScanf < Test::Unit::TestCase;end
+
+module ScanfTests
+
+ def tests
+ [
+
+# Scratchpad
+ [ "%2[a]", "nbc", []],
+ [ "%*d %*3d %*s", "123 +456 abc", [] ],
+ [ "%d%c", "123 x", [ 123, " " ] ],
+ [ "%d%c", "123x", [ 123, "x" ] ],
+ [ "%d %c", "123x", [ 123, "x" ] ],
+ [ "%d %c", "123 x", [ 123, "x" ] ],
+
+# Testing failures
+ [ "%x", "x", [] ],
+ [ "%2x", "x", [] ],
+ [ "%i", "x", [] ],
+# ]; end; def nothing; [
+ [ "%2i", "x", [] ],
+ [ "%2o", "x", [] ],
+ [ "%d", "x", [] ],
+ [ "%2d", "x", [] ],
+ [ "%3d", "+x3", [] ],
+ [ "%d%[abc]", "eabc", [] ],
+ [ "%d\n%[abc]", "\neabc", [] ],
+ [ "%d%[^abc]", "ghiabc", [ ] ],
+ [ "%d%[abc]", "abc", [] ],
+ [ "%d%s", "", [] ],
+ [ "%d%s", "blah 123 string", [] ],
+ [ "%[\n]", "abc\n", [] ],
+ [ "%f", "x", [] ],
+ [ "%f", "z", [] ],
+ [ "%f", "z3.2534", [] ],
+ [ "", "", [] ],
+ [ "", "abc 123", [] ],
+ [ '%[^\\w]%c', "a...1", [] ],
+
+# Testing 'x'
+ [ "%3x", "0xz", [0] ],
+
+# Testing 'i'
+ [ "%3i", "097", [0] ],
+ [ "%3i", "0xz", [0] ],
+ [ "%1i", "3", [ 3 ] ],
+ [ "%2i", "07", [ 7 ] ],
+ [ "%2i", "0a", [ 0 ] ],
+
+# Testing 'c'
+ [ "%3c", "abc", [ "abc" ] ],
+ [ "%3c", "a\nb", [ "a\nb" ] ],
+ [ "%3c", "a\nbcd", [ "a\nb" ] ],
+ [ "%c\n\n", "x\n\n", [ "x" ] ],
+ [ "%c", "\n", [ "\n" ] ],
+ [ "%c", "x\n", [ "x" ] ],
+ [ "%2c", " 123", [" 1"] ],
+ [ " %c", " x", ["x"] ],
+ [ "%c", " x", [" "] ],
+ [ "%c", "123", ["1"] ],
+ [ "%2c", "123", ["12"] ],
+ [ "%5c", "a\nb\n\n", [ "a\nb\n\n" ] ],
+ [ "%6c", "a\nb\n\nx", [ "a\nb\n\nx" ] ],
+ [ "%5c", "ab\ncd", [ "ab\ncd" ] ],
+
+# Testing 'o'
+ [ "%3o", "0xz", [0] ],
+
+# Testing 'd'
+ [ "%d", "\n123", [ 123 ] ],
+ [ "%d", "\n\n123", [ 123 ] ],
+ [ "%1d", "2", [2] ],
+
+# Mixed tests
+# Includes:
+# whitespace/newline
+# mixed integer bases
+# various mixed specifiers
+
+ [ "%[^\\w]%c", "...1", [ "...", "1"] ],
+ [ "%[abc\n]%d", "a\n\nb\n\nc 123", [ "a\n\nb\n\nc", 123 ] ],
+ [ "%[abc\n]%d", "a\n\nb\n\nc \t 123", [ "a\n\nb\n\nc", 123 ] ],
+ [ "%[abc\t]%d", "a\t\tb\t\tc 123", [ "a\t\tb\t\tc", 123 ] ],
+ [ "%d%3[abc\n]", "123a\nbeaab", [ 123, "a\nb" ] ],
+ [ "%d%20c", "42 is the key", [ 42, " is the key" ] ],
+ [ "%d %20c", "42 is the key", [ 42, "is the key" ] ],
+ [ "%d%3[^abc\n]%d", "123de\nf123", [ 123, "de" ] ],
+ [ "%d %4c", "3abc", [ 3, "abc" ] ],
+ [ "%f%d\n%[abc]", "1\neabc", [1.0] ],
+ [ "%d%3[abc]", "123aaab", [ 123, "aaa" ] ],
+ [ "%d%3[abc]", "123 aaab", [ 123 ] ],
+ [ "%d%3[abc]", "123aeaab", [ 123, "a" ] ],
+ [ "%d%[^abc]", "123defabc", [123, "def" ] ],
+ [ "%d%3[^abc]", "123defdef", [ 123, "def" ] ],
+ [ "%d%3[^abc] ", "123defdef ", [ 123, "def" ] ],
+ [ "%d%3[^abc]ghi", "123defghi", [ 123, "def" ] ],
+ [ "%d%3[^abc]", "123adefdef", [ 123 ] ],
+ [ "%d%3[^abc]", "123deafdef", [ 123, "de" ] ],
+ [ "%d%3[^abc\n]", "123de\nf", [ 123, "de" ] ],
+ [ "%s%c%c%s", "abc\n\ndef", ["abc", "\n","\n", "def" ] ],
+ [ "%c%d", "\n\n123", [ "\n",123 ] ],
+ [ "%s%c%d", "abc\n123", [ "abc", "\n", 123 ] ],
+ [ "%s%c%d", "abc\n\n123", [ "abc", "\n", 123 ] ],
+ [ "%c%d", "\t\n123", [ "\t",123 ] ],
+ [ "%s%c%d", "abc\t\n123", [ "abc", "\t", 123 ] ],
+ [ "%3c%d", "abc123", [ "abc", 123 ] ],
+ [ "%3c\n%d", "abc123", [ "abc", 123 ] ],
+ [ "%3c\n%d", "abc 123", [ "abc", 123 ] ],
+ [ "%3c %d", "abc123", [ "abc", 123 ] ],
+ [ "%3c\t%d", "abc \n 123", [ "abc", 123 ] ],
+ [ "%3c\t%d", "abc \n 123 ", [ "abc", 123 ] ],
+ [ "%3c%d", "a\nb123", [ "a\nb", 123 ] ],
+ [ "%f%3c", "1.2x\ny", [ 1.2, "x\ny"] ],
+ [ "%d\n%d\n%d", "123 456 789", [ 123,456,789 ] ],
+ [ "%d\n%i%2d%x\n%d", "123 0718932", [ 123, 071, 89, 0x32] ],
+ [ "%c\n%c", "x y", [ "x", "y" ] ],
+ [ "%c\t%c", "x y", [ "x", "y" ] ],
+ [ "%s\n%s", "x y", [ "x", "y" ] ],
+ [ "%s%s\n", "x y", [ "x", "y" ] ],
+ [ "%c\n\n%c", "x\n\ny", [ "x", "y" ] ],
+ [ "%s%d%d", "abc\n123\n456", [ "abc", 123, 456 ] ],
+ [ "%3s%c%3c%d", "1.2x\n\ny123", [ "1.2", "x", "\n\ny", 123 ] ],
+ [ "%c\n%c", "x\n\ny", [ "x", "y" ] ],
+ [ "%c %c", "x\n\ny", [ "x", "y" ] ],
+ [ "%s\n\n%c", "x\n\ny", [ "x", "y" ] ],
+ [ "%s\n\n%s", "x\n\ny", [ "x", "y" ] ],
+ [ "%d\n\n%d", "23\n\n45", [ 23, 45 ] ],
+ [ "%d\n%d", "23\n\n45", [ 23, 45 ] ],
+ [ "%c\n\n%c", "x y", [ "x", "y" ] ],
+ [ "%c%c", "x\n\ny", [ "x", "\n" ] ],
+ [ "%c%c", "x\n", [ "x", "\n" ] ],
+ [ "%d%c%c%d", "345 678", [ 345, " ", " ", 678] ],
+ [ "%d %c%s", "123 x hello", [123, "x", "hello"] ],
+ [ "%d%2c", "654 123", [654," 1"] ],
+ [ "%5c%s", "a\nb\n\nxyz", [ "a\nb\n\n","xyz" ] ],
+ [ "%s%[ xyz]%d", "hello x 32", ["hello", " x ", 32] ],
+ [ "%5s%8[a-z]%d", "helloblahblah 32", ["hello", "blahblah", 32] ],
+ [ '%s%[abcde\\s]%d', "hello badea 32", ["hello", " badea ", 32] ],
+ [ '%d%[\\s]%c', "123 \n\t X", [ 123," \n\t ", "X"] ],
+ [ "%4s%2c%c", "1.2x\n\ny", [ "1.2x", "\n\n","y"] ],
+ [ "%f%c %3c%d", "1.2x\n\ny123", [ 1.2, "x", "y12", 3 ] ],
+ [ "%s%5c", "abc ab\ncd", [ "abc", " ab\nc" ] ],
+ [ "%5c%f", "ab\ncd1.2", [ "ab\ncd",1.2 ] ],
+ [ "%5c%c", "ab\ncd1", [ "ab\ncd","1" ] ],
+ [ "%f%c%2c%d", "1.2x\ny123", [ 1.2, "x", "\ny", 123 ] ],
+ [ "%f%c%3c", "1.2x\ny123", [ 1.2, "x", "\ny1"] ],
+ [ "%s\n%s", "blah\n\nand\nmore stuff", [ "blah", "and" ] ],
+ [ "%o%d%x", "21912a3", [ "21".oct, 912, "a3".hex ] ],
+ [ "%3o%4d%3x", "21912a3", [ "21".oct, 912, "a3".hex ] ],
+ [ "%3o%4d%5x", "2191240xa3", [ "21".oct, 9124, "a3".hex ] ],
+ [ "%3d%3x", "12abc", [12, "abc".hex] ],
+ [ "%s%i%d", "hello +0xdef 123", [ "hello", "def".hex, 123] ],
+ [ "%s%i%d", "hello -0xdef 123", [ "hello", -"def".hex, 123] ],
+ [ "%s%i%i%i%i", "hello 012 -012 100 1", [ "hello", 10, -10, 100, 1 ] ],
+ [ "%s%i%i%i%i", "hello 012 0x12 100 1", [ "hello", 10, 18, 100, 1 ] ],
+ [ "%s%5i%3i%4i", "hello 0x123 123 0123", [ "hello", "0x123".hex, 123,"0123".oct] ],
+ [ "%s%3i%4i", "hello 1230123", [ "hello", 123,"0123".oct] ],
+ [ "%s%3i", "hello 1230", [ "hello", 123] ],
+ [ "%s%5x%d", "hello 0xdef 123", [ "hello", "def".hex, 123] ],
+ [ "%s%6x%d", "hello +0xdef 123", [ "hello", "def".hex, 123] ],
+ [ "%s%6x%d", "hello -0xdef 123", [ "hello", -"def".hex, 123] ],
+ [ "%s%4x%d", "hello -def 123", [ "hello", -"def".hex, 123] ],
+ [ "%s%3x%d", "hello def 123", [ "hello", "def".hex, 123] ],
+ [ "%s%x%d", "hello -def 123", [ "hello", -"def".hex, 123] ],
+ [ "%s%x%d", "hello -0xdef 123", [ "hello", -"def".hex, 123] ],
+ [ "%s%x%d", "hello 0xdef 123", [ "hello", "def".hex, 123] ],
+ [ "%s%d%x%s", "hello 123 abc def", [ "hello", 123, "abc".hex, "def"] ],
+ [ "%s%d%o%d", "hello 012 012 100", [ "hello", 12, 10, 100 ] ],
+ [ "%s%d%o%d", "hello 012 -012 100", [ "hello", 12, -10, 100 ] ],
+ [ "%s%o%x%d", "hello 012 0x12 100", [ "hello", 10, 18, 100 ] ],
+ [ "%s%d%o%d", "hello 012 +01288", [ "hello", 12, 10, 88 ] ],
+ [ "%f %d %s", "12.3e23 45 string", ["12.3e23".to_f, 45, "string"] ],
+ [ "%f %d %s", "12.3e+23 45 string", ["12.3e23".to_f, 45, "string"] ],
+ [ "%f %d %s", "12.3e-23 45 string", ["12.3e-23".to_f, 45, "string"] ],
+ [ "%f %d %s", "-12.3e-23 45 string", ["-12.3e-23".to_f, 45, "string"] ],
+ [ "%f %d %s", "12.e23 45 string", ["12.e23".to_f, 45, "string"] ],
+ [ "%5f %d %s", "1.2e23 string", ["1.2e2".to_f, 3, "string"] ],
+ [ "%5f%d %s", "1.2e23 string", ["1.2e2".to_f, 3, "string"] ],
+ [ "%5f%d %d %s", "1.2e23 45 string", ["1.2e2".to_f, 3, 45, "string"] ],
+ [ "%6f %d %d %s", "+1.2e23 45 string", ["1.2e2".to_f, 3, 45, "string"] ],
+ [ "%d %d", "123 \n 345", [123, 345] ],
+ [ "%d %*d", "123 \n 345", [123] ],
+ [ "%d %3d789", "123 +45789", [123, 45] ],
+ [ "%d %3d%d", "123 +456789", [123, 45, 6789] ],
+ [ "%d %3dabc", "123 456abc", [123, 456] ],
+ [ "%d %s", "123abc", [123, "abc"] ],
+ [ "%d%s %s", "123 abc def", [123, "abc", "def"] ],
+ [ "%s%s", "abc123 def", ["abc123", "def"] ],
+ [ "%s%s %s", "123 abc def", ["123", "abc", "def"] ],
+ [ "%s%%%s", "abc % def", ["abc", "def"] ],
+ [ "%d %3d %s", "+123 456abc", [123, 456, "abc"] ],
+ [ "%d %3d %s", "123 456abc", [123, 456, "abc"] ],
+ [ "%d %3d %s", "123 +456 abc", [123, 45, "6"] ],
+ [ "%d %3d %s", "-123-456abc", [-123, -45, "6abc"] ],
+ [ "%dabc%d", "123abc345", [123, 345] ],
+ [ "%d%5s%d", "123 abcde12", [123, "abcde", 12] ],
+ [ "%5d%5s%5d", "12345abcde67890", [12345, "abcde", 67890] ],
+ [ "%5d%*5s%5d", "12345abcde67890", [12345, 67890] ],
+ [ " 12345%5s%5d", "12345abcde67890", [ "abcde", 67890] ],
+ [ "%5dabcde%5d", "12345abcde67890", [ 12345, 67890] ],
+ [ "%s%%%*s", "abc % def", ["abc"] ],
+ [ "%*6s %d", "string 123", [123] ],
+ [ "%d %*3d %s", "-123-456abc", [-123, "6abc"] ],
+ [ "%d%s", "123", [123] ],
+ [ "%s%d", "abc", ["abc"] ],
+ [ "%f%x", "3.2e45x", ["3.2e45x".to_f] ],
+ [ "%*5f%d %d %s", "1.2e23 45 string", [3, 45, "string"] ],
+ [ "%5f%*d %d %s", "1.2e23 45 string", ["1.2e2".to_f, 45, "string"] ],
+ [ "%*5f%*d %*d %s", "1.2e23 45 string", ["string"] ],
+ [ "%f %*d %s", "12.e23 45 string", ["12.e23".to_f, "string"] ],
+ [ "%s %f %s %d %x%c%c%c%c",
+ "float: 1.2e23 dec/hex: 135a23 abc",
+ ["float:", "1.2e23".to_f, "dec/hex:", 135, "a23".hex, " ", "a", "b", "c" ] ],
+
+# Testing 's'
+ [ "%s\n", "blah\n\n\n", [ "blah" ] ],
+
+# Testing '['
+ [ "%[a\nb]", "a\nb", [ "a\nb" ] ],
+ [ "%[abc]", "acb", [ "acb" ] ],
+ [ "%[abc\n]", "a\nb", [ "a\nb" ] ],
+ [ "%[^abc]", "defabc", [ "def" ] ],
+ [ "%[-abc]", "abc-cba", [ "abc-cba" ] ],
+ [ "%[\n]", "\n", [ "\n" ] ],
+ [ "%[\n]", "\nabc", [ "\n" ] ],
+ [ "%[\n\t]", "\t\n", [ "\t\n" ] ],
+ [ "%[a-f]", "abczef", [ "abc" ] ],
+ [ "%d%3[[:lower:]] %f", "123ade1.2", [ 123,"ade",1.2 ] ],
+ [ "%d%3[[:lower:]] %f", "123ad1.2", [ 123,"ad",1.2 ] ],
+ [ "%d%3[[:lower:]] %f", "123 ad1.2", [ 123 ] ],
+ [ "%d%[[:lower:]]", "123abcdef1.2", [ 123, "abcdef" ] ],
+ [ "%[[:lower:]]%d", "abcdef123", [ "abcdef", 123 ] ],
+ [ "%[[:digit:]]%[[:alpha:]]", "123abcdef", [ "123", "abcdef" ] ],
+ [ "%[[:digit:]]%d", "123 123", [ "123", 123 ] ],
+ [ "%[[:upper:]]", "ABCdefGHI", [ "ABC" ] ],
+
+# Testing 'f'
+ [ "%2f", "x", [] ],
+ [ "%F", "1.23e45", [1.23e+45] ],
+ [ "%e", "3.25ee", [3.25] ],
+ [ "%E", "3..25", [3.0] ],
+ [ "%g", "+3.25", [3.25] ],
+ [ "%G", "+3.25e2", [325.0] ],
+ [ "%f", "3.z", [3.0] ],
+ [ "%a", "0X1P+10", [1024.0] ],
+ [ "%A", "0x1.deadbeefp+99", [1.1851510441583988e+30] ],
+
+# Testing embedded matches including literal '[' behavior
+ [",%d,%f", ",10,1.1", [10,1.1] ],
+ [" ,%d,%f", " ,10,1.1", [10,1.1] ],
+ ["[%d,%f", "[10,1.1", [10,1.1] ],
+ [" [%d,%f", " [10,1.1", [10,1.1] ],
+
+ ]
+ end
+
+ def each_test
+ self.tests.each do |test|
+ format, string, = test
+ yield test, "#{string.dump}(#{format.dump})"
+ end
+ end
+end
+
+class TestStringScanf
+ include Scanf
+ extend ScanfTests
+
+ self.each_test do |test, i|
+ define_method("test_#{i}") do ||
+ assert_equal(test[2], test[1].scanf(test[0]))
+ end
+ end
+end
+
+class TestIOScanf
+ include Scanf
+ extend ScanfTests
+
+ self.each_test do |test, i|
+ define_method("test_#{i}") do ||
+ Tempfile.create("iotest.dat") do |fh|
+ fh.print test[1]
+ fh.rewind
+ assert_equal(test[2], fh.scanf(test[0]))
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/scanf/test_scanfblocks.rb b/jni/ruby/test/scanf/test_scanfblocks.rb
new file mode 100644
index 0000000..ff68a83
--- /dev/null
+++ b/jni/ruby/test/scanf/test_scanfblocks.rb
@@ -0,0 +1,81 @@
+# $Id: test_scanfblocks.rb 25189 2009-10-02 12:04:37Z akr $
+#
+# scanf for Ruby
+#
+# Some not very comprehensive tests of block behavior.
+
+
+require 'test/unit'
+require 'scanf'
+require 'tmpdir'
+
+class TestScanfBlock < Test::Unit::TestCase
+
+ def setup
+ @str = <<-EOS
+ Beethoven 1770
+ Bach 1685
+ Handel 1685
+ Scarlatti 1685
+ Brahms 1833
+ EOS
+ end
+
+alias set_up setup
+ def test_str1
+ res = @str.scanf("%s%d") { |name, year| "#{name} was born in #{year}." }
+ assert_equal(res,
+ [ "Beethoven was born in 1770.",
+ "Bach was born in 1685.",
+ "Handel was born in 1685.",
+ "Scarlatti was born in 1685.",
+ "Brahms was born in 1833." ])
+ end
+
+ def test_str2
+ names = @str.scanf("%s%d") { |name, year| name.upcase }
+ assert_equal(names, ["BEETHOVEN", "BACH", "HANDEL", "SCARLATTI", "BRAHMS"])
+ end
+
+ def test_str3
+ assert_equal("".scanf("%d%f%s") {}, [])
+ end
+
+ def test_str4
+ assert_equal("abc".scanf("%d%f%s") {}, [])
+ end
+
+ def test_str5
+ assert_equal("abc".scanf("") {}, [])
+ end
+
+ def test_io1
+ fn = "#{Dir.tmpdir}/iotest.dat.#{$$}"
+ File.open(fn, "w") { |fh| fh.puts(@str) }
+ fh = File.open(fn, "rb")
+ res = fh.scanf("%s%d") { |name, year| "#{name} was born in #{year}." }
+
+ assert_equal(
+ [ "Beethoven was born in 1770.",
+ "Bach was born in 1685.",
+ "Handel was born in 1685.",
+ "Scarlatti was born in 1685.",
+ "Brahms was born in 1833." ],res)
+ fh.close
+ ensure
+ File.delete(fn)
+ end
+
+ def test_io2
+ fn = "#{Dir.tmpdir}/iotest.dat.#{$$}"
+ File.open(fn, "w").close
+ fh = File.open(fn,"rb")
+ assert_equal(fh.scanf("") {}, [])
+ fh.seek(0)
+ assert_equal(fh.scanf("%d%f%s") {}, [])
+ fh.close
+ ensure
+ File.delete(fn)
+ end
+
+end
diff --git a/jni/ruby/test/scanf/test_scanfio.rb b/jni/ruby/test/scanf/test_scanfio.rb
new file mode 100644
index 0000000..6b54210
--- /dev/null
+++ b/jni/ruby/test/scanf/test_scanfio.rb
@@ -0,0 +1,20 @@
+# $Id: test_scanfio.rb 25426 2009-10-21 03:44:56Z nobu $
+#
+# scanf for Ruby
+#
+# Ad hoc tests of IO#scanf (needs to be expanded)
+
+
+require "scanf"
+
+class TestScanfIO < Test::Unit::TestCase
+ def test_io
+ fh = File.new(File.join(File.dirname(__FILE__), "data.txt"), "r")
+ assert_equal(0, fh.pos)
+ assert_equal(["this", "is"], fh.scanf("%s%s"))
+ assert_equal([33, "little"], fh.scanf("%da fun%s"))
+ ensure
+ fh.close
+ end
+end
+
diff --git a/jni/ruby/test/sdbm/test_sdbm.rb b/jni/ruby/test/sdbm/test_sdbm.rb
new file mode 100644
index 0000000..f415584
--- /dev/null
+++ b/jni/ruby/test/sdbm/test_sdbm.rb
@@ -0,0 +1,541 @@
+require 'test/unit'
+require 'tmpdir'
+
+begin
+ require 'sdbm'
+rescue LoadError
+end
+
+class TestSDBM < Test::Unit::TestCase
+ def setup
+ @tmpdir = Dir.mktmpdir("tmptest_sdbm")
+ @prefix = "tmptest_sdbm_#{$$}"
+ @path = "#{@tmpdir}/#{@prefix}_"
+ assert_instance_of(SDBM, @sdbm = SDBM.new(@path))
+ end
+ def teardown
+ assert_nil(@sdbm.close)
+ ObjectSpace.each_object(SDBM) do |obj|
+ obj.close unless obj.closed?
+ end
+ FileUtils.remove_entry_secure @tmpdir
+ end
+
+ def check_size(expect, sdbm=@sdbm)
+ assert_equal(expect, sdbm.size)
+ n = 0
+ sdbm.each { n+=1 }
+ assert_equal(expect, n)
+ if expect == 0
+ assert_equal(true, sdbm.empty?)
+ else
+ assert_equal(false, sdbm.empty?)
+ end
+ end
+
+ def test_version
+ assert(! SDBM.const_defined?(:VERSION))
+ end
+
+ def test_s_new_has_no_block
+ # SDBM.new ignore the block
+ foo = true
+ assert_instance_of(SDBM, sdbm = SDBM.new("#{@tmpdir}/#{@prefix}") { foo = false })
+ assert_equal(foo, true)
+ assert_nil(sdbm.close)
+ end
+ def test_s_open_no_create
+ assert_nil(sdbm = SDBM.open("#{@tmpdir}/#{@prefix}", nil))
+ ensure
+ sdbm.close if sdbm
+ end
+ def test_s_open_with_block
+ assert_equal(SDBM.open("#{@tmpdir}/#{@prefix}") { :foo }, :foo)
+ end
+=begin
+ # Is it guaranteed on many OS?
+ def test_s_open_lock_one_process
+ # locking on one process
+ assert_instance_of(SDBM, sdbm = SDBM.open("#{@tmpdir}/#{@prefix}", 0644))
+ assert_raise(Errno::EWOULDBLOCK) {
+ begin
+ SDBM.open("#{@tmpdir}/#{@prefix}", 0644)
+ rescue Errno::EAGAIN
+ raise Errno::EWOULDBLOCK
+ end
+ }
+ end
+=end
+
+ def open_db_child(dbname, *opts)
+ opts = [0644, *opts].map(&:inspect).join(', ')
+ args = [EnvUtil.rubybin, "-rsdbm", <<-SRC, dbname]
+ STDOUT.sync = true
+ gdbm = SDBM.open(ARGV.shift, #{opts})
+ puts sdbm.class
+ gets
+ SRC
+ IO.popen(args, "r+") do |f|
+ dbclass = f.gets
+ assert_equal("SDBM", dbclass.chomp)
+ yield
+ end
+ end
+
+ def test_s_open_nolock
+ dbname = "#{@tmpdir}/#{@prefix}"
+
+ open_db_child(dbname, SDBM::NOLOCK) do
+ assert_no_exception(Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EACCES) {
+ SDBM.open(dbname, 0644) {|sdbm|
+ assert_instance_of(SDBM, sdbm)
+ }
+ }
+ end
+
+ p Dir.glob("#{@tmpdir}/#{@prefix}*") if $DEBUG
+
+ open_db_child(dbname) do
+ assert_no_exception(Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EACCES) {
+ # this test is failed on Cygwin98 (???)
+ SDBM.open(dbname, 0644, SDBM::NOLOCK) {|sdbm|
+ assert_instance_of(SDBM, sdbm)
+ }
+ }
+ end
+ end if defined? SDBM::NOLOCK # sdbm 1.8.0 specific
+
+ def test_s_open_error
+ skip "doesn't support to avoid read access by owner on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
+ assert_instance_of(SDBM, sdbm = SDBM.open("#{@tmpdir}/#{@prefix}", 0))
+ assert_raise(Errno::EACCES) {
+ SDBM.open("#{@tmpdir}/#{@prefix}", 0)
+ }
+ sdbm.close
+ end
+
+ def test_close
+ assert_instance_of(SDBM, sdbm = SDBM.open("#{@tmpdir}/#{@prefix}"))
+ assert_nil(sdbm.close)
+
+ # closed SDBM file
+ assert_raise(SDBMError) { sdbm.close }
+ end
+
+ def test_aref
+ assert_equal('bar', @sdbm['foo'] = 'bar')
+ assert_equal('bar', @sdbm['foo'])
+
+ assert_nil(@sdbm['bar'])
+ end
+
+ def test_fetch
+ assert_equal('bar', @sdbm['foo']='bar')
+ assert_equal('bar', @sdbm.fetch('foo'))
+
+ # key not found
+ assert_raise(IndexError) {
+ @sdbm.fetch('bar')
+ }
+
+ # test for `ifnone' arg
+ assert_equal('baz', @sdbm.fetch('bar', 'baz'))
+
+ # test for `ifnone' block
+ assert_equal('foobar', @sdbm.fetch('bar') {|key| 'foo' + key })
+ end
+
+ def test_aset
+ num = 0
+ 2.times {|i|
+ assert_equal('foo', @sdbm['foo'] = 'foo')
+ assert_equal('foo', @sdbm['foo'])
+ assert_equal('bar', @sdbm['foo'] = 'bar')
+ assert_equal('bar', @sdbm['foo'])
+
+ num += 1 if i == 0
+ assert_equal(num, @sdbm.size)
+
+ # assign nil
+ assert_equal('', @sdbm['bar'] = '')
+ assert_equal('', @sdbm['bar'])
+
+ num += 1 if i == 0
+ assert_equal(num, @sdbm.size)
+
+ # empty string
+ assert_equal('', @sdbm[''] = '')
+ assert_equal('', @sdbm[''])
+
+ num += 1 if i == 0
+ assert_equal(num, @sdbm.size)
+
+ # Fixnum
+ assert_equal('200', @sdbm['100'] = '200')
+ assert_equal('200', @sdbm['100'])
+
+ num += 1 if i == 0
+ assert_equal(num, @sdbm.size)
+
+ # Big key and value
+ assert_equal('y' * 100, @sdbm['x' * 100] = 'y' * 100)
+ assert_equal('y' * 100, @sdbm['x' * 100])
+
+ num += 1 if i == 0
+ assert_equal(num, @sdbm.size)
+ }
+ end
+
+ def test_key
+ assert_equal('bar', @sdbm['foo'] = 'bar')
+ assert_equal('foo', @sdbm.key('bar'))
+ assert_nil(@sdbm['bar'])
+ end
+
+ def test_values_at
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ @sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
+ assert_equal(values.reverse, @sdbm.values_at(*keys.reverse))
+ end
+
+ def test_select_with_block
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ @sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
+ ret = @sdbm.select {|k,v|
+ assert_equal(k.upcase, v)
+ k != "bar"
+ }
+ assert_equal([['baz', 'BAZ'], ['foo', 'FOO']],
+ ret.sort)
+ end
+
+ def test_length
+ num = 10
+ assert_equal(0, @sdbm.size)
+ num.times {|i|
+ i = i.to_s
+ @sdbm[i] = i
+ }
+ assert_equal(num, @sdbm.size)
+
+ @sdbm.shift
+
+ assert_equal(num - 1, @sdbm.size)
+ end
+
+ def test_empty?
+ assert_equal(true, @sdbm.empty?)
+ @sdbm['foo'] = 'FOO'
+ assert_equal(false, @sdbm.empty?)
+ end
+
+ def test_each_pair
+ n = 0
+ @sdbm.each_pair { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
+
+ n = 0
+ ret = @sdbm.each_pair {|key, val|
+ assert_not_nil(i = keys.index(key))
+ assert_equal(val, values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@sdbm, ret)
+ end
+
+ def test_each_value
+ n = 0
+ @sdbm.each_value { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
+
+ n = 0
+ ret = @sdbm.each_value {|val|
+ assert_not_nil(key = @sdbm.key(val))
+ assert_not_nil(i = keys.index(key))
+ assert_equal(val, values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@sdbm, ret)
+ end
+
+ def test_each_key
+ n = 0
+ @sdbm.each_key { n += 1 }
+ assert_equal(0, n)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
+
+ n = 0
+ ret = @sdbm.each_key {|key|
+ assert_not_nil(i = keys.index(key))
+ assert_equal(@sdbm[key], values[i])
+
+ n += 1
+ }
+ assert_equal(keys.size, n)
+ assert_equal(@sdbm, ret)
+ end
+
+ def test_keys
+ assert_equal([], @sdbm.keys)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
+
+ assert_equal(keys.sort, @sdbm.keys.sort)
+ assert_equal(values.sort, @sdbm.values.sort)
+ end
+
+ def test_values
+ test_keys
+ end
+
+ def test_shift
+ assert_nil(@sdbm.shift)
+ assert_equal(0, @sdbm.size)
+
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+
+ @sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
+
+ ret_keys = []
+ ret_values = []
+ while ret = @sdbm.shift
+ ret_keys.push ret[0]
+ ret_values.push ret[1]
+
+ assert_equal(keys.size - ret_keys.size, @sdbm.size)
+ end
+
+ assert_equal(keys.sort, ret_keys.sort)
+ assert_equal(values.sort, ret_values.sort)
+ end
+
+ def test_delete
+ keys = %w(foo bar baz)
+ values = %w(FOO BAR BAZ)
+ key = keys[1]
+
+ assert_nil(@sdbm.delete(key))
+ assert_equal(0, @sdbm.size)
+
+ @sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
+
+ assert_equal('BAR', @sdbm.delete(key))
+ assert_nil(@sdbm[key])
+ assert_equal(2, @sdbm.size)
+
+ assert_nil(@sdbm.delete(key))
+ end
+ def test_delete_with_block
+ key = 'no called block'
+ @sdbm[key] = 'foo'
+ assert_equal('foo', @sdbm.delete(key) {|k| k.replace 'called block'; :blockval})
+ assert_equal(0, @sdbm.size)
+
+ key = 'no called block'
+ assert_equal(:blockval, @sdbm.delete(key) {|k| k.replace 'called block'; :blockval})
+ assert_equal(0, @sdbm.size)
+ end
+
+ def test_delete_if
+ v = "0"
+ 100.times {@sdbm[v] = v; v = v.next}
+
+ ret = @sdbm.delete_if {|key, val| key.to_i < 50}
+ assert_equal(@sdbm, ret)
+ check_size(50, @sdbm)
+
+ ret = @sdbm.delete_if {|key, val| key.to_i >= 50}
+ assert_equal(@sdbm, ret)
+ check_size(0, @sdbm)
+
+ # break
+ v = "0"
+ 100.times {@sdbm[v] = v; v = v.next}
+ check_size(100, @sdbm)
+ n = 0;
+ @sdbm.delete_if {|key, val|
+ break if n > 50
+ n+=1
+ true
+ }
+ assert_equal(51, n)
+ check_size(49, @sdbm)
+
+ @sdbm.clear
+
+ # raise
+ v = "0"
+ 100.times {@sdbm[v] = v; v = v.next}
+ check_size(100, @sdbm)
+ n = 0;
+ begin
+ @sdbm.delete_if {|key, val|
+ raise "runtime error" if n > 50
+ n+=1
+ true
+ }
+ rescue RuntimeError
+ end
+ assert_equal(51, n)
+ check_size(49, @sdbm)
+ end
+
+ def test_reject
+ v = "0"
+ 100.times {@sdbm[v] = v; v = v.next}
+
+ hash = @sdbm.reject {|key, val| key.to_i < 50}
+ assert_instance_of(Hash, hash)
+ assert_equal(100, @sdbm.size)
+
+ assert_equal(50, hash.size)
+ hash.each_pair {|key,val|
+ assert_equal(false, key.to_i < 50)
+ assert_equal(key, val)
+ }
+
+ hash = @sdbm.reject {|key, val| key.to_i < 100}
+ assert_instance_of(Hash, hash)
+ assert_equal(true, hash.empty?)
+ end
+
+ def test_clear
+ v = "1"
+ 100.times {v = v.next; @sdbm[v] = v}
+
+ assert_equal(@sdbm, @sdbm.clear)
+
+ # validate SDBM#size
+ i = 0
+ @sdbm.each { i += 1 }
+ assert_equal(@sdbm.size, i)
+ assert_equal(0, i)
+ end
+
+ def test_invert
+ v = "0"
+ 100.times {@sdbm[v] = v; v = v.next}
+
+ hash = @sdbm.invert
+ assert_instance_of(Hash, hash)
+ assert_equal(100, hash.size)
+ hash.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_update
+ hash = {}
+ v = "0"
+ 100.times {v = v.next; hash[v] = v}
+
+ @sdbm["101"] = "101"
+ @sdbm.update hash
+ assert_equal(101, @sdbm.size)
+ @sdbm.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_replace
+ hash = {}
+ v = "0"
+ 100.times {v = v.next; hash[v] = v}
+
+ @sdbm["101"] = "101"
+ @sdbm.replace hash
+ assert_equal(100, @sdbm.size)
+ @sdbm.each_pair {|key, val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_haskey?
+ assert_equal('bar', @sdbm['foo']='bar')
+ assert_equal(true, @sdbm.has_key?('foo'))
+ assert_equal(false, @sdbm.has_key?('bar'))
+ end
+
+ def test_has_value?
+ assert_equal('bar', @sdbm['foo']='bar')
+ assert_equal(true, @sdbm.has_value?('bar'))
+ assert_equal(false, @sdbm.has_value?('foo'))
+ end
+
+ def test_to_a
+ v = "0"
+ 100.times {v = v.next; @sdbm[v] = v}
+
+ ary = @sdbm.to_a
+ assert_instance_of(Array, ary)
+ assert_equal(100, ary.size)
+ ary.each {|key,val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_to_hash
+ v = "0"
+ 100.times {v = v.next; @sdbm[v] = v}
+
+ hash = @sdbm.to_hash
+ assert_instance_of(Hash, hash)
+ assert_equal(100, hash.size)
+ hash.each {|key,val|
+ assert_equal(key.to_i, val.to_i)
+ }
+ end
+
+ def test_closed
+ assert_equal(false, @sdbm.closed?)
+ @sdbm.close
+ assert_equal(true, @sdbm.closed?)
+ @sdbm = SDBM.new(@path)
+ end
+
+ def test_readonly
+ @sdbm["bar"] = "baz"
+ @sdbm.close
+ File.chmod(0444, @path + ".dir")
+ File.chmod(0444, @path + ".pag")
+ @sdbm = SDBM.new(@path)
+ assert_raise(SDBMError) { @sdbm["bar"] = "foo" }
+ assert_raise(SDBMError) { @sdbm.delete("bar") }
+ assert_raise(SDBMError) { @sdbm.delete_if { true } }
+ assert_raise(SDBMError) { @sdbm.clear }
+ assert_nil(@sdbm.store("bar", nil))
+ end
+
+ def test_update2
+ obj = Object.new
+ def obj.each_pair
+ yield []
+ end
+ assert_raise(ArgumentError) { @sdbm.update(obj) }
+ end
+end if defined? SDBM
+
diff --git a/jni/ruby/test/shell/test_command_processor.rb b/jni/ruby/test/shell/test_command_processor.rb
new file mode 100644
index 0000000..66e4244
--- /dev/null
+++ b/jni/ruby/test/shell/test_command_processor.rb
@@ -0,0 +1,68 @@
+require 'shell'
+require 'tmpdir'
+
+class TestShell < Test::Unit::TestCase
+end
+class TestShell::CommandProcessor < Test::Unit::TestCase
+ def setup
+ @tmpdir = Dir.mktmpdir("test_shell")
+ @shell = Shell.new
+ @shell.system_path = [@tmpdir]
+ end
+
+ def teardown
+ Dir.rmdir(@tmpdir)
+ end
+
+ def catch_command_start(tc = Object.new)
+ @shell.process_controller.singleton_class.class_eval do
+ define_method(:add_schedule) {|cmd| throw tc, cmd}
+ end
+ tc
+ end
+
+ def exeext
+ RbConfig::CONFIG["EXECUTABLE_EXTS"][/\S+\z/]
+ end
+
+ def test_system_external
+ name = "foo#{exeext}"
+ path = File.join(@tmpdir, name)
+ open(path, "w", 0755) {}
+
+ cmd = assert_throw(catch_command_start) {@shell.system(name)}
+ assert_equal(path, cmd.command)
+ ensure
+ File.unlink(path)
+ end
+
+ def test_system_not_found
+ bug8918 = '[ruby-core:57235] [Bug #8918]'
+
+ name = "foo"
+ path = File.join(@tmpdir, name)
+ open(path, "w", 0644) {}
+
+ assert_raise(Shell::Error::CommandNotFound, bug8918) {
+ catch(catch_command_start) {@shell.system(name)}
+ }
+ ensure
+ Process.waitall
+ File.unlink(path)
+ end
+
+ def test_system_directory
+ bug8918 = '[ruby-core:57235] [Bug #8918]'
+
+ name = "foo#{exeext}"
+ path = File.join(@tmpdir, name)
+ Dir.mkdir(path)
+
+ assert_raise(Shell::Error::CommandNotFound, bug8918) {
+ catch(catch_command_start) {@shell.system(name)}
+ }
+ ensure
+ Process.waitall
+ Dir.rmdir(path)
+ end
+end
diff --git a/jni/ruby/test/socket/test_addrinfo.rb b/jni/ruby/test/socket/test_addrinfo.rb
new file mode 100644
index 0000000..96e1991
--- /dev/null
+++ b/jni/ruby/test/socket/test_addrinfo.rb
@@ -0,0 +1,651 @@
+begin
+ require "socket"
+rescue LoadError
+end
+
+require "test/unit"
+
+class TestSocketAddrinfo < Test::Unit::TestCase
+ HAS_UNIXSOCKET = defined?(UNIXSocket) && /cygwin/ !~ RUBY_PLATFORM
+
+ def tcp_unspecified_to_loopback(addrinfo)
+ if addrinfo.ipv4? && addrinfo.ip_address == "0.0.0.0"
+ Addrinfo.tcp("127.0.0.1", addrinfo.ip_port)
+ elsif addrinfo.ipv6? && addrinfo.ipv6_unspecified?
+ Addrinfo.tcp("::1", addrinfo.ip_port)
+ elsif addrinfo.ipv6? && (ai = addrinfo.ipv6_to_ipv4) && ai.ip_address == "0.0.0.0"
+ Addrinfo.tcp("127.0.0.1", addrinfo.ip_port)
+ else
+ addrinfo
+ end
+ end
+
+ def test_addrinfo_ip
+ ai = Addrinfo.ip("127.0.0.1")
+ assert_equal([0, "127.0.0.1"], Socket.unpack_sockaddr_in(ai))
+ assert_equal(Socket::AF_INET, ai.afamily)
+ assert_equal(Socket::PF_INET, ai.pfamily)
+ assert_equal(0, ai.socktype)
+ assert_equal(0, ai.protocol)
+ end
+
+ def test_addrinfo_tcp
+ ai = Addrinfo.tcp("127.0.0.1", 80)
+ assert_equal([80, "127.0.0.1"], Socket.unpack_sockaddr_in(ai))
+ assert_equal(Socket::AF_INET, ai.afamily)
+ assert_equal(Socket::PF_INET, ai.pfamily)
+ assert_equal(Socket::SOCK_STREAM, ai.socktype)
+ assert_include([0, Socket::IPPROTO_TCP], ai.protocol)
+ end
+
+ def test_addrinfo_udp
+ ai = Addrinfo.udp("127.0.0.1", 80)
+ assert_equal([80, "127.0.0.1"], Socket.unpack_sockaddr_in(ai))
+ assert_equal(Socket::AF_INET, ai.afamily)
+ assert_equal(Socket::PF_INET, ai.pfamily)
+ assert_equal(Socket::SOCK_DGRAM, ai.socktype)
+ assert_include([0, Socket::IPPROTO_UDP], ai.protocol)
+ end
+
+ def test_addrinfo_ip_unpack
+ ai = Addrinfo.tcp("127.0.0.1", 80)
+ assert_equal(["127.0.0.1", 80], ai.ip_unpack)
+ assert_equal("127.0.0.1", ai.ip_address)
+ assert_equal(80, ai.ip_port)
+ end
+
+ def test_addrinfo_inspect_sockaddr
+ ai = Addrinfo.tcp("127.0.0.1", 80)
+ assert_equal("127.0.0.1:80", ai.inspect_sockaddr)
+ end
+
+ def test_addrinfo_new_inet
+ ai = Addrinfo.new(["AF_INET", 46102, "localhost.localdomain", "127.0.0.2"])
+ assert_equal([46102, "127.0.0.2"], Socket.unpack_sockaddr_in(ai))
+ assert_equal(Socket::AF_INET, ai.afamily)
+ assert_equal(Socket::PF_INET, ai.pfamily)
+ assert_equal(0, ai.socktype)
+ assert_equal(0, ai.protocol)
+ end
+
+ def test_addrinfo_predicates
+ ipv4_ai = Addrinfo.new(Socket.sockaddr_in(80, "192.168.0.1"))
+ assert(ipv4_ai.ip?)
+ assert(ipv4_ai.ipv4?)
+ assert(!ipv4_ai.ipv6?)
+ assert(!ipv4_ai.unix?)
+ end
+
+ def test_ipv4_address_predicates
+ list = [
+ [:ipv4_private?, "10.0.0.0", "10.255.255.255",
+ "172.16.0.0", "172.31.255.255",
+ "192.168.0.0", "192.168.255.255"],
+ [:ipv4_loopback?, "127.0.0.1", "127.0.0.0", "127.255.255.255"],
+ [:ipv4_multicast?, "224.0.0.0", "224.255.255.255"]
+ ]
+ list.each {|meth, *addrs|
+ addrs.each {|addr|
+ assert(Addrinfo.ip(addr).send(meth), "Addrinfo.ip(#{addr.inspect}).#{meth}")
+ list.each {|meth2,|
+ next if meth == meth2
+ assert(!Addrinfo.ip(addr).send(meth2), "!Addrinfo.ip(#{addr.inspect}).#{meth2}")
+ }
+ }
+ }
+ end
+
+ def test_basicsocket_send
+ s1 = Socket.new(:INET, :DGRAM, 0)
+ s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ sa = s1.getsockname
+ ai = Addrinfo.new(sa)
+ s2 = Socket.new(:INET, :DGRAM, 0)
+ s2.send("test-basicsocket-send", 0, ai)
+ assert_equal("test-basicsocket-send", s1.recv(100))
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ end
+
+ def test_udpsocket_send
+ s1 = UDPSocket.new
+ s1.bind("127.0.0.1", 0)
+ ai = Addrinfo.new(s1.getsockname)
+ s2 = UDPSocket.new
+ s2.send("test-udp-send", 0, ai)
+ assert_equal("test-udp-send", s1.recv(100))
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ end
+
+ def test_socket_bind
+ s1 = Socket.new(:INET, :DGRAM, 0)
+ sa = Socket.sockaddr_in(0, "127.0.0.1")
+ ai = Addrinfo.new(sa)
+ s1.bind(ai)
+ s2 = UDPSocket.new
+ s2.send("test-socket-bind", 0, s1.getsockname)
+ assert_equal("test-socket-bind", s1.recv(100))
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ end
+
+ def test_socket_connect
+ s1 = Socket.new(:INET, :STREAM, 0)
+ s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ s1.listen(5)
+ ai = Addrinfo.new(s1.getsockname)
+ s2 = Socket.new(:INET, :STREAM, 0)
+ s2.connect(ai)
+ s3, _ = s1.accept
+ s2.send("test-socket-connect", 0)
+ assert_equal("test-socket-connect", s3.recv(100))
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ s3.close if s3 && !s3.closed?
+ end
+
+ def test_socket_connect_nonblock
+ s1 = Socket.new(:INET, :STREAM, 0)
+ s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ s1.listen(5)
+ ai = Addrinfo.new(s1.getsockname)
+ s2 = Socket.new(:INET, :STREAM, 0)
+ begin
+ s2.connect_nonblock(ai)
+ rescue IO::WaitWritable
+ IO.select(nil, [s2])
+ r = s2.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
+ assert_equal(0, r.int, "NOERROR is expected but #{r.inspect}")
+ begin
+ s2.connect_nonblock(ai)
+ rescue Errno::EISCONN
+ end
+ end
+ s3, _ = s1.accept
+ s2.send("test-socket-connect-nonblock", 0)
+ assert_equal("test-socket-connect-nonblock", s3.recv(100))
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ s3.close if s3 && !s3.closed?
+ end
+
+ def test_socket_getnameinfo
+ ai = Addrinfo.udp("127.0.0.1", 8888)
+ assert_equal(["127.0.0.1", "8888"], Socket.getnameinfo(ai, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV))
+ end
+
+ def test_basicsocket_local_address
+ s1 = Socket.new(:INET, :DGRAM, 0)
+ s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ e = Socket.unpack_sockaddr_in(s1.getsockname)
+ a = Socket.unpack_sockaddr_in(s1.local_address.to_sockaddr)
+ assert_equal(e, a)
+ assert_equal(Socket::AF_INET, s1.local_address.afamily)
+ assert_equal(Socket::PF_INET, s1.local_address.pfamily)
+ assert_equal(Socket::SOCK_DGRAM, s1.local_address.socktype)
+ ensure
+ s1.close if s1 && !s1.closed?
+ end
+
+ def test_basicsocket_remote_address
+ s1 = TCPServer.new("127.0.0.1", 0)
+ s2 = Socket.new(:INET, :STREAM, 0)
+ s2.connect(s1.getsockname)
+ s3, _ = s1.accept
+ e = Socket.unpack_sockaddr_in(s2.getsockname)
+ a = Socket.unpack_sockaddr_in(s3.remote_address.to_sockaddr)
+ assert_equal(e, a)
+ assert_equal(Socket::AF_INET, s3.remote_address.afamily)
+ assert_equal(Socket::PF_INET, s3.remote_address.pfamily)
+ assert_equal(Socket::SOCK_STREAM, s3.remote_address.socktype)
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ s3.close if s3 && !s3.closed?
+ end
+
+ def test_socket_accept
+ serv = Socket.new(:INET, :STREAM, 0)
+ serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ serv.listen(5)
+ c = Socket.new(:INET, :STREAM, 0)
+ c.connect(serv.local_address)
+ ret = serv.accept
+ s, ai = ret
+ assert_kind_of(Array, ret)
+ assert_equal(2, ret.length)
+ assert_kind_of(Addrinfo, ai)
+ e = Socket.unpack_sockaddr_in(c.getsockname)
+ a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
+ assert_equal(e, a)
+ ensure
+ serv.close if serv && !serv.closed?
+ s.close if s && !s.closed?
+ c.close if c && !c.closed?
+ end
+
+ def test_socket_accept_nonblock
+ serv = Socket.new(:INET, :STREAM, 0)
+ serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ serv.listen(5)
+ c = Socket.new(:INET, :STREAM, 0)
+ c.connect(serv.local_address)
+ begin
+ ret = serv.accept_nonblock
+ rescue IO::WaitReadable, Errno::EINTR
+ IO.select([serv])
+ retry
+ end
+ s, ai = ret
+ assert_kind_of(Array, ret)
+ assert_equal(2, ret.length)
+ assert_kind_of(Addrinfo, ai)
+ e = Socket.unpack_sockaddr_in(c.getsockname)
+ a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
+ assert_equal(e, a)
+ ensure
+ serv.close if serv && !serv.closed?
+ s.close if s && !s.closed?
+ c.close if c && !c.closed?
+ end
+
+ def test_socket_sysaccept
+ serv = Socket.new(:INET, :STREAM, 0)
+ serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ serv.listen(5)
+ c = Socket.new(:INET, :STREAM, 0)
+ c.connect(serv.local_address)
+ ret = serv.sysaccept
+ fd, ai = ret
+ s = IO.new(fd)
+ assert_kind_of(Array, ret)
+ assert_equal(2, ret.length)
+ assert_kind_of(Addrinfo, ai)
+ e = Socket.unpack_sockaddr_in(c.getsockname)
+ a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
+ assert_equal(e, a)
+ ensure
+ serv.close if serv && !serv.closed?
+ s.close if s && !s.closed?
+ c.close if c && !c.closed?
+ end
+
+ def test_socket_recvfrom
+ s1 = Socket.new(:INET, :DGRAM, 0)
+ s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ s2 = Socket.new(:INET, :DGRAM, 0)
+ s2.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ s2.send("test-socket-recvfrom", 0, s1.getsockname)
+ data, ai = s1.recvfrom(100)
+ assert_equal("test-socket-recvfrom", data)
+ assert_kind_of(Addrinfo, ai)
+ e = Socket.unpack_sockaddr_in(s2.getsockname)
+ a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
+ assert_equal(e, a)
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ end
+
+ def test_socket_recvfrom_nonblock
+ s1 = Socket.new(:INET, :DGRAM, 0)
+ s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ s2 = Socket.new(:INET, :DGRAM, 0)
+ s2.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ s2.send("test-socket-recvfrom", 0, s1.getsockname)
+ begin
+ data, ai = s1.recvfrom_nonblock(100)
+ rescue IO::WaitReadable
+ IO.select([s1])
+ retry
+ end
+ assert_equal("test-socket-recvfrom", data)
+ assert_kind_of(Addrinfo, ai)
+ e = Socket.unpack_sockaddr_in(s2.getsockname)
+ a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
+ assert_equal(e, a)
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ end
+
+ def test_family_addrinfo
+ ai = Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("127.0.0.1", 80)
+ assert_equal(["127.0.0.1", 80], ai.ip_unpack)
+ assert_equal(Socket::SOCK_STREAM, ai.socktype)
+ return unless Addrinfo.respond_to?(:unix)
+ ai = Addrinfo.unix("/testdir/sock").family_addrinfo("/testdir/sock2")
+ assert_equal("/testdir/sock2", ai.unix_path)
+ assert_equal(Socket::SOCK_STREAM, ai.socktype)
+ assert_raise(SocketError) { Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("::1", 80) }
+ end
+
+ def random_port
+ # IANA suggests dynamic port for 49152 to 65535
+ # http://www.iana.org/assignments/port-numbers
+ 49152 + rand(65535-49152+1)
+ end
+
+ def errors_addrinuse
+ [Errno::EADDRINUSE]
+ end
+
+ def test_connect_from
+ TCPServer.open("0.0.0.0", 0) {|serv|
+ serv_ai = Addrinfo.new(serv.getsockname, :INET, :STREAM)
+ serv_ai = tcp_unspecified_to_loopback(serv_ai)
+ port = random_port
+ begin
+ serv_ai.connect_from("0.0.0.0", port) {|s1|
+ s2 = serv.accept
+ begin
+ assert_equal(port, s2.remote_address.ip_port)
+ ensure
+ s2.close
+ end
+ }
+ rescue *errors_addrinuse
+ # not test failure
+ end
+ }
+
+ TCPServer.open("0.0.0.0", 0) {|serv|
+ serv_ai = Addrinfo.new(serv.getsockname, :INET, :STREAM)
+ serv_ai = tcp_unspecified_to_loopback(serv_ai)
+ port = random_port
+ begin
+ serv_ai.connect_from(Addrinfo.tcp("0.0.0.0", port)) {|s1|
+ s2 = serv.accept
+ begin
+ assert_equal(port, s2.remote_address.ip_port)
+ ensure
+ s2.close
+ end
+ }
+ rescue *errors_addrinuse
+ # not test failure
+ end
+ }
+ end
+
+ def test_connect_to
+ TCPServer.open("0.0.0.0", 0) {|serv|
+ serv_ai = Addrinfo.new(serv.getsockname, :INET, :STREAM)
+ serv_ai = tcp_unspecified_to_loopback(serv_ai)
+ port = random_port
+ client_ai = Addrinfo.tcp("0.0.0.0", port)
+ begin
+ client_ai.connect_to(*serv_ai.ip_unpack) {|s1|
+ s2 = serv.accept
+ begin
+ assert_equal(port, s2.remote_address.ip_port)
+ ensure
+ s2.close
+ end
+ }
+ rescue *errors_addrinuse
+ # not test failure
+ end
+ }
+ end
+
+ def test_connect
+ TCPServer.open("0.0.0.0", 0) {|serv|
+ serv_ai = Addrinfo.new(serv.getsockname, :INET, :STREAM)
+ serv_ai = tcp_unspecified_to_loopback(serv_ai)
+ begin
+ serv_ai.connect {|s1|
+ s2 = serv.accept
+ begin
+ assert_equal(s1.local_address.ip_unpack, s2.remote_address.ip_unpack)
+ assert_equal(s2.local_address.ip_unpack, s1.remote_address.ip_unpack)
+ ensure
+ s2.close
+ end
+ }
+ rescue *errors_addrinuse
+ # not test failure
+ end
+ }
+ end
+
+ def test_bind
+ port = random_port
+ client_ai = Addrinfo.tcp("0.0.0.0", port)
+ begin
+ client_ai.bind {|s|
+ assert_equal(port, s.local_address.ip_port)
+ }
+ rescue *errors_addrinuse
+ # not test failure
+ end
+ end
+
+ def test_listen
+ port = random_port
+ client_ai = Addrinfo.tcp("0.0.0.0", port)
+ begin
+ client_ai.listen {|serv|
+ assert_equal(port, serv.local_address.ip_port)
+ serv_addr, serv_port = serv.local_address.ip_unpack
+ case serv_addr
+ when "0.0.0.0" then serv_addr = "127.0.0.1"
+ end
+ TCPSocket.open(serv_addr, serv_port) {|s1|
+ s2, addr = serv.accept
+ begin
+ assert_equal(s1.local_address.ip_unpack, addr.ip_unpack)
+ ensure
+ s2.close
+ end
+ }
+ }
+ rescue *errors_addrinuse
+ # not test failure
+ end
+ end
+
+ def test_s_foreach
+ Addrinfo.foreach(nil, 80, nil, :STREAM) {|ai|
+ assert_kind_of(Addrinfo, ai)
+ }
+ end
+
+ def test_marshal
+ ai1 = Addrinfo.tcp("127.0.0.1", 80)
+ ai2 = Marshal.load(Marshal.dump(ai1))
+ assert_equal(ai1.afamily, ai2.afamily)
+ assert_equal(ai1.ip_unpack, ai2.ip_unpack)
+ assert_equal(ai1.pfamily, ai2.pfamily)
+ assert_equal(ai1.socktype, ai2.socktype)
+ assert_equal(ai1.protocol, ai2.protocol)
+ assert_equal(ai1.canonname, ai2.canonname)
+ end
+
+ def test_marshal_memory_leak
+ bug11051 = '[ruby-dev:48923] [Bug #11051]'
+ assert_no_memory_leak(%w[-rsocket], <<-PREP, <<-CODE, bug11051, rss: true, limit: 2.0)
+ d = Marshal.dump(Addrinfo.tcp("127.0.0.1", 80))
+ 1000.times {Marshal.load(d)}
+ PREP
+ GC.start
+ 20_000.times {Marshal.load(d)}
+ CODE
+ end
+
+ if Socket.const_defined?("AF_INET6") && Socket::AF_INET6.is_a?(Integer)
+
+ def test_addrinfo_new_inet6
+ ai = Addrinfo.new(["AF_INET6", 42304, "ip6-localhost", "::1"])
+ assert_equal([42304, "::1"], Socket.unpack_sockaddr_in(ai))
+ assert_equal(Socket::AF_INET6, ai.afamily)
+ assert_equal(Socket::PF_INET6, ai.pfamily)
+ assert_equal(0, ai.socktype)
+ assert_equal(0, ai.protocol)
+ end
+
+ def test_addrinfo_ip_unpack_inet6
+ ai = Addrinfo.tcp("::1", 80)
+ assert_equal(["::1", 80], ai.ip_unpack)
+ assert_equal("::1", ai.ip_address)
+ assert_equal(80, ai.ip_port)
+ end
+
+ def test_addrinfo_inspect_sockaddr_inet6
+ ai = Addrinfo.tcp("::1", 80)
+ assert_equal("[::1]:80", ai.inspect_sockaddr)
+ end
+
+ def test_marshal_inet6
+ ai1 = Addrinfo.tcp("::1", 80)
+ ai2 = Marshal.load(Marshal.dump(ai1))
+ assert_equal(ai1.afamily, ai2.afamily)
+ assert_equal(ai1.ip_unpack, ai2.ip_unpack)
+ assert_equal(ai1.pfamily, ai2.pfamily)
+ assert_equal(ai1.socktype, ai2.socktype)
+ assert_equal(ai1.protocol, ai2.protocol)
+ assert_equal(ai1.canonname, ai2.canonname)
+ end
+
+ def ipv6(str)
+ Addrinfo.getaddrinfo(str, nil, :INET6, :DGRAM).fetch(0)
+ end
+
+ def test_ipv6_address_predicates
+ list = [
+ [:ipv6_unspecified?, "::"],
+ [:ipv6_loopback?, "::1"],
+ [:ipv6_v4compat?, "::0.0.0.2", "::255.255.255.255"],
+ [:ipv6_v4mapped?, "::ffff:0.0.0.0", "::ffff:255.255.255.255"],
+ [:ipv6_linklocal?, "fe80::", "febf::"],
+ [:ipv6_sitelocal?, "fec0::", "feef::"],
+ [:ipv6_multicast?, "ff00::", "ffff::"],
+ [:ipv6_unique_local?, "fc00::", "fd00::"],
+ ]
+ mlist = [
+ [:ipv6_mc_nodelocal?, "ff01::", "ff11::"],
+ [:ipv6_mc_linklocal?, "ff02::", "ff12::"],
+ [:ipv6_mc_sitelocal?, "ff05::", "ff15::"],
+ [:ipv6_mc_orglocal?, "ff08::", "ff18::"],
+ [:ipv6_mc_global?, "ff0e::", "ff1e::"]
+ ]
+ list.each {|meth, *addrs|
+ addrs.each {|addr|
+ addr_exp = "Addrinfo.getaddrinfo(#{addr.inspect}, nil, :INET6, :DGRAM).fetch(0)"
+ if meth == :ipv6_v4compat? || meth == :ipv6_v4mapped?
+ # MacOS X returns IPv4 address for ::ffff:1.2.3.4 and ::1.2.3.4.
+ # Solaris returns IPv4 address for ::ffff:1.2.3.4.
+ ai = ipv6(addr)
+ assert(ai.ipv4? || ai.send(meth), "ai=#{addr_exp}; ai.ipv4? || .#{meth}")
+ else
+ assert(ipv6(addr).send(meth), "#{addr_exp}.#{meth}")
+ assert_equal(addr, ipv6(addr).ip_address)
+ end
+ list.each {|meth2,|
+ next if meth == meth2
+ assert(!ipv6(addr).send(meth2), "!#{addr_exp}.#{meth2}")
+ }
+ }
+ }
+ mlist.each {|meth, *addrs|
+ addrs.each {|addr|
+ addr_exp = "Addrinfo.getaddrinfo(#{addr.inspect}, nil, :INET6, :DGRAM).fetch(0)"
+ assert(ipv6(addr).send(meth), "#{addr_exp}.#{meth}")
+ assert(ipv6(addr).ipv6_multicast?, "#{addr_exp}.ipv6_multicast?")
+ mlist.each {|meth2,|
+ next if meth == meth2
+ assert(!ipv6(addr).send(meth2), "!#{addr_exp}.#{meth2}")
+ }
+ list.each {|meth2,|
+ next if :ipv6_multicast? == meth2
+ assert(!ipv6(addr).send(meth2), "!#{addr_exp}.#{meth2}")
+ }
+ }
+ }
+ end
+
+ def test_ipv6_to_ipv4
+ ai = Addrinfo.ip("::192.0.2.3")
+ ai = ai.ipv6_to_ipv4 if !ai.ipv4?
+ assert(ai.ipv4?)
+ assert_equal("192.0.2.3", ai.ip_address)
+
+ ai = Addrinfo.ip("::ffff:192.0.2.3")
+ ai = ai.ipv6_to_ipv4 if !ai.ipv4?
+ assert(ai.ipv4?)
+ assert_equal("192.0.2.3", ai.ip_address)
+
+ assert_nil(Addrinfo.ip("::1").ipv6_to_ipv4)
+ assert_nil(Addrinfo.ip("192.0.2.3").ipv6_to_ipv4)
+ if HAS_UNIXSOCKET
+ assert_nil(Addrinfo.unix("/testdir/sock").ipv6_to_ipv4)
+ end
+ end
+ end
+
+ if HAS_UNIXSOCKET
+
+ def test_addrinfo_unix
+ ai = Addrinfo.unix("/testdir/sock")
+ assert_equal("/testdir/sock", Socket.unpack_sockaddr_un(ai))
+ assert_equal(Socket::AF_UNIX, ai.afamily)
+ assert_equal(Socket::PF_UNIX, ai.pfamily)
+ assert_equal(Socket::SOCK_STREAM, ai.socktype)
+ assert_equal(0, ai.protocol)
+ end
+
+ def test_addrinfo_unix_dgram
+ ai = Addrinfo.unix("/testdir/sock", :DGRAM)
+ assert_equal("/testdir/sock", Socket.unpack_sockaddr_un(ai))
+ assert_equal(Socket::AF_UNIX, ai.afamily)
+ assert_equal(Socket::PF_UNIX, ai.pfamily)
+ assert_equal(Socket::SOCK_DGRAM, ai.socktype)
+ assert_equal(0, ai.protocol)
+ end
+
+ def test_addrinfo_unix_path
+ ai = Addrinfo.unix("/testdir/sock1")
+ assert_equal("/testdir/sock1", ai.unix_path)
+ end
+
+ def test_addrinfo_inspect_sockaddr_unix
+ ai = Addrinfo.unix("/testdir/test_addrinfo_inspect_sockaddr_unix")
+ assert_equal("/testdir/test_addrinfo_inspect_sockaddr_unix", ai.inspect_sockaddr)
+ end
+
+ def test_addrinfo_new_unix
+ ai = Addrinfo.new(["AF_UNIX", "/testdir/sock"])
+ assert_equal("/testdir/sock", Socket.unpack_sockaddr_un(ai))
+ assert_equal(Socket::AF_UNIX, ai.afamily)
+ assert_equal(Socket::PF_UNIX, ai.pfamily)
+ assert_equal(Socket::SOCK_STREAM, ai.socktype) # UNIXSocket/UNIXServer is SOCK_STREAM only.
+ assert_equal(0, ai.protocol)
+ end
+
+ def test_addrinfo_predicates_unix
+ unix_ai = Addrinfo.new(Socket.sockaddr_un("/testdir/sososo"))
+ assert(!unix_ai.ip?)
+ assert(!unix_ai.ipv4?)
+ assert(!unix_ai.ipv6?)
+ assert(unix_ai.unix?)
+ end
+
+ def test_marshal_unix
+ ai1 = Addrinfo.unix("/testdir/sock")
+ ai2 = Marshal.load(Marshal.dump(ai1))
+ assert_equal(ai1.afamily, ai2.afamily)
+ assert_equal(ai1.unix_path, ai2.unix_path)
+ assert_equal(ai1.pfamily, ai2.pfamily)
+ assert_equal(ai1.socktype, ai2.socktype)
+ assert_equal(ai1.protocol, ai2.protocol)
+ assert_equal(ai1.canonname, ai2.canonname)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/socket/test_ancdata.rb b/jni/ruby/test/socket/test_ancdata.rb
new file mode 100644
index 0000000..112b0c9
--- /dev/null
+++ b/jni/ruby/test/socket/test_ancdata.rb
@@ -0,0 +1,66 @@
+require 'test/unit'
+require 'socket'
+
+class TestSocketAncData < Test::Unit::TestCase
+ def test_int
+ ancdata = Socket::AncillaryData.int(0, 0, 0, 123)
+ assert_equal(123, ancdata.int)
+ assert_equal([123].pack("i"), ancdata.data)
+ end
+
+ def test_ip_pktinfo
+ addr = Addrinfo.ip("127.0.0.1")
+ ifindex = 0
+ spec_dst = Addrinfo.ip("127.0.0.2")
+ begin
+ ancdata = Socket::AncillaryData.ip_pktinfo(addr, ifindex, spec_dst)
+ rescue NotImplementedError
+ return
+ end
+ assert_equal(Socket::AF_INET, ancdata.family)
+ assert_equal(Socket::IPPROTO_IP, ancdata.level)
+ assert_equal(Socket::IP_PKTINFO, ancdata.type)
+ assert_equal(addr.ip_address, ancdata.ip_pktinfo[0].ip_address)
+ assert_equal(ifindex, ancdata.ip_pktinfo[1])
+ assert_equal(spec_dst.ip_address, ancdata.ip_pktinfo[2].ip_address)
+ assert(ancdata.cmsg_is?(:IP, :PKTINFO))
+ assert(ancdata.cmsg_is?("IP", "PKTINFO"))
+ assert(ancdata.cmsg_is?(Socket::IPPROTO_IP, Socket::IP_PKTINFO))
+ if defined? Socket::IPV6_PKTINFO
+ assert(!ancdata.cmsg_is?(:IPV6, :PKTINFO))
+ end
+ ancdata2 = Socket::AncillaryData.ip_pktinfo(addr, ifindex)
+ assert_equal(addr.ip_address, ancdata2.ip_pktinfo[2].ip_address)
+ end
+
+ def test_ipv6_pktinfo
+ addr = Addrinfo.ip("::1")
+ ifindex = 0
+ begin
+ ancdata = Socket::AncillaryData.ipv6_pktinfo(addr, ifindex)
+ rescue NotImplementedError
+ return
+ end
+ assert_equal(Socket::AF_INET6, ancdata.family)
+ assert_equal(Socket::IPPROTO_IPV6, ancdata.level)
+ assert_equal(Socket::IPV6_PKTINFO, ancdata.type)
+ assert_equal(addr.ip_address, ancdata.ipv6_pktinfo[0].ip_address)
+ assert_equal(ifindex, ancdata.ipv6_pktinfo[1])
+ assert_equal(addr.ip_address, ancdata.ipv6_pktinfo_addr.ip_address)
+ assert_equal(ifindex, ancdata.ipv6_pktinfo_ifindex)
+ assert(ancdata.cmsg_is?(:IPV6, :PKTINFO))
+ assert(ancdata.cmsg_is?("IPV6", "PKTINFO"))
+ assert(ancdata.cmsg_is?(Socket::IPPROTO_IPV6, Socket::IPV6_PKTINFO))
+ if defined? Socket::IP_PKTINFO
+ assert(!ancdata.cmsg_is?(:IP, :PKTINFO))
+ end
+ end
+
+ if defined?(Socket::SCM_RIGHTS) && defined?(Socket::SCM_TIMESTAMP)
+ def test_unix_rights
+ assert_raise(TypeError) {
+ Socket::AncillaryData.int(:UNIX, :SOL_SOCKET, :TIMESTAMP, 1).unix_rights
+ }
+ end
+ end
+end if defined? Socket::AncillaryData
diff --git a/jni/ruby/test/socket/test_basicsocket.rb b/jni/ruby/test/socket/test_basicsocket.rb
new file mode 100644
index 0000000..59b739e
--- /dev/null
+++ b/jni/ruby/test/socket/test_basicsocket.rb
@@ -0,0 +1,88 @@
+begin
+ require "socket"
+ require "test/unit"
+rescue LoadError
+end
+
+class TestSocket_BasicSocket < Test::Unit::TestCase
+ def inet_stream
+ sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ yield sock
+ ensure
+ assert_raise(IOError) {sock.close}
+ end
+
+ def test_getsockopt
+ inet_stream do |s|
+ n = s.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE)
+ assert_equal([Socket::SOCK_STREAM].pack("i"), n.data)
+
+ n = s.getsockopt("SOL_SOCKET", "SO_TYPE")
+ assert_equal([Socket::SOCK_STREAM].pack("i"), n.data)
+
+ n = s.getsockopt(:SOL_SOCKET, :SO_TYPE)
+ assert_equal([Socket::SOCK_STREAM].pack("i"), n.data)
+
+ n = s.getsockopt(:SOCKET, :TYPE)
+ assert_equal([Socket::SOCK_STREAM].pack("i"), n.data)
+
+ n = s.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
+ assert_equal([0].pack("i"), n.data)
+
+ val = Object.new
+ class << val; self end.send(:define_method, :to_int) {
+ s.close
+ Socket::SO_TYPE
+ }
+ assert_raise(IOError) {
+ n = s.getsockopt(Socket::SOL_SOCKET, val)
+ }
+ end
+ end
+
+ def test_setsockopt
+ s = nil
+ linger = [0, 0].pack("ii")
+
+ val = Object.new
+ class << val; self end.send(:define_method, :to_str) {
+ s.close
+ linger
+ }
+ inet_stream do |sock|
+ s = sock
+ assert_equal(0, s.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, linger))
+
+ assert_raise(IOError, "[ruby-dev:25039]") {
+ s.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, val)
+ }
+ end
+
+ val = Object.new
+ class << val; self end.send(:define_method, :to_int) {
+ s.close
+ Socket::SO_LINGER
+ }
+ inet_stream do |sock|
+ s = sock
+ assert_raise(IOError) {
+ s.setsockopt(Socket::SOL_SOCKET, val, linger)
+ }
+ end
+ end
+
+ def test_listen
+ s = nil
+ log = Object.new
+ class << log; self end.send(:define_method, :to_int) {
+ s.close
+ 2
+ }
+ inet_stream do |sock|
+ s = sock
+ assert_raise(IOError) {
+ s.listen(log)
+ }
+ end
+ end
+end if defined?(BasicSocket)
diff --git a/jni/ruby/test/socket/test_nonblock.rb b/jni/ruby/test/socket/test_nonblock.rb
new file mode 100644
index 0000000..882e438
--- /dev/null
+++ b/jni/ruby/test/socket/test_nonblock.rb
@@ -0,0 +1,297 @@
+begin
+ require "socket"
+rescue LoadError
+end
+
+require "test/unit"
+require "tempfile"
+require "timeout"
+
+class TestSocketNonblock < Test::Unit::TestCase
+ def test_accept_nonblock
+ serv = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ serv.listen(5)
+ assert_raise(IO::WaitReadable) { serv.accept_nonblock }
+ c = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ c.connect(serv.getsockname)
+ begin
+ s, sockaddr = serv.accept_nonblock
+ rescue IO::WaitReadable
+ IO.select [serv]
+ s, sockaddr = serv.accept_nonblock
+ end
+ assert_equal(Socket.unpack_sockaddr_in(c.getsockname), Socket.unpack_sockaddr_in(sockaddr))
+ ensure
+ serv.close if serv
+ c.close if c
+ s.close if s
+ end
+
+ def test_connect_nonblock
+ serv = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ serv.listen(5)
+ c = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ servaddr = serv.getsockname
+ begin
+ c.connect_nonblock(servaddr)
+ rescue IO::WaitWritable
+ IO.select nil, [c]
+ assert_nothing_raised {
+ begin
+ c.connect_nonblock(servaddr)
+ rescue Errno::EISCONN
+ end
+ }
+ end
+ s, sockaddr = serv.accept
+ assert_equal(Socket.unpack_sockaddr_in(c.getsockname), Socket.unpack_sockaddr_in(sockaddr))
+ ensure
+ serv.close if serv
+ c.close if c
+ s.close if s
+ end
+
+ def test_udp_recvfrom_nonblock
+ u1 = UDPSocket.new
+ u2 = UDPSocket.new
+ u1.bind("127.0.0.1", 0)
+ assert_raise(IO::WaitReadable) { u1.recvfrom_nonblock(100) }
+ assert_raise(IO::WaitReadable, Errno::EINVAL) { u2.recvfrom_nonblock(100) }
+ u2.send("aaa", 0, u1.getsockname)
+ IO.select [u1]
+ mesg, inet_addr = u1.recvfrom_nonblock(100)
+ assert_equal(4, inet_addr.length)
+ assert_equal("aaa", mesg)
+ _, port, _, _ = inet_addr
+ u2_port, _ = Socket.unpack_sockaddr_in(u2.getsockname)
+ assert_equal(u2_port, port)
+ assert_raise(IO::WaitReadable) { u1.recvfrom_nonblock(100) }
+ u2.send("", 0, u1.getsockname)
+ assert_nothing_raised("cygwin 1.5.19 has a problem to send an empty UDP packet. [ruby-dev:28915]") {
+ timeout(1) { IO.select [u1] }
+ }
+ mesg, inet_addr = u1.recvfrom_nonblock(100)
+ assert_equal("", mesg)
+ ensure
+ u1.close if u1
+ u2.close if u2
+ end
+
+ def test_udp_recv_nonblock
+ u1 = UDPSocket.new
+ u2 = UDPSocket.new
+ u1.bind("127.0.0.1", 0)
+ assert_raise(IO::WaitReadable) { u1.recv_nonblock(100) }
+ assert_raise(IO::WaitReadable, Errno::EINVAL) { u2.recv_nonblock(100) }
+ u2.send("aaa", 0, u1.getsockname)
+ IO.select [u1]
+ mesg = u1.recv_nonblock(100)
+ assert_equal("aaa", mesg)
+ assert_raise(IO::WaitReadable) { u1.recv_nonblock(100) }
+ u2.send("", 0, u1.getsockname)
+ assert_nothing_raised("cygwin 1.5.19 has a problem to send an empty UDP packet. [ruby-dev:28915]") {
+ timeout(1) { IO.select [u1] }
+ }
+ mesg = u1.recv_nonblock(100)
+ assert_equal("", mesg)
+ ensure
+ u1.close if u1
+ u2.close if u2
+ end
+
+ def test_socket_recvfrom_nonblock
+ s1 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
+ s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ s2 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
+ assert_raise(IO::WaitReadable) { s1.recvfrom_nonblock(100) }
+ assert_raise(IO::WaitReadable, Errno::EINVAL) { s2.recvfrom_nonblock(100) }
+ s2.send("aaa", 0, s1.getsockname)
+ IO.select [s1]
+ mesg, sockaddr = s1.recvfrom_nonblock(100)
+ assert_equal("aaa", mesg)
+ port, _ = Socket.unpack_sockaddr_in(sockaddr)
+ s2_port, _ = Socket.unpack_sockaddr_in(s2.getsockname)
+ assert_equal(s2_port, port)
+ ensure
+ s1.close if s1
+ s2.close if s2
+ end
+
+ def tcp_pair
+ serv = TCPServer.new("127.0.0.1", 0)
+ _, port, _, addr = serv.addr
+ c = TCPSocket.new(addr, port)
+ s = serv.accept
+ if block_given?
+ begin
+ yield c, s
+ ensure
+ c.close if !c.closed?
+ s.close if !s.closed?
+ end
+ else
+ return c, s
+ end
+ ensure
+ serv.close if serv && !serv.closed?
+ end
+
+ def udp_pair
+ s1 = UDPSocket.new
+ s1.bind('127.0.0.1', 0)
+ af, port1, host, addr1 = s1.addr
+
+ s2 = UDPSocket.new
+ s2.bind('127.0.0.1', 0)
+ af, port2, host, addr2 = s2.addr
+
+ s1.connect(addr2, port2)
+ s2.connect(addr1, port1)
+
+ if block_given?
+ begin
+ yield s1, s2
+ ensure
+ s1.close if !s1.closed?
+ s2.close if !s2.closed?
+ end
+ else
+ return s1, s2
+ end
+ end
+
+ def test_tcp_recv_nonblock
+ c, s = tcp_pair
+ assert_raise(IO::WaitReadable) { c.recv_nonblock(100) }
+ assert_raise(IO::WaitReadable) { s.recv_nonblock(100) }
+ c.write("abc")
+ IO.select [s]
+ assert_equal("a", s.recv_nonblock(1))
+ assert_equal("bc", s.recv_nonblock(100))
+ assert_raise(IO::WaitReadable) { s.recv_nonblock(100) }
+ ensure
+ c.close if c
+ s.close if s
+ end
+
+ def test_read_nonblock
+ c, s = tcp_pair
+ assert_raise(IO::WaitReadable) { c.read_nonblock(100) }
+ assert_raise(IO::WaitReadable) { s.read_nonblock(100) }
+ c.write("abc")
+ IO.select [s]
+ assert_equal("a", s.read_nonblock(1))
+ assert_equal("bc", s.read_nonblock(100))
+ assert_raise(IO::WaitReadable) { s.read_nonblock(100) }
+ ensure
+ c.close if c
+ s.close if s
+ end
+
+ def test_read_nonblock_no_exception
+ c, s = tcp_pair
+ assert_equal :wait_readable, c.read_nonblock(100, exception: false)
+ assert_equal :wait_readable, s.read_nonblock(100, exception: false)
+ c.write("abc")
+ IO.select [s]
+ assert_equal("a", s.read_nonblock(1, exception: false))
+ assert_equal("bc", s.read_nonblock(100, exception: false))
+ assert_equal :wait_readable, s.read_nonblock(100, exception: false)
+ ensure
+ c.close if c
+ s.close if s
+ end
+
+=begin
+ def test_write_nonblock
+ c, s = tcp_pair
+ str = "a" * 10000
+ _, ws, _ = IO.select(nil, [c], nil)
+ assert_equal([c], ws)
+ ret = c.write_nonblock(str)
+ assert_operator(ret, :>, 0)
+ loop {
+ assert_raise(IO::WaitWritable) {
+ loop {
+ ret = c.write_nonblock(str)
+ assert_operator(ret, :>, 0)
+ }
+ }
+ _, ws, _ = IO.select(nil, [c], nil, 0)
+ break if !ws
+ }
+ ensure
+ c.close if c
+ s.close if s
+ end
+=end
+
+ def test_sendmsg_nonblock_error
+ udp_pair {|s1, s2|
+ begin
+ loop {
+ s1.sendmsg_nonblock("a" * 100000)
+ }
+ rescue NotImplementedError, Errno::ENOSYS
+ skip "sendmsg not implemented on this platform: #{$!}"
+ rescue Errno::EMSGSIZE
+ # UDP has 64K limit (if no Jumbograms). No problem.
+ rescue Errno::EWOULDBLOCK
+ assert_kind_of(IO::WaitWritable, $!)
+ end
+ }
+ end
+
+ def test_recvmsg_nonblock_error
+ udp_pair {|s1, s2|
+ begin
+ s1.recvmsg_nonblock(4096)
+ rescue NotImplementedError
+ skip "recvmsg not implemented on this platform."
+ rescue Errno::EWOULDBLOCK
+ assert_kind_of(IO::WaitReadable, $!)
+ end
+ }
+ end
+
+ def test_recv_nonblock_error
+ tcp_pair {|c, s|
+ begin
+ c.recv_nonblock(4096)
+ rescue Errno::EWOULDBLOCK
+ assert_kind_of(IO::WaitReadable, $!)
+ end
+ }
+ end
+
+ def test_connect_nonblock_error
+ serv = TCPServer.new("127.0.0.1", 0)
+ _, port, _, _ = serv.addr
+ c = Socket.new(:INET, :STREAM)
+ begin
+ c.connect_nonblock(Socket.sockaddr_in(port, "127.0.0.1"))
+ rescue Errno::EINPROGRESS
+ assert_kind_of(IO::WaitWritable, $!)
+ end
+ ensure
+ serv.close if serv && !serv.closed?
+ c.close if c && !c.closed?
+ end
+
+ def test_accept_nonblock_error
+ serv = Socket.new(:INET, :STREAM)
+ serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ serv.listen(5)
+ begin
+ s, _ = serv.accept_nonblock
+ rescue Errno::EWOULDBLOCK
+ assert_kind_of(IO::WaitReadable, $!)
+ end
+ ensure
+ serv.close if serv && !serv.closed?
+ s.close if s && !s.closed?
+ end
+
+end if defined?(Socket)
diff --git a/jni/ruby/test/socket/test_socket.rb b/jni/ruby/test/socket/test_socket.rb
new file mode 100644
index 0000000..1db0ea2
--- /dev/null
+++ b/jni/ruby/test/socket/test_socket.rb
@@ -0,0 +1,654 @@
+begin
+ require "socket"
+ require "tmpdir"
+ require "fcntl"
+ require "etc"
+ require "test/unit"
+rescue LoadError
+end
+
+class TestSocket < Test::Unit::TestCase
+ def test_socket_new
+ begin
+ s = Socket.new(:INET, :STREAM)
+ assert_kind_of(Socket, s)
+ ensure
+ s.close
+ end
+ end
+
+ def test_socket_new_cloexec
+ return unless defined? Fcntl::FD_CLOEXEC
+ begin
+ s = Socket.new(:INET, :STREAM)
+ assert(s.close_on_exec?)
+ ensure
+ s.close
+ end
+ end
+
+ def test_unpack_sockaddr
+ sockaddr_in = Socket.sockaddr_in(80, "")
+ assert_raise(ArgumentError) { Socket.unpack_sockaddr_un(sockaddr_in) }
+ sockaddr_un = Socket.sockaddr_un("/testdir/s")
+ assert_raise(ArgumentError) { Socket.unpack_sockaddr_in(sockaddr_un) }
+ assert_raise(ArgumentError) { Socket.unpack_sockaddr_in("") }
+ assert_raise(ArgumentError) { Socket.unpack_sockaddr_un("") }
+ end if Socket.respond_to?(:sockaddr_un)
+
+ def test_sysaccept
+ serv = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ serv.listen 5
+ c = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ c.connect(serv.getsockname)
+ fd, peeraddr = serv.sysaccept
+ assert_equal(c.getsockname, peeraddr.to_sockaddr)
+ ensure
+ serv.close if serv
+ c.close if c
+ IO.for_fd(fd).close if fd
+ end
+
+ def test_initialize
+ Socket.open(Socket::AF_INET, Socket::SOCK_STREAM, 0) {|s|
+ s.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ addr = s.getsockname
+ assert_nothing_raised { Socket.unpack_sockaddr_in(addr) }
+ assert_raise(ArgumentError, NoMethodError) { Socket.unpack_sockaddr_un(addr) }
+ }
+ Socket.open("AF_INET", "SOCK_STREAM", 0) {|s|
+ s.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ addr = s.getsockname
+ assert_nothing_raised { Socket.unpack_sockaddr_in(addr) }
+ assert_raise(ArgumentError, NoMethodError) { Socket.unpack_sockaddr_un(addr) }
+ }
+ Socket.open(:AF_INET, :SOCK_STREAM, 0) {|s|
+ s.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ addr = s.getsockname
+ assert_nothing_raised { Socket.unpack_sockaddr_in(addr) }
+ assert_raise(ArgumentError, NoMethodError) { Socket.unpack_sockaddr_un(addr) }
+ }
+ end
+
+ def test_bind
+ Socket.open(Socket::AF_INET, Socket::SOCK_STREAM, 0) {|bound|
+ bound.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+ addr = bound.getsockname
+ port, = Socket.unpack_sockaddr_in(addr)
+
+ Socket.open(Socket::AF_INET, Socket::SOCK_STREAM, 0) {|s|
+ e = assert_raises(Errno::EADDRINUSE) do
+ s.bind(Socket.sockaddr_in(port, "127.0.0.1"))
+ end
+
+ assert_match "bind(2) for 127.0.0.1:#{port}", e.message
+ }
+ }
+ end
+
+ def test_getaddrinfo
+ # This should not send a DNS query because AF_UNIX.
+ assert_raise(SocketError) { Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX") }
+ end
+
+ def test_getaddrinfo_raises_no_errors_on_port_argument_of_0 # [ruby-core:29427]
+ assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', 0, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
+ assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', '0', Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
+ assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', '00', Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
+ assert_raise(SocketError, '[ruby-core:29427]'){ Socket.getaddrinfo(nil, nil, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
+ assert_nothing_raised('[ruby-core:29427]'){ TCPServer.open('localhost', 0) {} }
+ end
+
+
+ def test_getnameinfo
+ assert_raise(SocketError) { Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"]) }
+ end
+
+ def test_ip_address_list
+ begin
+ list = Socket.ip_address_list
+ rescue NotImplementedError
+ return
+ end
+ list.each {|ai|
+ assert_instance_of(Addrinfo, ai)
+ assert(ai.ip?)
+ }
+ end
+
+ def test_tcp
+ TCPServer.open(0) {|serv|
+ addr = serv.connect_address
+ addr.connect {|s1|
+ s2 = serv.accept
+ begin
+ assert_equal(s2.remote_address.ip_unpack, s1.local_address.ip_unpack)
+ ensure
+ s2.close
+ end
+ }
+ }
+ end
+
+ def test_tcp_cloexec
+ return unless defined? Fcntl::FD_CLOEXEC
+ TCPServer.open(0) {|serv|
+ addr = serv.connect_address
+ addr.connect {|s1|
+ s2 = serv.accept
+ begin
+ assert(s2.close_on_exec?)
+ ensure
+ s2.close
+ end
+ }
+
+ }
+ end
+
+ def random_port
+ # IANA suggests dynamic port for 49152 to 65535
+ # http://www.iana.org/assignments/port-numbers
+ 49152 + rand(65535-49152+1)
+ end
+
+ def errors_addrinuse
+ [Errno::EADDRINUSE]
+ end
+
+ def test_tcp_server_sockets
+ port = random_port
+ begin
+ sockets = Socket.tcp_server_sockets(port)
+ rescue *errors_addrinuse
+ return # not test failure
+ end
+ begin
+ sockets.each {|s|
+ assert_equal(port, s.local_address.ip_port)
+ }
+ ensure
+ sockets.each {|s|
+ s.close
+ }
+ end
+ end
+
+ def test_tcp_server_sockets_port0
+ sockets = Socket.tcp_server_sockets(0)
+ ports = sockets.map {|s| s.local_address.ip_port }
+ the_port = ports.first
+ ports.each {|port|
+ assert_equal(the_port, port)
+ }
+ ensure
+ if sockets
+ sockets.each {|s|
+ s.close
+ }
+ end
+ end
+
+ if defined? UNIXSocket
+ def test_unix
+ Dir.mktmpdir {|tmpdir|
+ path = "#{tmpdir}/sock"
+ UNIXServer.open(path) {|serv|
+ Socket.unix(path) {|s1|
+ s2 = serv.accept
+ begin
+ s2raddr = s2.remote_address
+ s1laddr = s1.local_address
+ assert(s2raddr.to_sockaddr.empty? ||
+ s1laddr.to_sockaddr.empty? ||
+ s2raddr.unix_path == s1laddr.unix_path)
+ assert(s2.close_on_exec?)
+ ensure
+ s2.close
+ end
+ }
+ }
+ }
+ end
+
+ def test_unix_server_socket
+ Dir.mktmpdir {|tmpdir|
+ path = "#{tmpdir}/sock"
+ 2.times {
+ serv = Socket.unix_server_socket(path)
+ begin
+ assert_kind_of(Socket, serv)
+ assert(File.socket?(path))
+ assert_equal(path, serv.local_address.unix_path)
+ ensure
+ serv.close
+ end
+ }
+ }
+ end
+
+ def test_accept_loop_with_unix
+ Dir.mktmpdir {|tmpdir|
+ tcp_servers = []
+ clients = []
+ accepted = []
+ begin
+ tcp_servers = Socket.tcp_server_sockets(0)
+ unix_server = Socket.unix_server_socket("#{tmpdir}/sock")
+ tcp_servers.each {|s|
+ addr = s.connect_address
+ assert_nothing_raised("connect to #{addr.inspect}") {
+ clients << addr.connect
+ }
+ }
+ addr = unix_server.connect_address
+ assert_nothing_raised("connect to #{addr.inspect}") {
+ clients << addr.connect
+ }
+ Socket.accept_loop(tcp_servers, unix_server) {|s|
+ accepted << s
+ break if clients.length == accepted.length
+ }
+ assert_equal(clients.length, accepted.length)
+ ensure
+ tcp_servers.each {|s| s.close if !s.closed? }
+ unix_server.close if unix_server && !unix_server.closed?
+ clients.each {|s| s.close if !s.closed? }
+ accepted.each {|s| s.close if !s.closed? }
+ end
+ }
+ end
+ end
+
+ def test_accept_loop
+ servers = []
+ begin
+ servers = Socket.tcp_server_sockets(0)
+ port = servers[0].local_address.ip_port
+ Socket.tcp("localhost", port) {|s1|
+ Socket.accept_loop(servers) {|s2, client_ai|
+ begin
+ assert_equal(s1.local_address.ip_unpack, client_ai.ip_unpack)
+ ensure
+ s2.close
+ end
+ break
+ }
+ }
+ ensure
+ servers.each {|s| s.close if !s.closed? }
+ end
+ end
+
+ def test_accept_loop_multi_port
+ servers = []
+ begin
+ servers = Socket.tcp_server_sockets(0)
+ port = servers[0].local_address.ip_port
+ servers2 = Socket.tcp_server_sockets(0)
+ servers.concat servers2
+ port2 = servers2[0].local_address.ip_port
+
+ Socket.tcp("localhost", port) {|s1|
+ Socket.accept_loop(servers) {|s2, client_ai|
+ begin
+ assert_equal(s1.local_address.ip_unpack, client_ai.ip_unpack)
+ ensure
+ s2.close
+ end
+ break
+ }
+ }
+ Socket.tcp("localhost", port2) {|s1|
+ Socket.accept_loop(servers) {|s2, client_ai|
+ begin
+ assert_equal(s1.local_address.ip_unpack, client_ai.ip_unpack)
+ ensure
+ s2.close
+ end
+ break
+ }
+ }
+ ensure
+ servers.each {|s| s.close if !s.closed? }
+ end
+ end
+
+ def test_udp_server
+ begin
+ ifaddrs = Socket.getifaddrs
+ rescue NotImplementedError
+ skip "Socket.getifaddrs not implemented"
+ end
+
+ ifconfig = nil
+ Socket.udp_server_sockets(0) {|sockets|
+ famlies = {}
+ sockets.each {|s| famlies[s.local_address.afamily] = s }
+ nd6 = {}
+ ifaddrs.reject! {|ifa|
+ ai = ifa.addr
+ next true unless ai
+ s = famlies[ai.afamily]
+ next true unless s
+ next true if ai.ipv6_linklocal? # IPv6 link-local address is too troublesome in this test.
+ case RUBY_PLATFORM
+ when /linux/
+ if ai.ip_address.include?('%') and
+ (Etc.uname[:release][/[0-9.]+/].split('.').map(&:to_i) <=> [2,6,18]) <= 0
+ # Cent OS 5.6 (2.6.18-238.19.1.el5xen) doesn't correctly work
+ # sendmsg with pktinfo for link-local ipv6 addresses
+ next true
+ end
+ when /freebsd/
+ if ifa.addr.ipv6_linklocal?
+ # FreeBSD 9.0 with default setting (ipv6_activate_all_interfaces
+ # is not YES) sets IFDISABLED to interfaces which don't have
+ # global IPv6 address.
+ # Link-local IPv6 addresses on those interfaces don't work.
+ ulSIOCGIFINFO_IN6 = 3225971052
+ ulND6_IFF_IFDISABLED = 8
+ in6_ondireq = ifa.name
+ s.ioctl(ulSIOCGIFINFO_IN6, in6_ondireq)
+ flag = in6_ondireq.unpack('A16L6').last
+ next true if flag & ulND6_IFF_IFDISABLED != 0
+ nd6[ai] = flag
+ end
+ when /darwin/
+ if !ai.ipv6?
+ elsif ai.ipv6_unique_local? && /darwin1[01]\./ =~ RUBY_PLATFORM
+ next true # iCloud addresses do not work, see Bug #6692
+ elsif ifr_name = ai.ip_address[/%(.*)/, 1]
+ # Mac OS X may sets IFDISABLED as FreeBSD does
+ ulSIOCGIFFLAGS = 3223349521
+ ulSIOCGIFINFO_IN6 = 3224398156
+ ulIFF_POINTOPOINT = 0x10
+ ulND6_IFF_IFDISABLED = 8
+ in6_ondireq = ifr_name
+ s.ioctl(ulSIOCGIFINFO_IN6, in6_ondireq)
+ flag = in6_ondireq.unpack('A16L6').last
+ next true if (flag & ulND6_IFF_IFDISABLED) != 0
+ nd6[ai] = flag
+ in6_ifreq = [ifr_name,ai.to_sockaddr].pack('a16A*')
+ s.ioctl(ulSIOCGIFFLAGS, in6_ifreq)
+ next true if in6_ifreq.unpack('A16L1').last & ulIFF_POINTOPOINT != 0
+ else
+ ifconfig ||= `/sbin/ifconfig`
+ next true if ifconfig.scan(/^(\w+):(.*(?:\n\t.*)*)/).find do|ifname, value|
+ value.include?(ai.ip_address) && value.include?('POINTOPOINT')
+ end
+ end
+ end
+ false
+ }
+ skipped = false
+ begin
+ port = sockets.first.local_address.ip_port
+
+ ping_p = false
+ th = Thread.new {
+ Socket.udp_server_loop_on(sockets) {|msg, msg_src|
+ break if msg == "exit"
+ rmsg = Marshal.dump([msg, msg_src.remote_address, msg_src.local_address])
+ ping_p = true
+ msg_src.reply rmsg
+ }
+ }
+
+ ifaddrs.each {|ifa|
+ ai = ifa.addr
+ Addrinfo.udp(ai.ip_address, port).connect {|s|
+ ping_p = false
+ msg1 = "<<<#{ai.inspect}>>>"
+ s.sendmsg msg1
+ unless IO.select([s], nil, nil, 10)
+ nd6options = nd6.key?(ai) ? "nd6=%x " % nd6[ai] : ''
+ raise "no response from #{ifa.inspect} #{nd6options}ping=#{ping_p}"
+ end
+ msg2, addr = s.recvmsg
+ msg2, _, _ = Marshal.load(msg2)
+ assert_equal(msg1, msg2)
+ assert_equal(ai.ip_address, addr.ip_address)
+ }
+ }
+ rescue NotImplementedError, Errno::ENOSYS
+ skipped = true
+ skip "need sendmsg and recvmsg: #{$!}"
+ ensure
+ if th
+ if skipped
+ Thread.kill th unless th.join(10)
+ else
+ Addrinfo.udp("127.0.0.1", port).connect {|s| s.sendmsg "exit" }
+ unless th.join(10)
+ Thread.kill th
+ th.join(10)
+ raise "thread killed"
+ end
+ end
+ end
+ end
+ }
+ end
+
+ def test_linger
+ opt = Socket::Option.linger(true, 0)
+ assert_equal([true, 0], opt.linger)
+ Addrinfo.tcp("127.0.0.1", 0).listen {|serv|
+ serv.local_address.connect {|s1|
+ s2, _ = serv.accept
+ begin
+ s1.setsockopt(opt)
+ s1.close
+ assert_raise(Errno::ECONNRESET) { s2.read }
+ ensure
+ s2.close
+ end
+ }
+ }
+ end
+
+ def test_timestamp
+ return if /linux|freebsd|netbsd|openbsd|solaris|darwin/ !~ RUBY_PLATFORM
+ return if !defined?(Socket::AncillaryData)
+ t1 = Time.now.strftime("%Y-%m-%d")
+ stamp = nil
+ Addrinfo.udp("127.0.0.1", 0).bind {|s1|
+ Addrinfo.udp("127.0.0.1", 0).bind {|s2|
+ s1.setsockopt(:SOCKET, :TIMESTAMP, true)
+ s2.send "a", 0, s1.local_address
+ msg, _, _, stamp = s1.recvmsg
+ assert_equal("a", msg)
+ assert(stamp.cmsg_is?(:SOCKET, :TIMESTAMP))
+ }
+ }
+ t2 = Time.now.strftime("%Y-%m-%d")
+ pat = Regexp.union([t1, t2].uniq)
+ assert_match(pat, stamp.inspect)
+ t = stamp.timestamp
+ assert_match(pat, t.strftime("%Y-%m-%d"))
+ pat = /\.#{"%06d" % t.usec}/
+ assert_match(pat, stamp.inspect)
+ end
+
+ def test_timestampns
+ return if /linux/ !~ RUBY_PLATFORM || !defined?(Socket::SO_TIMESTAMPNS)
+ t1 = Time.now.strftime("%Y-%m-%d")
+ stamp = nil
+ Addrinfo.udp("127.0.0.1", 0).bind {|s1|
+ Addrinfo.udp("127.0.0.1", 0).bind {|s2|
+ begin
+ s1.setsockopt(:SOCKET, :TIMESTAMPNS, true)
+ rescue Errno::ENOPROTOOPT
+ # SO_TIMESTAMPNS is available since Linux 2.6.22
+ return
+ end
+ s2.send "a", 0, s1.local_address
+ msg, _, _, stamp = s1.recvmsg
+ assert_equal("a", msg)
+ assert(stamp.cmsg_is?(:SOCKET, :TIMESTAMPNS))
+ }
+ }
+ t2 = Time.now.strftime("%Y-%m-%d")
+ pat = Regexp.union([t1, t2].uniq)
+ assert_match(pat, stamp.inspect)
+ t = stamp.timestamp
+ assert_match(pat, t.strftime("%Y-%m-%d"))
+ pat = /\.#{"%09d" % t.nsec}/
+ assert_match(pat, stamp.inspect)
+ end
+
+ def test_bintime
+ return if /freebsd/ !~ RUBY_PLATFORM
+ t1 = Time.now.strftime("%Y-%m-%d")
+ stamp = nil
+ Addrinfo.udp("127.0.0.1", 0).bind {|s1|
+ Addrinfo.udp("127.0.0.1", 0).bind {|s2|
+ s1.setsockopt(:SOCKET, :BINTIME, true)
+ s2.send "a", 0, s1.local_address
+ msg, _, _, stamp = s1.recvmsg
+ assert_equal("a", msg)
+ assert(stamp.cmsg_is?(:SOCKET, :BINTIME))
+ }
+ }
+ t2 = Time.now.strftime("%Y-%m-%d")
+ pat = Regexp.union([t1, t2].uniq)
+ assert_match(pat, stamp.inspect)
+ t = stamp.timestamp
+ assert_match(pat, t.strftime("%Y-%m-%d"))
+ assert_equal(stamp.data[-8,8].unpack("Q")[0], t.subsec * 2**64)
+ end
+
+ def test_closed_read
+ require 'timeout'
+ require 'socket'
+ bug4390 = '[ruby-core:35203]'
+ server = TCPServer.new("localhost", 0)
+ serv_thread = Thread.new {server.accept}
+ begin sleep(0.1) end until serv_thread.stop?
+ sock = TCPSocket.new("localhost", server.addr[1])
+ client_thread = Thread.new do
+ sock.readline
+ end
+ begin sleep(0.1) end until client_thread.stop?
+ Timeout.timeout(1) do
+ sock.close
+ sock = nil
+ assert_raise(IOError, bug4390) {client_thread.join}
+ end
+ ensure
+ serv_thread.value.close
+ server.close
+ end
+
+ def test_connect_timeout
+ host = "127.0.0.1"
+ server = TCPServer.new(host, 0)
+ port = server.addr[1]
+ serv_thread = Thread.new {server.accept}
+ sock = Socket.tcp(host, port, :connect_timeout => 30)
+ accepted = serv_thread.value
+ assert_kind_of TCPSocket, accepted
+ assert_equal sock, IO.select(nil, [ sock ])[1][0], "not writable"
+ sock.close
+
+ # some platforms may not timeout when the listener queue overflows,
+ # but we know Linux does with the default listen backlog of SOMAXCONN for
+ # TCPServer.
+ assert_raises(Errno::ETIMEDOUT) do
+ (Socket::SOMAXCONN*2).times do |i|
+ sock = Socket.tcp(host, port, :connect_timeout => 0)
+ assert_equal sock, IO.select(nil, [ sock ])[1][0],
+ "not writable (#{i})"
+ sock.close
+ end
+ end if RUBY_PLATFORM =~ /linux/
+ ensure
+ server.close
+ accepted.close if accepted
+ sock.close if sock && ! sock.closed?
+ end
+
+ def test_getifaddrs
+ begin
+ list = Socket.getifaddrs
+ rescue NotImplementedError
+ return
+ end
+ list.each {|ifaddr|
+ assert_instance_of(Socket::Ifaddr, ifaddr)
+ }
+ end
+
+ def test_connect_in_rescue
+ serv = Addrinfo.tcp(nil, 0).listen
+ addr = serv.connect_address
+ begin
+ raise "dummy error"
+ rescue
+ s = addr.connect
+ assert(!s.closed?)
+ end
+ ensure
+ serv.close if serv && !serv.closed?
+ s.close if s && !s.closed?
+ end
+
+ def test_bind_in_rescue
+ begin
+ raise "dummy error"
+ rescue
+ s = Addrinfo.tcp(nil, 0).bind
+ assert(!s.closed?)
+ end
+ ensure
+ s.close if s && !s.closed?
+ end
+
+ def test_listen_in_rescue
+ begin
+ raise "dummy error"
+ rescue
+ s = Addrinfo.tcp(nil, 0).listen
+ assert(!s.closed?)
+ end
+ ensure
+ s.close if s && !s.closed?
+ end
+
+ def test_udp_server_sockets_in_rescue
+ begin
+ raise "dummy error"
+ rescue
+ ss = Socket.udp_server_sockets(0)
+ ss.each {|s|
+ assert(!s.closed?)
+ }
+ end
+ ensure
+ if ss
+ ss.each {|s|
+ s.close if !s.closed?
+ }
+ end
+ end
+
+ def test_tcp_server_sockets_in_rescue
+ begin
+ raise "dummy error"
+ rescue
+ ss = Socket.tcp_server_sockets(0)
+ ss.each {|s|
+ assert(!s.closed?)
+ }
+ end
+ ensure
+ if ss
+ ss.each {|s|
+ s.close if !s.closed?
+ }
+ end
+ end
+
+end if defined?(Socket)
diff --git a/jni/ruby/test/socket/test_sockopt.rb b/jni/ruby/test/socket/test_sockopt.rb
new file mode 100644
index 0000000..2ee06db
--- /dev/null
+++ b/jni/ruby/test/socket/test_sockopt.rb
@@ -0,0 +1,65 @@
+require 'test/unit'
+require 'socket'
+
+class TestSocketOption < Test::Unit::TestCase
+ def test_new
+ data = [1].pack("i")
+ sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, data)
+ assert_equal(Socket::AF_INET, sockopt.family)
+ assert_equal(Socket::SOL_SOCKET, sockopt.level)
+ assert_equal(Socket::SO_KEEPALIVE, sockopt.optname)
+ assert_equal(Socket::SO_KEEPALIVE, sockopt.optname)
+ assert_equal(data, sockopt.data)
+ end
+
+ def test_bool
+ opt = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
+ assert_equal(1, opt.int)
+ opt = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false)
+ assert_equal(0, opt.int)
+ opt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 0)
+ assert_equal(false, opt.bool)
+ opt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
+ assert_equal(true, opt.bool)
+ opt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 2)
+ assert_equal(true, opt.bool)
+ end
+
+ def test_ipv4_multicast_loop
+ sockopt = Socket::Option.ipv4_multicast_loop(128)
+ assert_equal('#<Socket::Option: INET IP MULTICAST_LOOP 128>', sockopt.inspect)
+ assert_equal(Socket::AF_INET, sockopt.family)
+ assert_equal(Socket::IPPROTO_IP, sockopt.level)
+ assert_equal(Socket::IP_MULTICAST_LOOP, sockopt.optname)
+ assert_equal(128, sockopt.ipv4_multicast_loop)
+ end
+
+ def test_ipv4_multicast_loop_size
+ expected_size = Socket.open(:INET, :DGRAM) {|s|
+ s.getsockopt(:IP, :MULTICAST_LOOP).to_s.bytesize
+ }
+ assert_equal(expected_size, Socket::Option.ipv4_multicast_loop(0).to_s.bytesize)
+ end
+
+ def test_ipv4_multicast_ttl
+ sockopt = Socket::Option.ipv4_multicast_ttl(128)
+ assert_equal('#<Socket::Option: INET IP MULTICAST_TTL 128>', sockopt.inspect)
+ assert_equal(Socket::AF_INET, sockopt.family)
+ assert_equal(Socket::IPPROTO_IP, sockopt.level)
+ assert_equal(Socket::IP_MULTICAST_TTL, sockopt.optname)
+ assert_equal(128, sockopt.ipv4_multicast_ttl)
+ end
+
+ def test_ipv4_multicast_ttl_size
+ expected_size = Socket.open(:INET, :DGRAM) {|s|
+ s.getsockopt(:IP, :MULTICAST_TTL).to_s.bytesize
+ }
+ assert_equal(expected_size, Socket::Option.ipv4_multicast_ttl(0).to_s.bytesize)
+ end
+
+ def test_unpack
+ sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i"))
+ assert_equal([1], sockopt.unpack("i"))
+ assert_equal([1], sockopt.data.unpack("i"))
+ end
+end
diff --git a/jni/ruby/test/socket/test_tcp.rb b/jni/ruby/test/socket/test_tcp.rb
new file mode 100644
index 0000000..7328897
--- /dev/null
+++ b/jni/ruby/test/socket/test_tcp.rb
@@ -0,0 +1,79 @@
+begin
+ require "socket"
+ require "test/unit"
+rescue LoadError
+end
+
+
+class TestSocket_TCPSocket < Test::Unit::TestCase
+ def test_initialize_failure
+ # These addresses are chosen from TEST-NET-1, TEST-NET-2, and TEST-NET-3.
+ # [RFC 5737]
+ # They are choosen because probably they are not used as a host address.
+ # Anyway the addresses are used for bind() and should be failed.
+ # So no packets should be generated.
+ test_ip_addresses = [
+ '192.0.2.1', '192.0.2.42', # TEST-NET-1
+ '198.51.100.1', '198.51.100.42', # TEST-NET-2
+ '203.0.113.1', '203.0.113.42', # TEST-NET-3
+ ]
+ begin
+ list = Socket.ip_address_list
+ rescue NotImplementedError
+ return
+ end
+ test_ip_addresses -= list.reject {|ai| !ai.ipv4? }.map {|ai| ai.ip_address }
+ if test_ip_addresses.empty?
+ return
+ end
+ client_addr = test_ip_addresses.first
+ client_port = 8000
+
+ server_addr = '127.0.0.1'
+ server_port = 80
+
+ begin
+ # Since client_addr is not an IP address of this host,
+ # bind() in TCPSocket.new should fail as EADDRNOTAVAIL.
+ t = TCPSocket.new(server_addr, server_port, client_addr, client_port)
+ flunk "expected SystemCallError"
+ rescue SystemCallError => e
+ assert_match "for \"#{client_addr}\" port #{client_port}", e.message
+ end
+ ensure
+ t.close if t && !t.closed?
+ end
+
+ def test_recvfrom
+ TCPServer.open("localhost", 0) {|svr|
+ th = Thread.new {
+ c = svr.accept
+ c.write "foo"
+ c.close
+ }
+ addr = svr.addr
+ TCPSocket.open(addr[3], addr[1]) {|sock|
+ assert_equal(["foo", nil], sock.recvfrom(0x10000))
+ }
+ th.join
+ }
+ end
+
+ def test_encoding
+ TCPServer.open("localhost", 0) {|svr|
+ th = Thread.new {
+ c = svr.accept
+ c.write "foo\r\n"
+ c.close
+ }
+ addr = svr.addr
+ TCPSocket.open(addr[3], addr[1]) {|sock|
+ assert_equal(true, sock.binmode?)
+ s = sock.gets
+ assert_equal("foo\r\n", s)
+ assert_equal(Encoding.find("ASCII-8BIT"), s.encoding)
+ }
+ th.join
+ }
+ end
+end if defined?(TCPSocket)
diff --git a/jni/ruby/test/socket/test_udp.rb b/jni/ruby/test/socket/test_udp.rb
new file mode 100644
index 0000000..b07a4b7
--- /dev/null
+++ b/jni/ruby/test/socket/test_udp.rb
@@ -0,0 +1,72 @@
+begin
+ require "socket"
+ require "test/unit"
+rescue LoadError
+end
+
+
+class TestSocket_UDPSocket < Test::Unit::TestCase
+ def test_open
+ assert_nothing_raised { UDPSocket.open {} }
+ assert_nothing_raised { UDPSocket.open(Socket::AF_INET) {} }
+ assert_nothing_raised { UDPSocket.open("AF_INET") {} }
+ assert_nothing_raised { UDPSocket.open(:AF_INET) {} }
+ end
+
+ def test_connect
+ s = UDPSocket.new
+ host = Object.new
+ class << host; self end.send(:define_method, :to_str) {
+ s.close
+ "127.0.0.1"
+ }
+ assert_raise(IOError, "[ruby-dev:25045]") {
+ s.connect(host, 1)
+ }
+ end
+
+ def test_bind
+ s = UDPSocket.new
+ host = Object.new
+ class << host; self end.send(:define_method, :to_str) {
+ s.close
+ "127.0.0.1"
+ }
+ assert_raise(IOError, "[ruby-dev:25057]") {
+ s.bind(host, 2000)
+ }
+ ensure
+ s.close if s && !s.closed?
+ end
+
+ def test_bind_addrinuse
+ host = "127.0.0.1"
+
+ in_use = UDPSocket.new
+ in_use.bind(host, 0)
+ port = in_use.addr[1]
+
+ s = UDPSocket.new
+
+ e = assert_raises(Errno::EADDRINUSE) do
+ s.bind(host, port)
+ end
+
+ assert_match "bind(2) for \"#{host}\" port #{port}", e.message
+ ensure
+ in_use.close if in_use
+ s.close if s
+ end
+
+ def test_send_too_long
+ u = UDPSocket.new
+
+ e = assert_raises Errno::EMSGSIZE do
+ u.send "\0" * 100_000, 0, "127.0.0.1", 7 # echo
+ end
+
+ assert_match 'for "127.0.0.1" port 7', e.message
+ ensure
+ u.close if u
+ end
+end if defined?(UDPSocket)
diff --git a/jni/ruby/test/socket/test_unix.rb b/jni/ruby/test/socket/test_unix.rb
new file mode 100644
index 0000000..866c839
--- /dev/null
+++ b/jni/ruby/test/socket/test_unix.rb
@@ -0,0 +1,666 @@
+begin
+ require "socket"
+rescue LoadError
+end
+
+require "test/unit"
+require "tempfile"
+require "timeout"
+require "tmpdir"
+require "thread"
+require "io/nonblock"
+
+class TestSocket_UNIXSocket < Test::Unit::TestCase
+ def test_fd_passing
+ r1, w = IO.pipe
+ s1, s2 = UNIXSocket.pair
+ begin
+ s1.send_io(nil)
+ rescue NotImplementedError
+ assert_raise(NotImplementedError) { s2.recv_io }
+ rescue TypeError
+ s1.send_io(r1)
+ r2 = s2.recv_io
+ assert_equal(r1.stat.ino, r2.stat.ino)
+ assert_not_equal(r1.fileno, r2.fileno)
+ assert(r2.close_on_exec?)
+ w.syswrite "a"
+ assert_equal("a", r2.sysread(10))
+ ensure
+ s1.close
+ s2.close
+ w.close
+ r1.close
+ r2.close if r2 && !r2.closed?
+ end
+ end
+
+ def test_fd_passing_n
+ io_ary = []
+ return if !defined?(Socket::SCM_RIGHTS)
+ io_ary.concat IO.pipe
+ io_ary.concat IO.pipe
+ io_ary.concat IO.pipe
+ send_io_ary = []
+ io_ary.each {|io|
+ send_io_ary << io
+ UNIXSocket.pair {|s1, s2|
+ begin
+ ret = s1.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, Socket::SCM_RIGHTS,
+ send_io_ary.map {|io2| io2.fileno }.pack("i!*")])
+ rescue NotImplementedError
+ return
+ end
+ assert_equal(1, ret)
+ ret = s2.recvmsg(:scm_rights=>true)
+ _, _, _, *ctls = ret
+ recv_io_ary = []
+ begin
+ ctls.each {|ctl|
+ next if ctl.level != Socket::SOL_SOCKET || ctl.type != Socket::SCM_RIGHTS
+ recv_io_ary.concat ctl.unix_rights
+ }
+ assert_equal(send_io_ary.length, recv_io_ary.length)
+ send_io_ary.length.times {|i|
+ assert_not_equal(send_io_ary[i].fileno, recv_io_ary[i].fileno)
+ assert(File.identical?(send_io_ary[i], recv_io_ary[i]))
+ assert(recv_io_ary[i].close_on_exec?)
+ }
+ ensure
+ recv_io_ary.each {|io2| io2.close if !io2.closed? }
+ end
+ }
+ }
+ ensure
+ io_ary.each {|io| io.close if !io.closed? }
+ end
+
+ def test_fd_passing_n2
+ io_ary = []
+ return if !defined?(Socket::SCM_RIGHTS)
+ return if !defined?(Socket::AncillaryData)
+ io_ary.concat IO.pipe
+ io_ary.concat IO.pipe
+ io_ary.concat IO.pipe
+ send_io_ary = []
+ io_ary.each {|io|
+ send_io_ary << io
+ UNIXSocket.pair {|s1, s2|
+ begin
+ ancdata = Socket::AncillaryData.unix_rights(*send_io_ary)
+ ret = s1.sendmsg("\0", 0, nil, ancdata)
+ rescue NotImplementedError
+ return
+ end
+ assert_equal(1, ret)
+ ret = s2.recvmsg(:scm_rights=>true)
+ _, _, _, *ctls = ret
+ recv_io_ary = []
+ begin
+ ctls.each {|ctl|
+ next if ctl.level != Socket::SOL_SOCKET || ctl.type != Socket::SCM_RIGHTS
+ recv_io_ary.concat ctl.unix_rights
+ }
+ assert_equal(send_io_ary.length, recv_io_ary.length)
+ send_io_ary.length.times {|i|
+ assert_not_equal(send_io_ary[i].fileno, recv_io_ary[i].fileno)
+ assert(File.identical?(send_io_ary[i], recv_io_ary[i]))
+ assert(recv_io_ary[i].close_on_exec?)
+ }
+ ensure
+ recv_io_ary.each {|io2| io2.close if !io2.closed? }
+ end
+ }
+ }
+ ensure
+ io_ary.each {|io| io.close if !io.closed? }
+ end
+
+ def test_fd_passing_race_condition
+ r1, w = IO.pipe
+ s1, s2 = UNIXSocket.pair
+ s1.nonblock = s2.nonblock = true
+ lock = Mutex.new
+ nr = 0
+ x = 2
+ y = 1000
+ begin
+ s1.send_io(nil)
+ rescue NotImplementedError
+ assert_raise(NotImplementedError) { s2.recv_io }
+ rescue TypeError
+ thrs = x.times.map do
+ Thread.new do
+ y.times do
+ s2.recv_io.close
+ lock.synchronize { nr += 1 }
+ end
+ true
+ end
+ end
+ (x * y).times { s1.send_io r1 }
+ assert_equal([true]*x, thrs.map { |t| t.value })
+ assert_equal x * y, nr
+ ensure
+ s1.close
+ s2.close
+ w.close
+ r1.close
+ end
+ end
+
+ def test_sendmsg
+ return if !defined?(Socket::SCM_RIGHTS)
+ IO.pipe {|r1, w|
+ UNIXSocket.pair {|s1, s2|
+ begin
+ ret = s1.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, Socket::SCM_RIGHTS, [r1.fileno].pack("i!")])
+ rescue NotImplementedError
+ return
+ end
+ assert_equal(1, ret)
+ r2 = s2.recv_io
+ begin
+ assert(File.identical?(r1, r2))
+ assert(r2.close_on_exec?)
+ ensure
+ r2.close
+ end
+ }
+ }
+ end
+
+ def test_sendmsg_ancillarydata_int
+ return if !defined?(Socket::SCM_RIGHTS)
+ return if !defined?(Socket::AncillaryData)
+ IO.pipe {|r1, w|
+ UNIXSocket.pair {|s1, s2|
+ begin
+ ad = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, r1.fileno)
+ ret = s1.sendmsg("\0", 0, nil, ad)
+ rescue NotImplementedError
+ return
+ end
+ assert_equal(1, ret)
+ r2 = s2.recv_io
+ begin
+ assert(File.identical?(r1, r2))
+ ensure
+ r2.close
+ end
+ }
+ }
+ end
+
+ def test_sendmsg_ancillarydata_unix_rights
+ return if !defined?(Socket::SCM_RIGHTS)
+ return if !defined?(Socket::AncillaryData)
+ IO.pipe {|r1, w|
+ UNIXSocket.pair {|s1, s2|
+ begin
+ ad = Socket::AncillaryData.unix_rights(r1)
+ ret = s1.sendmsg("\0", 0, nil, ad)
+ rescue NotImplementedError
+ return
+ end
+ assert_equal(1, ret)
+ r2 = s2.recv_io
+ begin
+ assert(File.identical?(r1, r2))
+ ensure
+ r2.close
+ end
+ }
+ }
+ end
+
+ def test_recvmsg
+ return if !defined?(Socket::SCM_RIGHTS)
+ return if !defined?(Socket::AncillaryData)
+ IO.pipe {|r1, w|
+ UNIXSocket.pair {|s1, s2|
+ s1.send_io(r1)
+ ret = s2.recvmsg(:scm_rights=>true)
+ data, srcaddr, flags, *ctls = ret
+ assert_equal("\0", data)
+ if flags == nil
+ # struct msghdr is 4.3BSD style (msg_accrights field).
+ assert_instance_of(Array, ctls)
+ assert_equal(0, ctls.length)
+ else
+ # struct msghdr is POSIX/4.4BSD style (msg_control field).
+ assert_equal(0, flags & (Socket::MSG_TRUNC|Socket::MSG_CTRUNC))
+ assert_instance_of(Addrinfo, srcaddr)
+ assert_instance_of(Array, ctls)
+ assert_equal(1, ctls.length)
+ ctl = ctls[0]
+ assert_instance_of(Socket::AncillaryData, ctl)
+ assert_equal(Socket::SOL_SOCKET, ctl.level)
+ assert_equal(Socket::SCM_RIGHTS, ctl.type)
+ assert_instance_of(String, ctl.data)
+ ios = ctl.unix_rights
+ assert_equal(1, ios.length)
+ r2 = ios[0]
+ begin
+ assert(File.identical?(r1, r2))
+ assert(r2.close_on_exec?)
+ ensure
+ r2.close
+ end
+ end
+ }
+ }
+ end
+
+ def bound_unix_socket(klass)
+ tmpfile = Tempfile.new("s")
+ path = tmpfile.path
+ tmpfile.close(true)
+ io = klass.new(path)
+ yield io, path
+ ensure
+ io.close
+ File.unlink path if path && File.socket?(path)
+ end
+
+ def test_addr
+ bound_unix_socket(UNIXServer) {|serv, path|
+ UNIXSocket.open(path) {|c|
+ s = serv.accept
+ begin
+ assert_equal(["AF_UNIX", path], c.peeraddr)
+ assert_equal(["AF_UNIX", ""], c.addr)
+ assert_equal(["AF_UNIX", ""], s.peeraddr)
+ assert_equal(["AF_UNIX", path], s.addr)
+ assert_equal(path, s.path)
+ assert_equal("", c.path)
+ ensure
+ s.close
+ end
+ }
+ }
+ end
+
+ def test_cloexec
+ bound_unix_socket(UNIXServer) {|serv, path|
+ UNIXSocket.open(path) {|c|
+ s = serv.accept
+ begin
+ assert(serv.close_on_exec?)
+ assert(c.close_on_exec?)
+ assert(s.close_on_exec?)
+ ensure
+ s.close
+ end
+ }
+ }
+ end
+
+ def test_noname_path
+ s1, s2 = UNIXSocket.pair
+ assert_equal("", s1.path)
+ assert_equal("", s2.path)
+ ensure
+ s1.close
+ s2.close
+ end
+
+ def test_noname_addr
+ s1, s2 = UNIXSocket.pair
+ assert_equal(["AF_UNIX", ""], s1.addr)
+ assert_equal(["AF_UNIX", ""], s2.addr)
+ ensure
+ s1.close
+ s2.close
+ end
+
+ def test_noname_peeraddr
+ s1, s2 = UNIXSocket.pair
+ assert_equal(["AF_UNIX", ""], s1.peeraddr)
+ assert_equal(["AF_UNIX", ""], s2.peeraddr)
+ ensure
+ s1.close
+ s2.close
+ end
+
+ def test_noname_unpack_sockaddr_un
+ s1, s2 = UNIXSocket.pair
+ n = nil
+ assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != ""
+ assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != ""
+ assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getsockname) != ""
+ assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getpeername) != ""
+ assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getpeername) != ""
+ ensure
+ s1.close
+ s2.close
+ end
+
+ def test_noname_recvfrom
+ s1, s2 = UNIXSocket.pair
+ s2.write("a")
+ assert_equal(["a", ["AF_UNIX", ""]], s1.recvfrom(10))
+ ensure
+ s1.close
+ s2.close
+ end
+
+ def test_noname_recv_nonblock
+ s1, s2 = UNIXSocket.pair
+ s2.write("a")
+ IO.select [s1]
+ assert_equal("a", s1.recv_nonblock(10))
+ ensure
+ s1.close
+ s2.close
+ end
+
+ def test_too_long_path
+ assert_raise(ArgumentError) { Socket.sockaddr_un("a" * 3000) }
+ assert_raise(ArgumentError) { UNIXServer.new("a" * 3000) }
+ end
+
+ def test_abstract_namespace
+ return if /linux/ !~ RUBY_PLATFORM
+ addr = Socket.pack_sockaddr_un("\0foo")
+ assert_match(/\0foo\z/, addr)
+ assert_equal("\0foo", Socket.unpack_sockaddr_un(addr))
+ end
+
+ def test_dgram_pair
+ s1, s2 = UNIXSocket.pair(Socket::SOCK_DGRAM)
+ begin
+ s1.recv_nonblock(10)
+ fail
+ rescue => e
+ assert(IO::EAGAINWaitReadable === e)
+ assert(IO::WaitReadable === e)
+ end
+ s2.send("", 0)
+ s2.send("haha", 0)
+ s2.send("", 0)
+ s2.send("", 0)
+ assert_equal("", s1.recv(10))
+ assert_equal("haha", s1.recv(10))
+ assert_equal("", s1.recv(10))
+ assert_equal("", s1.recv(10))
+ assert_raise(IO::EAGAINWaitReadable) { s1.recv_nonblock(10) }
+ ensure
+ s1.close if s1
+ s2.close if s2
+ end
+
+ def test_dgram_pair_sendrecvmsg_errno_set
+ s1, s2 = to_close = UNIXSocket.pair(Socket::SOCK_DGRAM)
+ pipe = IO.pipe
+ to_close.concat(pipe)
+ set_errno = lambda do
+ begin
+ pipe[0].read_nonblock(1)
+ fail
+ rescue => e
+ assert(IO::EAGAINWaitReadable === e)
+ end
+ end
+ Timeout.timeout(10) do
+ set_errno.call
+ assert_equal(2, s1.sendmsg("HI"))
+ set_errno.call
+ assert_equal("HI", s2.recvmsg[0])
+ end
+ ensure
+ to_close.each(&:close) if to_close
+ end
+
+ def test_epipe # [ruby-dev:34619]
+ UNIXSocket.pair {|s1, s2|
+ s1.shutdown(Socket::SHUT_WR)
+ assert_raise(Errno::EPIPE) { s1.write "a" }
+ assert_equal(nil, s2.read(1))
+ s2.write "a"
+ assert_equal("a", s1.read(1))
+ }
+ end
+
+ def test_socket_pair_with_block
+ pair = nil
+ ret = Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) {|s1, s2|
+ pair = [s1, s2]
+ :return_value
+ }
+ assert_equal(:return_value, ret)
+ assert_kind_of(Socket, pair[0])
+ assert_kind_of(Socket, pair[1])
+ end
+
+ def test_unix_socket_pair_with_block
+ pair = nil
+ UNIXSocket.pair {|s1, s2|
+ pair = [s1, s2]
+ }
+ assert_kind_of(UNIXSocket, pair[0])
+ assert_kind_of(UNIXSocket, pair[1])
+ end
+
+ def test_unix_socket_pair_close_on_exec
+ UNIXSocket.pair {|s1, s2|
+ assert(s1.close_on_exec?)
+ assert(s2.close_on_exec?)
+ }
+ end
+
+ def test_initialize
+ Dir.mktmpdir {|d|
+ Socket.open(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) {|s|
+ s.bind(Socket.pack_sockaddr_un("#{d}/s1"))
+ addr = s.getsockname
+ assert_nothing_raised { Socket.unpack_sockaddr_un(addr) }
+ assert_raise(ArgumentError) { Socket.unpack_sockaddr_in(addr) }
+ }
+ Socket.open("AF_UNIX", "SOCK_STREAM", 0) {|s|
+ s.bind(Socket.pack_sockaddr_un("#{d}/s2"))
+ addr = s.getsockname
+ assert_nothing_raised { Socket.unpack_sockaddr_un(addr) }
+ assert_raise(ArgumentError) { Socket.unpack_sockaddr_in(addr) }
+ }
+ }
+ end
+
+ def test_unix_server_socket
+ Dir.mktmpdir {|d|
+ path = "#{d}/sock"
+ s0 = nil
+ Socket.unix_server_socket(path) {|s|
+ assert_equal(path, s.local_address.unix_path)
+ assert(File.socket?(path))
+ s0 = s
+ }
+ assert(s0.closed?)
+ assert_raise(Errno::ENOENT) { File.stat path }
+ }
+ end
+
+ def test_getcred_ucred
+ return if /linux/ !~ RUBY_PLATFORM
+ Dir.mktmpdir {|d|
+ sockpath = "#{d}/sock"
+ Socket.unix_server_socket(sockpath) {|serv|
+ Socket.unix(sockpath) {|c|
+ s, = serv.accept
+ begin
+ cred = s.getsockopt(:SOCKET, :PEERCRED)
+ inspect = cred.inspect
+ assert_match(/ pid=#{$$} /, inspect)
+ assert_match(/ euid=#{Process.euid} /, inspect)
+ assert_match(/ egid=#{Process.egid} /, inspect)
+ assert_match(/ \(ucred\)/, inspect)
+ ensure
+ s.close
+ end
+ }
+ }
+ }
+ end
+
+ def test_getcred_xucred
+ return if /freebsd|darwin/ !~ RUBY_PLATFORM
+ Dir.mktmpdir {|d|
+ sockpath = "#{d}/sock"
+ serv = Socket.unix_server_socket(sockpath)
+ Socket.unix(sockpath)
+ s, = serv.accept
+ cred = s.getsockopt(0, Socket::LOCAL_PEERCRED)
+ inspect = cred.inspect
+ assert_match(/ euid=#{Process.euid} /, inspect)
+ assert_match(/ \(xucred\)/, inspect)
+ }
+ end
+
+ def test_sendcred_ucred
+ return if /linux/ !~ RUBY_PLATFORM
+ Dir.mktmpdir {|d|
+ sockpath = "#{d}/sock"
+ Socket.unix_server_socket(sockpath) {|serv|
+ Socket.unix(sockpath) {|c|
+ s, = serv.accept
+ begin
+ s.setsockopt(:SOCKET, :PASSCRED, 1)
+ c.print "a"
+ msg, _, _, cred = s.recvmsg
+ inspect = cred.inspect
+ assert_equal("a", msg)
+ assert_match(/ pid=#{$$} /, inspect)
+ assert_match(/ uid=#{Process.uid} /, inspect)
+ assert_match(/ gid=#{Process.gid} /, inspect)
+ assert_match(/ \(ucred\)/, inspect)
+ ensure
+ s.close
+ end
+ }
+ }
+ }
+ end
+
+ def test_sendcred_sockcred
+ return if /netbsd|freebsd/ !~ RUBY_PLATFORM
+ Dir.mktmpdir {|d|
+ sockpath = "#{d}/sock"
+ serv = Socket.unix_server_socket(sockpath)
+ c = Socket.unix(sockpath)
+ s, = serv.accept
+ s.setsockopt(0, Socket::LOCAL_CREDS, 1)
+ c.print "a"
+ msg, _, _, cred = s.recvmsg
+ assert_equal("a", msg)
+ inspect = cred.inspect
+ assert_match(/ uid=#{Process.uid} /, inspect)
+ assert_match(/ euid=#{Process.euid} /, inspect)
+ assert_match(/ gid=#{Process.gid} /, inspect)
+ assert_match(/ egid=#{Process.egid} /, inspect)
+ assert_match(/ \(sockcred\)/, inspect)
+ }
+ end
+
+ def test_sendcred_cmsgcred
+ return if /freebsd/ !~ RUBY_PLATFORM
+ Dir.mktmpdir {|d|
+ sockpath = "#{d}/sock"
+ serv = Socket.unix_server_socket(sockpath)
+ c = Socket.unix(sockpath)
+ s, = serv.accept
+ c.sendmsg("a", 0, nil, [:SOCKET, Socket::SCM_CREDS, ""])
+ msg, _, _, cred = s.recvmsg
+ assert_equal("a", msg)
+ inspect = cred.inspect
+ assert_match(/ pid=#{$$} /, inspect)
+ assert_match(/ uid=#{Process.uid} /, inspect)
+ assert_match(/ euid=#{Process.euid} /, inspect)
+ assert_match(/ gid=#{Process.gid} /, inspect)
+ assert_match(/ \(cmsgcred\)/, inspect)
+ }
+ end
+
+ def test_getpeereid
+ Dir.mktmpdir {|d|
+ path = "#{d}/sock"
+ Socket.unix_server_socket(path) {|serv|
+ Socket.unix(path) {|c|
+ s, = serv.accept
+ begin
+ assert_equal([Process.euid, Process.egid], c.getpeereid)
+ assert_equal([Process.euid, Process.egid], s.getpeereid)
+ rescue NotImplementedError
+ ensure
+ s.close
+ end
+ }
+ }
+ }
+ end
+
+ def test_abstract_unix_server
+ return if /linux/ !~ RUBY_PLATFORM
+ name = "\0ruby-test_unix"
+ s0 = nil
+ UNIXServer.open(name) {|s|
+ assert_equal(name, s.local_address.unix_path)
+ s0 = s
+ UNIXSocket.open(name) {|c|
+ sock = s.accept
+ begin
+ assert_equal(name, c.remote_address.unix_path)
+ ensure
+ sock.close
+ end
+ }
+ }
+ assert(s0.closed?)
+ end
+
+ def test_abstract_unix_socket_econnrefused
+ return if /linux/ !~ RUBY_PLATFORM
+ name = "\0ruby-test_unix"
+ assert_raise(Errno::ECONNREFUSED) do
+ UNIXSocket.open(name) {}
+ end
+ end
+
+ def test_abstract_unix_server_socket
+ return if /linux/ !~ RUBY_PLATFORM
+ name = "\0ruby-test_unix"
+ s0 = nil
+ Socket.unix_server_socket(name) {|s|
+ assert_equal(name, s.local_address.unix_path)
+ s0 = s
+ Socket.unix(name) {|c|
+ sock, = s.accept
+ begin
+ assert_equal(name, c.remote_address.unix_path)
+ ensure
+ sock.close
+ end
+ }
+ }
+ assert(s0.closed?)
+ end
+
+ def test_autobind
+ return if /linux/ !~ RUBY_PLATFORM
+ s0 = nil
+ Socket.unix_server_socket("") {|s|
+ name = s.local_address.unix_path
+ assert_match(/\A\0[0-9a-f]{5}\z/, name)
+ s0 = s
+ Socket.unix(name) {|c|
+ sock, = s.accept
+ begin
+ assert_equal(name, c.remote_address.unix_path)
+ ensure
+ sock.close
+ end
+ }
+ }
+ assert(s0.closed?)
+ end
+
+end if defined?(UNIXSocket) && /cygwin/ !~ RUBY_PLATFORM
diff --git a/jni/ruby/test/stringio/test_stringio.rb b/jni/ruby/test/stringio/test_stringio.rb
new file mode 100644
index 0000000..07d89d1
--- /dev/null
+++ b/jni/ruby/test/stringio/test_stringio.rb
@@ -0,0 +1,589 @@
+require 'test/unit'
+require 'stringio'
+require_relative '../ruby/ut_eof'
+
+class TestStringIO < Test::Unit::TestCase
+ include TestEOF
+ def open_file(content)
+ f = StringIO.new(content)
+ yield f
+ end
+ alias open_file_rw open_file
+
+ include TestEOF::Seek
+
+ def test_truncate
+ io = StringIO.new("")
+ io.puts "abc"
+ io.truncate(0)
+ io.puts "def"
+ assert_equal("\0\0\0\0def\n", io.string, "[ruby-dev:24190]")
+ assert_raise(Errno::EINVAL) { io.truncate(-1) }
+ io.truncate(10)
+ assert_equal("\0\0\0\0def\n\0\0", io.string)
+ end
+
+ def test_seek_beyond_eof
+ io = StringIO.new
+ n = 100
+ io.seek(n)
+ io.print "last"
+ assert_equal("\0" * n + "last", io.string, "[ruby-dev:24194]")
+ end
+
+ def test_overwrite
+ stringio = StringIO.new
+ responses = ['', 'just another ruby', 'hacker']
+ responses.each do |resp|
+ stringio.puts(resp)
+ stringio.rewind
+ end
+ assert_equal("hacker\nother ruby\n", stringio.string, "[ruby-core:3836]")
+ end
+
+ def test_gets
+ assert_equal(nil, StringIO.new("").gets)
+ assert_equal("\n", StringIO.new("\n").gets)
+ assert_equal("a\n", StringIO.new("a\n").gets)
+ assert_equal("a\n", StringIO.new("a\nb\n").gets)
+ assert_equal("a", StringIO.new("a").gets)
+ assert_equal("a\n", StringIO.new("a\nb").gets)
+ assert_equal("abc\n", StringIO.new("abc\n\ndef\n").gets)
+ assert_equal("abc\n\ndef\n", StringIO.new("abc\n\ndef\n").gets(nil))
+ assert_equal("abc\n\n", StringIO.new("abc\n\ndef\n").gets(""))
+ assert_raise(TypeError){StringIO.new("").gets(1, 1)}
+ assert_nothing_raised {StringIO.new("").gets(nil, nil)}
+ end
+
+ def test_readlines
+ assert_equal([], StringIO.new("").readlines)
+ assert_equal(["\n"], StringIO.new("\n").readlines)
+ assert_equal(["a\n"], StringIO.new("a\n").readlines)
+ assert_equal(["a\n", "b\n"], StringIO.new("a\nb\n").readlines)
+ assert_equal(["a"], StringIO.new("a").readlines)
+ assert_equal(["a\n", "b"], StringIO.new("a\nb").readlines)
+ assert_equal(["abc\n", "\n", "def\n"], StringIO.new("abc\n\ndef\n").readlines)
+ assert_equal(["abc\n\ndef\n"], StringIO.new("abc\n\ndef\n").readlines(nil), "[ruby-dev:34591]")
+ assert_equal(["abc\n\n", "def\n"], StringIO.new("abc\n\ndef\n").readlines(""))
+ end
+
+ def test_write
+ s = ""
+ f = StringIO.new(s, "w")
+ f.print("foo")
+ f.close
+ assert_equal("foo", s)
+
+ f = StringIO.new(s, File::WRONLY)
+ f.print("bar")
+ f.close
+ assert_equal("bar", s)
+
+ f = StringIO.new(s, "a")
+ o = Object.new
+ def o.to_s; "baz"; end
+ f.print(o)
+ f.close
+ assert_equal("barbaz", s)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_write_nonblock_no_exceptions
+ s = ""
+ f = StringIO.new(s, "w")
+ f.write_nonblock("foo", exception: false)
+ f.close
+ assert_equal("foo", s)
+ end
+
+ def test_write_nonblock
+ s = ""
+ f = StringIO.new(s, "w")
+ f.write_nonblock("foo")
+ f.close
+ assert_equal("foo", s)
+
+ f = StringIO.new(s, File::WRONLY)
+ f.write_nonblock("bar")
+ f.close
+ assert_equal("bar", s)
+
+ f = StringIO.new(s, "a")
+ o = Object.new
+ def o.to_s; "baz"; end
+ f.write_nonblock(o)
+ f.close
+ assert_equal("barbaz", s)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_write_infection
+ bug9769 = '[ruby-dev:48118] [Bug #9769]'
+ s = "".untaint
+ f = StringIO.new(s, "w")
+ f.print("bar".taint)
+ f.close
+ assert_predicate(s, :tainted?, bug9769)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_write_encoding
+ s = "".force_encoding(Encoding::UTF_8)
+ f = StringIO.new(s)
+ f.print("\u{3053 3093 306b 3061 306f ff01}".b)
+ assert_equal(Encoding::UTF_8, s.encoding, "honor the original encoding over ASCII-8BIT")
+ end
+
+ def test_set_encoding
+ bug10285 = '[ruby-core:65240] [Bug #10285]'
+ f = StringIO.new()
+ f.set_encoding(Encoding::ASCII_8BIT)
+ f.write("quz \x83 mat".b)
+ s = "foo \x97 bar".force_encoding(Encoding::WINDOWS_1252)
+ assert_nothing_raised(Encoding::CompatibilityError, bug10285) {
+ f.write(s)
+ }
+ assert_equal(Encoding::ASCII_8BIT, f.string.encoding, bug10285)
+ end
+
+ def test_mode_error
+ f = StringIO.new("", "r")
+ assert_raise(IOError) { f.write("foo") }
+
+ f = StringIO.new("", "w")
+ assert_raise(IOError) { f.read }
+
+ assert_raise(Errno::EACCES) { StringIO.new("".freeze, "w") }
+ s = ""
+ f = StringIO.new(s, "w")
+ s.freeze
+ assert_raise(IOError) { f.write("foo") }
+
+ assert_raise(IOError) { StringIO.allocate.read }
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_open
+ s = ""
+ StringIO.open("foo") {|f| s = f.read }
+ assert_equal("foo", s)
+ end
+
+ def test_isatty
+ assert_equal(false, StringIO.new("").isatty)
+ end
+
+ def test_fsync
+ assert_equal(0, StringIO.new("").fsync)
+ end
+
+ def test_sync
+ assert_equal(true, StringIO.new("").sync)
+ assert_equal(false, StringIO.new("").sync = false)
+ end
+
+ def test_set_fcntl
+ assert_raise(NotImplementedError) { StringIO.new("").fcntl }
+ end
+
+ def test_close
+ f = StringIO.new("")
+ f.close
+ assert_raise(IOError) { f.close }
+
+ f = StringIO.new("")
+ f.close_read
+ f.close_write
+ assert_raise(IOError) { f.close }
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_close_read
+ f = StringIO.new("")
+ f.close_read
+ assert_raise(IOError) { f.read }
+ assert_raise(IOError) { f.close_read }
+ f.close
+
+ f = StringIO.new("", "w")
+ assert_raise(IOError) { f.close_read }
+ f.close
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_close_write
+ f = StringIO.new("")
+ f.close_write
+ assert_raise(IOError) { f.write("foo") }
+ assert_raise(IOError) { f.close_write }
+ f.close
+
+ f = StringIO.new("", "r")
+ assert_raise(IOError) { f.close_write }
+ f.close
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_closed
+ f = StringIO.new("")
+ assert_equal(false, f.closed?)
+ f.close
+ assert_equal(true, f.closed?)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_closed_read
+ f = StringIO.new("")
+ assert_equal(false, f.closed_read?)
+ f.close_write
+ assert_equal(false, f.closed_read?)
+ f.close_read
+ assert_equal(true, f.closed_read?)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_closed_write
+ f = StringIO.new("")
+ assert_equal(false, f.closed_write?)
+ f.close_read
+ assert_equal(false, f.closed_write?)
+ f.close_write
+ assert_equal(true, f.closed_write?)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_dup
+ f1 = StringIO.new("1234")
+ assert_equal("1", f1.getc)
+ f2 = f1.dup
+ assert_equal("2", f2.getc)
+ assert_equal("3", f1.getc)
+ assert_equal("4", f2.getc)
+ assert_equal(nil, f1.getc)
+ assert_equal(true, f2.eof?)
+ f1.close
+ assert_equal(false, f2.closed?, '[ruby-core:48443]')
+ ensure
+ f1.close unless f1.closed?
+ f2.close unless f2.closed?
+ end
+
+ def test_lineno
+ f = StringIO.new("foo\nbar\nbaz\n")
+ assert_equal([0, "foo\n"], [f.lineno, f.gets])
+ assert_equal([1, "bar\n"], [f.lineno, f.gets])
+ f.lineno = 1000
+ assert_equal([1000, "baz\n"], [f.lineno, f.gets])
+ assert_equal([1001, nil], [f.lineno, f.gets])
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_pos
+ f = StringIO.new("foo\nbar\nbaz\n")
+ assert_equal([0, "foo\n"], [f.pos, f.gets])
+ assert_equal([4, "bar\n"], [f.pos, f.gets])
+ assert_raise(Errno::EINVAL) { f.pos = -1 }
+ f.pos = 1
+ assert_equal([1, "oo\n"], [f.pos, f.gets])
+ assert_equal([4, "bar\n"], [f.pos, f.gets])
+ assert_equal([8, "baz\n"], [f.pos, f.gets])
+ assert_equal([12, nil], [f.pos, f.gets])
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_reopen
+ f = StringIO.new("foo\nbar\nbaz\n")
+ assert_equal("foo\n", f.gets)
+ f.reopen("qux\nquux\nquuux\n")
+ assert_equal("qux\n", f.gets)
+
+ f2 = StringIO.new("")
+ f2.reopen(f)
+ assert_equal("quux\n", f2.gets)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_seek
+ f = StringIO.new("1234")
+ assert_raise(Errno::EINVAL) { f.seek(-1) }
+ f.seek(-1, 2)
+ assert_equal("4", f.getc)
+ assert_raise(Errno::EINVAL) { f.seek(1, 3) }
+ f.close
+ assert_raise(IOError) { f.seek(0) }
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_each_byte
+ f = StringIO.new("1234")
+ a = []
+ f.each_byte {|c| a << c }
+ assert_equal(%w(1 2 3 4).map {|c| c.ord }, a)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_getbyte
+ f = StringIO.new("1234")
+ assert_equal("1".ord, f.getbyte)
+ assert_equal("2".ord, f.getbyte)
+ assert_equal("3".ord, f.getbyte)
+ assert_equal("4".ord, f.getbyte)
+ assert_equal(nil, f.getbyte)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_ungetbyte
+ s = "foo\nbar\n"
+ t = StringIO.new(s, "r")
+ t.ungetbyte(0x41)
+ assert_equal(0x41, t.getbyte)
+ t.ungetbyte("qux")
+ assert_equal("quxfoo\n", t.gets)
+ t.set_encoding("utf-8")
+ t.ungetbyte(0x89)
+ t.ungetbyte(0x8e)
+ t.ungetbyte("\xe7")
+ t.ungetbyte("\xe7\xb4\x85")
+ assert_equal("\u7d05\u7389bar\n", t.gets)
+ end
+
+ def test_ungetc
+ s = "1234"
+ f = StringIO.new(s, "r")
+ assert_nothing_raised { f.ungetc("x") }
+ assert_equal("x", f.getc) # bug? -> it's a feature from 1.9.
+ assert_equal("1", f.getc)
+
+ s = "1234"
+ f = StringIO.new(s, "r")
+ assert_equal("1", f.getc)
+ f.ungetc("y".ord)
+ assert_equal("y", f.getc)
+ assert_equal("2", f.getc)
+ ensure
+ f.close unless f.closed?
+ end
+
+ def test_readchar
+ f = StringIO.new("1234")
+ a = ""
+ assert_raise(EOFError) { loop { a << f.readchar } }
+ assert_equal("1234", a)
+ end
+
+ def test_readbyte
+ f = StringIO.new("1234")
+ a = []
+ assert_raise(EOFError) { loop { a << f.readbyte } }
+ assert_equal("1234".unpack("C*"), a)
+ end
+
+ def test_each_char
+ f = StringIO.new("1234")
+ assert_equal(%w(1 2 3 4), f.each_char.to_a)
+ end
+
+ def test_each_codepoint
+ f = StringIO.new("1234")
+ assert_equal([49, 50, 51, 52], f.each_codepoint.to_a)
+ end
+
+ def test_gets2
+ f = StringIO.new("foo\nbar\nbaz\n")
+ assert_equal("fo", f.gets(2))
+
+ o = Object.new
+ def o.to_str; "z"; end
+ assert_equal("o\nbar\nbaz", f.gets(o))
+
+ f = StringIO.new("foo\nbar\nbaz\n")
+ assert_equal("foo\nbar\nbaz", f.gets("az"))
+ f = StringIO.new("a" * 10000 + "zz!")
+ assert_equal("a" * 10000 + "zz", f.gets("zz"))
+ f = StringIO.new("a" * 10000 + "zz!")
+ assert_equal("a" * 10000 + "zz!", f.gets("zzz"))
+
+ bug4112 = '[ruby-dev:42674]'
+ ["a".encode("utf-16be"), "\u3042"].each do |s|
+ assert_equal(s, StringIO.new(s).gets(1), bug4112)
+ assert_equal(s, StringIO.new(s).gets(nil, 1), bug4112)
+ end
+ end
+
+ def test_each
+ f = StringIO.new("foo\nbar\nbaz\n")
+ assert_equal(["foo\n", "bar\n", "baz\n"], f.each.to_a)
+ end
+
+ def test_putc
+ s = ""
+ f = StringIO.new(s, "w")
+ f.putc("1")
+ f.putc("2")
+ f.putc("3")
+ f.close
+ assert_equal("123", s)
+
+ s = "foo"
+ f = StringIO.new(s, "a")
+ f.putc("1")
+ f.putc("2")
+ f.putc("3")
+ f.close
+ assert_equal("foo123", s)
+ end
+
+ def test_putc_nonascii
+ s = ""
+ f = StringIO.new(s, "w")
+ f.putc("\u{3042}")
+ f.putc(0x3044)
+ f.close
+ assert_equal("\u{3042}D", s)
+
+ s = "foo"
+ f = StringIO.new(s, "a")
+ f.putc("\u{3042}")
+ f.putc(0x3044)
+ f.close
+ assert_equal("foo\u{3042}D", s)
+ end
+
+ def test_read
+ f = StringIO.new("\u3042\u3044")
+ assert_raise(ArgumentError) { f.read(-1) }
+ assert_raise(ArgumentError) { f.read(1, 2, 3) }
+ assert_equal("\u3042\u3044", f.read)
+ f.rewind
+ assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read(f.size))
+
+ bug5207 = '[ruby-core:39026]'
+ f.rewind
+ assert_equal("\u3042\u3044", f.read(nil, nil), bug5207)
+ f.rewind
+ s = ""
+ f.read(nil, s)
+ assert_equal("\u3042\u3044", s, bug5207)
+ f.rewind
+ # not empty buffer
+ s = "0123456789"
+ f.read(nil, s)
+ assert_equal("\u3042\u3044", s)
+ end
+
+ def test_readpartial
+ f = StringIO.new("\u3042\u3044")
+ assert_raise(ArgumentError) { f.readpartial(-1) }
+ assert_raise(ArgumentError) { f.readpartial(1, 2, 3) }
+ assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(100))
+ f.rewind
+ assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(f.size))
+ f.rewind
+ # not empty buffer
+ s = '0123456789'
+ assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(f.size, s))
+ end
+
+ def test_read_nonblock
+ f = StringIO.new("\u3042\u3044")
+ assert_raise(ArgumentError) { f.read_nonblock(-1) }
+ assert_raise(ArgumentError) { f.read_nonblock(1, 2, 3) }
+ assert_equal("\u3042\u3044".force_encoding("BINARY"), f.read_nonblock(100))
+ assert_raise(EOFError) { f.read_nonblock(10) }
+ f.rewind
+ assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size))
+ end
+
+ def test_read_nonblock_no_exceptions
+ f = StringIO.new("\u3042\u3044")
+ assert_raise(ArgumentError) { f.read_nonblock(-1, exception: false) }
+ assert_raise(ArgumentError) { f.read_nonblock(1, 2, 3, exception: false) }
+ assert_raise(ArgumentError) { f.read_nonblock }
+ assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(100, exception: false))
+ assert_equal(nil, f.read_nonblock(10, exception: false))
+ f.rewind
+ assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size))
+ f.rewind
+ # not empty buffer
+ s = '0123456789'
+ assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size, s))
+ end
+
+ def test_size
+ f = StringIO.new("1234")
+ assert_equal(4, f.size)
+ end
+
+ # This test is should in ruby/test_method.rb
+ # However this test depends on stringio library,
+ # we write it here.
+ class C < StringIO
+ alias old_init initialize
+ attr_reader :foo
+ def initialize
+ @foo = :ok
+ old_init
+ end
+ end
+
+ def test_method
+ assert_equal(:ok, C.new.foo, 'Bug #632 [ruby-core:19282]')
+ end
+
+ def test_ungetc_pos
+ b = '\\b00010001 \\B00010001 \\b1 \\B1 \\b000100011'
+ s = StringIO.new( b )
+ expected_pos = 0
+ while n = s.getc
+ assert_equal( expected_pos + 1, s.pos )
+
+ s.ungetc( n )
+ assert_equal( expected_pos, s.pos )
+ assert_equal( n, s.getc )
+
+ expected_pos += 1
+ end
+ end
+
+ def test_frozen
+ s = StringIO.new
+ s.freeze
+ bug = '[ruby-core:33648]'
+ assert_raise(RuntimeError, bug) {s.puts("foo")}
+ assert_raise(RuntimeError, bug) {s.string = "foo"}
+ assert_raise(RuntimeError, bug) {s.reopen("")}
+ end
+
+ def test_frozen_string
+ s = StringIO.new("".freeze)
+ bug = '[ruby-core:48530]'
+ assert_raise(IOError, bug) {s.write("foo")}
+ assert_raise(IOError, bug) {s.ungetc("a")}
+ assert_raise(IOError, bug) {s.ungetbyte("a")}
+ end
+
+ def test_readlines_limit_0
+ assert_raise(ArgumentError, "[ruby-dev:43392]") { StringIO.new.readlines(0) }
+ end
+
+ def test_each_line_limit_0
+ assert_raise(ArgumentError, "[ruby-dev:43392]") { StringIO.new.each_line(0){} }
+ assert_raise(ArgumentError, "[ruby-dev:43392]") { StringIO.new.each_line("a",0){} }
+ end
+end
diff --git a/jni/ruby/test/strscan/test_stringscanner.rb b/jni/ruby/test/strscan/test_stringscanner.rb
new file mode 100644
index 0000000..260ab6c
--- /dev/null
+++ b/jni/ruby/test/strscan/test_stringscanner.rb
@@ -0,0 +1,720 @@
+# -*- coding: utf-8 -*-
+#
+# test/strscan/test_stringscanner.rb
+#
+
+require 'strscan'
+require 'test/unit'
+
+class TestStringScanner < Test::Unit::TestCase
+ def test_s_new
+ s = StringScanner.new('test string')
+ assert_instance_of StringScanner, s
+ assert_equal false, s.eos?
+ assert_equal false, s.tainted?
+
+ str = 'test string'
+ str.taint
+ s = StringScanner.new(str, false)
+ assert_instance_of StringScanner, s
+ assert_equal false, s.eos?
+ assert_same str, s.string
+ assert_equal true, s.string.tainted?
+
+ str = 'test string'
+ str.taint
+ s = StringScanner.new(str)
+ assert_equal true, s.string.tainted?
+ end
+
+ UNINIT_ERROR = ArgumentError
+
+ def test_s_allocate
+ s = StringScanner.allocate
+ assert_equal '#<StringScanner (uninitialized)>', s.inspect.sub(/StringScanner_C/, 'StringScanner')
+ assert_raise(UNINIT_ERROR) { s.eos? }
+ assert_raise(UNINIT_ERROR) { s.scan(/a/) }
+ s.string = 'test'
+ assert_equal '#<StringScanner 0/4 @ "test">', s.inspect.sub(/StringScanner_C/, 'StringScanner')
+ assert_nothing_raised(UNINIT_ERROR) { s.eos? }
+ assert_equal false, s.eos?
+ end
+
+ def test_s_mustc
+ assert_nothing_raised(NotImplementedError) {
+ StringScanner.must_C_version
+ }
+ end
+
+ def test_dup
+ s = StringScanner.new('test string')
+ d = s.dup
+ assert_equal s.inspect, d.inspect
+ assert_equal s.string, d.string
+ assert_equal s.pos, d.pos
+ assert_equal s.matched?, d.matched?
+ assert_equal s.eos?, d.eos?
+
+ s = StringScanner.new('test string')
+ s.scan(/test/)
+ d = s.dup
+ assert_equal s.inspect, d.inspect
+ assert_equal s.string, d.string
+ assert_equal s.pos, d.pos
+ assert_equal s.matched?, d.matched?
+ assert_equal s.eos?, d.eos?
+
+ s = StringScanner.new('test string')
+ s.scan(/test/)
+ s.scan(/NOT MATCH/)
+ d = s.dup
+ assert_equal s.inspect, d.inspect
+ assert_equal s.string, d.string
+ assert_equal s.pos, d.pos
+ assert_equal s.matched?, d.matched?
+ assert_equal s.eos?, d.eos?
+
+ s = StringScanner.new('test string')
+ s.terminate
+ d = s.dup
+ assert_equal s.inspect, d.inspect
+ assert_equal s.string, d.string
+ assert_equal s.pos, d.pos
+ assert_equal s.matched?, d.matched?
+ assert_equal s.eos?, d.eos?
+ end
+
+ def test_const_Version
+ assert_instance_of String, StringScanner::Version
+ assert_equal true, StringScanner::Version.frozen?
+ end
+
+ def test_const_Id
+ assert_instance_of String, StringScanner::Id
+ assert_equal true, StringScanner::Id.frozen?
+ end
+
+ def test_inspect
+ str = 'test string'
+ str.taint
+ s = StringScanner.new(str, false)
+ assert_instance_of String, s.inspect
+ assert_equal s.inspect, s.inspect
+ assert_equal '#<StringScanner 0/11 @ "test ...">', s.inspect.sub(/StringScanner_C/, 'StringScanner')
+ s.get_byte
+ assert_equal '#<StringScanner 1/11 "t" @ "est s...">', s.inspect.sub(/StringScanner_C/, 'StringScanner')
+ assert_equal true, s.inspect.tainted?
+
+ s = StringScanner.new("\n")
+ assert_equal '#<StringScanner 0/1 @ "\n">', s.inspect
+ end
+
+ def test_eos?
+ s = StringScanner.new('test string')
+ assert_equal false, s.eos?
+ assert_equal false, s.eos?
+ s.scan(/\w+/)
+ assert_equal false, s.eos?
+ assert_equal false, s.eos?
+ s.scan(/\s+/)
+ s.scan(/\w+/)
+ assert_equal true, s.eos?
+ assert_equal true, s.eos?
+ s.scan(/\w+/)
+ assert_equal true, s.eos?
+
+ s = StringScanner.new('test')
+ s.scan(/te/)
+ s.string.replace ''
+ assert_equal true, s.eos?
+ end
+
+ def test_bol?
+ s = StringScanner.new("a\nbbb\n\ncccc\nddd\r\neee")
+ assert_equal true, s.bol?
+ assert_equal true, s.bol?
+ s.scan(/a/)
+ assert_equal false, s.bol?
+ assert_equal false, s.bol?
+ s.scan(/\n/)
+ assert_equal true, s.bol?
+ s.scan(/b/)
+ assert_equal false, s.bol?
+ s.scan(/b/)
+ assert_equal false, s.bol?
+ s.scan(/b/)
+ assert_equal false, s.bol?
+ s.scan(/\n/)
+ assert_equal true, s.bol?
+ s.unscan
+ assert_equal false, s.bol?
+ s.scan(/\n/)
+ s.scan(/\n/)
+ assert_equal true, s.bol?
+ s.scan(/c+\n/)
+ assert_equal true, s.bol?
+ s.scan(/d+\r\n/)
+ assert_equal true, s.bol?
+ s.scan(/e+/)
+ assert_equal false, s.bol?
+ end
+
+ def test_string
+ s = StringScanner.new('test')
+ assert_equal 'test', s.string
+ s.string = 'a'
+ assert_equal 'a', s.string
+ s.scan(/a/)
+ s.string = 'b'
+ assert_equal 0, s.pos
+ end
+
+ def test_string_set_is_equal
+ name = 'tenderlove'
+
+ s = StringScanner.new(name)
+ assert_equal name.object_id, s.string.object_id
+
+ s.string = name
+ assert_equal name.object_id, s.string.object_id
+ end
+
+ def test_string_append
+ s = StringScanner.new('tender')
+ s << 'love'
+ assert_equal 'tenderlove', s.string
+
+ s.string = 'tender'
+ s << 'love'
+ assert_equal 'tenderlove', s.string
+ end
+
+ def test_pos
+ s = StringScanner.new('test string')
+ assert_equal 0, s.pos
+ s.get_byte
+ assert_equal 1, s.pos
+ s.get_byte
+ assert_equal 2, s.pos
+ s.terminate
+ assert_equal 11, s.pos
+ end
+
+ def test_pos_unicode
+ s = StringScanner.new("abcädeföghi")
+ assert_equal 0, s.charpos
+ assert_equal "abcä", s.scan_until(/ä/)
+ assert_equal 4, s.charpos
+ assert_equal "defö", s.scan_until(/ö/)
+ assert_equal 8, s.charpos
+ s.terminate
+ assert_equal 11, s.charpos
+ end
+
+ def test_concat
+ s = StringScanner.new('a')
+ s.scan(/a/)
+ s.concat 'b'
+ assert_equal false, s.eos?
+ assert_equal 'b', s.scan(/b/)
+ assert_equal true, s.eos?
+ s.concat 'c'
+ assert_equal false, s.eos?
+ assert_equal 'c', s.scan(/c/)
+ assert_equal true, s.eos?
+ end
+
+ def test_scan
+ s = StringScanner.new('stra strb strc', true)
+ tmp = s.scan(/\w+/)
+ assert_equal 'stra', tmp
+ assert_equal false, tmp.tainted?
+
+ tmp = s.scan(/\s+/)
+ assert_equal ' ', tmp
+ assert_equal false, tmp.tainted?
+
+ assert_equal 'strb', s.scan(/\w+/)
+ assert_equal ' ', s.scan(/\s+/)
+
+ tmp = s.scan(/\w+/)
+ assert_equal 'strc', tmp
+ assert_equal false, tmp.tainted?
+
+ assert_nil s.scan(/\w+/)
+ assert_nil s.scan(/\w+/)
+
+
+ str = 'stra strb strc'
+ str.taint
+ s = StringScanner.new(str, false)
+ tmp = s.scan(/\w+/)
+ assert_equal 'stra', tmp
+ assert_equal true, tmp.tainted?
+
+ tmp = s.scan(/\s+/)
+ assert_equal ' ', tmp
+ assert_equal true, tmp.tainted?
+
+ assert_equal 'strb', s.scan(/\w+/)
+ assert_equal ' ', s.scan(/\s+/)
+
+ tmp = s.scan(/\w+/)
+ assert_equal 'strc', tmp
+ assert_equal true, tmp.tainted?
+
+ assert_nil s.scan(/\w+/)
+ assert_nil s.scan(/\w+/)
+
+ s = StringScanner.new('test')
+ s.scan(/te/)
+ # This assumes #string does not duplicate string,
+ # but it is implementation specific issue.
+ # DO NOT RELY ON THIS FEATURE.
+ s.string.replace ''
+ # unspecified: assert_equal 2, s.pos
+ assert_equal nil, s.scan(/test/)
+
+ # [ruby-bugs:4361]
+ s = StringScanner.new("")
+ assert_equal "", s.scan(//)
+ assert_equal "", s.scan(//)
+ end
+
+ def test_skip
+ s = StringScanner.new('stra strb strc', true)
+ assert_equal 4, s.skip(/\w+/)
+ assert_equal 1, s.skip(/\s+/)
+ assert_equal 4, s.skip(/\w+/)
+ assert_equal 1, s.skip(/\s+/)
+ assert_equal 4, s.skip(/\w+/)
+ assert_nil s.skip(/\w+/)
+ assert_nil s.skip(/\s+/)
+ assert_equal true, s.eos?
+
+ s = StringScanner.new('test')
+ s.scan(/te/)
+ s.string.replace ''
+ assert_equal nil, s.skip(/./)
+
+ # [ruby-bugs:4361]
+ s = StringScanner.new("")
+ assert_equal 0, s.skip(//)
+ assert_equal 0, s.skip(//)
+ end
+
+ def test_getch
+ s = StringScanner.new('abcde')
+ assert_equal 'a', s.getch
+ assert_equal 'b', s.getch
+ assert_equal 'c', s.getch
+ assert_equal 'd', s.getch
+ assert_equal 'e', s.getch
+ assert_nil s.getch
+
+ str = 'abc'
+ str.taint
+ s = StringScanner.new(str)
+ assert_equal true, s.getch.tainted?
+ assert_equal true, s.getch.tainted?
+ assert_equal true, s.getch.tainted?
+ assert_nil s.getch
+
+ s = StringScanner.new("\244\242".force_encoding("euc-jp"))
+ assert_equal "\244\242".force_encoding("euc-jp"), s.getch
+ assert_nil s.getch
+
+ s = StringScanner.new('test')
+ s.scan(/te/)
+ s.string.replace ''
+ assert_equal nil, s.getch
+ end
+
+ def test_get_byte
+ s = StringScanner.new('abcde')
+ assert_equal 'a', s.get_byte
+ assert_equal 'b', s.get_byte
+ assert_equal 'c', s.get_byte
+ assert_equal 'd', s.get_byte
+ assert_equal 'e', s.get_byte
+ assert_nil s.get_byte
+ assert_nil s.get_byte
+
+ str = 'abc'
+ str.taint
+ s = StringScanner.new(str)
+ assert_equal true, s.get_byte.tainted?
+ assert_equal true, s.get_byte.tainted?
+ assert_equal true, s.get_byte.tainted?
+ assert_nil s.get_byte
+
+ s = StringScanner.new("\244\242".force_encoding("euc-jp"))
+ assert_equal "\244".force_encoding("euc-jp"), s.get_byte
+ assert_equal "\242".force_encoding("euc-jp"), s.get_byte
+ assert_nil s.get_byte
+
+ s = StringScanner.new('test')
+ s.scan(/te/)
+ s.string.replace ''
+ assert_equal nil, s.get_byte
+ end
+
+ def test_matched
+ s = StringScanner.new('stra strb strc')
+ s.scan(/\w+/)
+ assert_equal 'stra', s.matched
+ assert_equal false, s.matched.tainted?
+ s.scan(/\s+/)
+ assert_equal ' ', s.matched
+ s.scan(/\w+/)
+ assert_equal 'strb', s.matched
+ s.scan(/\s+/)
+ assert_equal ' ', s.matched
+ s.scan(/\w+/)
+ assert_equal 'strc', s.matched
+ s.scan(/\w+/)
+ assert_nil s.matched
+ s.getch
+ assert_nil s.matched
+
+ s = StringScanner.new('stra strb strc')
+ s.getch
+ assert_equal 's', s.matched
+ assert_equal false, s.matched.tainted?
+ s.get_byte
+ assert_equal 't', s.matched
+ assert_equal 't', s.matched
+ assert_equal false, s.matched.tainted?
+
+ str = 'test'
+ str.taint
+ s = StringScanner.new(str)
+ s.scan(/\w+/)
+ assert_equal true, s.matched.tainted?
+ assert_equal true, s.matched.tainted?
+ end
+
+ def test_AREF
+ s = StringScanner.new('stra strb strc')
+
+ s.scan(/\w+/)
+ assert_nil s[-2]
+ assert_equal 'stra', s[-1]
+ assert_equal 'stra', s[0]
+ assert_nil s[1]
+ assert_raise(IndexError) { s[:c] }
+ assert_raise(IndexError) { s['c'] }
+
+ assert_equal false, s[-1].tainted?
+ assert_equal false, s[0].tainted?
+
+ s.skip(/\s+/)
+ assert_nil s[-2]
+ assert_equal ' ', s[-1]
+ assert_equal ' ', s[0]
+ assert_nil s[1]
+
+ s.scan(/(s)t(r)b/)
+ assert_nil s[-100]
+ assert_nil s[-4]
+ assert_equal 'strb', s[-3]
+ assert_equal 's', s[-2]
+ assert_equal 'r', s[-1]
+ assert_equal 'strb', s[0]
+ assert_equal 's', s[1]
+ assert_equal 'r', s[2]
+ assert_nil s[3]
+ assert_nil s[100]
+
+ s.scan(/\s+/)
+
+ s.getch
+ assert_nil s[-2]
+ assert_equal 's', s[-1]
+ assert_equal 's', s[0]
+ assert_nil s[1]
+
+ s.get_byte
+ assert_nil s[-2]
+ assert_equal 't', s[-1]
+ assert_equal 't', s[0]
+ assert_nil s[1]
+
+ s.scan(/.*/)
+ s.scan(/./)
+ assert_nil s[0]
+ assert_nil s[0]
+
+
+ s = StringScanner.new("\244\242".force_encoding("euc-jp"))
+ s.getch
+ assert_equal "\244\242".force_encoding("euc-jp"), s[0]
+
+ str = 'test'
+ str.taint
+ s = StringScanner.new(str)
+ s.scan(/(t)(e)(s)(t)/)
+ assert_equal true, s[0].tainted?
+ assert_equal true, s[1].tainted?
+ assert_equal true, s[2].tainted?
+ assert_equal true, s[3].tainted?
+ assert_equal true, s[4].tainted?
+
+ s = StringScanner.new("foo bar baz")
+ s.scan(/(?<a>\w+) (?<b>\w+) (\w+)/)
+ assert_equal 'foo', s[1]
+ assert_equal 'bar', s[2]
+ assert_nil s[3]
+ assert_equal 'foo', s[:a]
+ assert_equal 'bar', s[:b]
+ assert_raise(IndexError) { s[:c] }
+ assert_equal 'foo', s['a']
+ assert_equal 'bar', s['b']
+ assert_raise(IndexError) { s['c'] }
+ assert_raise_with_message(IndexError, /\u{30c6 30b9 30c8}/) { s["\u{30c6 30b9 30c8}"] }
+ end
+
+ def test_pre_match
+ s = StringScanner.new('a b c d e')
+ s.scan(/\w/)
+ assert_equal '', s.pre_match
+ assert_equal false, s.pre_match.tainted?
+ s.skip(/\s/)
+ assert_equal 'a', s.pre_match
+ assert_equal false, s.pre_match.tainted?
+ s.scan(/\w/)
+ assert_equal 'a ', s.pre_match
+ s.scan_until(/c/)
+ assert_equal 'a b ', s.pre_match
+ s.getch
+ assert_equal 'a b c', s.pre_match
+ s.get_byte
+ assert_equal 'a b c ', s.pre_match
+ s.get_byte
+ assert_equal 'a b c d', s.pre_match
+ s.scan(/never match/)
+ assert_nil s.pre_match
+
+ str = 'test string'
+ str.taint
+ s = StringScanner.new(str)
+ s.scan(/\w+/)
+ assert_equal true, s.pre_match.tainted?
+ s.scan(/\s+/)
+ assert_equal true, s.pre_match.tainted?
+ s.scan(/\w+/)
+ assert_equal true, s.pre_match.tainted?
+ end
+
+ def test_post_match
+ s = StringScanner.new('a b c d e')
+ s.scan(/\w/)
+ assert_equal ' b c d e', s.post_match
+ s.skip(/\s/)
+ assert_equal 'b c d e', s.post_match
+ s.scan(/\w/)
+ assert_equal ' c d e', s.post_match
+ s.scan_until(/c/)
+ assert_equal ' d e', s.post_match
+ s.getch
+ assert_equal 'd e', s.post_match
+ s.get_byte
+ assert_equal ' e', s.post_match
+ s.get_byte
+ assert_equal 'e', s.post_match
+ s.scan(/never match/)
+ assert_nil s.post_match
+ s.scan(/./)
+ assert_equal '', s.post_match
+ s.scan(/./)
+ assert_nil s.post_match
+
+ str = 'test string'
+ str.taint
+ s = StringScanner.new(str)
+ s.scan(/\w+/)
+ assert_equal true, s.post_match.tainted?
+ s.scan(/\s+/)
+ assert_equal true, s.post_match.tainted?
+ s.scan(/\w+/)
+ assert_equal true, s.post_match.tainted?
+ end
+
+ def test_terminate
+ s = StringScanner.new('ssss')
+ s.getch
+ s.terminate
+ assert_equal true, s.eos?
+ s.terminate
+ assert_equal true, s.eos?
+ end
+
+ def test_reset
+ s = StringScanner.new('ssss')
+ s.getch
+ s.reset
+ assert_equal 0, s.pos
+ s.scan(/\w+/)
+ s.reset
+ assert_equal 0, s.pos
+ s.reset
+ assert_equal 0, s.pos
+ end
+
+ def test_matched_size
+ s = StringScanner.new('test string')
+ assert_nil s.matched_size
+ s.scan(/test/)
+ assert_equal 4, s.matched_size
+ assert_equal 4, s.matched_size
+ s.scan(//)
+ assert_equal 0, s.matched_size
+ s.scan(/x/)
+ assert_nil s.matched_size
+ assert_nil s.matched_size
+ s.terminate
+ assert_nil s.matched_size
+
+ s = StringScanner.new('test string')
+ assert_nil s.matched_size
+ s.scan(/test/)
+ assert_equal 4, s.matched_size
+ s.terminate
+ assert_nil s.matched_size
+ end
+
+ def test_encoding
+ ss = StringScanner.new("\xA1\xA2".force_encoding("euc-jp"))
+ assert_equal(Encoding::EUC_JP, ss.scan(/./e).encoding)
+ end
+
+ def test_generic_regexp
+ ss = StringScanner.new("\xA1\xA2".force_encoding("euc-jp"))
+ t = ss.scan(/./)
+ assert_equal("\xa1\xa2".force_encoding("euc-jp"), t)
+ end
+
+ def test_set_pos
+ s = StringScanner.new("test string")
+ s.pos = 7
+ assert_equal("ring", s.rest)
+ end
+
+ def test_match_p
+ s = StringScanner.new("test string")
+ assert_equal(4, s.match?(/\w+/))
+ assert_equal(4, s.match?(/\w+/))
+ assert_equal(nil, s.match?(/\s+/))
+ end
+
+ def test_check
+ s = StringScanner.new("Foo Bar Baz")
+ assert_equal("Foo", s.check(/Foo/))
+ assert_equal(0, s.pos)
+ assert_equal("Foo", s.matched)
+ assert_equal(nil, s.check(/Bar/))
+ assert_equal(nil, s.matched)
+ end
+
+ def test_scan_full
+ s = StringScanner.new("Foo Bar Baz")
+ assert_equal(4, s.scan_full(/Foo /, false, false))
+ assert_equal(0, s.pos)
+ assert_equal(nil, s.scan_full(/Baz/, false, false))
+ assert_equal("Foo ", s.scan_full(/Foo /, false, true))
+ assert_equal(0, s.pos)
+ assert_equal(nil, s.scan_full(/Baz/, false, false))
+ assert_equal(4, s.scan_full(/Foo /, true, false))
+ assert_equal(4, s.pos)
+ assert_equal(nil, s.scan_full(/Baz /, false, false))
+ assert_equal("Bar ", s.scan_full(/Bar /, true, true))
+ assert_equal(8, s.pos)
+ assert_equal(nil, s.scan_full(/az/, false, false))
+ end
+
+ def test_exist_p
+ s = StringScanner.new("test string")
+ assert_equal(3, s.exist?(/s/))
+ assert_equal(0, s.pos)
+ s.scan(/test/)
+ assert_equal(2, s.exist?(/s/))
+ assert_equal(4, s.pos)
+ assert_equal(nil, s.exist?(/e/))
+ end
+
+ def test_skip_until
+ s = StringScanner.new("Foo Bar Baz")
+ assert_equal(3, s.skip_until(/Foo/))
+ assert_equal(3, s.pos)
+ assert_equal(4, s.skip_until(/Bar/))
+ assert_equal(7, s.pos)
+ assert_equal(nil, s.skip_until(/Qux/))
+ end
+
+ def test_check_until
+ s = StringScanner.new("Foo Bar Baz")
+ assert_equal("Foo", s.check_until(/Foo/))
+ assert_equal(0, s.pos)
+ assert_equal("Foo Bar", s.check_until(/Bar/))
+ assert_equal(0, s.pos)
+ assert_equal(nil, s.check_until(/Qux/))
+ end
+
+ def test_search_full
+ s = StringScanner.new("Foo Bar Baz")
+ assert_equal(8, s.search_full(/Bar /, false, false))
+ assert_equal(0, s.pos)
+ assert_equal("Foo Bar ", s.search_full(/Bar /, false, true))
+ assert_equal(0, s.pos)
+ assert_equal(8, s.search_full(/Bar /, true, false))
+ assert_equal(8, s.pos)
+ assert_equal("Baz", s.search_full(/az/, true, true))
+ assert_equal(11, s.pos)
+ end
+
+ def test_peek
+ s = StringScanner.new("test string")
+ assert_equal("test st", s.peek(7))
+ assert_equal("test st", s.peek(7))
+ s.scan(/test/)
+ assert_equal(" stri", s.peek(5))
+ assert_equal(" string", s.peek(10))
+ s.scan(/ string/)
+ assert_equal("", s.peek(10))
+ end
+
+ def test_unscan
+ s = StringScanner.new('test string')
+ assert_equal("test", s.scan(/\w+/))
+ s.unscan
+ assert_equal("te", s.scan(/../))
+ assert_equal(nil, s.scan(/\d/))
+ assert_raise(ScanError) { s.unscan }
+ end
+
+ def test_rest
+ s = StringScanner.new('test string')
+ assert_equal("test string", s.rest)
+ s.scan(/test/)
+ assert_equal(" string", s.rest)
+ s.scan(/ string/)
+ assert_equal("", s.rest)
+ s.scan(/ string/)
+ end
+
+ def test_rest_size
+ s = StringScanner.new('test string')
+ assert_equal(11, s.rest_size)
+ s.scan(/test/)
+ assert_equal(7, s.rest_size)
+ s.scan(/ string/)
+ assert_equal(0, s.rest_size)
+ s.scan(/ string/)
+ end
+
+ def test_inspect2
+ s = StringScanner.new('test string test')
+ s.scan(/test strin/)
+ assert_equal('#<StringScanner 10/16 "...strin" @ "g tes...">', s.inspect)
+ end
+end
diff --git a/jni/ruby/test/syslog/test_syslog_logger.rb b/jni/ruby/test/syslog/test_syslog_logger.rb
new file mode 100644
index 0000000..d1998a1
--- /dev/null
+++ b/jni/ruby/test/syslog/test_syslog_logger.rb
@@ -0,0 +1,572 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'tempfile'
+begin
+ require 'syslog/logger'
+rescue LoadError
+ # skip. see the bottom of this file.
+end
+
+# These tests ensure Syslog::Logger works like Logger
+
+class TestSyslogRootLogger < Test::Unit::TestCase
+
+ module MockSyslog
+
+ PRIMASK = Syslog::Level.constants.inject(0) { |mask, name| mask | Syslog::Level.const_get(name) }
+
+ LEVEL_LABEL_MAP = {
+ Syslog::LOG_ALERT => 'ALERT',
+ Syslog::LOG_ERR => 'ERR',
+ Syslog::LOG_WARNING => 'WARNING',
+ Syslog::LOG_NOTICE => 'NOTICE',
+ Syslog::LOG_INFO => 'INFO',
+ Syslog::LOG_DEBUG => 'DEBUG'
+ }
+
+ @facility = Syslog::LOG_USER
+
+ class << self
+
+ attr_reader :facility
+ attr_reader :line
+ attr_reader :program_name
+
+ def log(priority, format, *args)
+ level = priority & PRIMASK
+ @line = "<#{priority}> #{LEVEL_LABEL_MAP[level]} - #{format % args}"
+ end
+
+ def open(program_name)
+ @program_name = program_name
+ end
+
+ def reset
+ @line = ''
+ end
+
+ end
+ end
+
+ Syslog::Logger.syslog = MockSyslog
+
+ LEVEL_LABEL_MAP = {
+ Logger::DEBUG => 'DEBUG',
+ Logger::INFO => 'INFO',
+ Logger::WARN => 'WARN',
+ Logger::ERROR => 'ERROR',
+ Logger::FATAL => 'FATAL',
+ Logger::UNKNOWN => 'ANY',
+ }
+
+ def setup
+ @logger = Logger.new(nil)
+ end
+
+ class Log
+ attr_reader :line, :label, :datetime, :pid, :severity, :progname, :msg
+ def initialize(line)
+ @line = line
+ /\A(\w+), \[([^#]*)#(\d+)\]\s+(\w+) -- (\w*): ([\x0-\xff]*)/ =~ @line
+ @label, @datetime, @pid, @severity, @progname, @msg = $1, $2, $3, $4, $5, $6
+ end
+ end
+
+ def log_add(severity, msg, progname = nil, &block)
+ log(:add, severity, msg, progname, &block)
+ end
+
+ def log(msg_id, *arg, &block)
+ Log.new(log_raw(msg_id, *arg, &block))
+ end
+
+ def log_raw(msg_id, *arg, &block)
+ Tempfile.create(File.basename(__FILE__) + '.log') {|logdev|
+ @logger.instance_eval { @logdev = Logger::LogDevice.new(logdev) }
+ assert_equal true, @logger.__send__(msg_id, *arg, &block)
+ logdev.rewind
+ logdev.read
+ }
+ end
+
+ def test_initialize
+ assert_equal Logger::DEBUG, @logger.level
+ end
+
+ def test_custom_formatter
+ @logger.formatter = Class.new {
+ def call severity, time, progname, msg
+ "hi mom!"
+ end
+ }.new
+
+ assert_match(/hi mom!/, log_raw(:fatal, 'fatal level message'))
+ end
+
+ def test_add
+ msg = log_add nil, 'unknown level message' # nil == unknown
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ msg = log_add Logger::FATAL, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ msg = log_add Logger::ERROR, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ msg = log_add Logger::WARN, 'warn level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
+
+ msg = log_add Logger::INFO, 'info level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
+
+ msg = log_add Logger::DEBUG, 'debug level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::DEBUG], msg.severity
+ end
+
+ def test_add_level_unknown
+ @logger.level = Logger::UNKNOWN
+
+ msg = log_add nil, 'unknown level message' # nil == unknown
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ msg = log_add Logger::FATAL, 'fatal level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::ERROR, 'error level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::WARN, 'warn level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::INFO, 'info level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::DEBUG, 'debug level message'
+ assert_equal '', msg.line
+ end
+
+ def test_add_level_fatal
+ @logger.level = Logger::FATAL
+
+ msg = log_add nil, 'unknown level message' # nil == unknown
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ msg = log_add Logger::FATAL, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ msg = log_add Logger::ERROR, 'error level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::WARN, 'warn level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::INFO, 'info level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::DEBUG, 'debug level message'
+ assert_equal '', msg.line
+ end
+
+ def test_add_level_error
+ @logger.level = Logger::ERROR
+
+ msg = log_add nil, 'unknown level message' # nil == unknown
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ msg = log_add Logger::FATAL, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ msg = log_add Logger::ERROR, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ msg = log_add Logger::WARN, 'warn level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::INFO, 'info level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::DEBUG, 'debug level message'
+ assert_equal '', msg.line
+ end
+
+ def test_add_level_warn
+ @logger.level = Logger::WARN
+
+ msg = log_add nil, 'unknown level message' # nil == unknown
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ msg = log_add Logger::FATAL, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ msg = log_add Logger::ERROR, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ msg = log_add Logger::WARN, 'warn level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
+
+ msg = log_add Logger::INFO, 'info level message'
+ assert_equal '', msg.line
+
+ msg = log_add Logger::DEBUG, 'debug level message'
+ assert_equal '', msg.line
+ end
+
+ def test_add_level_info
+ @logger.level = Logger::INFO
+
+ msg = log_add nil, 'unknown level message' # nil == unknown
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ msg = log_add Logger::FATAL, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ msg = log_add Logger::ERROR, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ msg = log_add Logger::WARN, 'warn level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
+
+ msg = log_add Logger::INFO, 'info level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
+
+ msg = log_add Logger::DEBUG, 'debug level message'
+ assert_equal '', msg.line
+ end
+
+ def test_add_level_debug
+ @logger.level = Logger::DEBUG
+
+ msg = log_add nil, 'unknown level message' # nil == unknown
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ msg = log_add Logger::FATAL, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ msg = log_add Logger::ERROR, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ msg = log_add Logger::WARN, 'warn level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
+
+ msg = log_add Logger::INFO, 'info level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
+
+ msg = log_add Logger::DEBUG, 'debug level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::DEBUG], msg.severity
+ end
+
+ def test_unknown
+ msg = log :unknown, 'unknown level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ @logger.level = Logger::UNKNOWN
+ msg = log :unknown, 'unknown level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ @logger.level = Logger::FATAL
+ msg = log :unknown, 'unknown level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ @logger.level = Logger::ERROR
+ msg = log :unknown, 'unknown level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ @logger.level = Logger::WARN
+ msg = log :unknown, 'unknown level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ @logger.level = Logger::INFO
+ msg = log :unknown, 'unknown level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+
+ @logger.level = Logger::DEBUG
+ msg = log :unknown, 'unknown level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::UNKNOWN], msg.severity
+ end
+
+ def test_fatal
+ msg = log :fatal, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ @logger.level = Logger::UNKNOWN
+ msg = log :fatal, 'fatal level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::FATAL
+ msg = log :fatal, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ @logger.level = Logger::ERROR
+ msg = log :fatal, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ @logger.level = Logger::WARN
+ msg = log :fatal, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ @logger.level = Logger::INFO
+ msg = log :fatal, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+
+ @logger.level = Logger::DEBUG
+ msg = log :fatal, 'fatal level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::FATAL], msg.severity
+ end
+
+ def test_fatal_eh
+ @logger.level = Logger::FATAL
+ assert_equal true, @logger.fatal?
+
+ @logger.level = Logger::UNKNOWN
+ assert_equal false, @logger.fatal?
+ end
+
+ def test_error
+ msg = log :error, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ @logger.level = Logger::UNKNOWN
+ msg = log :error, 'error level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::FATAL
+ msg = log :error, 'error level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::ERROR
+ msg = log :error, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ @logger.level = Logger::WARN
+ msg = log :error, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ @logger.level = Logger::INFO
+ msg = log :error, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+
+ @logger.level = Logger::DEBUG
+ msg = log :error, 'error level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::ERROR], msg.severity
+ end
+
+ def test_error_eh
+ @logger.level = Logger::ERROR
+ assert_equal true, @logger.error?
+
+ @logger.level = Logger::FATAL
+ assert_equal false, @logger.error?
+ end
+
+ def test_warn
+ msg = log :warn, 'warn level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
+
+ @logger.level = Logger::UNKNOWN
+ msg = log :warn, 'warn level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::FATAL
+ msg = log :warn, 'warn level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::ERROR
+ msg = log :warn, 'warn level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::WARN
+ msg = log :warn, 'warn level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
+
+ @logger.level = Logger::INFO
+ msg = log :warn, 'warn level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
+
+ @logger.level = Logger::DEBUG
+ msg = log :warn, 'warn level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::WARN], msg.severity
+ end
+
+ def test_warn_eh
+ @logger.level = Logger::WARN
+ assert_equal true, @logger.warn?
+
+ @logger.level = Logger::ERROR
+ assert_equal false, @logger.warn?
+ end
+
+ def test_info
+ msg = log :info, 'info level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
+
+ @logger.level = Logger::UNKNOWN
+ msg = log :info, 'info level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::FATAL
+ msg = log :info, 'info level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::ERROR
+ msg = log :info, 'info level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::WARN
+ msg = log :info, 'info level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::INFO
+ msg = log :info, 'info level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
+
+ @logger.level = Logger::DEBUG
+ msg = log :info, 'info level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::INFO], msg.severity
+ end
+
+ def test_info_eh
+ @logger.level = Logger::INFO
+ assert_equal true, @logger.info?
+
+ @logger.level = Logger::WARN
+ assert_equal false, @logger.info?
+ end
+
+ def test_debug
+ msg = log :debug, 'debug level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::DEBUG], msg.severity
+
+ @logger.level = Logger::UNKNOWN
+ msg = log :debug, 'debug level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::FATAL
+ msg = log :debug, 'debug level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::ERROR
+ msg = log :debug, 'debug level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::WARN
+ msg = log :debug, 'debug level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::INFO
+ msg = log :debug, 'debug level message'
+ assert_equal '', msg.line
+
+ @logger.level = Logger::DEBUG
+ msg = log :debug, 'debug level message'
+ assert_equal LEVEL_LABEL_MAP[Logger::DEBUG], msg.severity
+ end
+
+ def test_debug_eh
+ @logger.level = Logger::DEBUG
+ assert_equal true, @logger.debug?
+
+ @logger.level = Logger::INFO
+ assert_equal false, @logger.debug?
+ end
+
+end if defined?(Syslog)
+
+class TestSyslogLogger < TestSyslogRootLogger
+
+ @facility = Syslog::LOG_USER
+
+ def facility
+ self.class.instance_variable_get("@facility")
+ end
+
+ def setup
+ super
+ @logger = Syslog::Logger.new
+ end
+
+ SEVERITY_MAP = {}.tap { |map|
+ level2severity = Syslog::Logger::LEVEL_MAP.invert
+
+ MockSyslog::LEVEL_LABEL_MAP.each { |level, name|
+ map[name] = TestSyslogRootLogger::LEVEL_LABEL_MAP[level2severity[level]]
+ }
+ }
+
+ class Log
+ attr_reader :line, :label, :datetime, :pid, :severity, :progname, :msg, :priority
+ def initialize(line)
+ @line = line
+ return unless /\A<(\d+)> (\w+) - (.*)\Z/ =~ @line
+ priority, severity, @msg = $1, $2, $3
+ @severity = SEVERITY_MAP[severity]
+ @priority = priority.to_i
+ end
+ end
+
+ def log_add(severity, msg, progname = nil, &block)
+ log(:add, severity, msg, progname, &block)
+ end
+
+ def log(msg_id, *arg, &block)
+ Log.new(log_raw(msg_id, *arg, &block))
+ end
+
+ def log_raw(msg_id, *arg, &block)
+ assert_equal true, @logger.__send__(msg_id, *arg, &block)
+ msg = MockSyslog.line
+ MockSyslog.reset
+ return msg
+ end
+
+ def test_unknown_eh
+ @logger.level = Logger::UNKNOWN
+ assert_equal true, @logger.unknown?
+
+ @logger.level = Logger::UNKNOWN + 1
+ assert_equal false, @logger.unknown?
+ end
+
+ def test_facility
+ assert_equal facility, @logger.facility
+ end
+
+ def test_priority
+ msg = log_add nil, 'unknown level message' # nil == unknown
+ assert_equal facility|Syslog::LOG_ALERT, msg.priority
+
+ msg = log_add Logger::FATAL, 'fatal level message'
+ assert_equal facility|Syslog::LOG_ERR, msg.priority
+
+ msg = log_add Logger::ERROR, 'error level message'
+ assert_equal facility|Syslog::LOG_WARNING, msg.priority
+
+ msg = log_add Logger::WARN, 'warn level message'
+ assert_equal facility|Syslog::LOG_NOTICE, msg.priority
+
+ msg = log_add Logger::INFO, 'info level message'
+ assert_equal facility|Syslog::LOG_INFO, msg.priority
+
+ msg = log_add Logger::DEBUG, 'debug level message'
+ assert_equal facility|Syslog::LOG_DEBUG, msg.priority
+ end
+
+end if defined?(Syslog)
+
+
+# Create test class for each available facility
+
+Syslog::Facility.constants.each do |facility_symb|
+
+ test_syslog_class = Class.new(TestSyslogLogger) do
+
+ @facility = Syslog.const_get(facility_symb)
+
+ def setup
+ super
+ @logger.facility = facility
+ end
+
+ end
+ Object.const_set("TestSyslogLogger_#{facility_symb}", test_syslog_class)
+
+end if defined?(Syslog)
diff --git a/jni/ruby/test/test_abbrev.rb b/jni/ruby/test/test_abbrev.rb
new file mode 100644
index 0000000..f342c59
--- /dev/null
+++ b/jni/ruby/test/test_abbrev.rb
@@ -0,0 +1,54 @@
+require 'test/unit'
+require 'abbrev'
+
+class TestAbbrev < Test::Unit::TestCase
+ def test_abbrev
+ words = %w[summer winter win ruby rules]
+
+ assert_equal({
+ "rub" => "ruby",
+ "ruby" => "ruby",
+ "rul" => "rules",
+ "rule" => "rules",
+ "rules" => "rules",
+ "s" => "summer",
+ "su" => "summer",
+ "sum" => "summer",
+ "summ" => "summer",
+ "summe" => "summer",
+ "summer" => "summer",
+ "win" => "win",
+ "wint" => "winter",
+ "winte" => "winter",
+ "winter" => "winter",
+ }, words.abbrev)
+
+ assert_equal({
+ "rub" => "ruby",
+ "ruby" => "ruby",
+ "rul" => "rules",
+ "rule" => "rules",
+ "rules" => "rules",
+ }, words.abbrev('ru'))
+
+ assert_equal words.abbrev, Abbrev.abbrev(words)
+ assert_equal words.abbrev('ru'), Abbrev.abbrev(words, 'ru')
+ end
+
+ def test_abbrev_lf
+ words = ["abc", "abc\nd", "de"]
+
+ assert_equal({
+ "abc" => "abc",
+ "abc\n" => "abc\nd",
+ "abc\nd" => "abc\nd",
+ "d" => "de",
+ "de" => "de",
+ }, words.abbrev)
+
+ assert_equal({
+ "d" => "de",
+ "de" => "de",
+ }, words.abbrev('d'))
+ end
+end
diff --git a/jni/ruby/test/test_cmath.rb b/jni/ruby/test/test_cmath.rb
new file mode 100644
index 0000000..1e1824a
--- /dev/null
+++ b/jni/ruby/test/test_cmath.rb
@@ -0,0 +1,16 @@
+require 'test/unit'
+require 'cmath'
+
+class TestCMath < Test::Unit::TestCase
+ def test_sqrt
+ assert_equal CMath.sqrt(1.0.i), CMath.sqrt(1.i), '[ruby-core:31672]'
+ end
+
+ def test_acos
+ assert_in_delta CMath.acos(Complex(3, 4)), Complex(0.9368124611557199,-2.305509031243477)
+ end
+
+ def test_cbrt_returns_principal_value_of_cube_root
+ assert_equal CMath.cbrt(-8), (-8)**(1.0/3), '#3676'
+ end
+end
diff --git a/jni/ruby/test/test_delegate.rb b/jni/ruby/test/test_delegate.rb
new file mode 100644
index 0000000..6270cc6
--- /dev/null
+++ b/jni/ruby/test/test_delegate.rb
@@ -0,0 +1,240 @@
+require 'test/unit'
+require 'delegate'
+
+class TestDelegateClass < Test::Unit::TestCase
+ module M
+ attr_reader :m
+ end
+
+ def test_extend
+ obj = DelegateClass(Array).new([])
+ obj.instance_eval { @m = :m }
+ obj.extend M
+ assert_equal(:m, obj.m, "[ruby-dev:33116]")
+ end
+
+ def test_systemcallerror_eq
+ e = SystemCallError.new(0)
+ assert((SimpleDelegator.new(e) == e) == (e == SimpleDelegator.new(e)), "[ruby-dev:34808]")
+ end
+
+ class Myclass < DelegateClass(Array);end
+
+ def test_delegateclass_class
+ myclass=Myclass.new([])
+ assert_equal(Myclass,myclass.class)
+ assert_equal(Myclass,myclass.dup.class,'[ruby-dev:40313]')
+ assert_equal(Myclass,myclass.clone.class,'[ruby-dev:40313]')
+ end
+
+ def test_simpledelegator_class
+ simple=SimpleDelegator.new([])
+ assert_equal(SimpleDelegator,simple.class)
+ assert_equal(SimpleDelegator,simple.dup.class)
+ assert_equal(SimpleDelegator,simple.clone.class)
+ end
+
+ class Object
+ def m
+ :o
+ end
+ private
+ def delegate_test_m
+ :o
+ end
+ end
+
+ class Foo
+ def m
+ :m
+ end
+ def delegate_test_m
+ :m
+ end
+ end
+
+ class Bar < DelegateClass(Foo)
+ end
+
+ def test_override
+ foo = Foo.new
+ foo2 = SimpleDelegator.new(foo)
+ bar = Bar.new(foo)
+ assert_equal(:o, Object.new.m)
+ assert_equal(:m, foo.m)
+ assert_equal(:m, foo2.m)
+ assert_equal(:m, bar.m)
+ bug = '[ruby-dev:39154]'
+ assert_equal(:m, foo2.send(:delegate_test_m), bug)
+ assert_equal(:m, bar.send(:delegate_test_m), bug)
+ end
+
+ class IV < DelegateClass(Integer)
+ attr_accessor :var
+
+ def initialize
+ @var = 1
+ super(0)
+ end
+ end
+
+ def test_marshal
+ bug1744 = '[ruby-core:24211]'
+ c = IV.new
+ assert_equal(1, c.var)
+ d = Marshal.load(Marshal.dump(c))
+ assert_equal(1, d.var, bug1744)
+ end
+
+ def test_copy_frozen
+ bug2679 = '[ruby-dev:40242]'
+ a = [42, :hello].freeze
+ d = SimpleDelegator.new(a)
+ assert_nothing_raised(bug2679) {d.dup[0] += 1}
+ assert_raise(RuntimeError) {d.clone[0] += 1}
+ d.freeze
+ assert(d.clone.frozen?)
+ assert(!d.dup.frozen?)
+ end
+
+ def test_frozen
+ d = SimpleDelegator.new([1, :foo])
+ d.freeze
+ assert_raise(RuntimeError, '[ruby-dev:40314]#1') {d.__setobj__("foo")}
+ assert_equal([1, :foo], d)
+ end
+
+ def test_instance_method
+ s = SimpleDelegator.new("foo")
+ m = s.method("upcase")
+ s.__setobj__([1,2,3])
+ assert_raise(NoMethodError, '[ruby-dev:40314]#3') {m.call}
+ end
+
+ def test_methods
+ s = SimpleDelegator.new("foo")
+ assert_equal([], s.methods(false))
+ def s.bar; end
+ assert_equal([:bar], s.methods(false))
+ end
+
+ class Foo
+ private
+ def delegate_test_private
+ :m
+ end
+ end
+
+ def test_private_method
+ foo = Foo.new
+ d = SimpleDelegator.new(foo)
+ assert_raise(NoMethodError) {foo.delegate_test_private}
+ assert_equal(:m, foo.send(:delegate_test_private))
+ assert_raise(NoMethodError, '[ruby-dev:40314]#4') {d.delegate_test_private}
+ assert_raise(NoMethodError, '[ruby-dev:40314]#5') {d.send(:delegate_test_private)}
+ end
+
+ def test_global_function
+ klass = Class.new do
+ def open
+ end
+ end
+ obj = klass.new
+ d = SimpleDelegator.new(obj)
+ assert_nothing_raised(ArgumentError) {obj.open}
+ assert_nothing_raised(ArgumentError) {d.open}
+ assert_nothing_raised(ArgumentError) {d.send(:open)}
+ end
+
+ def test_send_method_in_delegator
+ d = Class.new(SimpleDelegator) do
+ def foo
+ "foo"
+ end
+ end.new(Object.new)
+ assert_equal("foo", d.send(:foo))
+ end
+
+ def test_unset_simple_delegator
+ d = SimpleDelegator.allocate
+ assert_raise_with_message(ArgumentError, /not delegated/) {
+ d.__getobj__
+ }
+ end
+
+ def test_unset_delegate_class
+ d = IV.allocate
+ assert_raise_with_message(ArgumentError, /not delegated/) {
+ d.__getobj__
+ }
+ end
+
+ class Bug9155 < DelegateClass(Integer)
+ def initialize(value)
+ super(Integer(value))
+ end
+ end
+
+ def test_global_method_if_no_target
+ bug9155 = '[ruby-core:58572] [Bug #9155]'
+ x = assert_nothing_raised(ArgumentError, bug9155) {break Bug9155.new(1)}
+ assert_equal(1, x.to_i, bug9155)
+ end
+
+ class Bug9403
+ Name = '[ruby-core:59718] [Bug #9403]'
+ SD = SimpleDelegator.new(new)
+ class << SD
+ def method_name
+ __method__
+ end
+ def callee_name
+ __callee__
+ end
+ alias aliased_name callee_name
+ def dir_name
+ __dir__
+ end
+ end
+ dc = DelegateClass(self)
+ dc.class_eval do
+ def method_name
+ __method__
+ end
+ def callee_name
+ __callee__
+ end
+ alias aliased_name callee_name
+ def dir_name
+ __dir__
+ end
+ end
+ DC = dc.new(new)
+ end
+
+ def test_method_in_simple_delegator
+ assert_equal(:method_name, Bug9403::SD.method_name, Bug9403::Name)
+ end
+
+ def test_callee_in_simple_delegator
+ assert_equal(:callee_name, Bug9403::SD.callee_name, Bug9403::Name)
+ assert_equal(:aliased_name, Bug9403::SD.aliased_name, Bug9403::Name)
+ end
+
+ def test_dir_in_simple_delegator
+ assert_equal(__dir__, Bug9403::SD.dir_name, Bug9403::Name)
+ end
+
+ def test_method_in_delegator_class
+ assert_equal(:method_name, Bug9403::DC.method_name, Bug9403::Name)
+ end
+
+ def test_callee_in_delegator_class
+ assert_equal(:callee_name, Bug9403::DC.callee_name, Bug9403::Name)
+ assert_equal(:aliased_name, Bug9403::DC.aliased_name, Bug9403::Name)
+ end
+
+ def test_dir_in_delegator_class
+ assert_equal(__dir__, Bug9403::DC.dir_name, Bug9403::Name)
+ end
+end
diff --git a/jni/ruby/test/test_find.rb b/jni/ruby/test/test_find.rb
new file mode 100644
index 0000000..cb2ce56
--- /dev/null
+++ b/jni/ruby/test/test_find.rb
@@ -0,0 +1,301 @@
+require 'test/unit'
+require 'find'
+require 'tmpdir'
+
+class TestFind < Test::Unit::TestCase
+ def test_empty
+ Dir.mktmpdir {|d|
+ a = []
+ Find.find(d) {|f| a << f }
+ assert_equal([d], a)
+ }
+ end
+
+ def test_rec
+ Dir.mktmpdir {|d|
+ File.open("#{d}/a", "w"){}
+ Dir.mkdir("#{d}/b")
+ File.open("#{d}/b/a", "w"){}
+ File.open("#{d}/b/b", "w"){}
+ Dir.mkdir("#{d}/c")
+ a = []
+ Find.find(d) {|f| a << f }
+ assert_equal([d, "#{d}/a", "#{d}/b", "#{d}/b/a", "#{d}/b/b", "#{d}/c"], a)
+ }
+ end
+
+ def test_relative
+ Dir.mktmpdir {|d|
+ File.open("#{d}/a", "w"){}
+ Dir.mkdir("#{d}/b")
+ File.open("#{d}/b/a", "w"){}
+ File.open("#{d}/b/b", "w"){}
+ Dir.mkdir("#{d}/c")
+ a = []
+ Dir.chdir(d) {
+ Find.find(".") {|f| a << f }
+ }
+ assert_equal([".", "./a", "./b", "./b/a", "./b/b", "./c"], a)
+ }
+ end
+
+ def test_dont_follow_symlink
+ Dir.mktmpdir {|d|
+ File.open("#{d}/a", "w"){}
+ Dir.mkdir("#{d}/b")
+ File.open("#{d}/b/a", "w"){}
+ File.open("#{d}/b/b", "w"){}
+ begin
+ File.symlink("#{d}/b", "#{d}/c")
+ rescue NotImplementedError
+ skip "symlink is not supported."
+ end
+ a = []
+ Find.find(d) {|f| a << f }
+ assert_equal([d, "#{d}/a", "#{d}/b", "#{d}/b/a", "#{d}/b/b", "#{d}/c"], a)
+ }
+ end
+
+ def test_prune
+ Dir.mktmpdir {|d|
+ File.open("#{d}/a", "w"){}
+ Dir.mkdir("#{d}/b")
+ File.open("#{d}/b/a", "w"){}
+ File.open("#{d}/b/b", "w"){}
+ Dir.mkdir("#{d}/c")
+ a = []
+ Find.find(d) {|f|
+ a << f
+ Find.prune if f == "#{d}/b"
+ }
+ assert_equal([d, "#{d}/a", "#{d}/b", "#{d}/c"], a)
+ }
+ end
+
+ def test_countup3
+ Dir.mktmpdir {|d|
+ 1.upto(3) {|n| File.open("#{d}/#{n}", "w"){} }
+ a = []
+ Find.find(d) {|f| a << f }
+ assert_equal([d, "#{d}/1", "#{d}/2", "#{d}/3"], a)
+ }
+ end
+
+ def test_countdown3
+ Dir.mktmpdir {|d|
+ 3.downto(1) {|n| File.open("#{d}/#{n}", "w"){} }
+ a = []
+ Find.find(d) {|f| a << f }
+ assert_equal([d, "#{d}/1", "#{d}/2", "#{d}/3"], a)
+ }
+ end
+
+ def test_unreadable_dir
+ skip "no meaning test on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
+ Dir.mktmpdir {|d|
+ Dir.mkdir(dir = "#{d}/dir")
+ File.open("#{dir}/foo", "w"){}
+ begin
+ File.chmod(0300, dir)
+ a = []
+ Find.find(d) {|f| a << f }
+ assert_equal([d, dir], a)
+
+ a = []
+ Find.find(d, ignore_error: true) {|f| a << f }
+ assert_equal([d, dir], a)
+
+ a = []
+ Find.find(d, ignore_error: true).each {|f| a << f }
+ assert_equal([d, dir], a)
+
+ a = []
+ assert_raise_with_message(Errno::EACCES, /#{Regexp.quote(dir)}/) do
+ Find.find(d, ignore_error: false) {|f| a << f }
+ end
+ assert_equal([d, dir], a)
+
+ a = []
+ assert_raise_with_message(Errno::EACCES, /#{Regexp.quote(dir)}/) do
+ Find.find(d, ignore_error: false).each {|f| a << f }
+ end
+ assert_equal([d, dir], a)
+ ensure
+ File.chmod(0700, dir)
+ end
+ }
+ end
+
+ def test_unsearchable_dir
+ Dir.mktmpdir {|d|
+ Dir.mkdir(dir = "#{d}/dir")
+ File.open(file = "#{dir}/foo", "w"){}
+ begin
+ File.chmod(0600, dir)
+ a = []
+ Find.find(d) {|f| a << f }
+ assert_equal([d, dir, file], a)
+
+ a = []
+ Find.find(d, ignore_error: true) {|f| a << f }
+ assert_equal([d, dir, file], a)
+
+ a = []
+ Find.find(d, ignore_error: true).each {|f| a << f }
+ assert_equal([d, dir, file], a)
+
+ skip "no meaning test on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
+ a = []
+ assert_raise_with_message(Errno::EACCES, /#{Regexp.quote(file)}/) do
+ Find.find(d, ignore_error: false) {|f| a << f }
+ end
+ assert_equal([d, dir, file], a)
+
+ a = []
+ assert_raise_with_message(Errno::EACCES, /#{Regexp.quote(file)}/) do
+ Find.find(d, ignore_error: false).each {|f| a << f }
+ end
+ assert_equal([d, dir, file], a)
+
+ assert_raise(Errno::EACCES) { File.lstat(file) }
+ ensure
+ File.chmod(0700, dir)
+ end
+ }
+ end
+
+ def test_dangling_symlink
+ Dir.mktmpdir {|d|
+ begin
+ File.symlink("foo", "#{d}/bar")
+ rescue NotImplementedError
+ skip "symlink is not supported."
+ end
+ a = []
+ Find.find(d) {|f| a << f }
+ assert_equal([d, "#{d}/bar"], a)
+ assert_raise(Errno::ENOENT) { File.stat("#{d}/bar") }
+ }
+ end
+
+ def test_dangling_symlink_stat_error
+ Dir.mktmpdir {|d|
+ begin
+ File.symlink("foo", "#{d}/bar")
+ rescue NotImplementedError
+ skip "symlink is not supported."
+ end
+ assert_raise(Errno::ENOENT) {
+ Find.find(d) {|f| File.stat(f) }
+ }
+ }
+ end
+
+ def test_change_dir_to_file
+ Dir.mktmpdir {|d|
+ Dir.mkdir(dir_1 = "#{d}/d1")
+ File.open(file_a = "#{dir_1}/a", "w"){}
+ File.open(file_b = "#{dir_1}/b", "w"){}
+ File.open(file_c = "#{dir_1}/c", "w"){}
+ Dir.mkdir(dir_d = "#{dir_1}/d")
+ File.open("#{dir_d}/e", "w"){}
+ dir_2 = "#{d}/d2"
+ a = []
+ Find.find(d) {|f|
+ a << f
+ if f == file_b
+ File.rename(dir_1, dir_2)
+ File.open(dir_1, "w") {}
+ end
+ }
+ assert_equal([d, dir_1, file_a, file_b, file_c, dir_d], a)
+ }
+ end
+
+ def test_change_dir_to_symlink_loop
+ Dir.mktmpdir {|d|
+ Dir.mkdir(dir_1 = "#{d}/d1")
+ File.open(file_a = "#{dir_1}/a", "w"){}
+ File.open(file_b = "#{dir_1}/b", "w"){}
+ File.open(file_c = "#{dir_1}/c", "w"){}
+ Dir.mkdir(dir_d = "#{dir_1}/d")
+ File.open("#{dir_d}/e", "w"){}
+ dir_2 = "#{d}/d2"
+ a = []
+ Find.find(d) {|f|
+ a << f
+ if f == file_b
+ File.rename(dir_1, dir_2)
+ begin
+ File.symlink("d1", dir_1)
+ rescue NotImplementedError
+ skip "symlink is not supported."
+ end
+ end
+ }
+ assert_equal([d, dir_1, file_a, file_b, file_c, dir_d], a)
+ }
+ end
+
+ def test_enumerator
+ Dir.mktmpdir {|d|
+ File.open("#{d}/a", "w"){}
+ Dir.mkdir("#{d}/b")
+ File.open("#{d}/b/a", "w"){}
+ File.open("#{d}/b/b", "w"){}
+ Dir.mkdir("#{d}/c")
+ e = Find.find(d)
+ a = []
+ e.each {|f| a << f }
+ assert_equal([d, "#{d}/a", "#{d}/b", "#{d}/b/a", "#{d}/b/b", "#{d}/c"], a)
+ }
+ end
+
+ def test_encoding_ascii
+ Dir.mktmpdir {|d|
+ File.open("#{d}/a", "w"){}
+ Dir.mkdir("#{d}/b")
+ a = []
+ Find.find(d.encode(Encoding::US_ASCII)) {|f| a << f }
+ a.each do |i|
+ assert(Encoding.compatible?(d.encode(Encoding.find('filesystem')), i))
+ end
+ }
+ end
+
+ def test_encoding_non_ascii
+ Dir.mktmpdir {|d|
+ File.open("#{d}/a", "w"){}
+ Dir.mkdir("#{d}/b")
+ euc_jp = Encoding::EUC_JP
+ win_31j = Encoding::Windows_31J
+ utf_8 = Encoding::UTF_8
+ a = []
+ Find.find(d.encode(euc_jp), d.encode(win_31j), d.encode(utf_8)) {|f| a << [f, f.encoding] }
+ assert_equal([[d, euc_jp], ["#{d}/a", euc_jp], ["#{d}/b", euc_jp],
+ [d, win_31j], ["#{d}/a", win_31j], ["#{d}/b", win_31j],
+ [d, utf_8], ["#{d}/a", utf_8], ["#{d}/b", utf_8]],
+ a)
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ a = []
+ Dir.mkdir("#{d}/\u{2660}")
+ Find.find("#{d}".encode(utf_8)) {|f| a << [f, f.encoding] }
+ assert_equal([[d, utf_8], ["#{d}/a", utf_8], ["#{d}/b", utf_8], ["#{d}/\u{2660}", utf_8]], a)
+ end
+ }
+ end
+
+ class TestInclude < Test::Unit::TestCase
+ include Find
+
+ def test_functional_call
+ Dir.mktmpdir {|d|
+ File.open("#{d}/a", "w"){}
+ a = []
+ find(d) {|f| a << f }
+ assert_equal([d, "#{d}/a"], a)
+ }
+ end
+ end
+
+end
diff --git a/jni/ruby/test/test_ipaddr.rb b/jni/ruby/test/test_ipaddr.rb
new file mode 100644
index 0000000..5c52247
--- /dev/null
+++ b/jni/ruby/test/test_ipaddr.rb
@@ -0,0 +1,271 @@
+require 'test/unit'
+require 'ipaddr'
+
+class TC_IPAddr < Test::Unit::TestCase
+ def test_s_new
+ [
+ ["3FFE:505:ffff::/48"],
+ ["0:0:0:1::"],
+ ["2001:200:300::/48"],
+ ["2001:200:300::192.168.1.2/48"],
+ ["1:2:3:4:5:6:7::"],
+ ["::2:3:4:5:6:7:8"],
+ ].each { |args|
+ assert_nothing_raised {
+ IPAddr.new(*args)
+ }
+ }
+
+ a = IPAddr.new
+ assert_equal("::", a.to_s)
+ assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+
+ a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
+ assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
+ assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+
+ a = IPAddr.new("3ffe:505:2::/48")
+ assert_equal("3ffe:505:2::", a.to_s)
+ assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(false, a.ipv4?)
+ assert_equal(true, a.ipv6?)
+ assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
+
+ a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
+ assert_equal("3ffe:505:2::", a.to_s)
+ assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+
+ a = IPAddr.new("0.0.0.0")
+ assert_equal("0.0.0.0", a.to_s)
+ assert_equal("0.0.0.0", a.to_string)
+ assert_equal(Socket::AF_INET, a.family)
+
+ a = IPAddr.new("192.168.1.2")
+ assert_equal("192.168.1.2", a.to_s)
+ assert_equal("192.168.1.2", a.to_string)
+ assert_equal(Socket::AF_INET, a.family)
+ assert_equal(true, a.ipv4?)
+ assert_equal(false, a.ipv6?)
+
+ a = IPAddr.new("192.168.1.2/24")
+ assert_equal("192.168.1.0", a.to_s)
+ assert_equal("192.168.1.0", a.to_string)
+ assert_equal(Socket::AF_INET, a.family)
+ assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
+
+ a = IPAddr.new("192.168.1.2/255.255.255.0")
+ assert_equal("192.168.1.0", a.to_s)
+ assert_equal("192.168.1.0", a.to_string)
+ assert_equal(Socket::AF_INET, a.family)
+
+ assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
+ assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
+
+ assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
+ assert_equal("1:2:3:4:5:6:7:0", IPAddr.new("1:2:3:4:5:6:7::").to_s)
+ assert_equal("0:2:3:4:5:6:7:8", IPAddr.new("::2:3:4:5:6:7:8").to_s)
+
+ assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.256") }
+ assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.011") }
+ assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("fe80::1%fxp0") }
+ assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("[192.168.1.2]/120") }
+ assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("::1/255.255.255.0") }
+ assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("::1/129") }
+ assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("192.168.0.1/33") }
+ assert_raises(IPAddr::AddressFamilyError) { IPAddr.new(1) }
+ assert_raises(IPAddr::AddressFamilyError) { IPAddr.new("::ffff:192.168.1.2/120", Socket::AF_INET) }
+ end
+
+ def test_s_new_ntoh
+ addr = ''
+ IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c|
+ addr += sprintf("%02x", c)
+ }
+ assert_equal("123456789abcdef0123456789abcdef0", addr)
+ addr = ''
+ IPAddr.new("123.45.67.89").hton.each_byte { |c|
+ addr += sprintf("%02x", c)
+ }
+ assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr)
+ a = IPAddr.new("3ffe:505:2::")
+ assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s)
+ a = IPAddr.new("192.168.2.1")
+ assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s)
+ end
+
+ def test_ipv4_compat
+ a = IPAddr.new("::192.168.1.2")
+ assert_equal("::192.168.1.2", a.to_s)
+ assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(true, a.ipv4_compat?)
+ b = a.native
+ assert_equal("192.168.1.2", b.to_s)
+ assert_equal(Socket::AF_INET, b.family)
+ assert_equal(false, b.ipv4_compat?)
+
+ a = IPAddr.new("192.168.1.2")
+ b = a.ipv4_compat
+ assert_equal("::192.168.1.2", b.to_s)
+ assert_equal(Socket::AF_INET6, b.family)
+ end
+
+ def test_ipv4_mapped
+ a = IPAddr.new("::ffff:192.168.1.2")
+ assert_equal("::ffff:192.168.1.2", a.to_s)
+ assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(true, a.ipv4_mapped?)
+ b = a.native
+ assert_equal("192.168.1.2", b.to_s)
+ assert_equal(Socket::AF_INET, b.family)
+ assert_equal(false, b.ipv4_mapped?)
+
+ a = IPAddr.new("192.168.1.2")
+ b = a.ipv4_mapped
+ assert_equal("::ffff:192.168.1.2", b.to_s)
+ assert_equal(Socket::AF_INET6, b.family)
+ end
+
+ def test_reverse
+ assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").reverse)
+ assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse)
+ end
+
+ def test_ip6_arpa
+ assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").ip6_arpa)
+ assert_raises(IPAddr::InvalidAddressError) {
+ IPAddr.new("192.168.2.1").ip6_arpa
+ }
+ end
+
+ def test_ip6_int
+ assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int", IPAddr.new("3ffe:505:2::f").ip6_int)
+ assert_raises(IPAddr::InvalidAddressError) {
+ IPAddr.new("192.168.2.1").ip6_int
+ }
+ end
+
+ def test_to_s
+ assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
+ assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)
+ end
+end
+
+class TC_Operator < Test::Unit::TestCase
+
+ IN6MASK32 = "ffff:ffff::"
+ IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+
+ def setup
+ @in6_addr_any = IPAddr.new()
+ @a = IPAddr.new("3ffe:505:2::/48")
+ @b = IPAddr.new("0:0:0:1::")
+ @c = IPAddr.new(IN6MASK32)
+ end
+ alias set_up setup
+
+ def test_or
+ assert_equal("3ffe:505:2:1::", (@a | @b).to_s)
+ a = @a
+ a |= @b
+ assert_equal("3ffe:505:2:1::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ assert_equal("3ffe:505:2:1::",
+ (@a | 0x00000000000000010000000000000000).to_s)
+ end
+
+ def test_and
+ assert_equal("3ffe:505::", (@a & @c).to_s)
+ a = @a
+ a &= @c
+ assert_equal("3ffe:505::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s)
+ end
+
+ def test_shift_right
+ assert_equal("0:3ffe:505:2::", (@a >> 16).to_s)
+ a = @a
+ a >>= 16
+ assert_equal("0:3ffe:505:2::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ end
+
+ def test_shift_left
+ assert_equal("505:2::", (@a << 16).to_s)
+ a = @a
+ a <<= 16
+ assert_equal("505:2::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ end
+
+ def test_carrot
+ a = ~@in6_addr_any
+ assert_equal(IN6MASK128, a.to_s)
+ assert_equal("::", @in6_addr_any.to_s)
+ end
+
+ def test_equal
+ assert_equal(true, @a == IPAddr.new("3FFE:505:2::"))
+ assert_equal(true, @a == IPAddr.new("3ffe:0505:0002::"))
+ assert_equal(true, @a == IPAddr.new("3ffe:0505:0002:0:0:0:0:0"))
+ assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
+ assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
+ assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
+ end
+
+ def test_mask
+ a = @a.mask(32)
+ assert_equal("3ffe:505::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ end
+
+ def test_include?
+ assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::")))
+ assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1")))
+ assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::")))
+ net1 = IPAddr.new("192.168.2.0/24")
+ assert_equal(true, net1.include?(IPAddr.new("192.168.2.0")))
+ assert_equal(true, net1.include?(IPAddr.new("192.168.2.255")))
+ assert_equal(false, net1.include?(IPAddr.new("192.168.3.0")))
+ # test with integer parameter
+ int = (192 << 24) + (168 << 16) + (2 << 8) + 13
+
+ assert_equal(true, net1.include?(int))
+ assert_equal(false, net1.include?(int+255))
+
+ end
+
+ def test_hash
+ a1 = IPAddr.new('192.168.2.0')
+ a2 = IPAddr.new('192.168.2.0')
+ a3 = IPAddr.new('3ffe:505:2::1')
+ a4 = IPAddr.new('3ffe:505:2::1')
+ a5 = IPAddr.new('127.0.0.1')
+ a6 = IPAddr.new('::1')
+ a7 = IPAddr.new('192.168.2.0/25')
+ a8 = IPAddr.new('192.168.2.0/25')
+
+ h = { a1 => 'ipv4', a2 => 'ipv4', a3 => 'ipv6', a4 => 'ipv6', a5 => 'ipv4', a6 => 'ipv6', a7 => 'ipv4', a8 => 'ipv4'}
+ assert_equal(5, h.size)
+ assert_equal('ipv4', h[a1])
+ assert_equal('ipv4', h[a2])
+ assert_equal('ipv6', h[a3])
+ assert_equal('ipv6', h[a4])
+
+ require 'set'
+ s = Set[a1, a2, a3, a4, a5, a6, a7, a8]
+ assert_equal(5, s.size)
+ assert_equal(true, s.include?(a1))
+ assert_equal(true, s.include?(a2))
+ assert_equal(true, s.include?(a3))
+ assert_equal(true, s.include?(a4))
+ assert_equal(true, s.include?(a5))
+ assert_equal(true, s.include?(a6))
+ end
+end
diff --git a/jni/ruby/test/test_mathn.rb b/jni/ruby/test/test_mathn.rb
new file mode 100644
index 0000000..8a482f3
--- /dev/null
+++ b/jni/ruby/test/test_mathn.rb
@@ -0,0 +1,119 @@
+require 'test/unit'
+
+# mathn redefines too much. It must be isolated to child processes.
+class TestMathn < Test::Unit::TestCase
+ def test_power
+ stderr = $VERBOSE ? ["lib/mathn.rb is deprecated"] : []
+ assert_in_out_err ['-r', 'mathn', '-e', 'a=1**2;!a'], "", [], stderr, '[ruby-core:25740]'
+ assert_in_out_err ['-r', 'mathn', '-e', 'a=(1 << 126)**2;!a'], "", [], stderr, '[ruby-core:25740]'
+ assert_in_out_err ['-r', 'mathn/complex', '-e', 'a=Complex(0,1)**4;!a'], "", [], [], '[ruby-core:44170]'
+ assert_in_out_err ['-r', 'mathn/complex', '-e', 'a=Complex(0,1)**5;!a'], "", [], [], '[ruby-core:44170]'
+ end
+
+ def test_quo
+ stderr = $VERBOSE ? ["lib/mathn.rb is deprecated"] : []
+ assert_in_out_err ['-r', 'mathn'], <<-EOS, %w(OK), stderr, '[ruby-core:41575]'
+ 1.quo(2); puts :OK
+ EOS
+ end
+
+ def test_floor
+ assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
+ assert_equal( 2, ( 13/5).floor)
+ assert_equal( 2, ( 5/2).floor)
+ assert_equal( 2, ( 12/5).floor)
+ assert_equal(-3, (-12/5).floor)
+ assert_equal(-3, ( -5/2).floor)
+ assert_equal(-3, (-13/5).floor)
+
+ assert_equal( 2, ( 13/5).floor(0))
+ assert_equal( 2, ( 5/2).floor(0))
+ assert_equal( 2, ( 12/5).floor(0))
+ assert_equal(-3, (-12/5).floor(0))
+ assert_equal(-3, ( -5/2).floor(0))
+ assert_equal(-3, (-13/5).floor(0))
+
+ assert_equal(( 13/5), ( 13/5).floor(2))
+ assert_equal(( 5/2), ( 5/2).floor(2))
+ assert_equal(( 12/5), ( 12/5).floor(2))
+ assert_equal((-12/5), (-12/5).floor(2))
+ assert_equal(( -5/2), ( -5/2).floor(2))
+ assert_equal((-13/5), (-13/5).floor(2))
+ EOS
+ end
+
+ def test_ceil
+ assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
+ assert_equal( 3, ( 13/5).ceil)
+ assert_equal( 3, ( 5/2).ceil)
+ assert_equal( 3, ( 12/5).ceil)
+ assert_equal(-2, (-12/5).ceil)
+ assert_equal(-2, ( -5/2).ceil)
+ assert_equal(-2, (-13/5).ceil)
+
+ assert_equal( 3, ( 13/5).ceil(0))
+ assert_equal( 3, ( 5/2).ceil(0))
+ assert_equal( 3, ( 12/5).ceil(0))
+ assert_equal(-2, (-12/5).ceil(0))
+ assert_equal(-2, ( -5/2).ceil(0))
+ assert_equal(-2, (-13/5).ceil(0))
+
+ assert_equal(( 13/5), ( 13/5).ceil(2))
+ assert_equal(( 5/2), ( 5/2).ceil(2))
+ assert_equal(( 12/5), ( 12/5).ceil(2))
+ assert_equal((-12/5), (-12/5).ceil(2))
+ assert_equal(( -5/2), ( -5/2).ceil(2))
+ assert_equal((-13/5), (-13/5).ceil(2))
+ EOS
+ end
+
+ def test_truncate
+ assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
+ assert_equal( 2, ( 13/5).truncate)
+ assert_equal( 2, ( 5/2).truncate)
+ assert_equal( 2, ( 12/5).truncate)
+ assert_equal(-2, (-12/5).truncate)
+ assert_equal(-2, ( -5/2).truncate)
+ assert_equal(-2, (-13/5).truncate)
+
+ assert_equal( 2, ( 13/5).truncate(0))
+ assert_equal( 2, ( 5/2).truncate(0))
+ assert_equal( 2, ( 12/5).truncate(0))
+ assert_equal(-2, (-12/5).truncate(0))
+ assert_equal(-2, ( -5/2).truncate(0))
+ assert_equal(-2, (-13/5).truncate(0))
+
+ assert_equal(( 13/5), ( 13/5).truncate(2))
+ assert_equal(( 5/2), ( 5/2).truncate(2))
+ assert_equal(( 12/5), ( 12/5).truncate(2))
+ assert_equal((-12/5), (-12/5).truncate(2))
+ assert_equal(( -5/2), ( -5/2).truncate(2))
+ assert_equal((-13/5), (-13/5).truncate(2))
+ EOS
+ end
+
+ def test_round
+ assert_separately(%w[-rmathn], <<-EOS, ignore_stderr: true)
+ assert_equal( 3, ( 13/5).round)
+ assert_equal( 3, ( 5/2).round)
+ assert_equal( 2, ( 12/5).round)
+ assert_equal(-2, (-12/5).round)
+ assert_equal(-3, ( -5/2).round)
+ assert_equal(-3, (-13/5).round)
+
+ assert_equal( 3, ( 13/5).round(0))
+ assert_equal( 3, ( 5/2).round(0))
+ assert_equal( 2, ( 12/5).round(0))
+ assert_equal(-2, (-12/5).round(0))
+ assert_equal(-3, ( -5/2).round(0))
+ assert_equal(-3, (-13/5).round(0))
+
+ assert_equal(( 13/5), ( 13/5).round(2))
+ assert_equal(( 5/2), ( 5/2).round(2))
+ assert_equal(( 12/5), ( 12/5).round(2))
+ assert_equal((-12/5), (-12/5).round(2))
+ assert_equal(( -5/2), ( -5/2).round(2))
+ assert_equal((-13/5), (-13/5).round(2))
+ EOS
+ end
+end
diff --git a/jni/ruby/test/test_mutex_m.rb b/jni/ruby/test/test_mutex_m.rb
new file mode 100644
index 0000000..a29f3f4
--- /dev/null
+++ b/jni/ruby/test/test_mutex_m.rb
@@ -0,0 +1,26 @@
+require 'test/unit'
+require 'thread'
+require 'mutex_m'
+
+class TestMutexM < Test::Unit::TestCase
+ def test_cv_wait
+ o = Object.new
+ o.instance_variable_set(:@foo, nil)
+ o.extend(Mutex_m)
+ c = ConditionVariable.new
+ t = Thread.start {
+ o.synchronize do
+ until foo = o.instance_variable_get(:@foo)
+ c.wait(o)
+ end
+ foo
+ end
+ }
+ sleep(0.0001)
+ o.synchronize do
+ o.instance_variable_set(:@foo, "abc")
+ end
+ c.signal
+ assert_equal "abc", t.value
+ end
+end
diff --git a/jni/ruby/test/test_open3.rb b/jni/ruby/test/test_open3.rb
new file mode 100644
index 0000000..e560fbe
--- /dev/null
+++ b/jni/ruby/test/test_open3.rb
@@ -0,0 +1,268 @@
+require 'test/unit'
+require 'open3'
+
+class TestOpen3 < Test::Unit::TestCase
+ RUBY = EnvUtil.rubybin
+
+ def test_exit_status
+ Open3.popen3(RUBY, '-e', 'exit true') {|i,o,e,t|
+ assert_equal(true, t.value.success?)
+ }
+ Open3.popen3(RUBY, '-e', 'exit false') {|i,o,e,t|
+ assert_equal(false, t.value.success?)
+ }
+ end
+
+ def test_stdin
+ Open3.popen3(RUBY, '-e', 'exit STDIN.gets.chomp == "t"') {|i,o,e,t|
+ i.puts 't'
+ assert_equal(true, t.value.success?)
+ }
+ Open3.popen3(RUBY, '-e', 'exit STDIN.gets.chomp == "t"') {|i,o,e,t|
+ i.puts 'f'
+ assert_equal(false, t.value.success?)
+ }
+ end
+
+ def test_stdout
+ Open3.popen3(RUBY, '-e', 'STDOUT.print "foo"') {|i,o,e,t|
+ assert_equal("foo", o.read)
+ }
+ end
+
+ def test_stderr
+ Open3.popen3(RUBY, '-e', 'STDERR.print "bar"') {|i,o,e,t|
+ assert_equal("bar", e.read)
+ }
+ end
+
+ def test_block
+ r = Open3.popen3(RUBY, '-e', 'STDOUT.print STDIN.read') {|i,o,e,t|
+ i.print "baz"
+ i.close
+ assert_equal("baz", o.read)
+ "qux"
+ }
+ assert_equal("qux", r)
+ end
+
+ def test_noblock
+ i,o,e,t = Open3.popen3(RUBY, '-e', 'STDOUT.print STDIN.read')
+ i.print "baz"
+ i.close
+ assert_equal("baz", o.read)
+ ensure
+ i.close if !i.closed?
+ o.close if !o.closed?
+ e.close if !e.closed?
+ t.join
+ end
+
+ def test_commandline
+ commandline = "echo quux\n"
+ Open3.popen3(commandline) {|i,o,e,t|
+ assert_equal("quux\n", o.read)
+ }
+ end
+
+ def test_pid
+ Open3.popen3(RUBY, '-e', 'print $$') {|i,o,e,t|
+ pid = o.read.to_i
+ assert_equal(pid, t[:pid])
+ assert_equal(pid, t.pid)
+ }
+ end
+
+ def test_env
+ result = Open3.popen3({'A' => 'B', 'C' => 'D'}, RUBY, '-e' 'p ENV["A"]') do |i, out, err, thr|
+ output = out.read
+ assert_equal("\"B\"\n", output)
+ end
+ end
+
+ def with_pipe
+ r, w = IO.pipe
+ yield r, w
+ ensure
+ r.close if !r.closed?
+ w.close if !w.closed?
+ end
+
+ def with_reopen(io, arg)
+ old = io.dup
+ io.reopen(arg)
+ yield old
+ ensure
+ io.reopen(old)
+ old.close if old && !old.closed?
+ end
+
+ def test_popen2
+ with_pipe {|r, w|
+ with_reopen(STDERR, w) {|old|
+ w.close
+ Open3.popen2(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDERR.print s+"e"') {|i,o,t|
+ assert_kind_of(Thread, t)
+ i.print "z"
+ i.close
+ STDERR.reopen(old)
+ assert_equal("zo", o.read)
+ assert_equal("ze", r.read)
+ }
+ }
+ }
+ end
+
+ def test_popen2e
+ with_pipe {|r, w|
+ with_reopen(STDERR, w) {|old|
+ w.close
+ Open3.popen2e(RUBY, '-e', 's=STDIN.read; STDOUT.print s+"o"; STDOUT.flush; STDERR.print s+"e"') {|i,o,t|
+ assert_kind_of(Thread, t)
+ i.print "y"
+ i.close
+ STDERR.reopen(old)
+ assert_equal("yoye", o.read)
+ assert_equal("", r.read)
+ }
+ }
+ }
+ end
+
+ def test_capture3
+ o, e, s = Open3.capture3(RUBY, '-e', 'i=STDIN.read; print i+"o"; STDOUT.flush; STDERR.print i+"e"', :stdin_data=>"i")
+ assert_equal("io", o)
+ assert_equal("ie", e)
+ assert(s.success?)
+ end
+
+ def test_capture3_flip
+ o, e, s = Open3.capture3(RUBY, '-e', 'STDOUT.sync=true; 1000.times { print "o"*1000; STDERR.print "e"*1000 }')
+ assert_equal("o"*1000000, o)
+ assert_equal("e"*1000000, e)
+ assert(s.success?)
+ end
+
+ def test_capture2
+ o, s = Open3.capture2(RUBY, '-e', 'i=STDIN.read; print i+"o"', :stdin_data=>"i")
+ assert_equal("io", o)
+ assert(s.success?)
+ end
+
+ def test_capture2e
+ oe, s = Open3.capture2e(RUBY, '-e', 'i=STDIN.read; print i+"o"; STDOUT.flush; STDERR.print i+"e"', :stdin_data=>"i")
+ assert_equal("ioie", oe)
+ assert(s.success?)
+ end
+
+ def test_capture3_stdin_data
+ o, e, s = Open3.capture3(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024))
+ assert_equal("", o)
+ assert_equal("", e)
+ assert(s.success?)
+ end
+
+ def test_capture2_stdin_data
+ o, s = Open3.capture2(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024))
+ assert_equal("", o)
+ assert(s.success?)
+ end
+
+ def test_capture2e_stdin_data
+ oe, s = Open3.capture2e(RUBY, '-e', '', :stdin_data=>"z"*(1024*1024))
+ assert_equal("", oe)
+ assert(s.success?)
+ end
+
+ def test_pipeline_rw
+ Open3.pipeline_rw([RUBY, '-e', 'print STDIN.read + "1"'],
+ [RUBY, '-e', 'print STDIN.read + "2"']) {|i,o,ts|
+ assert_kind_of(IO, i)
+ assert_kind_of(IO, o)
+ assert_kind_of(Array, ts)
+ assert_equal(2, ts.length)
+ ts.each {|t| assert_kind_of(Thread, t) }
+ i.print "0"
+ i.close
+ assert_equal("012", o.read)
+ ts.each {|t|
+ assert(t.value.success?)
+ }
+ }
+ end
+
+ def test_pipeline_r
+ Open3.pipeline_r([RUBY, '-e', 'print "1"'],
+ [RUBY, '-e', 'print STDIN.read + "2"']) {|o,ts|
+ assert_kind_of(IO, o)
+ assert_kind_of(Array, ts)
+ assert_equal(2, ts.length)
+ ts.each {|t| assert_kind_of(Thread, t) }
+ assert_equal("12", o.read)
+ ts.each {|t|
+ assert(t.value.success?)
+ }
+ }
+ end
+
+ def test_pipeline_w
+ command = [RUBY, '-e', 's=STDIN.read; print s[1..-1]; exit s[0] == ?t']
+ str = 'ttftff'
+ Open3.pipeline_w(*[command]*str.length) {|i,ts|
+ assert_kind_of(IO, i)
+ assert_kind_of(Array, ts)
+ assert_equal(str.length, ts.length)
+ ts.each {|t| assert_kind_of(Thread, t) }
+ i.print str
+ i.close
+ ts.each_with_index {|t, ii|
+ assert_equal(str[ii] == ?t, t.value.success?)
+ }
+ }
+ end
+
+ def test_pipeline_start
+ command = [RUBY, '-e', 's=STDIN.read; print s[1..-1]; exit s[0] == ?t']
+ str = 'ttftff'
+ Open3.pipeline_start([RUBY, '-e', 'print ARGV[0]', str],
+ *([command]*str.length)) {|ts|
+ assert_kind_of(Array, ts)
+ assert_equal(str.length+1, ts.length)
+ ts.each {|t| assert_kind_of(Thread, t) }
+ ts.each_with_index {|t, i|
+ if i == 0
+ assert(t.value.success?)
+ else
+ assert_equal(str[i-1] == ?t, t.value.success?)
+ end
+ }
+ }
+ end
+
+ def test_pipeline_start_noblock
+ ts = Open3.pipeline_start([RUBY, '-e', ''])
+ assert_kind_of(Array, ts)
+ assert_equal(1, ts.length)
+ ts.each {|t| assert_kind_of(Thread, t) }
+ t = ts[0]
+ assert(t.value.success?)
+ end
+
+ def test_pipeline
+ command = [RUBY, '-e', 's=STDIN.read; print s[1..-1]; exit s[0] == ?t']
+ str = 'ttftff'
+ ss = Open3.pipeline([RUBY, '-e', 'print ARGV[0]', str],
+ *([command]*str.length))
+ assert_kind_of(Array, ss)
+ assert_equal(str.length+1, ss.length)
+ ss.each {|s| assert_kind_of(Process::Status, s) }
+ ss.each_with_index {|s, i|
+ if i == 0
+ assert(s.success?)
+ else
+ assert_equal(str[i-1] == ?t, s.success?)
+ end
+ }
+ end
+
+end
diff --git a/jni/ruby/test/test_pp.rb b/jni/ruby/test/test_pp.rb
new file mode 100644
index 0000000..813daf0
--- /dev/null
+++ b/jni/ruby/test/test_pp.rb
@@ -0,0 +1,195 @@
+require 'pp'
+require 'delegate'
+require 'test/unit'
+
+module PPTestModule
+
+class PPTest < Test::Unit::TestCase
+ def test_list0123_12
+ assert_equal("[0, 1, 2, 3]\n", PP.pp([0,1,2,3], '', 12))
+ end
+
+ def test_list0123_11
+ assert_equal("[0,\n 1,\n 2,\n 3]\n", PP.pp([0,1,2,3], '', 11))
+ end
+
+ OverriddenStruct = Struct.new("OverriddenStruct", :members, :class)
+ def test_struct_override_members # [ruby-core:7865]
+ a = OverriddenStruct.new(1,2)
+ assert_equal("#<struct Struct::OverriddenStruct members=1, class=2>\n", PP.pp(a, ''))
+ end
+
+ def test_redefined_method
+ o = ""
+ def o.method
+ end
+ assert_equal(%(""\n), PP.pp(o, ""))
+ end
+end
+
+class HasInspect
+ def initialize(a)
+ @a = a
+ end
+
+ def inspect
+ return "<inspect:#{@a.inspect}>"
+ end
+end
+
+class HasPrettyPrint
+ def initialize(a)
+ @a = a
+ end
+
+ def pretty_print(q)
+ q.text "<pretty_print:"
+ q.pp @a
+ q.text ">"
+ end
+end
+
+class HasBoth
+ def initialize(a)
+ @a = a
+ end
+
+ def inspect
+ return "<inspect:#{@a.inspect}>"
+ end
+
+ def pretty_print(q)
+ q.text "<pretty_print:"
+ q.pp @a
+ q.text ">"
+ end
+end
+
+class PrettyPrintInspect < HasPrettyPrint
+ alias inspect pretty_print_inspect
+end
+
+class PrettyPrintInspectWithoutPrettyPrint
+ alias inspect pretty_print_inspect
+end
+
+class PPInspectTest < Test::Unit::TestCase
+ def test_hasinspect
+ a = HasInspect.new(1)
+ assert_equal("<inspect:1>\n", PP.pp(a, ''))
+ end
+
+ def test_hasprettyprint
+ a = HasPrettyPrint.new(1)
+ assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
+ end
+
+ def test_hasboth
+ a = HasBoth.new(1)
+ assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
+ end
+
+ def test_pretty_print_inspect
+ a = PrettyPrintInspect.new(1)
+ assert_equal("<pretty_print:1>", a.inspect)
+ a = PrettyPrintInspectWithoutPrettyPrint.new
+ assert_raise(RuntimeError) { a.inspect }
+ end
+
+ def test_proc
+ a = proc {1}
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ def test_to_s_with_iv
+ a = Object.new
+ def a.to_s() "aaa" end
+ a.instance_eval { @a = nil }
+ result = PP.pp(a, '')
+ assert_equal("#{a.inspect}\n", result)
+ end
+
+ def test_to_s_without_iv
+ a = Object.new
+ def a.to_s() "aaa" end
+ result = PP.pp(a, '')
+ assert_equal("#{a.inspect}\n", result)
+ end
+end
+
+class PPCycleTest < Test::Unit::TestCase
+ def test_array
+ a = []
+ a << a
+ assert_equal("[[...]]\n", PP.pp(a, ''))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ def test_hash
+ a = {}
+ a[0] = a
+ assert_equal("{0=>{...}}\n", PP.pp(a, ''))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ S = Struct.new("S", :a, :b)
+ def test_struct
+ a = S.new(1,2)
+ a.b = a
+ assert_equal("#<struct Struct::S a=1, b=#<struct Struct::S:...>>\n", PP.pp(a, ''))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ def test_object
+ a = Object.new
+ a.instance_eval {@a = a}
+ assert_equal(a.inspect + "\n", PP.pp(a, ''))
+ end
+
+ def test_anonymous
+ a = Class.new.new
+ assert_equal(a.inspect + "\n", PP.pp(a, ''))
+ end
+
+ def test_withinspect
+ a = []
+ a << HasInspect.new(a)
+ assert_equal("[<inspect:[...]>]\n", PP.pp(a, ''))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ def test_share_nil
+ begin
+ PP.sharing_detection = true
+ a = [nil, nil]
+ assert_equal("[nil, nil]\n", PP.pp(a, ''))
+ ensure
+ PP.sharing_detection = false
+ end
+ end
+end
+
+class PPSingleLineTest < Test::Unit::TestCase
+ def test_hash
+ assert_equal("{1=>1}", PP.singleline_pp({ 1 => 1}, '')) # [ruby-core:02699]
+ assert_equal("[1#{', 1'*99}]", PP.singleline_pp([1]*100, ''))
+ end
+end
+
+class PPDelegateTest < Test::Unit::TestCase
+ class A < DelegateClass(Array); end
+
+ def test_delegate
+ assert_equal("[]\n", A.new([]).pretty_inspect, "[ruby-core:25804]")
+ end
+end
+
+class PPFileStatTest < Test::Unit::TestCase
+ def test_nothing_raised
+ assert_nothing_raised do
+ File.stat(__FILE__).pretty_inspect
+ end
+ end
+end
+
+end
diff --git a/jni/ruby/test/test_prettyprint.rb b/jni/ruby/test/test_prettyprint.rb
new file mode 100644
index 0000000..1d6b75a
--- /dev/null
+++ b/jni/ruby/test/test_prettyprint.rb
@@ -0,0 +1,519 @@
+require 'prettyprint'
+require 'test/unit'
+
+module PrettyPrintTest
+
+class WadlerExample < Test::Unit::TestCase # :nodoc:
+ def setup
+ @tree = Tree.new("aaaa", Tree.new("bbbbb", Tree.new("ccc"),
+ Tree.new("dd")),
+ Tree.new("eee"),
+ Tree.new("ffff", Tree.new("gg"),
+ Tree.new("hhh"),
+ Tree.new("ii")))
+ end
+
+ def hello(width)
+ PrettyPrint.format('', width) {|hello|
+ hello.group {
+ hello.group {
+ hello.group {
+ hello.group {
+ hello.text 'hello'
+ hello.breakable; hello.text 'a'
+ }
+ hello.breakable; hello.text 'b'
+ }
+ hello.breakable; hello.text 'c'
+ }
+ hello.breakable; hello.text 'd'
+ }
+ }
+ end
+
+ def test_hello_00_06
+ expected = <<'End'.chomp
+hello
+a
+b
+c
+d
+End
+ assert_equal(expected, hello(0))
+ assert_equal(expected, hello(6))
+ end
+
+ def test_hello_07_08
+ expected = <<'End'.chomp
+hello a
+b
+c
+d
+End
+ assert_equal(expected, hello(7))
+ assert_equal(expected, hello(8))
+ end
+
+ def test_hello_09_10
+ expected = <<'End'.chomp
+hello a b
+c
+d
+End
+ out = hello(9); assert_equal(expected, out)
+ out = hello(10); assert_equal(expected, out)
+ end
+
+ def test_hello_11_12
+ expected = <<'End'.chomp
+hello a b c
+d
+End
+ assert_equal(expected, hello(11))
+ assert_equal(expected, hello(12))
+ end
+
+ def test_hello_13
+ expected = <<'End'.chomp
+hello a b c d
+End
+ assert_equal(expected, hello(13))
+ end
+
+ def tree(width)
+ PrettyPrint.format('', width) {|q| @tree.show(q)}
+ end
+
+ def test_tree_00_19
+ expected = <<'End'.chomp
+aaaa[bbbbb[ccc,
+ dd],
+ eee,
+ ffff[gg,
+ hhh,
+ ii]]
+End
+ assert_equal(expected, tree(0))
+ assert_equal(expected, tree(19))
+ end
+
+ def test_tree_20_22
+ expected = <<'End'.chomp
+aaaa[bbbbb[ccc, dd],
+ eee,
+ ffff[gg,
+ hhh,
+ ii]]
+End
+ assert_equal(expected, tree(20))
+ assert_equal(expected, tree(22))
+ end
+
+ def test_tree_23_43
+ expected = <<'End'.chomp
+aaaa[bbbbb[ccc, dd],
+ eee,
+ ffff[gg, hhh, ii]]
+End
+ assert_equal(expected, tree(23))
+ assert_equal(expected, tree(43))
+ end
+
+ def test_tree_44
+ assert_equal(<<'End'.chomp, tree(44))
+aaaa[bbbbb[ccc, dd], eee, ffff[gg, hhh, ii]]
+End
+ end
+
+ def tree_alt(width)
+ PrettyPrint.format('', width) {|q| @tree.altshow(q)}
+ end
+
+ def test_tree_alt_00_18
+ expected = <<'End'.chomp
+aaaa[
+ bbbbb[
+ ccc,
+ dd
+ ],
+ eee,
+ ffff[
+ gg,
+ hhh,
+ ii
+ ]
+]
+End
+ assert_equal(expected, tree_alt(0))
+ assert_equal(expected, tree_alt(18))
+ end
+
+ def test_tree_alt_19_20
+ expected = <<'End'.chomp
+aaaa[
+ bbbbb[ ccc, dd ],
+ eee,
+ ffff[
+ gg,
+ hhh,
+ ii
+ ]
+]
+End
+ assert_equal(expected, tree_alt(19))
+ assert_equal(expected, tree_alt(20))
+ end
+
+ def test_tree_alt_20_49
+ expected = <<'End'.chomp
+aaaa[
+ bbbbb[ ccc, dd ],
+ eee,
+ ffff[ gg, hhh, ii ]
+]
+End
+ assert_equal(expected, tree_alt(21))
+ assert_equal(expected, tree_alt(49))
+ end
+
+ def test_tree_alt_50
+ expected = <<'End'.chomp
+aaaa[ bbbbb[ ccc, dd ], eee, ffff[ gg, hhh, ii ] ]
+End
+ assert_equal(expected, tree_alt(50))
+ end
+
+ class Tree # :nodoc:
+ def initialize(string, *children)
+ @string = string
+ @children = children
+ end
+
+ def show(q)
+ q.group {
+ q.text @string
+ q.nest(@string.length) {
+ unless @children.empty?
+ q.text '['
+ q.nest(1) {
+ first = true
+ @children.each {|t|
+ if first
+ first = false
+ else
+ q.text ','
+ q.breakable
+ end
+ t.show(q)
+ }
+ }
+ q.text ']'
+ end
+ }
+ }
+ end
+
+ def altshow(q)
+ q.group {
+ q.text @string
+ unless @children.empty?
+ q.text '['
+ q.nest(2) {
+ q.breakable
+ first = true
+ @children.each {|t|
+ if first
+ first = false
+ else
+ q.text ','
+ q.breakable
+ end
+ t.altshow(q)
+ }
+ }
+ q.breakable
+ q.text ']'
+ end
+ }
+ end
+
+ end
+end
+
+class StrictPrettyExample < Test::Unit::TestCase # :nodoc:
+ def prog(width)
+ PrettyPrint.format('', width) {|q|
+ q.group {
+ q.group {q.nest(2) {
+ q.text "if"; q.breakable;
+ q.group {
+ q.nest(2) {
+ q.group {q.text "a"; q.breakable; q.text "=="}
+ q.breakable; q.text "b"}}}}
+ q.breakable
+ q.group {q.nest(2) {
+ q.text "then"; q.breakable;
+ q.group {
+ q.nest(2) {
+ q.group {q.text "a"; q.breakable; q.text "<<"}
+ q.breakable; q.text "2"}}}}
+ q.breakable
+ q.group {q.nest(2) {
+ q.text "else"; q.breakable;
+ q.group {
+ q.nest(2) {
+ q.group {q.text "a"; q.breakable; q.text "+"}
+ q.breakable; q.text "b"}}}}}
+ }
+ end
+
+ def test_00_04
+ expected = <<'End'.chomp
+if
+ a
+ ==
+ b
+then
+ a
+ <<
+ 2
+else
+ a
+ +
+ b
+End
+ assert_equal(expected, prog(0))
+ assert_equal(expected, prog(4))
+ end
+
+ def test_05
+ expected = <<'End'.chomp
+if
+ a
+ ==
+ b
+then
+ a
+ <<
+ 2
+else
+ a +
+ b
+End
+ assert_equal(expected, prog(5))
+ end
+
+ def test_06
+ expected = <<'End'.chomp
+if
+ a ==
+ b
+then
+ a <<
+ 2
+else
+ a +
+ b
+End
+ assert_equal(expected, prog(6))
+ end
+
+ def test_07
+ expected = <<'End'.chomp
+if
+ a ==
+ b
+then
+ a <<
+ 2
+else
+ a + b
+End
+ assert_equal(expected, prog(7))
+ end
+
+ def test_08
+ expected = <<'End'.chomp
+if
+ a == b
+then
+ a << 2
+else
+ a + b
+End
+ assert_equal(expected, prog(8))
+ end
+
+ def test_09
+ expected = <<'End'.chomp
+if a == b
+then
+ a << 2
+else
+ a + b
+End
+ assert_equal(expected, prog(9))
+ end
+
+ def test_10
+ expected = <<'End'.chomp
+if a == b
+then
+ a << 2
+else a + b
+End
+ assert_equal(expected, prog(10))
+ end
+
+ def test_11_31
+ expected = <<'End'.chomp
+if a == b
+then a << 2
+else a + b
+End
+ assert_equal(expected, prog(11))
+ assert_equal(expected, prog(15))
+ assert_equal(expected, prog(31))
+ end
+
+ def test_32
+ expected = <<'End'.chomp
+if a == b then a << 2 else a + b
+End
+ assert_equal(expected, prog(32))
+ end
+
+end
+
+class TailGroup < Test::Unit::TestCase # :nodoc:
+ def test_1
+ out = PrettyPrint.format('', 10) {|q|
+ q.group {
+ q.group {
+ q.text "abc"
+ q.breakable
+ q.text "def"
+ }
+ q.group {
+ q.text "ghi"
+ q.breakable
+ q.text "jkl"
+ }
+ }
+ }
+ assert_equal("abc defghi\njkl", out)
+ end
+end
+
+class NonString < Test::Unit::TestCase # :nodoc:
+ def format(width)
+ PrettyPrint.format([], width, 'newline', lambda {|n| "#{n} spaces"}) {|q|
+ q.text(3, 3)
+ q.breakable(1, 1)
+ q.text(3, 3)
+ }
+ end
+
+ def test_6
+ assert_equal([3, "newline", "0 spaces", 3], format(6))
+ end
+
+ def test_7
+ assert_equal([3, 1, 3], format(7))
+ end
+
+end
+
+class Fill < Test::Unit::TestCase # :nodoc:
+ def format(width)
+ PrettyPrint.format('', width) {|q|
+ q.group {
+ q.text 'abc'
+ q.fill_breakable
+ q.text 'def'
+ q.fill_breakable
+ q.text 'ghi'
+ q.fill_breakable
+ q.text 'jkl'
+ q.fill_breakable
+ q.text 'mno'
+ q.fill_breakable
+ q.text 'pqr'
+ q.fill_breakable
+ q.text 'stu'
+ }
+ }
+ end
+
+ def test_00_06
+ expected = <<'End'.chomp
+abc
+def
+ghi
+jkl
+mno
+pqr
+stu
+End
+ assert_equal(expected, format(0))
+ assert_equal(expected, format(6))
+ end
+
+ def test_07_10
+ expected = <<'End'.chomp
+abc def
+ghi jkl
+mno pqr
+stu
+End
+ assert_equal(expected, format(7))
+ assert_equal(expected, format(10))
+ end
+
+ def test_11_14
+ expected = <<'End'.chomp
+abc def ghi
+jkl mno pqr
+stu
+End
+ assert_equal(expected, format(11))
+ assert_equal(expected, format(14))
+ end
+
+ def test_15_18
+ expected = <<'End'.chomp
+abc def ghi jkl
+mno pqr stu
+End
+ assert_equal(expected, format(15))
+ assert_equal(expected, format(18))
+ end
+
+ def test_19_22
+ expected = <<'End'.chomp
+abc def ghi jkl mno
+pqr stu
+End
+ assert_equal(expected, format(19))
+ assert_equal(expected, format(22))
+ end
+
+ def test_23_26
+ expected = <<'End'.chomp
+abc def ghi jkl mno pqr
+stu
+End
+ assert_equal(expected, format(23))
+ assert_equal(expected, format(26))
+ end
+
+ def test_27
+ expected = <<'End'.chomp
+abc def ghi jkl mno pqr stu
+End
+ assert_equal(expected, format(27))
+ end
+
+end
+
+end
diff --git a/jni/ruby/test/test_prime.rb b/jni/ruby/test/test_prime.rb
new file mode 100644
index 0000000..885406f
--- /dev/null
+++ b/jni/ruby/test/test_prime.rb
@@ -0,0 +1,174 @@
+require 'test/unit'
+require 'prime'
+require 'stringio'
+require 'timeout'
+
+class TestPrime < Test::Unit::TestCase
+ # The first 100 prime numbers
+ PRIMES = [
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
+ 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
+ 89, 97, 101, 103, 107, 109, 113, 127, 131,
+ 137, 139, 149, 151, 157, 163, 167, 173, 179,
+ 181, 191, 193, 197, 199, 211, 223, 227, 229,
+ 233, 239, 241, 251, 257, 263, 269, 271, 277,
+ 281, 283, 293, 307, 311, 313, 317, 331, 337,
+ 347, 349, 353, 359, 367, 373, 379, 383, 389,
+ 397, 401, 409, 419, 421, 431, 433, 439, 443,
+ 449, 457, 461, 463, 467, 479, 487, 491, 499,
+ 503, 509, 521, 523, 541,
+ ]
+ def test_each
+ primes = []
+ Prime.each do |p|
+ break if p > 541
+ primes << p
+ end
+ assert_equal PRIMES, primes
+ end
+
+ def test_each_by_prime_number_theorem
+ 3.upto(15) do |i|
+ max = 2**i
+ primes = []
+ Prime.each do |p|
+ break if p >= max
+ primes << p
+ end
+
+ # Prime number theorem
+ assert primes.length >= max/Math.log(max)
+ delta = 0.05
+ li = (2..max).step(delta).inject(0){|sum,x| sum + delta/Math.log(x)}
+ assert primes.length <= li
+ end
+ end
+
+ def test_each_without_block
+ enum = Prime.each
+ assert enum.respond_to?(:each)
+ assert enum.kind_of?(Enumerable)
+ assert enum.respond_to?(:with_index)
+ assert enum.respond_to?(:next)
+ assert enum.respond_to?(:succ)
+ assert enum.respond_to?(:rewind)
+ end
+
+ def test_new
+ orig_stderr, orig_verbose = $stderr, $VERBOSE
+
+ $stderr = buf = StringIO.new('', 'w')
+ $VERBOSE = false
+
+ enum = Prime.new
+ assert_match("obsolete", buf.string)
+
+ assert enum.respond_to?(:each)
+ assert enum.kind_of?(Enumerable)
+ assert enum.respond_to?(:succ)
+
+ assert Prime === enum
+ ensure
+ $stderr = orig_stderr
+ $VERBOSE = orig_verbose
+ end
+
+ def test_enumerator_succ
+ enum = Prime.each
+ assert_equal PRIMES[0, 50], 50.times.map{ enum.succ }
+ assert_equal PRIMES[50, 50], 50.times.map{ enum.succ }
+ enum.rewind
+ assert_equal PRIMES[0, 100], 100.times.map{ enum.succ }
+ end
+
+ def test_enumerator_with_index
+ enum = Prime.each
+ last = -1
+ enum.with_index do |p,i|
+ break if i >= 100
+ assert_equal last+1, i
+ assert_equal PRIMES[i], p
+ last = i
+ end
+ end
+
+ def test_default_instance_does_not_have_compatibility_methods
+ assert !Prime.instance.respond_to?(:succ)
+ assert !Prime.instance.respond_to?(:next)
+ end
+
+ class TestInteger < Test::Unit::TestCase
+ def test_prime_division
+ pd = PRIMES.inject(&:*).prime_division
+ assert_equal PRIMES.map{|p| [p, 1]}, pd
+
+ pd = (-PRIMES.inject(&:*)).prime_division
+ assert_equal [-1, *PRIMES].map{|p| [p, 1]}, pd
+ end
+
+ def test_from_prime_division
+ assert_equal PRIMES.inject(&:*), Integer.from_prime_division(PRIMES.map{|p| [p,1]})
+
+ assert_equal(-PRIMES.inject(&:*), Integer.from_prime_division([[-1, 1]] + PRIMES.map{|p| [p,1]}))
+ end
+
+ def test_prime?
+ # zero and unit
+ assert !0.prime?
+ assert !1.prime?
+
+ # small primes
+ assert 2.prime?
+ assert 3.prime?
+
+ # squared prime
+ assert !4.prime?
+ assert !9.prime?
+
+ # mersenne numbers
+ assert((2**31-1).prime?)
+ assert !(2**32-1).prime?
+
+ # fermat numbers
+ assert((2**(2**4)+1).prime?)
+ assert !(2**(2**5)+1).prime? # Euler!
+
+ # large composite
+ assert !((2**13-1) * (2**17-1)).prime?
+
+ # factorial
+ assert !(2...100).inject(&:*).prime?
+
+ # negative
+ assert !-1.prime?
+ assert !-2.prime?
+ assert !-3.prime?
+ assert !-4.prime?
+ end
+ end
+
+ def test_eratosthenes_works_fine_after_timeout
+ sieve = Prime::EratosthenesSieve.instance
+ sieve.send(:initialize)
+ begin
+ # simulates that Timeout.timeout interrupts Prime::EratosthenesSieve#extend_table
+ def sieve.Integer(n)
+ n = super(n)
+ sleep 10 if /compute_primes/ =~ caller.first
+ return n
+ end
+
+ begin
+ Timeout.timeout(0.5) { Prime.each(7*37){} }
+ flunk("timeout expected")
+ rescue Timeout::Error
+ end
+ ensure
+ class << sieve
+ remove_method :Integer
+ end
+ end
+
+ assert_not_include Prime.each(7*37).to_a, 7*37, "[ruby-dev:39465]"
+ end
+end
diff --git a/jni/ruby/test/test_pstore.rb b/jni/ruby/test/test_pstore.rb
new file mode 100644
index 0000000..e028ed6
--- /dev/null
+++ b/jni/ruby/test/test_pstore.rb
@@ -0,0 +1,144 @@
+require 'test/unit'
+require 'pstore'
+require 'tmpdir'
+
+class PStoreTest < Test::Unit::TestCase
+ def setup
+ @pstore_file = File.join(Dir.tmpdir, "pstore.tmp.#{Process.pid}")
+ @pstore = PStore.new(@pstore_file)
+ end
+
+ def teardown
+ File.unlink(@pstore_file) rescue nil
+ end
+
+ def test_opening_new_file_in_readonly_mode_should_result_in_empty_values
+ @pstore.transaction(true) do
+ assert_nil @pstore[:foo]
+ assert_nil @pstore[:bar]
+ end
+ end
+
+ def test_opening_new_file_in_readwrite_mode_should_result_in_empty_values
+ @pstore.transaction do
+ assert_nil @pstore[:foo]
+ assert_nil @pstore[:bar]
+ end
+ end
+
+ def test_data_should_be_loaded_correctly_when_in_readonly_mode
+ @pstore.transaction do
+ @pstore[:foo] = "bar"
+ end
+ @pstore.transaction(true) do
+ assert_equal "bar", @pstore[:foo]
+ end
+ end
+
+ def test_data_should_be_loaded_correctly_when_in_readwrite_mode
+ @pstore.transaction do
+ @pstore[:foo] = "bar"
+ end
+ @pstore.transaction do
+ assert_equal "bar", @pstore[:foo]
+ end
+ end
+
+ def test_changes_after_commit_are_discarded
+ @pstore.transaction do
+ @pstore[:foo] = "bar"
+ @pstore.commit
+ @pstore[:foo] = "baz"
+ end
+ @pstore.transaction(true) do
+ assert_equal "bar", @pstore[:foo]
+ end
+ end
+
+ def test_changes_are_not_written_on_abort
+ @pstore.transaction do
+ @pstore[:foo] = "bar"
+ @pstore.abort
+ end
+ @pstore.transaction(true) do
+ assert_nil @pstore[:foo]
+ end
+ end
+
+ def test_writing_inside_readonly_transaction_raises_error
+ assert_raise(PStore::Error) do
+ @pstore.transaction(true) do
+ @pstore[:foo] = "bar"
+ end
+ end
+ end
+
+ def test_thread_safe
+ assert_raise(PStore::Error) do
+ flag = false
+ th = Thread.new do
+ @pstore.transaction do
+ @pstore[:foo] = "bar"
+ flag = true
+ sleep 1
+ end
+ end
+ begin
+ sleep 0.1 until flag
+ @pstore.transaction {}
+ ensure
+ th.join
+ end
+ end
+ begin
+ pstore = PStore.new(second_file, true)
+ flag = false
+ th = Thread.new do
+ pstore.transaction do
+ pstore[:foo] = "bar"
+ flag = true
+ sleep 1
+ end
+ end
+ begin
+ sleep 0.1 until flag
+ assert_equal("bar", pstore.transaction { pstore[:foo] })
+ ensure
+ th.join
+ end
+ end
+ ensure
+ File.unlink(second_file) rescue nil
+ end
+
+ def test_nested_transaction_raises_error
+ assert_raise(PStore::Error) do
+ @pstore.transaction { @pstore.transaction { } }
+ end
+ pstore = PStore.new(second_file, true)
+ assert_raise(PStore::Error) do
+ pstore.transaction { pstore.transaction { } }
+ end
+ ensure
+ File.unlink(second_file) rescue nil
+ end
+
+ # Test that PStore's file operations do not blow up when default encodings are set
+ def test_pstore_files_are_accessed_as_binary_files
+ bug5311 = '[ruby-core:39503]'
+ n = 128
+ assert_in_out_err(["-Eutf-8:utf-8", "-rpstore", "-", @pstore_file], <<-SRC, [bug5311], [], bug5311, timeout: 15)
+ @pstore = PStore.new(ARGV[0])
+ (1..#{n}).each do |i|
+ @pstore.transaction {@pstore["Key\#{i}"] = "value \#{i}"}
+ end
+ @pstore.transaction {@pstore["Bug5311"] = '#{bug5311}'}
+ puts @pstore.transaction {@pstore["Bug5311"]}
+ SRC
+ assert_equal(bug5311, @pstore.transaction {@pstore["Bug5311"]}, bug5311)
+ end
+
+ def second_file
+ File.join(Dir.tmpdir, "pstore.tmp2.#{Process.pid}")
+ end
+end
diff --git a/jni/ruby/test/test_pty.rb b/jni/ruby/test/test_pty.rb
new file mode 100644
index 0000000..0ec4f96
--- /dev/null
+++ b/jni/ruby/test/test_pty.rb
@@ -0,0 +1,241 @@
+require 'test/unit'
+require 'shellwords'
+require 'tmpdir'
+
+begin
+ require 'pty'
+rescue LoadError
+end
+
+class TestPTY < Test::Unit::TestCase
+ RUBY = EnvUtil.rubybin
+
+ def test_spawn_without_block
+ r, w, pid = PTY.spawn(RUBY, '-e', 'puts "a"')
+ rescue RuntimeError
+ skip $!
+ else
+ assert_equal("a\r\n", r.gets)
+ ensure
+ r.close if r
+ w.close if w
+ Process.wait pid if pid
+ end
+
+ def test_spawn_with_block
+ PTY.spawn(RUBY, '-e', 'puts "b"') {|r,w,pid|
+ begin
+ assert_equal("b\r\n", r.gets)
+ ensure
+ r.close
+ w.close
+ Process.wait(pid)
+ end
+ }
+ rescue RuntimeError
+ skip $!
+ end
+
+ def test_commandline
+ commandline = Shellwords.join([RUBY, '-e', 'puts "foo"'])
+ PTY.spawn(commandline) {|r,w,pid|
+ begin
+ assert_equal("foo\r\n", r.gets)
+ ensure
+ r.close
+ w.close
+ Process.wait(pid)
+ end
+ }
+ rescue RuntimeError
+ skip $!
+ end
+
+ def test_argv0
+ PTY.spawn([RUBY, "argv0"], '-e', 'puts "bar"') {|r,w,pid|
+ begin
+ assert_equal("bar\r\n", r.gets)
+ ensure
+ r.close
+ w.close
+ Process.wait(pid)
+ end
+ }
+ rescue RuntimeError
+ skip $!
+ end
+
+ def test_open_without_block
+ ret = PTY.open
+ rescue RuntimeError
+ skip $!
+ else
+ assert_kind_of(Array, ret)
+ assert_equal(2, ret.length)
+ assert_equal(IO, ret[0].class)
+ assert_equal(File, ret[1].class)
+ _, slave = ret
+ assert(slave.tty?)
+ assert(File.chardev?(slave.path))
+ ensure
+ if ret
+ ret[0].close
+ ret[1].close
+ end
+ end
+
+ def test_open_with_block
+ r = nil
+ x = Object.new
+ y = PTY.open {|ret|
+ r = ret;
+ assert_kind_of(Array, ret)
+ assert_equal(2, ret.length)
+ assert_equal(IO, ret[0].class)
+ assert_equal(File, ret[1].class)
+ _, slave = ret
+ assert(slave.tty?)
+ assert(File.chardev?(slave.path))
+ x
+ }
+ rescue RuntimeError
+ skip $!
+ else
+ assert(r[0].closed?)
+ assert(r[1].closed?)
+ assert_equal(y, x)
+ end
+
+ def test_close_in_block
+ PTY.open {|master, slave|
+ slave.close
+ master.close
+ assert(slave.closed?)
+ assert(master.closed?)
+ }
+ rescue RuntimeError
+ skip $!
+ else
+ assert_nothing_raised {
+ PTY.open {|master, slave|
+ slave.close
+ master.close
+ }
+ }
+ end
+
+ def test_open
+ PTY.open {|master, slave|
+ slave.puts "foo"
+ assert_equal("foo", master.gets.chomp)
+ master.puts "bar"
+ assert_equal("bar", slave.gets.chomp)
+ }
+ rescue RuntimeError
+ skip $!
+ end
+
+ def test_stat_slave
+ PTY.open {|master, slave|
+ s = File.stat(slave.path)
+ assert_equal(Process.uid, s.uid)
+ assert_equal(0600, s.mode & 0777)
+ }
+ rescue RuntimeError
+ skip $!
+ end
+
+ def test_close_master
+ PTY.open {|master, slave|
+ master.close
+ assert_raise(EOFError) { slave.readpartial(10) }
+ }
+ rescue RuntimeError
+ skip $!
+ end
+
+ def test_close_slave
+ PTY.open {|master, slave|
+ slave.close
+ # This exception is platform dependent.
+ assert_raise(
+ EOFError, # FreeBSD
+ Errno::EIO # GNU/Linux
+ ) { master.readpartial(10) }
+ }
+ rescue RuntimeError
+ skip $!
+ end
+
+ def test_getpty_nonexistent
+ bug3672 = '[ruby-dev:41965]'
+ Dir.mktmpdir do |tmpdir|
+ assert_raise(Errno::ENOENT, bug3672) {
+ begin
+ PTY.getpty(File.join(tmpdir, "no-such-command"))
+ rescue RuntimeError
+ skip $!
+ end
+ }
+ end
+ end
+
+ def test_pty_check_default
+ st1 = st2 = pid = nil
+ `echo` # preset $?
+ PTY.spawn("cat") do |r,w,id|
+ pid = id
+ st1 = PTY.check(pid)
+ w.close
+ r.close
+ begin
+ sleep(0.1)
+ end until st2 = PTY.check(pid)
+ end
+ rescue RuntimeError
+ skip $!
+ else
+ assert_equal(pid, st1.pid) if st1
+ assert_nil(st1)
+ assert_equal(pid, st2.pid)
+ end
+
+ def test_pty_check_raise
+ bug2642 = '[ruby-dev:44600]'
+ st1 = st2 = pid = nil
+ PTY.spawn("cat") do |r,w,id|
+ pid = id
+ assert_nothing_raised(PTY::ChildExited, bug2642) {st1 = PTY.check(pid, true)}
+ w.close
+ r.close
+ sleep(0.1)
+ st2 = assert_raise(PTY::ChildExited, bug2642) {PTY.check(pid, true)}.status
+ end
+ rescue RuntimeError
+ skip $!
+ else
+ assert_equal(pid, st1.pid) if st1
+ assert_nil(st1)
+ assert_equal(pid, st2.pid)
+ end
+
+ def test_cloexec
+ PTY.open {|m, s|
+ assert(m.close_on_exec?)
+ assert(s.close_on_exec?)
+ }
+ PTY.spawn(RUBY, '-e', '') {|r, w, pid|
+ begin
+ assert(r.close_on_exec?)
+ assert(w.close_on_exec?)
+ ensure
+ r.close
+ w.close
+ Process.wait(pid)
+ end
+ }
+ rescue RuntimeError
+ skip $!
+ end
+end if defined? PTY
+
diff --git a/jni/ruby/test/test_rbconfig.rb b/jni/ruby/test/test_rbconfig.rb
new file mode 100644
index 0000000..3620450
--- /dev/null
+++ b/jni/ruby/test/test_rbconfig.rb
@@ -0,0 +1,53 @@
+require 'test/unit'
+require 'rbconfig'
+require 'shellwords'
+
+class TestRbConfig < Test::Unit::TestCase
+ @@with_config = {}
+
+ Shellwords::shellwords(RbConfig::CONFIG["configure_args"]).grep(/\A--with-([^=]*)=(.*)/) do
+ @@with_config[$1.tr('_', '-')] = $2
+ end
+
+ def test_sitedirs
+ RbConfig::MAKEFILE_CONFIG.each do |key, val|
+ next unless /\Asite(?!arch)/ =~ key
+ next if @@with_config[key]
+ assert_match(/(?:\$\(|\/)site/, val, key)
+ end
+ end
+
+ def test_vendordirs
+ RbConfig::MAKEFILE_CONFIG.each do |key, val|
+ next unless /\Avendor(?!arch)/ =~ key
+ next if @@with_config[key]
+ assert_match(/(?:\$\(|\/)vendor/, val, key)
+ end
+ end
+
+ def test_archdirs
+ RbConfig::MAKEFILE_CONFIG.each do |key, val|
+ next unless /\A(?!site|vendor|archdir\z).*arch.*dir\z/ =~ key
+ next if @@with_config[key]
+ assert_match(/\$\(arch|\$\(rubyarchprefix\)/, val, key)
+ end
+ end
+
+ def test_sitearchdirs
+ bug7823 = '[ruby-dev:46964] [Bug #7823]'
+ RbConfig::MAKEFILE_CONFIG.each do |key, val|
+ next unless /\Asite.*arch.*dir\z/ =~ key
+ next if @@with_config[key]
+ assert_match(/\$\(sitearch|\$\(rubysitearchprefix\)/, val, "#{key} #{bug7823}")
+ end
+ end
+
+ def test_vendorarchdirs
+ bug7823 = '[ruby-dev:46964] [Bug #7823]'
+ RbConfig::MAKEFILE_CONFIG.each do |key, val|
+ next unless /\Avendor.*arch.*dir\z/ =~ key
+ next if @@with_config[key]
+ assert_match(/\$\(sitearch|\$\(rubysitearchprefix\)/, val, "#{key} #{bug7823}")
+ end
+ end
+end
diff --git a/jni/ruby/test/test_securerandom.rb b/jni/ruby/test/test_securerandom.rb
new file mode 100644
index 0000000..0a78624
--- /dev/null
+++ b/jni/ruby/test/test_securerandom.rb
@@ -0,0 +1,185 @@
+require 'test/unit'
+require 'securerandom'
+require 'tempfile'
+
+# This testcase does NOT aim to test cryptographically strongness and randomness.
+class TestSecureRandom < Test::Unit::TestCase
+ def setup
+ @it = SecureRandom
+ end
+
+ def test_s_random_bytes
+ assert_equal(16, @it.random_bytes.size)
+ assert_equal(Encoding::ASCII_8BIT, @it.random_bytes.encoding)
+ 65.times do |idx|
+ assert_equal(idx, @it.random_bytes(idx).size)
+ end
+ end
+
+# This test took 2 minutes on my machine.
+# And 65536 times loop could not be enough for forcing PID recycle.
+if false
+ def test_s_random_bytes_is_fork_safe
+ begin
+ require 'openssl'
+ rescue LoadError
+ return
+ end
+ SecureRandom.random_bytes(8)
+ pid, v1 = forking_random_bytes
+ assert(check_forking_random_bytes(pid, v1), 'Process ID not recycled?')
+ end
+
+ def forking_random_bytes
+ r, w = IO.pipe
+ pid = fork {
+ r.close
+ w.write SecureRandom.random_bytes(8)
+ w.close
+ }
+ w.close
+ v = r.read(8)
+ r.close
+ Process.waitpid2(pid)
+ [pid, v]
+ end
+
+ def check_forking_random_bytes(target_pid, target)
+ 65536.times do
+ pid = fork {
+ if $$ == target_pid
+ v2 = SecureRandom.random_bytes(8)
+ if v2 == target
+ exit(1)
+ else
+ exit(2)
+ end
+ end
+ exit(3)
+ }
+ pid, status = Process.waitpid2(pid)
+ case status.exitstatus
+ when 1
+ raise 'returned same sequence for same PID'
+ when 2
+ return true
+ end
+ end
+ false # not recycled?
+ end
+end
+
+ def test_s_random_bytes_without_openssl
+ begin
+ require 'openssl'
+ rescue LoadError
+ return
+ end
+ begin
+ load_path = $LOAD_PATH.dup
+ loaded_features = $LOADED_FEATURES.dup
+ openssl = Object.instance_eval { remove_const(:OpenSSL) }
+
+ remove_feature('securerandom.rb')
+ remove_feature('openssl.rb')
+ Dir.mktmpdir do |dir|
+ open(File.join(dir, 'openssl.rb'), 'w') { |f|
+ f << 'raise LoadError'
+ }
+ $LOAD_PATH.unshift(dir)
+ v = $VERBOSE
+ begin
+ $VERBOSE = false
+ require 'securerandom'
+ ensure
+ $VERBOSE = v
+ end
+ test_s_random_bytes
+ end
+ ensure
+ $LOADED_FEATURES.replace(loaded_features)
+ $LOAD_PATH.replace(load_path)
+ Object.const_set(:OpenSSL, openssl)
+ end
+ end
+
+ def test_s_hex
+ assert_equal(16 * 2, @it.hex.size)
+ 33.times do |idx|
+ assert_equal(idx * 2, @it.hex(idx).size)
+ assert_equal(idx, @it.hex(idx).gsub(/(..)/) { [$1].pack('H*') }.size)
+ end
+ end
+
+ def test_hex_encoding
+ assert_equal(Encoding::US_ASCII, @it.hex.encoding)
+ end
+
+ def test_s_base64
+ assert_equal(16, @it.base64.unpack('m*')[0].size)
+ 17.times do |idx|
+ assert_equal(idx, @it.base64(idx).unpack('m*')[0].size)
+ end
+ end
+
+ def test_s_urlsafe_base64
+ safe = /[\n+\/]/
+ 65.times do |idx|
+ assert_not_match(safe, @it.urlsafe_base64(idx))
+ end
+ # base64 can include unsafe byte
+ 10001.times do |idx|
+ return if safe =~ @it.base64(idx)
+ end
+ flunk
+ end
+
+ def test_s_random_number_float
+ 101.times do
+ v = @it.random_number
+ assert(0.0 <= v && v < 1.0)
+ end
+ end
+
+ def test_s_random_number_float_by_zero
+ 101.times do
+ v = @it.random_number(0)
+ assert(0.0 <= v && v < 1.0)
+ end
+ end
+
+ def test_s_random_number_int
+ 101.times do |idx|
+ next if idx.zero?
+ v = @it.random_number(idx)
+ assert(0 <= v && v < idx)
+ end
+ end
+
+ def test_uuid
+ uuid = @it.uuid
+ assert_equal(36, uuid.size)
+ uuid.unpack('a8xa4xa4xa4xa12').each do |e|
+ assert_match(/^[0-9a-f]+$/, e)
+ end
+ end
+
+ def protect
+ begin
+ yield
+ rescue NotImplementedError
+ # ignore
+ end
+ end
+
+ def remove_feature(basename)
+ $LOADED_FEATURES.delete_if { |path|
+ if File.basename(path) == basename
+ $LOAD_PATH.any? { |dir|
+ File.exist?(File.join(dir, basename))
+ }
+ end
+ }
+ end
+
+end
diff --git a/jni/ruby/test/test_set.rb b/jni/ruby/test/test_set.rb
new file mode 100644
index 0000000..961578b
--- /dev/null
+++ b/jni/ruby/test/test_set.rb
@@ -0,0 +1,697 @@
+require 'test/unit'
+require 'set'
+
+class TC_Set < Test::Unit::TestCase
+ def test_aref
+ assert_nothing_raised {
+ Set[]
+ Set[nil]
+ Set[1,2,3]
+ }
+
+ assert_equal(0, Set[].size)
+ assert_equal(1, Set[nil].size)
+ assert_equal(1, Set[[]].size)
+ assert_equal(1, Set[[nil]].size)
+
+ set = Set[2,4,6,4]
+ assert_equal(Set.new([2,4,6]), set)
+ end
+
+ def test_s_new
+ assert_nothing_raised {
+ Set.new()
+ Set.new(nil)
+ Set.new([])
+ Set.new([1,2])
+ Set.new('a'..'c')
+ }
+ assert_raises(ArgumentError) {
+ Set.new(false)
+ }
+ assert_raises(ArgumentError) {
+ Set.new(1)
+ }
+ assert_raises(ArgumentError) {
+ Set.new(1,2)
+ }
+
+ assert_equal(0, Set.new().size)
+ assert_equal(0, Set.new(nil).size)
+ assert_equal(0, Set.new([]).size)
+ assert_equal(1, Set.new([nil]).size)
+
+ ary = [2,4,6,4]
+ set = Set.new(ary)
+ ary.clear
+ assert_equal(false, set.empty?)
+ assert_equal(3, set.size)
+
+ ary = [1,2,3]
+
+ s = Set.new(ary) { |o| o * 2 }
+ assert_equal([2,4,6], s.sort)
+ end
+
+ def test_clone
+ set1 = Set.new
+ set2 = set1.clone
+ set1 << 'abc'
+ assert_equal(Set.new, set2)
+ end
+
+ def test_dup
+ set1 = Set[1,2]
+ set2 = set1.dup
+
+ assert_not_same(set1, set2)
+
+ assert_equal(set1, set2)
+
+ set1.add(3)
+
+ assert_not_equal(set1, set2)
+ end
+
+ def test_size
+ assert_equal(0, Set[].size)
+ assert_equal(2, Set[1,2].size)
+ assert_equal(2, Set[1,2,1].size)
+ end
+
+ def test_empty?
+ assert_equal(true, Set[].empty?)
+ assert_equal(false, Set[1, 2].empty?)
+ end
+
+ def test_clear
+ set = Set[1,2]
+ ret = set.clear
+
+ assert_same(set, ret)
+ assert_equal(true, set.empty?)
+ end
+
+ def test_replace
+ set = Set[1,2]
+ ret = set.replace('a'..'c')
+
+ assert_same(set, ret)
+ assert_equal(Set['a','b','c'], set)
+
+ set = Set[1,2]
+ assert_raise(ArgumentError) {
+ set.replace(3)
+ }
+ assert_equal(Set[1,2], set)
+ end
+
+ def test_to_a
+ set = Set[1,2,3,2]
+ ary = set.to_a
+
+ assert_equal([1,2,3], ary.sort)
+ end
+
+ def test_flatten
+ # test1
+ set1 = Set[
+ 1,
+ Set[
+ 5,
+ Set[7,
+ Set[0]
+ ],
+ Set[6,2],
+ 1
+ ],
+ 3,
+ Set[3,4]
+ ]
+
+ set2 = set1.flatten
+ set3 = Set.new(0..7)
+
+ assert_not_same(set2, set1)
+ assert_equal(set3, set2)
+
+ # test2; destructive
+ orig_set1 = set1
+ set1.flatten!
+
+ assert_same(orig_set1, set1)
+ assert_equal(set3, set1)
+
+ # test3; multiple occurrences of a set in an set
+ set1 = Set[1, 2]
+ set2 = Set[set1, Set[set1, 4], 3]
+
+ assert_nothing_raised {
+ set2.flatten!
+ }
+
+ assert_equal(Set.new(1..4), set2)
+
+ # test4; recursion
+ set2 = Set[]
+ set1 = Set[1, set2]
+ set2.add(set1)
+
+ assert_raises(ArgumentError) {
+ set1.flatten!
+ }
+
+ # test5; miscellaneous
+ empty = Set[]
+ set = Set[Set[empty, "a"],Set[empty, "b"]]
+
+ assert_nothing_raised {
+ set.flatten
+ }
+
+ set1 = empty.merge(Set["no_more", set])
+
+ assert_nil(Set.new(0..31).flatten!)
+
+ x = Set[Set[],Set[1,2]].flatten!
+ y = Set[1,2]
+
+ assert_equal(x, y)
+ end
+
+ def test_include?
+ set = Set[1,2,3]
+
+ assert_equal(true, set.include?(1))
+ assert_equal(true, set.include?(2))
+ assert_equal(true, set.include?(3))
+ assert_equal(false, set.include?(0))
+ assert_equal(false, set.include?(nil))
+
+ set = Set["1",nil,"2",nil,"0","1",false]
+ assert_equal(true, set.include?(nil))
+ assert_equal(true, set.include?(false))
+ assert_equal(true, set.include?("1"))
+ assert_equal(false, set.include?(0))
+ assert_equal(false, set.include?(true))
+ end
+
+ def test_superset?
+ set = Set[1,2,3]
+
+ assert_raises(ArgumentError) {
+ set.superset?()
+ }
+
+ assert_raises(ArgumentError) {
+ set.superset?(2)
+ }
+
+ assert_raises(ArgumentError) {
+ set.superset?([2])
+ }
+
+ assert_equal(true, set.superset?(Set[]))
+ assert_equal(true, set.superset?(Set[1,2]))
+ assert_equal(true, set.superset?(Set[1,2,3]))
+ assert_equal(false, set.superset?(Set[1,2,3,4]))
+ assert_equal(false, set.superset?(Set[1,4]))
+
+ assert_equal(true, set >= Set[1,2,3])
+ assert_equal(true, set >= Set[1,2])
+
+ assert_equal(true, Set[].superset?(Set[]))
+ end
+
+ def test_proper_superset?
+ set = Set[1,2,3]
+
+ assert_raises(ArgumentError) {
+ set.proper_superset?()
+ }
+
+ assert_raises(ArgumentError) {
+ set.proper_superset?(2)
+ }
+
+ assert_raises(ArgumentError) {
+ set.proper_superset?([2])
+ }
+
+ assert_equal(true, set.proper_superset?(Set[]))
+ assert_equal(true, set.proper_superset?(Set[1,2]))
+ assert_equal(false, set.proper_superset?(Set[1,2,3]))
+ assert_equal(false, set.proper_superset?(Set[1,2,3,4]))
+ assert_equal(false, set.proper_superset?(Set[1,4]))
+
+ assert_equal(false, set > Set[1,2,3])
+ assert_equal(true, set > Set[1,2])
+
+ assert_equal(false, Set[].proper_superset?(Set[]))
+ end
+
+ def test_subset?
+ set = Set[1,2,3]
+
+ assert_raises(ArgumentError) {
+ set.subset?()
+ }
+
+ assert_raises(ArgumentError) {
+ set.subset?(2)
+ }
+
+ assert_raises(ArgumentError) {
+ set.subset?([2])
+ }
+
+ assert_equal(true, set.subset?(Set[1,2,3,4]))
+ assert_equal(true, set.subset?(Set[1,2,3]))
+ assert_equal(false, set.subset?(Set[1,2]))
+ assert_equal(false, set.subset?(Set[]))
+
+ assert_equal(true, set <= Set[1,2,3])
+ assert_equal(true, set <= Set[1,2,3,4])
+
+ assert_equal(true, Set[].subset?(Set[1]))
+ assert_equal(true, Set[].subset?(Set[]))
+ end
+
+ def test_proper_subset?
+ set = Set[1,2,3]
+
+ assert_raises(ArgumentError) {
+ set.proper_subset?()
+ }
+
+ assert_raises(ArgumentError) {
+ set.proper_subset?(2)
+ }
+
+ assert_raises(ArgumentError) {
+ set.proper_subset?([2])
+ }
+
+ assert_equal(true, set.proper_subset?(Set[1,2,3,4]))
+ assert_equal(false, set.proper_subset?(Set[1,2,3]))
+ assert_equal(false, set.proper_subset?(Set[1,2]))
+ assert_equal(false, set.proper_subset?(Set[]))
+
+ assert_equal(false, set < Set[1,2,3])
+ assert_equal(true, set < Set[1,2,3,4])
+
+ assert_equal(false, Set[].proper_subset?(Set[]))
+ end
+
+ def assert_intersect(expected, set, other)
+ case expected
+ when true
+ assert_send([set, :intersect?, other])
+ assert_send([other, :intersect?, set])
+ assert_not_send([set, :disjoint?, other])
+ assert_not_send([other, :disjoint?, set])
+ when false
+ assert_not_send([set, :intersect?, other])
+ assert_not_send([other, :intersect?, set])
+ assert_send([set, :disjoint?, other])
+ assert_send([other, :disjoint?, set])
+ when Class
+ assert_raises(expected) {
+ set.intersect?(other)
+ }
+ assert_raises(expected) {
+ set.disjoint?(other)
+ }
+ else
+ raise ArgumentError, "%s: unsupported expected value: %s" % [__method__, expected.inspect]
+ end
+ end
+
+ def test_intersect?
+ set = Set[3,4,5]
+
+ assert_intersect(ArgumentError, set, 3)
+ assert_intersect(ArgumentError, set, [2,4,6])
+
+ assert_intersect(true, set, set)
+ assert_intersect(true, set, Set[2,4])
+ assert_intersect(true, set, Set[5,6,7])
+ assert_intersect(true, set, Set[1,2,6,8,4])
+
+ assert_intersect(false, set, Set[])
+ assert_intersect(false, set, Set[0,2])
+ assert_intersect(false, set, Set[0,2,6])
+ assert_intersect(false, set, Set[0,2,6,8,10])
+
+ # Make sure set hasn't changed
+ assert_equal(Set[3,4,5], set)
+ end
+
+ def test_each
+ ary = [1,3,5,7,10,20]
+ set = Set.new(ary)
+
+ ret = set.each { |o| }
+ assert_same(set, ret)
+
+ e = set.each
+ assert_instance_of(Enumerator, e)
+
+ assert_nothing_raised {
+ set.each { |o|
+ ary.delete(o) or raise "unexpected element: #{o}"
+ }
+
+ ary.empty? or raise "forgotten elements: #{ary.join(', ')}"
+ }
+ end
+
+ def test_add
+ set = Set[1,2,3]
+
+ ret = set.add(2)
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3], set)
+
+ ret = set.add?(2)
+ assert_nil(ret)
+ assert_equal(Set[1,2,3], set)
+
+ ret = set.add(4)
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3,4], set)
+
+ ret = set.add?(5)
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3,4,5], set)
+ end
+
+ def test_delete
+ set = Set[1,2,3]
+
+ ret = set.delete(4)
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3], set)
+
+ ret = set.delete?(4)
+ assert_nil(ret)
+ assert_equal(Set[1,2,3], set)
+
+ ret = set.delete(2)
+ assert_equal(set, ret)
+ assert_equal(Set[1,3], set)
+
+ ret = set.delete?(1)
+ assert_equal(set, ret)
+ assert_equal(Set[3], set)
+ end
+
+ def test_delete_if
+ set = Set.new(1..10)
+ ret = set.delete_if { |i| i > 10 }
+ assert_same(set, ret)
+ assert_equal(Set.new(1..10), set)
+
+ set = Set.new(1..10)
+ ret = set.delete_if { |i| i % 3 == 0 }
+ assert_same(set, ret)
+ assert_equal(Set[1,2,4,5,7,8,10], set)
+ end
+
+ def test_keep_if
+ set = Set.new(1..10)
+ ret = set.keep_if { |i| i <= 10 }
+ assert_same(set, ret)
+ assert_equal(Set.new(1..10), set)
+
+ set = Set.new(1..10)
+ ret = set.keep_if { |i| i % 3 != 0 }
+ assert_same(set, ret)
+ assert_equal(Set[1,2,4,5,7,8,10], set)
+ end
+
+ def test_collect!
+ set = Set[1,2,3,'a','b','c',-1..1,2..4]
+
+ ret = set.collect! { |i|
+ case i
+ when Numeric
+ i * 2
+ when String
+ i.upcase
+ else
+ nil
+ end
+ }
+
+ assert_same(set, ret)
+ assert_equal(Set[2,4,6,'A','B','C',nil], set)
+ end
+
+ def test_reject!
+ set = Set.new(1..10)
+
+ ret = set.reject! { |i| i > 10 }
+ assert_nil(ret)
+ assert_equal(Set.new(1..10), set)
+
+ ret = set.reject! { |i| i % 3 == 0 }
+ assert_same(set, ret)
+ assert_equal(Set[1,2,4,5,7,8,10], set)
+ end
+
+ def test_merge
+ set = Set[1,2,3]
+
+ ret = set.merge([2,4,6])
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3,4,6], set)
+ end
+
+ def test_subtract
+ set = Set[1,2,3]
+
+ ret = set.subtract([2,4,6])
+ assert_same(set, ret)
+ assert_equal(Set[1,3], set)
+ end
+
+ def test_plus
+ set = Set[1,2,3]
+
+ ret = set + [2,4,6]
+ assert_not_same(set, ret)
+ assert_equal(Set[1,2,3,4,6], ret)
+ end
+
+ def test_minus
+ set = Set[1,2,3]
+
+ ret = set - [2,4,6]
+ assert_not_same(set, ret)
+ assert_equal(Set[1,3], ret)
+ end
+
+ def test_and
+ set = Set[1,2,3,4]
+
+ ret = set & [2,4,6]
+ assert_not_same(set, ret)
+ assert_equal(Set[2,4], ret)
+ end
+
+ def test_xor
+ set = Set[1,2,3,4]
+ ret = set ^ [2,4,5,5]
+ assert_not_same(set, ret)
+ assert_equal(Set[1,3,5], ret)
+ end
+
+ def test_eq
+ set1 = Set[2,3,1]
+ set2 = Set[1,2,3]
+
+ assert_equal(set1, set1)
+ assert_equal(set1, set2)
+ assert_not_equal(Set[1], [1])
+
+ set1 = Class.new(Set)["a", "b"]
+ set2 = Set["a", "b", set1]
+ set1 = set1.add(set1.clone)
+
+ assert_equal(set2, set2.clone)
+ assert_equal(set1.clone, set1)
+
+ assert_not_equal(Set[Exception.new,nil], Set[Exception.new,Exception.new], "[ruby-dev:26127]")
+ end
+
+ def test_classify
+ set = Set.new(1..10)
+ ret = set.classify { |i| i % 3 }
+
+ assert_equal(3, ret.size)
+ assert_instance_of(Hash, ret)
+ ret.each_value { |value| assert_instance_of(Set, value) }
+ assert_equal(Set[3,6,9], ret[0])
+ assert_equal(Set[1,4,7,10], ret[1])
+ assert_equal(Set[2,5,8], ret[2])
+ end
+
+ def test_divide
+ set = Set.new(1..10)
+ ret = set.divide { |i| i % 3 }
+
+ assert_equal(3, ret.size)
+ n = 0
+ ret.each { |s| n += s.size }
+ assert_equal(set.size, n)
+ assert_equal(set, ret.flatten)
+
+ set = Set[7,10,5,11,1,3,4,9,0]
+ ret = set.divide { |a,b| (a - b).abs == 1 }
+
+ assert_equal(4, ret.size)
+ n = 0
+ ret.each { |s| n += s.size }
+ assert_equal(set.size, n)
+ assert_equal(set, ret.flatten)
+ ret.each { |s|
+ if s.include?(0)
+ assert_equal(Set[0,1], s)
+ elsif s.include?(3)
+ assert_equal(Set[3,4,5], s)
+ elsif s.include?(7)
+ assert_equal(Set[7], s)
+ elsif s.include?(9)
+ assert_equal(Set[9,10,11], s)
+ else
+ raise "unexpected group: #{s.inspect}"
+ end
+ }
+ end
+
+ def test_taintness
+ orig = set = Set[1,2,3]
+ assert_equal false, set.tainted?
+ assert_same orig, set.taint
+ assert_equal true, set.tainted?
+ assert_same orig, set.untaint
+ assert_equal false, set.tainted?
+ end
+
+ def test_freeze
+ orig = set = Set[1,2,3]
+ assert_equal false, set.frozen?
+ set << 4
+ assert_same orig, set.freeze
+ assert_equal true, set.frozen?
+ assert_raises(RuntimeError) {
+ set << 5
+ }
+ assert_equal 4, set.size
+ end
+
+ def test_freeze_dup
+ set1 = Set[1,2,3]
+ set1.freeze
+ set2 = set1.dup
+
+ assert_not_predicate set2, :frozen?
+ assert_nothing_raised {
+ set2.add 4
+ }
+ end
+
+ def test_freeze_clone
+ set1 = Set[1,2,3]
+ set1.freeze
+ set2 = set1.clone
+
+ assert_predicate set2, :frozen?
+ assert_raise(RuntimeError) {
+ set2.add 5
+ }
+ end
+
+ def test_inspect
+ set1 = Set[1]
+
+ assert_equal('#<Set: {1}>', set1.inspect)
+
+ set2 = Set[Set[0], 1, 2, set1]
+ assert_equal(false, set2.inspect.include?('#<Set: {...}>'))
+
+ set1.add(set2)
+ assert_equal(true, set1.inspect.include?('#<Set: {...}>'))
+ end
+end
+
+class TC_SortedSet < Test::Unit::TestCase
+ def test_sortedset
+ s = SortedSet[4,5,3,1,2]
+
+ assert_equal([1,2,3,4,5], s.to_a)
+
+ prev = nil
+ s.each { |o| assert(prev < o) if prev; prev = o }
+ assert_not_nil(prev)
+
+ s.map! { |o| -2 * o }
+
+ assert_equal([-10,-8,-6,-4,-2], s.to_a)
+
+ prev = nil
+ ret = s.each { |o| assert(prev < o) if prev; prev = o }
+ assert_not_nil(prev)
+ assert_same(s, ret)
+
+ s = SortedSet.new([2,1,3]) { |o| o * -2 }
+ assert_equal([-6,-4,-2], s.to_a)
+
+ s = SortedSet.new(['one', 'two', 'three', 'four'])
+ a = []
+ ret = s.delete_if { |o| a << o; o.start_with?('t') }
+ assert_same(s, ret)
+ assert_equal(['four', 'one'], s.to_a)
+ assert_equal(['four', 'one', 'three', 'two'], a)
+
+ s = SortedSet.new(['one', 'two', 'three', 'four'])
+ a = []
+ ret = s.reject! { |o| a << o; o.start_with?('t') }
+ assert_same(s, ret)
+ assert_equal(['four', 'one'], s.to_a)
+ assert_equal(['four', 'one', 'three', 'two'], a)
+
+ s = SortedSet.new(['one', 'two', 'three', 'four'])
+ a = []
+ ret = s.reject! { |o| a << o; false }
+ assert_same(nil, ret)
+ assert_equal(['four', 'one', 'three', 'two'], s.to_a)
+ assert_equal(['four', 'one', 'three', 'two'], a)
+ end
+end
+
+class TC_Enumerable < Test::Unit::TestCase
+ def test_to_set
+ ary = [2,5,4,3,2,1,3]
+
+ set = ary.to_set
+ assert_instance_of(Set, set)
+ assert_equal([1,2,3,4,5], set.sort)
+
+ set = ary.to_set { |o| o * -2 }
+ assert_instance_of(Set, set)
+ assert_equal([-10,-8,-6,-4,-2], set.sort)
+
+ assert_same set, set.to_set
+ assert_not_same set, set.to_set { |o| o }
+
+ set = ary.to_set(SortedSet)
+ assert_instance_of(SortedSet, set)
+ assert_equal([1,2,3,4,5], set.to_a)
+
+ set = ary.to_set(SortedSet) { |o| o * -2 }
+ assert_instance_of(SortedSet, set)
+ assert_equal([-10,-8,-6,-4,-2], set.sort)
+ end
+end
diff --git a/jni/ruby/test/test_shellwords.rb b/jni/ruby/test/test_shellwords.rb
new file mode 100644
index 0000000..59cdbe9
--- /dev/null
+++ b/jni/ruby/test/test_shellwords.rb
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+require 'test/unit'
+require 'shellwords'
+
+class TestShellwords < Test::Unit::TestCase
+
+ include Shellwords
+
+ def setup
+ @not_string = Class.new
+ @cmd = "ruby my_prog.rb | less"
+ end
+
+
+ def test_string
+ assert_instance_of(Array, shellwords(@cmd))
+ assert_equal(4, shellwords(@cmd).length)
+ end
+
+ def test_unmatched_double_quote
+ bad_cmd = 'one two "three'
+ assert_raise ArgumentError do
+ shellwords(bad_cmd)
+ end
+ end
+
+ def test_unmatched_single_quote
+ bad_cmd = "one two 'three"
+ assert_raise ArgumentError do
+ shellwords(bad_cmd)
+ end
+ end
+
+ def test_unmatched_quotes
+ bad_cmd = "one '"'"''""'""
+ assert_raise ArgumentError do
+ shellwords(bad_cmd)
+ end
+ end
+
+ def test_backslashes
+ cmdline, expected = [
+ %q{/a//b///c////d/////e/ "/a//b///c////d/////e/ "'/a//b///c////d/////e/ '/a//b///c////d/////e/ },
+ %q{a/b/c//d//e a/b/c//d//e /a//b///c////d/////e/ a/b/c//d//e }
+ ].map { |str| str.tr("/", "\\\\") }
+
+ assert_equal [expected], shellwords(cmdline)
+ end
+
+ def test_stringification
+ assert_equal "3", shellescape(3)
+ assert_equal "ps -p #{$$}", ['ps', '-p', $$].shelljoin
+ end
+
+ def test_multibyte_characters
+ # This is not a spec. It describes the current behavior which may
+ # be changed in future. There would be no multibyte character
+ # used as shell meta-character that needs to be escaped.
+ assert_equal "\\あ\\い", "あい".shellescape
+ end
+end
diff --git a/jni/ruby/test/test_singleton.rb b/jni/ruby/test/test_singleton.rb
new file mode 100644
index 0000000..259f9c5
--- /dev/null
+++ b/jni/ruby/test/test_singleton.rb
@@ -0,0 +1,103 @@
+require 'test/unit'
+require 'singleton'
+
+class TestSingleton < Test::Unit::TestCase
+ class SingletonTest
+ include Singleton
+ end
+
+ def test_marshal
+ o1 = SingletonTest.instance
+ m = Marshal.dump(o1)
+ o2 = Marshal.load(m)
+ assert_same(o1, o2)
+ end
+
+ def test_instance_never_changes
+ a = SingletonTest.instance
+ b = SingletonTest.instance
+ assert_same a, b
+ end
+
+ def test_initialize_raises_exception
+ assert_raises NoMethodError do
+ SingletonTest.new
+ end
+ end
+
+ def test_allocate_raises_exception
+ assert_raises NoMethodError do
+ SingletonTest.allocate
+ end
+ end
+
+ def test_clone_raises_exception
+ exception = assert_raises TypeError do
+ SingletonTest.instance.clone
+ end
+
+ expected = "can't clone instance of singleton TestSingleton::SingletonTest"
+
+ assert_equal expected, exception.message
+ end
+
+ def test_dup_raises_exception
+ exception = assert_raises TypeError do
+ SingletonTest.instance.dup
+ end
+
+ expected = "can't dup instance of singleton TestSingleton::SingletonTest"
+
+ assert_equal expected, exception.message
+ end
+
+ def test_include_in_module_raises_exception
+ mod = Module.new
+
+ exception = assert_raises TypeError do
+ mod.class_eval do
+ include Singleton
+ end
+ end
+
+ expected = "Inclusion of the OO-Singleton module in module #{mod}"
+
+ assert_equal expected, exception.message
+ end
+
+ def test_extending_singleton_raises_exception
+ assert_raises NoMethodError do
+ 'foo'.extend Singleton
+ end
+ end
+
+ def test_inheritance_works_with_overridden_inherited_method
+ super_super_called = false
+
+ outer = Class.new do
+ define_singleton_method :inherited do |sub|
+ super_super_called = true
+ end
+ end
+
+ inner = Class.new(outer) do
+ include Singleton
+ end
+
+ tester = Class.new(inner)
+
+ assert super_super_called
+
+ a = tester.instance
+ b = tester.instance
+ assert_same a, b
+ end
+
+ def test_class_level_cloning_preserves_singleton_behavior
+ klass = SingletonTest.clone
+
+ a = klass.instance
+ b = klass.instance
+ assert_same a, b
+ end
+end
diff --git a/jni/ruby/test/test_syslog.rb b/jni/ruby/test/test_syslog.rb
new file mode 100644
index 0000000..3164d07
--- /dev/null
+++ b/jni/ruby/test/test_syslog.rb
@@ -0,0 +1,189 @@
+# Please only run this test on machines reasonable for testing.
+# If in doubt, ask your admin.
+
+require 'test/unit'
+
+begin
+ require 'syslog'
+rescue LoadError
+ # suppress error messages.
+end
+
+class TestSyslog < Test::Unit::TestCase
+ def test_new
+ assert_raises(NoMethodError) {
+ Syslog.new
+ }
+ end
+
+ def test_instance
+ sl1 = Syslog.instance
+ sl2 = Syslog.open
+ sl3 = Syslog.instance
+
+ assert_equal(Syslog, sl1)
+ assert_equal(Syslog, sl2)
+ assert_equal(Syslog, sl3)
+ ensure
+ Syslog.close if Syslog.opened?
+ end
+
+ def test_open
+ # default parameters
+ Syslog.open
+
+ assert_equal($0, Syslog.ident)
+ assert_equal(Syslog::LOG_PID | Syslog::LOG_CONS, Syslog.options)
+ assert_equal(Syslog::LOG_USER, Syslog.facility)
+
+ # open without close
+ assert_raises(RuntimeError) {
+ Syslog.open
+ }
+
+ Syslog.close
+
+ # given parameters
+ options = Syslog::LOG_NDELAY | Syslog::LOG_PID
+ Syslog.open("foo", options, Syslog::LOG_DAEMON)
+
+ assert_equal('foo', Syslog.ident)
+ assert_equal(options, Syslog.options)
+ assert_equal(Syslog::LOG_DAEMON, Syslog.facility)
+
+ Syslog.close
+
+ # default parameters again (after close)
+ Syslog.open
+ Syslog.close
+
+ assert_equal(nil, Syslog.ident)
+ assert_equal(nil, Syslog.options)
+ assert_equal(nil, Syslog.facility)
+
+ # block
+ param = nil
+ Syslog.open { |syslog|
+ param = syslog
+ }
+ assert_equal(Syslog, param)
+ ensure
+ Syslog.close if Syslog.opened?
+ end
+
+ def test_opened?
+ assert_equal(false, Syslog.opened?)
+
+ Syslog.open
+ assert_equal(true, Syslog.opened?)
+
+ Syslog.close
+ assert_equal(false, Syslog.opened?)
+
+ Syslog.open {
+ assert_equal(true, Syslog.opened?)
+ }
+
+ assert_equal(false, Syslog.opened?)
+ end
+
+ def test_close
+ assert_raises(RuntimeError) {
+ Syslog.close
+ }
+ end
+
+ def test_mask
+ assert_equal(nil, Syslog.mask)
+
+ Syslog.open
+
+ orig = Syslog.mask
+
+ Syslog.mask = Syslog.LOG_UPTO(Syslog::LOG_ERR)
+ assert_equal(Syslog.LOG_UPTO(Syslog::LOG_ERR), Syslog.mask)
+
+ Syslog.mask = Syslog.LOG_MASK(Syslog::LOG_CRIT)
+ assert_equal(Syslog.LOG_MASK(Syslog::LOG_CRIT), Syslog.mask)
+
+ Syslog.mask = orig
+ ensure
+ Syslog.close if Syslog.opened?
+ end
+
+ def syslog_line_regex(ident, message)
+ /(?:^| )#{Regexp.quote(ident)}(?:\[([1-9][0-9]*)\])?(?: |[: ].* )#{Regexp.quote(message)}$/
+ end
+
+ def test_log
+ IO.pipe {|stderr|
+ pid = fork {
+ stderr[0].close
+ STDERR.reopen(stderr[1])
+ stderr[1].close
+
+ options = Syslog::LOG_PERROR | Syslog::LOG_NDELAY
+
+ Syslog.open("syslog_test", options) { |sl|
+ sl.log(Syslog::LOG_NOTICE, "test1 - hello, %s!", "world")
+ sl.notice("test1 - hello, %s!", "world")
+ }
+
+ Syslog.open("syslog_test", options | Syslog::LOG_PID) { |sl|
+ sl.log(Syslog::LOG_CRIT, "test2 - pid")
+ sl.crit("test2 - pid")
+ }
+ exit!
+ }
+
+ stderr[1].close
+ Process.waitpid(pid)
+
+ # LOG_PERROR is not implemented on Cygwin or Solaris. Only test
+ # these on systems that define it.
+ return unless Syslog.const_defined?(:LOG_PERROR)
+
+ 2.times {
+ re = syslog_line_regex("syslog_test", "test1 - hello, world!")
+ line = stderr[0].gets
+ # In AIX, each LOG_PERROR output line has an appended empty line.
+ if /aix/ =~ RUBY_PLATFORM && line =~ /^$/
+ line = stderr[0].gets
+ end
+ m = re.match(line)
+ assert_not_nil(m)
+ if m[1]
+ # pid is written regardless of LOG_PID on OS X 10.7+
+ assert_equal(pid, m[1].to_i)
+ end
+ }
+
+ 2.times {
+ re = syslog_line_regex("syslog_test", "test2 - pid")
+ line = stderr[0].gets
+ # In AIX, each LOG_PERROR output line has an appended empty line.
+ if /aix/ =~ RUBY_PLATFORM && line =~ /^$/
+ line = stderr[0].gets
+ end
+ m = re.match(line)
+ assert_not_nil(m)
+ assert_not_nil(m[1])
+ assert_equal(pid, m[1].to_i)
+ }
+ }
+ end
+
+ def test_inspect
+ Syslog.open { |sl|
+ assert_equal(format('<#%s: opened=true, ident="%s", options=%d, facility=%d, mask=%d>',
+ Syslog,
+ sl.ident,
+ sl.options,
+ sl.facility,
+ sl.mask),
+ sl.inspect)
+ }
+
+ assert_equal(format('<#%s: opened=false>', Syslog), Syslog.inspect)
+ end
+end if defined?(Syslog)
diff --git a/jni/ruby/test/test_tempfile.rb b/jni/ruby/test/test_tempfile.rb
new file mode 100644
index 0000000..2fe62d3
--- /dev/null
+++ b/jni/ruby/test/test_tempfile.rb
@@ -0,0 +1,343 @@
+require 'test/unit'
+require 'tempfile'
+require 'thread'
+
+class TestTempfile < Test::Unit::TestCase
+ def initialize(*)
+ super
+ @tempfile = nil
+ end
+
+ def tempfile(*args, &block)
+ t = Tempfile.new(*args, &block)
+ @tempfile = (t unless block)
+ end
+
+ def teardown
+ if @tempfile
+ @tempfile.close!
+ end
+ end
+
+ def test_basic
+ t = tempfile("foo")
+ path = t.path
+ t.write("hello world")
+ t.close
+ assert_equal "hello world", File.read(path)
+ end
+
+ def test_saves_in_dir_tmpdir_by_default
+ t = tempfile("foo")
+ assert_equal Dir.tmpdir, File.dirname(t.path)
+ bug3733 = '[ruby-dev:42089]'
+ assert_nothing_raised(SecurityError, bug3733) {
+ proc {$SAFE = 1; File.expand_path(Dir.tmpdir)}.call
+ }
+ end
+
+ def test_saves_in_given_directory
+ subdir = File.join(Dir.tmpdir, "tempfile-test-#{rand}")
+ Dir.mkdir(subdir)
+ begin
+ tempfile = Tempfile.new("foo", subdir)
+ tempfile.close
+ begin
+ assert_equal subdir, File.dirname(tempfile.path)
+ ensure
+ tempfile.unlink
+ end
+ ensure
+ Dir.rmdir(subdir)
+ end
+ end
+
+ def test_basename
+ t = tempfile("foo")
+ assert_match(/^foo/, File.basename(t.path))
+ end
+
+ def test_basename_with_suffix
+ t = tempfile(["foo", ".txt"])
+ assert_match(/^foo/, File.basename(t.path))
+ assert_match(/\.txt$/, File.basename(t.path))
+ end
+
+ def test_unlink
+ t = tempfile("foo")
+ path = t.path
+
+ t.close
+ assert File.exist?(path)
+
+ t.unlink
+ assert !File.exist?(path)
+
+ assert_nil t.path
+ end
+
+ def test_unlink_silently_fails_on_windows
+ tempfile = tempfile("foo")
+ path = tempfile.path
+ begin
+ assert_nothing_raised do
+ tempfile.unlink
+ end
+ ensure
+ tempfile.close
+ File.unlink(path) if File.exist?(path)
+ end
+ end
+
+ def test_unlink_before_close_works_on_posix_systems
+ tempfile = tempfile("foo")
+ begin
+ path = tempfile.path
+ tempfile.unlink
+ assert !File.exist?(path)
+ tempfile.write("hello ")
+ tempfile.write("world\n")
+ tempfile.rewind
+ assert_equal "hello world\n", tempfile.read
+ ensure
+ tempfile.close
+ tempfile.unlink
+ end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_close_and_close_p
+ t = tempfile("foo")
+ assert !t.closed?
+ t.close
+ assert t.closed?
+ end
+
+ def test_close_with_unlink_now_true_works
+ t = tempfile("foo")
+ path = t.path
+ t.close(true)
+ assert t.closed?
+ assert_nil t.path
+ assert !File.exist?(path)
+ end
+
+ def test_close_with_unlink_now_true_does_not_unlink_if_already_unlinked
+ t = tempfile("foo")
+ path = t.path
+ t.unlink
+ File.open(path, "w").close
+ begin
+ t.close(true)
+ assert File.exist?(path)
+ ensure
+ File.unlink(path) rescue nil
+ end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_close_bang_works
+ t = tempfile("foo")
+ path = t.path
+ t.close!
+ assert t.closed?
+ assert_nil t.path
+ assert !File.exist?(path)
+ end
+
+ def test_close_bang_does_not_unlink_if_already_unlinked
+ t = tempfile("foo")
+ path = t.path
+ t.unlink
+ File.open(path, "w").close
+ begin
+ t.close!
+ assert File.exist?(path)
+ ensure
+ File.unlink(path) rescue nil
+ end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_finalizer_does_not_unlink_if_already_unlinked
+ assert_in_out_err('-rtempfile', <<-'EOS') do |(filename,*), (error,*)|
+file = Tempfile.new('foo')
+path = file.path
+puts path
+file.close!
+File.open(path, "w").close
+ EOS
+ assert File.exist?(filename)
+ File.unlink(filename)
+ assert_nil error
+ end
+
+ assert_in_out_err('-rtempfile', <<-'EOS') do |(filename,*), (error,*)|
+file = Tempfile.new('foo')
+path = file.path
+file.unlink
+puts path
+File.open(path, "w").close
+ EOS
+ if !filename.empty?
+ # POSIX unlink semantics supported, continue with test
+ assert File.exist?(filename)
+ File.unlink(filename)
+ end
+ assert_nil error
+ end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_close_does_not_make_path_nil
+ t = tempfile("foo")
+ t.close
+ assert_not_nil t.path
+ end
+
+ def test_close_flushes_buffer
+ t = tempfile("foo")
+ t.write("hello")
+ t.close
+ assert_equal 5, File.size(t.path)
+ end
+
+ def test_tempfile_is_unlinked_when_ruby_exits
+ assert_in_out_err('-rtempfile', <<-'EOS') do |(filename), (error)|
+puts Tempfile.new('foo').path
+ EOS
+ assert !File.exist?(filename)
+ assert_nil(error)
+ end
+ end
+
+ def test_tempfile_finalizer_does_not_run_if_unlinked
+ bug8768 = '[ruby-core:56521] [Bug #8768]'
+ args = %w(--disable-gems -rtempfile)
+ assert_in_out_err(args, <<-'EOS') do |(filename), (error)|
+ tmp = Tempfile.new('foo')
+ puts tmp.path
+ tmp.close
+ tmp.unlink
+ $DEBUG = true
+ EOS
+ assert_file.not_exist?(filename)
+ assert_nil(error, "#{bug8768} we used to get a confusing 'removing ...done' here")
+ end
+ end
+
+ def test_size_flushes_buffer_before_determining_file_size
+ t = tempfile("foo")
+ t.write("hello")
+ assert_equal 0, File.size(t.path)
+ assert_equal 5, t.size
+ assert_equal 5, File.size(t.path)
+ end
+
+ def test_size_works_if_file_is_closed
+ t = tempfile("foo")
+ t.write("hello")
+ t.close
+ assert_equal 5, t.size
+ end
+
+ def test_concurrency
+ threads = []
+ tempfiles = []
+ lock = Mutex.new
+ cond = ConditionVariable.new
+ start = false
+
+ 4.times do
+ threads << Thread.new do
+ lock.synchronize do
+ while !start
+ cond.wait(lock)
+ end
+ end
+ result = []
+ 30.times do
+ result << Tempfile.new('foo')
+ end
+ Thread.current[:result] = result
+ end
+ end
+
+ lock.synchronize do
+ start = true
+ cond.broadcast
+ end
+ threads.each do |thread|
+ thread.join
+ tempfiles |= thread[:result]
+ end
+ filenames = tempfiles.map { |f| f.path }
+ begin
+ assert_equal filenames.size, filenames.uniq.size
+ ensure
+ tempfiles.each do |tempfile|
+ tempfile.close!
+ end
+ end
+ end
+
+ module M
+ end
+
+ def test_extend
+ o = tempfile("foo")
+ o.extend M
+ assert(M === o, "[ruby-dev:32932]")
+ end
+
+ def test_tempfile_encoding_nooption
+ default_external=Encoding.default_external
+ t = tempfile("TEST")
+ t.write("\xE6\x9D\xBE\xE6\xB1\x9F")
+ t.rewind
+ assert_equal(default_external,t.read.encoding)
+ end
+
+ def test_tempfile_encoding_ascii8bit
+ t = tempfile("TEST",:encoding=>"ascii-8bit")
+ t.write("\xE6\x9D\xBE\xE6\xB1\x9F")
+ t.rewind
+ assert_equal(Encoding::ASCII_8BIT,t.read.encoding)
+ end
+
+ def test_tempfile_encoding_ascii8bit2
+ t = tempfile("TEST",Dir::tmpdir,:encoding=>"ascii-8bit")
+ t.write("\xE6\x9D\xBE\xE6\xB1\x9F")
+ t.rewind
+ assert_equal(Encoding::ASCII_8BIT,t.read.encoding)
+ end
+
+ def test_binmode
+ t = tempfile("TEST", mode: IO::BINARY)
+ if IO::BINARY.nonzero?
+ assert(t.binmode?)
+ t.open
+ assert(t.binmode?, 'binmode after reopen')
+ else
+ assert_equal(0600, t.stat.mode & 0777)
+ end
+ end
+
+ def test_create_with_block
+ path = nil
+ Tempfile.create("tempfile-create") {|f|
+ path = f.path
+ assert(File.exist?(path))
+ }
+ assert(!File.exist?(path))
+ end
+
+ def test_create_without_block
+ path = nil
+ f = Tempfile.create("tempfile-create")
+ path = f.path
+ assert(File.exist?(path))
+ f.close
+ assert(File.exist?(path))
+ ensure
+ f.close if f && !f.closed?
+ File.unlink path if path
+ end
+end
+
diff --git a/jni/ruby/test/test_time.rb b/jni/ruby/test/test_time.rb
new file mode 100644
index 0000000..2f34464
--- /dev/null
+++ b/jni/ruby/test/test_time.rb
@@ -0,0 +1,500 @@
+require 'time'
+require 'test/unit'
+
+class TestTimeExtension < Test::Unit::TestCase # :nodoc:
+ def test_rfc822
+ t = Time.rfc2822("26 Aug 76 14:30 EDT")
+ assert_equal(Time.utc(1976, 8, 26, 14, 30) + 4 * 3600, t)
+ assert_equal(-4 * 3600, t.utc_offset)
+ t = Time.rfc2822("27 Aug 76 09:32 PDT")
+ assert_equal(Time.utc(1976, 8, 27, 9, 32) + 7 * 3600, t)
+ assert_equal(-7 * 3600, t.utc_offset)
+ end
+
+ def test_rfc2822
+ t = Time.rfc2822("Fri, 21 Nov 1997 09:55:06 -0600")
+ assert_equal(Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600, t)
+ assert_equal(-6 * 3600, t.utc_offset)
+ t = Time.rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")
+ assert_equal(Time.utc(2003, 7, 1, 10, 52, 37) - 2 * 3600, t)
+ assert_equal(2 * 3600, t.utc_offset)
+ t = Time.rfc2822("Fri, 21 Nov 1997 10:01:10 -0600")
+ assert_equal(Time.utc(1997, 11, 21, 10, 1, 10) + 6 * 3600, t)
+ assert_equal(-6 * 3600, t.utc_offset)
+ t = Time.rfc2822("Fri, 21 Nov 1997 11:00:00 -0600")
+ assert_equal(Time.utc(1997, 11, 21, 11, 0, 0) + 6 * 3600, t)
+ assert_equal(-6 * 3600, t.utc_offset)
+ t = Time.rfc2822("Mon, 24 Nov 1997 14:22:01 -0800")
+ assert_equal(Time.utc(1997, 11, 24, 14, 22, 1) + 8 * 3600, t)
+ assert_equal(-8 * 3600, t.utc_offset)
+ begin
+ Time.at(-1)
+ rescue ArgumentError
+ # ignore
+ else
+ t = Time.rfc2822("Thu, 13 Feb 1969 23:32:54 -0330")
+ assert_equal(Time.utc(1969, 2, 13, 23, 32, 54) + 3 * 3600 + 30 * 60, t)
+ assert_equal(-3 * 3600 - 30 * 60, t.utc_offset)
+ t = Time.rfc2822(" Thu,
+ 13
+ Feb
+ 1969
+ 23:32
+ -0330 (Newfoundland Time)")
+ assert_equal(Time.utc(1969, 2, 13, 23, 32, 0) + 3 * 3600 + 30 * 60, t)
+ assert_equal(-3 * 3600 - 30 * 60, t.utc_offset)
+ end
+ t = Time.rfc2822("21 Nov 97 09:55:06 GMT")
+ assert_equal(Time.utc(1997, 11, 21, 9, 55, 6), t)
+ assert_equal(0, t.utc_offset)
+ t = Time.rfc2822("Fri, 21 Nov 1997 09 : 55 : 06 -0600")
+ assert_equal(Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600, t)
+ assert_equal(-6 * 3600, t.utc_offset)
+ assert_raise(ArgumentError) {
+ # inner comment is not supported.
+ Time.rfc2822("Fri, 21 Nov 1997 09(comment): 55 : 06 -0600")
+ }
+ t = Time.rfc2822("Mon, 01 Jan 0001 00:00:00 -0000")
+ assert_equal(Time.utc(1, 1, 1, 0, 0, 0), t)
+ assert_equal(0, t.utc_offset)
+ assert_equal(true, t.utc?)
+ end
+
+ def test_encode_rfc2822
+ t = Time.utc(1)
+ assert_equal("Mon, 01 Jan 0001 00:00:00 -0000", t.rfc2822)
+ end
+
+ def test_rfc2616
+ t = Time.utc(1994, 11, 6, 8, 49, 37)
+ assert_equal(t, Time.httpdate("Sun, 06 Nov 1994 08:49:37 GMT"))
+ assert_equal(t, Time.httpdate("Sunday, 06-Nov-94 08:49:37 GMT"))
+ assert_equal(t, Time.httpdate("Sun Nov 6 08:49:37 1994"))
+ assert_equal(Time.utc(1995, 11, 15, 6, 25, 24),
+ Time.httpdate("Wed, 15 Nov 1995 06:25:24 GMT"))
+ assert_equal(Time.utc(1995, 11, 15, 4, 58, 8),
+ Time.httpdate("Wed, 15 Nov 1995 04:58:08 GMT"))
+ assert_equal(Time.utc(1994, 11, 15, 8, 12, 31),
+ Time.httpdate("Tue, 15 Nov 1994 08:12:31 GMT"))
+ assert_equal(Time.utc(1994, 12, 1, 16, 0, 0),
+ Time.httpdate("Thu, 01 Dec 1994 16:00:00 GMT"))
+ assert_equal(Time.utc(1994, 10, 29, 19, 43, 31),
+ Time.httpdate("Sat, 29 Oct 1994 19:43:31 GMT"))
+ assert_equal(Time.utc(1994, 11, 15, 12, 45, 26),
+ Time.httpdate("Tue, 15 Nov 1994 12:45:26 GMT"))
+ assert_equal(Time.utc(1999, 12, 31, 23, 59, 59),
+ Time.httpdate("Fri, 31 Dec 1999 23:59:59 GMT"))
+
+ assert_equal(Time.utc(2007, 12, 23, 11, 22, 33),
+ Time.httpdate('Sunday, 23-Dec-07 11:22:33 GMT'))
+ end
+
+ def test_encode_httpdate
+ t = Time.utc(1)
+ assert_equal("Mon, 01 Jan 0001 00:00:00 GMT", t.httpdate)
+ end
+
+ def subtest_xmlschema_alias(method)
+ t = Time.utc(1985, 4, 12, 23, 20, 50, 520000)
+ s = "1985-04-12T23:20:50.52Z"
+ assert_equal(t, Time.iso8601(s))
+ assert_equal(s, t.iso8601(2))
+
+ t = Time.utc(1996, 12, 20, 0, 39, 57)
+ s = "1996-12-19T16:39:57-08:00"
+ assert_equal(t, Time.iso8601(s))
+ # There is no way to generate time string with arbitrary timezone.
+ s = "1996-12-20T00:39:57Z"
+ assert_equal(t, Time.iso8601(s))
+ assert_equal(s, t.iso8601)
+
+ t = Time.utc(1990, 12, 31, 23, 59, 60)
+ s = "1990-12-31T23:59:60Z"
+ assert_equal(t, Time.iso8601(s))
+ # leap second is representable only if timezone file has it.
+ s = "1990-12-31T15:59:60-08:00"
+ assert_equal(t, Time.iso8601(s))
+
+ begin
+ Time.at(-1)
+ rescue ArgumentError
+ # ignore
+ else
+ t = Time.utc(1937, 1, 1, 11, 40, 27, 870000)
+ s = "1937-01-01T12:00:27.87+00:20"
+ assert_equal(t, Time.iso8601(s))
+ end
+ end
+
+ # http://www.w3.org/TR/xmlschema-2/
+ def subtest_xmlschema(method)
+ assert_equal(Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600,
+ Time.__send__(method, "1999-05-31T13:20:00-05:00"))
+ assert_equal(Time.local(2000, 1, 20, 12, 0, 0),
+ Time.__send__(method, "2000-01-20T12:00:00"))
+ assert_equal(Time.utc(2000, 1, 20, 12, 0, 0),
+ Time.__send__(method, "2000-01-20T12:00:00Z"))
+ assert_equal(Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600,
+ Time.__send__(method, "2000-01-20T12:00:00+12:00"))
+ assert_equal(Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600,
+ Time.__send__(method, "2000-01-20T12:00:00-13:00"))
+ assert_equal(Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600,
+ Time.__send__(method, "2000-03-04T23:00:00+03:00"))
+ assert_equal(Time.utc(2000, 3, 4, 20, 0, 0),
+ Time.__send__(method, "2000-03-04T20:00:00Z"))
+ assert_equal(Time.local(2000, 1, 15, 0, 0, 0),
+ Time.__send__(method, "2000-01-15T00:00:00"))
+ assert_equal(Time.local(2000, 2, 15, 0, 0, 0),
+ Time.__send__(method, "2000-02-15T00:00:00"))
+ assert_equal(Time.local(2000, 1, 15, 12, 0, 0),
+ Time.__send__(method, "2000-01-15T12:00:00"))
+ assert_equal(Time.utc(2000, 1, 16, 12, 0, 0),
+ Time.__send__(method, "2000-01-16T12:00:00Z"))
+ assert_equal(Time.local(2000, 1, 1, 12, 0, 0),
+ Time.__send__(method, "2000-01-01T12:00:00"))
+ assert_equal(Time.utc(1999, 12, 31, 23, 0, 0),
+ Time.__send__(method, "1999-12-31T23:00:00Z"))
+ assert_equal(Time.local(2000, 1, 16, 12, 0, 0),
+ Time.__send__(method, "2000-01-16T12:00:00"))
+ assert_equal(Time.local(2000, 1, 16, 0, 0, 0),
+ Time.__send__(method, "2000-01-16T00:00:00"))
+ assert_equal(Time.utc(2000, 1, 12, 12, 13, 14),
+ Time.__send__(method, "2000-01-12T12:13:14Z"))
+ assert_equal(Time.utc(2001, 4, 17, 19, 23, 17, 300000),
+ Time.__send__(method, "2001-04-17T19:23:17.3Z"))
+ assert_equal(Time.utc(2000, 1, 2, 0, 0, 0),
+ Time.__send__(method, "2000-01-01T24:00:00Z"))
+ assert_raise(ArgumentError) { Time.__send__(method, "2000-01-01T00:00:00.+00:00") }
+ end
+
+ def subtest_xmlschema_encode(method)
+ bug6100 = '[ruby-core:42997]'
+
+ t = Time.utc(2001, 4, 17, 19, 23, 17, 300000)
+ assert_equal("2001-04-17T19:23:17Z", t.__send__(method))
+ assert_equal("2001-04-17T19:23:17.3Z", t.__send__(method, 1))
+ assert_equal("2001-04-17T19:23:17.300000Z", t.__send__(method, 6))
+ assert_equal("2001-04-17T19:23:17.3000000Z", t.__send__(method, 7))
+ assert_equal("2001-04-17T19:23:17.3Z", t.__send__(method, 1.9), bug6100)
+
+ t = Time.utc(2001, 4, 17, 19, 23, 17, 123456)
+ assert_equal("2001-04-17T19:23:17.1234560Z", t.__send__(method, 7))
+ assert_equal("2001-04-17T19:23:17.123456Z", t.__send__(method, 6))
+ assert_equal("2001-04-17T19:23:17.12345Z", t.__send__(method, 5))
+ assert_equal("2001-04-17T19:23:17.1Z", t.__send__(method, 1))
+ assert_equal("2001-04-17T19:23:17.1Z", t.__send__(method, 1.9), bug6100)
+
+ t = Time.at(2.quo(3)).getlocal("+09:00")
+ assert_equal("1970-01-01T09:00:00.666+09:00", t.__send__(method, 3))
+ assert_equal("1970-01-01T09:00:00.6666666666+09:00", t.__send__(method, 10))
+ assert_equal("1970-01-01T09:00:00.66666666666666666666+09:00", t.__send__(method, 20))
+ assert_equal("1970-01-01T09:00:00.6+09:00", t.__send__(method, 1.1), bug6100)
+ assert_equal("1970-01-01T09:00:00.666+09:00", t.__send__(method, 3.2), bug6100)
+
+ t = Time.at(123456789.quo(9999999999)).getlocal("+09:00")
+ assert_equal("1970-01-01T09:00:00.012+09:00", t.__send__(method, 3))
+ assert_equal("1970-01-01T09:00:00.012345678+09:00", t.__send__(method, 9))
+ assert_equal("1970-01-01T09:00:00.0123456789+09:00", t.__send__(method, 10))
+ assert_equal("1970-01-01T09:00:00.0123456789012345678+09:00", t.__send__(method, 19))
+ assert_equal("1970-01-01T09:00:00.01234567890123456789+09:00", t.__send__(method, 20))
+ assert_equal("1970-01-01T09:00:00.012+09:00", t.__send__(method, 3.8), bug6100)
+
+ t = Time.utc(1)
+ assert_equal("0001-01-01T00:00:00Z", t.__send__(method))
+
+ begin
+ Time.at(-1)
+ rescue ArgumentError
+ # ignore
+ else
+ t = Time.utc(1960, 12, 31, 23, 0, 0, 123456)
+ assert_equal("1960-12-31T23:00:00.123456Z", t.__send__(method, 6))
+ end
+
+ assert_equal(249, Time.__send__(method, "2008-06-05T23:49:23.000249+09:00").usec)
+
+ assert_equal("10000-01-01T00:00:00Z", Time.utc(10000).__send__(method))
+ assert_equal("9999-01-01T00:00:00Z", Time.utc(9999).__send__(method))
+ assert_equal("0001-01-01T00:00:00Z", Time.utc(1).__send__(method)) # 1 AD
+ assert_equal("0000-01-01T00:00:00Z", Time.utc(0).__send__(method)) # 1 BC
+ assert_equal("-0001-01-01T00:00:00Z", Time.utc(-1).__send__(method)) # 2 BC
+ assert_equal("-0004-01-01T00:00:00Z", Time.utc(-4).__send__(method)) # 5 BC
+ assert_equal("-9999-01-01T00:00:00Z", Time.utc(-9999).__send__(method))
+ assert_equal("-10000-01-01T00:00:00Z", Time.utc(-10000).__send__(method))
+ end
+
+ def test_completion
+ now = Time.local(2001,11,29,21,26,35)
+ assert_equal(Time.local( 2001,11,29,21,12),
+ Time.parse("2001/11/29 21:12", now))
+ assert_equal(Time.local( 2001,11,29),
+ Time.parse("2001/11/29", now))
+ assert_equal(Time.local( 2001,11,29),
+ Time.parse( "11/29", now))
+ #assert_equal(Time.local(2001,11,1), Time.parse("Nov", now))
+ assert_equal(Time.local( 2001,11,29,10,22),
+ Time.parse( "10:22", now))
+ assert_raise(ArgumentError) { Time.parse("foo", now) }
+ end
+
+ def test_completion_with_different_timezone
+ now = Time.new(2001,2,3,0,0,0,"+09:00") # 2001-02-02 15:00:00 UTC
+ t = Time.parse("10:20:30 GMT", now)
+ assert_equal(Time.utc(2001,2,2,10,20,30), t)
+ assert_equal(false, t.utc?)
+ assert_equal(0, t.utc_offset)
+ end
+
+ def test_invalid
+ # They were actually used in some web sites.
+ assert_raise(ArgumentError) { Time.httpdate("1 Dec 2001 10:23:57 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Sat, 1 Dec 2001 10:25:42 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Sat, 1-Dec-2001 10:53:55 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Saturday, 01-Dec-2001 10:15:34 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Saturday, 01-Dec-101 11:10:07 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Fri, 30 Nov 2001 21:30:00 JST") }
+
+ # They were actually used in some mails.
+ assert_raise(ArgumentError) { Time.rfc2822("01-5-20") }
+ assert_raise(ArgumentError) { Time.rfc2822("7/21/00") }
+ assert_raise(ArgumentError) { Time.rfc2822("2001-8-28") }
+ assert_raise(ArgumentError) { Time.rfc2822("00-5-6 1:13:06") }
+ assert_raise(ArgumentError) { Time.rfc2822("2001-9-27 9:36:49") }
+ assert_raise(ArgumentError) { Time.rfc2822("2000-12-13 11:01:11") }
+ assert_raise(ArgumentError) { Time.rfc2822("2001/10/17 04:29:55") }
+ assert_raise(ArgumentError) { Time.rfc2822("9/4/2001 9:23:19 PM") }
+ assert_raise(ArgumentError) { Time.rfc2822("01 Nov 2001 09:04:31") }
+ assert_raise(ArgumentError) { Time.rfc2822("13 Feb 2001 16:4 GMT") }
+ assert_raise(ArgumentError) { Time.rfc2822("01 Oct 00 5:41:19 PM") }
+ assert_raise(ArgumentError) { Time.rfc2822("2 Jul 00 00:51:37 JST") }
+ assert_raise(ArgumentError) { Time.rfc2822("01 11 2001 06:55:57 -0500") }
+ assert_raise(ArgumentError) { Time.rfc2822("18 \343\366\356\341\370 2000") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, Oct 2001 18:53:32") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 2 Nov 2001 03:47:54") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 27 Jul 2001 11.14.14 +0200") }
+ assert_raise(ArgumentError) { Time.rfc2822("Thu, 2 Nov 2000 04:13:53 -600") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed, 5 Apr 2000 22:57:09 JST") }
+ assert_raise(ArgumentError) { Time.rfc2822("Mon, 11 Sep 2000 19:47:33 00000") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 28 Apr 2000 20:40:47 +-900") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 19 Jan 2001 8:15:36 AM -0500") }
+ assert_raise(ArgumentError) { Time.rfc2822("Thursday, Sep 27 2001 7:42:35 AM EST") }
+ assert_raise(ArgumentError) { Time.rfc2822("3/11/2001 1:31:57 PM Pacific Daylight Time") }
+ assert_raise(ArgumentError) { Time.rfc2822("Mi, 28 Mrz 2001 11:51:36") }
+ assert_raise(ArgumentError) { Time.rfc2822("P, 30 sept 2001 23:03:14") }
+ assert_raise(ArgumentError) { Time.rfc2822("fr, 11 aug 2000 18:39:22") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fr, 21 Sep 2001 17:44:03 -1000") }
+ assert_raise(ArgumentError) { Time.rfc2822("Mo, 18 Jun 2001 19:21:40 -1000") }
+ assert_raise(ArgumentError) { Time.rfc2822("l\366, 12 aug 2000 18:53:20") }
+ assert_raise(ArgumentError) { Time.rfc2822("l\366, 26 maj 2001 00:15:58") }
+ assert_raise(ArgumentError) { Time.rfc2822("Dom, 30 Sep 2001 17:36:30") }
+ assert_raise(ArgumentError) { Time.rfc2822("%&, 31 %2/ 2000 15:44:47 -0500") }
+ assert_raise(ArgumentError) { Time.rfc2822("dom, 26 ago 2001 03:57:07 -0300") }
+ assert_raise(ArgumentError) { Time.rfc2822("ter, 04 set 2001 16:27:58 -0300") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") }
+ assert_raise(ArgumentError) { Time.rfc2822("ele, 11 h: 2000 12:42:15 -0500") }
+ assert_raise(ArgumentError) { Time.rfc2822("Tue, 14 Aug 2001 3:55:3 +0200") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 25 Aug 2000 9:3:48 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 1 Dec 2000 0:57:50 EST") }
+ assert_raise(ArgumentError) { Time.rfc2822("Mon, 7 May 2001 9:39:51 +0200") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed, 1 Aug 2001 16:9:15 +0200") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed, 23 Aug 2000 9:17:36 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 11 Aug 2000 10:4:42 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("Sat, 15 Sep 2001 13:22:2 +0300") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed,16 \276\305\324\302 2001 20:06:25 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed,7 \312\256\322\273\324\302 2001 23:47:22 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=C5=DA),?= 10 2 2001 23:32:26 +0900 (JST)") }
+ assert_raise(ArgumentError) { Time.rfc2822("\307\341\314\343\332\311, 30 \344\346\335\343\310\321 2001 10:01:06") }
+ assert_raise(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=BF=E5),?= 12 =?iso-8859-1?Q?9=B7=EE?= 2001 14:52:41\n+0900 (JST)") }
+
+ # Out of range arguments
+ assert_raise(ArgumentError) { Time.parse("2014-13-13T18:00:00-0900") }
+ end
+
+ def test_zone_0000
+ assert_equal(true, Time.parse("2000-01-01T00:00:00Z").utc?)
+ assert_equal(true, Time.parse("2000-01-01T00:00:00-00:00").utc?)
+ assert_equal(false, Time.parse("2000-01-01T00:00:00+00:00").utc?)
+ assert_equal(false, Time.parse("Sat, 01 Jan 2000 00:00:00 GMT").utc?)
+ assert_equal(true, Time.parse("Sat, 01 Jan 2000 00:00:00 -0000").utc?)
+ assert_equal(false, Time.parse("Sat, 01 Jan 2000 00:00:00 +0000").utc?)
+ assert_equal(false, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 GMT").utc?)
+ assert_equal(true, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 -0000").utc?)
+ assert_equal(false, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 +0000").utc?)
+ assert_equal(true, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 UTC").utc?)
+ end
+
+ def test_rfc2822_utc_roundtrip_winter
+ t1 = Time.local(2008,12,1)
+ t2 = Time.rfc2822(t1.rfc2822)
+ assert_equal(t1.utc?, t2.utc?, "[ruby-dev:37126]")
+ end
+
+ def test_rfc2822_utc_roundtrip_summer
+ t1 = Time.local(2008,8,1)
+ t2 = Time.rfc2822(t1.rfc2822)
+ assert_equal(t1.utc?, t2.utc?)
+ end
+
+ def test_parse_leap_second
+ t = Time.utc(1998,12,31,23,59,59)
+ assert_equal(t, Time.parse("Thu Dec 31 23:59:59 UTC 1998"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:59:59 -0000 1998"));t.localtime
+ assert_equal(t, Time.parse("Fri Jan 1 08:59:59 +0900 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 00:59:59 +0100 1999"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:59:59 +0000 1998"))
+ assert_equal(t, Time.parse("Fri Dec 31 22:59:59 -0100 1998"));t.utc
+ t += 1
+ assert_equal(t, Time.parse("Thu Dec 31 23:59:60 UTC 1998"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:59:60 -0000 1998"));t.localtime
+ assert_equal(t, Time.parse("Fri Jan 1 08:59:60 +0900 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 00:59:60 +0100 1999"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:59:60 +0000 1998"))
+ assert_equal(t, Time.parse("Fri Dec 31 22:59:60 -0100 1998"));t.utc
+ t += 1 if t.sec == 60
+ assert_equal(t, Time.parse("Thu Jan 1 00:00:00 UTC 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 00:00:00 -0000 1999"));t.localtime
+ assert_equal(t, Time.parse("Fri Jan 1 09:00:00 +0900 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 01:00:00 +0100 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 00:00:00 +0000 1999"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:00:00 -0100 1998"))
+ end
+
+ def test_rfc2822_leap_second
+ t = Time.utc(1998,12,31,23,59,59)
+ assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:59 UTC"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:59 +0900"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:59 +0100"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 +0000"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:59 -0100"));t.utc
+ t += 1
+ assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:60 UTC"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:60 +0900"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:60 +0100"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 +0000"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:60 -0100"));t.utc
+ t += 1 if t.sec == 60
+ assert_equal(t, Time.rfc2822("Thu, 1 Jan 1999 00:00:00 UTC"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 09:00:00 +0900"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 01:00:00 +0100"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 +0000"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:00:00 -0100"))
+ end
+
+ def subtest_xmlschema_leap_second(method)
+ t = Time.utc(1998,12,31,23,59,59)
+ assert_equal(t, Time.__send__(method, "1998-12-31T23:59:59Z"))
+ assert_equal(t, Time.__send__(method, "1998-12-31T23:59:59-00:00"));t.localtime
+ assert_equal(t, Time.__send__(method, "1999-01-01T08:59:59+09:00"))
+ assert_equal(t, Time.__send__(method, "1999-01-01T00:59:59+01:00"))
+ assert_equal(t, Time.__send__(method, "1998-12-31T23:59:59+00:00"))
+ assert_equal(t, Time.__send__(method, "1998-12-31T22:59:59-01:00"));t.utc
+ t += 1
+ assert_equal(t, Time.__send__(method, "1998-12-31T23:59:60Z"))
+ assert_equal(t, Time.__send__(method, "1998-12-31T23:59:60-00:00"));t.localtime
+ assert_equal(t, Time.__send__(method, "1999-01-01T08:59:60+09:00"))
+ assert_equal(t, Time.__send__(method, "1999-01-01T00:59:60+01:00"))
+ assert_equal(t, Time.__send__(method, "1998-12-31T23:59:60+00:00"))
+ assert_equal(t, Time.__send__(method, "1998-12-31T22:59:60-01:00"));t.utc
+ t += 1 if t.sec == 60
+ assert_equal(t, Time.__send__(method, "1999-01-01T00:00:00Z"))
+ assert_equal(t, Time.__send__(method, "1999-01-01T00:00:00-00:00"));t.localtime
+ assert_equal(t, Time.__send__(method, "1999-01-01T09:00:00+09:00"))
+ assert_equal(t, Time.__send__(method, "1999-01-01T01:00:00+01:00"))
+ assert_equal(t, Time.__send__(method, "1999-01-01T00:00:00+00:00"))
+ assert_equal(t, Time.__send__(method, "1998-12-31T23:00:00-01:00"))
+ end
+
+ def subtest_xmlschema_fraction(method)
+ assert_equal(500000, Time.__send__(method, "2000-01-01T00:00:00.5+00:00").tv_usec)
+ end
+
+ def test_ruby_talk_152866
+ t = Time::xmlschema('2005-08-30T22:48:00-07:00')
+ assert_equal(30, t.day)
+ assert_equal(8, t.mon)
+ assert_equal(-7*3600, t.utc_offset)
+ t.utc
+ assert_equal(31, t.day)
+ assert_equal(8, t.mon)
+ assert_equal(0, t.utc_offset)
+ end
+
+ def test_parse_fraction
+ assert_equal(500000, Time.parse("2000-01-01T00:00:00.5+00:00").tv_usec)
+ end
+
+ def test_strptime
+ assert_equal(Time.utc(2005, 8, 28, 06, 54, 20), Time.strptime("28/Aug/2005:06:54:20 +0000", "%d/%b/%Y:%T %z"))
+ assert_equal(Time.at(1).localtime, Time.strptime("1", "%s"))
+ assert_equal(false, Time.strptime('0', '%s').utc?)
+ end
+
+ def test_strptime_empty
+ assert_raise(ArgumentError) { Time.strptime('', '') }
+ assert_raise(ArgumentError) { Time.strptime('+09:00', '%z') } # [ruby-core:62351] by matz.
+ end
+
+ def test_strptime_s_z
+ t = Time.strptime('0 +0100', '%s %z')
+ assert_equal(0, t.to_r)
+ assert_equal(3600, t.utc_offset)
+ t = Time.strptime('0 UTC', '%s %z')
+ assert_equal(0, t.to_r)
+ assert_equal(0, t.utc_offset)
+ assert_equal(true, t.utc?)
+ end
+
+ def test_strptime_s_N
+ assert_equal(Time.at(1, 500000), Time.strptime("1.5", "%s.%N"))
+ assert_equal(Time.at(-2, 500000), Time.strptime("-1.5", "%s.%N"))
+ t = Time.strptime("1.000000000001", "%s.%N")
+ assert_equal(1, t.to_i)
+ assert_equal(Rational("0.000000000001"), t.subsec)
+ t = Time.strptime("-1.000000000001", "%s.%N")
+ assert_equal(-2, t.to_i)
+ assert_equal(1-Rational("0.000000000001"), t.subsec)
+ end
+
+ def test_strptime_Ymd_z
+ t = Time.strptime('20010203 -0200', '%Y%m%d %z')
+ assert_equal(2001, t.year)
+ assert_equal(2, t.mon)
+ assert_equal(3, t.day)
+ assert_equal(0, t.hour)
+ assert_equal(0, t.min)
+ assert_equal(0, t.sec)
+ assert_equal(-7200, t.utc_offset)
+ t = Time.strptime('20010203 UTC', '%Y%m%d %z')
+ assert_equal(2001, t.year)
+ assert_equal(2, t.mon)
+ assert_equal(3, t.day)
+ assert_equal(0, t.hour)
+ assert_equal(0, t.min)
+ assert_equal(0, t.sec)
+ assert_equal(0, t.utc_offset)
+ assert_equal(true, t.utc?)
+ end
+
+ def test_nsec
+ assert_equal(123456789, Time.parse("2000-01-01T00:00:00.123456789+00:00").tv_nsec)
+ end
+
+ def subtest_xmlschema_nsec(method)
+ assert_equal(123456789, Time.__send__(method, "2000-01-01T00:00:00.123456789+00:00").tv_nsec)
+ end
+
+ def test_huge_precision
+ bug4456 = '[ruby-dev:43284]'
+ assert_normal_exit %q{ Time.now.strftime("%1000000000F") }, bug4456
+ end
+
+ instance_methods(false).grep(/\Asub(test_xmlschema.*)/) do |sub|
+ test = $1
+ define_method(test) {__send__(sub, :xmlschema)}
+ define_method(test.sub(/xmlschema/, 'iso8601')) {__send__(sub, :iso8601)}
+ end
+end
diff --git a/jni/ruby/test/test_timeout.rb b/jni/ruby/test/test_timeout.rb
new file mode 100644
index 0000000..e4eba7f
--- /dev/null
+++ b/jni/ruby/test/test_timeout.rb
@@ -0,0 +1,100 @@
+require 'test/unit'
+require 'timeout'
+require 'thread'
+
+class TestTimeout < Test::Unit::TestCase
+ def test_queue
+ q = Queue.new
+ assert_raise(Timeout::Error, "[ruby-dev:32935]") {
+ timeout(0.01) { q.pop }
+ }
+ end
+
+ def test_timeout
+ assert_raise(Timeout::Error) do
+ Timeout.timeout(0.1) {
+ nil while true
+ }
+ end
+ end
+
+ def test_cannot_convert_into_time_interval
+ bug3168 = '[ruby-dev:41010]'
+ def (n = Object.new).zero?; false; end
+ assert_raise(TypeError, bug3168) {Timeout.timeout(n) { sleep 0.1 }}
+ end
+
+ def test_skip_rescue
+ bug8730 = '[Bug #8730]'
+ e = nil
+ assert_raise_with_message(Timeout::Error, /execution expired/, bug8730) do
+ timeout 0.01 do
+ begin
+ sleep 3
+ rescue Exception => e
+ end
+ end
+ end
+ assert_nil(e, bug8730)
+ end
+
+ def test_rescue_exit
+ exc = Class.new(RuntimeError)
+ e = nil
+ assert_nothing_raised(exc) do
+ timeout 0.01, exc do
+ begin
+ sleep 3
+ rescue exc => e
+ end
+ end
+ end
+ assert_raise_with_message(exc, /execution expired/) {raise e if e}
+ end
+
+ def test_custom_exception
+ bug9354 = '[ruby-core:59511] [Bug #9354]'
+ err = Class.new(StandardError) do
+ def initialize(msg) super end
+ end
+ assert_nothing_raised(ArgumentError, bug9354) do
+ assert_equal(:ok, timeout(100, err) {:ok})
+ end
+ end
+
+ def test_exit_exception
+ assert_raise_with_message(Timeout::Error, "boon") do
+ Timeout.timeout(10, Timeout::Error) do
+ raise Timeout::Error, "boon"
+ end
+ end
+ end
+
+ def test_enumerator_next
+ bug9380 = '[ruby-dev:47872] [Bug #9380]: timeout in Enumerator#next'
+ e = (o=Object.new).to_enum
+ def o.each
+ sleep
+ end
+ assert_raise_with_message(Timeout::Error, 'execution expired', bug9380) do
+ Timeout.timeout(0.01) {e.next}
+ end
+ end
+
+ def test_handle_interrupt
+ bug11344 = '[ruby-dev:49179] [Bug #11344]'
+ ok = false
+ assert_raise(Timeout::Error) {
+ Thread.handle_interrupt(Timeout::Error => :never) {
+ Timeout.timeout(0.01) {
+ sleep 0.2
+ ok = true
+ Thread.handle_interrupt(Timeout::Error => :on_blocking) {
+ sleep 0.2
+ }
+ }
+ }
+ }
+ assert(ok, bug11344)
+ end
+end
diff --git a/jni/ruby/test/test_tmpdir.rb b/jni/ruby/test/test_tmpdir.rb
new file mode 100644
index 0000000..62b4abf
--- /dev/null
+++ b/jni/ruby/test/test_tmpdir.rb
@@ -0,0 +1,39 @@
+require 'test/unit'
+require 'tmpdir'
+
+class TestTmpdir < Test::Unit::TestCase
+ def test_world_writable
+ skip "no meaning on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM
+ Dir.mktmpdir do |tmpdir|
+ # ToDo: fix for parallel test
+ olddir, ENV["TMPDIR"] = ENV["TMPDIR"], tmpdir
+ begin
+ assert_equal(tmpdir, Dir.tmpdir)
+ File.chmod(0777, tmpdir)
+ assert_not_equal(tmpdir, Dir.tmpdir)
+ File.chmod(01777, tmpdir)
+ assert_equal(tmpdir, Dir.tmpdir)
+ ensure
+ ENV["TMPDIR"] = olddir
+ end
+ end
+ end
+
+ def test_no_homedir
+ bug7547 = '[ruby-core:50793]'
+ home, ENV["HOME"] = ENV["HOME"], nil
+ dir = assert_nothing_raised(bug7547) do
+ break Dir.mktmpdir("~")
+ end
+ assert_match(/\A~/, File.basename(dir), bug7547)
+ ensure
+ ENV["HOME"] = home
+ Dir.rmdir(dir) if dir
+ end
+
+ def test_mktmpdir_nil
+ Dir.mktmpdir(nil) {|d|
+ assert_kind_of(String, d)
+ }
+ end
+end
diff --git a/jni/ruby/test/test_tracer.rb b/jni/ruby/test/test_tracer.rb
new file mode 100644
index 0000000..ff3ba87
--- /dev/null
+++ b/jni/ruby/test/test_tracer.rb
@@ -0,0 +1,60 @@
+require 'test/unit'
+require 'tmpdir'
+
+class TestTracer < Test::Unit::TestCase
+ include EnvUtil
+
+ def test_tracer_with_option_r
+ assert_in_out_err(%w[-rtracer -e 1]) do |(*lines),|
+ case lines.size
+ when 1
+ # do nothing
+ else
+ assert_match(%r{rubygems/core_ext/kernel_require\.rb:\d+:Kernel:<:}, lines[0])
+ end
+ assert_equal "#0:-e:1::-: 1", lines.last
+ end
+ end
+
+ def test_tracer_with_option_r_without_gems
+ assert_in_out_err(%w[--disable-gems -rtracer -e 1]) do |(*lines),|
+ case lines.size
+ when 1
+ # do nothing
+ else
+ flunk "unexpected output from `ruby --disable-gems -rtracer -e 1`"
+ end
+ assert_equal "#0:-e:1::-: 1", lines.last
+ end
+ end
+
+ def test_tracer_with_require
+ Dir.mktmpdir("test_ruby_tracer") do |dir|
+ script = File.join(dir, "require_tracer.rb")
+ open(script, "w") do |f|
+ f.print <<-EOF
+require 'tracer'
+1
+ EOF
+ end
+ assert_in_out_err([script]) do |(*lines),|
+ assert_empty(lines)
+ end
+ end
+ end
+
+ def test_tracer_with_require_without_gems
+ Dir.mktmpdir("test_ruby_tracer") do |dir|
+ script = File.join(dir, "require_tracer.rb")
+ open(script, "w") do |f|
+ f.print <<-EOF
+require 'tracer'
+1
+ EOF
+ end
+ assert_in_out_err(["--disable-gems", script]) do |(*lines),|
+ assert_empty(lines)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/test_tsort.rb b/jni/ruby/test/test_tsort.rb
new file mode 100644
index 0000000..4a13a6e
--- /dev/null
+++ b/jni/ruby/test/test_tsort.rb
@@ -0,0 +1,113 @@
+require 'tsort'
+require 'test/unit'
+
+class TSortHash < Hash # :nodoc:
+ include TSort
+ alias tsort_each_node each_key
+ def tsort_each_child(node, &block)
+ fetch(node).each(&block)
+ end
+end
+
+class TSortArray < Array # :nodoc:
+ include TSort
+ alias tsort_each_node each_index
+ def tsort_each_child(node, &block)
+ fetch(node).each(&block)
+ end
+end
+
+class TSortTest < Test::Unit::TestCase # :nodoc:
+ def test_dag
+ h = TSortHash[{1=>[2, 3], 2=>[3], 3=>[]}]
+ assert_equal([3, 2, 1], h.tsort)
+ assert_equal([[3], [2], [1]], h.strongly_connected_components)
+ end
+
+ def test_cycle
+ h = TSortHash[{1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}]
+ assert_equal([[4], [2, 3], [1]],
+ h.strongly_connected_components.map {|nodes| nodes.sort})
+ assert_raise(TSort::Cyclic) { h.tsort }
+ end
+
+ def test_array
+ a = TSortArray[[1], [0], [0], [2]]
+ assert_equal([[0, 1], [2], [3]],
+ a.strongly_connected_components.map {|nodes| nodes.sort})
+
+ a = TSortArray[[], [0]]
+ assert_equal([[0], [1]],
+ a.strongly_connected_components.map {|nodes| nodes.sort})
+ end
+
+ def test_s_tsort
+ g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ each_node = lambda {|&b| g.each_key(&b) }
+ each_child = lambda {|n, &b| g[n].each(&b) }
+ assert_equal([4, 2, 3, 1], TSort.tsort(each_node, each_child))
+ g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ assert_raise(TSort::Cyclic) { TSort.tsort(each_node, each_child) }
+ end
+
+ def test_s_tsort_each
+ g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ each_node = lambda {|&b| g.each_key(&b) }
+ each_child = lambda {|n, &b| g[n].each(&b) }
+ r = []
+ TSort.tsort_each(each_node, each_child) {|n| r << n }
+ assert_equal([4, 2, 3, 1], r)
+
+ r = TSort.tsort_each(each_node, each_child).map {|n| n.to_s }
+ assert_equal(['4', '2', '3', '1'], r)
+ end
+
+ def test_s_strongly_connected_components
+ g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ each_node = lambda {|&b| g.each_key(&b) }
+ each_child = lambda {|n, &b| g[n].each(&b) }
+ assert_equal([[4], [2], [3], [1]],
+ TSort.strongly_connected_components(each_node, each_child))
+ g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ assert_equal([[4], [2, 3], [1]],
+ TSort.strongly_connected_components(each_node, each_child))
+ end
+
+ def test_s_each_strongly_connected_component
+ g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
+ each_node = lambda {|&b| g.each_key(&b) }
+ each_child = lambda {|n, &b| g[n].each(&b) }
+ r = []
+ TSort.each_strongly_connected_component(each_node, each_child) {|scc|
+ r << scc
+ }
+ assert_equal([[4], [2], [3], [1]], r)
+ g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ r = []
+ TSort.each_strongly_connected_component(each_node, each_child) {|scc|
+ r << scc
+ }
+ assert_equal([[4], [2, 3], [1]], r)
+
+ r = TSort.each_strongly_connected_component(each_node, each_child).map {|scc|
+ scc.map(&:to_s)
+ }
+ assert_equal([['4'], ['2', '3'], ['1']], r)
+ end
+
+ def test_s_each_strongly_connected_component_from
+ g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ each_child = lambda {|n, &b| g[n].each(&b) }
+ r = []
+ TSort.each_strongly_connected_component_from(1, each_child) {|scc|
+ r << scc
+ }
+ assert_equal([[4], [2, 3], [1]], r)
+
+ r = TSort.each_strongly_connected_component_from(1, each_child).map {|scc|
+ scc.map(&:to_s)
+ }
+ assert_equal([['4'], ['2', '3'], ['1']], r)
+ end
+end
+
diff --git a/jni/ruby/test/test_unicode_normalize.rb b/jni/ruby/test/test_unicode_normalize.rb
new file mode 100644
index 0000000..fdc9d3c
--- /dev/null
+++ b/jni/ruby/test/test_unicode_normalize.rb
@@ -0,0 +1,184 @@
+# coding: utf-8
+
+# Copyright Ayumu Nojima (野島 歩) and Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require 'test/unit'
+require 'unicode_normalize/normalize'
+
+class TestUnicodeNormalize < Test::Unit::TestCase
+
+ UNICODE_VERSION = UnicodeNormalize::UNICODE_VERSION
+
+ NormTest = Struct.new :source, :NFC, :NFD, :NFKC, :NFKD, :line
+
+ def read_tests
+ IO.readlines(File.expand_path("../enc/unicode/data/#{UNICODE_VERSION}/NormalizationTest.txt", __dir__), encoding: 'utf-8')
+ .tap { |lines| assert_include(lines[0], "NormalizationTest-#{UNICODE_VERSION}.txt")}
+ .collect.with_index { |linedata, linenumber| [linedata, linenumber]}
+ .reject { |line| line[0] =~ /^[\#@]/ }
+ .collect do |line|
+ NormTest.new *(line[0].split(';').take(5).collect do |code_string|
+ code_string.split(/\s/).collect { |cp| cp.to_i(16) }.pack('U*')
+ end + [line[1]+1])
+ end
+ end
+
+ def to_codepoints(string)
+ string.codepoints.collect { |cp| cp.to_s(16).upcase.rjust(4, '0') }
+ end
+
+ def setup
+ @@tests ||= read_tests
+ rescue Errno::ENOENT => e
+ @@tests ||= []
+ skip e.message
+ end
+
+ def self.generate_test_normalize(target, normalization, source, prechecked)
+ define_method "test_normalize_to_#{target}_from_#{source}_with_#{normalization}" do
+ expected = actual = test = nil
+ mesg = proc {"#{to_codepoints(expected)} expected but was #{to_codepoints(actual)} on line #{test[:line]} (#{normalization})"}
+ @@tests.each do |t|
+ test = t
+ if prechecked.nil? or test[prechecked]==test[source]
+ expected = test[target]
+ actual = test[source].unicode_normalize(normalization)
+ assert_equal expected, actual, mesg
+ end
+ end
+ end
+ end
+
+# source; NFC; NFD; NFKC; NFKD
+# NFC
+# :NFC == toNFC(:source) == toNFC(:NFC) == toNFC(:NFD)
+ generate_test_normalize :NFC, :nfc, :source, nil
+ generate_test_normalize :NFC, :nfc, :NFC, :source
+ generate_test_normalize :NFC, :nfc, :NFD, :source
+# :NFKC == toNFC(:NFKC) == toNFC(:NFKD)
+ generate_test_normalize :NFKC, :nfc, :NFKC, nil
+ generate_test_normalize :NFKC, :nfc, :NFKD, :NFKC
+#
+# NFD
+# :NFD == toNFD(:source) == toNFD(:NFC) == toNFD(:NFD)
+ generate_test_normalize :NFD, :nfd, :source, nil
+ generate_test_normalize :NFD, :nfd, :NFC, :source
+ generate_test_normalize :NFD, :nfd, :NFD, :source
+# :NFKD == toNFD(:NFKC) == toNFD(:NFKD)
+ generate_test_normalize :NFKD, :nfd, :NFKC, nil
+ generate_test_normalize :NFKD, :nfd, :NFKD, :NFKC
+#
+# NFKC
+# :NFKC == toNFKC(:source) == toNFKC(:NFC) == toNFKC(:NFD) == toNFKC(:NFKC) == toNFKC(:NFKD)
+ generate_test_normalize :NFKC, :nfkc, :source, nil
+ generate_test_normalize :NFKC, :nfkc, :NFC, :source
+ generate_test_normalize :NFKC, :nfkc, :NFD, :source
+ generate_test_normalize :NFKC, :nfkc, :NFKC, :NFC
+ generate_test_normalize :NFKC, :nfkc, :NFKD, :NFD
+#
+# NFKD
+# :NFKD == toNFKD(:source) == toNFKD(:NFC) == toNFKD(:NFD) == toNFKD(:NFKC) == toNFKD(:NFKD)
+ generate_test_normalize :NFKD, :nfkd, :source, nil
+ generate_test_normalize :NFKD, :nfkd, :NFC, :source
+ generate_test_normalize :NFKD, :nfkd, :NFD, :source
+ generate_test_normalize :NFKD, :nfkd, :NFKC, :NFC
+ generate_test_normalize :NFKD, :nfkd, :NFKD, :NFD
+
+ def self.generate_test_check_true(source, normalization)
+ define_method "test_check_true_#{source}_as_#{normalization}" do
+ test = nil
+ mesg = proc {"#{to_codepoints(test[source])} should check as #{normalization} but does not on line #{test[:line]}"}
+ @@tests.each do |t|
+ test = t
+ actual = test[source].unicode_normalized?(normalization)
+ assert_equal true, actual, mesg
+ end
+ end
+ end
+
+ def self.generate_test_check_false(source, compare, normalization)
+ define_method "test_check_false_#{source}_as_#{normalization}" do
+ test = nil
+ mesg = proc {"#{to_codepoints(test[source])} should not check as #{normalization} but does on line #{test[:line]}"}
+ @@tests.each do |t|
+ test = t
+ if test[source] != test[compare]
+ actual = test[source].unicode_normalized?(normalization)
+ assert_equal false, actual, mesg
+ end
+ end
+ end
+ end
+
+ generate_test_check_true :NFC, :nfc
+ generate_test_check_true :NFD, :nfd
+ generate_test_check_true :NFKC, :nfc
+ generate_test_check_true :NFKC, :nfkc
+ generate_test_check_true :NFKD, :nfd
+ generate_test_check_true :NFKD, :nfkd
+
+ generate_test_check_false :source, :NFD, :nfd
+ generate_test_check_false :NFC, :NFD, :nfd
+ generate_test_check_false :NFKC, :NFKD, :nfd
+ generate_test_check_false :source, :NFC, :nfc
+ generate_test_check_false :NFD, :NFC, :nfc
+ generate_test_check_false :NFKD, :NFKC, :nfc
+ generate_test_check_false :source, :NFKD, :nfkd
+ generate_test_check_false :NFC, :NFKD, :nfkd
+ generate_test_check_false :NFD, :NFKD, :nfkd
+ generate_test_check_false :NFKC, :NFKD, :nfkd
+ generate_test_check_false :source, :NFKC, :nfkc
+ generate_test_check_false :NFC, :NFKC, :nfkc
+ generate_test_check_false :NFD, :NFKC, :nfkc
+ generate_test_check_false :NFKD, :NFKC, :nfkc
+
+ def test_non_UTF_8
+ assert_equal "\u1E0A".encode('UTF-16BE'), "D\u0307".encode('UTF-16BE').unicode_normalize(:nfc)
+ assert_equal true, "\u1E0A".encode('UTF-16BE').unicode_normalized?(:nfc)
+ assert_equal false, "D\u0307".encode('UTF-16BE').unicode_normalized?(:nfc)
+ end
+
+ def test_singleton_with_accents
+ assert_equal "\u0136", "\u212A\u0327".unicode_normalize(:nfc)
+ end
+
+ def test_partial_jamo_compose
+ assert_equal "\uAC01", "\uAC00\u11A8".unicode_normalize(:nfc)
+ end
+
+ def test_partial_jamo_decompose
+ assert_equal "\u1100\u1161\u11A8", "\uAC00\u11A8".unicode_normalize(:nfd)
+ end
+
+ def test_hangul_plus_accents
+ assert_equal "\uAC00\u0323\u0300", "\uAC00\u0300\u0323".unicode_normalize(:nfc)
+ assert_equal "\uAC00\u0323\u0300", "\u1100\u1161\u0300\u0323".unicode_normalize(:nfc)
+ assert_equal "\u1100\u1161\u0323\u0300", "\uAC00\u0300\u0323".unicode_normalize(:nfd)
+ assert_equal "\u1100\u1161\u0323\u0300", "\u1100\u1161\u0300\u0323".unicode_normalize(:nfd)
+ end
+
+ def test_raise_exception_for_non_unicode_encoding
+ assert_raise(Encoding::CompatibilityError) { "abc".force_encoding('ISO-8859-1').unicode_normalize }
+ assert_raise(Encoding::CompatibilityError) { "abc".force_encoding('ISO-8859-1').unicode_normalize! }
+ assert_raise(Encoding::CompatibilityError) { "abc".force_encoding('ISO-8859-1').unicode_normalized? }
+ end
+
+ def test_us_ascii
+ ascii_string = 'abc'.encode('US-ASCII')
+
+ assert_equal ascii_string, ascii_string.unicode_normalize
+ assert_equal ascii_string, ascii_string.unicode_normalize(:nfd)
+ assert_equal ascii_string, ascii_string.unicode_normalize(:nfkc)
+ assert_equal ascii_string, ascii_string.unicode_normalize(:nfkd)
+
+ assert_equal ascii_string, ascii_string.unicode_normalize!
+ assert_equal ascii_string, ascii_string.unicode_normalize!(:nfd)
+ assert_equal ascii_string, ascii_string.unicode_normalize!(:nfkc)
+ assert_equal ascii_string, ascii_string.unicode_normalize!(:nfkd)
+
+ assert_equal true, ascii_string.unicode_normalized?
+ assert_equal true, ascii_string.unicode_normalized?(:nfd)
+ assert_equal true, ascii_string.unicode_normalized?(:nfkc)
+ assert_equal true, ascii_string.unicode_normalized?(:nfkd)
+ end
+end
diff --git a/jni/ruby/test/test_weakref.rb b/jni/ruby/test/test_weakref.rb
new file mode 100644
index 0000000..d21b431
--- /dev/null
+++ b/jni/ruby/test/test_weakref.rb
@@ -0,0 +1,71 @@
+require 'test/unit'
+require 'weakref'
+
+class TestWeakRef < Test::Unit::TestCase
+ def make_weakref(level = 10)
+ if level > 0
+ make_weakref(level - 1)
+ else
+ WeakRef.new(Object.new)
+ end
+ end
+
+ def test_ref
+ obj = Object.new
+ weak = WeakRef.new(obj)
+ assert_equal(obj.to_s, weak.to_s)
+ assert_predicate(weak, :weakref_alive?)
+ end
+
+ def test_recycled
+ weaks = []
+ weak = nil
+ 100.times do
+ weaks << make_weakref
+ ObjectSpace.garbage_collect
+ ObjectSpace.garbage_collect
+ break if weak = weaks.find {|w| !w.weakref_alive?}
+ end
+ assert_raise(WeakRef::RefError) {weak.to_s}
+ assert_not_predicate(weak, :weakref_alive?)
+ end
+
+ def test_not_reference_different_object
+ bug7304 = '[ruby-core:49044]'
+ weakrefs = []
+ 3.times do
+ obj = Object.new
+ def obj.foo; end
+ weakrefs << WeakRef.new(obj)
+ ObjectSpace.garbage_collect
+ end
+ assert_nothing_raised(NoMethodError, bug7304) {
+ weakrefs.each do |weak|
+ begin
+ weak.foo
+ rescue WeakRef::RefError
+ end
+ end
+ }
+ end
+
+ def test_weakref_finalize
+ bug7304 = '[ruby-core:49044]'
+ assert_normal_exit %q{
+ require 'weakref'
+ obj = Object.new
+ 3.times do
+ WeakRef.new(obj)
+ ObjectSpace.garbage_collect
+ end
+ }, bug7304
+ end
+
+ def test_repeated_object_leak
+ bug10537 = '[ruby-core:66428]'
+ assert_no_memory_leak(%w(-rweakref), '', <<-'end;', bug10537, timeout: 60)
+ a = Object.new
+ 150_000.times { WeakRef.new(a) }
+ end;
+ end
+end
diff --git a/jni/ruby/test/test_win32api.rb b/jni/ruby/test/test_win32api.rb
new file mode 100644
index 0000000..b1856a3
--- /dev/null
+++ b/jni/ruby/test/test_win32api.rb
@@ -0,0 +1,23 @@
+require "test/unit"
+begin
+ require "Win32API"
+rescue LoadError
+end
+
+class TestWin32API < Test::Unit::TestCase
+ def test_params_string
+ m2w = Win32API.new("kernel32", "MultiByteToWideChar", "ilpipi", "i")
+ str = "utf-8 string".encode("utf-8")
+ buf = "\0" * (str.size * 2)
+ assert_equal str.size, m2w.call(65001, 0, str, str.bytesize, buf, str.size)
+ assert_equal str.encode("utf-16le"), buf.force_encoding("utf-16le")
+ end
+
+ def test_params_array
+ m2w = Win32API.new("kernel32", "MultiByteToWideChar", ["i", "l", "p", "i", "p", "i"], "i")
+ str = "utf-8 string".encode("utf-8")
+ buf = "\0" * (str.size * 2)
+ assert_equal str.size, m2w.call(65001, 0, str, str.bytesize, buf, str.size)
+ assert_equal str.encode("utf-16le"), buf.force_encoding("utf-16le")
+ end
+end if defined?(Win32API)
diff --git a/jni/ruby/test/testunit/test4test_hideskip.rb b/jni/ruby/test/testunit/test4test_hideskip.rb
new file mode 100644
index 0000000..7dba6f5
--- /dev/null
+++ b/jni/ruby/test/testunit/test4test_hideskip.rb
@@ -0,0 +1,9 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+
+require 'test/unit'
+
+class TestForTestHideSkip < Test::Unit::TestCase
+ def test_skip
+ skip "do nothing"
+ end
+end
diff --git a/jni/ruby/test/testunit/test4test_redefinition.rb b/jni/ruby/test/testunit/test4test_redefinition.rb
new file mode 100644
index 0000000..77cfd45
--- /dev/null
+++ b/jni/ruby/test/testunit/test4test_redefinition.rb
@@ -0,0 +1,13 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+
+require 'test/unit'
+
+class TestForTestRedefinition < Test::Unit::TestCase
+ def test_redefinition
+ skip "do nothing (1)"
+ end
+
+ def test_redefinition
+ skip "do nothing (2)"
+ end
+end
diff --git a/jni/ruby/test/testunit/test4test_sorting.rb b/jni/ruby/test/testunit/test4test_sorting.rb
new file mode 100644
index 0000000..b8bb943
--- /dev/null
+++ b/jni/ruby/test/testunit/test4test_sorting.rb
@@ -0,0 +1,17 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+
+require 'test/unit'
+
+class TestForTestHideSkip < Test::Unit::TestCase
+ def test_c
+ skip "do nothing"
+ end
+
+ def test_b
+ assert_equal true, false
+ end
+
+ def test_a
+ raise
+ end
+end
diff --git a/jni/ruby/test/testunit/test_assertion.rb b/jni/ruby/test/testunit/test_assertion.rb
new file mode 100644
index 0000000..374d6e0
--- /dev/null
+++ b/jni/ruby/test/testunit/test_assertion.rb
@@ -0,0 +1,8 @@
+require 'test/unit'
+class TestAssertion < Test::Unit::TestCase
+ def test_wrong_assertion
+ error, line = assert_raise(ArgumentError) {assert(true, true)}, __LINE__
+ assert_match(/assertion message must be String or Proc, but TrueClass was given/, error.message)
+ assert_match(/\A#{Regexp.quote(__FILE__)}:#{line}:/, error.backtrace[0])
+ end
+end
diff --git a/jni/ruby/test/testunit/test_hideskip.rb b/jni/ruby/test/testunit/test_hideskip.rb
new file mode 100644
index 0000000..e696b5e
--- /dev/null
+++ b/jni/ruby/test/testunit/test_hideskip.rb
@@ -0,0 +1,16 @@
+require 'test/unit'
+
+class TestHideSkip < Test::Unit::TestCase
+ def test_hideskip
+ assert_not_match(/assertions\/s.\n\n 1\) Skipped/, hideskip)
+ assert_match(/assertions\/s.\n\n 1\) Skipped/, hideskip("--show-skip"))
+ assert_match(/assertions\/s.\n\n1 tests, 0 assertions, 0 failures, 0 errors, 1 skips/, hideskip("--hide-skip"))
+ end
+
+ def hideskip(*args)
+ IO.popen([*@options[:ruby], "#{File.dirname(__FILE__)}/test4test_hideskip.rb",
+ "--verbose", *args], err: [:child, :out]) {|f|
+ f.read
+ }
+ end
+end
diff --git a/jni/ruby/test/testunit/test_parallel.rb b/jni/ruby/test/testunit/test_parallel.rb
new file mode 100644
index 0000000..db3f47e
--- /dev/null
+++ b/jni/ruby/test/testunit/test_parallel.rb
@@ -0,0 +1,191 @@
+require 'test/unit'
+require 'timeout'
+
+module TestParallel
+ PARALLEL_RB = "#{File.dirname(__FILE__)}/../lib/test/unit/parallel.rb"
+ TESTS = "#{File.dirname(__FILE__)}/tests_for_parallel"
+
+ class TestParallelWorker < Test::Unit::TestCase
+ def setup
+ i, @worker_in = IO.pipe
+ @worker_out, o = IO.pipe
+ @worker_pid = spawn(*@options[:ruby], PARALLEL_RB,
+ "--ruby", @options[:ruby].join(" "),
+ "-j", "t1", "-v", out: o, in: i)
+ [i,o].each(&:close)
+ end
+
+ def teardown
+ if @worker_pid && @worker_in
+ begin
+ begin
+ @worker_in.puts "quit"
+ rescue IOError, Errno::EPIPE
+ end
+ timeout(2) do
+ Process.waitpid(@worker_pid)
+ end
+ rescue Timeout::Error
+ begin
+ Process.kill(:KILL, @worker_pid)
+ rescue Errno::ESRCH
+ end
+ end
+ end
+ ensure
+ @worker_in.close
+ @worker_out.close
+ end
+
+ def test_run
+ timeout(10) do
+ assert_match(/^ready/,@worker_out.gets)
+ @worker_in.puts "run #{TESTS}/ptest_first.rb test"
+ assert_match(/^okay/,@worker_out.gets)
+ assert_match(/^p/,@worker_out.gets)
+ assert_match(/^done/,@worker_out.gets)
+ assert_match(/^ready/,@worker_out.gets)
+ end
+ end
+
+ def test_run_multiple_testcase_in_one_file
+ timeout(10) do
+ assert_match(/^ready/,@worker_out.gets)
+ @worker_in.puts "run #{TESTS}/ptest_second.rb test"
+ assert_match(/^okay/,@worker_out.gets)
+ assert_match(/^p/,@worker_out.gets)
+ assert_match(/^done/,@worker_out.gets)
+ assert_match(/^p/,@worker_out.gets)
+ assert_match(/^done/,@worker_out.gets)
+ assert_match(/^ready/,@worker_out.gets)
+ end
+ end
+
+ def test_accept_run_command_multiple_times
+ timeout(10) do
+ assert_match(/^ready/,@worker_out.gets)
+ @worker_in.puts "run #{TESTS}/ptest_first.rb test"
+ assert_match(/^okay/,@worker_out.gets)
+ assert_match(/^p/,@worker_out.gets)
+ assert_match(/^done/,@worker_out.gets)
+ assert_match(/^ready/,@worker_out.gets)
+ @worker_in.puts "run #{TESTS}/ptest_second.rb test"
+ assert_match(/^okay/,@worker_out.gets)
+ assert_match(/^p/,@worker_out.gets)
+ assert_match(/^done/,@worker_out.gets)
+ assert_match(/^p/,@worker_out.gets)
+ assert_match(/^done/,@worker_out.gets)
+ assert_match(/^ready/,@worker_out.gets)
+ end
+ end
+
+ def test_p
+ timeout(10) do
+ @worker_in.puts "run #{TESTS}/ptest_first.rb test"
+ while buf = @worker_out.gets
+ break if /^p (.+?)$/ =~ buf
+ end
+ assert_match(/TestA#test_nothing_test = \d+\.\d+ s = \.\n/, $1.chomp.unpack("m")[0])
+ end
+ end
+
+ def test_done
+ timeout(10) do
+ @worker_in.puts "run #{TESTS}/ptest_forth.rb test"
+ 7.times { @worker_out.gets }
+ buf = @worker_out.gets
+ assert_match(/^done (.+?)$/, buf)
+
+ /^done (.+?)$/ =~ buf
+
+ result = Marshal.load($1.chomp.unpack("m")[0])
+
+ assert_equal(5, result[0])
+ assert_equal(2, result[1])
+ assert_kind_of(Array,result[2])
+ assert_kind_of(Array,result[3])
+ assert_kind_of(Array,result[4])
+ assert_kind_of(Array,result[2][1])
+ assert_kind_of(MiniTest::Assertion,result[2][0][2])
+ assert_kind_of(MiniTest::Skip,result[2][1][2])
+ assert_kind_of(MiniTest::Skip,result[2][2][2])
+ assert_kind_of(Exception, result[2][3][2])
+ assert_equal(result[5], "TestE")
+ end
+ end
+
+ def test_quit
+ timeout(10) do
+ @worker_in.puts "quit"
+ assert_match(/^bye$/m,@worker_out.read)
+ end
+ end
+ end
+
+ class TestParallel < Test::Unit::TestCase
+ def spawn_runner(*opt_args)
+ @test_out, o = IO.pipe
+ @test_pid = spawn(*@options[:ruby], TESTS+"/runner.rb",
+ "--ruby", @options[:ruby].join(" "),
+ "-j","t1",*opt_args, out: o, err: o)
+ o.close
+ end
+
+ def teardown
+ begin
+ if @test_pid
+ timeout(2) do
+ Process.waitpid(@test_pid)
+ end
+ end
+ rescue Timeout::Error
+ Process.kill(:KILL, @test_pid) if @test_pid
+ ensure
+ @test_out.close if @test_out
+ end
+ end
+
+ def test_ignore_jzero
+ @test_out, o = IO.pipe
+ @test_pid = spawn(*@options[:ruby], TESTS+"/runner.rb",
+ "--ruby", @options[:ruby].join(" "),
+ "-j","0", out: File::NULL, err: o)
+ o.close
+ timeout(10) {
+ assert_match(/Error: parameter of -j option should be greater than 0/,@test_out.read)
+ }
+ end
+
+ def test_should_run_all_without_any_leaks
+ spawn_runner
+ buf = timeout(10){@test_out.read}
+ assert_match(/^[SFE\.]{9}$/,buf)
+ end
+
+ def test_should_retry_failed_on_workers
+ spawn_runner
+ buf = timeout(10){@test_out.read}
+ assert_match(/^Retrying\.+$/,buf)
+ end
+
+ def test_no_retry_option
+ spawn_runner "--no-retry"
+ buf = timeout(10){@test_out.read}
+ refute_match(/^Retrying\.+$/,buf)
+ assert_match(/^ +\d+\) Failure:\nTestD#test_fail_at_worker/,buf)
+ end
+
+ def test_jobs_status
+ spawn_runner "--jobs-status"
+ buf = timeout(10){@test_out.read}
+ assert_match(/\d+=ptest_(first|second|third|forth) */,buf)
+ end
+
+ def test_separate
+ # this test depends to --jobs-status
+ spawn_runner "--jobs-status", "--separate"
+ buf = timeout(10){@test_out.read}
+ assert(buf.scan(/(\d+?)[:=]/).flatten.uniq.size > 1)
+ end
+ end
+end
diff --git a/jni/ruby/test/testunit/test_rake_integration.rb b/jni/ruby/test/testunit/test_rake_integration.rb
new file mode 100644
index 0000000..0d0f8ef
--- /dev/null
+++ b/jni/ruby/test/testunit/test_rake_integration.rb
@@ -0,0 +1,35 @@
+require 'minitest/autorun'
+require 'tmpdir'
+
+class RakeIntegration < MiniTest::Unit::TestCase
+ include Test::Unit::Assertions
+ RAKE_LOADER = File.expand_path(
+ File.join(
+ File.dirname(__FILE__),
+ '..',
+ '..',
+ 'lib',
+ 'rake',
+ 'rake_test_loader.rb'))
+
+ def test_with_rake_runner
+ Dir.mktmpdir do |dir|
+ filename = File.join dir, 'testing.rb'
+ File.open(filename, 'wb') do |f|
+ f.write <<-eotest
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+require 'test/unit'
+raise 'loaded twice' if defined?(FooTest)
+class FooTest; end
+ eotest
+ end
+
+ args = %w{ -w } + [RAKE_LOADER, filename]
+ bug3972 = "[ruby-core:32864]"
+ status = assert_in_out_err(args, "",
+ /0 tests, 0 assertions, 0 failures, 0 errors, 0 skips/,
+ [], bug3972)
+ assert_equal(true, status.success?, bug3972)
+ end
+ end
+end
diff --git a/jni/ruby/test/testunit/test_redefinition.rb b/jni/ruby/test/testunit/test_redefinition.rb
new file mode 100644
index 0000000..ed11621
--- /dev/null
+++ b/jni/ruby/test/testunit/test_redefinition.rb
@@ -0,0 +1,15 @@
+require 'test/unit'
+
+class TestRedefinition < Test::Unit::TestCase
+ def test_redefinition
+ assert_match(/^test\/unit warning: method TestForTestRedefinition#test_redefinition is redefined$/,
+ redefinition)
+ end
+
+ def redefinition(*args)
+ IO.popen([*@options[:ruby], "#{File.dirname(__FILE__)}/test4test_redefinition.rb", *args],
+ err: [:child, :out]) {|f|
+ f.read
+ }
+ end
+end
diff --git a/jni/ruby/test/testunit/test_sorting.rb b/jni/ruby/test/testunit/test_sorting.rb
new file mode 100644
index 0000000..e13ca1f
--- /dev/null
+++ b/jni/ruby/test/testunit/test_sorting.rb
@@ -0,0 +1,17 @@
+require 'test/unit'
+
+class TestTestUnitSorting < Test::Unit::TestCase
+ def test_sorting
+ result = sorting("--show-skip")
+ assert_match(/^ 1\) Skipped:/, result)
+ assert_match(/^ 2\) Failure:/, result)
+ assert_match(/^ 3\) Error:/, result)
+ end
+
+ def sorting(*args)
+ IO.popen([*@options[:ruby], "#{File.dirname(__FILE__)}/test4test_sorting.rb",
+ "--verbose", *args], err: [:child, :out]) {|f|
+ f.read
+ }
+ end
+end
diff --git a/jni/ruby/test/testunit/tests_for_parallel/ptest_first.rb b/jni/ruby/test/testunit/tests_for_parallel/ptest_first.rb
new file mode 100644
index 0000000..66c7704
--- /dev/null
+++ b/jni/ruby/test/testunit/tests_for_parallel/ptest_first.rb
@@ -0,0 +1,7 @@
+require 'test/unit'
+
+class TestA < Test::Unit::TestCase
+ def test_nothing_test
+ end
+end
+
diff --git a/jni/ruby/test/testunit/tests_for_parallel/ptest_forth.rb b/jni/ruby/test/testunit/tests_for_parallel/ptest_forth.rb
new file mode 100644
index 0000000..46c88da
--- /dev/null
+++ b/jni/ruby/test/testunit/tests_for_parallel/ptest_forth.rb
@@ -0,0 +1,29 @@
+require 'test/unit'
+
+class TestE < Test::Unit::TestCase
+ class UnknownError < RuntimeError; end
+
+ def test_not_fail
+ assert_equal(1,1)
+ end
+
+ def test_always_skip
+ skip "always"
+ end
+
+ def test_always_fail
+ assert_equal(0,1)
+ end
+
+ def test_skip_after_unknown_error
+ begin
+ raise UnknownError, "unknown error"
+ rescue
+ skip "after raise"
+ end
+ end
+
+ def test_unknown_error
+ raise UnknownError, "unknown error"
+ end
+end
diff --git a/jni/ruby/test/testunit/tests_for_parallel/ptest_second.rb b/jni/ruby/test/testunit/tests_for_parallel/ptest_second.rb
new file mode 100644
index 0000000..7004d68
--- /dev/null
+++ b/jni/ruby/test/testunit/tests_for_parallel/ptest_second.rb
@@ -0,0 +1,11 @@
+require 'test/unit'
+
+class TestB < Test::Unit::TestCase
+ def test_nothing
+ end
+end
+
+class TestC < Test::Unit::TestCase
+ def test_nothing
+ end
+end
diff --git a/jni/ruby/test/testunit/tests_for_parallel/ptest_third.rb b/jni/ruby/test/testunit/tests_for_parallel/ptest_third.rb
new file mode 100644
index 0000000..338fc32
--- /dev/null
+++ b/jni/ruby/test/testunit/tests_for_parallel/ptest_third.rb
@@ -0,0 +1,10 @@
+require 'test/unit'
+
+class TestD < Test::Unit::TestCase
+ def test_fail_at_worker
+ #if /test\/unit\/parallel\.rb/ =~ $0
+ if on_parallel_worker?
+ assert_equal(0,1)
+ end
+ end
+end
diff --git a/jni/ruby/test/testunit/tests_for_parallel/runner.rb b/jni/ruby/test/testunit/tests_for_parallel/runner.rb
new file mode 100644
index 0000000..e262e1e
--- /dev/null
+++ b/jni/ruby/test/testunit/tests_for_parallel/runner.rb
@@ -0,0 +1,13 @@
+require 'rbconfig'
+
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib"
+
+require 'test/unit'
+
+src_testdir = File.dirname(File.expand_path(__FILE__))
+
+class Test::Unit::Runner
+ @@testfile_prefix = "ptest"
+end
+
+exit Test::Unit::AutoRunner.run(true, src_testdir)
diff --git a/jni/ruby/test/thread/test_cv.rb b/jni/ruby/test/thread/test_cv.rb
new file mode 100644
index 0000000..b399b47
--- /dev/null
+++ b/jni/ruby/test/thread/test_cv.rb
@@ -0,0 +1,221 @@
+require 'test/unit'
+require 'thread'
+require 'tmpdir'
+
+class TestConditionVariable < Test::Unit::TestCase
+ def test_initialized
+ assert_raise(TypeError) {
+ ConditionVariable.allocate.wait(nil)
+ }
+ end
+
+ def test_condvar_signal_and_wait
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ result = []
+ mutex.synchronize do
+ t = Thread.new do
+ mutex.synchronize do
+ result << 1
+ condvar.signal
+ end
+ end
+
+ result << 0
+ condvar.wait(mutex)
+ result << 2
+ t.join
+ end
+ assert_equal([0, 1, 2], result)
+ end
+
+ def test_condvar_wait_exception_handling
+ # Calling wait in the only thread running should raise a ThreadError of
+ # 'stopping only thread'
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ locked = false
+ thread = Thread.new do
+ Thread.current.abort_on_exception = false
+ mutex.synchronize do
+ begin
+ condvar.wait(mutex)
+ rescue Exception
+ locked = mutex.locked?
+ raise
+ end
+ end
+ end
+
+ until thread.stop?
+ sleep(0.1)
+ end
+
+ thread.raise Interrupt, "interrupt a dead condition variable"
+ assert_raise(Interrupt) { thread.value }
+ assert(locked)
+ end
+
+ def test_condvar_wait_and_broadcast
+ nr_threads = 3
+ threads = Array.new
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ result = []
+
+ nr_threads.times do |i|
+ threads[i] = Thread.new do
+ mutex.synchronize do
+ result << "C1"
+ condvar.wait mutex
+ result << "C2"
+ end
+ end
+ end
+ sleep 0.1
+ mutex.synchronize do
+ result << "P1"
+ condvar.broadcast
+ result << "P2"
+ end
+ Timeout.timeout(5) do
+ nr_threads.times do |i|
+ threads[i].join
+ end
+ end
+
+ assert_equal ["C1", "C1", "C1", "P1", "P2", "C2", "C2", "C2"], result
+ end
+
+ def test_condvar_wait_deadlock
+ assert_in_out_err([], <<-INPUT, ["fatal", "No live threads left. Deadlock?"], [])
+ require "thread"
+
+ mutex = Mutex.new
+ cv = ConditionVariable.new
+
+ klass = nil
+ mesg = nil
+ begin
+ mutex.lock
+ cv.wait mutex
+ mutex.unlock
+ rescue Exception => e
+ klass = e.class
+ mesg = e.message
+ end
+ puts klass
+ print mesg
+INPUT
+ end
+
+ def test_condvar_wait_deadlock_2
+ nr_threads = 3
+ threads = Array.new
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ nr_threads.times do |i|
+ if (i != 0)
+ mutex.unlock
+ end
+ threads[i] = Thread.new do
+ mutex.synchronize do
+ condvar.wait mutex
+ end
+ end
+ mutex.lock
+ end
+
+ assert_raise(Timeout::Error) do
+ Timeout.timeout(0.1) { condvar.wait mutex }
+ end
+ mutex.unlock
+ threads.each(&:kill)
+ threads.each(&:join)
+ end
+
+ def test_condvar_timed_wait
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ timeout = 0.3
+ locked = false
+
+ t0 = Time.now
+ mutex.synchronize do
+ begin
+ condvar.wait(mutex, timeout)
+ ensure
+ locked = mutex.locked?
+ end
+ end
+ t1 = Time.now
+ t = t1-t0
+
+ assert_operator(timeout*0.9, :<, t)
+ assert(locked)
+ end
+
+ def test_condvar_nolock
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_raise(ThreadError) {condvar.wait(mutex)}
+ end
+
+ def test_condvar_nolock_2
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ Thread.new do
+ assert_raise(ThreadError) {condvar.wait(mutex)}
+ end.join
+ end
+
+ def test_condvar_nolock_3
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ Thread.new do
+ assert_raise(ThreadError) {condvar.wait(mutex, 0.1)}
+ end.join
+ end
+
+ def test_condvar_empty_signal
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_nothing_raised(Exception) { mutex.synchronize {condvar.signal} }
+ end
+
+ def test_condvar_empty_broadcast
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_nothing_raised(Exception) { mutex.synchronize {condvar.broadcast} }
+ end
+
+ def test_dup
+ bug9440 = '[ruby-core:59961] [Bug #9440]'
+ condvar = ConditionVariable.new
+ assert_raise(NoMethodError, bug9440) do
+ condvar.dup
+ end
+ end
+
+ (DumpableCV = ConditionVariable.dup).class_eval {remove_method :marshal_dump}
+
+ def test_dump
+ bug9674 = '[ruby-core:61677] [Bug #9674]'
+ condvar = ConditionVariable.new
+ assert_raise_with_message(TypeError, /#{ConditionVariable}/, bug9674) do
+ Marshal.dump(condvar)
+ end
+
+ condvar = DumpableCV.new
+ assert_raise_with_message(TypeError, /internal Array/, bug9674) do
+ Marshal.dump(condvar)
+ end
+ end
+end
diff --git a/jni/ruby/test/thread/test_queue.rb b/jni/ruby/test/thread/test_queue.rb
new file mode 100644
index 0000000..2bd71db
--- /dev/null
+++ b/jni/ruby/test/thread/test_queue.rb
@@ -0,0 +1,280 @@
+require 'test/unit'
+require 'thread'
+require 'tmpdir'
+require 'timeout'
+
+class TestQueue < Test::Unit::TestCase
+ def test_queue_initialized
+ assert_raise(TypeError) {
+ Queue.allocate.push(nil)
+ }
+ end
+
+ def test_sized_queue_initialized
+ assert_raise(TypeError) {
+ SizedQueue.allocate.push(nil)
+ }
+ end
+
+ def test_queue
+ grind(5, 1000, 15, Queue)
+ end
+
+ def test_sized_queue
+ grind(5, 1000, 15, SizedQueue, 1000)
+ end
+
+ def grind(num_threads, num_objects, num_iterations, klass, *args)
+ from_workers = klass.new(*args)
+ to_workers = klass.new(*args)
+
+ workers = (1..num_threads).map {
+ Thread.new {
+ while object = to_workers.pop
+ from_workers.push object
+ end
+ }
+ }
+
+ Thread.new {
+ num_iterations.times {
+ num_objects.times { to_workers.push 99 }
+ num_objects.times { from_workers.pop }
+ }
+ }.join
+
+ num_threads.times { to_workers.push nil }
+ workers.each { |t| t.join }
+
+ assert_equal 0, from_workers.size
+ assert_equal 0, to_workers.size
+ end
+
+ def test_sized_queue_initialize
+ q = SizedQueue.new(1)
+ assert_equal 1, q.max
+ assert_raise(ArgumentError) { SizedQueue.new(0) }
+ assert_raise(ArgumentError) { SizedQueue.new(-1) }
+ end
+
+ def test_sized_queue_assign_max
+ q = SizedQueue.new(2)
+ assert_equal(2, q.max)
+ q.max = 1
+ assert_equal(1, q.max)
+ assert_raise(ArgumentError) { q.max = 0 }
+ assert_equal(1, q.max)
+ assert_raise(ArgumentError) { q.max = -1 }
+ assert_equal(1, q.max)
+
+ before = q.max
+ q.max.times { q << 1 }
+ t1 = Thread.new { q << 1 }
+ sleep 0.01 until t1.stop?
+ q.max = q.max + 1
+ assert_equal before + 1, q.max
+ ensure
+ t1.join if t1
+ end
+
+ def test_queue_pop_interrupt
+ q = Queue.new
+ t1 = Thread.new { q.pop }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_queue_pop_non_block
+ q = Queue.new
+ assert_raise_with_message(ThreadError, /empty/) do
+ q.pop(true)
+ end
+ end
+
+ def test_sized_queue_pop_interrupt
+ q = SizedQueue.new(1)
+ t1 = Thread.new { q.pop }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_sized_queue_pop_non_block
+ q = SizedQueue.new(1)
+ assert_raise_with_message(ThreadError, /empty/) do
+ q.pop(true)
+ end
+ end
+
+ def test_sized_queue_push_interrupt
+ q = SizedQueue.new(1)
+ q.push(1)
+ assert_raise_with_message(ThreadError, /full/) do
+ q.push(2, true)
+ end
+ end
+
+ def test_sized_queue_push_non_block
+ q = SizedQueue.new(1)
+ q.push(1)
+ t1 = Thread.new { q.push(2) }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_thr_kill
+ bug5343 = '[ruby-core:39634]'
+ Dir.mktmpdir {|d|
+ timeout = 30
+ total_count = 250
+ begin
+ assert_normal_exit(<<-"_eom", bug5343, {:timeout => timeout, :chdir=>d})
+ require "thread"
+ #{total_count}.times do |i|
+ open("test_thr_kill_count", "w") {|f| f.puts i }
+ queue = Queue.new
+ r, w = IO.pipe
+ th = Thread.start {
+ queue.push(nil)
+ r.read 1
+ }
+ queue.pop
+ th.kill
+ th.join
+ end
+ _eom
+ rescue Timeout::Error
+ count = File.read("#{d}/test_thr_kill_count").to_i
+ flunk "only #{count}/#{total_count} done in #{timeout} seconds."
+ end
+ }
+ end
+
+ def test_queue_push_return_value
+ q = Queue.new
+ retval = q.push(1)
+ assert_same q, retval
+ end
+
+ def test_queue_clear_return_value
+ q = Queue.new
+ retval = q.clear
+ assert_same q, retval
+ end
+
+ def test_sized_queue_clear
+ # Fill queue, then test that SizedQueue#clear wakes up all waiting threads
+ sq = SizedQueue.new(2)
+ 2.times { sq << 1 }
+
+ t1 = Thread.new do
+ sq << 1
+ end
+
+ t2 = Thread.new do
+ sq << 1
+ end
+
+ t3 = Thread.new do
+ Thread.pass
+ sq.clear
+ end
+
+ [t3, t2, t1].each(&:join)
+ assert_equal sq.length, 2
+ end
+
+ def test_sized_queue_push_return_value
+ q = SizedQueue.new(1)
+ retval = q.push(1)
+ assert_same q, retval
+ end
+
+ def test_sized_queue_clear_return_value
+ q = SizedQueue.new(1)
+ retval = q.clear
+ assert_same q, retval
+ end
+
+ def test_sized_queue_throttle
+ q = SizedQueue.new(1)
+ i = 0
+ consumer = Thread.new do
+ while q.pop
+ i += 1
+ Thread.pass
+ end
+ end
+ nprod = 4
+ npush = 100
+
+ producer = nprod.times.map do
+ Thread.new do
+ npush.times { q.push(true) }
+ end
+ end
+ producer.each(&:join)
+ q.push(nil)
+ consumer.join
+ assert_equal(nprod * npush, i)
+ end
+
+ def test_queue_thread_raise
+ q = Queue.new
+ th1 = Thread.new do
+ begin
+ q.pop
+ rescue RuntimeError
+ sleep
+ end
+ end
+ th2 = Thread.new do
+ sleep 0.1
+ q.pop
+ end
+ sleep 0.1
+ th1.raise
+ sleep 0.1
+ q << :s
+ assert_nothing_raised(Timeout::Error) do
+ timeout(1) { th2.join }
+ end
+ ensure
+ [th1, th2].each do |th|
+ if th and th.alive?
+ th.wakeup
+ th.join
+ end
+ end
+ end
+
+ def test_dup
+ bug9440 = '[ruby-core:59961] [Bug #9440]'
+ q = Queue.new
+ assert_raise(NoMethodError, bug9440) do
+ q.dup
+ end
+ end
+
+ (DumpableQueue = Queue.dup).class_eval {remove_method :marshal_dump}
+
+ def test_dump
+ bug9674 = '[ruby-core:61677] [Bug #9674]'
+ q = Queue.new
+ assert_raise_with_message(TypeError, /#{Queue}/, bug9674) do
+ Marshal.dump(q)
+ end
+
+ sq = SizedQueue.new(1)
+ assert_raise_with_message(TypeError, /#{SizedQueue}/, bug9674) do
+ Marshal.dump(sq)
+ end
+
+ q = DumpableQueue.new
+ assert_raise_with_message(TypeError, /internal Array/, bug9674) do
+ Marshal.dump(q)
+ end
+ end
+end
diff --git a/jni/ruby/test/thread/test_sync.rb b/jni/ruby/test/thread/test_sync.rb
new file mode 100644
index 0000000..9509cac
--- /dev/null
+++ b/jni/ruby/test/thread/test_sync.rb
@@ -0,0 +1,63 @@
+require 'test/unit'
+require 'sync'
+require 'timeout'
+
+class SyncTest < Test::Unit::TestCase
+ class Tester
+ include Sync_m
+ end
+
+ def test_sync_lock_and_wakeup
+ tester = Tester.new
+
+ tester.sync_lock(:EX)
+
+ t = Thread.new { tester.sync_lock(:EX) }
+
+ sleep 0.1 until t.stop?
+ t.wakeup
+ sleep 0.1 until t.stop?
+
+ assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
+ ensure
+ t.kill
+ t.join
+ end
+
+ def test_sync_upgrade_and_wakeup
+ tester = Tester.new
+ tester.sync_lock(:SH)
+
+ t = Thread.new do
+ tester.sync_lock(:SH)
+ tester.sync_lock(:EX)
+ end
+
+ sleep 0.1 until t.stop?
+ t.wakeup
+ sleep 0.1 until t.stop?
+
+ tester.sync_upgrade_waiting.each { |ary|
+ assert(!tester.sync_waiting.include?(ary[0]))
+ }
+ assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
+ assert_equal(tester.sync_waiting, [])
+ ensure
+ t.kill
+ t.join
+ end
+
+ def test_sync_lock_and_raise
+ tester= Tester.new
+ tester.sync_lock(:EX)
+
+ t = Thread.new { tester.sync_lock(:EX) }
+
+ sleep 0.1 until t.stop?
+ t.raise
+ sleep 0.1 while t.alive?
+
+ assert_equal(tester.sync_waiting.uniq, tester.sync_waiting)
+ assert_equal(tester.sync_waiting, [])
+ end
+end
diff --git a/jni/ruby/test/uri/test_common.rb b/jni/ruby/test/uri/test_common.rb
new file mode 100644
index 0000000..5620415
--- /dev/null
+++ b/jni/ruby/test/uri/test_common.rb
@@ -0,0 +1,174 @@
+require 'test/unit'
+require 'uri'
+
+module URI
+
+
+class TestCommon < Test::Unit::TestCase
+ def setup
+ end
+
+ def teardown
+ end
+
+ def test_extract
+ EnvUtil.suppress_warning do
+ assert_equal(['http://example.com'],
+ URI.extract('http://example.com'))
+ assert_equal(['http://example.com'],
+ URI.extract('(http://example.com)'))
+ assert_equal(['http://example.com/foo)'],
+ URI.extract('(http://example.com/foo)'))
+ assert_equal(['http://example.jphttp://example.jp'],
+ URI.extract('http://example.jphttp://example.jp'), "[ruby-list:36086]")
+ assert_equal(['http://example.jphttp://example.jp'],
+ URI.extract('http://example.jphttp://example.jp', ['http']), "[ruby-list:36086]")
+ assert_equal(['http://', 'mailto:'].sort,
+ URI.extract('ftp:// http:// mailto: https://', ['http', 'mailto']).sort)
+ # reported by Doug Kearns <djkea2@mugca.its.monash.edu.au>
+ assert_equal(['From:', 'mailto:xxx@xxx.xxx.xxx]'].sort,
+ URI.extract('From: XXX [mailto:xxx@xxx.xxx.xxx]').sort)
+ end
+ end
+
+ def test_regexp
+ EnvUtil.suppress_warning do
+ assert_instance_of Regexp, URI.regexp
+ assert_instance_of Regexp, URI.regexp(['http'])
+ assert_equal URI.regexp, URI.regexp
+ assert_equal 'http://', 'x http:// x'.slice(URI.regexp)
+ assert_equal 'http://', 'x http:// x'.slice(URI.regexp(['http']))
+ assert_equal 'http://', 'x http:// x ftp://'.slice(URI.regexp(['http']))
+ assert_equal nil, 'http://'.slice(URI.regexp([]))
+ assert_equal nil, ''.slice(URI.regexp)
+ assert_equal nil, 'xxxx'.slice(URI.regexp)
+ assert_equal nil, ':'.slice(URI.regexp)
+ assert_equal 'From:', 'From:'.slice(URI.regexp)
+ end
+ end
+
+ def test_kernel_uri
+ expected = URI.parse("http://www.ruby-lang.org/")
+ assert_equal(expected, URI("http://www.ruby-lang.org/"))
+ assert_equal(expected, Kernel::URI("http://www.ruby-lang.org/"))
+ assert_raise(NoMethodError) { Object.new.URI("http://www.ruby-lang.org/") }
+ end
+
+ def test_encode_www_form_component
+ assert_equal("%00+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40" \
+ "AZ%5B%5C%5D%5E_%60az%7B%7C%7D%7E",
+ URI.encode_www_form_component("\x00 !\"\#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~"))
+ assert_equal("%95A", URI.encode_www_form_component(
+ "\x95\x41".force_encoding(Encoding::Shift_JIS)))
+ assert_equal("0B", URI.encode_www_form_component(
+ "\x30\x42".force_encoding(Encoding::UTF_16BE)))
+ assert_equal("%1B%24B%24%22%1B%28B", URI.encode_www_form_component(
+ "\e$B$\"\e(B".force_encoding(Encoding::ISO_2022_JP)))
+
+ assert_equal("%E3%81%82", URI.encode_www_form_component(
+ "\u3042", Encoding::ASCII_8BIT))
+ assert_equal("%82%A0", URI.encode_www_form_component(
+ "\u3042", Encoding::Windows_31J))
+ assert_equal("%E3%81%82", URI.encode_www_form_component(
+ "\u3042", Encoding::UTF_8))
+
+ assert_equal("%82%A0", URI.encode_www_form_component(
+ "\u3042".encode("sjis"), Encoding::ASCII_8BIT))
+ assert_equal("%A4%A2", URI.encode_www_form_component(
+ "\u3042".encode("sjis"), Encoding::EUC_JP))
+ assert_equal("%E3%81%82", URI.encode_www_form_component(
+ "\u3042".encode("sjis"), Encoding::UTF_8))
+ assert_equal("B0", URI.encode_www_form_component(
+ "\u3042".encode("sjis"), Encoding::UTF_16LE))
+
+ # invalid
+ assert_equal("%EF%BF%BD%EF%BF%BD", URI.encode_www_form_component(
+ "\xE3\x81\xFF", "utf-8"))
+ assert_equal("%E6%9F%8A%EF%BF%BD%EF%BF%BD", URI.encode_www_form_component(
+ "\x95\x41\xff\xff".force_encoding(Encoding::Shift_JIS), "utf-8"))
+ end
+
+ def test_decode_www_form_component
+ assert_equal(" !\"\#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~",
+ URI.decode_www_form_component(
+ "%20+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40" \
+ "AZ%5B%5C%5D%5E_%60az%7B%7C%7D%7E"))
+ assert_equal("\xA1\xA2".force_encoding(Encoding::EUC_JP),
+ URI.decode_www_form_component("%A1%A2", "EUC-JP"))
+ assert_equal("\xE3\x81\x82\xE3\x81\x82".force_encoding("UTF-8"),
+ URI.decode_www_form_component("\xE3\x81\x82%E3%81%82".force_encoding("UTF-8")))
+
+ assert_raise(ArgumentError){URI.decode_www_form_component("%")}
+ assert_raise(ArgumentError){URI.decode_www_form_component("%a")}
+ assert_raise(ArgumentError){URI.decode_www_form_component("x%a_")}
+ assert_nothing_raised(ArgumentError){URI.decode_www_form_component("x"*(1024*1024))}
+ end
+
+ def test_encode_www_form
+ assert_equal("a=1", URI.encode_www_form("a" => "1"))
+ assert_equal("a=1", URI.encode_www_form(a: 1))
+ assert_equal("a=1", URI.encode_www_form([["a", "1"]]))
+ assert_equal("a=1", URI.encode_www_form([[:a, 1]]))
+ expected = "a=1&%E3%81%82=%E6%BC%A2"
+ assert_equal(expected, URI.encode_www_form("a" => "1", "\u3042" => "\u6F22"))
+ assert_equal(expected, URI.encode_www_form(a: 1, :"\u3042" => "\u6F22"))
+ assert_equal(expected, URI.encode_www_form([["a", "1"], ["\u3042", "\u6F22"]]))
+ assert_equal(expected, URI.encode_www_form([[:a, 1], [:"\u3042", "\u6F22"]]))
+ assert_equal("a=1&%82%A0=%8A%BF",
+ URI.encode_www_form({"a" => "1", "\u3042" => "\u6F22"}, "sjis"))
+
+ assert_equal('+a+=+1+', URI.encode_www_form([[' a ', ' 1 ']]))
+ assert_equal('text=x%0Ay', URI.encode_www_form([['text', "x\u000Ay"]]))
+ assert_equal('constellation=Bo%C3%B6tes', URI.encode_www_form([['constellation', "Bo\u00F6tes"]]))
+ assert_equal('name=%00value', URI.encode_www_form([['name', "\u0000value"]]))
+ assert_equal('Cipher=c%3D%28m%5Ee%29%25n', URI.encode_www_form([['Cipher', 'c=(m^e)%n']]))
+ assert_equal('&', URI.encode_www_form([['', nil], ['', nil]]))
+ assert_equal('&=', URI.encode_www_form([['', nil], ['', '']]))
+ assert_equal('=&', URI.encode_www_form([['', ''], ['', nil]]))
+ assert_equal('=&=', URI.encode_www_form([['', ''], ['', '']]))
+ assert_equal('', URI.encode_www_form([['', nil]]))
+ assert_equal('', URI.encode_www_form([]))
+ assert_equal('=', URI.encode_www_form([['', '']]))
+ assert_equal('a%26b=1&c=2%3B3&e=4', URI.encode_www_form([['a&b', '1'], ['c', '2;3'], ['e', '4']]))
+ assert_equal('image&title&price', URI.encode_www_form([['image', nil], ['title', nil], ['price', nil]]))
+
+ assert_equal("q=ruby&lang=en", URI.encode_www_form([["q", "ruby"], ["lang", "en"]]))
+ assert_equal("q=ruby&lang=en", URI.encode_www_form("q" => "ruby", "lang" => "en"))
+ assert_equal("q=ruby&q=perl&lang=en", URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en"))
+ assert_equal("q=ruby&q=perl&lang=en", URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]]))
+ end
+
+ def test_decode_www_form
+ assert_equal([%w[a 1], %w[a 2]], URI.decode_www_form("a=1&a=2"))
+ assert_equal([%w[a 1;a=2]], URI.decode_www_form("a=1;a=2"))
+ assert_equal([%w[a 1], ['', ''], %w[a 2]], URI.decode_www_form("a=1&&a=2"))
+ assert_raise(ArgumentError){URI.decode_www_form("\u3042")}
+ assert_equal([%w[a 1], ["\u3042", "\u6F22"]],
+ URI.decode_www_form("a=1&%E3%81%82=%E6%BC%A2"))
+ assert_equal([%w[a 1], ["\uFFFD%8", "\uFFFD"]],
+ URI.decode_www_form("a=1&%E3%81%8=%E6%BC"))
+ assert_equal([%w[?a 1], %w[a 2]], URI.decode_www_form("?a=1&a=2"))
+ assert_equal([], URI.decode_www_form(""))
+ assert_equal([%w[% 1]], URI.decode_www_form("%=1"))
+ assert_equal([%w[a %]], URI.decode_www_form("a=%"))
+ assert_equal([%w[a 1], %w[% 2]], URI.decode_www_form("a=1&%=2"))
+ assert_equal([%w[a 1], %w[b %]], URI.decode_www_form("a=1&b=%"))
+ assert_equal([['a', ''], ['b', '']], URI.decode_www_form("a&b"))
+ bug4098 = '[ruby-core:33464]'
+ assert_equal([['a', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'], ['b', '']], URI.decode_www_form("a=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&b"), bug4098)
+
+ assert_raise(ArgumentError){ URI.decode_www_form("a=1&%82%A0=%8A%BF", "x-sjis") }
+ assert_equal([["a", "1"], [s("\x82\xA0"), s("\x8a\xBF")]],
+ URI.decode_www_form("a=1&%82%A0=%8A%BF", "sjis"))
+ assert_equal([["a", "1"], [s("\x82\xA0"), s("\x8a\xBF")], %w[_charset_ sjis], [s("\x82\xA1"), s("\x8a\xC0")]],
+ URI.decode_www_form("a=1&%82%A0=%8A%BF&_charset_=sjis&%82%A1=%8A%C0", use__charset_: true))
+ assert_equal([["", "isindex"], ["a", "1"]],
+ URI.decode_www_form("isindex&a=1", isindex: true))
+ end
+
+ private
+ def s(str) str.force_encoding(Encoding::Windows_31J); end
+end
+
+
+end
diff --git a/jni/ruby/test/uri/test_ftp.rb b/jni/ruby/test/uri/test_ftp.rb
new file mode 100644
index 0000000..cc6843e
--- /dev/null
+++ b/jni/ruby/test/uri/test_ftp.rb
@@ -0,0 +1,66 @@
+require 'test/unit'
+require 'uri/ftp'
+
+module URI
+
+
+class TestFTP < Test::Unit::TestCase
+ def setup
+ end
+
+ def test_parse
+ url = URI.parse('ftp://user:pass@host.com/abc/def')
+ assert_kind_of(URI::FTP, url)
+
+ exp = [
+ 'ftp',
+ 'user:pass', 'host.com', URI::FTP.default_port,
+ 'abc/def', nil,
+ ]
+ ary = [
+ url.scheme, url.userinfo, url.host, url.port,
+ url.path, url.opaque
+ ]
+ assert_equal(exp, ary)
+
+ assert_equal('user', url.user)
+ assert_equal('pass', url.password)
+ end
+
+ def test_parse_invalid
+ assert_raise(InvalidURIError){URI.parse('ftp:example')}
+ end
+
+ def test_paths
+ # If you think what's below is wrong, please read RubyForge bug 2055,
+ # RFC 1738 section 3.2.2, and RFC 2396.
+ u = URI.parse('ftp://ftp.example.com/foo/bar/file.ext')
+ assert(u.path == 'foo/bar/file.ext')
+ u = URI.parse('ftp://ftp.example.com//foo/bar/file.ext')
+ assert(u.path == '/foo/bar/file.ext')
+ u = URI.parse('ftp://ftp.example.com/%2Ffoo/bar/file.ext')
+ assert(u.path == '/foo/bar/file.ext')
+ end
+
+ def test_assemble
+ # uri/ftp is conservative and uses the older RFC 1738 rules, rather than
+ # assuming everyone else has implemented RFC 2396.
+ uri = URI::FTP.build(['user:password', 'ftp.example.com', nil,
+ '/path/file.zip', 'i'])
+ assert(uri.to_s ==
+ 'ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=i')
+ end
+
+ def test_select
+ assert_equal(['ftp', 'a.b.c', 21], URI.parse('ftp://a.b.c/').select(:scheme, :host, :port))
+ u = URI.parse('ftp://a.b.c/')
+ ary = u.component.collect {|c| u.send(c)}
+ assert_equal(ary, u.select(*u.component))
+ assert_raise(ArgumentError) do
+ u.select(:scheme, :host, :not_exist, :port)
+ end
+ end
+end
+
+
+end
diff --git a/jni/ruby/test/uri/test_generic.rb b/jni/ruby/test/uri/test_generic.rb
new file mode 100644
index 0000000..37605d5
--- /dev/null
+++ b/jni/ruby/test/uri/test_generic.rb
@@ -0,0 +1,847 @@
+require 'test/unit'
+require 'uri'
+
+class URI::TestGeneric < Test::Unit::TestCase
+ def setup
+ @url = 'http://a/b/c/d;p?q'
+ @base_url = URI.parse(@url)
+ end
+
+ def teardown
+ end
+
+ def uri_to_ary(uri)
+ uri.class.component.collect {|c| uri.send(c)}
+ end
+
+ def test_parse
+ # 0
+ assert_kind_of(URI::HTTP, @base_url)
+
+ exp = [
+ 'http',
+ nil, 'a', URI::HTTP.default_port,
+ '/b/c/d;p',
+ 'q',
+ nil
+ ]
+ ary = uri_to_ary(@base_url)
+ assert_equal(exp, ary)
+
+ # 1
+ url = URI.parse('ftp://ftp.is.co.za/rfc/rfc1808.txt')
+ assert_kind_of(URI::FTP, url)
+
+ exp = [
+ 'ftp',
+ nil, 'ftp.is.co.za', URI::FTP.default_port,
+ 'rfc/rfc1808.txt', nil,
+ ]
+ ary = uri_to_ary(url)
+ assert_equal(exp, ary)
+ # 1'
+ url = URI.parse('ftp://ftp.is.co.za/%2Frfc/rfc1808.txt')
+ assert_kind_of(URI::FTP, url)
+
+ exp = [
+ 'ftp',
+ nil, 'ftp.is.co.za', URI::FTP.default_port,
+ '/rfc/rfc1808.txt', nil,
+ ]
+ ary = uri_to_ary(url)
+ assert_equal(exp, ary)
+
+ # 2
+ url = URI.parse('gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles')
+ assert_kind_of(URI::Generic, url)
+
+ exp = [
+ 'gopher',
+ nil, 'spinaltap.micro.umn.edu', nil, nil,
+ '/00/Weather/California/Los%20Angeles', nil,
+ nil,
+ nil
+ ]
+ ary = uri_to_ary(url)
+ assert_equal(exp, ary)
+
+ # 3
+ url = URI.parse('http://www.math.uio.no/faq/compression-faq/part1.html')
+ assert_kind_of(URI::HTTP, url)
+
+ exp = [
+ 'http',
+ nil, 'www.math.uio.no', URI::HTTP.default_port,
+ '/faq/compression-faq/part1.html',
+ nil,
+ nil
+ ]
+ ary = uri_to_ary(url)
+ assert_equal(exp, ary)
+
+ # 4
+ url = URI.parse('mailto:mduerst@ifi.unizh.ch')
+ assert_kind_of(URI::Generic, url)
+
+ exp = [
+ 'mailto',
+ 'mduerst@ifi.unizh.ch',
+ []
+ ]
+ ary = uri_to_ary(url)
+ assert_equal(exp, ary)
+
+ # 5
+ url = URI.parse('news:comp.infosystems.www.servers.unix')
+ assert_kind_of(URI::Generic, url)
+
+ exp = [
+ 'news',
+ nil, nil, nil, nil,
+ nil, 'comp.infosystems.www.servers.unix',
+ nil,
+ nil
+ ]
+ ary = uri_to_ary(url)
+ assert_equal(exp, ary)
+
+ # 6
+ url = URI.parse('telnet://melvyl.ucop.edu/')
+ assert_kind_of(URI::Generic, url)
+
+ exp = [
+ 'telnet',
+ nil, 'melvyl.ucop.edu', nil, nil,
+ '/', nil,
+ nil,
+ nil
+ ]
+ ary = uri_to_ary(url)
+ assert_equal(exp, ary)
+
+ # 7
+ # reported by Mr. Kubota <em6t-kbt@asahi-net.or.jp>
+ assert_nothing_raised(URI::InvalidURIError) { URI.parse('http://a_b:80/') }
+ assert_nothing_raised(URI::InvalidURIError) { URI.parse('http://a_b/') }
+
+ # 8
+ # reported by m_seki
+ url = URI.parse('file:///foo/bar.txt')
+ assert_kind_of(URI::Generic, url)
+ url = URI.parse('file:/foo/bar.txt')
+ assert_kind_of(URI::Generic, url)
+
+ # 9
+ url = URI.parse('ftp://:pass@localhost/')
+ assert_equal('', url.user, "[ruby-dev:25667]")
+ assert_equal('pass', url.password)
+ assert_equal(':pass', url.userinfo, "[ruby-dev:25667]")
+ url = URI.parse('ftp://user@localhost/')
+ assert_equal('user', url.user)
+ assert_equal(nil, url.password)
+ assert_equal('user', url.userinfo)
+ url = URI.parse('ftp://localhost/')
+ assert_equal(nil, url.user)
+ assert_equal(nil, url.password)
+ assert_equal(nil, url.userinfo)
+ end
+
+ def test_merge
+ u1 = URI.parse('http://foo')
+ u2 = URI.parse('http://foo/')
+ u3 = URI.parse('http://foo/bar')
+ u4 = URI.parse('http://foo/bar/')
+
+ {
+ u1 => {
+ 'baz' => 'http://foo/baz',
+ '/baz' => 'http://foo/baz',
+ },
+ u2 => {
+ 'baz' => 'http://foo/baz',
+ '/baz' => 'http://foo/baz',
+ },
+ u3 => {
+ 'baz' => 'http://foo/baz',
+ '/baz' => 'http://foo/baz',
+ },
+ u4 => {
+ 'baz' => 'http://foo/bar/baz',
+ '/baz' => 'http://foo/baz',
+ },
+ }.each { |base, map|
+ map.each { |url, result|
+ expected = URI.parse(result)
+ uri = URI.parse(url)
+ assert_equal expected, base + url, "<#{base}> + #{url.inspect} to become <#{expected}>"
+ assert_equal expected, base + uri, "<#{base}> + <#{uri}> to become <#{expected}>"
+ }
+ }
+
+ url = URI.parse('http://hoge/a.html') + 'b.html'
+ assert_equal('http://hoge/b.html', url.to_s, "[ruby-dev:11508]")
+
+ # reported by Mr. Kubota <em6t-kbt@asahi-net.or.jp>
+ url = URI.parse('http://a/b') + 'http://x/y'
+ assert_equal('http://x/y', url.to_s)
+ assert_equal(url, URI.parse('') + 'http://x/y')
+ assert_equal(url, URI.parse('').normalize + 'http://x/y')
+ assert_equal(url, URI.parse('http://a/b').normalize + 'http://x/y')
+
+ u = URI.parse('http://foo/bar/baz')
+ assert_equal(nil, u.merge!(""))
+ assert_equal(nil, u.merge!(u))
+ assert(nil != u.merge!("."))
+ assert_equal('http://foo/bar/', u.to_s)
+ assert(nil != u.merge!("../baz"))
+ assert_equal('http://foo/baz', u.to_s)
+
+ u0 = URI.parse('mailto:foo@example.com')
+ u1 = URI.parse('mailto:foo@example.com#bar')
+ assert_equal(uri_to_ary(u0 + '#bar'), uri_to_ary(u1), "[ruby-dev:23628]")
+
+ u0 = URI.parse('http://www.example.com/')
+ u1 = URI.parse('http://www.example.com/foo/..') + './'
+ assert_equal(u0, u1, "[ruby-list:39838]")
+ u0 = URI.parse('http://www.example.com/foo/')
+ u1 = URI.parse('http://www.example.com/foo/bar/..') + './'
+ assert_equal(u0, u1)
+ u0 = URI.parse('http://www.example.com/foo/bar/')
+ u1 = URI.parse('http://www.example.com/foo/bar/baz/..') + './'
+ assert_equal(u0, u1)
+ u0 = URI.parse('http://www.example.com/')
+ u1 = URI.parse('http://www.example.com/foo/bar/../..') + './'
+ assert_equal(u0, u1)
+ u0 = URI.parse('http://www.example.com/foo/')
+ u1 = URI.parse('http://www.example.com/foo/bar/baz/../..') + './'
+ assert_equal(u0, u1)
+
+ u = URI.parse('http://www.example.com/')
+ u0 = u + './foo/'
+ u1 = u + './foo/bar/..'
+ assert_equal(u0, u1, "[ruby-list:39844]")
+ u = URI.parse('http://www.example.com/')
+ u0 = u + './'
+ u1 = u + './foo/bar/../..'
+ assert_equal(u0, u1)
+ end
+
+ def test_route
+ url = URI.parse('http://hoge/a.html').route_to('http://hoge/b.html')
+ assert_equal('b.html', url.to_s)
+
+ url = URI.parse('http://hoge/a/').route_to('http://hoge/b/')
+ assert_equal('../b/', url.to_s)
+ url = URI.parse('http://hoge/a/b').route_to('http://hoge/b/')
+ assert_equal('../b/', url.to_s)
+
+ url = URI.parse('http://hoge/a/b/').route_to('http://hoge/b/')
+ assert_equal('../../b/', url.to_s)
+
+ url = URI.parse('http://hoge/a/b/').route_to('http://HOGE/b/')
+ assert_equal('../../b/', url.to_s)
+
+ url = URI.parse('http://hoge/a/b/').route_to('http://MOGE/b/')
+ assert_equal('//MOGE/b/', url.to_s)
+
+ url = URI.parse('http://hoge/b').route_to('http://hoge/b/')
+ assert_equal('b/', url.to_s)
+ url = URI.parse('http://hoge/b/a').route_to('http://hoge/b/')
+ assert_equal('./', url.to_s)
+ url = URI.parse('http://hoge/b/').route_to('http://hoge/b')
+ assert_equal('../b', url.to_s)
+ url = URI.parse('http://hoge/b').route_to('http://hoge/b:c')
+ assert_equal('./b:c', url.to_s)
+
+ url = URI.parse('file:///a/b/').route_to('file:///a/b/')
+ assert_equal('', url.to_s)
+ url = URI.parse('file:///a/b/').route_to('file:///a/b')
+ assert_equal('../b', url.to_s)
+
+ url = URI.parse('mailto:foo@example.com').route_to('mailto:foo@example.com#bar')
+ assert_equal('#bar', url.to_s)
+
+ url = URI.parse('mailto:foo@example.com#bar').route_to('mailto:foo@example.com')
+ assert_equal('', url.to_s)
+
+ url = URI.parse('mailto:foo@example.com').route_to('mailto:foo@example.com')
+ assert_equal('', url.to_s)
+ end
+
+ def test_rfc3986_examples
+# http://a/b/c/d;p?q
+# g:h = g:h
+ url = @base_url.merge('g:h')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g:h', url.to_s)
+ url = @base_url.route_to('g:h')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g:h', url.to_s)
+
+# http://a/b/c/d;p?q
+# g = http://a/b/c/g
+ url = @base_url.merge('g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g', url.to_s)
+
+# http://a/b/c/d;p?q
+# ./g = http://a/b/c/g
+ url = @base_url.merge('./g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g')
+ assert_kind_of(URI::Generic, url)
+ assert('./g' != url.to_s) # ok
+ assert_equal('g', url.to_s)
+
+# http://a/b/c/d;p?q
+# g/ = http://a/b/c/g/
+ url = @base_url.merge('g/')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g/', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g/')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g/', url.to_s)
+
+# http://a/b/c/d;p?q
+# /g = http://a/g
+ url = @base_url.merge('/g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/g', url.to_s)
+ url = @base_url.route_to('http://a/g')
+ assert_kind_of(URI::Generic, url)
+ assert('/g' != url.to_s) # ok
+ assert_equal('../../g', url.to_s)
+
+# http://a/b/c/d;p?q
+# //g = http://g
+ url = @base_url.merge('//g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://g', url.to_s)
+ url = @base_url.route_to('http://g')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('//g', url.to_s)
+
+# http://a/b/c/d;p?q
+# ?y = http://a/b/c/d;p?y
+ url = @base_url.merge('?y')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/d;p?y', url.to_s)
+ url = @base_url.route_to('http://a/b/c/d;p?y')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('?y', url.to_s)
+
+# http://a/b/c/d;p?q
+# g?y = http://a/b/c/g?y
+ url = @base_url.merge('g?y')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g?y', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g?y')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g?y', url.to_s)
+
+# http://a/b/c/d;p?q
+# #s = http://a/b/c/d;p?q#s
+ url = @base_url.merge('#s')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/d;p?q#s', url.to_s)
+ url = @base_url.route_to('http://a/b/c/d;p?q#s')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('#s', url.to_s)
+
+# http://a/b/c/d;p?q
+# g#s = http://a/b/c/g#s
+ url = @base_url.merge('g#s')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g#s', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g#s')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g#s', url.to_s)
+
+# http://a/b/c/d;p?q
+# g?y#s = http://a/b/c/g?y#s
+ url = @base_url.merge('g?y#s')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g?y#s', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g?y#s')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g?y#s', url.to_s)
+
+# http://a/b/c/d;p?q
+# ;x = http://a/b/c/;x
+ url = @base_url.merge(';x')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/;x', url.to_s)
+ url = @base_url.route_to('http://a/b/c/;x')
+ assert_kind_of(URI::Generic, url)
+ assert_equal(';x', url.to_s)
+
+# http://a/b/c/d;p?q
+# g;x = http://a/b/c/g;x
+ url = @base_url.merge('g;x')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g;x', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g;x')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g;x', url.to_s)
+
+# http://a/b/c/d;p?q
+# g;x?y#s = http://a/b/c/g;x?y#s
+ url = @base_url.merge('g;x?y#s')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g;x?y#s', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g;x?y#s')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g;x?y#s', url.to_s)
+
+# http://a/b/c/d;p?q
+# . = http://a/b/c/
+ url = @base_url.merge('.')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/', url.to_s)
+ url = @base_url.route_to('http://a/b/c/')
+ assert_kind_of(URI::Generic, url)
+ assert('.' != url.to_s) # ok
+ assert_equal('./', url.to_s)
+
+# http://a/b/c/d;p?q
+# ./ = http://a/b/c/
+ url = @base_url.merge('./')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/', url.to_s)
+ url = @base_url.route_to('http://a/b/c/')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('./', url.to_s)
+
+# http://a/b/c/d;p?q
+# .. = http://a/b/
+ url = @base_url.merge('..')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/', url.to_s)
+ url = @base_url.route_to('http://a/b/')
+ assert_kind_of(URI::Generic, url)
+ assert('..' != url.to_s) # ok
+ assert_equal('../', url.to_s)
+
+# http://a/b/c/d;p?q
+# ../ = http://a/b/
+ url = @base_url.merge('../')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/', url.to_s)
+ url = @base_url.route_to('http://a/b/')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('../', url.to_s)
+
+# http://a/b/c/d;p?q
+# ../g = http://a/b/g
+ url = @base_url.merge('../g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/g', url.to_s)
+ url = @base_url.route_to('http://a/b/g')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('../g', url.to_s)
+
+# http://a/b/c/d;p?q
+# ../.. = http://a/
+ url = @base_url.merge('../..')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/', url.to_s)
+ url = @base_url.route_to('http://a/')
+ assert_kind_of(URI::Generic, url)
+ assert('../..' != url.to_s) # ok
+ assert_equal('../../', url.to_s)
+
+# http://a/b/c/d;p?q
+# ../../ = http://a/
+ url = @base_url.merge('../../')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/', url.to_s)
+ url = @base_url.route_to('http://a/')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('../../', url.to_s)
+
+# http://a/b/c/d;p?q
+# ../../g = http://a/g
+ url = @base_url.merge('../../g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/g', url.to_s)
+ url = @base_url.route_to('http://a/g')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('../../g', url.to_s)
+
+# http://a/b/c/d;p?q
+# <> = (current document)
+ url = @base_url.merge('')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/d;p?q', url.to_s)
+ url = @base_url.route_to('http://a/b/c/d;p?q')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('', url.to_s)
+
+# http://a/b/c/d;p?q
+# /./g = http://a/g
+ url = @base_url.merge('/./g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/g', url.to_s)
+# url = @base_url.route_to('http://a/./g')
+# assert_kind_of(URI::Generic, url)
+# assert_equal('/./g', url.to_s)
+
+# http://a/b/c/d;p?q
+# /../g = http://a/g
+ url = @base_url.merge('/../g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/g', url.to_s)
+# url = @base_url.route_to('http://a/../g')
+# assert_kind_of(URI::Generic, url)
+# assert_equal('/../g', url.to_s)
+
+# http://a/b/c/d;p?q
+# g. = http://a/b/c/g.
+ url = @base_url.merge('g.')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g.', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g.')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g.', url.to_s)
+
+# http://a/b/c/d;p?q
+# .g = http://a/b/c/.g
+ url = @base_url.merge('.g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/.g', url.to_s)
+ url = @base_url.route_to('http://a/b/c/.g')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('.g', url.to_s)
+
+# http://a/b/c/d;p?q
+# g.. = http://a/b/c/g..
+ url = @base_url.merge('g..')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g..', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g..')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g..', url.to_s)
+
+# http://a/b/c/d;p?q
+# ..g = http://a/b/c/..g
+ url = @base_url.merge('..g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/..g', url.to_s)
+ url = @base_url.route_to('http://a/b/c/..g')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('..g', url.to_s)
+
+# http://a/b/c/d;p?q
+# ../../../g = http://a/g
+ url = @base_url.merge('../../../g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/g', url.to_s)
+ url = @base_url.route_to('http://a/g')
+ assert_kind_of(URI::Generic, url)
+ assert('../../../g' != url.to_s) # ok? yes, it confuses you
+ assert_equal('../../g', url.to_s) # and it is clearly
+
+# http://a/b/c/d;p?q
+# ../../../../g = http://a/g
+ url = @base_url.merge('../../../../g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/g', url.to_s)
+ url = @base_url.route_to('http://a/g')
+ assert_kind_of(URI::Generic, url)
+ assert('../../../../g' != url.to_s) # ok? yes, it confuses you
+ assert_equal('../../g', url.to_s) # and it is clearly
+
+# http://a/b/c/d;p?q
+# ./../g = http://a/b/g
+ url = @base_url.merge('./../g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/g', url.to_s)
+ url = @base_url.route_to('http://a/b/g')
+ assert_kind_of(URI::Generic, url)
+ assert('./../g' != url.to_s) # ok
+ assert_equal('../g', url.to_s)
+
+# http://a/b/c/d;p?q
+# ./g/. = http://a/b/c/g/
+ url = @base_url.merge('./g/.')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g/', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g/')
+ assert_kind_of(URI::Generic, url)
+ assert('./g/.' != url.to_s) # ok
+ assert_equal('g/', url.to_s)
+
+# http://a/b/c/d;p?q
+# g/./h = http://a/b/c/g/h
+ url = @base_url.merge('g/./h')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g/h', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g/h')
+ assert_kind_of(URI::Generic, url)
+ assert('g/./h' != url.to_s) # ok
+ assert_equal('g/h', url.to_s)
+
+# http://a/b/c/d;p?q
+# g/../h = http://a/b/c/h
+ url = @base_url.merge('g/../h')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/h', url.to_s)
+ url = @base_url.route_to('http://a/b/c/h')
+ assert_kind_of(URI::Generic, url)
+ assert('g/../h' != url.to_s) # ok
+ assert_equal('h', url.to_s)
+
+# http://a/b/c/d;p?q
+# g;x=1/./y = http://a/b/c/g;x=1/y
+ url = @base_url.merge('g;x=1/./y')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g;x=1/y', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g;x=1/y')
+ assert_kind_of(URI::Generic, url)
+ assert('g;x=1/./y' != url.to_s) # ok
+ assert_equal('g;x=1/y', url.to_s)
+
+# http://a/b/c/d;p?q
+# g;x=1/../y = http://a/b/c/y
+ url = @base_url.merge('g;x=1/../y')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/y', url.to_s)
+ url = @base_url.route_to('http://a/b/c/y')
+ assert_kind_of(URI::Generic, url)
+ assert('g;x=1/../y' != url.to_s) # ok
+ assert_equal('y', url.to_s)
+
+# http://a/b/c/d;p?q
+# g?y/./x = http://a/b/c/g?y/./x
+ url = @base_url.merge('g?y/./x')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g?y/./x', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g?y/./x')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g?y/./x', url.to_s)
+
+# http://a/b/c/d;p?q
+# g?y/../x = http://a/b/c/g?y/../x
+ url = @base_url.merge('g?y/../x')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g?y/../x', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g?y/../x')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g?y/../x', url.to_s)
+
+# http://a/b/c/d;p?q
+# g#s/./x = http://a/b/c/g#s/./x
+ url = @base_url.merge('g#s/./x')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g#s/./x', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g#s/./x')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g#s/./x', url.to_s)
+
+# http://a/b/c/d;p?q
+# g#s/../x = http://a/b/c/g#s/../x
+ url = @base_url.merge('g#s/../x')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http://a/b/c/g#s/../x', url.to_s)
+ url = @base_url.route_to('http://a/b/c/g#s/../x')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('g#s/../x', url.to_s)
+
+# http://a/b/c/d;p?q
+# http:g = http:g ; for validating parsers
+# | http://a/b/c/g ; for backwards compatibility
+ url = @base_url.merge('http:g')
+ assert_kind_of(URI::HTTP, url)
+ assert_equal('http:g', url.to_s)
+ url = @base_url.route_to('http:g')
+ assert_kind_of(URI::Generic, url)
+ assert_equal('http:g', url.to_s)
+ end
+
+ def test_join
+ assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo/bar'))
+ assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo', 'bar'))
+ assert_equal(URI.parse('http://foo/bar/'), URI.join('http://foo', 'bar/'))
+
+ assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', 'baz'))
+ assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', '/baz'))
+ assert_equal(URI.parse('http://foo/baz/'), URI.join('http://foo', 'bar', '/baz/'))
+ assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/', 'baz'))
+ assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar', 'baz', 'hoge'))
+
+ assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/baz'))
+ assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge'))
+ assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge'))
+ assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge'))
+ assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge'))
+ assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge'))
+ assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge'))
+ end
+
+ # ruby-dev:16728
+ def test_set_component
+ uri = URI.parse('http://foo:bar@baz')
+ assert_equal('oof', uri.user = 'oof')
+ assert_equal('http://oof:bar@baz', uri.to_s)
+ assert_equal('rab', uri.password = 'rab')
+ assert_equal('http://oof:rab@baz', uri.to_s)
+ assert_equal('foo', uri.userinfo = 'foo')
+ assert_equal('http://foo:rab@baz', uri.to_s)
+ assert_equal(['foo', 'bar'], uri.userinfo = ['foo', 'bar'])
+ assert_equal('http://foo:bar@baz', uri.to_s)
+ assert_equal(['foo'], uri.userinfo = ['foo'])
+ assert_equal('http://foo:bar@baz', uri.to_s)
+ assert_equal('zab', uri.host = 'zab')
+ assert_equal('http://foo:bar@zab', uri.to_s)
+ uri.port = ""
+ assert_nil(uri.port)
+ uri.port = "80"
+ assert_equal(80, uri.port)
+ uri.port = "080"
+ assert_equal(80, uri.port)
+ uri.port = " 080 "
+ assert_equal(80, uri.port)
+ assert_equal(8080, uri.port = 8080)
+ assert_equal('http://foo:bar@zab:8080', uri.to_s)
+ assert_equal('/', uri.path = '/')
+ assert_equal('http://foo:bar@zab:8080/', uri.to_s)
+ assert_equal('a=1', uri.query = 'a=1')
+ assert_equal('http://foo:bar@zab:8080/?a=1', uri.to_s)
+ assert_equal('b123', uri.fragment = 'b123')
+ assert_equal('http://foo:bar@zab:8080/?a=1#b123', uri.to_s)
+ assert_equal('a[]=1', uri.query = 'a[]=1')
+ assert_equal('http://foo:bar@zab:8080/?a[]=1#b123', uri.to_s)
+ uri = URI.parse('http://foo:bar@zab:8080/?a[]=1#b123')
+ assert_equal('http://foo:bar@zab:8080/?a[]=1#b123', uri.to_s)
+
+ uri = URI.parse('http://example.com')
+ assert_raise(URI::InvalidURIError) { uri.password = 'bar' }
+ assert_equal("foo\nbar", uri.query = "foo\nbar")
+ uri.userinfo = 'foo:bar'
+ assert_equal('http://foo:bar@example.com?foobar', uri.to_s)
+ assert_raise(URI::InvalidURIError) { uri.registry = 'bar' }
+ assert_raise(URI::InvalidURIError) { uri.opaque = 'bar' }
+
+ uri = URI.parse('mailto:foo@example.com')
+ assert_raise(URI::InvalidURIError) { uri.user = 'bar' }
+ assert_raise(URI::InvalidURIError) { uri.password = 'bar' }
+ assert_raise(URI::InvalidURIError) { uri.userinfo = ['bar', 'baz'] }
+ assert_raise(URI::InvalidURIError) { uri.host = 'bar' }
+ assert_raise(URI::InvalidURIError) { uri.port = 'bar' }
+ assert_raise(URI::InvalidURIError) { uri.path = 'bar' }
+ assert_raise(URI::InvalidURIError) { uri.query = 'bar' }
+
+ uri = URI.parse('foo:bar')
+ assert_raise(URI::InvalidComponentError) { uri.opaque = '/baz' }
+ uri.opaque = 'xyzzy'
+ assert_equal('foo:xyzzy', uri.to_s)
+ end
+
+ def test_set_scheme
+ uri = URI.parse 'HTTP://example'
+
+ assert_equal 'http://example', uri.to_s
+ end
+
+ def test_ipv6
+ assert_equal("[::1]", URI("http://[::1]/bar/baz").host)
+ assert_equal("::1", URI("http://[::1]/bar/baz").hostname)
+
+ u = URI("http://foo/bar")
+ assert_equal("http://foo/bar", u.to_s)
+ u.hostname = "::1"
+ assert_equal("http://[::1]/bar", u.to_s)
+ end
+
+ def test_build
+ u = URI::Generic.build(['http', nil, 'example.com', 80, nil, '/foo', nil, nil, nil])
+ assert_equal('http://example.com:80/foo', u.to_s)
+
+ u = URI::Generic.build(:scheme => "http", :host => "::1", :path => "/bar/baz")
+ assert_equal("http://[::1]/bar/baz", u.to_s)
+ assert_equal("[::1]", u.host)
+ assert_equal("::1", u.hostname)
+
+ u = URI::Generic.build(:scheme => "http", :host => "[::1]", :path => "/bar/baz")
+ assert_equal("http://[::1]/bar/baz", u.to_s)
+ assert_equal("[::1]", u.host)
+ assert_equal("::1", u.hostname)
+ end
+
+ def test_build2
+ u = URI::Generic.build2(path: "/foo bar/baz")
+ assert_equal('/foo%20bar/baz', u.to_s)
+
+ u = URI::Generic.build2(['http', nil, 'example.com', 80, nil, '/foo bar' , nil, nil, nil])
+ assert_equal('http://example.com:80/foo%20bar', u.to_s)
+ end
+
+ # 192.0.2.0/24 is TEST-NET. [RFC3330]
+
+ def test_find_proxy
+ assert_raise(URI::BadURIError){ URI("foo").find_proxy }
+ with_env({}) {
+ assert_nil(URI("http://192.0.2.1/").find_proxy)
+ assert_nil(URI("ftp://192.0.2.1/").find_proxy)
+ }
+ with_env('http_proxy'=>'http://127.0.0.1:8080') {
+ assert_equal(URI('http://127.0.0.1:8080'), URI("http://192.0.2.1/").find_proxy)
+ assert_nil(URI("ftp://192.0.2.1/").find_proxy)
+ }
+ with_env('ftp_proxy'=>'http://127.0.0.1:8080') {
+ assert_nil(URI("http://192.0.2.1/").find_proxy)
+ assert_equal(URI('http://127.0.0.1:8080'), URI("ftp://192.0.2.1/").find_proxy)
+ }
+ with_env('REQUEST_METHOD'=>'GET') {
+ assert_nil(URI("http://192.0.2.1/").find_proxy)
+ }
+ with_env('CGI_HTTP_PROXY'=>'http://127.0.0.1:8080', 'REQUEST_METHOD'=>'GET') {
+ assert_equal(URI('http://127.0.0.1:8080'), URI("http://192.0.2.1/").find_proxy)
+ }
+ with_env('http_proxy'=>'http://127.0.0.1:8080', 'no_proxy'=>'192.0.2.2') {
+ assert_equal(URI('http://127.0.0.1:8080'), URI("http://192.0.2.1/").find_proxy)
+ assert_nil(URI("http://192.0.2.2/").find_proxy)
+ }
+ with_env('http_proxy'=>'') {
+ assert_nil(URI("http://192.0.2.1/").find_proxy)
+ assert_nil(URI("ftp://192.0.2.1/").find_proxy)
+ }
+ with_env('ftp_proxy'=>'') {
+ assert_nil(URI("http://192.0.2.1/").find_proxy)
+ assert_nil(URI("ftp://192.0.2.1/").find_proxy)
+ }
+ end
+
+ def test_find_proxy_case_sensitive_env
+ with_env('http_proxy'=>'http://127.0.0.1:8080', 'REQUEST_METHOD'=>'GET') {
+ assert_equal(URI('http://127.0.0.1:8080'), URI("http://192.0.2.1/").find_proxy)
+ }
+ with_env('HTTP_PROXY'=>'http://127.0.0.1:8081', 'REQUEST_METHOD'=>'GET') {
+ assert_nil(nil, URI("http://192.0.2.1/").find_proxy)
+ }
+ with_env('http_proxy'=>'http://127.0.0.1:8080', 'HTTP_PROXY'=>'http://127.0.0.1:8081', 'REQUEST_METHOD'=>'GET') {
+ assert_equal(URI('http://127.0.0.1:8080'), URI("http://192.0.2.1/").find_proxy)
+ }
+ end unless RUBY_PLATFORM =~ /mswin|mingw/
+
+ def with_env(h)
+ ['http', 'https', 'ftp'].each do |scheme|
+ name = "#{scheme}_proxy"
+ h[name] ||= nil
+ h["CGI_#{name.upcase}"] ||= nil
+ end
+ begin
+ old = {}
+ h.each_key {|k| old[k] = ENV[k] }
+ h.each {|k, v| ENV[k] = v }
+ yield
+ ensure
+ h.each_key {|k| ENV[k] = old[k] }
+ end
+ end
+
+end
diff --git a/jni/ruby/test/uri/test_http.rb b/jni/ruby/test/uri/test_http.rb
new file mode 100644
index 0000000..5d04e8c
--- /dev/null
+++ b/jni/ruby/test/uri/test_http.rb
@@ -0,0 +1,64 @@
+require 'test/unit'
+require 'uri/http'
+
+module URI
+
+
+class TestHTTP < Test::Unit::TestCase
+ def setup
+ end
+
+ def teardown
+ end
+
+ def uri_to_ary(uri)
+ uri.class.component.collect {|c| uri.send(c)}
+ end
+
+ def test_parse
+ u = URI.parse('http://a')
+ assert_kind_of(URI::HTTP, u)
+ assert_equal(['http',
+ nil, 'a', URI::HTTP.default_port,
+ '', nil, nil], uri_to_ary(u))
+ end
+
+ def test_normalize
+ host = 'aBcD'
+ u1 = URI.parse('http://' + host + '/eFg?HiJ')
+ u2 = URI.parse('http://' + host.downcase + '/eFg?HiJ')
+ assert(u1.normalize.host == 'abcd')
+ assert(u1.normalize.path == u1.path)
+ assert(u1.normalize == u2.normalize)
+ assert(!u1.normalize.host.equal?(u1.host))
+ assert( u2.normalize.host.equal?(u2.host))
+
+ assert_equal('http://abc/', URI.parse('http://abc').normalize.to_s)
+ end
+
+ def test_equal
+ assert(URI.parse('http://abc') == URI.parse('http://ABC'))
+ assert(URI.parse('http://abc/def') == URI.parse('http://ABC/def'))
+ assert(URI.parse('http://abc/def') != URI.parse('http://ABC/DEF'))
+ end
+
+ def test_request_uri
+ assert_equal('/', URI.parse('http://a.b.c/').request_uri)
+ assert_equal('/?abc=def', URI.parse('http://a.b.c/?abc=def').request_uri)
+ assert_equal('/', URI.parse('http://a.b.c').request_uri)
+ assert_equal('/?abc=def', URI.parse('http://a.b.c?abc=def').request_uri)
+ assert_equal(nil, URI.parse('http:foo').request_uri)
+ end
+
+ def test_select
+ assert_equal(['http', 'a.b.c', 80], URI.parse('http://a.b.c/').select(:scheme, :host, :port))
+ u = URI.parse('http://a.b.c/')
+ assert_equal(uri_to_ary(u), u.select(*u.component))
+ assert_raise(ArgumentError) do
+ u.select(:scheme, :host, :not_exist, :port)
+ end
+ end
+end
+
+
+end
diff --git a/jni/ruby/test/uri/test_ldap.rb b/jni/ruby/test/uri/test_ldap.rb
new file mode 100644
index 0000000..a4cdbff
--- /dev/null
+++ b/jni/ruby/test/uri/test_ldap.rb
@@ -0,0 +1,100 @@
+require 'test/unit'
+require 'uri/ldap'
+
+module URI
+
+
+class TestLDAP < Test::Unit::TestCase
+ def setup
+ end
+
+ def teardown
+ end
+
+ def uri_to_ary(uri)
+ uri.class.component.collect {|c| uri.send(c)}
+ end
+
+ def test_parse
+ url = 'ldap://ldap.jaist.ac.jp/o=JAIST,c=JP?sn?base?(sn=ttate*)'
+ u = URI.parse(url)
+ assert_kind_of(URI::LDAP, u)
+ assert_equal(url, u.to_s)
+ assert_equal('o=JAIST,c=JP', u.dn)
+ assert_equal('sn', u.attributes)
+ assert_equal('base', u.scope)
+ assert_equal('(sn=ttate*)', u.filter)
+ assert_equal(nil, u.extensions)
+
+ u.scope = URI::LDAP::SCOPE_SUB
+ u.attributes = 'sn,cn,mail'
+ assert_equal('ldap://ldap.jaist.ac.jp/o=JAIST,c=JP?sn,cn,mail?sub?(sn=ttate*)', u.to_s)
+ assert_equal('o=JAIST,c=JP', u.dn)
+ assert_equal('sn,cn,mail', u.attributes)
+ assert_equal('sub', u.scope)
+ assert_equal('(sn=ttate*)', u.filter)
+ assert_equal(nil, u.extensions)
+
+ # from RFC2255, section 6.
+ {
+ 'ldap:///o=University%20of%20Michigan,c=US' =>
+ ['ldap', nil, URI::LDAP::DEFAULT_PORT,
+ 'o=University%20of%20Michigan,c=US',
+ nil, nil, nil, nil],
+
+ 'ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US' =>
+ ['ldap', 'ldap.itd.umich.edu', URI::LDAP::DEFAULT_PORT,
+ 'o=University%20of%20Michigan,c=US',
+ nil, nil, nil, nil],
+
+ 'ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress' =>
+ ['ldap', 'ldap.itd.umich.edu', URI::LDAP::DEFAULT_PORT,
+ 'o=University%20of%20Michigan,c=US',
+ 'postalAddress', nil, nil, nil],
+
+ 'ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen)' =>
+ ['ldap', 'host.com', 6666,
+ 'o=University%20of%20Michigan,c=US',
+ nil, 'sub', '(cn=Babs%20Jensen)', nil],
+
+ 'ldap://ldap.itd.umich.edu/c=GB?objectClass?one' =>
+ ['ldap', 'ldap.itd.umich.edu', URI::LDAP::DEFAULT_PORT,
+ 'c=GB',
+ 'objectClass', 'one', nil, nil],
+
+ 'ldap://ldap.question.com/o=Question%3f,c=US?mail' =>
+ ['ldap', 'ldap.question.com', URI::LDAP::DEFAULT_PORT,
+ 'o=Question%3f,c=US',
+ 'mail', nil, nil, nil],
+
+ 'ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04)' =>
+ ['ldap', 'ldap.netscape.com', URI::LDAP::DEFAULT_PORT,
+ 'o=Babsco,c=US',
+ nil, '(int=%5c00%5c00%5c00%5c04)', nil, nil],
+
+ 'ldap:///??sub??bindname=cn=Manager%2co=Foo' =>
+ ['ldap', nil, URI::LDAP::DEFAULT_PORT,
+ '',
+ nil, 'sub', nil, 'bindname=cn=Manager%2co=Foo'],
+
+ 'ldap:///??sub??!bindname=cn=Manager%2co=Foo' =>
+ ['ldap', nil, URI::LDAP::DEFAULT_PORT,
+ '',
+ nil, 'sub', nil, '!bindname=cn=Manager%2co=Foo'],
+ }.each do |url2, ary|
+ u = URI.parse(url2)
+ assert_equal(ary, uri_to_ary(u))
+ end
+ end
+
+ def test_select
+ u = URI.parse('ldap:///??sub??!bindname=cn=Manager%2co=Foo')
+ assert_equal(uri_to_ary(u), u.select(*u.component))
+ assert_raise(ArgumentError) do
+ u.select(:scheme, :host, :not_exist, :port)
+ end
+ end
+end
+
+
+end
diff --git a/jni/ruby/test/uri/test_mailto.rb b/jni/ruby/test/uri/test_mailto.rb
new file mode 100644
index 0000000..661f7f7
--- /dev/null
+++ b/jni/ruby/test/uri/test_mailto.rb
@@ -0,0 +1,136 @@
+require 'test/unit'
+require 'uri/mailto'
+
+module URI
+
+
+class TestMailTo < Test::Unit::TestCase
+ def setup
+ @u = URI::MailTo
+ end
+
+ def teardown
+ end
+
+ def uri_to_ary(uri)
+ uri.class.component.collect {|c| uri.send(c)}
+ end
+
+ def test_build
+ ok = []
+ bad = []
+
+ # RFC2368, 6. Examples
+ # mailto:chris@example.com
+ ok << ["mailto:chris@example.com"]
+ ok[-1] << ["chris@example.com", nil]
+ ok[-1] << {:to => "chris@example.com"}
+
+ ok << ["mailto:foo+@example.com,bar@example.com"]
+ ok[-1] << [["foo+@example.com", "bar@example.com"], nil]
+ ok[-1] << {:to => "foo+@example.com,bar@example.com"}
+
+ # mailto:infobot@example.com?subject=current-issue
+ ok << ["mailto:infobot@example.com?subject=current-issue"]
+ ok[-1] << ["infobot@example.com", ["subject=current-issue"]]
+ ok[-1] << {:to => "infobot@example.com",
+ :headers => ["subject=current-issue"]}
+
+ # mailto:infobot@example.com?body=send%20current-issue
+ ok << ["mailto:infobot@example.com?body=send%20current-issue"]
+ ok[-1] << ["infobot@example.com", ["body=send%20current-issue"]]
+ ok[-1] << {:to => "infobot@example.com",
+ :headers => ["body=send%20current-issue"]}
+
+ # mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index
+ ok << ["mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index"]
+ ok[-1] << ["infobot@example.com",
+ ["body=send%20current-issue%0D%0Asend%20index"]]
+ ok[-1] << {:to => "infobot@example.com",
+ :headers => ["body=send%20current-issue%0D%0Asend%20index"]}
+
+ # mailto:foobar@example.com?In-Reply-To=%3c3469A91.D10AF4C@example.com
+ ok << ["mailto:foobar@example.com?In-Reply-To=%3c3469A91.D10AF4C@example.com"]
+ ok[-1] << ["foobar@example.com",
+ ["In-Reply-To=%3c3469A91.D10AF4C@example.com"]]
+ ok[-1] << {:to => "foobar@example.com",
+ :headers => ["In-Reply-To=%3c3469A91.D10AF4C@example.com"]}
+
+ # mailto:majordomo@example.com?body=subscribe%20bamboo-l
+ ok << ["mailto:majordomo@example.com?body=subscribe%20bamboo-l"]
+ ok[-1] << ["majordomo@example.com", ["body=subscribe%20bamboo-l"]]
+ ok[-1] << {:to => "majordomo@example.com",
+ :headers => ["body=subscribe%20bamboo-l"]}
+
+ # mailto:joe@example.com?cc=bob@example.com&body=hello
+ ok << ["mailto:joe@example.com?cc=bob@example.com&body=hello"]
+ ok[-1] << ["joe@example.com", ["cc=bob@example.com", "body=hello"]]
+ ok[-1] << {:to => "joe@example.com",
+ :headers => ["cc=bob@example.com", "body=hello"]}
+
+ # mailto:?to=joe@example.com&cc=bob@example.com&body=hello
+ ok << ["mailto:?to=joe@example.com&cc=bob@example.com&body=hello"]
+ ok[-1] << [nil,
+ ["to=joe@example.com", "cc=bob@example.com", "body=hello"]]
+ ok[-1] << {:headers => ["to=joe@example.com",
+ "cc=bob@example.com", "body=hello"]}
+
+ # mailto:gorby%25kremvax@example.com
+ ok << ["mailto:gorby%25kremvax@example.com"]
+ ok[-1] << ["gorby%25kremvax@example.com", nil]
+ ok[-1] << {:to => "gorby%25kremvax@example.com"}
+
+ # mailto:unlikely%3Faddress@example.com?blat=foop
+ ok << ["mailto:unlikely%3Faddress@example.com?blat=foop"]
+ ok[-1] << ["unlikely%3Faddress@example.com", ["blat=foop"]]
+ ok[-1] << {:to => "unlikely%3Faddress@example.com",
+ :headers => ["blat=foop"]}
+
+ # mailto:john@example.com?Subject=Ruby&Cc=jack@example.com
+ ok << ["mailto:john@example.com?Subject=Ruby&Cc=jack@example.com"]
+ ok[-1] << ['john@example.com', [['Subject', 'Ruby'], ['Cc', 'jack@example.com']]]
+ ok[-1] << {:to=>"john@example.com", :headers=>[["Subject", "Ruby"], ["Cc", "jack@example.com"]]}
+
+ # mailto:listman@example.com?subject=subscribe
+ ok << ["mailto:listman@example.com?subject=subscribe"]
+ ok[-1] << {:to => 'listman@example.com', :headers => [['subject', 'subscribe']]}
+ ok[-1] << {:to => 'listman@example.com', :headers => [['subject', 'subscribe']]}
+
+ ok_all = ok.flatten.join("\0")
+
+ # mailto:joe@example.com?cc=bob@example.com?body=hello ; WRONG!
+ bad << ["joe@example.com", ["cc=bob@example.com?body=hello"]]
+
+ # mailto:javascript:alert()
+ bad << ["javascript:alert()", []]
+
+ # '=' which is in hname or hvalue is wrong.
+ bad << ["foo@example.jp?subject=1+1=2", []]
+
+ ok.each do |x|
+ assert_equal(x[0],
+ @u.build(x[1]).to_s)
+ assert_equal(x[0],
+ @u.build(x[2]).to_s)
+ end
+
+ bad.each do |x|
+ assert_raise(URI::InvalidComponentError) {
+ @u.build(x)
+ }
+ end
+
+ assert_equal(ok_all, ok.flatten.join("\0"))
+ end
+
+ def test_select
+ u = URI.parse('mailto:joe@example.com?cc=bob@example.com&body=hello')
+ assert_equal(uri_to_ary(u), u.select(*u.component))
+ assert_raise(ArgumentError) do
+ u.select(:scheme, :host, :not_exist, :port)
+ end
+ end
+end
+
+
+end
diff --git a/jni/ruby/test/uri/test_parser.rb b/jni/ruby/test/uri/test_parser.rb
new file mode 100644
index 0000000..188b4f8
--- /dev/null
+++ b/jni/ruby/test/uri/test_parser.rb
@@ -0,0 +1,47 @@
+require 'test/unit'
+require 'uri'
+
+class URI::TestParser < Test::Unit::TestCase
+ def uri_to_ary(uri)
+ uri.class.component.collect {|c| uri.send(c)}
+ end
+
+ def test_compare
+ url = 'http://a/b/c/d;p?q'
+ u0 = URI.parse(url)
+ u1 = URI.parse(url)
+ p = URI::Parser.new
+ u2 = p.parse(url)
+ u3 = p.parse(url)
+
+ assert(u0 == u1)
+ assert(u0.eql?(u1))
+ assert(!u0.equal?(u1))
+
+ assert(u1 == u2)
+ assert(!u1.eql?(u2))
+ assert(!u1.equal?(u2))
+
+ assert(u2 == u3)
+ assert(u2.eql?(u3))
+ assert(!u2.equal?(u3))
+ end
+
+ def test_parse
+ escaped = URI::REGEXP::PATTERN::ESCAPED
+ hex = URI::REGEXP::PATTERN::HEX
+ p1 = URI::Parser.new(:ESCAPED => "(?:#{escaped}|%u[#{hex}]{4})")
+ u1 = p1.parse('http://a/b/%uABCD')
+ assert_equal(['http', nil, 'a', URI::HTTP.default_port, '/b/%uABCD', nil, nil],
+ uri_to_ary(u1))
+ u1.path = '/%uDCBA'
+ assert_equal(['http', nil, 'a', URI::HTTP.default_port, '/%uDCBA', nil, nil],
+ uri_to_ary(u1))
+ end
+
+ def test_raise_bad_uri_for_integer
+ assert_raise(URI::InvalidURIError) do
+ URI.parse(1)
+ end
+ end
+end
diff --git a/jni/ruby/test/webrick/.htaccess b/jni/ruby/test/webrick/.htaccess
new file mode 100644
index 0000000..69d4659
--- /dev/null
+++ b/jni/ruby/test/webrick/.htaccess
@@ -0,0 +1 @@
+this file should not be published.
diff --git a/jni/ruby/test/webrick/test_cgi.rb b/jni/ruby/test/webrick/test_cgi.rb
new file mode 100644
index 0000000..5507dfe
--- /dev/null
+++ b/jni/ruby/test/webrick/test_cgi.rb
@@ -0,0 +1,151 @@
+# coding: US-ASCII
+require_relative "utils"
+require "webrick"
+require "test/unit"
+
+class TestWEBrickCGI < Test::Unit::TestCase
+ CRLF = "\r\n"
+
+ def start_cgi_server(log_tester=TestWEBrick::DefaultLogTester, &block)
+ config = {
+ :CGIInterpreter => TestWEBrick::RubyBin,
+ :DocumentRoot => File.dirname(__FILE__),
+ :DirectoryIndex => ["webrick.cgi"],
+ :RequestCallback => Proc.new{|req, res|
+ def req.meta_vars
+ meta = super
+ meta["RUBYLIB"] = $:.join(File::PATH_SEPARATOR)
+ meta[RbConfig::CONFIG['LIBPATHENV']] = ENV[RbConfig::CONFIG['LIBPATHENV']] if RbConfig::CONFIG['LIBPATHENV']
+ return meta
+ end
+ },
+ }
+ if RUBY_PLATFORM =~ /mswin|mingw|cygwin|bccwin32/
+ config[:CGIPathEnv] = ENV['PATH'] # runtime dll may not be in system dir.
+ end
+ TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
+ block.call(server, addr, port, log)
+ }
+ end
+
+ def test_cgi
+ start_cgi_server{|server, addr, port, log|
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/webrick.cgi")
+ http.request(req){|res| assert_equal("/webrick.cgi", res.body, log.call)}
+ req = Net::HTTP::Get.new("/webrick.cgi/path/info")
+ http.request(req){|res| assert_equal("/path/info", res.body, log.call)}
+ req = Net::HTTP::Get.new("/webrick.cgi/%3F%3F%3F?foo=bar")
+ http.request(req){|res| assert_equal("/???", res.body, log.call)}
+ req = Net::HTTP::Get.new("/webrick.cgi/%A4%DB%A4%B2/%A4%DB%A4%B2")
+ # Path info of res.body is passed via ENV.
+ # ENV[] returns different value on Windows depending on locale.
+ unless RUBY_PLATFORM =~ /mswin|mingw|cygwin|bccwin32/ &&
+ Encoding.find("locale") != Encoding.find("filesystem")
+ http.request(req){|res|
+ assert_equal("/\xA4\xDB\xA4\xB2/\xA4\xDB\xA4\xB2", res.body, log.call)}
+ end
+ req = Net::HTTP::Get.new("/webrick.cgi?a=1;a=2;b=x")
+ http.request(req){|res| assert_equal("a=1, a=2, b=x", res.body, log.call)}
+ req = Net::HTTP::Get.new("/webrick.cgi?a=1&a=2&b=x")
+ http.request(req){|res| assert_equal("a=1, a=2, b=x", res.body, log.call)}
+
+ req = Net::HTTP::Post.new("/webrick.cgi?a=x;a=y;b=1")
+ req["Content-Type"] = "application/x-www-form-urlencoded"
+ http.request(req, "a=1;a=2;b=x"){|res|
+ assert_equal("a=1, a=2, b=x", res.body, log.call)}
+ req = Net::HTTP::Post.new("/webrick.cgi?a=x&a=y&b=1")
+ req["Content-Type"] = "application/x-www-form-urlencoded"
+ http.request(req, "a=1&a=2&b=x"){|res|
+ assert_equal("a=1, a=2, b=x", res.body, log.call)}
+ req = Net::HTTP::Get.new("/")
+ http.request(req){|res|
+ ary = res.body.lines.to_a
+ assert_match(%r{/$}, ary[0], log.call)
+ assert_match(%r{/webrick.cgi$}, ary[1], log.call)
+ }
+
+ req = Net::HTTP::Get.new("/webrick.cgi")
+ req["Cookie"] = "CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001"
+ http.request(req){|res|
+ assert_equal(
+ "CUSTOMER=WILE_E_COYOTE\nPART_NUMBER=ROCKET_LAUNCHER_0001\n",
+ res.body, log.call)
+ }
+
+ req = Net::HTTP::Get.new("/webrick.cgi")
+ cookie = %{$Version="1"; }
+ cookie << %{Customer="WILE_E_COYOTE"; $Path="/acme"; }
+ cookie << %{Part_Number="Rocket_Launcher_0001"; $Path="/acme"; }
+ cookie << %{Shipping="FedEx"; $Path="/acme"}
+ req["Cookie"] = cookie
+ http.request(req){|res|
+ assert_equal("Customer=WILE_E_COYOTE, Shipping=FedEx",
+ res["Set-Cookie"], log.call)
+ assert_equal("Customer=WILE_E_COYOTE\n" +
+ "Part_Number=Rocket_Launcher_0001\n" +
+ "Shipping=FedEx\n", res.body, log.call)
+ }
+ }
+ end
+
+ def test_bad_request
+ log_tester = lambda {|log, access_log|
+ assert_match(/BadRequest/, log.join)
+ }
+ start_cgi_server(log_tester) {|server, addr, port, log|
+ sock = TCPSocket.new(addr, port)
+ begin
+ sock << "POST /webrick.cgi HTTP/1.0" << CRLF
+ sock << "Content-Type: application/x-www-form-urlencoded" << CRLF
+ sock << "Content-Length: 1024" << CRLF
+ sock << CRLF
+ sock << "a=1&a=2&b=x"
+ sock.close_write
+ assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, sock.read, log.call)
+ ensure
+ sock.close
+ end
+ }
+ end
+
+ CtrlSeq = [0x7f, *(1..31)].pack("C*").gsub(/\s+/, '')
+ CtrlPat = /#{Regexp.quote(CtrlSeq)}/o
+ DumpPat = /#{Regexp.quote(CtrlSeq.dump[1...-1])}/o
+
+ def test_bad_uri
+ log_tester = lambda {|log, access_log|
+ assert_equal(1, log.length)
+ assert_match(/ERROR bad URI/, log[0])
+ }
+ start_cgi_server(log_tester) {|server, addr, port, log|
+ res = TCPSocket.open(addr, port) {|sock|
+ sock << "GET /#{CtrlSeq}#{CRLF}#{CRLF}"
+ sock.close_write
+ sock.read
+ }
+ assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, res)
+ s = log.call.each_line.grep(/ERROR bad URI/)[0]
+ assert_match(DumpPat, s)
+ assert_not_match(CtrlPat, s)
+ }
+ end
+
+ def test_bad_header
+ log_tester = lambda {|log, access_log|
+ assert_equal(1, log.length)
+ assert_match(/ERROR bad header/, log[0])
+ }
+ start_cgi_server(log_tester) {|server, addr, port, log|
+ res = TCPSocket.open(addr, port) {|sock|
+ sock << "GET / HTTP/1.0#{CRLF}#{CtrlSeq}#{CRLF}#{CRLF}"
+ sock.close_write
+ sock.read
+ }
+ assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, res)
+ s = log.call.each_line.grep(/ERROR bad header/)[0]
+ assert_match(DumpPat, s)
+ assert_not_match(CtrlPat, s)
+ }
+ end
+end
diff --git a/jni/ruby/test/webrick/test_cookie.rb b/jni/ruby/test/webrick/test_cookie.rb
new file mode 100644
index 0000000..1652f39
--- /dev/null
+++ b/jni/ruby/test/webrick/test_cookie.rb
@@ -0,0 +1,131 @@
+require "test/unit"
+require "webrick/cookie"
+
+class TestWEBrickCookie < Test::Unit::TestCase
+ def test_new
+ cookie = WEBrick::Cookie.new("foo","bar")
+ assert_equal("foo", cookie.name)
+ assert_equal("bar", cookie.value)
+ assert_equal("foo=bar", cookie.to_s)
+ end
+
+ def test_time
+ cookie = WEBrick::Cookie.new("foo","bar")
+ t = 1000000000
+ cookie.max_age = t
+ assert_match(t.to_s, cookie.to_s)
+
+ cookie = WEBrick::Cookie.new("foo","bar")
+ t = Time.at(1000000000)
+ cookie.expires = t
+ assert_equal(Time, cookie.expires.class)
+ assert_equal(t, cookie.expires)
+ ts = t.httpdate
+ cookie.expires = ts
+ assert_equal(Time, cookie.expires.class)
+ assert_equal(t, cookie.expires)
+ assert_match(ts, cookie.to_s)
+ end
+
+ def test_parse
+ data = ""
+ data << '$Version="1"; '
+ data << 'Customer="WILE_E_COYOTE"; $Path="/acme"; '
+ data << 'Part_Number="Rocket_Launcher_0001"; $Path="/acme"; '
+ data << 'Shipping="FedEx"; $Path="/acme"'
+ cookies = WEBrick::Cookie.parse(data)
+ assert_equal(3, cookies.size)
+ assert_equal(1, cookies[0].version)
+ assert_equal("Customer", cookies[0].name)
+ assert_equal("WILE_E_COYOTE", cookies[0].value)
+ assert_equal("/acme", cookies[0].path)
+ assert_equal(1, cookies[1].version)
+ assert_equal("Part_Number", cookies[1].name)
+ assert_equal("Rocket_Launcher_0001", cookies[1].value)
+ assert_equal(1, cookies[2].version)
+ assert_equal("Shipping", cookies[2].name)
+ assert_equal("FedEx", cookies[2].value)
+
+ data = "hoge=moge; __div__session=9865ecfd514be7f7"
+ cookies = WEBrick::Cookie.parse(data)
+ assert_equal(0, cookies[0].version)
+ assert_equal("hoge", cookies[0].name)
+ assert_equal("moge", cookies[0].value)
+ assert_equal("__div__session", cookies[1].name)
+ assert_equal("9865ecfd514be7f7", cookies[1].value)
+ end
+
+ def test_parse_no_whitespace
+ data = [
+ '$Version="1"; ',
+ 'Customer="WILE_E_COYOTE";$Path="/acme";', # no SP between cookie-string
+ 'Part_Number="Rocket_Launcher_0001";$Path="/acme";', # no SP between cookie-string
+ 'Shipping="FedEx";$Path="/acme"'
+ ].join
+ cookies = WEBrick::Cookie.parse(data)
+ assert_equal(1, cookies.size)
+ end
+
+ def test_parse_too_much_whitespaces
+ # According to RFC6265,
+ # cookie-string = cookie-pair *( ";" SP cookie-pair )
+ # So single 0x20 is needed after ';'. We allow multiple spaces here for
+ # compatibility with older WEBrick versions.
+ data = [
+ '$Version="1"; ',
+ 'Customer="WILE_E_COYOTE";$Path="/acme"; ', # no SP between cookie-string
+ 'Part_Number="Rocket_Launcher_0001";$Path="/acme"; ', # no SP between cookie-string
+ 'Shipping="FedEx";$Path="/acme"'
+ ].join
+ cookies = WEBrick::Cookie.parse(data)
+ assert_equal(3, cookies.size)
+ end
+
+ def test_parse_set_cookie
+ data = %(Customer="WILE_E_COYOTE"; Version="1"; Path="/acme")
+ cookie = WEBrick::Cookie.parse_set_cookie(data)
+ assert_equal("Customer", cookie.name)
+ assert_equal("WILE_E_COYOTE", cookie.value)
+ assert_equal(1, cookie.version)
+ assert_equal("/acme", cookie.path)
+
+ data = %(Shipping="FedEx"; Version="1"; Path="/acme"; Secure)
+ cookie = WEBrick::Cookie.parse_set_cookie(data)
+ assert_equal("Shipping", cookie.name)
+ assert_equal("FedEx", cookie.value)
+ assert_equal(1, cookie.version)
+ assert_equal("/acme", cookie.path)
+ assert_equal(true, cookie.secure)
+ end
+
+ def test_parse_set_cookies
+ data = %(Shipping="FedEx"; Version="1"; Path="/acme"; Secure)
+ data << %(, CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT; path=/; Secure)
+ data << %(, name="Aaron"; Version="1"; path="/acme")
+ cookies = WEBrick::Cookie.parse_set_cookies(data)
+ assert_equal(3, cookies.length)
+
+ fed_ex = cookies.find { |c| c.name == 'Shipping' }
+ assert_not_nil(fed_ex)
+ assert_equal("Shipping", fed_ex.name)
+ assert_equal("FedEx", fed_ex.value)
+ assert_equal(1, fed_ex.version)
+ assert_equal("/acme", fed_ex.path)
+ assert_equal(true, fed_ex.secure)
+
+ name = cookies.find { |c| c.name == 'name' }
+ assert_not_nil(name)
+ assert_equal("name", name.name)
+ assert_equal("Aaron", name.value)
+ assert_equal(1, name.version)
+ assert_equal("/acme", name.path)
+
+ customer = cookies.find { |c| c.name == 'CUSTOMER' }
+ assert_not_nil(customer)
+ assert_equal("CUSTOMER", customer.name)
+ assert_equal("WILE_E_COYOTE", customer.value)
+ assert_equal(0, customer.version)
+ assert_equal("/", customer.path)
+ assert_equal(Time.utc(1999, 11, 9, 23, 12, 40), customer.expires)
+ end
+end
diff --git a/jni/ruby/test/webrick/test_filehandler.rb b/jni/ruby/test/webrick/test_filehandler.rb
new file mode 100644
index 0000000..f984efb
--- /dev/null
+++ b/jni/ruby/test/webrick/test_filehandler.rb
@@ -0,0 +1,309 @@
+require "test/unit"
+require_relative "utils.rb"
+require "webrick"
+require "stringio"
+
+class WEBrick::TestFileHandler < Test::Unit::TestCase
+ def default_file_handler(filename)
+ klass = WEBrick::HTTPServlet::DefaultFileHandler
+ klass.new(WEBrick::Config::HTTP, filename)
+ end
+
+ def windows?
+ File.directory?("\\")
+ end
+
+ def get_res_body(res)
+ body = res.body
+ if defined? body.read
+ begin
+ body.read
+ ensure
+ body.close
+ end
+ else
+ body
+ end
+ end
+
+ def make_range_request(range_spec)
+ msg = <<-END_OF_REQUEST
+ GET / HTTP/1.0
+ Range: #{range_spec}
+
+ END_OF_REQUEST
+ return StringIO.new(msg.gsub(/^ {6}/, ""))
+ end
+
+ def make_range_response(file, range_spec)
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(make_range_request(range_spec))
+ res = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP)
+ size = File.size(file)
+ handler = default_file_handler(file)
+ handler.make_partial_content(req, res, file, size)
+ return res
+ end
+
+ def test_make_partial_content
+ filename = __FILE__
+ filesize = File.size(filename)
+
+ res = make_range_response(filename, "bytes=#{filesize-100}-")
+ assert_match(%r{^text/plain}, res["content-type"])
+ assert_equal(100, get_res_body(res).size)
+
+ res = make_range_response(filename, "bytes=-100")
+ assert_match(%r{^text/plain}, res["content-type"])
+ assert_equal(100, get_res_body(res).size)
+
+ res = make_range_response(filename, "bytes=0-99")
+ assert_match(%r{^text/plain}, res["content-type"])
+ assert_equal(100, get_res_body(res).size)
+
+ res = make_range_response(filename, "bytes=100-199")
+ assert_match(%r{^text/plain}, res["content-type"])
+ assert_equal(100, get_res_body(res).size)
+
+ res = make_range_response(filename, "bytes=0-0")
+ assert_match(%r{^text/plain}, res["content-type"])
+ assert_equal(1, get_res_body(res).size)
+
+ res = make_range_response(filename, "bytes=-1")
+ assert_match(%r{^text/plain}, res["content-type"])
+ assert_equal(1, get_res_body(res).size)
+
+ res = make_range_response(filename, "bytes=0-0, -2")
+ assert_match(%r{^multipart/byteranges}, res["content-type"])
+ end
+
+ def test_filehandler
+ config = { :DocumentRoot => File.dirname(__FILE__), }
+ this_file = File.basename(__FILE__)
+ filesize = File.size(__FILE__)
+ this_data = File.open(__FILE__, "rb") {|f| f.read}
+ range = nil
+ bug2593 = '[ruby-dev:40030]'
+
+ TestWEBrick.start_httpserver(config) do |server, addr, port, log|
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/")
+ http.request(req){|res|
+ assert_equal("200", res.code, log.call)
+ assert_equal("text/html", res.content_type, log.call)
+ assert_match(/HREF="#{this_file}"/, res.body, log.call)
+ }
+ req = Net::HTTP::Get.new("/#{this_file}")
+ http.request(req){|res|
+ assert_equal("200", res.code, log.call)
+ assert_equal("text/plain", res.content_type, log.call)
+ assert_equal(File.read(__FILE__), res.body, log.call)
+ }
+
+ req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=#{filesize-100}-")
+ http.request(req){|res|
+ assert_equal("206", res.code, log.call)
+ assert_equal("text/plain", res.content_type, log.call)
+ assert_nothing_raised(bug2593) {range = res.content_range}
+ assert_equal((filesize-100)..(filesize-1), range, log.call)
+ assert_equal(this_data[-100..-1], res.body, log.call)
+ }
+
+ req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=-100")
+ http.request(req){|res|
+ assert_equal("206", res.code, log.call)
+ assert_equal("text/plain", res.content_type, log.call)
+ assert_nothing_raised(bug2593) {range = res.content_range}
+ assert_equal((filesize-100)..(filesize-1), range, log.call)
+ assert_equal(this_data[-100..-1], res.body, log.call)
+ }
+
+ req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-99")
+ http.request(req){|res|
+ assert_equal("206", res.code, log.call)
+ assert_equal("text/plain", res.content_type, log.call)
+ assert_nothing_raised(bug2593) {range = res.content_range}
+ assert_equal(0..99, range, log.call)
+ assert_equal(this_data[0..99], res.body, log.call)
+ }
+
+ req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=100-199")
+ http.request(req){|res|
+ assert_equal("206", res.code, log.call)
+ assert_equal("text/plain", res.content_type, log.call)
+ assert_nothing_raised(bug2593) {range = res.content_range}
+ assert_equal(100..199, range, log.call)
+ assert_equal(this_data[100..199], res.body, log.call)
+ }
+
+ req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-0")
+ http.request(req){|res|
+ assert_equal("206", res.code, log.call)
+ assert_equal("text/plain", res.content_type, log.call)
+ assert_nothing_raised(bug2593) {range = res.content_range}
+ assert_equal(0..0, range, log.call)
+ assert_equal(this_data[0..0], res.body, log.call)
+ }
+
+ req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=-1")
+ http.request(req){|res|
+ assert_equal("206", res.code, log.call)
+ assert_equal("text/plain", res.content_type, log.call)
+ assert_nothing_raised(bug2593) {range = res.content_range}
+ assert_equal((filesize-1)..(filesize-1), range, log.call)
+ assert_equal(this_data[-1, 1], res.body, log.call)
+ }
+
+ req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-0, -2")
+ http.request(req){|res|
+ assert_equal("206", res.code, log.call)
+ assert_equal("multipart/byteranges", res.content_type, log.call)
+ }
+
+ end
+ end
+
+ def test_non_disclosure_name
+ config = { :DocumentRoot => File.dirname(__FILE__), }
+ log_tester = lambda {|log, access_log|
+ log = log.reject {|s| /ERROR `.*' not found\./ =~ s }
+ log = log.reject {|s| /WARN the request refers nondisclosure name/ =~ s }
+ assert_equal([], log)
+ }
+ this_file = File.basename(__FILE__)
+ TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
+ http = Net::HTTP.new(addr, port)
+ doc_root_opts = server[:DocumentRootOptions]
+ doc_root_opts[:NondisclosureName] = %w(.ht* *~ test_*)
+ req = Net::HTTP::Get.new("/")
+ http.request(req){|res|
+ assert_equal("200", res.code, log.call)
+ assert_equal("text/html", res.content_type, log.call)
+ assert_no_match(/HREF="#{File.basename(__FILE__)}"/, res.body)
+ }
+ req = Net::HTTP::Get.new("/#{this_file}")
+ http.request(req){|res|
+ assert_equal("404", res.code, log.call)
+ }
+ doc_root_opts[:NondisclosureName] = %w(.ht* *~ TEST_*)
+ http.request(req){|res|
+ assert_equal("404", res.code, log.call)
+ }
+ end
+ end
+
+ def test_directory_traversal
+ config = { :DocumentRoot => File.dirname(__FILE__), }
+ log_tester = lambda {|log, access_log|
+ log = log.reject {|s| /ERROR bad URI/ =~ s }
+ log = log.reject {|s| /ERROR `.*' not found\./ =~ s }
+ assert_equal([], log)
+ }
+ TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/../../")
+ http.request(req){|res| assert_equal("400", res.code, log.call) }
+ req = Net::HTTP::Get.new("/..%5c../#{File.basename(__FILE__)}")
+ http.request(req){|res| assert_equal(windows? ? "200" : "404", res.code, log.call) }
+ req = Net::HTTP::Get.new("/..%5c..%5cruby.c")
+ http.request(req){|res| assert_equal("404", res.code, log.call) }
+ end
+ end
+
+ def test_unwise_in_path
+ if windows?
+ config = { :DocumentRoot => File.dirname(__FILE__), }
+ TestWEBrick.start_httpserver(config) do |server, addr, port, log|
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/..%5c..")
+ http.request(req){|res| assert_equal("301", res.code, log.call) }
+ end
+ end
+ end
+
+ def test_short_filename
+ config = {
+ :CGIInterpreter => TestWEBrick::RubyBin,
+ :DocumentRoot => File.dirname(__FILE__),
+ :CGIPathEnv => ENV['PATH'],
+ }
+ log_tester = lambda {|log, access_log|
+ log = log.reject {|s| /ERROR `.*' not found\./ =~ s }
+ log = log.reject {|s| /WARN the request refers nondisclosure name/ =~ s }
+ assert_equal([], log)
+ }
+ TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
+ http = Net::HTTP.new(addr, port)
+ if windows?
+ fname = nil
+ Dir.chdir(config[:DocumentRoot]) do
+ fname = `dir /x webrick_long_filename.cgi`.match(/\s(w.+?cgi)\s/i)[1].downcase
+ end
+ else
+ fname = "webric~1.cgi"
+ end
+ req = Net::HTTP::Get.new("/#{fname}/test")
+ http.request(req) do |res|
+ if windows?
+ assert_equal("200", res.code, log.call)
+ assert_equal("/test", res.body, log.call)
+ else
+ assert_equal("404", res.code, log.call)
+ end
+ end
+
+ req = Net::HTTP::Get.new("/.htaccess")
+ http.request(req) {|res| assert_equal("404", res.code, log.call) }
+ req = Net::HTTP::Get.new("/htacce~1")
+ http.request(req) {|res| assert_equal("404", res.code, log.call) }
+ req = Net::HTTP::Get.new("/HTACCE~1")
+ http.request(req) {|res| assert_equal("404", res.code, log.call) }
+ end
+ end
+
+ def test_script_disclosure
+ config = {
+ :CGIInterpreter => TestWEBrick::RubyBin,
+ :DocumentRoot => File.dirname(__FILE__),
+ :CGIPathEnv => ENV['PATH'],
+ :RequestCallback => Proc.new{|req, res|
+ def req.meta_vars
+ meta = super
+ meta["RUBYLIB"] = $:.join(File::PATH_SEPARATOR)
+ meta[RbConfig::CONFIG['LIBPATHENV']] = ENV[RbConfig::CONFIG['LIBPATHENV']] if RbConfig::CONFIG['LIBPATHENV']
+ return meta
+ end
+ },
+ }
+ log_tester = lambda {|log, access_log|
+ log = log.reject {|s| /ERROR `.*' not found\./ =~ s }
+ assert_equal([], log)
+ }
+ TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
+ http = Net::HTTP.new(addr, port)
+
+ req = Net::HTTP::Get.new("/webrick.cgi/test")
+ http.request(req) do |res|
+ assert_equal("200", res.code, log.call)
+ assert_equal("/test", res.body, log.call)
+ end
+
+ resok = windows?
+ response_assertion = Proc.new do |res|
+ if resok
+ assert_equal("200", res.code, log.call)
+ assert_equal("/test", res.body, log.call)
+ else
+ assert_equal("404", res.code, log.call)
+ end
+ end
+ req = Net::HTTP::Get.new("/webrick.cgi%20/test")
+ http.request(req, &response_assertion)
+ req = Net::HTTP::Get.new("/webrick.cgi./test")
+ http.request(req, &response_assertion)
+ resok &&= File.exist?(__FILE__+"::$DATA")
+ req = Net::HTTP::Get.new("/webrick.cgi::$DATA/test")
+ http.request(req, &response_assertion)
+ end
+ end
+end
diff --git a/jni/ruby/test/webrick/test_htmlutils.rb b/jni/ruby/test/webrick/test_htmlutils.rb
new file mode 100644
index 0000000..1fe49ee
--- /dev/null
+++ b/jni/ruby/test/webrick/test_htmlutils.rb
@@ -0,0 +1,20 @@
+require "test/unit"
+require "webrick/htmlutils"
+
+class TestWEBrickHTMLUtils < Test::Unit::TestCase
+ include WEBrick::HTMLUtils
+
+ def test_escape
+ assert_equal("foo", escape("foo"))
+ assert_equal("foo bar", escape("foo bar"))
+ assert_equal("foo&amp;bar", escape("foo&bar"))
+ assert_equal("foo&quot;bar", escape("foo\"bar"))
+ assert_equal("foo&gt;bar", escape("foo>bar"))
+ assert_equal("foo&lt;bar", escape("foo<bar"))
+ assert_equal("\u{3053 3093 306B 3061 306F}", escape("\u{3053 3093 306B 3061 306F}"))
+ bug8425 = '[Bug #8425] [ruby-core:55052]'
+ assert_nothing_raised(ArgumentError, Encoding::CompatibilityError, bug8425) {
+ assert_equal("\u{3053 3093 306B}\xff&lt;", escape("\u{3053 3093 306B}\xff<"))
+ }
+ end
+end
diff --git a/jni/ruby/test/webrick/test_httpauth.rb b/jni/ruby/test/webrick/test_httpauth.rb
new file mode 100644
index 0000000..27c37f3
--- /dev/null
+++ b/jni/ruby/test/webrick/test_httpauth.rb
@@ -0,0 +1,198 @@
+require "test/unit"
+require "net/http"
+require "tempfile"
+require "webrick"
+require "webrick/httpauth/basicauth"
+require_relative "utils"
+
+class TestWEBrickHTTPAuth < Test::Unit::TestCase
+ def test_basic_auth
+ log_tester = lambda {|log, access_log|
+ assert_equal(1, log.length)
+ assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, log[0])
+ }
+ TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log|
+ realm = "WEBrick's realm"
+ path = "/basic_auth"
+
+ server.mount_proc(path){|req, res|
+ WEBrick::HTTPAuth.basic_auth(req, res, realm){|user, pass|
+ user == "webrick" && pass == "supersecretpassword"
+ }
+ res.body = "hoge"
+ }
+ http = Net::HTTP.new(addr, port)
+ g = Net::HTTP::Get.new(path)
+ g.basic_auth("webrick", "supersecretpassword")
+ http.request(g){|res| assert_equal("hoge", res.body, log.call)}
+ g.basic_auth("webrick", "not super")
+ http.request(g){|res| assert_not_equal("hoge", res.body, log.call)}
+ }
+ end
+
+ def test_basic_auth2
+ log_tester = lambda {|log, access_log|
+ log.reject! {|line| /\A\s*\z/ =~ line }
+ pats = [
+ /ERROR Basic WEBrick's realm: webrick: password unmatch\./,
+ /ERROR WEBrick::HTTPStatus::Unauthorized/
+ ]
+ pats.each {|pat|
+ assert(!log.grep(pat).empty?, "webrick log doesn't have expected error: #{pat.inspect}")
+ log.reject! {|line| pat =~ line }
+ }
+ assert_equal([], log)
+ }
+ TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log|
+ realm = "WEBrick's realm"
+ path = "/basic_auth2"
+
+ Tempfile.create("test_webrick_auth") {|tmpfile|
+ tmpfile.close
+ tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
+ tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
+ tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
+ tmp_pass.flush
+
+ htpasswd = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
+ users = []
+ htpasswd.each{|user, pass| users << user }
+ assert_equal(2, users.size, log.call)
+ assert(users.member?("webrick"), log.call)
+ assert(users.member?("foo"), log.call)
+
+ server.mount_proc(path){|req, res|
+ auth = WEBrick::HTTPAuth::BasicAuth.new(
+ :Realm => realm, :UserDB => htpasswd,
+ :Logger => server.logger
+ )
+ auth.authenticate(req, res)
+ res.body = "hoge"
+ }
+ http = Net::HTTP.new(addr, port)
+ g = Net::HTTP::Get.new(path)
+ g.basic_auth("webrick", "supersecretpassword")
+ http.request(g){|res| assert_equal("hoge", res.body, log.call)}
+ g.basic_auth("webrick", "not super")
+ http.request(g){|res| assert_not_equal("hoge", res.body, log.call)}
+ }
+ }
+ end
+
+ def test_basic_auth3
+ Tempfile.create("test_webrick_auth") {|tmpfile|
+ tmpfile.puts("webrick:{SHA}GJYFRpBbdchp595jlh3Bhfmgp8k=")
+ tmpfile.flush
+ assert_raise(NotImplementedError){
+ WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
+ }
+ }
+
+ Tempfile.create("test_webrick_auth") {|tmpfile|
+ tmpfile.puts("webrick:$apr1$IOVMD/..$rmnOSPXr0.wwrLPZHBQZy0")
+ tmpfile.flush
+ assert_raise(NotImplementedError){
+ WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
+ }
+ }
+ end
+
+ DIGESTRES_ = /
+ ([a-zA-Z\-]+)
+ [ \t]*(?:\r\n[ \t]*)*
+ =
+ [ \t]*(?:\r\n[ \t]*)*
+ (?:
+ "((?:[^"]+|\\[\x00-\x7F])*)" |
+ ([!\#$%&'*+\-.0-9A-Z^_`a-z|~]+)
+ )/x
+
+ def test_digest_auth
+ log_tester = lambda {|log, access_log|
+ log.reject! {|line| /\A\s*\z/ =~ line }
+ pats = [
+ /ERROR Digest WEBrick's realm: no credentials in the request\./,
+ /ERROR WEBrick::HTTPStatus::Unauthorized/,
+ /ERROR Digest WEBrick's realm: webrick: digest unmatch\./
+ ]
+ pats.each {|pat|
+ assert(!log.grep(pat).empty?, "webrick log doesn't have expected error: #{pat.inspect}")
+ log.reject! {|line| pat =~ line }
+ }
+ assert_equal([], log)
+ }
+ TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log|
+ realm = "WEBrick's realm"
+ path = "/digest_auth"
+
+ Tempfile.create("test_webrick_auth") {|tmpfile|
+ tmpfile.close
+ tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path)
+ tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
+ tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
+ tmp_pass.flush
+
+ htdigest = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path)
+ users = []
+ htdigest.each{|user, pass| users << user }
+ assert_equal(2, users.size, log.call)
+ assert(users.member?("webrick"), log.call)
+ assert(users.member?("foo"), log.call)
+
+ auth = WEBrick::HTTPAuth::DigestAuth.new(
+ :Realm => realm, :UserDB => htdigest,
+ :Algorithm => 'MD5',
+ :Logger => server.logger
+ )
+ server.mount_proc(path){|req, res|
+ auth.authenticate(req, res)
+ res.body = "hoge"
+ }
+
+ Net::HTTP.start(addr, port) do |http|
+ g = Net::HTTP::Get.new(path)
+ params = {}
+ http.request(g) do |res|
+ assert_equal('401', res.code, log.call)
+ res["www-authenticate"].scan(DIGESTRES_) do |key, quoted, token|
+ params[key.downcase] = token || quoted.delete('\\')
+ end
+ params['uri'] = "http://#{addr}:#{port}#{path}"
+ end
+
+ g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params)
+ http.request(g){|res| assert_equal("hoge", res.body, log.call)}
+
+ params['algorithm'].downcase! #4936
+ g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params)
+ http.request(g){|res| assert_equal("hoge", res.body, log.call)}
+
+ g['Authorization'] = credentials_for_request('webrick', "not super", params)
+ http.request(g){|res| assert_not_equal("hoge", res.body, log.call)}
+ end
+ }
+ }
+ end
+
+ private
+ def credentials_for_request(user, password, params)
+ cnonce = "hoge"
+ nonce_count = 1
+ ha1 = "#{user}:#{params['realm']}:#{password}"
+ ha2 = "GET:#{params['uri']}"
+ request_digest =
+ "#{Digest::MD5.hexdigest(ha1)}:" \
+ "#{params['nonce']}:#{'%08x' % nonce_count}:#{cnonce}:#{params['qop']}:" \
+ "#{Digest::MD5.hexdigest(ha2)}"
+ "Digest username=\"#{user}\"" \
+ ", realm=\"#{params['realm']}\"" \
+ ", nonce=\"#{params['nonce']}\"" \
+ ", uri=\"#{params['uri']}\"" \
+ ", qop=#{params['qop']}" \
+ ", nc=#{'%08x' % nonce_count}" \
+ ", cnonce=\"#{cnonce}\"" \
+ ", response=\"#{Digest::MD5.hexdigest(request_digest)}\"" \
+ ", opaque=\"#{params['opaque']}\"" \
+ ", algorithm=#{params['algorithm']}"
+ end
+end
diff --git a/jni/ruby/test/webrick/test_httpproxy.rb b/jni/ruby/test/webrick/test_httpproxy.rb
new file mode 100644
index 0000000..501bb4c
--- /dev/null
+++ b/jni/ruby/test/webrick/test_httpproxy.rb
@@ -0,0 +1,284 @@
+require "test/unit"
+require "net/http"
+require "webrick"
+require "webrick/httpproxy"
+begin
+ require "webrick/ssl"
+ require "net/https"
+ require File.expand_path("../openssl/utils.rb", File.dirname(__FILE__))
+rescue LoadError
+ # test_connect will be skipped
+end
+require File.expand_path("utils.rb", File.dirname(__FILE__))
+
+class TestWEBrickHTTPProxy < Test::Unit::TestCase
+ def test_fake_proxy
+ assert_nil(WEBrick::FakeProxyURI.scheme)
+ assert_nil(WEBrick::FakeProxyURI.host)
+ assert_nil(WEBrick::FakeProxyURI.port)
+ assert_nil(WEBrick::FakeProxyURI.path)
+ assert_nil(WEBrick::FakeProxyURI.userinfo)
+ assert_raise(NoMethodError){ WEBrick::FakeProxyURI.foo }
+ end
+
+ def test_proxy
+ # Testing GET or POST to the proxy server
+ # Note that the proxy server works as the origin server.
+ # +------+
+ # V |
+ # client -------> proxy ---+
+ # GET / POST GET / POST
+ #
+ proxy_handler_called = request_handler_called = 0
+ config = {
+ :ServerName => "localhost.localdomain",
+ :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1 },
+ :RequestCallback => Proc.new{|req, res| request_handler_called += 1 }
+ }
+ TestWEBrick.start_httpproxy(config){|server, addr, port, log|
+ server.mount_proc("/"){|req, res|
+ res.body = "#{req.request_method} #{req.path} #{req.body}"
+ }
+ http = Net::HTTP.new(addr, port, addr, port)
+
+ req = Net::HTTP::Get.new("/")
+ http.request(req){|res|
+ assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call)
+ assert_equal("GET / ", res.body, log.call)
+ }
+ assert_equal(1, proxy_handler_called, log.call)
+ assert_equal(2, request_handler_called, log.call)
+
+ req = Net::HTTP::Head.new("/")
+ http.request(req){|res|
+ assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call)
+ assert_nil(res.body, log.call)
+ }
+ assert_equal(2, proxy_handler_called, log.call)
+ assert_equal(4, request_handler_called, log.call)
+
+ req = Net::HTTP::Post.new("/")
+ req.body = "post-data"
+ http.request(req){|res|
+ assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call)
+ assert_equal("POST / post-data", res.body, log.call)
+ }
+ assert_equal(3, proxy_handler_called, log.call)
+ assert_equal(6, request_handler_called, log.call)
+ }
+ end
+
+ def test_no_proxy
+ # Testing GET or POST to the proxy server without proxy request.
+ #
+ # client -------> proxy
+ # GET / POST
+ #
+ proxy_handler_called = request_handler_called = 0
+ config = {
+ :ServerName => "localhost.localdomain",
+ :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1 },
+ :RequestCallback => Proc.new{|req, res| request_handler_called += 1 }
+ }
+ TestWEBrick.start_httpproxy(config){|server, addr, port, log|
+ server.mount_proc("/"){|req, res|
+ res.body = "#{req.request_method} #{req.path} #{req.body}"
+ }
+ http = Net::HTTP.new(addr, port)
+
+ req = Net::HTTP::Get.new("/")
+ http.request(req){|res|
+ assert_nil(res["via"], log.call)
+ assert_equal("GET / ", res.body, log.call)
+ }
+ assert_equal(0, proxy_handler_called, log.call)
+ assert_equal(1, request_handler_called, log.call)
+
+ req = Net::HTTP::Head.new("/")
+ http.request(req){|res|
+ assert_nil(res["via"], log.call)
+ assert_nil(res.body, log.call)
+ }
+ assert_equal(0, proxy_handler_called, log.call)
+ assert_equal(2, request_handler_called, log.call)
+
+ req = Net::HTTP::Post.new("/")
+ req.body = "post-data"
+ http.request(req){|res|
+ assert_nil(res["via"], log.call)
+ assert_equal("POST / post-data", res.body, log.call)
+ }
+ assert_equal(0, proxy_handler_called, log.call)
+ assert_equal(3, request_handler_called, log.call)
+ }
+ end
+
+ def make_certificate(key, cn)
+ subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=#{cn}")
+ exts = [
+ ["keyUsage", "keyEncipherment,digitalSignature", true],
+ ]
+ cert = OpenSSL::TestUtils.issue_cert(
+ subject, key, 1, Time.now, Time.now + 3600, exts,
+ nil, nil, OpenSSL::Digest::SHA1.new
+ )
+ return cert
+ end if defined?(OpenSSL::TestUtils)
+
+ def test_connect
+ # Testing CONNECT to proxy server
+ #
+ # client -----------> proxy -----------> https
+ # 1. CONNECT establish TCP
+ # 2. ---- establish SSL session --->
+ # 3. ------- GET or POST ---------->
+ #
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ cert = make_certificate(key, "127.0.0.1")
+ s_config = {
+ :SSLEnable =>true,
+ :ServerName => "localhost",
+ :SSLCertificate => cert,
+ :SSLPrivateKey => key,
+ }
+ config = {
+ :ServerName => "localhost.localdomain",
+ :RequestCallback => Proc.new{|req, res|
+ assert_equal("CONNECT", req.request_method)
+ },
+ }
+ TestWEBrick.start_httpserver(s_config){|s_server, s_addr, s_port, s_log|
+ s_server.mount_proc("/"){|req, res|
+ res.body = "SSL #{req.request_method} #{req.path} #{req.body}"
+ }
+ TestWEBrick.start_httpproxy(config){|server, addr, port, log|
+ http = Net::HTTP.new("127.0.0.1", s_port, addr, port)
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == cert.to_der
+ end
+
+ req = Net::HTTP::Get.new("/")
+ req["Content-Type"] = "application/x-www-form-urlencoded"
+ http.request(req){|res|
+ assert_equal("SSL GET / ", res.body, s_log.call + log.call)
+ }
+
+ req = Net::HTTP::Post.new("/")
+ req["Content-Type"] = "application/x-www-form-urlencoded"
+ req.body = "post-data"
+ http.request(req){|res|
+ assert_equal("SSL POST / post-data", res.body, s_log.call + log.call)
+ }
+ }
+ }
+ end if defined?(OpenSSL::TestUtils)
+
+ def test_upstream_proxy
+ # Testing GET or POST through the upstream proxy server
+ # Note that the upstream proxy server works as the origin server.
+ # +------+
+ # V |
+ # client -------> proxy -------> proxy ---+
+ # GET / POST GET / POST GET / POST
+ #
+ up_proxy_handler_called = up_request_handler_called = 0
+ proxy_handler_called = request_handler_called = 0
+ up_config = {
+ :ServerName => "localhost.localdomain",
+ :ProxyContentHandler => Proc.new{|req, res| up_proxy_handler_called += 1},
+ :RequestCallback => Proc.new{|req, res| up_request_handler_called += 1}
+ }
+ TestWEBrick.start_httpproxy(up_config){|up_server, up_addr, up_port, up_log|
+ up_server.mount_proc("/"){|req, res|
+ res.body = "#{req.request_method} #{req.path} #{req.body}"
+ }
+ config = {
+ :ServerName => "localhost.localdomain",
+ :ProxyURI => URI.parse("http://localhost:#{up_port}"),
+ :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1},
+ :RequestCallback => Proc.new{|req, res| request_handler_called += 1},
+ }
+ TestWEBrick.start_httpproxy(config){|server, addr, port, log|
+ http = Net::HTTP.new(up_addr, up_port, addr, port)
+
+ req = Net::HTTP::Get.new("/")
+ http.request(req){|res|
+ skip res.message unless res.code == '200'
+ via = res["via"].split(/,\s+/)
+ assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call)
+ assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call)
+ assert_equal("GET / ", res.body)
+ }
+ assert_equal(1, up_proxy_handler_called, up_log.call + log.call)
+ assert_equal(2, up_request_handler_called, up_log.call + log.call)
+ assert_equal(1, proxy_handler_called, up_log.call + log.call)
+ assert_equal(1, request_handler_called, up_log.call + log.call)
+
+ req = Net::HTTP::Head.new("/")
+ http.request(req){|res|
+ via = res["via"].split(/,\s+/)
+ assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call)
+ assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call)
+ assert_nil(res.body, up_log.call + log.call)
+ }
+ assert_equal(2, up_proxy_handler_called, up_log.call + log.call)
+ assert_equal(4, up_request_handler_called, up_log.call + log.call)
+ assert_equal(2, proxy_handler_called, up_log.call + log.call)
+ assert_equal(2, request_handler_called, up_log.call + log.call)
+
+ req = Net::HTTP::Post.new("/")
+ req.body = "post-data"
+ http.request(req){|res|
+ via = res["via"].split(/,\s+/)
+ assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call)
+ assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call)
+ assert_equal("POST / post-data", res.body, up_log.call + log.call)
+ }
+ assert_equal(3, up_proxy_handler_called, up_log.call + log.call)
+ assert_equal(6, up_request_handler_called, up_log.call + log.call)
+ assert_equal(3, proxy_handler_called, up_log.call + log.call)
+ assert_equal(3, request_handler_called, up_log.call + log.call)
+
+ if defined?(OpenSSL::TestUtils)
+ # Testing CONNECT to the upstream proxy server
+ #
+ # client -------> proxy -------> proxy -------> https
+ # 1. CONNECT CONNECT establish TCP
+ # 2. -------- establish SSL session ------>
+ # 3. ---------- GET or POST -------------->
+ #
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ cert = make_certificate(key, "127.0.0.1")
+ s_config = {
+ :SSLEnable =>true,
+ :ServerName => "localhost",
+ :SSLCertificate => cert,
+ :SSLPrivateKey => key,
+ }
+ TestWEBrick.start_httpserver(s_config){|s_server, s_addr, s_port, s_log|
+ s_server.mount_proc("/"){|req2, res|
+ res.body = "SSL #{req2.request_method} #{req2.path} #{req2.body}"
+ }
+ http = Net::HTTP.new("127.0.0.1", s_port, addr, port, up_log.call + log.call + s_log.call)
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == cert.to_der
+ end
+
+ req2 = Net::HTTP::Get.new("/")
+ http.request(req2){|res|
+ assert_equal("SSL GET / ", res.body, up_log.call + log.call + s_log.call)
+ }
+
+ req2 = Net::HTTP::Post.new("/")
+ req2.body = "post-data"
+ http.request(req2){|res|
+ assert_equal("SSL POST / post-data", res.body, up_log.call + log.call + s_log.call)
+ }
+ }
+ end
+ }
+ }
+ end
+end
diff --git a/jni/ruby/test/webrick/test_httprequest.rb b/jni/ruby/test/webrick/test_httprequest.rb
new file mode 100644
index 0000000..3a7e675
--- /dev/null
+++ b/jni/ruby/test/webrick/test_httprequest.rb
@@ -0,0 +1,411 @@
+require "webrick"
+require "stringio"
+require "test/unit"
+
+class TestWEBrickHTTPRequest < Test::Unit::TestCase
+ def test_simple_request
+ msg = <<-_end_of_message_
+GET /
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ assert(req.meta_vars) # fails if @header was not initialized and iteration is attempted on the nil reference
+ end
+
+ def test_parse_09
+ msg = <<-_end_of_message_
+ GET /
+ foobar # HTTP/0.9 request don't have header nor entity body.
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal("GET", req.request_method)
+ assert_equal("/", req.unparsed_uri)
+ assert_equal(WEBrick::HTTPVersion.new("0.9"), req.http_version)
+ assert_equal(WEBrick::Config::HTTP[:ServerName], req.host)
+ assert_equal(80, req.port)
+ assert_equal(false, req.keep_alive?)
+ assert_equal(nil, req.body)
+ assert(req.query.empty?)
+ end
+
+ def test_parse_10
+ msg = <<-_end_of_message_
+ GET / HTTP/1.0
+
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal("GET", req.request_method)
+ assert_equal("/", req.unparsed_uri)
+ assert_equal(WEBrick::HTTPVersion.new("1.0"), req.http_version)
+ assert_equal(WEBrick::Config::HTTP[:ServerName], req.host)
+ assert_equal(80, req.port)
+ assert_equal(false, req.keep_alive?)
+ assert_equal(nil, req.body)
+ assert(req.query.empty?)
+ end
+
+ def test_parse_11
+ msg = <<-_end_of_message_
+ GET /path HTTP/1.1
+
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal("GET", req.request_method)
+ assert_equal("/path", req.unparsed_uri)
+ assert_equal("", req.script_name)
+ assert_equal("/path", req.path_info)
+ assert_equal(WEBrick::HTTPVersion.new("1.1"), req.http_version)
+ assert_equal(WEBrick::Config::HTTP[:ServerName], req.host)
+ assert_equal(80, req.port)
+ assert_equal(true, req.keep_alive?)
+ assert_equal(nil, req.body)
+ assert(req.query.empty?)
+ end
+
+ def test_request_uri_too_large
+ msg = <<-_end_of_message_
+ GET /#{"a"*2084} HTTP/1.1
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ assert_raise(WEBrick::HTTPStatus::RequestURITooLarge){
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ }
+ end
+
+ def test_parse_headers
+ msg = <<-_end_of_message_
+ GET /path HTTP/1.1
+ Host: test.ruby-lang.org:8080
+ Connection: close
+ Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1,
+ text/html;level=2;q=0.4, */*;q=0.5
+ Accept-Encoding: compress;q=0.5
+ Accept-Encoding: gzip;q=1.0, identity; q=0.4, *;q=0
+ Accept-Language: en;q=0.5, *; q=0
+ Accept-Language: ja
+ Content-Type: text/plain
+ Content-Length: 7
+ X-Empty-Header:
+
+ foobar
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal(
+ URI.parse("http://test.ruby-lang.org:8080/path"), req.request_uri)
+ assert_equal("test.ruby-lang.org", req.host)
+ assert_equal(8080, req.port)
+ assert_equal(false, req.keep_alive?)
+ assert_equal(
+ %w(text/html;level=1 text/html */* text/html;level=2 text/*),
+ req.accept)
+ assert_equal(%w(gzip compress identity *), req.accept_encoding)
+ assert_equal(%w(ja en *), req.accept_language)
+ assert_equal(7, req.content_length)
+ assert_equal("text/plain", req.content_type)
+ assert_equal("foobar\n", req.body)
+ assert_equal("", req["x-empty-header"])
+ assert_equal(nil, req["x-no-header"])
+ assert(req.query.empty?)
+ end
+
+ def test_parse_header2()
+ msg = <<-_end_of_message_
+ POST /foo/bar/../baz?q=a HTTP/1.0
+ Content-Length: 9
+ User-Agent:
+ FOO BAR
+ BAZ
+
+ hogehoge
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal("POST", req.request_method)
+ assert_equal("/foo/baz", req.path)
+ assert_equal("", req.script_name)
+ assert_equal("/foo/baz", req.path_info)
+ assert_equal("9", req['content-length'])
+ assert_equal("FOO BAR BAZ", req['user-agent'])
+ assert_equal("hogehoge\n", req.body)
+ end
+
+ def test_parse_headers3
+ msg = <<-_end_of_message_
+ GET /path HTTP/1.1
+ Host: test.ruby-lang.org
+
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal(URI.parse("http://test.ruby-lang.org/path"), req.request_uri)
+ assert_equal("test.ruby-lang.org", req.host)
+ assert_equal(80, req.port)
+
+ msg = <<-_end_of_message_
+ GET /path HTTP/1.1
+ Host: 192.168.1.1
+
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal(URI.parse("http://192.168.1.1/path"), req.request_uri)
+ assert_equal("192.168.1.1", req.host)
+ assert_equal(80, req.port)
+
+ msg = <<-_end_of_message_
+ GET /path HTTP/1.1
+ Host: [fe80::208:dff:feef:98c7]
+
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal(URI.parse("http://[fe80::208:dff:feef:98c7]/path"),
+ req.request_uri)
+ assert_equal("[fe80::208:dff:feef:98c7]", req.host)
+ assert_equal(80, req.port)
+
+ msg = <<-_end_of_message_
+ GET /path HTTP/1.1
+ Host: 192.168.1.1:8080
+
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal(URI.parse("http://192.168.1.1:8080/path"), req.request_uri)
+ assert_equal("192.168.1.1", req.host)
+ assert_equal(8080, req.port)
+
+ msg = <<-_end_of_message_
+ GET /path HTTP/1.1
+ Host: [fe80::208:dff:feef:98c7]:8080
+
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ assert_equal(URI.parse("http://[fe80::208:dff:feef:98c7]:8080/path"),
+ req.request_uri)
+ assert_equal("[fe80::208:dff:feef:98c7]", req.host)
+ assert_equal(8080, req.port)
+ end
+
+ def test_parse_get_params
+ param = "foo=1;foo=2;foo=3;bar=x"
+ msg = <<-_end_of_message_
+ GET /path?#{param} HTTP/1.1
+ Host: test.ruby-lang.org:8080
+
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ query = req.query
+ assert_equal("1", query["foo"])
+ assert_equal(["1", "2", "3"], query["foo"].to_ary)
+ assert_equal(["1", "2", "3"], query["foo"].list)
+ assert_equal("x", query["bar"])
+ assert_equal(["x"], query["bar"].list)
+ end
+
+ def test_parse_post_params
+ param = "foo=1;foo=2;foo=3;bar=x"
+ msg = <<-_end_of_message_
+ POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1
+ Host: test.ruby-lang.org:8080
+ Content-Length: #{param.size}
+ Content-Type: application/x-www-form-urlencoded
+
+ #{param}
+ _end_of_message_
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ query = req.query
+ assert_equal("1", query["foo"])
+ assert_equal(["1", "2", "3"], query["foo"].to_ary)
+ assert_equal(["1", "2", "3"], query["foo"].list)
+ assert_equal("x", query["bar"])
+ assert_equal(["x"], query["bar"].list)
+ end
+
+ def test_chunked
+ crlf = "\x0d\x0a"
+ msg = <<-_end_of_message_
+ POST /path HTTP/1.1
+ Host: test.ruby-lang.org:8080
+ Transfer-Encoding: chunked
+
+ _end_of_message_
+ msg.gsub!(/^ {6}/, "")
+ open(__FILE__){|io|
+ while chunk = io.read(100)
+ msg << chunk.size.to_s(16) << crlf
+ msg << chunk << crlf
+ end
+ }
+ msg << "0" << crlf
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ assert_equal(File.read(__FILE__), req.body)
+ end
+
+ def test_forwarded
+ msg = <<-_end_of_message_
+ GET /foo HTTP/1.1
+ Host: localhost:10080
+ User-Agent: w3m/0.5.2
+ X-Forwarded-For: 123.123.123.123
+ X-Forwarded-Host: forward.example.com
+ X-Forwarded-Server: server.example.com
+ Connection: Keep-Alive
+
+ _end_of_message_
+ msg.gsub!(/^ {6}/, "")
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ assert_equal("server.example.com", req.server_name)
+ assert_equal("http://forward.example.com/foo", req.request_uri.to_s)
+ assert_equal("forward.example.com", req.host)
+ assert_equal(80, req.port)
+ assert_equal("123.123.123.123", req.remote_ip)
+ assert(!req.ssl?)
+
+ msg = <<-_end_of_message_
+ GET /foo HTTP/1.1
+ Host: localhost:10080
+ User-Agent: w3m/0.5.2
+ X-Forwarded-For: 192.168.1.10, 172.16.1.1, 123.123.123.123
+ X-Forwarded-Host: forward.example.com:8080
+ X-Forwarded-Server: server.example.com
+ Connection: Keep-Alive
+
+ _end_of_message_
+ msg.gsub!(/^ {6}/, "")
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ assert_equal("server.example.com", req.server_name)
+ assert_equal("http://forward.example.com:8080/foo", req.request_uri.to_s)
+ assert_equal("forward.example.com", req.host)
+ assert_equal(8080, req.port)
+ assert_equal("123.123.123.123", req.remote_ip)
+ assert(!req.ssl?)
+
+ msg = <<-_end_of_message_
+ GET /foo HTTP/1.1
+ Host: localhost:10080
+ Client-IP: 234.234.234.234
+ X-Forwarded-Proto: https
+ X-Forwarded-For: 192.168.1.10, 10.0.0.1, 123.123.123.123
+ X-Forwarded-Host: forward.example.com
+ X-Forwarded-Server: server.example.com
+ X-Requested-With: XMLHttpRequest
+ Connection: Keep-Alive
+
+ _end_of_message_
+ msg.gsub!(/^ {6}/, "")
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ assert_equal("server.example.com", req.server_name)
+ assert_equal("https://forward.example.com/foo", req.request_uri.to_s)
+ assert_equal("forward.example.com", req.host)
+ assert_equal(443, req.port)
+ assert_equal("234.234.234.234", req.remote_ip)
+ assert(req.ssl?)
+
+ msg = <<-_end_of_message_
+ GET /foo HTTP/1.1
+ Host: localhost:10080
+ Client-IP: 234.234.234.234
+ X-Forwarded-Proto: https
+ X-Forwarded-For: 192.168.1.10
+ X-Forwarded-Host: forward1.example.com:1234, forward2.example.com:5678
+ X-Forwarded-Server: server1.example.com, server2.example.com
+ X-Requested-With: XMLHttpRequest
+ Connection: Keep-Alive
+
+ _end_of_message_
+ msg.gsub!(/^ {6}/, "")
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ assert_equal("server1.example.com", req.server_name)
+ assert_equal("https://forward1.example.com:1234/foo", req.request_uri.to_s)
+ assert_equal("forward1.example.com", req.host)
+ assert_equal(1234, req.port)
+ assert_equal("234.234.234.234", req.remote_ip)
+ assert(req.ssl?)
+ end
+
+ def test_continue_sent
+ msg = <<-_end_of_message_
+ POST /path HTTP/1.1
+ Expect: 100-continue
+
+ _end_of_message_
+ msg.gsub!(/^ {6}/, "")
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ assert req['expect']
+ l = msg.size
+ req.continue
+ assert_not_equal l, msg.size
+ assert_match(/HTTP\/1.1 100 continue\r\n\r\n\z/, msg)
+ assert !req['expect']
+ end
+
+ def test_continue_not_sent
+ msg = <<-_end_of_message_
+ POST /path HTTP/1.1
+
+ _end_of_message_
+ msg.gsub!(/^ {6}/, "")
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg))
+ assert !req['expect']
+ l = msg.size
+ req.continue
+ assert_equal l, msg.size
+ end
+
+ def test_bad_messages
+ param = "foo=1;foo=2;foo=3;bar=x"
+ msg = <<-_end_of_message_
+ POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1
+ Host: test.ruby-lang.org:8080
+ Content-Type: application/x-www-form-urlencoded
+
+ #{param}
+ _end_of_message_
+ assert_raise(WEBrick::HTTPStatus::LengthRequired){
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ req.body
+ }
+
+ msg = <<-_end_of_message_
+ POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1
+ Host: test.ruby-lang.org:8080
+ Content-Length: 100000
+
+ body is too short.
+ _end_of_message_
+ assert_raise(WEBrick::HTTPStatus::BadRequest){
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ req.body
+ }
+
+ msg = <<-_end_of_message_
+ POST /path?foo=x;foo=y;foo=z;bar=1 HTTP/1.1
+ Host: test.ruby-lang.org:8080
+ Transfer-Encoding: foobar
+
+ body is too short.
+ _end_of_message_
+ assert_raise(WEBrick::HTTPStatus::NotImplemented){
+ req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+ req.parse(StringIO.new(msg.gsub(/^ {6}/, "")))
+ req.body
+ }
+ end
+end
diff --git a/jni/ruby/test/webrick/test_httpresponse.rb b/jni/ruby/test/webrick/test_httpresponse.rb
new file mode 100644
index 0000000..c916ed5
--- /dev/null
+++ b/jni/ruby/test/webrick/test_httpresponse.rb
@@ -0,0 +1,149 @@
+require "webrick"
+require "minitest/autorun"
+require "stringio"
+
+module WEBrick
+ class TestHTTPResponse < MiniTest::Unit::TestCase
+ class FakeLogger
+ attr_reader :messages
+
+ def initialize
+ @messages = []
+ end
+
+ def warn msg
+ @messages << msg
+ end
+ end
+
+ attr_reader :config, :logger, :res
+
+ def setup
+ super
+ @logger = FakeLogger.new
+ @config = Config::HTTP
+ @config[:Logger] = logger
+ @res = HTTPResponse.new config
+ @res.keep_alive = true
+ end
+
+ def test_304_does_not_log_warning
+ res.status = 304
+ res.setup_header
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_204_does_not_log_warning
+ res.status = 204
+ res.setup_header
+
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_1xx_does_not_log_warnings
+ res.status = 105
+ res.setup_header
+
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_send_body_io
+ IO.pipe {|body_r, body_w|
+ body_w.write 'hello'
+ body_w.close
+
+ @res.body = body_r
+
+ IO.pipe {|r, w|
+
+ @res.send_body w
+
+ w.close
+
+ assert_equal 'hello', r.read
+ }
+ }
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_send_body_string
+ @res.body = 'hello'
+
+ IO.pipe {|r, w|
+ @res.send_body w
+
+ w.close
+
+ assert_equal 'hello', r.read
+ }
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_send_body_string_io
+ @res.body = StringIO.new 'hello'
+
+ IO.pipe {|r, w|
+ @res.send_body w
+
+ w.close
+
+ assert_equal 'hello', r.read
+ }
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_send_body_io_chunked
+ @res.chunked = true
+
+ IO.pipe {|body_r, body_w|
+
+ body_w.write 'hello'
+ body_w.close
+
+ @res.body = body_r
+
+ IO.pipe {|r, w|
+ @res.send_body w
+
+ w.close
+
+ r.binmode
+ assert_equal "5\r\nhello\r\n0\r\n\r\n", r.read
+ }
+ }
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_send_body_string_chunked
+ @res.chunked = true
+
+ @res.body = 'hello'
+
+ IO.pipe {|r, w|
+ @res.send_body w
+
+ w.close
+
+ r.binmode
+ assert_equal "5\r\nhello\r\n0\r\n\r\n", r.read
+ }
+ assert_equal 0, logger.messages.length
+ end
+
+ def test_send_body_string_io_chunked
+ @res.chunked = true
+
+ @res.body = StringIO.new 'hello'
+
+ IO.pipe {|r, w|
+ @res.send_body w
+
+ w.close
+
+ r.binmode
+ assert_equal "5\r\nhello\r\n0\r\n\r\n", r.read
+ }
+ assert_equal 0, logger.messages.length
+ end
+ end
+end
diff --git a/jni/ruby/test/webrick/test_httpserver.rb b/jni/ruby/test/webrick/test_httpserver.rb
new file mode 100644
index 0000000..5cd4ba5
--- /dev/null
+++ b/jni/ruby/test/webrick/test_httpserver.rb
@@ -0,0 +1,412 @@
+require "test/unit"
+require "net/http"
+require "webrick"
+require_relative "utils"
+
+class TestWEBrickHTTPServer < Test::Unit::TestCase
+ empty_log = Object.new
+ def empty_log.<<(str)
+ assert_equal('', str)
+ self
+ end
+ NoLog = WEBrick::Log.new(empty_log, WEBrick::BasicLog::WARN)
+
+ def test_mount
+ httpd = WEBrick::HTTPServer.new(
+ :Logger => NoLog,
+ :DoNotListen=>true
+ )
+ httpd.mount("/", :Root)
+ httpd.mount("/foo", :Foo)
+ httpd.mount("/foo/bar", :Bar, :bar1)
+ httpd.mount("/foo/bar/baz", :Baz, :baz1, :baz2)
+
+ serv, opts, script_name, path_info = httpd.search_servlet("/")
+ assert_equal(:Root, serv)
+ assert_equal([], opts)
+ assert_equal("", script_name)
+ assert_equal("/", path_info)
+
+ serv, opts, script_name, path_info = httpd.search_servlet("/sub")
+ assert_equal(:Root, serv)
+ assert_equal([], opts)
+ assert_equal("", script_name)
+ assert_equal("/sub", path_info)
+
+ serv, opts, script_name, path_info = httpd.search_servlet("/sub/")
+ assert_equal(:Root, serv)
+ assert_equal([], opts)
+ assert_equal("", script_name)
+ assert_equal("/sub/", path_info)
+
+ serv, opts, script_name, path_info = httpd.search_servlet("/foo")
+ assert_equal(:Foo, serv)
+ assert_equal([], opts)
+ assert_equal("/foo", script_name)
+ assert_equal("", path_info)
+
+ serv, opts, script_name, path_info = httpd.search_servlet("/foo/")
+ assert_equal(:Foo, serv)
+ assert_equal([], opts)
+ assert_equal("/foo", script_name)
+ assert_equal("/", path_info)
+
+ serv, opts, script_name, path_info = httpd.search_servlet("/foo/sub")
+ assert_equal(:Foo, serv)
+ assert_equal([], opts)
+ assert_equal("/foo", script_name)
+ assert_equal("/sub", path_info)
+
+ serv, opts, script_name, path_info = httpd.search_servlet("/foo/bar")
+ assert_equal(:Bar, serv)
+ assert_equal([:bar1], opts)
+ assert_equal("/foo/bar", script_name)
+ assert_equal("", path_info)
+
+ serv, opts, script_name, path_info = httpd.search_servlet("/foo/bar/baz")
+ assert_equal(:Baz, serv)
+ assert_equal([:baz1, :baz2], opts)
+ assert_equal("/foo/bar/baz", script_name)
+ assert_equal("", path_info)
+ end
+
+ class Req
+ attr_reader :port, :host
+ def initialize(addr, port, host)
+ @addr, @port, @host = addr, port, host
+ end
+ def addr
+ [0,0,0,@addr]
+ end
+ end
+
+ def httpd(addr, port, host, ali)
+ config ={
+ :Logger => NoLog,
+ :DoNotListen => true,
+ :BindAddress => addr,
+ :Port => port,
+ :ServerName => host,
+ :ServerAlias => ali,
+ }
+ return WEBrick::HTTPServer.new(config)
+ end
+
+ def assert_eql?(v1, v2)
+ assert_equal(v1.object_id, v2.object_id)
+ end
+
+ def test_lookup_server
+ addr1 = "192.168.100.1"
+ addr2 = "192.168.100.2"
+ addrz = "192.168.100.254"
+ local = "127.0.0.1"
+ port1 = 80
+ port2 = 8080
+ port3 = 10080
+ portz = 32767
+ name1 = "www.example.com"
+ name2 = "www2.example.com"
+ name3 = "www3.example.com"
+ namea = "www.example.co.jp"
+ nameb = "www.example.jp"
+ namec = "www2.example.co.jp"
+ named = "www2.example.jp"
+ namez = "foobar.example.com"
+ alias1 = [namea, nameb]
+ alias2 = [namec, named]
+
+ host1 = httpd(nil, port1, name1, nil)
+ hosts = [
+ host2 = httpd(addr1, port1, name1, nil),
+ host3 = httpd(addr1, port1, name2, alias1),
+ host4 = httpd(addr1, port2, name1, nil),
+ host5 = httpd(addr1, port2, name2, alias1),
+ httpd(addr1, port2, name3, alias2),
+ host7 = httpd(addr2, nil, name1, nil),
+ host8 = httpd(addr2, nil, name2, alias1),
+ httpd(addr2, nil, name3, alias2),
+ host10 = httpd(local, nil, nil, nil),
+ host11 = httpd(nil, port3, nil, nil),
+ ].sort_by{ rand }
+ hosts.each{|h| host1.virtual_host(h) }
+
+ # connect to addr1
+ assert_eql?(host2, host1.lookup_server(Req.new(addr1, port1, name1)))
+ assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, name2)))
+ assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, namea)))
+ assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr1, port1, namez)))
+ assert_eql?(host4, host1.lookup_server(Req.new(addr1, port2, name1)))
+ assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, name2)))
+ assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, namea)))
+ assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr1, port2, namez)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, name1)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, name2)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, namea)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, nameb)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, namez)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, name1)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, name2)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, namea)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, namez)))
+
+ # connect to addr2
+ assert_eql?(host7, host1.lookup_server(Req.new(addr2, port1, name1)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, name2)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, namea)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr2, port1, namez)))
+ assert_eql?(host7, host1.lookup_server(Req.new(addr2, port2, name1)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, name2)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, namea)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr2, port2, namez)))
+ assert_eql?(host7, host1.lookup_server(Req.new(addr2, port3, name1)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, name2)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, namea)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, nameb)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addr2, port3, namez)))
+ assert_eql?(host7, host1.lookup_server(Req.new(addr2, portz, name1)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, name2)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, namea)))
+ assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addr2, portz, namez)))
+
+ # connect to addrz
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, name1)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, name2)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, namea)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, namez)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, name1)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, name2)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, namea)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, namez)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name1)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name2)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namea)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, nameb)))
+ assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namez)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, name1)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, name2)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, namea)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, nameb)))
+ assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, namez)))
+
+ # connect to localhost
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name1)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name2)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namea)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port1, nameb)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namez)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name1)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name2)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namea)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port2, nameb)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namez)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name1)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name2)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namea)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port3, nameb)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namez)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name1)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name2)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namea)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, portz, nameb)))
+ assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namez)))
+ end
+
+ def test_callbacks
+ accepted = started = stopped = 0
+ requested0 = requested1 = 0
+ config = {
+ :ServerName => "localhost",
+ :AcceptCallback => Proc.new{ accepted += 1 },
+ :StartCallback => Proc.new{ started += 1 },
+ :StopCallback => Proc.new{ stopped += 1 },
+ :RequestCallback => Proc.new{|req, res| requested0 += 1 },
+ }
+ log_tester = lambda {|log, access_log|
+ assert(log.find {|s| %r{ERROR `/' not found\.} =~ s })
+ assert_equal([], log.reject {|s| %r{ERROR `/' not found\.} =~ s })
+ }
+ TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
+ vhost_config = {
+ :ServerName => "myhostname",
+ :BindAddress => addr,
+ :Port => port,
+ :DoNotListen => true,
+ :Logger => NoLog,
+ :AccessLog => [],
+ :RequestCallback => Proc.new{|req, res| requested1 += 1 },
+ }
+ server.virtual_host(WEBrick::HTTPServer.new(vhost_config))
+
+ Thread.pass while server.status != :Running
+ assert_equal(1, started, log.call)
+ assert_equal(0, stopped, log.call)
+ assert_equal(0, accepted, log.call)
+
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/")
+ req["Host"] = "myhostname:#{port}"
+ http.request(req){|res| assert_equal("404", res.code, log.call)}
+ http.request(req){|res| assert_equal("404", res.code, log.call)}
+ http.request(req){|res| assert_equal("404", res.code, log.call)}
+ req["Host"] = "localhost:#{port}"
+ http.request(req){|res| assert_equal("404", res.code, log.call)}
+ http.request(req){|res| assert_equal("404", res.code, log.call)}
+ http.request(req){|res| assert_equal("404", res.code, log.call)}
+ assert_equal(6, accepted, log.call)
+ assert_equal(3, requested0, log.call)
+ assert_equal(3, requested1, log.call)
+ }
+ assert_equal(started, 1)
+ assert_equal(stopped, 1)
+ end
+
+ # This class is needed by test_response_io_with_chunked_set method
+ class EventManagerForChunkedResponseTest
+ def initialize
+ @listeners = []
+ end
+ def add_listener( &block )
+ @listeners << block
+ end
+ def raise_str_event( str )
+ @listeners.each{ |e| e.call( :str, str ) }
+ end
+ def raise_close_event()
+ @listeners.each{ |e| e.call( :cls ) }
+ end
+ end
+ def test_response_io_with_chunked_set
+ evt_man = EventManagerForChunkedResponseTest.new
+ t = Thread.new do
+ begin
+ config = {
+ :ServerName => "localhost"
+ }
+ TestWEBrick.start_httpserver(config) do |server, addr, port, log|
+ body_strs = [ 'aaaaaa', 'bb', 'cccc' ]
+ server.mount_proc( "/", ->( req, res ){
+ # Test for setting chunked...
+ res.chunked = true
+ r,w = IO.pipe
+ evt_man.add_listener do |type,str|
+ type == :cls ? ( w.close ) : ( w << str )
+ end
+ res.body = r
+ } )
+ Thread.pass while server.status != :Running
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/")
+ http.request(req) do |res|
+ i = 0
+ evt_man.raise_str_event( body_strs[i] )
+ res.read_body do |s|
+ assert_equal( body_strs[i], s )
+ i += 1
+ if i < body_strs.length
+ evt_man.raise_str_event( body_strs[i] )
+ else
+ evt_man.raise_close_event()
+ end
+ end
+ assert_equal( body_strs.length, i )
+ end
+ end
+ rescue => err
+ flunk( 'exception raised in thread: ' + err.to_s )
+ end
+ end
+ if t.join( 3 ).nil?
+ evt_man.raise_close_event()
+ flunk( 'timeout' )
+ if t.join( 1 ).nil?
+ Thread.kill t
+ end
+ end
+ end
+
+ def test_response_io_without_chunked_set
+ config = {
+ :ServerName => "localhost"
+ }
+ log_tester = lambda {|log, access_log|
+ assert_equal(1, log.length)
+ assert_match(/WARN Could not determine content-length of response body./, log[0])
+ }
+ TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
+ server.mount_proc("/", lambda { |req, res|
+ r,w = IO.pipe
+ # Test for not setting chunked...
+ # res.chunked = true
+ res.body = r
+ w << "foo"
+ w.close
+ })
+ Thread.pass while server.status != :Running
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/")
+ req['Connection'] = 'Keep-Alive'
+ begin
+ timeout(2) do
+ http.request(req){|res| assert_equal("foo", res.body) }
+ end
+ rescue Timeout::Error
+ flunk('corrupted reponse')
+ end
+ }
+ end
+
+ def test_request_handler_callback_is_deprecated
+ requested = 0
+ config = {
+ :ServerName => "localhost",
+ :RequestHandler => Proc.new{|req, res| requested += 1 },
+ }
+ log_tester = lambda {|log, access_log|
+ assert_equal(2, log.length)
+ assert_match(/WARN :RequestHandler is deprecated, please use :RequestCallback/, log[0])
+ assert_match(%r{ERROR `/' not found\.}, log[1])
+ }
+ TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
+ Thread.pass while server.status != :Running
+
+ http = Net::HTTP.new(addr, port)
+ req = Net::HTTP::Get.new("/")
+ req["Host"] = "localhost:#{port}"
+ http.request(req){|res| assert_equal("404", res.code, log.call)}
+ assert_match(%r{:RequestHandler is deprecated, please use :RequestCallback$}, log.call, log.call)
+ }
+ assert_equal(1, requested)
+ end
+
+ def test_shutdown_with_busy_keepalive_connection
+ requested = 0
+ config = {
+ :ServerName => "localhost",
+ }
+ TestWEBrick.start_httpserver(config){|server, addr, port, log|
+ server.mount_proc("/", lambda {|req, res| res.body = "heffalump" })
+ Thread.pass while server.status != :Running
+
+ Net::HTTP.start(addr, port) do |http|
+ req = Net::HTTP::Get.new("/")
+ http.request(req){|res| assert_equal('Keep-Alive', res['Connection'], log.call) }
+ server.shutdown
+ begin
+ 10.times {|n| http.request(req); requested += 1 }
+ rescue
+ # Errno::ECONNREFUSED or similar
+ end
+ end
+ }
+ assert_equal(0, requested, "Server responded to #{requested} requests after shutdown")
+ end
+end
diff --git a/jni/ruby/test/webrick/test_httputils.rb b/jni/ruby/test/webrick/test_httputils.rb
new file mode 100644
index 0000000..2753cbe
--- /dev/null
+++ b/jni/ruby/test/webrick/test_httputils.rb
@@ -0,0 +1,100 @@
+require "test/unit"
+require "webrick/httputils"
+
+class TestWEBrickHTTPUtils < Test::Unit::TestCase
+ include WEBrick::HTTPUtils
+
+ def test_normilize_path
+ assert_equal("/foo", normalize_path("/foo"))
+ assert_equal("/foo/bar/", normalize_path("/foo/bar/"))
+
+ assert_equal("/", normalize_path("/foo/../"))
+ assert_equal("/", normalize_path("/foo/.."))
+ assert_equal("/", normalize_path("/foo/bar/../../"))
+ assert_equal("/", normalize_path("/foo/bar/../.."))
+ assert_equal("/", normalize_path("/foo/bar/../.."))
+ assert_equal("/baz", normalize_path("/foo/bar/../../baz"))
+ assert_equal("/baz", normalize_path("/foo/../bar/../baz"))
+ assert_equal("/baz/", normalize_path("/foo/../bar/../baz/"))
+ assert_equal("/...", normalize_path("/bar/../..."))
+ assert_equal("/.../", normalize_path("/bar/../.../"))
+
+ assert_equal("/foo/", normalize_path("/foo/./"))
+ assert_equal("/foo/", normalize_path("/foo/."))
+ assert_equal("/foo/", normalize_path("/foo/././"))
+ assert_equal("/foo/", normalize_path("/foo/./."))
+ assert_equal("/foo/bar", normalize_path("/foo/./bar"))
+ assert_equal("/foo/bar/", normalize_path("/foo/./bar/."))
+ assert_equal("/foo/bar/", normalize_path("/./././foo/./bar/."))
+
+ assert_equal("/foo/bar/", normalize_path("//foo///.//bar/.///.//"))
+ assert_equal("/", normalize_path("//foo///..///bar/.///..//.//"))
+
+ assert_raise(RuntimeError){ normalize_path("foo/bar") }
+ assert_raise(RuntimeError){ normalize_path("..") }
+ assert_raise(RuntimeError){ normalize_path("/..") }
+ assert_raise(RuntimeError){ normalize_path("/./..") }
+ assert_raise(RuntimeError){ normalize_path("/./../") }
+ assert_raise(RuntimeError){ normalize_path("/./../..") }
+ assert_raise(RuntimeError){ normalize_path("/./../../") }
+ assert_raise(RuntimeError){ normalize_path("/./../") }
+ assert_raise(RuntimeError){ normalize_path("/../..") }
+ assert_raise(RuntimeError){ normalize_path("/../../") }
+ assert_raise(RuntimeError){ normalize_path("/../../..") }
+ assert_raise(RuntimeError){ normalize_path("/../../../") }
+ assert_raise(RuntimeError){ normalize_path("/../foo/../") }
+ assert_raise(RuntimeError){ normalize_path("/../foo/../../") }
+ assert_raise(RuntimeError){ normalize_path("/foo/bar/../../../../") }
+ assert_raise(RuntimeError){ normalize_path("/foo/../bar/../../") }
+ assert_raise(RuntimeError){ normalize_path("/./../bar/") }
+ assert_raise(RuntimeError){ normalize_path("/./../") }
+ end
+
+ def test_split_header_value
+ assert_equal(['foo', 'bar'], split_header_value('foo, bar'))
+ assert_equal(['"foo"', 'bar'], split_header_value('"foo", bar'))
+ assert_equal(['foo', '"bar"'], split_header_value('foo, "bar"'))
+ assert_equal(['*'], split_header_value('*'))
+ assert_equal(['W/"xyzzy"', 'W/"r2d2xxxx"', 'W/"c3piozzzz"'],
+ split_header_value('W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"'))
+ end
+
+ def test_escape
+ assert_equal("/foo/bar", escape("/foo/bar"))
+ assert_equal("/~foo/bar", escape("/~foo/bar"))
+ assert_equal("/~foo%20bar", escape("/~foo bar"))
+ assert_equal("/~foo%20bar", escape("/~foo bar"))
+ assert_equal("/~foo%09bar", escape("/~foo\tbar"))
+ assert_equal("/~foo+bar", escape("/~foo+bar"))
+ bug8425 = '[Bug #8425] [ruby-core:55052]'
+ assert_nothing_raised(ArgumentError, Encoding::CompatibilityError, bug8425) {
+ assert_equal("%E3%83%AB%E3%83%93%E3%83%BC%E3%81%95%E3%82%93", escape("\u{30EB 30D3 30FC 3055 3093}"))
+ }
+ end
+
+ def test_escape_form
+ assert_equal("%2Ffoo%2Fbar", escape_form("/foo/bar"))
+ assert_equal("%2F~foo%2Fbar", escape_form("/~foo/bar"))
+ assert_equal("%2F~foo+bar", escape_form("/~foo bar"))
+ assert_equal("%2F~foo+%2B+bar", escape_form("/~foo + bar"))
+ end
+
+ def test_unescape
+ assert_equal("/foo/bar", unescape("%2ffoo%2fbar"))
+ assert_equal("/~foo/bar", unescape("/%7efoo/bar"))
+ assert_equal("/~foo/bar", unescape("%2f%7efoo%2fbar"))
+ assert_equal("/~foo+bar", unescape("/%7efoo+bar"))
+ end
+
+ def test_unescape_form
+ assert_equal("//foo/bar", unescape_form("/%2Ffoo/bar"))
+ assert_equal("//foo/bar baz", unescape_form("/%2Ffoo/bar+baz"))
+ assert_equal("/~foo/bar baz", unescape_form("/%7Efoo/bar+baz"))
+ end
+
+ def test_escape_path
+ assert_equal("/foo/bar", escape_path("/foo/bar"))
+ assert_equal("/foo/bar/", escape_path("/foo/bar/"))
+ assert_equal("/%25foo/bar/", escape_path("/%foo/bar/"))
+ end
+end
diff --git a/jni/ruby/test/webrick/test_httpversion.rb b/jni/ruby/test/webrick/test_httpversion.rb
new file mode 100644
index 0000000..81a871a
--- /dev/null
+++ b/jni/ruby/test/webrick/test_httpversion.rb
@@ -0,0 +1,40 @@
+require "test/unit"
+require "webrick/httpversion"
+
+class TestWEBrickHTTPVersion < Test::Unit::TestCase
+ def setup
+ @v09 = WEBrick::HTTPVersion.new("0.9")
+ @v10 = WEBrick::HTTPVersion.new("1.0")
+ @v11 = WEBrick::HTTPVersion.new("1.001")
+ end
+
+ def test_to_s()
+ assert_equal("0.9", @v09.to_s)
+ assert_equal("1.0", @v10.to_s)
+ assert_equal("1.1", @v11.to_s)
+ end
+
+ def test_major()
+ assert_equal(0, @v09.major)
+ assert_equal(1, @v10.major)
+ assert_equal(1, @v11.major)
+ end
+
+ def test_minor()
+ assert_equal(9, @v09.minor)
+ assert_equal(0, @v10.minor)
+ assert_equal(1, @v11.minor)
+ end
+
+ def test_compar()
+ assert_equal(0, @v09 <=> "0.9")
+ assert_equal(0, @v09 <=> "0.09")
+
+ assert_equal(-1, @v09 <=> @v10)
+ assert_equal(-1, @v09 <=> "1.00")
+
+ assert_equal(1, @v11 <=> @v09)
+ assert_equal(1, @v11 <=> "1.0")
+ assert_equal(1, @v11 <=> "0.9")
+ end
+end
diff --git a/jni/ruby/test/webrick/test_server.rb b/jni/ruby/test/webrick/test_server.rb
new file mode 100644
index 0000000..043d439
--- /dev/null
+++ b/jni/ruby/test/webrick/test_server.rb
@@ -0,0 +1,131 @@
+require "test/unit"
+require "tempfile"
+require "webrick"
+require_relative "utils"
+
+class TestWEBrickServer < Test::Unit::TestCase
+ class Echo < WEBrick::GenericServer
+ def run(sock)
+ while line = sock.gets
+ sock << line
+ end
+ end
+ end
+
+ def test_server
+ TestWEBrick.start_server(Echo){|server, addr, port, log|
+ TCPSocket.open(addr, port){|sock|
+ sock.puts("foo"); assert_equal("foo\n", sock.gets, log.call)
+ sock.puts("bar"); assert_equal("bar\n", sock.gets, log.call)
+ sock.puts("baz"); assert_equal("baz\n", sock.gets, log.call)
+ sock.puts("qux"); assert_equal("qux\n", sock.gets, log.call)
+ }
+ }
+ end
+
+ def test_start_exception
+ stopped = 0
+
+ log = []
+ logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN)
+
+ assert_raises(SignalException) do
+ listener = Object.new
+ def listener.to_io # IO.select invokes #to_io.
+ raise SignalException, 'SIGTERM' # simulate signal in main thread
+ end
+ def listener.shutdown
+ end
+ def listener.close
+ end
+
+ server = WEBrick::HTTPServer.new({
+ :BindAddress => "127.0.0.1", :Port => 0,
+ :StopCallback => Proc.new{ stopped += 1 },
+ :Logger => logger,
+ })
+ server.listeners[0].close
+ server.listeners[0] = listener
+
+ server.start
+ end
+
+ assert_equal(1, stopped)
+ assert_equal(1, log.length)
+ assert_match(/FATAL SignalException: SIGTERM/, log[0])
+ end
+
+ def test_callbacks
+ accepted = started = stopped = 0
+ config = {
+ :AcceptCallback => Proc.new{ accepted += 1 },
+ :StartCallback => Proc.new{ started += 1 },
+ :StopCallback => Proc.new{ stopped += 1 },
+ }
+ TestWEBrick.start_server(Echo, config){|server, addr, port, log|
+ true while server.status != :Running
+ assert_equal(1, started, log.call)
+ assert_equal(0, stopped, log.call)
+ assert_equal(0, accepted, log.call)
+ TCPSocket.open(addr, port){|sock| (sock << "foo\n").gets }
+ TCPSocket.open(addr, port){|sock| (sock << "foo\n").gets }
+ TCPSocket.open(addr, port){|sock| (sock << "foo\n").gets }
+ assert_equal(3, accepted, log.call)
+ }
+ assert_equal(1, started)
+ assert_equal(1, stopped)
+ end
+
+ def test_daemon
+ begin
+ r, w = IO.pipe
+ pid1 = Process.fork{
+ r.close
+ WEBrick::Daemon.start
+ w.puts(Process.pid)
+ sleep 10
+ }
+ pid2 = r.gets.to_i
+ assert(Process.kill(:KILL, pid2))
+ assert_not_equal(pid1, pid2)
+ rescue NotImplementedError
+ # snip this test
+ ensure
+ Process.wait(pid1) if pid1
+ r.close
+ w.close
+ end
+ end
+
+ def test_restart
+ address = '127.0.0.1'
+ port = 0
+ log = []
+ config = {
+ :BindAddress => address,
+ :Port => port,
+ :Logger => WEBrick::Log.new(log, WEBrick::BasicLog::WARN),
+ }
+ server = Echo.new(config)
+ client_proc = lambda {|str|
+ begin
+ ret = server.listeners.first.connect_address.connect {|s|
+ s.write(str)
+ s.close_write
+ s.read
+ }
+ assert_equal(str, ret)
+ ensure
+ server.shutdown
+ end
+ }
+ server_thread = Thread.new { server.start }
+ client_thread = Thread.new { client_proc.call("a") }
+ assert_join_threads([client_thread, server_thread])
+ server.listen(address, port)
+ server_thread = Thread.new { server.start }
+ client_thread = Thread.new { client_proc.call("b") }
+ assert_join_threads([client_thread, server_thread])
+ assert_equal([], log)
+ end
+end
diff --git a/jni/ruby/test/webrick/test_utils.rb b/jni/ruby/test/webrick/test_utils.rb
new file mode 100644
index 0000000..e63e0eb
--- /dev/null
+++ b/jni/ruby/test/webrick/test_utils.rb
@@ -0,0 +1,64 @@
+require "test/unit"
+require "webrick/utils"
+
+class TestWEBrickUtils < Test::Unit::TestCase
+ def assert_expired(flag, m)
+ if m == WEBrick::Utils
+ handler = WEBrick::Utils::TimeoutHandler.instance
+ assert_equal(flag, handler.instance_eval{ @timeout_info.empty? })
+ end
+ end
+
+ def do_test_timeout(m)
+ ex = Class.new(StandardError)
+
+ assert_equal(:foo, m.timeout(10){ :foo })
+ assert_expired(true, m)
+
+ i = 0
+ assert_raise(Timeout::Error){
+ m.timeout(2){
+ assert_raise(Timeout::Error){ m.timeout(1){ i += 1; sleep } }
+ assert_expired(false, m)
+ i += 1
+ sleep
+ }
+ }
+ assert_equal(2, i)
+ assert_expired(true, m)
+
+ assert_raise(Timeout::Error){ m.timeout(0.1){ sleep } }
+ assert_expired(true, m)
+
+ assert_raise(ex){ m.timeout(0.1, ex){ sleep } }
+ assert_expired(true, m)
+
+ i = 0
+ assert_raise(ex){
+ m.timeout(10){
+ m.timeout(1, ex){ i += 1; sleep }
+ }
+ sleep
+ }
+ assert_equal(1, i)
+ assert_expired(true, m)
+
+ i = 0
+ assert_raise(Timeout::Error){
+ m.timeout(1){
+ m.timeout(10, ex){ i += 1; sleep }
+ }
+ sleep
+ }
+ assert_equal(1, i)
+ assert_expired(true, m)
+ end
+
+ def test_webrick_timeout
+ do_test_timeout(WEBrick::Utils)
+ end
+
+ #def test_timeout
+ # do_test_timeout(Timeout)
+ #end
+end
diff --git a/jni/ruby/test/webrick/utils.rb b/jni/ruby/test/webrick/utils.rb
new file mode 100644
index 0000000..71559a5
--- /dev/null
+++ b/jni/ruby/test/webrick/utils.rb
@@ -0,0 +1,70 @@
+require "webrick"
+begin
+ require "webrick/https"
+rescue LoadError
+end
+require "webrick/httpproxy"
+
+module TestWEBrick
+ NullWriter = Object.new
+ def NullWriter.<<(msg)
+ puts msg if $DEBUG
+ return self
+ end
+
+ class WEBrick::HTTPServlet::CGIHandler
+ remove_const :Ruby
+ Ruby = EnvUtil.rubybin
+ remove_const :CGIRunner
+ CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc:
+ end
+
+ RubyBin = "\"#{EnvUtil.rubybin}\""
+ RubyBin << " --disable-gems"
+ RubyBin << " \"-I#{File.expand_path("../..", File.dirname(__FILE__))}/lib\""
+ RubyBin << " \"-I#{File.dirname(EnvUtil.rubybin)}/.ext/common\""
+ RubyBin << " \"-I#{File.dirname(EnvUtil.rubybin)}/.ext/#{RUBY_PLATFORM}\""
+
+ include Test::Unit::Assertions
+ extend Test::Unit::Assertions
+
+ module_function
+
+ DefaultLogTester = lambda {|log, access_log| assert_equal([], log) }
+
+ def start_server(klass, config={}, log_tester=DefaultLogTester, &block)
+ log_ary = []
+ access_log_ary = []
+ log = proc { "webrick log start:\n" + (log_ary+access_log_ary).join.gsub(/^/, " ").chomp + "\nwebrick log end" }
+ server = klass.new({
+ :BindAddress => "127.0.0.1", :Port => 0,
+ :ServerType => Thread,
+ :Logger => WEBrick::Log.new(log_ary, WEBrick::BasicLog::WARN),
+ :AccessLog => [[access_log_ary, ""]]
+ }.update(config))
+ server_thread = server.start
+ server_thread2 = Thread.new {
+ server_thread.join
+ if log_tester
+ log_tester.call(log_ary, access_log_ary)
+ end
+ }
+ addr = server.listeners[0].addr
+ client_thread = Thread.new {
+ begin
+ block.yield([server, addr[3], addr[1], log])
+ ensure
+ server.shutdown
+ end
+ }
+ assert_join_threads([client_thread, server_thread2])
+ end
+
+ def start_httpserver(config={}, log_tester=DefaultLogTester, &block)
+ start_server(WEBrick::HTTPServer, config, log_tester, &block)
+ end
+
+ def start_httpproxy(config={}, log_tester=DefaultLogTester, &block)
+ start_server(WEBrick::HTTPProxyServer, config, log_tester, &block)
+ end
+end
diff --git a/jni/ruby/test/webrick/webrick.cgi b/jni/ruby/test/webrick/webrick.cgi
new file mode 100644
index 0000000..43c1af8
--- /dev/null
+++ b/jni/ruby/test/webrick/webrick.cgi
@@ -0,0 +1,36 @@
+#!ruby
+require "webrick/cgi"
+
+class TestApp < WEBrick::CGI
+ def do_GET(req, res)
+ res["content-type"] = "text/plain"
+ if (p = req.path_info) && p.length > 0
+ res.body = p
+ elsif (q = req.query).size > 0
+ res.body = q.keys.sort.collect{|key|
+ q[key].list.sort.collect{|v|
+ "#{key}=#{v}"
+ }.join(", ")
+ }.join(", ")
+ elsif %r{/$} =~ req.request_uri.to_s
+ res.body = ""
+ res.body << req.request_uri.to_s << "\n"
+ res.body << req.script_name
+ elsif !req.cookies.empty?
+ res.body = req.cookies.inject(""){|result, cookie|
+ result << "%s=%s\n" % [cookie.name, cookie.value]
+ }
+ res.cookies << WEBrick::Cookie.new("Customer", "WILE_E_COYOTE")
+ res.cookies << WEBrick::Cookie.new("Shipping", "FedEx")
+ else
+ res.body = req.script_name
+ end
+ end
+
+ def do_POST(req, res)
+ do_GET(req, res)
+ end
+end
+
+cgi = TestApp.new
+cgi.start
diff --git a/jni/ruby/test/webrick/webrick_long_filename.cgi b/jni/ruby/test/webrick/webrick_long_filename.cgi
new file mode 100644
index 0000000..43c1af8
--- /dev/null
+++ b/jni/ruby/test/webrick/webrick_long_filename.cgi
@@ -0,0 +1,36 @@
+#!ruby
+require "webrick/cgi"
+
+class TestApp < WEBrick::CGI
+ def do_GET(req, res)
+ res["content-type"] = "text/plain"
+ if (p = req.path_info) && p.length > 0
+ res.body = p
+ elsif (q = req.query).size > 0
+ res.body = q.keys.sort.collect{|key|
+ q[key].list.sort.collect{|v|
+ "#{key}=#{v}"
+ }.join(", ")
+ }.join(", ")
+ elsif %r{/$} =~ req.request_uri.to_s
+ res.body = ""
+ res.body << req.request_uri.to_s << "\n"
+ res.body << req.script_name
+ elsif !req.cookies.empty?
+ res.body = req.cookies.inject(""){|result, cookie|
+ result << "%s=%s\n" % [cookie.name, cookie.value]
+ }
+ res.cookies << WEBrick::Cookie.new("Customer", "WILE_E_COYOTE")
+ res.cookies << WEBrick::Cookie.new("Shipping", "FedEx")
+ else
+ res.body = req.script_name
+ end
+ end
+
+ def do_POST(req, res)
+ do_GET(req, res)
+ end
+end
+
+cgi = TestApp.new
+cgi.start
diff --git a/jni/ruby/test/win32ole/err_in_callback.rb b/jni/ruby/test/win32ole/err_in_callback.rb
new file mode 100644
index 0000000..fabb7be
--- /dev/null
+++ b/jni/ruby/test/win32ole/err_in_callback.rb
@@ -0,0 +1,9 @@
+require 'win32ole'
+db = WIN32OLE.new('ADODB.Connection')
+db.connectionString = "Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
+ev = WIN32OLE_EVENT.new(db)
+ev.on_event('WillConnect') {|*args|
+ foo
+}
+db.open
+WIN32OLE_EVENT.message_loop
diff --git a/jni/ruby/test/win32ole/orig_data.csv b/jni/ruby/test/win32ole/orig_data.csv
new file mode 100644
index 0000000..3931c6a
--- /dev/null
+++ b/jni/ruby/test/win32ole/orig_data.csv
@@ -0,0 +1,5 @@
+ID,VALUE
+1,"A"
+2,"B"
+3,"C"
+4,"B"
diff --git a/jni/ruby/test/win32ole/test_err_in_callback.rb b/jni/ruby/test/win32ole/test_err_in_callback.rb
new file mode 100644
index 0000000..cae6d49
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_err_in_callback.rb
@@ -0,0 +1,55 @@
+#
+# test Win32OLE avoids cfp consistency error when the exception raised
+# in WIN32OLE_EVENT handler block. [ruby-dev:35450]
+#
+
+begin
+ require 'win32ole'
+rescue LoadError
+end
+if defined?(WIN32OLE)
+ require 'mkmf'
+ require 'pathname'
+ require 'test/unit'
+ require 'tmpdir'
+ class TestErrInCallBack < Test::Unit::TestCase
+ def setup
+ @ruby = nil
+ if File.exist?("./" + CONFIG["RUBY_INSTALL_NAME"] + CONFIG["EXEEXT"])
+ sep = File::ALT_SEPARATOR || "/"
+ @ruby = "." + sep + CONFIG["RUBY_INSTALL_NAME"]
+ cwd = Pathname.new(File.expand_path('.'))
+ @iopt = $:.map {|e|
+ " -I " + (Pathname.new(e).relative_path_from(cwd).to_s rescue e)
+ }.join("")
+ script = File.join(File.dirname(__FILE__), "err_in_callback.rb")
+ @script = Pathname.new(script).relative_path_from(cwd).to_s rescue script
+ end
+ end
+
+ def available_adodb?
+ begin
+ WIN32OLE.new('ADODB.Connection')
+ rescue WIN32OLERuntimeError
+ return false
+ end
+ return true
+ end
+
+ def test_err_in_callback
+ skip "'ADODB.Connection' is not available" unless available_adodb?
+ if @ruby
+ Dir.mktmpdir do |tmpdir|
+ logfile = File.join(tmpdir, "test_err_in_callback.log")
+ cmd = "#{@ruby} -v #{@iopt} #{@script} > #{logfile.gsub(%r(/), '\\')} 2>&1"
+ system(cmd)
+ str = ""
+ open(logfile) {|ifs|
+ str = ifs.read
+ }
+ assert_match(/NameError/, str)
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_folderitem2_invokeverb.rb b/jni/ruby/test/win32ole/test_folderitem2_invokeverb.rb
new file mode 100644
index 0000000..c5ec88c
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_folderitem2_invokeverb.rb
@@ -0,0 +1,65 @@
+#
+# This script check that Win32OLE can execute InvokeVerb method of FolderItem2.
+#
+
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require 'test/unit'
+
+if defined?(WIN32OLE)
+ class TestInvokeVerb < Test::Unit::TestCase
+ def setup
+ # make dummy file for InvokeVerb test.
+ @fso = WIN32OLE.new('Scripting.FileSystemObject')
+ dummy_file = @fso.GetTempName
+ @cfolder = @fso.getFolder(".")
+ f = @cfolder.CreateTextFile(dummy_file)
+ f.close
+ @dummy_path = @cfolder.path + "\\" + dummy_file
+
+ shell=WIN32OLE.new('Shell.Application')
+ @nsp = shell.NameSpace(@cfolder.path)
+ @fi2 = @nsp.parseName(dummy_file)
+ end
+
+ def find_link(path)
+ arlink = []
+ @cfolder.files.each do |f|
+ if /\.lnk$/ =~ f.path
+ linkinfo = @nsp.parseName(f.name).getLink
+ arlink.push f if linkinfo.path == path
+ end
+ end
+ arlink
+ end
+
+ def test_invokeverb
+ # in Windows Vista (not tested), Windows 7
+ # The verb must be in English.
+ # Creating Shortcut is "Link"
+ links = find_link(@dummy_path)
+ assert_equal(0, links.size)
+
+ # Now create shortcut to @dummy_path
+ arg = WIN32OLE_VARIANT.new("Link")
+ @fi2.InvokeVerb(arg)
+
+ # Now search shortcut to @dummy_path
+ links = find_link(@dummy_path)
+ assert_equal(1, links.size)
+ @lpath = links[0].path
+ end
+
+ def teardown
+ if @lpath
+ @fso.deleteFile(@lpath)
+ end
+ if @dummy_path
+ @fso.deleteFile(@dummy_path)
+ end
+ end
+
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_nil2vtempty.rb b/jni/ruby/test/win32ole/test_nil2vtempty.rb
new file mode 100644
index 0000000..8508ecd
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_nil2vtempty.rb
@@ -0,0 +1,36 @@
+# This is test script to check that WIN32OLE should convert nil to VT_EMPTY in second try.
+# [ruby-talk:137054]
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require 'test/unit'
+
+if defined?(WIN32OLE)
+ class TestNIL2VT_EMPTY < Test::Unit::TestCase
+ def setup
+ fs = WIN32OLE.new('Scripting.FileSystemObject')
+ @path = fs.GetFolder(".").path
+ end
+ def test_openSchema
+ con = nil
+ begin
+ con = WIN32OLE.new('ADODB.Connection')
+ con.connectionString = "Provider=MSDASQL;Extended Properties="
+ con.connectionString +="\"DRIVER={Microsoft Text Driver (*.txt; *.csv)};DBQ=#{@path}\""
+ con.open
+ rescue
+ con = nil
+ end
+ if con
+ rs = con.openSchema(4, [nil,nil,"DUMMY", "TABLE"])
+ assert(rs)
+ assert_equal("_Recordset", rs.ole_type.name)
+
+ rs = con.openSchema(4, [WIN32OLE_VARIANT::Empty, WIN32OLE_VARIANT::Empty, "DUMMY", "TABLE"])
+ assert(rs)
+ assert_equal("_Recordset", rs.ole_type.name)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_ole_methods.rb b/jni/ruby/test/win32ole/test_ole_methods.rb
new file mode 100644
index 0000000..869828d
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_ole_methods.rb
@@ -0,0 +1,36 @@
+#
+# This is test for [ruby-talk:196897]
+#
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(WIN32OLE)
+ class TestWIN32OLE_FOR_PROPERTYPUTREF < Test::Unit::TestCase
+
+ def setup
+ @obj = WIN32OLE.new('Scripting.Dictionary')
+ end
+
+ def test_ole_methods
+ x = @obj.ole_methods.select {|m|
+ m.invoke_kind == 'PROPERTYPUTREF'
+ }
+ assert(x.size > 0)
+ assert_equal(1, x.size)
+ assert_equal('Item', x[0].name)
+ end
+
+ def test_ole_put_methods
+ x = @obj.ole_put_methods.select {|m|
+ m.invoke_kind == 'PROPERTYPUTREF'
+ }
+ assert(x.size > 0)
+ assert_equal(1, x.size)
+ assert_equal('Item', x[0].name)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_propertyputref.rb b/jni/ruby/test/win32ole/test_propertyputref.rb
new file mode 100644
index 0000000..24459af
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_propertyputref.rb
@@ -0,0 +1,30 @@
+require 'test/unit'
+begin
+ require 'win32ole'
+rescue LoadError
+end
+
+if defined?(WIN32OLE)
+ class TestWIN32OLE_PROPERTYPUTREF < Test::Unit::TestCase
+ def setup
+ begin
+ @sapi = WIN32OLE.new('SAPI.SpVoice')
+ @sv = @sapi.voice
+ rescue WIN32OLERuntimeError
+ @sapi = nil
+ end
+ end
+ def test_sapi
+ if @sapi
+ new_id = @sapi.getvoices.item(0).Id
+ @sapi.voice = @sapi.getvoices.item(0)
+ assert_equal(new_id, @sapi.voice.Id)
+ end
+ end
+ def teardown
+ if @sapi
+ @sapi.voice = @sv
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_thread.rb b/jni/ruby/test/win32ole/test_thread.rb
new file mode 100644
index 0000000..56a6357
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_thread.rb
@@ -0,0 +1,33 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require 'test/unit'
+
+if defined?(WIN32OLE)
+ class TestThread < Test::Unit::TestCase
+ #
+ # test for Bug #2618(ruby-core:27634)
+ #
+ def assert_creating_win32ole_object_in_thread(meth)
+ t = Thread.__send__(meth) {
+ WIN32OLE.new('Scripting.Dictionary')
+ }
+ assert_nothing_raised(WIN32OLERuntimeError, "[Bug #2618] Thread.#{meth}") {
+ t.join
+ }
+ end
+
+ def test_creating_win32ole_object_in_thread_new
+ assert_creating_win32ole_object_in_thread(:new)
+ end
+
+ def test_creating_win32ole_object_in_thread_start
+ assert_creating_win32ole_object_in_thread(:start)
+ end
+
+ def test_creating_win32ole_object_in_thread_fork
+ assert_creating_win32ole_object_in_thread(:fork)
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole.rb b/jni/ruby/test/win32ole/test_win32ole.rb
new file mode 100644
index 0000000..b41fd59
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole.rb
@@ -0,0 +1,564 @@
+# coding: us-ascii
+
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require 'test/unit'
+
+if defined?(WIN32OLE)
+ module CONST1
+ end
+ module CONST2
+ end
+
+ module TestCaseForDict
+ def test_convert_bignum
+ @dict1.add("a", 9999999999)
+ @dict1.add("b", 999999999)
+ @dict1.add("c", @dict1.item("b") * 10 + 9)
+ assert_equal(9999999999, @dict1.item("a"))
+ assert_equal(9999999999, @dict1.item("c"))
+ end
+ def test_add
+ @dict1.add("a", 1000)
+ assert_equal(1000, @dict1.item("a"))
+ end
+ def test_setproperty_equal_ended
+ @dict1.compareMode = 1
+ @dict1.add("one", 1)
+ assert_equal(1, @dict1.item("ONE"))
+ @dict2.add("one", 1)
+ assert_nil(@dict2.item("ONE"))
+ assert_equal(1, @dict2.item("one"))
+ end
+ def test_non_exist_property
+ assert_raise(WIN32OLERuntimeError) {
+ @dict1.unknown_property = 1
+ }
+ end
+
+ def test_raise_message
+ exc = assert_raise(WIN32OLERuntimeError) {
+ @dict1.add
+ }
+ assert_match(/^\(in OLE method `add': \)/, exc.message) #`
+
+ exc = assert_raise(WIN32OLERuntimeError) {
+ @dict1._invoke(1, [], [])
+ }
+ assert_match(/^\(in OLE method `<dispatch id:1>': \)/, exc.message) #`
+
+ exc = assert_raise(WIN32OLERuntimeError) {
+ @dict1.compareMode = -1
+ }
+ assert_match(/^\(in setting property `compareMode': \)/, exc.message) #`
+ end
+
+ def test_no_method_error
+ exc = assert_raise(NoMethodError) {
+ @dict1.non_exist_method
+ }
+ assert_match(/non_exist_method/, exc.message)
+ end
+
+ def test_ole_methods
+ methods = @dict1.ole_methods
+ mnames = methods.collect {|m|
+ m.name
+ }
+ assert(mnames.include?("Add"))
+ end
+
+ def test_ole_func_methods
+ methods = @dict1.ole_func_methods
+ mnames = methods.collect {|m|
+ m.name
+ }
+ assert(mnames.include?("Add"))
+ end
+
+ def test_ole_put_methods
+ methods = @dict1.ole_put_methods
+ mnames = methods.collect {|m|
+ m.name
+ }
+ assert(mnames.include?("CompareMode"))
+ end
+
+ def test_ole_get_methods
+ methods = @dict1.ole_get_methods
+ mnames = methods.collect {|m|
+ m.name
+ }
+ assert(mnames.include?("Count"))
+ end
+
+ def test_ole_mehtod_help
+ minfo = @dict1.ole_method_help("Add")
+ assert_equal(2, minfo.size_params)
+ end
+
+ def test_ole_typelib
+ tlib = @dict1.ole_typelib
+ assert_equal("Microsoft Scripting Runtime", tlib.name);
+ end
+
+ def test_each
+ @dict1.add("one", 1)
+ @dict1.add("two", 2)
+ i = 0
+ @dict1.keys.each do |item|
+ i += 1
+ end
+ assert_equal(2, i)
+ end
+
+ def test_bracket
+ @dict1.add("foo", "FOO")
+ assert_equal("FOO", @dict1.item("foo"))
+ assert_equal("FOO", @dict1["foo"])
+ end
+
+ def test_bracket_equal
+ @dict1.add("foo", "FOO")
+ @dict1["foo"] = "BAR"
+ assert_equal("BAR", @dict1["foo"])
+ end
+
+ def test_bracket_with_numkey
+ @dict1.add(1, "ONE")
+ @dict1.add(2, "two")
+ assert_equal("ONE", @dict1[1])
+ @dict1[2] = "TWO"
+ assert_equal("TWO", @dict1[2])
+ end
+
+ def test_invoke_with_array
+ @dict1.add("ary1", [1,2,3])
+ assert_equal([1,2,3], @dict1["ary1"])
+
+ @dict1.add("ary2", [[1,2,"a"], [3,4,"b"]])
+ assert_equal([[1,2,"a"], [3,4,"b"]], @dict1["ary2"])
+
+ @dict1.add("ary3", [[[1]]])
+ assert_equal([[[1]]], @dict1["ary3"])
+
+ @dict1.add("ary4", [[[1], [2], [3]], [[4], [5], [6]]])
+ assert_equal([[[1],[2], [3]], [[4], [5], [6]]], @dict1["ary4"])
+ end
+ end
+
+ class TestWin32OLE < Test::Unit::TestCase
+ include TestCaseForDict
+ def setup
+ @dict1 = WIN32OLE.new('Scripting.Dictionary')
+ @dict2 = WIN32OLE.new('Scripting.Dictionary')
+ end
+ def test_s_new
+ assert_instance_of(WIN32OLE, @dict1)
+ assert_instance_of(WIN32OLE, @dict2)
+ end
+
+ def test_s_new_exc
+ assert_raise(TypeError) {
+ WIN32OLE.new(1)
+ }
+ assert_raise(TypeError) {
+ WIN32OLE.new("Scripting.Dictionary", 1)
+ }
+ end
+
+ def test_s_new_exc_svr_tainted
+ th = Thread.start {
+ $SAFE = 1
+ svr = "Scripting.Dictionary"
+ svr.taint
+ WIN32OLE.new(svr)
+ }
+ exc = assert_raise(SecurityError) {
+ th.join
+ }
+ assert_match(/insecure object creation - `Scripting.Dictionary'/, exc.message)
+ end
+
+ def test_s_new_exc_host_tainted
+ th = Thread.start {
+ $SAFE = 1
+ svr = "Scripting.Dictionary"
+ host = "localhost"
+ host.taint
+ WIN32OLE.new(svr, host)
+ }
+ exc = assert_raise(SecurityError) {
+ th.join
+ }
+ assert_match(/insecure object creation - `localhost'/, exc.message)
+ end
+
+ def test_s_new_DCOM
+ rshell = WIN32OLE.new("Shell.Application")
+ assert_instance_of(WIN32OLE, rshell)
+ end
+
+ def test_s_new_from_clsid
+ shell = WIN32OLE.new("{13709620-C279-11CE-A49E-444553540000}")
+ assert_instance_of(WIN32OLE, shell)
+ exc = assert_raise(WIN32OLERuntimeError) {
+ WIN32OLE.new("{000}")
+ }
+ assert_match(/unknown OLE server: `\{000\}'/, exc.message) #`
+ end
+
+ def test_s_connect
+ obj = WIN32OLE.connect("winmgmts:")
+ assert_instance_of(WIN32OLE, obj)
+ end
+
+ def test_s_connect_exc
+ assert_raise(TypeError) {
+ WIN32OLE.connect(1)
+ }
+ end
+
+ def test_s_coonect_exc_tainted
+ th = Thread.start {
+ $SAFE = 1
+ svr = "winmgmts:"
+ svr.taint
+ WIN32OLE.connect(svr)
+ }
+ exc = assert_raise(SecurityError) {
+ th.join
+ }
+ assert_match(/insecure connection - `winmgmts:'/, exc.message)
+ end
+
+ def test_invoke_accept_symbol_hash_key
+ fso = WIN32OLE.new('Scripting.FileSystemObject')
+ afolder = fso.getFolder(".")
+ bfolder = fso.getFolder({"FolderPath" => "."})
+ cfolder = fso.getFolder({:FolderPath => "."})
+ assert_equal(afolder.path, bfolder.path)
+ assert_equal(afolder.path, cfolder.path)
+ fso = nil
+ end
+
+ def test_setproperty
+ installer = WIN32OLE.new("WindowsInstaller.Installer")
+ record = installer.CreateRecord(2)
+ # this is the way to set property with argument in Win32OLE.
+ record.setproperty( "StringData", 1, 'dddd')
+ assert_equal('dddd', record.StringData(1))
+ end
+
+ def test_ole_type
+ fso = WIN32OLE.new('Scripting.FileSystemObject')
+ tobj = fso.ole_type
+ assert_match(/^IFileSystem/, tobj.name)
+ end
+
+ def test_ole_obj_help
+ fso = WIN32OLE.new('Scripting.FileSystemObject')
+ tobj = fso.ole_obj_help
+ assert_match(/^IFileSystem/, tobj.name)
+ end
+
+
+ def test_invoke_hash_key_non_str_sym
+ fso = WIN32OLE.new('Scripting.FileSystemObject')
+ begin
+ fso.getFolder({1 => "."})
+ assert(false)
+ rescue TypeError
+ assert(true)
+ end
+ fso = nil
+ end
+
+ def test_get_win32ole_object
+ shell = WIN32OLE.new('Shell.Application')
+ folder = shell.nameSpace(0)
+ assert_instance_of(WIN32OLE, folder)
+ end
+
+ def test_invoke_accept_multi_hash_key
+ shell = WIN32OLE.new('Shell.Application')
+ folder = shell.nameSpace(0)
+ item = folder.items.item(0)
+ name = folder.getDetailsOf(item, 0)
+ assert_equal(item.name, name)
+ name = folder.getDetailsOf({:vItem => item, :iColumn => 0})
+ assert_equal(item.name, name)
+ name = folder.getDetailsOf({"vItem" => item, :iColumn => 0})
+ assert_equal(item.name, name)
+ end
+
+ def test_ole_invoke_with_named_arg_last
+ shell = WIN32OLE.new('Shell.Application')
+ folder = shell.nameSpace(0)
+ item = folder.items.item(0)
+ name = folder.getDetailsOf(item, {:iColumn => 0})
+ assert_equal(item.name, name)
+ end
+
+ def test__invoke
+ shell=WIN32OLE.new('Shell.Application')
+ assert_equal(shell.NameSpace(0).title, shell._invoke(0x60020002, [0], [WIN32OLE::VARIANT::VT_VARIANT]).title)
+ end
+
+ def test_ole_query_interface
+ shell=WIN32OLE.new('Shell.Application')
+ assert_raise(ArgumentError) {
+ shell.ole_query_interface
+ }
+ shell2 = shell.ole_query_interface('{A4C6892C-3BA9-11D2-9DEA-00C04FB16162}')
+ assert_instance_of(WIN32OLE, shell2)
+ end
+
+ def test_ole_respond_to
+ fso = WIN32OLE.new('Scripting.FileSystemObject')
+ assert(fso.ole_respond_to?('getFolder'))
+ assert(fso.ole_respond_to?('GETFOLDER'))
+ assert(fso.ole_respond_to?(:getFolder))
+ assert(!fso.ole_respond_to?('XXXXX'))
+ assert_raise(TypeError) {
+ assert_raise(fso.ole_respond_to?(1))
+ }
+ end
+
+ def test_invoke
+ fso = WIN32OLE.new('Scripting.FileSystemObject')
+ assert(fso.invoke(:getFolder, "."))
+ assert(fso.invoke('getFolder', "."))
+ end
+
+ def test_s_const_load
+ assert(!defined?(CONST1::SsfWINDOWS))
+ shell=WIN32OLE.new('Shell.Application')
+ WIN32OLE.const_load(shell, CONST1)
+ assert_equal(36, CONST1::SsfWINDOWS)
+
+ assert(!defined?(CONST2::SsfWINDOWS))
+ WIN32OLE.const_load("Microsoft Shell Controls And Automation", CONST2)
+ assert_equal(36, CONST2::SsfWINDOWS)
+ end
+
+ def test_s_create_guid
+ guid = WIN32OLE.create_guid
+ assert_match(/^\{[A-Z0-9]{8}\-[A-Z0-9]{4}\-[A-Z0-9]{4}\-[A-Z0-9]{4}\-[A-Z0-9]{12}/,
+ guid)
+ end
+
+ #
+ # WIN32OLE.codepage is initialized according to Encoding.default_external.
+ #
+ # def test_s_codepage
+ # assert_equal(WIN32OLE::CP_ACP, WIN32OLE.codepage)
+ # end
+
+ def test_s_codepage_set
+ cp = WIN32OLE.codepage
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
+ assert_equal(WIN32OLE::CP_UTF8, WIN32OLE.codepage)
+ WIN32OLE.codepage = cp
+ end
+
+ def test_s_codepage_changed
+ cp = WIN32OLE.codepage
+ fso = WIN32OLE.new("Scripting.FileSystemObject")
+ fname = fso.getTempName
+ begin
+ obj = WIN32OLE_VARIANT.new([0x3042].pack("U*").force_encoding("UTF-8"))
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
+ assert_equal("\xE3\x81\x82".force_encoding("CP65001"), obj.value)
+
+ begin
+ WIN32OLE.codepage = 932 # Windows-31J
+ rescue WIN32OLERuntimeError
+ end
+ if (WIN32OLE.codepage == 932)
+ assert_equal("\x82\xA0".force_encoding("CP932"), obj.value)
+ end
+
+ begin
+ WIN32OLE.codepage = 20932 # MS EUC-JP
+ rescue WIN32OLERuntimeError
+ end
+ if (WIN32OLE.codepage == 20932)
+ assert_equal("\xA4\xA2".force_encoding("CP20932"), obj.value)
+ end
+
+ WIN32OLE.codepage = cp
+ file = fso.opentextfile(fname, 2, true)
+ test_str = [0x3042].pack("U*").encode("UTF-16LE")
+ begin
+ file.write test_str.force_encoding("UTF-16")
+ ensure
+ file.close
+ end
+ str = ""
+ open(fname, "r:ascii-8bit") {|ifs|
+ str = ifs.read
+ }
+ assert_equal(test_str.force_encoding("ascii-8bit"), str)
+
+ # This test fail if codepage 20932 (euc) is not installed.
+ begin
+ WIN32OLE.codepage = 20932
+ rescue WIN32OLERuntimeError
+ end
+ if (WIN32OLE.codepage == 20932)
+ WIN32OLE.codepage = cp
+ file = fso.opentextfile(fname, 2, true)
+ begin
+ file.write [164, 162].pack("c*").force_encoding("UTF-16")
+ ensure
+ file.close
+ end
+ open(fname, "r:ascii-8bit") {|ifs|
+ str = ifs.read
+ }
+ assert_equal("\244\242", str)
+ end
+
+ ensure
+ WIN32OLE.codepage = cp
+ if (File.exist?(fname))
+ File.unlink(fname)
+ end
+ end
+ end
+
+ def test_cp51932
+ cp = WIN32OLE.codepage
+ begin
+ obj = WIN32OLE_VARIANT.new([0x3042].pack("U*").force_encoding("UTF-8"))
+ begin
+ WIN32OLE.codepage = 51932
+ rescue
+ end
+ if WIN32OLE.codepage == 51932
+ assert_equal("\xA4\xA2".force_encoding("CP51932"), obj.value)
+ end
+ ensure
+ WIN32OLE.codepage = cp
+ end
+ end
+
+ def test_s_locale
+ assert_equal(WIN32OLE::LOCALE_SYSTEM_DEFAULT, WIN32OLE.locale)
+ end
+
+ def test_s_locale_set
+ begin
+ begin
+ WIN32OLE.locale = 1041
+ rescue WIN32OLERuntimeError
+ STDERR.puts("\n#{__FILE__}:#{__LINE__}:#{self.class.name}.test_s_locale_set is skipped(Japanese locale is not installed)")
+ return
+ end
+ assert_equal(1041, WIN32OLE.locale)
+ WIN32OLE.locale = WIN32OLE::LOCALE_SYSTEM_DEFAULT
+ assert_raise(WIN32OLERuntimeError) {
+ WIN32OLE.locale = 111
+ }
+ assert_equal(WIN32OLE::LOCALE_SYSTEM_DEFAULT, WIN32OLE.locale)
+ ensure
+ WIN32OLE.locale = WIN32OLE::LOCALE_SYSTEM_DEFAULT
+ end
+ end
+
+ def test_s_locale_change
+ begin
+ begin
+ WIN32OLE.locale = 0x0411
+ rescue WIN32OLERuntimeError
+ end
+ if WIN32OLE.locale == 0x0411
+ obj = WIN32OLE_VARIANT.new("\\100,000", WIN32OLE::VARIANT::VT_CY)
+ assert_equal("100000", obj.value)
+ assert_raise(WIN32OLERuntimeError) {
+ obj = WIN32OLE_VARIANT.new("$100.000", WIN32OLE::VARIANT::VT_CY)
+ }
+ else
+ STDERR.puts("\n#{__FILE__}:#{__LINE__}:#{self.class.name}.test_s_locale_change is skipped(Japanese locale is not installed)")
+ end
+
+ begin
+ WIN32OLE.locale = 1033
+ rescue WIN32OLERuntimeError
+ end
+ if WIN32OLE.locale == 1033
+ obj = WIN32OLE_VARIANT.new("$100,000", WIN32OLE::VARIANT::VT_CY)
+ assert_equal("100000", obj.value)
+ else
+ STDERR.puts("\n#{__FILE__}:#{__LINE__}:#{self.class.name}.test_s_locale_change is skipped(US English locale is not installed)")
+ end
+ ensure
+ WIN32OLE.locale = WIN32OLE::LOCALE_SYSTEM_DEFAULT
+ end
+ end
+
+ def test_const_CP_ACP
+ assert_equal(0, WIN32OLE::CP_ACP)
+ end
+
+ def test_const_CP_OEMCP
+ assert_equal(1, WIN32OLE::CP_OEMCP)
+ end
+
+ def test_const_CP_MACCP
+ assert_equal(2, WIN32OLE::CP_MACCP)
+ end
+
+ def test_const_CP_THREAD_ACP
+ assert_equal(3, WIN32OLE::CP_THREAD_ACP)
+ end
+
+ def test_const_CP_SYMBOL
+ assert_equal(42, WIN32OLE::CP_SYMBOL)
+ end
+
+ def test_const_CP_UTF7
+ assert_equal(65000, WIN32OLE::CP_UTF7)
+ end
+
+ def test_const_CP_UTF8
+ assert_equal(65001, WIN32OLE::CP_UTF8)
+ end
+
+ def test_const_LOCALE_SYSTEM_DEFAULT
+ assert_equal(0x0800, WIN32OLE::LOCALE_SYSTEM_DEFAULT);
+ end
+
+ def test_const_LOCALE_USER_DEFAULT
+ assert_equal(0x0400, WIN32OLE::LOCALE_USER_DEFAULT);
+ end
+
+ def test_method_missing
+ assert_raise(ArgumentError) {@dict1.method_missing}
+ assert_raise(TypeError) {@dict1.method_missing(1)}
+ assert_raise(ArgumentError) {@dict1.method_missing("foo=")}
+ assert_raise(ArgumentError) {@dict1.method_missing("foo=", 1, 2)}
+ end
+ end
+
+ # test of subclass of WIN32OLE
+ class MyDict < WIN32OLE
+ def MyDict.new
+ super('Scripting.Dictionary')
+ end
+ end
+ class TestMyDict < Test::Unit::TestCase
+ include TestCaseForDict
+ def setup
+ @dict1 = MyDict.new
+ @dict2 = MyDict.new
+ end
+ def test_s_new
+ assert_instance_of(MyDict, @dict1)
+ assert_instance_of(MyDict, @dict2)
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_event.rb b/jni/ruby/test/win32ole/test_win32ole_event.rb
new file mode 100644
index 0000000..86004cd
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_event.rb
@@ -0,0 +1,391 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require 'test/unit'
+
+def ado_installed?
+ installed = false
+ if defined?(WIN32OLE)
+ db = nil
+ begin
+ db = WIN32OLE.new('ADODB.Connection')
+ db.connectionString = "Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
+ db.open
+ db.close
+ db = nil
+ installed = true
+ rescue
+ end
+ end
+ installed
+end
+
+def swbemsink_avairable?
+ available = false
+ if defined?(WIN32OLE)
+ wmi = nil
+ begin
+ wmi = WIN32OLE.new('WbemScripting.SWbemSink')
+ available = true
+ rescue
+ end
+ end
+ available
+end
+
+if defined?(WIN32OLE_EVENT)
+ class TestWIN32OLE_EVENT < Test::Unit::TestCase
+ def test_s_new_exception
+ assert_raise(TypeError) {
+ WIN32OLE_EVENT.new("A")
+ }
+ end
+ def test_s_new_non_exist_event
+ dict = WIN32OLE.new('Scripting.Dictionary')
+ assert_raise(RuntimeError) {
+ WIN32OLE_EVENT.new(dict)
+ }
+ end
+ end
+
+ class TestWIN32OLE_EVENT_SWbemSink < Test::Unit::TestCase
+ unless swbemsink_avairable?
+ def test_dummy_for_skip_message
+ skip "'WbemScripting.SWbemSink' is not available"
+ end
+ else
+ def setup
+ @wmi = WIN32OLE.connect('winmgmts://localhost/root/cimv2')
+ @sws = WIN32OLE.new('WbemScripting.SWbemSink')
+ @event = @event1 = @event2 = ""
+ @sql = "SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_LocalTime'"
+ end
+
+ def message_loop
+ 2.times do
+ WIN32OLE_EVENT.message_loop
+ sleep 1
+ end
+ end
+
+ def default_handler(event, *args)
+ @event += event
+ end
+
+ def handler1
+ @event1 = "handler1"
+ end
+
+ def test_s_new_non_exist_event
+ assert_raise(RuntimeError) {
+ WIN32OLE_EVENT.new(@sws, 'XXXXX')
+ }
+ end
+
+ def test_s_new
+ obj = WIN32OLE_EVENT.new(@sws, 'ISWbemSinkEvents')
+ assert_instance_of(WIN32OLE_EVENT, obj)
+ obj = WIN32OLE_EVENT.new(@sws)
+ assert_instance_of(WIN32OLE_EVENT, obj)
+ end
+
+ def test_s_new_loop
+ @wmi.ExecNotificationQueryAsync(@sws, @sql)
+ ev = WIN32OLE_EVENT.new(@sws)
+ ev.on_event {|*args| default_handler(*args)}
+ message_loop
+ 10.times do |i|
+ WIN32OLE_EVENT.new(@sws)
+ message_loop
+ GC.start
+ end
+ assert_match(/OnObjectReady/, @event)
+ end
+
+ def test_on_event
+ @wmi.ExecNotificationQueryAsync(@sws, @sql)
+ ev = WIN32OLE_EVENT.new(@sws, 'ISWbemSinkEvents')
+ ev.on_event {|*args| default_handler(*args)}
+ message_loop
+ assert_match(/OnObjectReady/, @event)
+ end
+
+ def test_on_event_symbol
+ @wmi.ExecNotificationQueryAsync(@sws, @sql)
+ ev = WIN32OLE_EVENT.new(@sws)
+ ev.on_event(:OnObjectReady) {|*args|
+ handler1
+ }
+ message_loop
+ assert_equal("handler1", @event1)
+ end
+
+ end
+ end
+
+ class TestWIN32OLE_EVENT_ADO < Test::Unit::TestCase
+ unless ado_installed?
+ def test_dummy_for_skip_message
+ skip "ActiveX Data Object Library not found"
+ end
+ else
+ CONNSTR="Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
+ module ADO
+ end
+ def message_loop
+ WIN32OLE_EVENT.message_loop
+ end
+
+ def default_handler(event, *args)
+ @event += event
+ end
+
+ def setup
+ @db = WIN32OLE.new('ADODB.Connection')
+ if !defined?(ADO::AdStateOpen)
+ WIN32OLE.const_load(@db, ADO)
+ end
+ @db.connectionString = CONNSTR
+ @event = ""
+ @event2 = ""
+ @event3 = ""
+ end
+
+ def test_on_event2
+ ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
+ ev.on_event('WillConnect') {|*args| handler1}
+ ev.on_event('WillConnect') {|*args| handler2}
+ @db.open
+ message_loop
+ assert_equal("handler2", @event2)
+ end
+
+ def test_on_event4
+ ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
+ ev.on_event{|*args| handler1}
+ ev.on_event{|*args| handler2}
+ ev.on_event('WillConnect'){|*args| handler3(*args)}
+ @db.open
+ message_loop
+ assert_equal(CONNSTR, @event3)
+ assert("handler2", @event2)
+ end
+
+ def test_on_event5
+ ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
+ ev.on_event {|*args| default_handler(*args)}
+ ev.on_event('WillConnect'){|*args| handler3(*args)}
+ @db.open
+ message_loop
+ assert_match(/ConnectComplete/, @event)
+ assert(/WillConnect/ !~ @event)
+ assert_equal(CONNSTR, @event3)
+ end
+
+ def test_unadvise
+ ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
+ ev.on_event {|*args| default_handler(*args)}
+ @db.open
+ message_loop
+ assert_match(/WillConnect/, @event)
+ ev.unadvise
+ @event = ""
+ @db.close
+ @db.open
+ message_loop
+ assert_equal("", @event);
+ assert_raise(WIN32OLERuntimeError) {
+ ev.on_event {|*args| default_handler(*args)}
+ }
+ end
+
+
+ def test_on_event_with_outargs
+ ev = WIN32OLE_EVENT.new(@db)
+ @db.connectionString = 'XXX' # set illegal connection string
+ assert_raise(WIN32OLERuntimeError) {
+ @db.open
+ }
+ ev.on_event_with_outargs('WillConnect'){|*args|
+ args.last[0] = CONNSTR # ConnectionString = CONNSTR
+ }
+ @db.open
+ message_loop
+ assert(true)
+ end
+
+ def test_on_event_hash_return
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event('WillConnect'){|*args|
+ {:return => 1, :ConnectionString => CONNSTR}
+ }
+ @db.connectionString = 'XXX'
+ @db.open
+ assert(true)
+ end
+
+ def test_on_event_hash_return2
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event('WillConnect'){|*args|
+ {:ConnectionString => CONNSTR}
+ }
+ @db.connectionString = 'XXX'
+ @db.open
+ assert(true)
+ end
+
+ def test_on_event_hash_return3
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event('WillConnect'){|*args|
+ {'ConnectionString' => CONNSTR}
+ }
+ @db.connectionString = 'XXX'
+ @db.open
+ assert(true)
+ end
+
+ def test_on_event_hash_return4
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event('WillConnect'){|*args|
+ {'return' => 1, 'ConnectionString' => CONNSTR}
+ }
+ @db.connectionString = 'XXX'
+ @db.open
+ assert(true)
+ end
+
+ def test_on_event_hash_return5
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event('WillConnect'){|*args|
+ {0 => CONNSTR}
+ }
+ @db.connectionString = 'XXX'
+ @db.open
+ assert(true)
+ end
+
+ def test_off_event
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event{handler1}
+ ev.off_event
+ @db.open
+ message_loop
+ assert_equal("", @event2)
+ end
+
+ def test_off_event_arg
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event('WillConnect'){handler1}
+ ev.off_event('WillConnect')
+ @db.open
+ message_loop
+ assert_equal("", @event2)
+ end
+
+ def test_off_event_arg2
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event('WillConnect'){handler1}
+ ev.on_event('ConnectComplete'){handler1}
+ ev.off_event('WillConnect')
+ @db.open
+ message_loop
+ assert_equal("handler1", @event2)
+ end
+
+ def test_off_event_sym_arg
+ ev = WIN32OLE_EVENT.new(@db)
+ ev.on_event('WillConnect'){handler1}
+ ev.off_event(:WillConnect)
+ @db.open
+ message_loop
+ assert_equal("", @event2)
+ end
+
+ def handler1
+ @event2 = "handler1"
+ end
+
+ def handler2
+ @event2 = "handler2"
+ end
+
+ def handler3(*arg)
+ @event3 += arg[0]
+ end
+
+ def teardown
+ if @db && @db.state == ADO::AdStateOpen
+ @db.close
+ end
+ message_loop
+ @db = nil
+ end
+
+ class Handler1
+ attr_reader :val1, :val2, :val3, :val4
+ def initialize
+ @val1 = nil
+ @val2 = nil
+ @val3 = nil
+ @val4 = nil
+ end
+ def onWillConnect(conn, uid, pwd, opts, stat, pconn)
+ @val1 = conn
+ end
+ def onConnectComplete(err, stat, pconn)
+ @val2 = err
+ @val3 = stat
+ end
+ def onInfoMessage(err, stat, pconn)
+ @val4 = stat
+ end
+ end
+
+ class Handler2
+ attr_reader :ev
+ def initialize
+ @ev = ""
+ end
+ def method_missing(ev, *arg)
+ @ev += ev
+ end
+ end
+
+ def test_handler1
+ ev = WIN32OLE_EVENT.new(@db)
+ h1 = Handler1.new
+ ev.handler = h1
+ @db.open
+ message_loop
+ assert_equal(CONNSTR, h1.val1)
+ assert_equal(h1.val1, ev.handler.val1)
+ assert_equal(nil, h1.val2)
+ assert_equal(ADO::AdStateOpen, h1.val3)
+ assert_equal(ADO::AdStateOpen, h1.val4)
+ end
+
+ def test_handler2
+ ev = WIN32OLE_EVENT.new(@db)
+ h2 = Handler2.new
+ ev.handler = h2
+ @db.open
+ message_loop
+ assert(h2.ev != "")
+ end
+
+ def test_s_new_exc_tainted
+ th = Thread.new {
+ $SAFE=1
+ str = 'ConnectionEvents'
+ str.taint
+ ev = WIN32OLE_EVENT.new(@db, str)
+ }
+ exc = assert_raise(SecurityError) {
+ th.join
+ }
+ assert_match(/insecure event creation - `ConnectionEvents'/, exc.message)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_method.rb b/jni/ruby/test/win32ole/test_win32ole_method.rb
new file mode 100644
index 0000000..2a4e3d0
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_method.rb
@@ -0,0 +1,146 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(WIN32OLE_METHOD)
+ class TestWIN32OLE_METHOD < Test::Unit::TestCase
+
+ def setup
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ @m_open = WIN32OLE_METHOD.new(ole_type, "open")
+ @m_namespace = WIN32OLE_METHOD.new(ole_type, "namespace")
+ @m_parent = WIN32OLE_METHOD.new(ole_type, "parent")
+ @m_invoke = WIN32OLE_METHOD.new(ole_type, "invoke")
+ @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder")
+
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File")
+ @m_file_name = WIN32OLE_METHOD.new(ole_type, "name")
+
+ ole_type = WIN32OLE_TYPE.new("Microsoft Internet Controls", "WebBrowser")
+ @m_navigate_complete = WIN32OLE_METHOD.new(ole_type, "NavigateComplete")
+ end
+
+ def test_initialize
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ assert_raise(TypeError) {
+ WIN32OLE_METHOD.new(1, 2)
+ }
+ assert_raise(ArgumentError) {
+ WIN32OLE_METHOD.new("foo")
+ }
+ assert_raise(ArgumentError) {
+ WIN32OLE_METHOD.new(ole_type)
+ }
+ assert_raise(WIN32OLERuntimeError) {
+ WIN32OLE_METHOD.new(ole_type, "NonExistMethod")
+ }
+ assert_raise(TypeError) {
+ WIN32OLE_METHOD.new(ole_type, 1)
+ }
+ method = WIN32OLE_METHOD.new(ole_type, "Open")
+ assert_instance_of(WIN32OLE_METHOD, method)
+ method = WIN32OLE_METHOD.new(ole_type, "open")
+ assert_instance_of(WIN32OLE_METHOD, method)
+ end
+
+ def test_name
+ assert_equal("Open", @m_open.name)
+ end
+
+ def test_return_type
+ assert_equal("VOID", @m_open.return_type)
+ assert_equal("Folder", @m_namespace.return_type)
+ end
+
+ def test_return_vtype
+ assert_equal(24, @m_open.return_vtype)
+ assert_equal(26, @m_namespace.return_vtype)
+ end
+
+ def test_return_type_detail
+ assert_equal(['VOID'], @m_open.return_type_detail)
+ assert_equal(['PTR', 'USERDEFINED', 'Folder'], @m_namespace.return_type_detail)
+ end
+
+ def test_invoke_kind
+ assert_equal('FUNC', @m_open.invoke_kind)
+ assert_equal('FUNC', @m_namespace.invoke_kind)
+ assert_equal('PROPERTYGET', @m_parent.invoke_kind)
+ end
+
+ def test_invkind
+ assert_equal(1, @m_namespace.invkind)
+ assert_equal(2, @m_parent.invkind)
+ end
+
+ def test_visible?
+ assert(@m_namespace.visible?)
+ assert(!@m_invoke.visible?)
+ end
+
+ def test_event?
+ assert(@m_navigate_complete.event?)
+ assert(!@m_namespace.event?)
+ end
+
+ def test_event_interface
+ assert_equal("DWebBrowserEvents", @m_navigate_complete.event_interface)
+ assert_equal(nil, @m_namespace.event_interface)
+ end
+
+ def test_helpstring
+ assert_equal("Get special folder from ShellSpecialFolderConstants", @m_namespace.helpstring)
+ end
+
+ def test_helpfile
+ assert_equal("", @m_namespace.helpfile)
+ assert_match(/VBENLR.*\.CHM$/i, @m_file_name.helpfile)
+ end
+
+ def test_helpcontext
+ assert_equal(0, @m_namespace.helpcontext)
+ assert_equal(2181996, @m_file_name.helpcontext)
+ end
+
+ def test_dispid
+ assert_equal(1610743810, @m_namespace.dispid)
+ end
+
+ def is_ruby64?
+ /mswin64|x64-mingw/ =~ RUBY_PLATFORM
+ end
+
+ def test_offset_vtbl
+ exp = is_ruby64? ? 48 : 24
+ assert_equal(exp, @m_invoke.offset_vtbl)
+ end
+
+ def test_size_params
+ assert_equal(1, @m_open.size_params)
+ assert_equal(4, @m_browse_for_folder.size_params)
+ end
+
+ def test_size_opt_params
+ assert_equal(0, @m_open.size_opt_params)
+ assert_equal(1, @m_browse_for_folder.size_opt_params)
+ end
+
+ def test_params
+ params = @m_browse_for_folder.params
+ assert_instance_of(Array, params)
+ assert_equal(4, params.size)
+ assert_instance_of(WIN32OLE_PARAM, params[0])
+ end
+
+ def test_to_s
+ assert_equal(@m_namespace.name, @m_namespace.to_s)
+ end
+
+ def test_inspect
+ assert_equal("#<WIN32OLE_METHOD:NameSpace>", @m_namespace.inspect)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_param.rb b/jni/ruby/test/win32ole/test_win32ole_param.rb
new file mode 100644
index 0000000..1a4eeff
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_param.rb
@@ -0,0 +1,106 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(WIN32OLE_PARAM)
+ class TestWIN32OLE_PARAM < Test::Unit::TestCase
+
+ def setup
+ ole_type = WIN32OLE_TYPE.new("Microsoft Internet Controls", "WebBrowser")
+ m_navigate = WIN32OLE_METHOD.new(ole_type, "Navigate")
+ m_before_navigate = WIN32OLE_METHOD.new(ole_type, "BeforeNavigate")
+ params = m_navigate.params
+ @param_url = params[0]
+ @param_flags = params[1]
+ @param_cancel = m_before_navigate.params[5]
+
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellLinkObject")
+ m_geticonlocation = WIN32OLE_METHOD.new(ole_type, "GetIconLocation")
+ @param_pbs = m_geticonlocation.params[0]
+
+ ole_type = WIN32OLE_TYPE.new("Microsoft HTML Object Library", "FontNames")
+ m_count = WIN32OLE_METHOD.new(ole_type, "Count")
+ @param_p = m_count.params[0]
+
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile")
+ @param_overwritefiles = m_copyfile.params[2]
+ end
+
+ def test_s_new
+ assert_raise(ArgumentError) {
+ WIN32OLE_PARAM.new("hoge")
+ }
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject")
+ m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile")
+ assert_raise(IndexError) {
+ WIN32OLE_PARAM.new(m_copyfile, 4);
+ }
+ assert_raise(IndexError) {
+ WIN32OLE_PARAM.new(m_copyfile, 0);
+ }
+ assert_raise(IndexError) {
+ WIN32OLE_PARAM.new(m_copyfile, 0);
+ }
+ param = WIN32OLE_PARAM.new(m_copyfile, 3)
+ assert_equal("OverWriteFiles", param.name)
+ assert_equal(WIN32OLE_PARAM, param.class)
+ assert_equal(true, param.default)
+ assert_equal("#<WIN32OLE_PARAM:OverWriteFiles=true>", param.inspect)
+ end
+
+ def test_name
+ assert_equal('URL', @param_url.name)
+ assert_equal('Flags', @param_flags.name)
+ assert_equal('Cancel', @param_cancel.name)
+ end
+
+ def test_ole_type
+ assert_equal('BSTR', @param_url.ole_type)
+ assert_equal('VARIANT', @param_flags.ole_type)
+ end
+
+ def test_ole_type_detail
+ assert_equal(['BSTR'], @param_url.ole_type_detail)
+ assert_equal(['PTR', 'VARIANT'], @param_flags.ole_type_detail)
+ end
+
+ def test_input?
+ assert(@param_url.input?)
+ assert(@param_cancel.input?)
+ assert(!@param_pbs.input?)
+ end
+
+ def test_output?
+ assert(!@param_url.output?)
+ assert(@param_cancel.output?)
+ assert(@param_pbs.output?)
+ end
+
+ def test_optional?
+ assert(!@param_url.optional?)
+ assert(@param_flags.optional?)
+ end
+
+ def test_retval?
+ assert(!@param_url.retval?)
+ assert(@param_p.retval?)
+ end
+
+ def test_default
+ assert_equal(nil, @param_url.default)
+ assert_equal(true, @param_overwritefiles.default)
+ end
+
+ def test_to_s
+ assert_equal(@param_url.name, @param_url.to_s)
+ end
+
+ def test_inspect
+ assert_equal("#<WIN32OLE_PARAM:URL>", @param_url.inspect)
+ assert_equal("#<WIN32OLE_PARAM:OverWriteFiles=true>", @param_overwritefiles.inspect)
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_record.rb b/jni/ruby/test/win32ole/test_win32ole_record.rb
new file mode 100644
index 0000000..265feb8
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_record.rb
@@ -0,0 +1,313 @@
+# coding: us-ascii
+
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require 'test/unit'
+
+PROGID_CLR='System.Runtime.Serialization.FormatterConverter'
+PROGID_RBCOMTEST='RbComTest.ComSrvTest'
+
+=begin
+RbComTest.ComSrvTest is following VB.NET COM server(RbComTest solution).
+
+Imports System.Runtime.InteropServices
+Public Class ComSrvTest
+ <StructLayout(LayoutKind.Sequential)> _
+ Public Structure Book
+ <MarshalAs(UnmanagedType.BStr)> _
+ Public title As String
+ Public cost As Integer
+ End Structure
+
+ Public Function getBook() As Book
+ Dim book As New Book
+ book.title = "The Ruby Book"
+ book.cost = 20
+ Return book
+ End Function
+
+ Public Function getBooks() As Book()
+ Dim book() As Book = {New Book, New Book}
+ book(0).title = "The CRuby Book"
+ book(0).cost = 30
+ book(1).title = "The JRuby Book"
+ book(1).cost = 40
+ Return book
+ End Function
+
+ Public Sub getBookByRefObject(ByRef obj As Object)
+ Dim book As New Book
+ book.title = "The Ruby Reference Book"
+ book.cost = 50
+ obj = book
+ End Sub
+
+ Public Function getVer2BookByValBook(<MarshalAs(UnmanagedType.Struct)> ByVal book As Book) As Book
+ Dim ret As New Book
+ ret.title = book.title + " ver2"
+ ret.cost = book.cost * 1.1
+ Return ret
+ End Function
+
+ Public Sub getBookByRefBook(<MarshalAs(UnmanagedType.LPStruct)> ByRef book As Book)
+ book.title = "The Ruby Reference Book2"
+ book.cost = 44
+ End Sub
+
+ Public Sub getVer3BookByRefBook(<MarshalAs(UnmanagedType.LPStruct)> ByRef book As Book)
+ book.title += " ver3"
+ book.cost *= 1.2
+ End Sub
+End Class
+=end
+
+
+if defined?(WIN32OLE_RECORD)
+ def rbcomtest_exist?
+ exist = false
+ begin
+ obj = WIN32OLE.new(PROGID_RBCOMTEST)
+ exist = true
+ rescue WIN32OLERuntimeError
+ exist = false
+ end
+ exist
+ end
+
+ class TestWIN32OLE_RECORD_BY_RBCOMTEST < Test::Unit::TestCase
+ unless rbcomtest_exist?
+ def test_dummy_for_skip_message
+ skip "#{PROGID_RBCOMTEST} for WIN32OLE_RECORD test is not installed"
+ end
+ else
+ def setup
+ @obj = WIN32OLE.new(PROGID_RBCOMTEST)
+ end
+
+ def test_s_new_from_win32ole
+ rec = WIN32OLE_RECORD.new('Book', @obj)
+ assert(rec)
+ assert_instance_of(WIN32OLE_RECORD, rec)
+ end
+
+ def test_s_new_from_win32ole_typelib
+ tlib = @obj.ole_typelib
+ rec = WIN32OLE_RECORD.new('Book', tlib)
+ assert(rec)
+ assert_instance_of(WIN32OLE_RECORD, rec)
+ end
+
+ def test_s_new_raise
+ assert_raise(WIN32OLERuntimeError) {
+ rec = WIN32OLE_RECORD.new('NonExistRecordName', @obj)
+ }
+ assert_raise(ArgumentError) {
+ rec = WIN32OLE_RECORD.new
+ }
+ assert_raise(ArgumentError) {
+ rec = WIN32OLE_RECORD.new('NonExistRecordName')
+ }
+ end
+
+ def test_to_h
+ rec = WIN32OLE_RECORD.new('Book', @obj)
+ assert_equal({'title'=>nil, 'cost'=>nil}, rec.to_h)
+ end
+
+ def test_typename
+ rec = WIN32OLE_RECORD.new('Book', @obj)
+ assert_equal('Book', rec.typename)
+ end
+
+ def test_method_missing_getter
+ rec = WIN32OLE_RECORD.new('Book', @obj)
+ assert_equal(nil, rec.title)
+ assert_raise(KeyError) {
+ rec.non_exist_name
+ }
+ end
+
+ def test_method_missing_setter
+ rec = WIN32OLE_RECORD.new('Book', @obj)
+ rec.title = "Ruby Book"
+ assert_equal("Ruby Book", rec.title)
+ end
+
+ def test_get_record_from_comserver
+ rec = @obj.getBook
+ assert_instance_of(WIN32OLE_RECORD, rec)
+ assert_equal("The Ruby Book", rec.title)
+ assert_equal(20, rec.cost)
+ end
+
+ def test_get_record_array_from_comserver
+ rec = @obj.getBooks
+ assert_instance_of(Array, rec)
+ assert_equal(2, rec.size)
+ assert_instance_of(WIN32OLE_RECORD, rec[0])
+ assert_equal("The CRuby Book", rec[0].title)
+ assert_equal(30, rec[0].cost)
+ assert_instance_of(WIN32OLE_RECORD, rec[1])
+ assert_equal("The JRuby Book", rec[1].title)
+ assert_equal(40, rec[1].cost)
+ end
+
+ def test_pass_record_parameter
+ rec = WIN32OLE_RECORD.new('Book', @obj)
+ rec.title = "Ruby Book"
+ rec.cost = 60
+ book = @obj.getVer2BookByValBook(rec)
+ assert_equal("Ruby Book ver2", book.title)
+ assert_equal(66, book.cost)
+ end
+
+ def test_pass_variant_parameter_byref
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
+ @obj.getBookByRefBook(obj)
+ assert_instance_of(WIN32OLE_RECORD, obj.value)
+ book = obj.value
+ assert_equal("The Ruby Reference Book2", book.title)
+ assert_equal(44, book.cost)
+ end
+
+ def test_pass_record_parameter_byref
+ book = WIN32OLE_RECORD.new('Book', @obj)
+ @obj.getBookByRefBook(book)
+ assert_equal("The Ruby Reference Book2", book.title)
+ assert_equal(44, book.cost)
+ end
+
+ def test_pass_and_get_record_parameter_byref
+ book = WIN32OLE_RECORD.new('Book', @obj)
+ book.title = "Ruby Book"
+ book.cost = 60
+ @obj.getVer3BookByRefBook(book)
+ assert_equal("Ruby Book ver3", book.title)
+ assert_equal(72, book.cost)
+ end
+
+ def test_ole_instance_variable_get
+ obj = WIN32OLE_RECORD.new('ComObject', @obj)
+ assert_equal(nil, obj.ole_instance_variable_get(:object_id))
+ assert_equal(nil, obj.ole_instance_variable_get('object_id'))
+ end
+
+ def test_ole_instance_variable_set
+ book = WIN32OLE_RECORD.new('Book', @obj)
+ book.ole_instance_variable_set(:title, "Ruby Book")
+ assert_equal("Ruby Book", book.title)
+ book.ole_instance_variable_set('title', "Ruby Book2")
+ assert_equal("Ruby Book2", book.title)
+ end
+
+ def test_inspect
+ book = WIN32OLE_RECORD.new('Book', @obj)
+ assert_equal(%q[#<WIN32OLE_RECORD(Book) {"title"=>nil, "cost"=>nil}>], book.inspect)
+ end
+ end
+ end
+
+ def clr_exist?
+ exist = false
+ begin
+ obj = WIN32OLE.new(PROGID_CLR)
+ exist = true
+ rescue WIN32OLERuntimeError
+ exist = false
+ end
+ exist
+ end
+
+ class TestWIN32OLE_CLR < Test::Unit::TestCase
+ unless clr_exist?
+ def test_dummy_for_skip_message
+ skip "#{PROGID_CLR}(.Net Framework 3.5) not found."
+ end
+ else
+ def setup
+ @obj = WIN32OLE.new(PROGID_CLR)
+ end
+
+ def test_s_new_from_win32ole
+ rec = WIN32OLE_RECORD.new('Decimal', @obj)
+ assert(rec)
+ assert_instance_of(WIN32OLE_RECORD, rec)
+ end
+
+ def test_s_new_from_win32ole_typelib
+ tlib = @obj.ole_typelib
+ rec = WIN32OLE_RECORD.new('Decimal', tlib)
+ assert(rec)
+ assert_instance_of(WIN32OLE_RECORD, rec)
+ end
+
+ def test_s_new_raise
+ assert_raise(WIN32OLERuntimeError) {
+ rec = WIN32OLE_RECORD.new('NonExistRecordName', @obj)
+ }
+ assert_raise(ArgumentError) {
+ rec = WIN32OLE_RECORD.new
+ }
+ assert_raise(ArgumentError) {
+ rec = WIN32OLE_RECORD.new('NonExistRecordName')
+ }
+ end
+
+ def test_to_h
+ rec = WIN32OLE_RECORD.new('Decimal', @obj)
+ assert_equal({'lo'=>nil, 'mid'=>nil, 'hi'=>nil, 'flags'=>nil}, rec.to_h)
+ end
+
+ def test_typename
+ rec = WIN32OLE_RECORD.new('Decimal', @obj)
+ assert_equal('Decimal', rec.typename)
+ end
+
+ def test_method_missing_getter
+ rec = WIN32OLE_RECORD.new('Decimal', @obj)
+ assert_equal(nil, rec.lo)
+ assert_raise(KeyError) {
+ rec.non_exist_name
+ }
+ end
+
+ def test_method_missing_setter
+ rec = WIN32OLE_RECORD.new('Decimal', @obj)
+ rec.lo = 1
+ assert_equal(1, rec.lo)
+ end
+
+ def test_pass_record_parameter
+ rec = WIN32OLE_RECORD.new('Decimal', @obj)
+ rec.lo = 0
+ rec.mid = 1
+ rec.hi = 0
+ rec.flags = false
+ assert_equal(2**32, @obj.ToInt64(rec))
+ end
+
+ def test_ole_instance_variable_get
+ obj = WIN32OLE_RECORD.new('Decimal', @obj)
+ assert_equal(nil, obj.ole_instance_variable_get(:lo))
+ assert_equal(nil, obj.ole_instance_variable_get('lo'))
+ end
+
+ def test_ole_instance_variable_set
+ rec = WIN32OLE_RECORD.new('Decimal', @obj)
+ rec.ole_instance_variable_set(:lo, 1)
+ assert_equal(1, rec.lo)
+ rec.ole_instance_variable_set('lo', 2)
+ assert_equal(2, rec.lo)
+ end
+
+ def test_inspect
+ rec = WIN32OLE_RECORD.new('Decimal', @obj)
+ assert_equal(%q[#<WIN32OLE_RECORD(Decimal) {"flags"=>nil, "hi"=>nil, "lo"=>nil, "mid"=>nil}>], rec.inspect)
+ end
+
+ end
+ end
+
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_type.rb b/jni/ruby/test/win32ole/test_win32ole_type.rb
new file mode 100644
index 0000000..5422352
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_type.rb
@@ -0,0 +1,249 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(WIN32OLE_TYPE)
+ class TestWIN32OLE_TYPE < Test::Unit::TestCase
+
+ def test_s_progids
+ progids = WIN32OLE_TYPE.progids
+ assert_instance_of(Array, progids)
+ assert(progids.size > 0)
+ assert_instance_of(String, progids[0])
+ assert(progids.include?("Shell.Application.1"))
+ end
+
+ def test_initialize
+ assert_raise(ArgumentError) {
+ WIN32OLE_TYPE.new
+ }
+ assert_raise(ArgumentError) {
+ WIN32OLE_TYPE.new("foo")
+ }
+ assert_raise(TypeError) {
+ WIN32OLE_TYPE.new(1, 2)
+ }
+ assert_raise(TypeError) {
+ WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", 1)
+ }
+ assert_raise(WIN32OLERuntimeError) {
+ WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "foo")
+ }
+ assert_raise(WIN32OLERuntimeError) {
+ WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Application")
+ }
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ assert_instance_of(WIN32OLE_TYPE, ole_type)
+ assert_equal("Shell", ole_type.name)
+ assert_equal("Class", ole_type.ole_type)
+ assert_equal("{13709620-C279-11CE-A49E-444553540000}", ole_type.guid)
+ assert_equal("Shell.Application.1", ole_type.progid)
+ assert_equal(true, ole_type.visible?)
+ assert_equal("Shell", ole_type.to_s)
+ assert_equal(0, ole_type.major_version)
+ assert_equal(0, ole_type.minor_version)
+ assert_equal(5, ole_type.typekind)
+ assert_equal("Shell Object Type Information", ole_type.helpstring)
+ assert_equal(nil, ole_type.src_type)
+ assert_equal("", ole_type.helpfile)
+ assert_equal(0, ole_type.helpcontext)
+ assert_equal([], ole_type.variables)
+ assert(ole_type.ole_methods.select{|m|/NameSpace/i =~ m.name}.size > 0)
+
+ ole_type2 = WIN32OLE_TYPE.new("{13709620-C279-11CE-A49E-444553540000}", "Shell")
+ assert_instance_of(WIN32OLE_TYPE, ole_type)
+ assert_equal(ole_type.name, ole_type2.name)
+ assert_equal(ole_type.ole_type, ole_type2.ole_type)
+ assert_equal(ole_type.guid, ole_type2.guid)
+ assert_equal(ole_type.progid, ole_type2.progid)
+ assert_equal(ole_type.visible?, ole_type2.visible?)
+ assert_equal(ole_type.to_s, ole_type2.to_s)
+ assert_equal(ole_type.major_version, ole_type2.major_version)
+ assert_equal(ole_type.minor_version, ole_type2.minor_version)
+ assert_equal(ole_type.typekind, ole_type2.typekind)
+ assert_equal(ole_type.helpstring, ole_type2.helpstring)
+ assert_equal(ole_type.src_type, ole_type2.src_type)
+ assert_equal(ole_type.helpfile, ole_type2.helpfile)
+ assert_equal(ole_type.helpcontext, ole_type2.helpcontext)
+ assert_equal(ole_type.variables.size, ole_type2.variables.size)
+ assert_equal(ole_type.ole_methods[0].name, ole_type2.ole_methods[0].name)
+ assert_equal(ole_type.ole_typelib.name, ole_type2.ole_typelib.name)
+ assert_equal(ole_type.implemented_ole_types.size, ole_type2.implemented_ole_types.size)
+ assert_equal(ole_type.inspect, ole_type2.inspect)
+ end
+
+ def setup
+ @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell")
+ end
+
+ def test_name
+ assert_equal("Shell", @ole_type.name)
+ end
+
+ def test_ole_type
+ assert_equal("Class", @ole_type.ole_type)
+ end
+
+ def test_guid
+ assert_equal("{13709620-C279-11CE-A49E-444553540000}", @ole_type.guid)
+ end
+
+ def test_progid
+ assert_equal("Shell.Application.1", @ole_type.progid)
+ end
+
+ def test_visible?
+ assert(@ole_type.visible?)
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "IShellDispatch")
+ assert(!ole_type.visible?)
+ end
+
+ def test_to_s
+ assert_equal(@ole_type.to_s, @ole_type.name)
+ end
+
+ def test_major_version
+ assert_equal(0, @ole_type.major_version)
+ # ole_type = WIN32OLE_TYPE.new("Microsoft Word 11.0 Object Library", "Documents")
+ # assert_equal(8, ole_type.major_version)
+ end
+
+ def test_minor_version
+ assert_equal(0, @ole_type.minor_version)
+ # ole_type = WIN32OLE_TYPE.new("Microsoft Word 11.0 Object Library", "Documents")
+ # assert_equal(3, ole_type.minor_version)
+ end
+
+ def test_typekind
+ assert_equal(5, @ole_type.typekind)
+ end
+
+ def test_helpstring
+ assert_equal("Shell Object Type Information", @ole_type.helpstring)
+ end
+
+ def test_src_type
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "DriveTypeConst")
+ assert_match(/__MIDL___MIDL_itf_scrrun_/, ole_type.src_type)
+ assert_equal(nil, @ole_type.src_type)
+ end
+
+ def test_helpfile
+ assert_equal("", @ole_type.helpfile)
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "Folders")
+ assert_match(/VBENLR98\.CHM$/i, ole_type.helpfile)
+ end
+
+ def test_helpcontext
+ assert_equal(0, @ole_type.helpcontext)
+ ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "Folders")
+ assert_equal(2181929, ole_type.helpcontext)
+ end
+
+ def test_variables
+ variables = @ole_type.variables
+ assert_instance_of(Array, variables)
+ assert(variables.size == 0)
+
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ variables = ole_type.variables
+ assert_instance_of(Array, variables)
+ assert(variables.size > 0)
+
+ assert_instance_of(WIN32OLE_VARIABLE, variables[0])
+ end
+
+ def test_ole_methods
+ methods = @ole_type.ole_methods
+ assert_instance_of(Array, methods)
+ assert(methods.size > 0)
+ assert_instance_of(WIN32OLE_METHOD, methods[0]);
+ assert(methods.collect{|m| m.name}.include?("Application"))
+ end
+
+ def test_ole_typelib
+ tlib = @ole_type.ole_typelib
+ assert_instance_of(WIN32OLE_TYPELIB, tlib)
+ assert_equal("Microsoft Shell Controls And Automation", tlib.name)
+ end
+
+ def test_implemented_ole_types
+ ole_types = @ole_type.implemented_ole_types
+ assert_instance_of(Array, ole_types)
+ assert_equal(1, ole_types.size)
+ assert_match(/^IShellDispatch\d{0,1}$/, ole_types[0].name)
+
+ ie_otype = WIN32OLE_TYPE.new("Microsoft Internet Controls", "InternetExplorer")
+ ole_types = ie_otype.implemented_ole_types
+ assert_equal(4, ole_types.size)
+ otype = ole_types.select{|t| t.name == "IWebBrowser2"}
+ assert_equal(1, otype.size)
+ otype = ole_types.select{|t| t.name == "IWebBrowserApp"}
+ assert_equal(1, otype.size)
+ otype = ole_types.select{|t| t.name == "DWebBrowserEvents2"}
+ assert_equal(1, otype.size)
+ otype = ole_types.select{|t| t.name == "DWebBrowserEvents"}
+ assert_equal(1, otype.size)
+ end
+
+ def test_default_ole_types
+ ie_otype = WIN32OLE_TYPE.new("Microsoft Internet Controls", "InternetExplorer")
+ ole_types = ie_otype.default_ole_types
+ otype = ole_types.select{|t| t.name == "IWebBrowser2"}
+ assert_equal(1, otype.size)
+ otype = ole_types.select{|t| t.name == "IWebBrowserApp"}
+ assert_equal(0, otype.size)
+ otype = ole_types.select{|t| t.name == "DWebBrowserEvents2"}
+ assert_equal(1, otype.size)
+ otype = ole_types.select{|t| t.name == "DWebBrowserEvents"}
+ assert_equal(0, otype.size)
+ end
+
+ def test_source_ole_types
+ ie_otype = WIN32OLE_TYPE.new("Microsoft Internet Controls", "InternetExplorer")
+ ole_types = ie_otype.source_ole_types
+ otype = ole_types.select{|t| t.name == "IWebBrowser2"}
+ assert_equal(0, otype.size)
+ otype = ole_types.select{|t| t.name == "IWebBrowserApp"}
+ assert_equal(0, otype.size)
+ otype = ole_types.select{|t| t.name == "DWebBrowserEvents2"}
+ assert_equal(1, otype.size)
+ otype = ole_types.select{|t| t.name == "DWebBrowserEvents"}
+ assert_equal(1, otype.size)
+ end
+
+ def test_default_event_sources
+ ie_otype = WIN32OLE_TYPE.new("Microsoft Internet Controls", "InternetExplorer")
+ ole_types = ie_otype.default_event_sources
+ otype = ole_types.select{|t| t.name == "IWebBrowser2"}
+ assert_equal(0, otype.size)
+ otype = ole_types.select{|t| t.name == "IWebBrowserApp"}
+ assert_equal(0, otype.size)
+ otype = ole_types.select{|t| t.name == "DWebBrowserEvents2"}
+ assert_equal(1, otype.size)
+ otype = ole_types.select{|t| t.name == "DWebBrowserEvents"}
+ assert_equal(0, otype.size)
+ end
+
+ def test_inspect
+ assert_equal("#<WIN32OLE_TYPE:Shell>", @ole_type.inspect)
+ end
+ # WIN32OLE_TYPE.typelibs will be obsoleted.
+ def test_s_typelibs
+ tlibs = WIN32OLE_TYPE.typelibs.sort
+ tlibs2 = WIN32OLE_TYPELIB.typelibs.collect{|t|t.name}.sort
+ assert_equal(tlibs2, tlibs)
+ end
+
+ # WIN32OLE_TYPE.ole_classes will be obsoleted.
+ def test_s_ole_classes
+ ots1 = WIN32OLE_TYPE.ole_classes("Microsoft Shell Controls And Automation")
+ ots2 = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation").ole_types
+ otns1 = ots1.collect{|t| t.name}.sort
+ otns2 = ots2.collect{|t| t.name}.sort
+ assert_equal(otns2, otns1)
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_typelib.rb b/jni/ruby/test/win32ole/test_win32ole_typelib.rb
new file mode 100644
index 0000000..a548bc1
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_typelib.rb
@@ -0,0 +1,116 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(WIN32OLE_TYPELIB)
+ class TestWIN32OLE_TYPELIB < Test::Unit::TestCase
+ def test_s_typelibs
+ tlibs = WIN32OLE_TYPELIB.typelibs
+ assert_instance_of(Array, tlibs)
+ assert(tlibs.size > 0)
+ tlib = tlibs.find {|t| t.name == "Microsoft Shell Controls And Automation"}
+ assert(tlib)
+ end
+
+ def test_initialize
+ assert_raise(ArgumentError) {
+ WIN32OLE_TYPELIB.new(1,2,3,4)
+ }
+
+ assert_raise(TypeError) {
+ WIN32OLE_TYPELIB.new(100)
+ }
+
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_instance_of(WIN32OLE_TYPELIB, tlib)
+
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation", 1.0)
+ assert_instance_of(WIN32OLE_TYPELIB, tlib)
+
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation", 1, 0)
+ assert_instance_of(WIN32OLE_TYPELIB, tlib)
+ guid = tlib.guid
+
+ tlib_by_guid = WIN32OLE_TYPELIB.new(guid, 1, 0)
+ assert_instance_of(WIN32OLE_TYPELIB, tlib_by_guid)
+ assert_equal("Microsoft Shell Controls And Automation" , tlib_by_guid.name)
+
+ path = tlib.path
+ tlib_by_path = WIN32OLE_TYPELIB.new(path)
+ assert_equal("Microsoft Shell Controls And Automation" , tlib_by_path.name)
+
+ assert_raise(WIN32OLERuntimeError) {
+ WIN32OLE_TYPELIB.new("Non Exist Type Library")
+ }
+ end
+
+ # #Bug:3907 [ruby-dev:42338]
+ def test_initialize_with_REG_EXPAND_SZ
+ tlib = WIN32OLE_TYPELIB.new("Disk Management Snap-In Object Library")
+ assert_instance_of(WIN32OLE_TYPELIB, tlib)
+ end
+
+ def test_guid
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_equal("{50A7E9B0-70EF-11D1-B75A-00A0C90564FE}", tlib.guid)
+ end
+
+ def test_name
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_equal("Microsoft Shell Controls And Automation", tlib.name)
+ tlib = WIN32OLE_TYPELIB.new("{50A7E9B0-70EF-11D1-B75A-00A0C90564FE}")
+ assert_equal("Microsoft Shell Controls And Automation", tlib.name)
+ end
+
+ def test_version
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_equal("1.0", tlib.version)
+ end
+
+ def test_major_version
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_equal(1, tlib.major_version)
+ end
+
+ def test_minor_version
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_equal(0, tlib.minor_version)
+ end
+
+ def test_path
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_match(/shell32\.dll$/i, tlib.path)
+ end
+
+ def test_visible?
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert(tlib.visible?)
+ end
+
+ def test_library_name
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_equal("Shell32", tlib.library_name)
+ end
+
+ def test_to_s
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_equal("Microsoft Shell Controls And Automation", tlib.to_s)
+ end
+
+ def test_ole_types
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ ole_types = tlib.ole_types
+ assert_instance_of(Array, ole_types)
+ assert(ole_types.size > 0)
+ assert_instance_of(WIN32OLE_TYPE, ole_types[0])
+ end
+
+ def test_inspect
+ tlib = WIN32OLE_TYPELIB.new("Microsoft Shell Controls And Automation")
+ assert_equal("#<WIN32OLE_TYPELIB:Microsoft Shell Controls And Automation>", tlib.inspect)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_variable.rb b/jni/ruby/test/win32ole/test_win32ole_variable.rb
new file mode 100644
index 0000000..5fdcb07
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_variable.rb
@@ -0,0 +1,61 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(WIN32OLE_VARIABLE)
+ class TestWIN32OLE_VARIABLE < Test::Unit::TestCase
+
+ def setup
+ ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants")
+ @var1 = ole_type.variables.find {|v| v.name == 'ssfDESKTOP'}
+
+ variables = WIN32OLE_TYPE.new("Microsoft Windows Installer Object Library", "Installer").variables
+ @var2 = variables.find {|v| v.name == 'UILevel'}
+ end
+
+ def test_name
+ assert_equal('ssfDESKTOP', @var1.name)
+ end
+
+ def test_ole_type
+ assert_equal('INT', @var1.ole_type)
+ assert_equal('MsiUILevel', @var2.ole_type)
+ end
+
+ def test_ole_type_detail
+ assert_equal(['INT'], @var1.ole_type_detail)
+ assert_equal(['USERDEFINED', 'MsiUILevel'], @var2.ole_type_detail)
+ end
+
+ def test_ole_type_value
+ assert_equal(0, @var1.value)
+ assert_equal(nil, @var2.value)
+ end
+
+ def test_ole_type_visible?
+ assert(@var1.visible?)
+ end
+
+ def test_ole_type_variable_kind
+ assert_equal("CONSTANT", @var1.variable_kind)
+ assert_equal("DISPATCH", @var2.variable_kind)
+ end
+
+ def test_ole_type_varkind
+ assert_equal(2, @var1.varkind)
+ assert_equal(3, @var2.varkind)
+ end
+
+ def test_to_s
+ assert_equal(@var1.name, @var1.to_s)
+ end
+
+ def test_inspect
+ assert_equal("#<WIN32OLE_VARIABLE:ssfDESKTOP=0>", @var1.inspect)
+ assert_equal("#<WIN32OLE_VARIABLE:UILevel=nil>", @var2.inspect)
+ end
+
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_variant.rb b/jni/ruby/test/win32ole/test_win32ole_variant.rb
new file mode 100644
index 0000000..9583a01
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_variant.rb
@@ -0,0 +1,725 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(WIN32OLE_VARIANT)
+
+ class TestWIN32OLE_VARIANT < Test::Unit::TestCase
+ def setup
+ @orglocale = WIN32OLE.locale
+ WIN32OLE.locale = 0x0409 # set locale US-Eng
+ end
+
+ def teardown
+ WIN32OLE.locale = @orglocale
+ end
+
+ def test_s_new
+ obj = WIN32OLE_VARIANT.new('foo')
+ assert_instance_of(WIN32OLE_VARIANT, obj)
+ end
+
+ def test_s_new_exc
+ assert_raise(TypeError) {
+ WIN32OLE_VARIANT.new(/foo/)
+ }
+ end
+
+ def test_s_new_ary
+ obj = WIN32OLE_VARIANT.new([1])
+ assert_instance_of(WIN32OLE_VARIANT, obj)
+ assert_raise(TypeError) {
+ WIN32OLE_VARIANT.new([/foo/])
+ }
+ end
+
+ def test_s_new_no_argument
+ ex = nil
+ begin
+ WIN32OLE_VARIANT.new
+ rescue ArgumentError
+ ex = $!
+ end
+ assert_instance_of(ArgumentError, ex)
+ assert_equal("wrong number of arguments (0 for 1..3)", ex.message);
+ end
+
+ def test_s_new_one_argument
+ ex = nil
+ begin
+ WIN32OLE_VARIANT.new('foo')
+ rescue
+ ex = $!
+ end
+ assert_equal(nil, ex);
+ end
+
+ def test_s_new_with_nil
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_I2)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I2, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_I4)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_R4)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_R4, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_R8)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_R8, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_CY)
+ assert_equal("0", obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_CY, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(1899,12,30), obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_DATE, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_BSTR)
+ assert_equal("", obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_BSTR, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_DISPATCH)
+ assert_nil(obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_DISPATCH, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_BOOL)
+ assert_equal(false, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_BOOL, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_VARIANT)
+ assert_nil(obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_VARIANT, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_I1)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I1, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UI1)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI1, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UI2)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI2, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UI4)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI4, obj.vartype)
+
+
+ if defined?(WIN32OLE::VARIANT::VT_I8)
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_I8)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I8, obj.vartype)
+ end
+
+ if defined?(WIN32OLE::VARIANT::VT_UI8)
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UI8)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI8, obj.vartype)
+ end
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_INT)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_INT, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_UINT)
+ assert_equal(0, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UINT, obj.vartype)
+ end
+
+ def test_s_new_with_non_nil
+ obj = WIN32OLE_VARIANT.new(2, WIN32OLE::VARIANT::VT_I2)
+ assert_equal(2, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I2, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(3, WIN32OLE::VARIANT::VT_I4)
+ assert_equal(3, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(4.5, WIN32OLE::VARIANT::VT_R4)
+ assert_equal(4.5, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_R4, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(5.5, WIN32OLE::VARIANT::VT_R8)
+ assert_equal(5.5, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_R8, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(600, WIN32OLE::VARIANT::VT_CY)
+ assert_equal("600", obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_CY, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new("2001-06-15 12:17:34", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(2001,06,15,12,17,34), obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_DATE, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new("foo", WIN32OLE::VARIANT::VT_BSTR)
+ assert_equal("foo", obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_BSTR, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(true, WIN32OLE::VARIANT::VT_BOOL)
+ assert_equal(true, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_BOOL, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(2, WIN32OLE::VARIANT::VT_I1)
+ assert_equal(2, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I1, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(3, WIN32OLE::VARIANT::VT_UI1)
+ assert_equal(3, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI1, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(4, WIN32OLE::VARIANT::VT_UI2)
+ assert_equal(4, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI2, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(5, WIN32OLE::VARIANT::VT_UI4)
+ assert_equal(5, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI4, obj.vartype)
+
+ if defined?(WIN32OLE::VARIANT::VT_I8)
+ obj = WIN32OLE_VARIANT.new(-123456789012345, WIN32OLE::VARIANT::VT_I8)
+ assert_equal(-123456789012345, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I8, obj.vartype)
+ end
+
+ if defined?(WIN32OLE::VARIANT::VT_UI8)
+ obj = WIN32OLE_VARIANT.new(123456789012345, WIN32OLE::VARIANT::VT_UI8)
+ assert_equal(123456789012345, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI8, obj.vartype)
+ end
+
+ obj = WIN32OLE_VARIANT.new(4, WIN32OLE::VARIANT::VT_INT)
+ assert_equal(4, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_INT, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(5, WIN32OLE::VARIANT::VT_UINT)
+ assert_equal(5, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UINT, obj.vartype)
+ end
+
+ def test_s_new_with_non_nil_byref
+ obj = WIN32OLE_VARIANT.new(2, WIN32OLE::VARIANT::VT_I2|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(2, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I2|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(3, WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(3, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(4.5, WIN32OLE::VARIANT::VT_R4|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(4.5, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_R4|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(5.5, WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(5.5, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(600, WIN32OLE::VARIANT::VT_CY|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal("600", obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_CY|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new("2001-06-15 12:17:34", WIN32OLE::VARIANT::VT_DATE|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(Time.new(2001,06,15,12,17,34), obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_DATE|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new("foo", WIN32OLE::VARIANT::VT_BSTR|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal("foo", obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_BSTR|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(true, WIN32OLE::VARIANT::VT_BOOL|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(true, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_BOOL|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(2, WIN32OLE::VARIANT::VT_I1|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(2, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I1|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(3, WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(3, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(4, WIN32OLE::VARIANT::VT_UI2|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(4, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI2|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(5, WIN32OLE::VARIANT::VT_UI4|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(5, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI4|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(4, WIN32OLE::VARIANT::VT_INT|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(4, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_INT|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new(5, WIN32OLE::VARIANT::VT_UINT|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(5, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UINT|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+ end
+
+ def test_s_new_with_i8_byref
+ obj = WIN32OLE_VARIANT.new(-123456789012345, WIN32OLE::VARIANT::VT_I8|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(-123456789012345, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I8|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+ end
+
+ def test_s_new_with_ui8_byref
+ obj = WIN32OLE_VARIANT.new(123456789012345, WIN32OLE::VARIANT::VT_UI8|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(123456789012345, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_UI8|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+ end
+
+ def test_value
+ obj = WIN32OLE_VARIANT.new('foo')
+ assert_equal('foo', obj.value)
+ end
+
+ def test_s_new_2_argument
+ obj = WIN32OLE_VARIANT.new('foo', WIN32OLE::VARIANT::VT_BSTR|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal('foo', obj.value);
+ end
+
+ def test_s_new_2_argument2
+ obj = WIN32OLE_VARIANT.new('foo', WIN32OLE::VARIANT::VT_BSTR)
+ assert_equal('foo', obj.value);
+ end
+
+ def test_s_new_dispatch_array
+ vt = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_DISPATCH
+ obj = WIN32OLE_VARIANT.new(nil, vt)
+ assert_equal(vt, obj.vartype)
+ assert_nil(obj.value)
+
+ vt = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_DISPATCH|WIN32OLE::VARIANT::VT_BYREF
+ obj = WIN32OLE_VARIANT.new(nil, vt)
+ assert_equal(vt, obj.vartype)
+ assert_nil(obj.value)
+ end
+
+ def test_s_new_array
+ # should not occur stack over flow
+ ar = (1..500000).to_a.map{|i| [i]}
+ ar2 = WIN32OLE_VARIANT.new(ar)
+ assert_equal(ar, ar2.value)
+ end
+
+ def test_s_new_vt_record_exc
+ # VT_RECORD (= 36) should not be allowed in WIN32OLE_VARIANT#new
+ assert_raise(ArgumentError) {
+ WIN32OLE_VARIANT.new(nil, 36)
+ }
+ end
+
+ def test_s_array
+ obj = WIN32OLE_VARIANT.array([2,3], WIN32OLE::VARIANT::VT_I4)
+ assert_instance_of(WIN32OLE_VARIANT, obj)
+ assert_equal(WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_ARRAY, obj.vartype)
+ assert_equal([[0, 0, 0],[0, 0, 0]], obj.value)
+
+ obj = WIN32OLE_VARIANT.array([2,3], WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF|WIN32OLE::VARIANT::VT_ARRAY, obj.vartype)
+ assert_equal([[0, 0, 0],[0, 0, 0]], obj.value)
+
+ obj = WIN32OLE_VARIANT.array([2,3], WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_ARRAY)
+ assert_instance_of(WIN32OLE_VARIANT, obj)
+ assert_equal(WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_ARRAY, obj.vartype)
+ assert_equal([[0, 0, 0],[0, 0, 0]], obj.value)
+
+ assert_equal(0, obj[0,0])
+ obj[0,0] = 10
+ assert_equal([[10, 0, 0],[0, 0, 0]], obj.value)
+ obj[0,1] = "13.2"
+ assert_equal([[10, 13, 0],[0, 0, 0]], obj.value)
+
+ obj = WIN32OLE_VARIANT.array([3, 2], WIN32OLE::VARIANT::VT_VARIANT)
+ obj[0,0] = 10
+ obj[0,1] = "string"
+ obj[1,0] = 12.735
+ assert_equal([[10, "string"],[12.735, nil],[nil,nil]], obj.value)
+
+ obj = WIN32OLE_VARIANT.array([2,3], WIN32OLE::VARIANT::VT_DISPATCH)
+ assert_equal([[nil, nil, nil],[nil,nil,nil]], obj.value)
+
+ end
+
+ def test_s_array_exc
+ assert_raise(TypeError) {
+ WIN32OLE_VARIANT.array(2, WIN32OLE::VARIANT::VT_I4)
+ }
+ end
+
+ def test_conversion_num2str
+ obj = WIN32OLE_VARIANT.new(124, WIN32OLE::VARIANT::VT_BSTR)
+ assert_equal("124", obj.value);
+ end
+
+ def test_conversion_float2int
+ obj = WIN32OLE_VARIANT.new(12.345, WIN32OLE::VARIANT::VT_I4)
+ assert_equal(12, obj.value)
+ obj = WIN32OLE_VARIANT.new(12.345, WIN32OLE::VARIANT::VT_I4|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(12, obj.value)
+ end
+
+ def test_conversion_str2num
+ obj = WIN32OLE_VARIANT.new("12.345", WIN32OLE::VARIANT::VT_R8)
+ assert_equal(12.345, obj.value)
+ end
+
+ def test_conversion_ole_variant2ole_variant
+ obj = WIN32OLE_VARIANT.new("12.345", WIN32OLE::VARIANT::VT_R4)
+ obj = WIN32OLE_VARIANT.new(obj, WIN32OLE::VARIANT::VT_I4)
+ assert_equal(12, obj.value)
+ end
+
+ def test_conversion_str2date
+ obj = WIN32OLE_VARIANT.new("2004-12-24 12:24:45", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(2004,12,24,12,24,45), obj.value)
+ end
+
+ def test_conversion_time2date
+ dt = Time.mktime(2004, 12, 24, 12, 24, 45)
+ obj = WIN32OLE_VARIANT.new(dt, WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(dt, obj.value)
+ end
+
+ def test_conversion_dbl2date_with_msec
+ # Date is "2014/8/27 12:34:56.789"
+ obj = WIN32OLE_VARIANT.new(41878.524268391200167, WIN32OLE::VARIANT::VT_DATE)
+ t = obj.value
+ assert_equal("2014-08-27 12:34:56", t.strftime('%Y-%m-%d %H:%M:%S'))
+ assert_in_delta(0.789, t.nsec / 1000000000.0, 0.001)
+ end
+
+ def test_conversion_time2date_with_msec
+ t0 = Time.new(2014, 8, 27, 12, 34, 56)
+ t0 += 0.789
+ t1 = WIN32OLE_VARIANT.new(t0).value
+
+ # The t0.nsec is 789000000 and t1.nsec is 789000465
+ # because of error range by conversion Time between VT_DATE Variant.
+ # So check t1 and t0 are in error by less than one millisecond.
+ msg = "Expected:#{t0.strftime('%Y-%m-%dT%H:%M:%S.%N')} but was:#{t1.strftime('%Y-%m-%dT%H:%M:%S.%N')}"
+ assert_in_delta(t0, t1, 0.001, msg)
+
+ t0 = Time.new(2014, 8, 27, 12, 34, 56)
+ t0 += 0.999999999
+ t1 = WIN32OLE_VARIANT.new(t0).value
+ msg = "Expected:#{t0.strftime('%Y-%m-%dT%H:%M:%S.%N')} but was:#{t1.strftime('%Y-%m-%dT%H:%M:%S.%N')}"
+
+ # The t0 is "2014/08/27 12:34.56.999999999" and
+ # the t1 is "2014/08/27 12:34:57.000000628"
+ assert_in_delta(t0, t1, 0.001, msg)
+
+ t0 = Time.now
+ t1 = WIN32OLE_VARIANT.new(t0).value
+ msg = "Expected:#{t0.strftime('%Y-%m-%dT%H:%M:%S.%N')} but was:#{t1.strftime('%Y-%m-%dT%H:%M:%S.%N')}"
+ assert_in_delta(t0, t1, 0.001, msg)
+ end
+
+ # this test failed because of VariantTimeToSystemTime
+ # and SystemTimeToVariantTime API ignores wMilliseconds
+ # member of SYSTEMTIME struct.
+ #
+ # def test_conversion_time_nsec2date
+ # dt = Time.new(2004, 12,24, 12, 24, 45)
+ # dt += 0.1
+ # obj = WIN32OLE_VARIANT.new(dt, WIN32OLE::VARIANT::VT_DATE)
+ # assert_equal(dt, obj.value)
+ # end
+
+ def test_conversion_str2cy
+ begin
+ WIN32OLE.locale = 0x0411 # set locale Japanese
+ rescue WIN32OLERuntimeError
+ skip("Japanese locale is not installed")
+ end
+ if WIN32OLE.locale == 0x0411
+ obj = WIN32OLE_VARIANT.new("\\10,000", WIN32OLE::VARIANT::VT_CY)
+ assert_equal("10000", obj.value)
+ end
+ end
+
+ def test_create_vt_array
+ obj = WIN32OLE_VARIANT.new([1.2, 2.3], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8)
+ assert_equal([1.2, 2.3], obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new([1.2, 2.3], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal([1.2, 2.3], obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_R8|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+ end
+
+ def test_create_vt_array2
+ obj = WIN32OLE_VARIANT.new([1.2, "a"], WIN32OLE::VARIANT::VT_ARRAY)
+ assert_equal([1.2, "a"], obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_VARIANT, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new([1.2, "a"])
+ assert_equal([1.2, "a"], obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_VARIANT, obj.vartype)
+ end
+
+
+ def test_create_vt_nested_array
+ obj = WIN32OLE_VARIANT.new([[1.2, "a", "b"], [3.4, "C", "D"]], WIN32OLE::VARIANT::VT_ARRAY)
+ assert_equal([[1.2, "a", "b"], [3.4, "C", "D"]], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([[1.2, "a", "b"], [3.4, "C", "D"]])
+ assert_equal([[1.2, "a", "b"], [3.4, "C", "D"]], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([[1.2, "a", "b"], [3.4, "C", "D"], [5.6, "E", "F"]])
+ assert_equal([[1.2, "a", "b"], [3.4, "C", "D"], [5.6, "E", "F"]], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([[[1.2], [3.4]], [[5.6], [7.8]], [[9.1],[9.2]]])
+ assert_equal([[[1.2], [3.4]], [[5.6], [7.8]], [[9.1],[9.2]]], obj.value)
+ end
+
+ def test_create_vt_array3
+ obj = WIN32OLE_VARIANT.new([])
+ assert_equal([], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([[]])
+ assert_equal([[]], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([[],[]])
+ assert_equal([[],[]], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal([], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([[]], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal([[]], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([[],[]], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal([[],[]], obj.value)
+ end
+
+ def test_create_vt_array_nil
+ vartype = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_DISPATCH|WIN32OLE::VARIANT::VT_BYREF
+ obj = WIN32OLE_VARIANT.new(nil, vartype)
+ assert_nil(obj.value)
+ assert_equal(vartype, obj.vartype)
+
+ vartype = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_DISPATCH
+ obj = WIN32OLE_VARIANT.new(nil, vartype)
+ assert_nil(obj.value)
+ assert_equal(vartype, obj.vartype)
+ end
+
+ def test_create_vt_array_str
+ vartype = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BSTR
+ obj = WIN32OLE_VARIANT.new(["abc", "123"], vartype)
+ assert_equal(vartype, obj.vartype)
+ assert_equal(["abc", "123"], obj.value)
+
+ vartype = WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_BYREF|WIN32OLE::VARIANT::VT_BSTR
+ obj = WIN32OLE_VARIANT.new(["abc", "123"], vartype)
+ assert_equal(vartype, obj.vartype)
+ assert_equal(["abc", "123"], obj.value)
+ end
+
+ def test_create_vt_array_exc
+ exc = assert_raise(TypeError) {
+ WIN32OLE_VARIANT.new(1, WIN32OLE::VARIANT::VT_ARRAY);
+ }
+ assert_match(/wrong argument type Fixnum \(expected Array\)/, exc.message)
+ end
+
+ def test_create_vt_array_str2ui1array
+ obj = WIN32OLE_VARIANT.new("ABC", WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
+ assert_equal("ABC", obj.value)
+
+ obj.value = "DEF"
+ assert_equal("DEF", obj.value)
+ obj[0] = 71
+ assert_equal("GEF", obj.value)
+
+ obj = WIN32OLE_VARIANT.new([65, 0].pack("C*"), WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
+ assert_equal([65, 0].pack("C*"), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("abc", WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal("abc", obj.value)
+ obj.value = "DEF"
+ assert_equal("DEF", obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1|WIN32OLE::VARIANT::VT_BYREF, obj.vartype)
+ obj[1] = 71
+ assert_equal("DGF", obj.value)
+
+ end
+
+ def test_create_vt_array_int
+ obj = WIN32OLE_VARIANT.new([65, 0], WIN32OLE::VARIANT::VT_ARRAY|WIN32OLE::VARIANT::VT_UI1)
+ assert_equal([65, 0].pack("C*"), obj.value)
+
+ obj = WIN32OLE_VARIANT.new([65, 0])
+ assert_equal([65, 0], obj.value)
+
+ obj = WIN32OLE_VARIANT.new([65, 0], WIN32OLE::VARIANT::VT_I2|WIN32OLE::VARIANT::VT_ARRAY)
+ assert_equal([65, 0], obj.value)
+
+ end
+
+ def test_vt_array_bracket
+ obj = WIN32OLE_VARIANT.new([[1,2,3],[4,5,6]])
+ assert_equal(1, obj[0,0])
+ assert_equal(2, obj[0,1])
+ assert_equal(3, obj[0,2])
+ assert_equal(4, obj[1,0])
+ assert_equal(5, obj[1,1])
+ assert_equal(6, obj[1,2])
+
+ assert_raise(WIN32OLERuntimeError) {
+ obj[0,4]
+ }
+ assert_raise(WIN32OLERuntimeError) {
+ obj[0,-1]
+ }
+ assert_raise(ArgumentError) {
+ obj[0]
+ }
+
+ obj[0,0] = 7
+ obj[1,2] = 8
+ assert_equal([[7,2,3], [4,5,8]], obj.value)
+
+ assert_raise(WIN32OLERuntimeError) {
+ obj[0,4] = 9
+ }
+ assert_raise(WIN32OLERuntimeError) {
+ obj[0,-1] = 10
+ }
+ assert_raise(ArgumentError) {
+ obj[0] = 11
+ }
+ end
+
+ def test_conversion_vt_date
+ obj = WIN32OLE_VARIANT.new(-657434, WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(100,1,1), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("1500/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(1500,12,29,23,59,59), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("1500/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(1500,12,30), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("1500/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(1500,12,30,0,0,1), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("1899/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(1899,12,29,23,59,59), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("1899/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(1899,12,30), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("1899/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(1899,12,30,0,0,1), obj.value)
+
+ obj = WIN32OLE_VARIANT.new(0, WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(1899,12,30), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("2008/12/29 23:59:59", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(2008,12,29,23,59,59), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("2008/12/30 00:00:00", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(2008,12,30,0,0,0), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("2008/12/30 00:00:01", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(2008,12,30,0,0,1), obj.value)
+
+ obj = WIN32OLE_VARIANT.new("9999/12/31 23:59:59", WIN32OLE::VARIANT::VT_DATE)
+ assert_equal(Time.new(9999,12,31,23,59,59), obj.value)
+ end
+
+ def test_create_nil_dispatch
+ var = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_DISPATCH)
+ assert_nil(var.value)
+ end
+
+ def test_create_variant_byref
+ obj = WIN32OLE_VARIANT.new("Str", WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF);
+ assert_equal("Str", obj.value);
+ end
+
+ def test_vartype
+ obj = WIN32OLE_VARIANT.new("Str")
+ assert_equal(WIN32OLE::VARIANT::VT_BSTR, obj.vartype)
+ end
+
+ def test_set_value
+ obj = WIN32OLE_VARIANT.new(10)
+ obj.value = 12
+ assert_equal(12, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
+ obj.value = "14"
+ assert_equal(14, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
+ obj.value = 11.2
+ assert_equal(11, obj.value)
+ assert_equal(WIN32OLE::VARIANT::VT_I4, obj.vartype)
+
+ obj = WIN32OLE_VARIANT.new([1,2])
+ assert_raise(WIN32OLERuntimeError) {
+ obj.value = [3,4]
+ }
+
+ obj = WIN32OLE_VARIANT.new("2007/01/01", WIN32OLE::VARIANT::VT_DATE)
+ assert_raise(WIN32OLERuntimeError) {
+ obj.value = "hogehoge"
+ }
+ assert_equal(Time.new(2007,1,1), obj.value)
+
+ obj2 = WIN32OLE_VARIANT.new("2006/01/01", WIN32OLE::VARIANT::VT_DATE)
+ obj.value = obj2
+ assert_equal(Time.new(2006,01,01), obj.value)
+ end
+
+ def test_c_nothing
+ assert_nil(WIN32OLE_VARIANT::Nothing.value)
+ end
+
+ def test_c_empty
+ assert_nil(WIN32OLE_VARIANT::Empty.value)
+ end
+
+ def test_c_null
+ assert_nil(WIN32OLE_VARIANT::Null.value)
+ end
+
+ def test_c_noparam
+ # DISP_E_PARAMNOTFOUND
+ assert_equal(-2147352572, WIN32OLE_VARIANT::NoParam.value)
+ end
+
+ def test_vt_error_noparam
+ v = WIN32OLE_VARIANT.new(-1, WIN32OLE::VARIANT::VT_ERROR)
+ assert_equal(-1, v.value)
+ fso = WIN32OLE.new("Scripting.FileSystemObject")
+ exc = assert_raise(WIN32OLERuntimeError) {
+ fso.openTextFile("NonExistingFile", v, false)
+ }
+ assert_match(/Type mismatch/i, exc.message)
+ exc = assert_raise(WIN32OLERuntimeError) {
+ fso.openTextFile("NonExistingFile", WIN32OLE_VARIANT::NoParam, false)
+ }
+ # 800A0035 is 'file not found' error.
+ assert_match(/800A0035/, exc.message)
+
+ # -2147352572 is DISP_E_PARAMNOTFOUND
+ v = WIN32OLE_VARIANT.new(-2147352572, WIN32OLE::VARIANT::VT_ERROR)
+ exc = assert_raise(WIN32OLERuntimeError) {
+ fso.openTextFile("NonExistingFile", WIN32OLE_VARIANT::NoParam, false)
+ }
+ # 800A0035 is 'file not found' error code.
+ assert_match(/800A0035/, exc.message)
+ end
+
+ end
+end
+
diff --git a/jni/ruby/test/win32ole/test_win32ole_variant_m.rb b/jni/ruby/test/win32ole/test_win32ole_variant_m.rb
new file mode 100644
index 0000000..b3e48c2
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_variant_m.rb
@@ -0,0 +1,35 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+if defined?(WIN32OLE::VARIANT)
+ class TestWin32OLE_VARIANT_MODULE < Test::Unit::TestCase
+ include WIN32OLE::VARIANT
+ def test_variant
+ assert_equal(0, VT_EMPTY)
+ assert_equal(1, VT_NULL)
+ assert_equal(2, VT_I2)
+ assert_equal(3, VT_I4)
+ assert_equal(4, VT_R4)
+ assert_equal(5, VT_R8)
+ assert_equal(6, VT_CY)
+ assert_equal(7, VT_DATE)
+ assert_equal(8, VT_BSTR)
+ assert_equal(9, VT_DISPATCH)
+ assert_equal(10, VT_ERROR)
+ assert_equal(11, VT_BOOL)
+ assert_equal(12, VT_VARIANT)
+ assert_equal(13, VT_UNKNOWN)
+ assert_equal(16, VT_I1)
+ assert_equal(17, VT_UI1)
+ assert_equal(18, VT_UI2)
+ assert_equal(19, VT_UI4)
+ assert_equal(22, VT_INT)
+ assert_equal(23, VT_UINT)
+ assert_equal(0x2000, VT_ARRAY)
+ assert_equal(0x4000, VT_BYREF)
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_win32ole_variant_outarg.rb b/jni/ruby/test/win32ole/test_win32ole_variant_outarg.rb
new file mode 100644
index 0000000..ffc8e06
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_win32ole_variant_outarg.rb
@@ -0,0 +1,68 @@
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require 'test/unit'
+require 'fileutils'
+
+def ado_csv_installed?
+ installed = false
+ if defined?(WIN32OLE)
+ db = nil
+ begin
+ db = WIN32OLE.new('ADODB.Connection')
+ db.connectionString = "Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
+ db.open
+ db.close
+ db = nil
+ installed = true
+ rescue
+ end
+ end
+ installed
+end
+
+if defined?(WIN32OLE_VARIANT)
+ class TestWIN32OLE_VARIANT_OUTARG < Test::Unit::TestCase
+ module ADO
+ end
+ CONNSTR="Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
+ def setup
+ return if !ado_csv_installed?
+
+ FileUtils.cp(File.dirname(__FILE__) + '/orig_data.csv', './data.csv')
+ @db = WIN32OLE.new('ADODB.Connection')
+ if !defined?(ADO::AdStateOpen)
+ WIN32OLE.const_load(@db, ADO)
+ end
+ @db.connectionString = CONNSTR
+ @db.open
+ end
+
+ def test_variant_ref_and_argv
+ if !ado_csv_installed?
+ skip("ActiveX Data Object Library not found")
+ end
+ sql = "INSERT INTO data.csv VALUES (5, 'E')"
+ @db.execute(sql, -1)
+ c = WIN32OLE::ARGV[1]
+ assert_equal(1, c)
+ obj = WIN32OLE_VARIANT.new(nil, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(nil, obj.value)
+ @db.execute(sql , obj)
+ assert_equal(1, obj.value)
+ obj = WIN32OLE_VARIANT.new(-100, WIN32OLE::VARIANT::VT_VARIANT|WIN32OLE::VARIANT::VT_BYREF)
+ assert_equal(-100, obj.value)
+ @db.execute(sql, obj)
+ assert_equal(1, obj.value)
+ end
+
+ def teardown
+ return if !ado_csv_installed?
+ if @db && @db.state == ADO::AdStateOpen
+ @db.close
+ end
+ File.unlink("data.csv")
+ end
+ end
+end
diff --git a/jni/ruby/test/win32ole/test_word.rb b/jni/ruby/test/win32ole/test_word.rb
new file mode 100644
index 0000000..acfa214
--- /dev/null
+++ b/jni/ruby/test/win32ole/test_word.rb
@@ -0,0 +1,71 @@
+#
+# This is test for [ruby-Bugs#3237]
+#
+begin
+ require 'win32ole'
+rescue LoadError
+end
+require "test/unit"
+
+def word_installed?
+ installed = false
+ w = nil
+ if defined?(WIN32OLE)
+ begin
+ w = WIN32OLE.new('Word.Application')
+ installed = true
+ rescue
+ ensure
+ if w
+ w.quit
+ w = nil
+ end
+ end
+ end
+ return installed
+end
+
+if defined?(WIN32OLE)
+ dotest = word_installed?
+ if !dotest
+ STDERR.puts("\n#{__FILE__} skipped(Microsoft Word not found.)")
+ end
+ if dotest
+ class TestWIN32OLE_WITH_WORD < Test::Unit::TestCase
+ def setup
+ begin
+ @obj = WIN32OLE.new('Word.Application')
+ rescue WIN32OLERuntimeError
+ @obj = nil
+ if !$skipped
+ $skipped = true
+ end
+ end
+ end
+
+ def test_ole_methods
+ if @obj
+ @obj.visible = true
+ @obj.wordbasic.disableAutoMacros(true)
+ assert(true)
+ else
+ end
+ end
+
+ def test_s_connect
+ if @obj
+ obj2 = WIN32OLE.connect("Word.Application")
+ assert_instance_of(WIN32OLE, obj2)
+ obj2.visible = true
+ end
+ end
+
+ def teardown
+ if @obj
+ @obj.quit
+ @obj = nil
+ end
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/xmlrpc/data/blog.xml b/jni/ruby/test/xmlrpc/data/blog.xml
new file mode 100644
index 0000000..cb325f1
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/blog.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<methodResponse>
+ <params>
+ <param>
+ <value>
+ <array><data>
+ <value><struct>
+ <member><name>isAdmin</name><value><boolean>1</boolean></value></member>
+ <member><name>url</name><value><string>http://tenderlovemaking.com/</string></value></member>
+ <member><name>blogid</name><value><string>1</string></value></member>
+ <member><name>blogName</name><value><string>Tender Lovemaking</string></value></member>
+ <member><name>xmlrpc</name><value><string>http://tenderlovemaking.com/xmlrpc.php</string></value></member>
+</struct></value>
+</data></array>
+ </value>
+ </param>
+ </params>
+</methodResponse>
diff --git a/jni/ruby/test/xmlrpc/data/bug_bool.expected b/jni/ruby/test/xmlrpc/data/bug_bool.expected
new file mode 100644
index 0000000..121e3a8
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/bug_bool.expected
@@ -0,0 +1,3 @@
+---
+- true
+- false \ No newline at end of file
diff --git a/jni/ruby/test/xmlrpc/data/bug_bool.xml b/jni/ruby/test/xmlrpc/data/bug_bool.xml
new file mode 100644
index 0000000..04ed007
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/bug_bool.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<methodResponse>
+ <params>
+ <param>
+ <value><boolean>0</boolean></value>
+ </param>
+ </params>
+</methodResponse>
diff --git a/jni/ruby/test/xmlrpc/data/bug_cdata.expected b/jni/ruby/test/xmlrpc/data/bug_cdata.expected
new file mode 100644
index 0000000..17d7861
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/bug_cdata.expected
@@ -0,0 +1,3 @@
+---
+- true
+- test \ No newline at end of file
diff --git a/jni/ruby/test/xmlrpc/data/bug_cdata.xml b/jni/ruby/test/xmlrpc/data/bug_cdata.xml
new file mode 100644
index 0000000..ba990e0
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/bug_cdata.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<methodResponse>
+ <params>
+ <param>
+ <value><string><![CDATA[test]]></string></value>
+ </param>
+ </params>
+</methodResponse>
diff --git a/jni/ruby/test/xmlrpc/data/bug_covert.expected b/jni/ruby/test/xmlrpc/data/bug_covert.expected
new file mode 100644
index 0000000..a9ac103
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/bug_covert.expected
@@ -0,0 +1,10 @@
+---
+- true
+- >
+ Site,SANs,Array
+
+ Configured Capacity,Array Reserved Capacity,Array Ava
+
+ ilable Capacity,Array % Reserved,Host Allocated,Host Used,Host Free,Host %
+
+ Used
diff --git a/jni/ruby/test/xmlrpc/data/bug_covert.xml b/jni/ruby/test/xmlrpc/data/bug_covert.xml
new file mode 100644
index 0000000..1d9abd2
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/bug_covert.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"
+encoding="ISO-8859-1"?><methodResponse><params><param><value>Site,SANs,Array
+Configured Capacity,Array Reserved Capacity,Array Ava
+ilable Capacity,Array % Reserved,Host Allocated,Host Used,Host Free,Host %
+Used
+</value></param></params></methodResponse>
diff --git a/jni/ruby/test/xmlrpc/data/datetime_iso8601.xml b/jni/ruby/test/xmlrpc/data/datetime_iso8601.xml
new file mode 100644
index 0000000..43d8da6
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/datetime_iso8601.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<methodResponse>
+ <params>
+ <param>
+ <value><dateTime.iso8601>20041105T01:15:23Z</dateTime.iso8601></value>
+ </param>
+ </params>
+</methodResponse>
diff --git a/jni/ruby/test/xmlrpc/data/fault.xml b/jni/ruby/test/xmlrpc/data/fault.xml
new file mode 100644
index 0000000..041c464
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/fault.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<methodResponse>
+ <fault>
+ <value><struct>
+ <member>
+ <name>faultCode</name>
+ <value><int>4</int></value>
+ </member>
+ <member>
+ <name>faultString</name>
+ <value>an error message</value>
+ </member>
+ </struct></value>
+ </fault>
+</methodResponse>
+
diff --git a/jni/ruby/test/xmlrpc/data/value.expected b/jni/ruby/test/xmlrpc/data/value.expected
new file mode 100644
index 0000000..9463d02
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/value.expected
@@ -0,0 +1,7 @@
+---
+- Test
+-
+ - Hallo Leute
+ - " Hallo "
+ - ''
+ - " " \ No newline at end of file
diff --git a/jni/ruby/test/xmlrpc/data/value.xml b/jni/ruby/test/xmlrpc/data/value.xml
new file mode 100644
index 0000000..1978616
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/value.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<methodCall>
+ <methodName>Test</methodName>
+ <params>
+ <param>
+ <value>Hallo Leute</value>
+ </param>
+ <param>
+ <value> Hallo </value>
+ </param>
+ <param>
+ <value></value>
+ </param>
+ <param>
+ <value> </value>
+ </param>
+ </params>
+</methodCall>
+
+
+
+
diff --git a/jni/ruby/test/xmlrpc/data/xml1.expected b/jni/ruby/test/xmlrpc/data/xml1.expected
new file mode 100644
index 0000000..ff96de8
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/xml1.expected
@@ -0,0 +1,243 @@
+---
+- true
+-
+ -
+ subscriber: MegaCorp
+ lastName: Baker
+ telephone1: 1-508-791-1267
+ telephone2: 1-800-445-2588
+ password: p1111
+ OID: "1"
+ email: hbaker@yahoo.com
+ adminId: hbaker
+ objectName: AdministratorDO
+ -
+ subscriber: CornerStore
+ lastName: Dragon
+ telephone1: 1-781-789-9089
+ telephone2: 1-800-445-2588
+ password: p3333
+ OID: "3"
+ email: adragon@yahoo.com
+ adminId: adragon
+ objectName: AdministratorDO
+ -
+ subscriber: Cyberdyne
+ lastName: Rodman
+ telephone1: 1-617-789-1890
+ telephone2: 1-800-445-2588
+ password: p4444
+ OID: "4"
+ email: mrodman@yahoo.com
+ adminId: mrodman
+ objectName: AdministratorDO
+ -
+ subscriber: StarSports
+ lastName: Jordan
+ telephone1: 1-617-890-7897
+ telephone2: 1-800-445-2588
+ password: p5555
+ OID: "5"
+ email: mjordan@yahoo.com
+ adminId: mjordan
+ objectName: AdministratorDO
+ -
+ subscriber: GreatBooks
+ lastName: Pippen
+ telephone1: 1-781-789-9876
+ telephone2: 1-800-445-2588
+ password: p6666
+ OID: "6"
+ email: gpippen@yahoo.com
+ adminId: gpippen
+ objectName: AdministratorDO
+ -
+ subscriber: AxisChemicals
+ lastName: Andhrew
+ telephone1: 1-781-678-8970
+ telephone2: 1-800-445-2588
+ password: p7777
+ OID: "7"
+ email: aandrew@yahoo.com
+ adminId: aandrew
+ objectName: AdministratorDO
+ -
+ subscriber: MediaShop
+ lastName: Vincent
+ telephone1: 1-786-897-8908
+ telephone2: 1-800-445-2588
+ password: p8888
+ OID: "8"
+ email: tvincent@yahoo.com
+ adminId: tvincent
+ objectName: AdministratorDO
+ -
+ subscriber: SmartShop
+ lastName: Richard
+ telephone1: 1-508-789-6789
+ telephone2: 1-800-445-2588
+ password: p9999
+ OID: "9"
+ email: krichard@yahoo.com
+ adminId: krichard
+ objectName: AdministratorDO
+ -
+ subscriber: HomeNeeds
+ lastName: Cornell
+ telephone1: 1-617-789-8979
+ telephone2: 1-800-445-2588
+ password: paaaa
+ OID: "10"
+ email: gconell@yahoo.com
+ adminId: gcornell
+ objectName: AdministratorDO
+ -
+ subscriber: MegaCorp
+ lastName: HorstMann
+ telephone1: 1-508-791-1267
+ telephone2: 1-800-445-2588
+ password: p1111
+ OID: "11"
+ email: shorstmann@yahoo.com
+ adminId: shorstmann
+ objectName: AdministratorDO
+ -
+ subscriber: CornerStore
+ lastName: Bob
+ telephone1: 1-781-789-9089
+ telephone2: 1-800-445-2588
+ password: p3333
+ OID: "13"
+ email: rbob@yahoo.com
+ adminId: rbob
+ objectName: AdministratorDO
+ -
+ subscriber: Cyberdyne
+ lastName: Peter
+ telephone1: 1-617-789-1890
+ telephone2: 1-800-445-2588
+ password: p4444
+ OID: "14"
+ email: speter@yahoo.com
+ adminId: speter
+ objectName: AdministratorDO
+ -
+ subscriber: StarSports
+ lastName: Novak
+ telephone1: 1-617-890-7897
+ telephone2: 1-800-445-2588
+ password: p5555
+ OID: "15"
+ email: pnovak@yahoo.com
+ adminId: pnovak
+ objectName: AdministratorDO
+ -
+ subscriber: GreatBooks
+ lastName: Nancy
+ telephone1: 1-781-789-9876
+ telephone2: 1-800-445-2588
+ password: p6666
+ OID: "16"
+ email: pnancy@yahoo.com
+ adminId: pnancy
+ objectName: AdministratorDO
+ -
+ subscriber: AxisChemicals
+ lastName: Michel
+ telephone1: 1-781-678-8970
+ telephone2: 1-800-445-2588
+ password: p7777
+ OID: "17"
+ email: hmichel@yahoo.com
+ adminId: hmichel
+ objectName: AdministratorDO
+ -
+ subscriber: MediaShop
+ lastName: David
+ telephone1: 1-786-897-8908
+ telephone2: 1-800-445-2588
+ password: p8888
+ OID: "18"
+ email: kdavid@yahoo.com
+ adminId: kdavid
+ objectName: AdministratorDO
+ -
+ subscriber: SmartShop
+ lastName: Valnoor
+ telephone1: 1-508-789-6789
+ telephone2: 1-800-445-2588
+ password: p9999
+ OID: "19"
+ email: pvalnoor@yahoo.com
+ adminId: pvalnoor
+ objectName: AdministratorDO
+ -
+ subscriber: HomeNeeds
+ lastName: Smith
+ telephone1: 1-617-789-8979
+ telephone2: 1-800-445-2588
+ password: paaaa
+ OID: "20"
+ email: wsmith@yahoo.com
+ adminId: wsmith
+ objectName: AdministratorDO
+ -
+ subscriber: MegaCorp
+ lastName: Caral
+ telephone1: 1-781-789-9876
+ telephone2: 1-800-445-2588
+ password: p6666
+ OID: "21"
+ email: gcaral@yahoo.com
+ adminId: gcaral
+ objectName: AdministratorDO
+ -
+ subscriber: CornerStore
+ lastName: Hillary
+ telephone1: 1-786-897-8908
+ telephone2: 1-800-445-2588
+ password: p8888
+ OID: "23"
+ email: phillary@yahoo.com
+ adminId: phillary
+ objectName: AdministratorDO
+ -
+ subscriber: Cyberdyne
+ lastName: Philip
+ telephone1: 1-508-789-6789
+ telephone2: 1-800-445-2588
+ password: p9999
+ OID: "24"
+ email: bphilip@yahoo.com
+ adminId: bphilip
+ objectName: AdministratorDO
+ -
+ subscriber: StarSports
+ lastName: Andrea
+ telephone1: 1-617-789-8979
+ telephone2: 1-800-445-2588
+ password: paaaa
+ OID: "25"
+ email: sandrea@yahoo.com
+ adminId: sandrea
+ objectName: AdministratorDO
+ -
+ subscriber: s4
+ lastName: "null"
+ telephone1: "null"
+ telephone2: "null"
+ password: s4
+ OID: "26"
+ email: "null"
+ adminId: s4
+ objectName: AdministratorDO
+ -
+ subscriber: BigBank
+ lastName: administrator
+ telephone1: ''
+ telephone2: ''
+ password: admin
+ OID: "82"
+ email: ''
+ adminId: admin
+ objectName: AdministratorDO \ No newline at end of file
diff --git a/jni/ruby/test/xmlrpc/data/xml1.xml b/jni/ruby/test/xmlrpc/data/xml1.xml
new file mode 100644
index 0000000..10aa554
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/data/xml1.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="ISO-8859-1"?><methodResponse><params><param><value><array><data><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>hbaker</value></member><member><name>email</name><value>hbaker@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-508-791-1267</value></member><member><name>OID</name><value>1</value></member><member><name>password</name><value>p1111</value></member><member><name>lastName</name><value>Baker</value></member><member><name>subscriber</name><value>MegaCorp</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>adragon</value></member><member><name>email</name><value>adragon@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-781-789-9089</value></member><member><name>OID</name><value>3</value></member><member><name>password</name><value>p3333</value></member><member><name>lastName</name><value>Dragon</value></member><member><name>subscriber</name><value>CornerStore</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>mrodman</value></member><member><name>email</name><value>mrodman@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-617-789-1890</value></member><member><name>OID</name><value>4</value></member><member><name>password</name><value>p4444</value></member><member><name>lastName</name><value>Rodman</value></member><member><name>subscriber</name><value>Cyberdyne</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>mjordan</value></member><member><name>email</name><value>mjordan@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-617-890-7897</value></member><member><name>OID</name><value>5</value></member><member><name>password</name><value>p5555</value></member><member><name>lastName</name><value>Jordan</value></member><member><name>subscriber</name><value>StarSports</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>gpippen</value></member><member><name>email</name><value>gpippen@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-781-789-9876</value></member><member><name>OID</name><value>6</value></member><member><name>password</name><value>p6666</value></member><member><name>lastName</name><value>Pippen</value></member><member><name>subscriber</name><value>GreatBooks</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>aandrew</value></member><member><name>email</name><value>aandrew@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-781-678-8970</value></member><member><name>OID</name><value>7</value></member><member><name>password</name><value>p7777</value></member><member><name>lastName</name><value>Andhrew</value></member><member><name>subscriber</name><value>AxisChemicals</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>tvincent</value></member><member><name>email</name><value>tvincent@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-786-897-8908</value></member><member><name>OID</name><value>8</value></member><member><name>password</name><value>p8888</value></member><member><name>lastName</name><value>Vincent</value></member><member><name>subscriber</name><value>MediaShop</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>krichard</value></member><member><name>email</name><value>krichard@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-508-789-6789</value></member><member><name>OID</name><value>9</value></member><member><name>password</name><value>p9999</value></member><member><name>lastName</name><value>Richard</value></member><member><name>subscriber</name><value>SmartShop</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>gcornell</value></member><member><name>email</name><value>gconell@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-617-789-8979</value></member><member><name>OID</name><value>10</value></member><member><name>password</name><value>paaaa</value></member><member><name>lastName</name><value>Cornell</value></member><member><name>subscriber</name><value>HomeNeeds</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>shorstmann</value></member><member><name>email</name><value>shorstmann@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-508-791-1267</value></member><member><name>OID</name><value>11</value></member><member><name>password</name><value>p1111</value></member><member><name>lastName</name><value>HorstMann</value></member><member><name>subscriber</name><value>MegaCorp</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>rbob</value></member><member><name>email</name><value>rbob@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-781-789-9089</value></member><member><name>OID</name><value>13</value></member><member><name>password</name><value>p3333</value></member><member><name>lastName</name><value>Bob</value></member><member><name>subscriber</name><value>CornerStore</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>speter</value></member><member><name>email</name><value>speter@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-617-789-1890</value></member><member><name>OID</name><value>14</value></member><member><name>password</name><value>p4444</value></member><member><name>lastName</name><value>Peter</value></member><member><name>subscriber</name><value>Cyberdyne</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>pnovak</value></member><member><name>email</name><value>pnovak@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-617-890-7897</value></member><member><name>OID</name><value>15</value></member><member><name>password</name><value>p5555</value></member><member><name>lastName</name><value>Novak</value></member><member><name>subscriber</name><value>StarSports</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>pnancy</value></member><member><name>email</name><value>pnancy@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-781-789-9876</value></member><member><name>OID</name><value>16</value></member><member><name>password</name><value>p6666</value></member><member><name>lastName</name><value>Nancy</value></member><member><name>subscriber</name><value>GreatBooks</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>hmichel</value></member><member><name>email</name><value>hmichel@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-781-678-8970</value></member><member><name>OID</name><value>17</value></member><member><name>password</name><value>p7777</value></member><member><name>lastName</name><value>Michel</value></member><member><name>subscriber</name><value>AxisChemicals</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>kdavid</value></member><member><name>email</name><value>kdavid@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-786-897-8908</value></member><member><name>OID</name><value>18</value></member><member><name>password</name><value>p8888</value></member><member><name>lastName</name><value>David</value></member><member><name>subscriber</name><value>MediaShop</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>pvalnoor</value></member><member><name>email</name><value>pvalnoor@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-508-789-6789</value></member><member><name>OID</name><value>19</value></member><member><name>password</name><value>p9999</value></member><member><name>lastName</name><value>Valnoor</value></member><member><name>subscriber</name><value>SmartShop</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>wsmith</value></member><member><name>email</name><value>wsmith@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-617-789-8979</value></member><member><name>OID</name><value>20</value></member><member><name>password</name><value>paaaa</value></member><member><name>lastName</name><value>Smith</value></member><member><name>subscriber</name><value>HomeNeeds</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>gcaral</value></member><member><name>email</name><value>gcaral@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-781-789-9876</value></member><member><name>OID</name><value>21</value></member><member><name>password</name><value>p6666</value></member><member><name>lastName</name><value>Caral</value></member><member><name>subscriber</name><value>MegaCorp</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>phillary</value></member><member><name>email</name><value>phillary@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-786-897-8908</value></member><member><name>OID</name><value>23</value></member><member><name>password</name><value>p8888</value></member><member><name>lastName</name><value>Hillary</value></member><member><name>subscriber</name><value>CornerStore</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>bphilip</value></member><member><name>email</name><value>bphilip@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-508-789-6789</value></member><member><name>OID</name><value>24</value></member><member><name>password</name><value>p9999</value></member><member><name>lastName</name><value>Philip</value></member><member><name>subscriber</name><value>Cyberdyne</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>sandrea</value></member><member><name>email</name><value>sandrea@yahoo.com</value></member><member><name>telephone2</name><value>1-800-445-2588</value></member><member><name>telephone1</name><value>1-617-789-8979</value></member><member><name>OID</name><value>25</value></member><member><name>password</name><value>paaaa</value></member><member><name>lastName</name><value>Andrea</value></member><member><name>subscriber</name><value>StarSports</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>s4</value></member><member><name>email</name><value>null</value></member><member><name>telephone2</name><value>null</value></member><member><name>telephone1</name><value>null</value></member><member><name>OID</name><value>26</value></member><member><name>password</name><value>s4</value></member><member><name>lastName</name><value>null</value></member><member><name>subscriber</name><value>s4</value></member></struct></value><value><struct><member><name>objectName</name><value>AdministratorDO</value></member><member><name>adminId</name><value>admin</value></member><member><name>email</name><value></value></member><member><name>telephone2</name><value></value></member><member><name>telephone1</name><value></value></member><member><name>OID</name><value>82</value></member><member><name>password</name><value>admin</value></member><member><name>lastName</name><value>administrator</value></member><member><name>subscriber</name><value>BigBank</value></member></struct></value></data></array></value></param></params></methodResponse>
diff --git a/jni/ruby/test/xmlrpc/htpasswd b/jni/ruby/test/xmlrpc/htpasswd
new file mode 100644
index 0000000..898fc86
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/htpasswd
@@ -0,0 +1,2 @@
+admin:Qg266hq/YYKe2
+01234567890123456789012345678901234567890123456789012345678901234567890123456789:Yl.SJmoFETpS2
diff --git a/jni/ruby/test/xmlrpc/test_client.rb b/jni/ruby/test/xmlrpc/test_client.rb
new file mode 100644
index 0000000..83f8c6b
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/test_client.rb
@@ -0,0 +1,317 @@
+require 'minitest/autorun'
+require 'xmlrpc/client'
+require 'net/http'
+begin
+ require 'openssl'
+rescue LoadError
+end
+
+module XMLRPC
+ class ClientTest < MiniTest::Unit::TestCase
+ module Fake
+ class HTTP
+ attr_accessor :read_timeout, :open_timeout, :use_ssl
+
+ def initialize responses = {}
+ @started = false
+ @responses = responses
+ end
+
+ def started?
+ @started
+ end
+
+ def start
+ @started = true
+ if block_given?
+ begin
+ return yield(self)
+ ensure
+ @started = false
+ end
+ end
+ self
+ end
+
+ def request_post path, request, headers
+ @responses[path].shift
+ end
+ end
+
+ class Client < XMLRPC::Client
+ attr_reader :args, :http
+
+ def initialize(*args)
+ @args = args
+ super
+ end
+
+ private
+ def net_http host, port, proxy_host, proxy_port
+ HTTP.new
+ end
+ end
+
+ class Response
+ def self.new body, fields = [], status = '200'
+ klass = Class.new(Net::HTTPResponse::CODE_TO_OBJ[status]) {
+ def initialize(*args)
+ super
+ @read = true
+ end
+ }
+
+ resp = klass.new '1.1', status, 'OK'
+ resp.body = body
+ fields.each do |k,v|
+ resp.add_field k, v
+ end
+ resp
+ end
+ end
+ end
+
+ def test_new2_host_path_port
+ client = Fake::Client.new2 'http://example.org/foo'
+ host, path, port, *rest = client.args
+
+ assert_equal 'example.org', host
+ assert_equal '/foo', path
+ assert_equal 80, port
+
+ rest.each { |x| refute x }
+ end
+
+ def test_new2_custom_port
+ client = Fake::Client.new2 'http://example.org:1234/foo'
+ host, path, port, *rest = client.args
+
+ assert_equal 'example.org', host
+ assert_equal '/foo', path
+ assert_equal 1234, port
+
+ rest.each { |x| refute x }
+ end
+
+ def test_new2_ssl
+ client = Fake::Client.new2 'https://example.org/foo'
+ host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args
+
+ assert_equal 'example.org', host
+ assert_equal '/foo', path
+ assert_equal 443, port
+ assert use_ssl
+
+ refute proxy_host
+ refute proxy_port
+ refute user
+ refute password
+ refute timeout
+ end if defined?(OpenSSL)
+
+ def test_new2_ssl_custom_port
+ client = Fake::Client.new2 'https://example.org:1234/foo'
+ host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args
+
+ assert_equal 'example.org', host
+ assert_equal '/foo', path
+ assert_equal 1234, port
+ assert use_ssl
+
+ refute proxy_host
+ refute proxy_port
+ refute user
+ refute password
+ refute timeout
+ end if defined?(OpenSSL)
+
+ def test_new2_user_password
+ client = Fake::Client.new2 'http://aaron:tenderlove@example.org/foo'
+ host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args
+
+ [ host, path, port ].each { |x| assert x }
+ assert_equal 'aaron', user
+ assert_equal 'tenderlove', password
+
+ [ proxy_host, proxy_port, use_ssl, timeout ].each { |x| refute x }
+ end
+
+ def test_new2_proxy_host
+ client = Fake::Client.new2 'http://example.org/foo', 'example.com'
+ host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args
+
+ [ host, path, port ].each { |x| assert x }
+
+ assert_equal 'example.com', proxy_host
+
+ [ user, password, proxy_port, use_ssl, timeout ].each { |x| refute x }
+ end
+
+ def test_new2_proxy_port
+ client = Fake::Client.new2 'http://example.org/foo', 'example.com:1234'
+ host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args
+
+ [ host, path, port ].each { |x| assert x }
+
+ assert_equal 'example.com', proxy_host
+ assert_equal 1234, proxy_port
+
+ [ user, password, use_ssl, timeout ].each { |x| refute x }
+ end
+
+ def test_new2_no_path
+ client = Fake::Client.new2 'http://example.org'
+ host, path, port, *rest = client.args
+
+ assert_equal 'example.org', host
+ assert_nil path
+ assert port
+
+ rest.each { |x| refute x }
+ end
+
+ def test_new2_slash_path
+ client = Fake::Client.new2 'http://example.org/'
+ host, path, port, *rest = client.args
+
+ assert_equal 'example.org', host
+ assert_equal '/', path
+ assert port
+
+ rest.each { |x| refute x }
+ end
+
+ def test_new2_bad_protocol
+ assert_raises(ArgumentError) do
+ XMLRPC::Client.new2 'ftp://example.org'
+ end
+ end
+
+ def test_new2_bad_uri
+ assert_raises(ArgumentError) do
+ XMLRPC::Client.new2 ':::::'
+ end
+ end
+
+ def test_new2_path_with_query
+ client = Fake::Client.new2 'http://example.org/foo?bar=baz'
+ host, path, port, *rest = client.args
+
+ assert_equal 'example.org', host
+ assert_equal '/foo?bar=baz', path
+ assert port
+
+ rest.each { |x| refute x }
+ end
+
+ def test_request
+ fh = read 'blog.xml'
+
+ responses = {
+ '/foo' => [ Fake::Response.new(fh, [['Content-Type', 'text/xml']]) ]
+ }
+
+ client = fake_client(responses).new2 'http://example.org/foo'
+
+ resp = client.call('wp.getUsersBlogs', 'tlo', 'omg')
+
+ expected = [{
+ "isAdmin" => true,
+ "url" => "http://tenderlovemaking.com/",
+ "blogid" => "1",
+ "blogName" => "Tender Lovemaking",
+ "xmlrpc" => "http://tenderlovemaking.com/xmlrpc.php"
+ }]
+
+ assert_equal expected, resp
+ end
+
+ def test_async_request
+ fh = read 'blog.xml'
+
+ responses = {
+ '/foo' => [ Fake::Response.new(fh, [['Content-Type', 'text/xml']]) ]
+ }
+
+ client = fake_client(responses).new2 'http://example.org/foo'
+
+ resp = client.call_async('wp.getUsersBlogs', 'tlo', 'omg')
+
+ expected = [{
+ "isAdmin" => true,
+ "url" => "http://tenderlovemaking.com/",
+ "blogid" => "1",
+ "blogName" => "Tender Lovemaking",
+ "xmlrpc" => "http://tenderlovemaking.com/xmlrpc.php"
+ }]
+
+ assert_equal expected, resp
+ end
+
+ # make a request without content-type header
+ def test_bad_content_type
+ fh = read 'blog.xml'
+
+ responses = {
+ '/foo' => [ Fake::Response.new(fh) ]
+ }
+
+ client = fake_client(responses).new2 'http://example.org/foo'
+
+ resp = client.call('wp.getUsersBlogs', 'tlo', 'omg')
+
+ expected = [{
+ "isAdmin" => true,
+ "url" => "http://tenderlovemaking.com/",
+ "blogid" => "1",
+ "blogName" => "Tender Lovemaking",
+ "xmlrpc" => "http://tenderlovemaking.com/xmlrpc.php"
+ }]
+
+ assert_equal expected, resp
+ end
+
+ def test_i8_tag
+ fh = read('blog.xml').gsub(/string/, 'i8')
+
+ responses = {
+ '/foo' => [ Fake::Response.new(fh) ]
+ }
+
+ client = fake_client(responses).new2 'http://example.org/foo'
+
+ resp = client.call('wp.getUsersBlogs', 'tlo', 'omg')
+
+ assert_equal 1, resp.first['blogid']
+ end
+
+ def test_cookie_simple
+ client = Fake::Client.new2('http://example.org/cookie')
+ assert_nil(client.cookie)
+ client.send(:parse_set_cookies, ["param1=value1", "param2=value2"])
+ assert_equal("param1=value1; param2=value2", client.cookie)
+ end
+
+ def test_cookie_override
+ client = Fake::Client.new2('http://example.org/cookie')
+ client.send(:parse_set_cookies,
+ [
+ "param1=value1",
+ "param2=value2",
+ "param1=value3",
+ ])
+ assert_equal("param2=value2; param1=value3", client.cookie)
+ end
+
+ private
+ def read filename
+ File.read File.expand_path(File.join(__FILE__, '..', 'data', filename))
+ end
+
+ def fake_client responses
+ Class.new(Fake::Client) {
+ define_method(:net_http) { |*_| Fake::HTTP.new(responses) }
+ }
+ end
+ end
+end
diff --git a/jni/ruby/test/xmlrpc/test_cookie.rb b/jni/ruby/test/xmlrpc/test_cookie.rb
new file mode 100644
index 0000000..cfcfe4c
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/test_cookie.rb
@@ -0,0 +1,96 @@
+require 'test/unit'
+require 'time'
+require 'webrick'
+require_relative 'webrick_testing'
+require "xmlrpc/server"
+require 'xmlrpc/client'
+
+module TestXMLRPC
+class TestCookie < Test::Unit::TestCase
+ include WEBrick_Testing
+
+ def create_servlet
+ s = XMLRPC::WEBrickServlet.new
+
+ def s.logged_in_users
+ @logged_in_users ||= {}
+ end
+ def s.request
+ @request
+ end
+ def s.response
+ @response
+ end
+ def s.service(request, response)
+ @request = request
+ @response = response
+ super
+ ensure
+ @request = nil
+ @response = nil
+ end
+
+ key = Time.now.to_i.to_s
+ valid_user = "valid-user"
+ s.add_handler("test.login") do |user, password|
+ ok = (user == valid_user and password == "secret")
+ if ok
+ s.logged_in_users[key] = user
+ expires = (Time.now + 60 * 60).httpdate
+ cookies = s.response.cookies
+ cookies << "key=\"#{key}\"; path=\"/RPC2\"; expires=#{expires}"
+ cookies << "user=\"#{user}\"; path=\"/RPC2\""
+ end
+ ok
+ end
+
+ s.add_handler("test.require_authenticate_echo") do |string|
+ cookies = {}
+ s.request.cookies.each do |cookie|
+ cookies[cookie.name] = cookie.value
+ end
+ if cookies == {"key" => key, "user" => valid_user}
+ string
+ else
+ raise XMLRPC::FaultException.new(29, "Authentication required")
+ end
+ end
+
+ s.set_default_handler do |name, *args|
+ raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
+ " or wrong number of parameters!")
+ end
+
+ s.add_introspection
+
+ s
+ end
+
+ def setup_http_server_option
+ option = {:Port => 0}
+ end
+
+ def test_cookie
+ option = setup_http_server_option
+ with_server(option, create_servlet) {|addr|
+ begin
+ @s = XMLRPC::Client.new3(:host => addr.ip_address, :port => addr.ip_port)
+ do_test
+ ensure
+ @s.http.finish
+ end
+ }
+ end
+
+ def do_test
+ assert(!@s.call("test.login", "invalid-user", "invalid-password"))
+ exception = assert_raise(XMLRPC::FaultException) do
+ @s.call("test.require_authenticate_echo", "Hello")
+ end
+ assert_equal(29, exception.faultCode)
+
+ assert(@s.call("test.login", "valid-user", "secret"))
+ assert_equal("Hello", @s.call("test.require_authenticate_echo", "Hello"))
+ end
+end
+end
diff --git a/jni/ruby/test/xmlrpc/test_datetime.rb b/jni/ruby/test/xmlrpc/test_datetime.rb
new file mode 100644
index 0000000..d6bb95d
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/test_datetime.rb
@@ -0,0 +1,161 @@
+require 'test/unit'
+require "xmlrpc/datetime"
+
+module TestXMLRPC
+class Test_DateTime < Test::Unit::TestCase
+
+ def test_new
+ dt = createDateTime()
+
+ assert_instance_of(XMLRPC::DateTime, dt)
+ end
+
+ def test_new_exception
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(4.5, 13, 32, 25, 60, 60) }
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 32, 25, 60, 60) }
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 31, 25, 60, 60) }
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 31, 24, 60, 60) }
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 31, 24, 59, 60) }
+ assert_nothing_raised(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 31, 24, 59, 59) }
+
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 0, 0, -1, -1, -1) }
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 0, -1, -1, -1) }
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 1, -1, -1, -1) }
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 1, 0, -1, -1) }
+ assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 1, 0, 0, -1) }
+ assert_nothing_raised(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 1, 0, 0, 0) }
+ end
+
+
+ def test_get_values
+ y, m, d, h, mi, s = 1970, 3, 24, 12, 0, 5
+ dt = XMLRPC::DateTime.new(y, m, d, h, mi, s)
+
+ assert_equal(y, dt.year)
+ assert_equal(m, dt.month)
+ assert_equal(m, dt.mon)
+ assert_equal(d, dt.day)
+
+ assert_equal(h, dt.hour)
+ assert_equal(mi,dt.min)
+ assert_equal(s, dt.sec)
+ end
+
+ def test_set_values
+ dt = createDateTime()
+ y, m, d, h, mi, s = 1950, 12, 9, 8, 52, 30
+
+ dt.year = y
+ dt.month = m
+ dt.day = d
+ dt.hour = h
+ dt.min = mi
+ dt.sec = s
+
+ assert_equal(y, dt.year)
+ assert_equal(m, dt.month)
+ assert_equal(m, dt.mon)
+ assert_equal(d, dt.day)
+
+ assert_equal(h, dt.hour)
+ assert_equal(mi,dt.min)
+ assert_equal(s, dt.sec)
+
+ dt.mon = 5
+ assert_equal(5, dt.month)
+ assert_equal(5, dt.mon)
+ end
+
+ def test_set_exception
+ dt = createDateTime()
+
+ assert_raise(ArgumentError) { dt.year = 4.5 }
+ assert_nothing_raised(ArgumentError) { dt.year = -2000 }
+
+ assert_raise(ArgumentError) { dt.month = 0 }
+ assert_raise(ArgumentError) { dt.month = 13 }
+ assert_nothing_raised(ArgumentError) { dt.month = 7 }
+
+ assert_raise(ArgumentError) { dt.mon = 0 }
+ assert_raise(ArgumentError) { dt.mon = 13 }
+ assert_nothing_raised(ArgumentError) { dt.mon = 7 }
+
+ assert_raise(ArgumentError) { dt.day = 0 }
+ assert_raise(ArgumentError) { dt.day = 32 }
+ assert_nothing_raised(ArgumentError) { dt.day = 16 }
+
+ assert_raise(ArgumentError) { dt.hour = -1 }
+ assert_raise(ArgumentError) { dt.hour = 25 }
+ assert_nothing_raised(ArgumentError) { dt.hour = 12 }
+
+ assert_raise(ArgumentError) { dt.min = -1 }
+ assert_raise(ArgumentError) { dt.min = 60 }
+ assert_nothing_raised(ArgumentError) { dt.min = 30 }
+
+ assert_raise(ArgumentError) { dt.sec = -1 }
+ assert_raise(ArgumentError) { dt.sec = 60 }
+ assert_nothing_raised(ArgumentError) { dt.sec = 30 }
+ end
+
+ def test_to_a
+ y, m, d, h, mi, s = 1970, 3, 24, 12, 0, 5
+ dt = XMLRPC::DateTime.new(y, m, d, h, mi, s)
+ a = dt.to_a
+
+ assert_instance_of(Array, a)
+ assert_equal(6, a.size, "Returned array has wrong size")
+
+ assert_equal(y, a[0])
+ assert_equal(m, a[1])
+ assert_equal(d, a[2])
+ assert_equal(h, a[3])
+ assert_equal(mi, a[4])
+ assert_equal(s, a[5])
+ end
+
+ def test_to_time1
+ y, m, d, h, mi, s = 1970, 3, 24, 12, 0, 5
+ dt = XMLRPC::DateTime.new(y, m, d, h, mi, s)
+ time = dt.to_time
+
+ assert_not_nil(time)
+
+ assert_equal(y, time.year)
+ assert_equal(m, time.month)
+ assert_equal(d, time.day)
+ assert_equal(h, time.hour)
+ assert_equal(mi, time.min)
+ assert_equal(s, time.sec)
+ end
+
+ def test_to_time2
+ dt = createDateTime()
+ dt.year = 1969
+
+ assert_nil(dt.to_time)
+ end
+
+ def test_to_date1
+ y, m, d, h, mi, s = 1970, 3, 24, 12, 0, 5
+ dt = XMLRPC::DateTime.new(y, m, d, h, mi, s)
+ date = dt.to_date
+
+ assert_equal(y, date.year)
+ assert_equal(m, date.month)
+ assert_equal(d, date.day)
+ end
+
+ def test_to_date2
+ dt = createDateTime()
+ dt.year = 666
+
+ assert_equal(666, dt.to_date.year)
+ end
+
+
+ def createDateTime
+ XMLRPC::DateTime.new(1970, 3, 24, 12, 0, 5)
+ end
+
+end
+end
diff --git a/jni/ruby/test/xmlrpc/test_features.rb b/jni/ruby/test/xmlrpc/test_features.rb
new file mode 100644
index 0000000..48bb0d4
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/test_features.rb
@@ -0,0 +1,50 @@
+require 'test/unit'
+require "xmlrpc/create"
+require "xmlrpc/parser"
+require "xmlrpc/config"
+
+module TestXMLRPC
+class Test_Features < Test::Unit::TestCase
+
+ def setup
+ @params = [nil, {"test" => nil}, [nil, 1, nil]]
+ end
+
+ def test_nil_create
+ XMLRPC::XMLWriter.each_installed_writer do |writer|
+ c = XMLRPC::Create.new(writer)
+
+ XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_CREATE)}
+ XMLRPC::Config.const_set(:ENABLE_NIL_CREATE, false)
+ assert_raise(RuntimeError) { c.methodCall("test", *@params) }
+
+ XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_CREATE)}
+ XMLRPC::Config.const_set(:ENABLE_NIL_CREATE, true)
+ assert_nothing_raised { c.methodCall("test", *@params) }
+ end
+ end
+
+ def test_nil_parse
+ XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_CREATE)}
+ XMLRPC::Config.const_set(:ENABLE_NIL_CREATE, true)
+
+ XMLRPC::XMLWriter.each_installed_writer do |writer|
+ c = XMLRPC::Create.new(writer)
+ str = c.methodCall("test", *@params)
+ XMLRPC::XMLParser.each_installed_parser do |parser|
+ para = nil
+
+ XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_PARSER)}
+ XMLRPC::Config.const_set(:ENABLE_NIL_PARSER, false)
+ assert_raise(RuntimeError) { para = parser.parseMethodCall(str) }
+
+ XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_PARSER)}
+ XMLRPC::Config.const_set(:ENABLE_NIL_PARSER, true)
+ assert_nothing_raised { para = parser.parseMethodCall(str) }
+ assert_equal(para[1], @params)
+ end
+ end
+ end
+
+end
+end
diff --git a/jni/ruby/test/xmlrpc/test_marshal.rb b/jni/ruby/test/xmlrpc/test_marshal.rb
new file mode 100644
index 0000000..34ddfa9
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/test_marshal.rb
@@ -0,0 +1,110 @@
+require 'test/unit'
+require "xmlrpc/marshal"
+
+module TestXMLRPC
+class Test_Marshal < Test::Unit::TestCase
+ # for test_parser_values
+ class Person
+ include XMLRPC::Marshallable
+ attr_reader :name
+ def initialize(name)
+ @name = name
+ end
+ end
+
+
+ def test1_dump_response
+ assert_nothing_raised(NameError) {
+ XMLRPC::Marshal.dump_response('arg')
+ }
+ end
+
+ def test1_dump_call
+ assert_nothing_raised(NameError) {
+ XMLRPC::Marshal.dump_call('methodName', 'arg')
+ }
+ end
+
+ def test2_dump_load_response
+ value = [1, 2, 3, {"test" => true}, 3.4]
+ res = XMLRPC::Marshal.dump_response(value)
+
+ assert_equal(value, XMLRPC::Marshal.load_response(res))
+ end
+
+ def test2_dump_load_call
+ methodName = "testMethod"
+ value = [1, 2, 3, {"test" => true}, 3.4]
+ exp = [methodName, [value, value]]
+
+ res = XMLRPC::Marshal.dump_call(methodName, value, value)
+
+ assert_equal(exp, XMLRPC::Marshal.load_call(res))
+ end
+
+ def test_parser_values
+ v1 = [
+ 1, -7778, -(2**31), 2**31-1, # integers
+ 1.0, 0.0, -333.0, 2343434343.0, # floats
+ false, true, true, false, # booleans
+ "Hallo", "with < and >", "" # strings
+ ]
+
+ v2 = [
+ [v1, v1, v1],
+ {"a" => v1}
+ ]
+
+ v3 = [
+ XMLRPC::Base64.new("\001"*1000), # base64
+ :aSymbol, :anotherSym # symbols (-> string)
+ ]
+ v3_exp = [
+ "\001"*1000,
+ "aSymbol", "anotherSym"
+ ]
+ person = Person.new("Michael")
+
+ XMLRPC::XMLParser.each_installed_parser do |parser|
+ m = XMLRPC::Marshal.new(parser)
+
+ assert_equal( v1, m.load_response(m.dump_response(v1)) )
+ assert_equal( v2, m.load_response(m.dump_response(v2)) )
+ assert_equal( v3_exp, m.load_response(m.dump_response(v3)) )
+
+ pers = m.load_response(m.dump_response(person))
+
+ assert( pers.is_a?(Person) )
+ assert( person.name == pers.name )
+ end
+
+ # missing, Date, Time, DateTime
+ # Struct
+ end
+
+ def test_parser_invalid_values
+ values = [
+ -1-(2**31), 2**31,
+ Float::INFINITY, -Float::INFINITY, Float::NAN
+ ]
+ XMLRPC::XMLParser.each_installed_parser do |parser|
+ m = XMLRPC::Marshal.new(parser)
+
+ values.each do |v|
+ assert_raise(RuntimeError, "#{v} shouldn't be dumped, but dumped") \
+ { m.dump_response(v) }
+ end
+ end
+ end
+
+ def test_no_params_tag
+ # bug found by Idan Sofer
+
+ expect = %{<?xml version="1.0" ?><methodCall><methodName>myMethod</methodName><params/></methodCall>\n}
+
+ str = XMLRPC::Marshal.dump_call("myMethod")
+ assert_equal(expect, str)
+ end
+
+end
+end
diff --git a/jni/ruby/test/xmlrpc/test_parser.rb b/jni/ruby/test/xmlrpc/test_parser.rb
new file mode 100644
index 0000000..5591645
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/test_parser.rb
@@ -0,0 +1,93 @@
+require 'test/unit'
+require 'xmlrpc/datetime'
+require "xmlrpc/parser"
+require 'yaml'
+
+module TestXMLRPC
+module GenericParserTest
+ def datafile(base)
+ File.join(File.dirname(__FILE__), "data", base)
+ end
+
+ def load_data(name)
+ [File.read(datafile(name) + ".xml"), YAML.load(File.read(datafile(name) + ".expected"))]
+ end
+
+ def setup
+ @xml1, @expected1 = load_data('xml1')
+ @xml2, @expected2 = load_data('bug_covert')
+ @xml3, @expected3 = load_data('bug_bool')
+ @xml4, @expected4 = load_data('value')
+
+ @cdata_xml, @cdata_expected = load_data('bug_cdata')
+
+ @datetime_xml = File.read(datafile('datetime_iso8601.xml'))
+ @datetime_expected = XMLRPC::DateTime.new(2004, 11, 5, 1, 15, 23)
+
+ @fault_doc = File.read(datafile('fault.xml'))
+ end
+
+ # test parseMethodResponse --------------------------------------------------
+
+ def test_parseMethodResponse1
+ assert_equal(@expected1, @p.parseMethodResponse(@xml1))
+ end
+
+ def test_parseMethodResponse2
+ assert_equal(@expected2, @p.parseMethodResponse(@xml2))
+ end
+
+ def test_parseMethodResponse3
+ assert_equal(@expected3, @p.parseMethodResponse(@xml3))
+ end
+
+ def test_cdata
+ assert_equal(@cdata_expected, @p.parseMethodResponse(@cdata_xml))
+ end
+
+ def test_dateTime
+ assert_equal(@datetime_expected, @p.parseMethodResponse(@datetime_xml)[1])
+ end
+
+ # test parseMethodCall ------------------------------------------------------
+
+ def test_parseMethodCall
+ assert_equal(@expected4, @p.parseMethodCall(@xml4))
+ end
+
+ # test fault ----------------------------------------------------------------
+
+ def test_fault
+ flag, fault = @p.parseMethodResponse(@fault_doc)
+ assert_equal(flag, false)
+ unless fault.is_a? XMLRPC::FaultException
+ assert(false, "must be an instance of class XMLRPC::FaultException")
+ end
+ assert_equal(fault.faultCode, 4)
+ assert_equal(fault.faultString, "an error message")
+ end
+
+ def test_fault_message
+ fault = XMLRPC::FaultException.new(1234, 'an error message')
+ assert_equal('an error message', fault.to_s)
+ assert_equal('#<XMLRPC::FaultException: an error message>', fault.inspect)
+ end
+end
+
+# create test class for each installed parser
+XMLRPC::XMLParser.each_installed_parser do |parser|
+ klass = parser.class
+ name = klass.to_s.split("::").last
+
+ eval %{
+ class Test_#{name} < Test::Unit::TestCase
+ include GenericParserTest
+
+ def setup
+ super
+ @p = #{klass}.new
+ end
+ end
+ }
+end
+end
diff --git a/jni/ruby/test/xmlrpc/test_webrick_server.rb b/jni/ruby/test/xmlrpc/test_webrick_server.rb
new file mode 100644
index 0000000..bc10239
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/test_webrick_server.rb
@@ -0,0 +1,135 @@
+# coding: utf-8
+
+require 'test/unit'
+require 'webrick'
+require_relative 'webrick_testing'
+require "xmlrpc/server"
+require 'xmlrpc/client'
+require 'logger'
+
+module TestXMLRPC
+class Test_Webrick < Test::Unit::TestCase
+ include WEBrick_Testing
+
+ def create_servlet(server)
+ s = XMLRPC::WEBrickServlet.new
+
+ basic_auth = WEBrick::HTTPAuth::BasicAuth.new(
+ :Realm => 'auth',
+ :UserDB => WEBrick::HTTPAuth::Htpasswd.new(File.expand_path('./htpasswd', File.dirname(__FILE__))),
+ :Logger => server.logger,
+ )
+
+ class << s; self end.send(:define_method, :service) {|req, res|
+ basic_auth.authenticate(req, res)
+ super(req, res)
+ }
+
+ s.add_handler("test.add") do |a,b|
+ a + b
+ end
+
+ s.add_handler("test.div") do |a,b|
+ if b == 0
+ raise XMLRPC::FaultException.new(1, "division by zero")
+ else
+ a / b
+ end
+ end
+
+ s.set_default_handler do |name, *args|
+ raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
+ " or wrong number of parameters!")
+ end
+
+ s.add_introspection
+
+ return s
+ end
+
+ def setup_http_server_option(use_ssl)
+ option = {
+ :BindAddress => "localhost",
+ :Port => 0,
+ :SSLEnable => use_ssl,
+ }
+ if use_ssl
+ require 'webrick/https'
+ option.update(
+ :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
+ :SSLCertName => []
+ )
+ end
+
+ option
+ end
+
+ def test_client_server
+ # NOTE: I don't enable SSL testing as this hangs
+ [false].each do |use_ssl|
+ option = setup_http_server_option(use_ssl)
+ with_server(option, method(:create_servlet)) {|addr|
+ @s = XMLRPC::Client.new3(:host => addr.ip_address, :port => addr.ip_port, :use_ssl => use_ssl)
+ @s.user = 'admin'
+ @s.password = 'admin'
+ silent do
+ do_test
+ end
+ @s.http.finish
+ @s = XMLRPC::Client.new3(:host => addr.ip_address, :port => addr.ip_port, :use_ssl => use_ssl)
+ @s.user = '01234567890123456789012345678901234567890123456789012345678901234567890123456789'
+ @s.password = 'guest'
+ silent do
+ do_test
+ end
+ @s.http.finish
+ }
+ end
+ end
+
+ def silent
+ begin
+ back, $VERBOSE = $VERBOSE, nil
+ yield
+ ensure
+ $VERBOSE = back
+ end
+ end
+
+ def do_test
+ # simple call
+ assert_equal 9, @s.call('test.add', 4, 5)
+
+ # fault exception
+ assert_raise(XMLRPC::FaultException) { @s.call('test.div', 1, 0) }
+
+ # fault exception via call2
+ ok, param = @s.call2('test.div', 1, 0)
+ assert_equal false, ok
+ assert_instance_of XMLRPC::FaultException, param
+ assert_equal 1, param.faultCode
+ assert_equal 'division by zero', param.faultString
+
+ # call2 without fault exception
+ ok, param = @s.call2('test.div', 10, 5)
+ assert_equal true, ok
+ assert_equal param, 2
+
+ # introspection
+ assert_equal ["test.add", "test.div", "system.listMethods", "system.methodSignature", "system.methodHelp"], @s.call("system.listMethods")
+
+ # default handler (missing handler)
+ ok, param = @s.call2('test.nonexisting')
+ assert_equal false, ok
+ assert_equal(-99, param.faultCode)
+
+ # default handler (wrong number of arguments)
+ ok, param = @s.call2('test.add', 1, 2, 3)
+ assert_equal false, ok
+ assert_equal(-99, param.faultCode)
+
+ # multibyte characters
+ assert_equal "あいうえおかきくけこ", @s.call('test.add', "あいうえお", "かきくけこ")
+ end
+end
+end
diff --git a/jni/ruby/test/xmlrpc/webrick_testing.rb b/jni/ruby/test/xmlrpc/webrick_testing.rb
new file mode 100644
index 0000000..37c0b69
--- /dev/null
+++ b/jni/ruby/test/xmlrpc/webrick_testing.rb
@@ -0,0 +1,55 @@
+require 'timeout'
+
+module TestXMLRPC
+module WEBrick_Testing
+ def start_server(logger, config={})
+ raise "already started" if defined?(@__server) && @__server
+ @__started = false
+
+ @__server = WEBrick::HTTPServer.new(
+ {
+ :BindAddress => "localhost",
+ :Logger => logger,
+ :AccessLog => [],
+ }.update(config))
+ yield @__server
+ @__started = true
+
+ addr = @__server.listeners.first.connect_address
+
+ @__server_thread = Thread.new {
+ begin
+ @__server.start
+ rescue IOError => e
+ assert_match(/closed/, e.message)
+ ensure
+ @__started = false
+ end
+ }
+
+ addr
+ end
+
+ def with_server(config, servlet)
+ log = []
+ logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN)
+ addr = start_server(logger, config) {|w|
+ servlet = servlet.call(w) if servlet.respond_to? :call
+ w.mount('/RPC2', servlet)
+ }
+ client_thread = Thread.new {
+ begin
+ yield addr
+ ensure
+ @__server.shutdown
+ end
+ }
+ server_thread = Thread.new {
+ @__server_thread.join
+ @__server = nil
+ assert_equal([], log)
+ }
+ assert_join_threads([client_thread, server_thread])
+ end
+end
+end
diff --git a/jni/ruby/test/zlib/test_zlib.rb b/jni/ruby/test/zlib/test_zlib.rb
new file mode 100644
index 0000000..a7fa943
--- /dev/null
+++ b/jni/ruby/test/zlib/test_zlib.rb
@@ -0,0 +1,1094 @@
+require 'test/unit'
+require 'stringio'
+require 'tempfile'
+
+begin
+ require 'zlib'
+rescue LoadError
+end
+
+if defined? Zlib
+ class TestZlibDeflate < Test::Unit::TestCase
+ def test_initialize
+ z = Zlib::Deflate.new
+ s = z.deflate("foo", Zlib::FINISH)
+ assert_equal("foo", Zlib::Inflate.inflate(s))
+
+ z = Zlib::Deflate.new
+ s = z.deflate("foo")
+ s << z.deflate(nil, Zlib::FINISH)
+ assert_equal("foo", Zlib::Inflate.inflate(s))
+
+ assert_raise(Zlib::StreamError) { Zlib::Deflate.new(10000) }
+ end
+
+ def test_dup
+ z1 = Zlib::Deflate.new
+ s = z1.deflate("foo")
+ z2 = z1.dup
+ s1 = s + z1.deflate("bar", Zlib::FINISH)
+ s2 = s + z2.deflate("baz", Zlib::FINISH)
+ assert_equal("foobar", Zlib::Inflate.inflate(s1))
+ assert_equal("foobaz", Zlib::Inflate.inflate(s2))
+ end
+
+ def test_deflate
+ s = Zlib::Deflate.deflate("foo")
+ assert_equal("foo", Zlib::Inflate.inflate(s))
+
+ assert_raise(Zlib::StreamError) { Zlib::Deflate.deflate("foo", 10000) }
+ end
+
+ def test_deflate_chunked
+ original = ''
+ chunks = []
+ r = Random.new 0
+
+ z = Zlib::Deflate.new
+
+ 2.times do
+ input = r.bytes(20000)
+ original << input
+ z.deflate(input) do |chunk|
+ chunks << chunk
+ end
+ end
+
+ assert_equal [16384, 16384],
+ chunks.map { |chunk| chunk.length }
+
+ final = z.finish
+
+ assert_equal 7253, final.length
+
+ chunks << final
+ all = chunks.join
+
+ inflated = Zlib.inflate all
+
+ assert_equal original, inflated
+ end
+
+ def test_deflate_chunked_break
+ chunks = []
+ r = Random.new 0
+
+ z = Zlib::Deflate.new
+
+ input = r.bytes(20000)
+ z.deflate(input) do |chunk|
+ chunks << chunk
+ break
+ end
+
+ assert_equal [16384], chunks.map { |chunk| chunk.length }
+
+ final = z.finish
+
+ assert_equal 3632, final.length
+
+ all = chunks.join
+ all << final
+
+ original = Zlib.inflate all
+
+ assert_equal input, original
+ end
+
+ def test_addstr
+ z = Zlib::Deflate.new
+ z << "foo"
+ s = z.deflate(nil, Zlib::FINISH)
+ assert_equal("foo", Zlib::Inflate.inflate(s))
+ end
+
+ def test_flush
+ z = Zlib::Deflate.new
+ z << "foo"
+ s = z.flush
+ z << "bar"
+ s << z.flush_next_in
+ z << "baz"
+ s << z.flush_next_out
+ s << z.deflate("qux", Zlib::FINISH)
+ assert_equal("foobarbazqux", Zlib::Inflate.inflate(s))
+ end
+
+ def test_avail
+ z = Zlib::Deflate.new
+ assert_equal(0, z.avail_in)
+ assert_equal(0, z.avail_out)
+ z << "foo"
+ z.avail_out += 100
+ z << "bar"
+ s = z.finish
+ assert_equal("foobar", Zlib::Inflate.inflate(s))
+ end
+
+ def test_total
+ z = Zlib::Deflate.new
+ 1000.times { z << "foo" }
+ s = z.finish
+ assert_equal(3000, z.total_in)
+ assert_operator(3000, :>, z.total_out)
+ assert_equal("foo" * 1000, Zlib::Inflate.inflate(s))
+ end
+
+ def test_data_type
+ z = Zlib::Deflate.new
+ assert([Zlib::ASCII, Zlib::BINARY, Zlib::UNKNOWN].include?(z.data_type))
+ end
+
+ def test_adler
+ z = Zlib::Deflate.new
+ z << "foo"
+ z.finish
+ assert_equal(0x02820145, z.adler)
+ end
+
+ def test_finished_p
+ z = Zlib::Deflate.new
+ assert_equal(false, z.finished?)
+ z << "foo"
+ assert_equal(false, z.finished?)
+ z.finish
+ assert_equal(true, z.finished?)
+ z.close
+ assert_raise(Zlib::Error) { z.finished? }
+ end
+
+ def test_closed_p
+ z = Zlib::Deflate.new
+ assert_equal(false, z.closed?)
+ z << "foo"
+ assert_equal(false, z.closed?)
+ z.finish
+ assert_equal(false, z.closed?)
+ z.close
+ assert_equal(true, z.closed?)
+ end
+
+ def test_params
+ z = Zlib::Deflate.new
+ z << "foo"
+ z.params(Zlib::DEFAULT_COMPRESSION, Zlib::DEFAULT_STRATEGY)
+ z << "bar"
+ s = z.finish
+ assert_equal("foobar", Zlib::Inflate.inflate(s))
+
+ data = ('a'..'z').to_a.join
+ z = Zlib::Deflate.new(Zlib::NO_COMPRESSION, Zlib::MAX_WBITS,
+ Zlib::DEF_MEM_LEVEL, Zlib::DEFAULT_STRATEGY)
+ z << data[0, 10]
+ z.params(Zlib::BEST_COMPRESSION, Zlib::DEFAULT_STRATEGY)
+ z << data[10 .. -1]
+ assert_equal(data, Zlib::Inflate.inflate(z.finish))
+
+ z = Zlib::Deflate.new
+ s = z.deflate("foo", Zlib::FULL_FLUSH)
+ z.avail_out = 0
+ z.params(Zlib::NO_COMPRESSION, Zlib::FILTERED)
+ s << z.deflate("bar", Zlib::FULL_FLUSH)
+ z.avail_out = 0
+ z.params(Zlib::BEST_COMPRESSION, Zlib::HUFFMAN_ONLY)
+ s << z.deflate("baz", Zlib::FINISH)
+ assert_equal("foobarbaz", Zlib::Inflate.inflate(s))
+
+ z = Zlib::Deflate.new
+ assert_raise(Zlib::StreamError) { z.params(10000, 10000) }
+ z.close # without this, outputs `zlib(finalizer): the stream was freed prematurely.'
+ end
+
+ def test_set_dictionary
+ z = Zlib::Deflate.new
+ z.set_dictionary("foo")
+ s = z.deflate("foo" * 100, Zlib::FINISH)
+ z = Zlib::Inflate.new
+ assert_raise(Zlib::NeedDict) { z.inflate(s) }
+ z.set_dictionary("foo")
+ assert_equal("foo" * 100, z.inflate(s)) # ???
+
+ z = Zlib::Deflate.new
+ z << "foo"
+ assert_raise(Zlib::StreamError) { z.set_dictionary("foo") }
+ z.close # without this, outputs `zlib(finalizer): the stream was freed prematurely.'
+ end
+
+ def test_reset
+ z = Zlib::Deflate.new
+ z << "foo"
+ z.reset
+ z << "bar"
+ s = z.finish
+ assert_equal("bar", Zlib::Inflate.inflate(s))
+ end
+
+ def test_close
+ z = Zlib::Deflate.new
+ z.close
+ assert_raise(Zlib::Error) { z << "foo" }
+ assert_raise(Zlib::Error) { z.reset }
+ end
+ end
+
+ class TestZlibInflate < Test::Unit::TestCase
+ def test_class_inflate_dictionary
+ assert_raises(Zlib::NeedDict) do
+ Zlib::Inflate.inflate([0x08,0x3C,0x0,0x0,0x0,0x0].pack("c*"))
+ end
+ end
+
+ def test_initialize
+ assert_raise(Zlib::StreamError) { Zlib::Inflate.new(-1) }
+
+ s = Zlib::Deflate.deflate("foo")
+ z = Zlib::Inflate.new
+ z << s << nil
+ assert_equal("foo", z.finish)
+ end
+
+ def test_add_dictionary
+ dictionary = "foo"
+
+ deflate = Zlib::Deflate.new
+ deflate.set_dictionary dictionary
+ compressed = deflate.deflate "foofoofoo", Zlib::FINISH
+ deflate.close
+
+ out = nil
+ inflate = Zlib::Inflate.new
+ inflate.add_dictionary "foo"
+
+ out = inflate.inflate compressed
+
+ assert_equal "foofoofoo", out
+ end
+
+ def test_finish_chunked
+ # zeros = Zlib::Deflate.deflate("0" * 100_000)
+ zeros = "x\234\355\3011\001\000\000\000\302\240J\353\237\316\032\036@" \
+ "\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\257\006\351\247BH"
+
+ chunks = []
+
+ z = Zlib::Inflate.new
+
+ z.inflate(zeros) do |chunk|
+ chunks << chunk
+ break
+ end
+
+ z.finish do |chunk|
+ chunks << chunk
+ end
+
+ assert_equal [16384, 16384, 16384, 16384, 16384, 16384, 1696],
+ chunks.map { |chunk| chunk.size }
+
+ assert chunks.all? { |chunk|
+ chunk =~ /\A0+\z/
+ }
+ end
+
+ def test_inflate
+ s = Zlib::Deflate.deflate("foo")
+ z = Zlib::Inflate.new
+ s = z.inflate(s)
+ s << z.inflate(nil)
+ assert_equal("foo", s)
+ z.inflate("foo") # ???
+ z << "foo" # ???
+ end
+
+ def test_inflate_partial_input
+ deflated = Zlib::Deflate.deflate "\0"
+
+ z = Zlib::Inflate.new
+
+ inflated = ""
+
+ deflated.each_char do |byte|
+ inflated << z.inflate(byte)
+ end
+
+ inflated << z.finish
+
+ assert_equal "\0", inflated
+ end
+
+ def test_inflate_chunked
+ # s = Zlib::Deflate.deflate("0" * 100_000)
+ zeros = "x\234\355\3011\001\000\000\000\302\240J\353\237\316\032\036@" \
+ "\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\257\006\351\247BH"
+
+ chunks = []
+
+ z = Zlib::Inflate.new
+
+ z.inflate(zeros) do |chunk|
+ chunks << chunk
+ end
+
+ assert_equal [16384, 16384, 16384, 16384, 16384, 16384, 1696],
+ chunks.map { |chunk| chunk.size }
+
+ assert chunks.all? { |chunk|
+ chunk =~ /\A0+\z/
+ }
+ end
+
+ def test_inflate_chunked_break
+ # zeros = Zlib::Deflate.deflate("0" * 100_000)
+ zeros = "x\234\355\3011\001\000\000\000\302\240J\353\237\316\032\036@" \
+ "\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" \
+ "\000\000\000\000\000\000\000\257\006\351\247BH"
+
+ chunks = []
+
+ z = Zlib::Inflate.new
+
+ z.inflate(zeros) do |chunk|
+ chunks << chunk
+ break
+ end
+
+ out = z.inflate nil
+
+ assert_equal 100_000 - chunks.first.length, out.length
+ end
+
+ def test_inflate_dictionary
+ dictionary = "foo"
+
+ deflate = Zlib::Deflate.new
+ deflate.set_dictionary dictionary
+ compressed = deflate.deflate "foofoofoo", Zlib::FINISH
+ deflate.close
+
+ out = nil
+ inflate = Zlib::Inflate.new
+
+ begin
+ out = inflate.inflate compressed
+
+ flunk "Zlib::NeedDict was not raised"
+ rescue Zlib::NeedDict
+ inflate.set_dictionary dictionary
+ out = inflate.inflate ""
+ end
+
+ assert_equal "foofoofoo", out
+ end
+
+ def test_sync
+ z = Zlib::Deflate.new
+ s = z.deflate("foo" * 1000, Zlib::FULL_FLUSH)
+ z.avail_out = 0
+ z.params(Zlib::NO_COMPRESSION, Zlib::FILTERED)
+ s << z.deflate("bar" * 1000, Zlib::FULL_FLUSH)
+ z.avail_out = 0
+ z.params(Zlib::BEST_COMPRESSION, Zlib::HUFFMAN_ONLY)
+ s << z.deflate("baz" * 1000, Zlib::FINISH)
+
+ z = Zlib::Inflate.new
+ assert_raise(Zlib::DataError) { z << "\0" * 100 }
+ assert_equal(false, z.sync(""))
+ assert_equal(false, z.sync_point?)
+
+ z = Zlib::Inflate.new
+ assert_raise(Zlib::DataError) { z << "\0" * 100 + s }
+ assert_equal(true, z.sync(""))
+
+ z = Zlib::Inflate.new
+ assert_equal(false, z.sync("\0" * 100))
+ assert_equal(false, z.sync_point?)
+
+ z = Zlib::Inflate.new
+ assert_equal(true, z.sync("\0" * 100 + s))
+ end
+
+ def test_set_dictionary
+ z = Zlib::Inflate.new
+ assert_raise(Zlib::StreamError) { z.set_dictionary("foo") }
+ z.close
+ end
+ end
+
+ class TestZlibGzipFile < Test::Unit::TestCase
+ def test_to_io
+ Tempfile.create("test_zlib_gzip_file_to_io") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_kind_of(IO, f.to_io)
+ end
+ }
+ end
+
+ def test_crc
+ Tempfile.create("test_zlib_gzip_file_crc") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ f.read
+ assert_equal(0x8c736521, f.crc)
+ end
+ }
+ end
+
+ def test_mtime
+ tim = Time.now
+
+ Tempfile.create("test_zlib_gzip_file_mtime") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) do |gz|
+ gz.mtime = -1
+ gz.mtime = tim
+ gz.print("foo")
+ gz.flush
+ assert_raise(Zlib::GzipFile::Error) { gz.mtime = Time.now }
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal(tim.to_i, f.mtime.to_i)
+ end
+ }
+ end
+
+ def test_level
+ Tempfile.create("test_zlib_gzip_file_level") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal(Zlib::DEFAULT_COMPRESSION, f.level)
+ end
+ }
+ end
+
+ def test_os_code
+ Tempfile.create("test_zlib_gzip_file_os_code") {|t|
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal(Zlib::OS_CODE, f.os_code)
+ end
+ }
+ end
+
+ def test_orig_name
+ Tempfile.create("test_zlib_gzip_file_orig_name") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) do |gz|
+ gz.orig_name = "foobarbazqux\0quux"
+ gz.print("foo")
+ gz.flush
+ assert_raise(Zlib::GzipFile::Error) { gz.orig_name = "quux" }
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal("foobarbazqux", f.orig_name)
+ end
+ }
+ end
+
+ def test_comment
+ Tempfile.create("test_zlib_gzip_file_comment") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) do |gz|
+ gz.comment = "foobarbazqux\0quux"
+ gz.print("foo")
+ gz.flush
+ assert_raise(Zlib::GzipFile::Error) { gz.comment = "quux" }
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal("foobarbazqux", f.comment)
+ end
+ }
+ end
+
+ def test_lineno
+ Tempfile.create("test_zlib_gzip_file_lineno") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo\nbar\nbaz\nqux\n") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal([0, "foo\n"], [f.lineno, f.gets])
+ assert_equal([1, "bar\n"], [f.lineno, f.gets])
+ f.lineno = 1000
+ assert_equal([1000, "baz\n"], [f.lineno, f.gets])
+ assert_equal([1001, "qux\n"], [f.lineno, f.gets])
+ end
+ }
+ end
+
+ def test_closed_p
+ Tempfile.create("test_zlib_gzip_file_closed_p") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal(false, f.closed?)
+ f.read
+ assert_equal(false, f.closed?)
+ f.close
+ assert_equal(true, f.closed?)
+ end
+ }
+ end
+
+ def test_sync
+ Tempfile.create("test_zlib_gzip_file_sync") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ f.sync = true
+ assert_equal(true, f.sync)
+ f.read
+ f.sync = false
+ assert_equal(false, f.sync)
+ end
+ }
+ end
+
+ def test_pos
+ Tempfile.create("test_zlib_gzip_file_pos") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) do |gz|
+ gz.print("foo")
+ gz.flush
+ assert_equal(3, gz.tell)
+ end
+ }
+ end
+
+ def test_path
+ Tempfile.create("test_zlib_gzip_file_path") {|t|
+ t.close
+
+ gz = Zlib::GzipWriter.open(t.path)
+ gz.print("foo")
+ assert_equal(t.path, gz.path)
+ gz.close
+ assert_equal(t.path, gz.path)
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal(t.path, f.path)
+ f.close
+ assert_equal(t.path, f.path)
+ end
+
+ s = ""
+ sio = StringIO.new(s)
+ gz = Zlib::GzipWriter.new(sio)
+ gz.print("foo")
+ assert_raise(NoMethodError) { gz.path }
+ gz.close
+
+ sio = StringIO.new(s)
+ Zlib::GzipReader.new(sio) do |f|
+ assert_raise(NoMethodError) { f.path }
+ end
+ }
+ end
+ end
+
+ class TestZlibGzipReader < Test::Unit::TestCase
+ D0 = "\037\213\010\000S`\017A\000\003\003\000\000\000\000\000\000\000\000\000"
+ def test_read0
+ assert_equal("", Zlib::GzipReader.new(StringIO.new(D0)).read(0))
+ end
+
+ def test_ungetc
+ s = ""
+ w = Zlib::GzipWriter.new(StringIO.new(s))
+ w << (1...1000).to_a.inspect
+ w.close
+ r = Zlib::GzipReader.new(StringIO.new(s))
+ r.read(100)
+ r.ungetc ?a
+ assert_nothing_raised("[ruby-dev:24060]") {
+ r.read(100)
+ r.read
+ r.close
+ }
+ end
+
+ def test_ungetc_paragraph
+ s = ""
+ w = Zlib::GzipWriter.new(StringIO.new(s))
+ w << "abc"
+ w.close
+ r = Zlib::GzipReader.new(StringIO.new(s))
+ r.ungetc ?\n
+ assert_equal("abc", r.gets(""))
+ assert_nothing_raised("[ruby-dev:24065]") {
+ r.read
+ r.close
+ }
+ end
+
+ def test_open
+ Tempfile.create("test_zlib_gzip_reader_open") {|t|
+ t.close
+ e = assert_raise(Zlib::GzipFile::Error) {
+ Zlib::GzipReader.open(t.path)
+ }
+ assert_equal("not in gzip format", e.message)
+ assert_nil(e.input)
+ open(t.path, "wb") {|f| f.write("foo")}
+ e = assert_raise(Zlib::GzipFile::Error) {
+ Zlib::GzipReader.open(t.path)
+ }
+ assert_equal("not in gzip format", e.message)
+ assert_equal("foo", e.input)
+ open(t.path, "wb") {|f| f.write("foobarzothoge")}
+ e = assert_raise(Zlib::GzipFile::Error) {
+ Zlib::GzipReader.open(t.path)
+ }
+ assert_equal("not in gzip format", e.message)
+ assert_equal("foobarzothoge", e.input)
+
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+
+ assert_raise(ArgumentError) { Zlib::GzipReader.open }
+
+ assert_equal("foo", Zlib::GzipReader.open(t.path) {|gz| gz.read })
+
+ f = Zlib::GzipReader.open(t.path)
+ begin
+ assert_equal("foo", f.read)
+ ensure
+ f.close
+ end
+ }
+ end
+
+ def test_rewind
+ bug8467 = '[ruby-core:55220] [Bug #8467]'
+ Tempfile.create("test_zlib_gzip_reader_rewind") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal("foo", f.read)
+ f.rewind
+ assert_equal("foo", f.read)
+
+ f.rewind
+ bytes = []
+ f.each_byte { |b| bytes << b }
+ assert_equal "foo".bytes.to_a, bytes, '[Bug #10101]'
+ end
+ open(t.path, "rb") do |f|
+ gz = Zlib::GzipReader.new(f)
+ gz.rewind
+ assert_equal(["foo"], gz.to_a, bug8467)
+ end
+ }
+ end
+
+ def test_unused
+ Tempfile.create("test_zlib_gzip_reader_unused") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foobar") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal(nil, f.unused)
+ assert_equal("foo", f.read(3))
+ assert_equal(nil, f.unused)
+ assert_equal("bar", f.read)
+ assert_equal(nil, f.unused)
+ end
+ }
+ end
+
+ def test_unused2
+ zio = StringIO.new
+
+ io = Zlib::GzipWriter.new zio
+ io.write 'aaaa'
+ io.finish
+
+ io = Zlib::GzipWriter.new zio
+ io.write 'bbbb'
+ io.finish
+
+ zio.rewind
+
+ io = Zlib::GzipReader.new zio
+ assert_equal('aaaa', io.read)
+ unused = io.unused
+ assert_equal(24, unused.bytesize)
+ io.finish
+
+ zio.pos -= unused.length
+
+ io = Zlib::GzipReader.new zio
+ assert_equal('bbbb', io.read)
+ assert_equal(nil, io.unused)
+ io.finish
+ end
+
+ def test_read
+ Tempfile.create("test_zlib_gzip_reader_read") {|t|
+ t.close
+ str = "\u3042\u3044\u3046"
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print(str) }
+
+ Zlib::GzipReader.open(t.path, encoding: "UTF-8") do |f|
+ assert_raise(ArgumentError) { f.read(-1) }
+ assert_equal(str, f.read)
+ end
+ }
+ end
+
+ def test_readpartial
+ Tempfile.create("test_zlib_gzip_reader_readpartial") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foobar") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert("foo".start_with?(f.readpartial(3)))
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ s = ""
+ f.readpartial(3, s)
+ assert("foo".start_with?(s))
+
+ assert_raise(ArgumentError) { f.readpartial(-1) }
+ end
+ }
+ end
+
+ def test_getc
+ Tempfile.create("test_zlib_gzip_reader_getc") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foobar") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ "foobar".each_char {|c| assert_equal(c, f.getc) }
+ assert_nil(f.getc)
+ end
+ }
+ end
+
+ def test_getbyte
+ Tempfile.create("test_zlib_gzip_reader_getbyte") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foobar") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ "foobar".each_byte {|c| assert_equal(c, f.getbyte) }
+ assert_nil(f.getbyte)
+ end
+ }
+ end
+
+ def test_readchar
+ Tempfile.create("test_zlib_gzip_reader_readchar") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foobar") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ "foobar".each_byte {|c| assert_equal(c, f.readchar.ord) }
+ assert_raise(EOFError) { f.readchar }
+ end
+ }
+ end
+
+ def test_each_byte
+ Tempfile.create("test_zlib_gzip_reader_each_byte") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foobar") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ a = []
+ f.each_byte {|c| a << c }
+ assert_equal("foobar".each_byte.to_a, a)
+ end
+ }
+ end
+
+ def test_gets
+ Tempfile.create("test_zlib_gzip_reader_gets") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo\nbar\nbaz\n") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal("foo\n", f.gets)
+ assert_equal("bar\n", f.gets)
+ assert_equal("baz\n", f.gets)
+ assert_nil(f.gets)
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal("foo\nbar\nbaz\n", f.gets(nil))
+ end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal("foo\n", f.gets(10))
+ assert_equal("ba", f.gets(2))
+ assert_equal("r\nb", f.gets(nil, 3))
+ assert_equal("az\n", f.gets(nil, 10))
+ assert_nil(f.gets)
+ end
+ }
+ end
+
+ def test_gets2
+ Tempfile.create("test_zlib_gzip_reader_gets2") {|t|
+ t.close
+ ustrs = %W"\u{3042 3044 3046}\n \u{304b 304d 304f}\n \u{3055 3057 3059}\n"
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print(*ustrs) }
+
+ Zlib::GzipReader.open(t.path, encoding: "UTF-8") do |f|
+ assert_equal(ustrs[0], f.gets)
+ assert_equal(ustrs[1], f.gets)
+ assert_equal(ustrs[2], f.gets)
+ assert_nil(f.gets)
+ end
+
+ Zlib::GzipReader.open(t.path, encoding: "UTF-8") do |f|
+ assert_equal(ustrs.join(''), f.gets(nil))
+ end
+
+ Zlib::GzipReader.open(t.path, encoding: "UTF-8") do |f|
+ assert_equal(ustrs[0], f.gets(20))
+ assert_equal(ustrs[1][0,2], f.gets(5))
+ assert_equal(ustrs[1][2..-1]+ustrs[2][0,1], f.gets(nil, 5))
+ assert_equal(ustrs[2][1..-1], f.gets(nil, 20))
+ assert_nil(f.gets)
+ end
+ }
+ end
+
+ def test_readline
+ Tempfile.create("test_zlib_gzip_reader_readline") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo\nbar\nbaz\n") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal("foo\n", f.readline)
+ assert_equal("bar\n", f.readline)
+ assert_equal("baz\n", f.readline)
+ assert_raise(EOFError) { f.readline }
+ end
+ }
+ end
+
+ def test_each
+ Tempfile.create("test_zlib_gzip_reader_each") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo\nbar\nbaz\n") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ a = ["foo\n", "bar\n", "baz\n"]
+ f.each {|l| assert_equal(a.shift, l) }
+ end
+ }
+ end
+
+ def test_readlines
+ Tempfile.create("test_zlib_gzip_reader_readlines") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo\nbar\nbaz\n") }
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal(["foo\n", "bar\n", "baz\n"], f.readlines)
+ end
+ }
+ end
+
+ def test_reader_wrap
+ Tempfile.create("test_zlib_gzip_reader_wrap") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+ f = open(t.path)
+ f.binmode
+ assert_equal("foo", Zlib::GzipReader.wrap(f) {|gz| gz.read })
+ assert_raise(IOError) { f.close }
+ }
+ end
+
+ def test_corrupted_header
+ gz = Zlib::GzipWriter.new(StringIO.new(s = ""))
+ gz.orig_name = "X"
+ gz.comment = "Y"
+ gz.print("foo")
+ gz.finish
+ # 14: magic(2) + method(1) + flag(1) + mtime(4) + exflag(1) + os(1) + orig_name(2) + comment(2)
+ 1.upto(14) do |idx|
+ assert_raise(Zlib::GzipFile::Error, idx) do
+ Zlib::GzipReader.new(StringIO.new(s[0, idx])).read
+ end
+ end
+ end
+
+ def test_encoding
+ Tempfile.create("test_zlib_gzip_reader_encoding") {|t|
+ t.binmode
+ content = (0..255).to_a.pack('c*')
+ Zlib::GzipWriter.wrap(t) {|gz| gz.print(content) }
+
+ read_all = Zlib::GzipReader.open(t.path) {|gz| gz.read }
+ assert_equal(Encoding.default_external, read_all.encoding)
+
+ # chunks are in BINARY regardless of encoding settings
+ read_size = Zlib::GzipReader.open(t.path) {|gz| gz.read(1024) }
+ assert_equal(Encoding::ASCII_8BIT, read_size.encoding)
+ assert_equal(content, read_size)
+ }
+ end
+ end
+
+ class TestZlibGzipWriter < Test::Unit::TestCase
+ def test_invalid_new
+ assert_raise(NoMethodError, "[ruby-dev:23228]") { Zlib::GzipWriter.new(nil).close }
+ assert_raise(NoMethodError, "[ruby-dev:23344]") { Zlib::GzipWriter.new(true).close }
+ assert_raise(NoMethodError, "[ruby-dev:23344]") { Zlib::GzipWriter.new(0).close }
+ assert_raise(NoMethodError, "[ruby-dev:23344]") { Zlib::GzipWriter.new(:hoge).close }
+ end
+
+ def test_open
+ assert_raise(ArgumentError) { Zlib::GzipWriter.open }
+
+ Tempfile.create("test_zlib_gzip_writer_open") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+ assert_equal("foo", Zlib::GzipReader.open(t.path) {|gz| gz.read })
+
+ f = Zlib::GzipWriter.open(t.path)
+ begin
+ f.print("bar")
+ ensure
+ f.close
+ end
+ assert_equal("bar", Zlib::GzipReader.open(t.path) {|gz| gz.read })
+
+ assert_raise(Zlib::StreamError) { Zlib::GzipWriter.open(t.path, 10000) }
+ }
+ end
+
+ def test_write
+ Tempfile.create("test_zlib_gzip_writer_write") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print("foo") }
+ assert_equal("foo", Zlib::GzipReader.open(t.path) {|gz| gz.read })
+
+ o = Object.new
+ def o.to_s; "bar"; end
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print(o) }
+ assert_equal("bar", Zlib::GzipReader.open(t.path) {|gz| gz.read })
+ }
+ end
+
+ def test_putc
+ Tempfile.create("test_zlib_gzip_writer_putc") {|t|
+ t.close
+ Zlib::GzipWriter.open(t.path) {|gz| gz.putc(?x) }
+ assert_equal("x", Zlib::GzipReader.open(t.path) {|gz| gz.read })
+
+ # todo: multibyte char
+ }
+ end
+
+ def test_writer_wrap
+ Tempfile.create("test_zlib_gzip_writer_wrap") {|t|
+ t.binmode
+ Zlib::GzipWriter.wrap(t) {|gz| gz.print("foo") }
+ assert_equal("foo", Zlib::GzipReader.open(t.path) {|gz| gz.read })
+ }
+ end
+ end
+
+ class TestZlib < Test::Unit::TestCase
+ def test_version
+ assert_instance_of(String, Zlib.zlib_version)
+ assert(Zlib.zlib_version.tainted?)
+ end
+
+ def test_adler32
+ assert_equal(0x00000001, Zlib.adler32)
+ assert_equal(0x02820145, Zlib.adler32("foo"))
+ assert_equal(0x02820145, Zlib.adler32("o", Zlib.adler32("fo")))
+ assert_equal(0x8a62c964, Zlib.adler32("abc\x01\x02\x03" * 10000))
+ end
+
+ def test_adler32_combine
+ one = Zlib.adler32("fo")
+ two = Zlib.adler32("o")
+ begin
+ assert_equal(0x02820145, Zlib.adler32_combine(one, two, 1))
+ rescue NotImplementedError
+ skip "adler32_combine is not implemented"
+ end
+ end
+
+ def test_crc32
+ assert_equal(0x00000000, Zlib.crc32)
+ assert_equal(0x8c736521, Zlib.crc32("foo"))
+ assert_equal(0x8c736521, Zlib.crc32("o", Zlib.crc32("fo")))
+ assert_equal(0x07f0d68f, Zlib.crc32("abc\x01\x02\x03" * 10000))
+ end
+
+ def test_crc32_combine
+ one = Zlib.crc32("fo")
+ two = Zlib.crc32("o")
+ begin
+ assert_equal(0x8c736521, Zlib.crc32_combine(one, two, 1))
+ rescue NotImplementedError
+ skip "crc32_combine is not implemented"
+ end
+ end
+
+ def test_crc_table
+ t = Zlib.crc_table
+ assert_instance_of(Array, t)
+ t.each {|x| assert_kind_of(Integer, x) }
+ end
+
+ def test_inflate
+ TestZlibInflate.new(__name__).test_inflate
+ end
+
+ def test_deflate
+ TestZlibDeflate.new(__name__).test_deflate
+ end
+
+ def test_deflate_stream
+ r = Random.new 0
+
+ deflated = ''
+
+ Zlib.deflate(r.bytes(20000)) do |chunk|
+ deflated << chunk
+ end
+
+ assert_equal 20016, deflated.length
+ end
+
+ end
+end